日期:2014-05-16 浏览次数:20380 次
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。