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