`
shfzhzhr
  • 浏览: 70588 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

firefox中input隐藏之后用js获取选择起始和结束位置引起异常(源码分析)

阅读更多
var textEl = document.getElementById("testText");
textEl.style.display = "none";
			
try{
    var a = textEl.selectionStart;
}catch(e){
    alert(e);
}
 

 

    textEl是一个很简单的html的input输入框。但是在设置隐藏之后获取选中的起始和结束位置就会报异常。

异常如下:

 

"[Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIDOMHTMLInputElement.selectionStart]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: file:///C:/1.html :: <TOP_LEVEL> :: line 15" data: no]"

 

    很明显这是从firefox内核中报出来的异常。

 

    我们来看下获取input选中起始位置的源码,在content\html\content\src\nsHTMLInputElement.cpp文件中:

 

 

nsresult
nsHTMLInputElement::GetSelectionRange(PRInt32* aSelectionStart,
                                      PRInt32* aSelectionEnd)
{
  nsresult rv = NS_ERROR_FAILURE;
  nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);

  if (formControlFrame) {
    nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
    if (textControlFrame)
      rv = textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
  }

  return rv;
}
 

 

 

     重要的是nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);这句代码,

在input隐藏的时候,它返回了null。为什么呢?因为传进去的参数是PR_TRUE!!!

ok,我们跟进去看为什么会返回null,经过中间几个小方法的调用,我们看content\html\content\src\nsGenericHTMLElement.cpp中的方法:

 

// static
nsIFormControlFrame*
nsGenericHTMLElement::GetFormControlFrameFor(nsIContent* aContent,
                                             nsIDocument* aDocument,
                                             PRBool aFlushContent)
{
  if (aFlushContent) {
    // Cause a flush of the frames, so we get up-to-date frame information
    aDocument->FlushPendingNotifications(Flush_Frames);
  }
  nsIFrame* frame = GetPrimaryFrameFor(aContent, aDocument);
  if (frame) {
    nsIFormControlFrame* form_frame = do_QueryFrame(frame);
    if (form_frame) {
      return form_frame;
    }

    // If we have generated content, the primary frame will be a
    // wrapper frame..  out real frame will be in its child list.
    for (frame = frame->GetFirstChild(nsnull);
         frame;
         frame = frame->GetNextSibling()) {
      form_frame = do_QueryFrame(frame);
      if (form_frame) {
        return form_frame;
      }
    }
  }

  return nsnull;
}
 

 

   这个方法里面用到了我们的PR_TRUE参数,是会调用aDocument->FlushPendingNotifications(Flush_Frames);

我们继续跟进,我们会来到layout\base\nsPresShell.cpp中的FlushPendingNotifications方法,在这个方法中,firefox会处理本shell(本iframe)中以前挂起的一些操作(比如说我们设置display为none),其中有一段代码就是处理挂起的样式操作,如下所示:

 

// Process pending restyles, since any flush of the presshell wants
    // up-to-date style data.
    if (!mIsDestroying) {
      mPresContext->FlushPendingMediaFeatureValuesChanged();

      // Flush any pending update of the user font set, since that could
      // cause style changes (for updating ex/ch units, and to cause a
      // reflow).
      mPresContext->FlushUserFontSet();

      nsAutoScriptBlocker scriptBlocker;
      mFrameConstructor->ProcessPendingRestyles();
    }
 

 

    在处理pending restyles的时候会进入到mFrameConstructor->ProcessPendingRestyles()中,它会处理一个

mPendingRestyles列表中被添加的所有的pending restyles。 我们设置的display为none会产生一个hint为

nsChangeHint_ReconstructFrame的pending restyles。

firefox会根据这个hint重现构建这个frame(不可见的容器会从nsFrameManager中移除),就是调用的

RecreateFramesForContent这个方法,这个方法里调用ContentRemoved方法,ContentRemoved方法

::DeletingFrameSubtree(frameManager, childFrame)来从nsFrameManager中删除掉这个不可见的元素,所以

FlushPendingNotifications之后我们就无法取到这个formControlFrame了,所以firefox内核返回了

NS_ERROR_FAILURE,导致了js抛出异常。

 

 

 

 

附1: 设置display为none是怎么加到pending restyles中的

看下调用堆栈

 

 

 

void
nsCSSFrameConstructor::PostRestyleEvent(nsIContent* aContent,
                                        nsReStyleHint aRestyleHint,
                                        nsChangeHint aMinChangeHint)
{
  if (NS_UNLIKELY(mPresShell->IsDestroying())) {
    return;
  }

  if (aRestyleHint == 0 && !aMinChangeHint) {
    // Nothing to do here
    return;
  }

  NS_ASSERTION(aContent->IsNodeOfType(nsINode::eELEMENT),
               "Shouldn't be trying to restyle non-elements directly");

  RestyleData existingData;
  existingData.mRestyleHint = nsReStyleHint(0);
  existingData.mChangeHint = NS_STYLE_HINT_NONE;

  mPendingRestyles.Get(aContent, &existingData);
  existingData.mRestyleHint =
    nsReStyleHint(existingData.mRestyleHint | aRestyleHint);
  NS_UpdateHint(existingData.mChangeHint, aMinChangeHint);

  mPendingRestyles.Put(aContent, existingData);

  PostRestyleEventInternal();
}
 

 

ok,就是在这个方法里面加入进去的。

 

 

附2:为什么得到input的value是没问题的

    input设置隐藏之后,即使从nsFrameManager移除掉对应的frame,也可以得到正确的value的值,这是为什么,

我们看下content\html\content\src\nsHTMLInputElement.cpp中对应的方法:

 

NS_IMETHODIMP 
nsHTMLInputElement::GetValue(nsAString& aValue)
{
  if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD) {
    // No need to flush here, if there's no frame created for this
    // input yet, there won't be a value in it (that we don't already
    // have) even if we force it to be created
    nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);

    PRBool frameOwnsValue = PR_FALSE;
    if (formControlFrame) {
      nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
      if (textControlFrame) {
        textControlFrame->OwnsValue(&frameOwnsValue);
      } else {
        // We assume if it's not a text control frame that it owns the value
        frameOwnsValue = PR_TRUE;
      }
    }

    if (frameOwnsValue) {
      formControlFrame->GetFormProperty(nsGkAtoms::value, aValue);
    } else {
      if (!GET_BOOLBIT(mBitField, BF_VALUE_CHANGED) || !mValue) {
        GetDefaultValue(aValue);
      } else {
        CopyUTF8toUTF16(mValue, aValue);
      }
    }

    return NS_OK;
  }

  if (mType == NS_FORM_INPUT_FILE) {
    if (nsContentUtils::IsCallerTrustedForCapability("UniversalFileRead")) {
      if (!mFileNames.IsEmpty()) {
        aValue = mFileNames[0];
      }
      else {
        aValue.Truncate();
      }
    } else {
      // Just return the leaf name
      nsCOMArray<nsIFile> files;
      GetFileArray(files);
      if (files.Count() == 0 || NS_FAILED(files[0]->GetLeafName(aValue))) {
        aValue.Truncate();
      }
    }
    
    return NS_OK;
  }

  // Treat value == defaultValue for other input elements
  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue) &&
      (mType == NS_FORM_INPUT_RADIO || mType == NS_FORM_INPUT_CHECKBOX)) {
    // The default value of a radio or checkbox input is "on".
    aValue.AssignLiteral("on");
  }

  if (mType != NS_FORM_INPUT_HIDDEN) {
    aValue = nsContentUtils::TrimCharsInSet(kWhitespace, aValue);
  }

  return NS_OK;
}
 

 

    GetValue方法也会首先去获取frame,但是它传进去的值是PR_FALSE,也就是说如果在设置隐藏后不首先调用

textEl.selectionStart之类的方法的话,此处得到的是正确的frame,那也就是可以得到正确的值。但是如果我们首先调用了textEl.selectionStart的话,此input对应的frame已经被移除,所以得到的frame将是空的,会得到错误的值么?不会!

    其实nsHTMLInputElement使用了一种模型+控件的模式,你在设置值的时候,如果可以得到对应的frame(控件),则值被设置到控件上,如果得不到frame(input设置了隐藏等),则firefox会首先把值保存在模型上,而firefox会保持一些监听器,等控件重现展现的时候把模型的值同步到控件上。而你取值的时候也会保持同样的规则。

 

 

 

附3: 哪些方法在input隐藏后调用会报错

在nsHTMLInputElement使用到GetFormControlFrame(PR_TRUE)的有下面6个方法:

 

 

void

nsHTMLInputElement::SelectAll

 

 

NS_IMETHODIMP

nsHTMLInputElement::SetSelectionRange

 

 

NS_IMETHODIMP

nsHTMLInputElement::SetSelectionStart

 

 

NS_IMETHODIMP

nsHTMLInputElement::SetSelectionEnd

 

 

nsresult

nsHTMLInputElement::GetSelectionRange

 

 

NS_IMETHODIMP

nsHTMLInputElement::GetPhonetic

 

 

     但是由于SelectAll没有返回值,GetPhonetic即使frame为null也会返回NS_OK。所以在input框隐藏的时候,用js来设置和获取选中的起始和结束位置都会在js里抛出异常,而其他方法都是安全的。

 

      估计Mozilla认为在文本框隐藏的时候,选择位置都是没有意义的,不应该在文本框隐藏的时候去涉及选择位置。

 

 

 

 

 

 

 

 

 

 

 

 

1
1
分享到:
评论

相关推荐

    js(javascript)获取网页源码

    //FireFox、Opera等浏览器支持的创建方式 } else { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");//IE浏览器支持的创建方式 } } //直接通过XMLHttpRequest对象获取远程网页源代码 function getSource()...

    火狐浏览器插件-获取mac地址源码

    火狐浏览器插件是一种增强火狐功能的软件组件,它们通常是用JavaScript、XUL或CSS等Web技术编写的,能够扩展火狐的功能,提供个性化的浏览体验。在本例中,我们关注的是一个特定的插件,其核心功能是获取计算机的MAC...

    js获取电脑名、ip、mac(ie&火狐)

    在Firefox中,获取电脑名和MAC地址更为复杂,因为浏览器的安全策略更加严格。火狐并不直接支持ActiveXObject,但可以使用一些插件或者WebExtensions来扩展其能力。由于获取MAC地址涉及用户隐私,大多数现代浏览器都...

    js firefox支持问题

    - **`event.x`与`event.y`**:IE中直接提供`event.x`和`event.y`属性来获取鼠标位置,而在Firefox中则需使用`event.pageX`和`event.pageY`。此外,`event.clientX`和`event.clientY`在两个浏览器中的表现也有所不同...

    JavaScript在IE和Firefox(火狐)的不兼容问题解决

    标题与描述均聚焦于“JavaScript在IE和Firefox(火狐)的不兼容问题解决”,这表明文章旨在探讨并提供解决方案来处理在不同浏览器环境下的JavaScript兼容性问题,尤其是在Internet Explorer(IE)和Mozilla Firefox...

    Firefox ios版本源码

    在这个项目中,我们从GitHub上获取了Firefox iOS的源代码,版本为Firefox Beta v6.0b4。 首先,我们要理解Firefox for iOS的架构。与桌面版Firefox不同,iOS版基于Apple的WebKit框架构建,因为Apple不允许在iOS上...

    获取Firefox火狐和所有IE核心浏览器当前访问的URL地址

    另外还支持Firefox火狐浏览器URL的获取。支持效果极佳。 如果您还需要支持其它浏览器,请联系加我的QQ群:40308283 说明一点,软件是用delphi 7 开发的。绿色软件。 最新版本下载地址:...

    JS日期选择器(兼容IE,FireFox,Oprea,chrome等主流浏览器)

    "JS日期选择器(兼容IE,FireFox,Oprea,chrome等主流浏览器)"是一个专门为JavaScript环境设计的,旨在解决跨浏览器兼容性问题的日期选择工具。这个组件的目标是确保在不同浏览器如Internet Explorer(IE)、Firefox、...

    Firefox火狐浏览器官方51.0-win32版本exe安装包

    《Firefox火狐浏览器51.0-win32版本:深入解析与使用指南》 Firefox火狐浏览器,由Mozilla基金会开发,是一款全球广受欢迎的开源网络浏览器。本文将围绕"Firefox火狐浏览器官方51.0-win32版本exe安装包"进行详细...

    JavaScript调试(Firefox) 手册

    在Firefox中,Venkman是一个强大的JavaScript调试器,专为深入理解JavaScript代码的行为而设计。以下是在Firefox中使用Venkman进行JavaScript调试的详细步骤。 ### 下载和安装Venkman 1. **方法1**:你可以直接...

    火狐兼容获取鼠标的坐标

    标题“火狐兼容获取鼠标的坐标”涉及到的是在网页开发中如何在Firefox浏览器上获取鼠标指针的位置。在Web开发中,特别是在JavaScript编程中,获取鼠标坐标是一项常见的需求,用于实现各种交互效果,如拖放功能、点击...

    deepin linux如何安装最新版firefox火狐浏览器.docx

    Deepin Linux 安装最新版 Firefox火狐浏览器详解 在 Deepin Linux 操作系统中,安装最新版的 Firefox 火狐浏览器可以通过下载最新版的 Firefox 压缩包并解压缩来实现。下面是详细的安装步骤: 首先,用户需要在 ...

    火狐Firefox24版本

    火狐Firefox是一款深受全球用户喜爱的开源网络浏览器,它的24版本在当时被视为一个重要的里程碑。这个版本的Firefox以其稳定性、安全性和性能优化为特点,是开发者和测试人员的理想选择。下面将详细介绍Firefox 24...

    FireFox Home源码

    【Firefox Home源码】是Firefox浏览器为iOS平台开发的一款辅助应用,它的主要功能是帮助用户在iPhone设备上无缝同步和访问他们在桌面电脑上的Firefox浏览数据,包括历史记录、书签和打开的标签页。这款应用并非一个...

    Firefox 火狐浏览器安装包

    火狐浏览器(Mozilla Firefox)是一款由Mozilla基金会和志愿者共同开发的开源网络浏览器,以其高度定制性、安全性和性能而受到用户欢迎。以下是一份描述: 火狐浏览器是一款跨平台的开源网络浏览器,由Mozilla基金...

    C# 实时获取IE和FireFox浏览器中URL

    经过三天的苦战,查询无数的国外网站终于实现了对IE和...利用API和DDE分别对IE和FireFox进行了浏览器地址获取,完整的源码程序,与大家分享学习。后期将完善,实现对IE,FireFox,360,搜狗等主流浏览器URL地址的监控。

    Firefox插件例子源码

    【Firefox插件例子源码】是一个关于Firefox浏览器插件开发的学习资源,包含了示例代码,可供开发者参考和学习。这个压缩包中包含了多个文件,它们各自在插件开发过程中扮演着不同的角色。以下是对这些文件及其相关的...

    Firefox火狐浏览器官方1.5.0.6-win32版本exe安装包

    《Firefox火狐浏览器1.5.0.6-win32版本安装详解》 Firefox火狐浏览器,由Mozilla基金会开发,是一款开源、免费的网络浏览器,以其强大的安全性、隐私保护和高度可定制性著称。本资源是Firefox 1.5.0.6-win32的官方...

    firefox debug 调js有用

    标题中的“firefox debug 调js有用”表明我们主要探讨的是使用Firefox浏览器的调试工具来对JavaScript代码进行调试。在Web开发中,JavaScript是至关重要的客户端脚本语言,用于实现网页的动态交互功能。Firefox提供...

    Javascript的IE和Firefox(火狐)兼容性

    ### Javascript的IE与Firefox(火狐)兼容性解决方案 在Web开发过程中,浏览器兼容性问题一直是开发者们关注的重点之一。由于不同的浏览器对于Web标准的支持程度存在差异,这导致了同样的代码在不同浏览器中的表现...

Global site tag (gtag.js) - Google Analytics