author | Henri Sivonen <hsivonen@iki.fi> |
Fri, 18 Jan 2013 16:27:03 +0200 | |
changeset 119257 | 010f52e1cf84d3d89faf3985c7e79c4ff06a33a7 |
parent 119256 | b814f5638b20c2bdd44e135b22717026f269c901 |
child 119258 | 2aa7bb583a6d1a21f5b082df8ea49d19dda45320 |
push id | 24195 |
push user | Ms2ger@gmail.com |
push date | Sat, 19 Jan 2013 16:10:11 +0000 |
treeherder | mozilla-central@02e12a80aef9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bzbarsky |
bugs | 234628 |
milestone | 21.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -95,18 +95,18 @@ class HTMLBodyElement; class Link; class ProcessingInstruction; class UndoManager; template<typename> class Sequence; } // namespace dom } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0xff03d72f, 0x87cd, 0x4d11, \ - { 0x81, 0x8d, 0xa8, 0xb4, 0xf5, 0x98, 0x1a, 0x10 } } +{ 0x2df7f766, 0xf70b, 0x4de4, \ + { 0xb0, 0xba, 0x78, 0x25, 0x07, 0x41, 0xd6, 0xce } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) // Enum for requesting a particular type of document when creating a doc enum DocumentFlavor { DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true @@ -563,16 +563,22 @@ public: /** * Return the root element for this document. */ Element* GetRootElement() const; virtual nsViewportInfo GetViewportInfo(uint32_t aDisplayWidth, uint32_t aDisplayHeight) = 0; + /** + * True iff this doc will ignore manual character encoding overrides. + */ + virtual bool WillIgnoreCharsetOverride() { + return true; + } protected: virtual Element *GetRootElementInternal() const = 0; public: // Get the root <html> element, or return null if there isn't one (e.g. // if the root isn't <html>) Element* GetHtmlElement();
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -3181,41 +3181,36 @@ nsDocument::SetHeaderData(nsIAtom* aHead aHeaderField == nsGkAtoms::viewport_initial_scale || aHeaderField == nsGkAtoms::viewport_height || aHeaderField == nsGkAtoms::viewport_width || aHeaderField == nsGkAtoms::viewport_user_scalable) { mViewportType = Unknown; } } -bool +void nsDocument::TryChannelCharset(nsIChannel *aChannel, int32_t& aCharsetSource, nsACString& aCharset, nsHtml5TreeOpExecutor* aExecutor) { - if(kCharsetFromChannel <= aCharsetSource) { - return true; - } - if (aChannel) { nsAutoCString charsetVal; nsresult rv = aChannel->GetContentCharset(charsetVal); if (NS_SUCCEEDED(rv)) { nsAutoCString preferred; if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) { aCharset = preferred; aCharsetSource = kCharsetFromChannel; - return true; + return; } else if (aExecutor && !charsetVal.IsEmpty()) { aExecutor->ComplainAboutBogusProtocolCharset(this); } } } - return false; } nsresult nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager, nsStyleSet* aStyleSet, nsIPresShell** aInstancePtrResult) { // Don't add anything here. Add it to |doCreateShell| instead.
--- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1045,17 +1045,17 @@ protected: } void ReportEmptyGetElementByIdArg(); void DispatchContentLoadedEvents(); void RetrieveRelevantHeaders(nsIChannel *aChannel); - bool TryChannelCharset(nsIChannel *aChannel, + void TryChannelCharset(nsIChannel *aChannel, int32_t& aCharsetSource, nsACString& aCharset, nsHtml5TreeOpExecutor* aExecutor); // Call this before the document does something that will unbind all content. // That will stop us from doing a lot of work as each element is removed. void DestroyElementMaps();
--- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -100,16 +100,17 @@ #include "mozilla/Preferences.h" #include "nsMimeTypes.h" #include "nsIRequest.h" #include "nsHtml5TreeOpExecutor.h" #include "nsHtml5Parser.h" #include "nsIDOMJSWindow.h" #include "nsSandboxFlags.h" #include "mozilla/dom/HTMLBodyElement.h" +#include "nsCharsetSource.h" using namespace mozilla; using namespace mozilla::dom; #define NS_MAX_DOCUMENT_WRITE_DEPTH 20 #include "prtime.h" @@ -336,79 +337,86 @@ nsHTMLDocument::TryHintCharset(nsIMarkup return; } } } return; } -bool +void nsHTMLDocument::TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV, nsIDocShell* aDocShell, int32_t& aCharsetSource, nsACString& aCharset) { nsresult rv = NS_OK; if(kCharsetFromUserForced <= aCharsetSource) - return true; + return; + + // mCharacterSet not updated yet for channel, so check aCharset, too. + if (WillIgnoreCharsetOverride() || !IsAsciiCompatible(aCharset)) { + return; + } nsAutoCString forceCharsetFromDocShell; if (aMarkupDV) { + // XXX mailnews-only rv = aMarkupDV->GetForceCharacterSet(forceCharsetFromDocShell); } - // Not making the IsAsciiCompatible() check here to allow the user to - // force UTF-16 from the menu. - if(NS_SUCCEEDED(rv) && !forceCharsetFromDocShell.IsEmpty()) { + if(NS_SUCCEEDED(rv) && + !forceCharsetFromDocShell.IsEmpty() && + IsAsciiCompatible(forceCharsetFromDocShell)) { aCharset = forceCharsetFromDocShell; - //TODO: we should define appropriate constant for force charset aCharsetSource = kCharsetFromUserForced; - } else if (aDocShell) { + return; + } + + if (aDocShell) { + // This is the Character Encoding menu code path in Firefox nsCOMPtr<nsIAtom> csAtom; aDocShell->GetForcedCharset(getter_AddRefs(csAtom)); if (csAtom) { - csAtom->ToUTF8String(aCharset); + nsAutoCString charset; + csAtom->ToUTF8String(charset); + if (!IsAsciiCompatible(charset)) { + return; + } + aCharset = charset; aCharsetSource = kCharsetFromUserForced; aDocShell->SetForcedCharset(nullptr); - return true; } } - - return false; } -bool +void nsHTMLDocument::TryCacheCharset(nsICachingChannel* aCachingChannel, int32_t& aCharsetSource, nsACString& aCharset) { nsresult rv; if (kCharsetFromCache <= aCharsetSource) { - return true; + return; } nsCString cachedCharset; rv = aCachingChannel->GetCacheTokenCachedCharset(cachedCharset); // Check IsAsciiCompatible() even in the cache case, because the value // might be stale and in the case of a stale charset that is not a rough // ASCII superset, the parser has no way to recover. if (NS_SUCCEEDED(rv) && !cachedCharset.IsEmpty() && IsAsciiCompatible(cachedCharset)) { aCharset = cachedCharset; aCharsetSource = kCharsetFromCache; - - return true; } - - return false; } static bool CheckSameOrigin(nsINode* aNode1, nsINode* aNode2) { NS_PRECONDITION(aNode1, "Null node?"); NS_PRECONDITION(aNode2, "Null node?"); @@ -433,62 +441,79 @@ void nsHTMLDocument::TryParentCharset(nsIDocShell* aDocShell, nsIDocument* aParentDocument, int32_t& aCharsetSource, nsACString& aCharset) { if (!aDocShell) { return; } - int32_t source; + if (aCharsetSource >= kCharsetFromParentForced) { + return; + } + nsCOMPtr<nsIAtom> csAtom; int32_t parentSource; nsAutoCString parentCharset; aDocShell->GetParentCharset(getter_AddRefs(csAtom)); if (!csAtom) { return; } aDocShell->GetParentCharsetSource(&parentSource); csAtom->ToUTF8String(parentCharset); - if (kCharsetFromParentForced <= parentSource) { - source = kCharsetFromParentForced; - } else if (kCharsetFromHintPrevDoc == parentSource) { + if (kCharsetFromParentForced == parentSource || + kCharsetFromUserForced == parentSource) { + if (WillIgnoreCharsetOverride() || + !IsAsciiCompatible(aCharset) || // if channel said UTF-16 + !IsAsciiCompatible(parentCharset)) { + return; + } + aCharset.Assign(parentCharset); + aCharsetSource = kCharsetFromParentForced; + return; + } + + if (aCharsetSource >= kCharsetFromHintPrevDoc) { + return; + } + + if (kCharsetFromHintPrevDoc == parentSource) { // Make sure that's OK if (!aParentDocument || !CheckSameOrigin(this, aParentDocument) || !IsAsciiCompatible(parentCharset)) { return; } // if parent is posted doc, set this prevent autodetections // I'm not sure this makes much sense... but whatever. - source = kCharsetFromHintPrevDoc; - } else if (kCharsetFromCache <= parentSource) { + aCharset.Assign(parentCharset); + aCharsetSource = kCharsetFromHintPrevDoc; + return; + } + + if (aCharsetSource >= kCharsetFromParentFrame) { + return; + } + + if (kCharsetFromCache <= parentSource) { // Make sure that's OK if (!aParentDocument || !CheckSameOrigin(this, aParentDocument) || !IsAsciiCompatible(parentCharset)) { return; } - source = kCharsetFromParentFrame; - } else { - return; + aCharset.Assign(parentCharset); + aCharsetSource = kCharsetFromParentFrame; } - - if (source < aCharsetSource) { - return; - } - - aCharset.Assign(parentCharset); - aCharsetSource = source; } void -nsHTMLDocument::UseWeakDocTypeDefault(int32_t& aCharsetSource, +nsHTMLDocument::TryWeakDocTypeDefault(int32_t& aCharsetSource, nsACString& aCharset) { if (kCharsetFromWeakDocTypeDefault <= aCharsetSource) return; const nsAdoptingCString& defCharset = Preferences::GetLocalizedCString("intl.charset.default"); @@ -498,38 +523,33 @@ nsHTMLDocument::UseWeakDocTypeDefault(in aCharset = defCharset; } else { aCharset.AssignLiteral("ISO-8859-1"); } aCharsetSource = kCharsetFromWeakDocTypeDefault; return; } -bool +void nsHTMLDocument::TryDefaultCharset( nsIMarkupDocumentViewer* aMarkupDV, int32_t& aCharsetSource, nsACString& aCharset) { if(kCharsetFromUserDefault <= aCharsetSource) - return true; + return; nsAutoCString defaultCharsetFromDocShell; if (aMarkupDV) { nsresult rv = aMarkupDV->GetDefaultCharacterSet(defaultCharsetFromDocShell); - // Not making the IsAsciiCompatible() check here to allow the user to - // force UTF-16 from the menu. - if(NS_SUCCEEDED(rv)) { + if(NS_SUCCEEDED(rv) && IsAsciiCompatible(defaultCharsetFromDocShell)) { aCharset = defaultCharsetFromDocShell; - aCharsetSource = kCharsetFromUserDefault; - return true; } } - return false; } void nsHTMLDocument::SetDocumentCharacterSet(const nsACString& aCharSetID) { nsDocument::SetDocumentCharacterSet(aCharSetID); // Make sure to stash this charset on our channel as needed if it's a wyciwyg // channel. @@ -731,47 +751,49 @@ nsHTMLDocument::StartDocumentLoad(const parserCharsetSource = charsetSource; parserCharset = charset; } else { NS_ASSERTION(docShell && docShellAsItem, "Unexpected null value"); charsetSource = kCharsetUninitialized; wyciwygChannel = do_QueryInterface(aChannel); - // The following charset resolving calls has implied knowledge - // about charset source priority order. Each try will return true - // if the source is higher or equal to the source as its name - // describes. Some try call might change charset source to - // multiple values, like TryHintCharset and TryParentCharset. It - // should be always safe to try more sources. - if (!TryUserForcedCharset(muCV, docShell, charsetSource, charset)) { - TryHintCharset(muCV, charsetSource, charset); - TryParentCharset(docShell, parentDocument, charsetSource, charset); - - // Don't actually get the charset from the channel if this is a - // wyciwyg channel; it'll always be UTF-16 - if (!wyciwygChannel && - TryChannelCharset(aChannel, charsetSource, charset, executor)) { - // Use the channel's charset (e.g., charset from HTTP - // "Content-Type" header). - } - else if (cachingChan && !urlSpec.IsEmpty() && - TryCacheCharset(cachingChan, charsetSource, charset)) { - // Use the cache's charset. - } - else if (TryDefaultCharset(muCV, charsetSource, charset)) { - // Use the default charset. - // previous document charset might be inherited as default charset. - } - else { - // Use the weak doc type default charset - UseWeakDocTypeDefault(charsetSource, charset); - } + // The following will try to get the character encoding from various + // sources. Each Try* function will return early if the source is already + // at least as large as any of the sources it might look at. Some of + // these functions (like TryHintCharset and TryParentCharset) can set + // charsetSource to various values depending on where the charset they + // end up finding originally comes from. + + // Don't actually get the charset from the channel if this is a + // wyciwyg channel; it'll always be UTF-16 + if (!wyciwygChannel) { + // Otherwise, try the channel's charset (e.g., charset from HTTP + // "Content-Type" header) first. This way, we get to reject overrides in + // TryParentCharset and TryUserForcedCharset if the channel said UTF-16. + // This is to avoid socially engineered XSS by adding user-supplied + // content to a UTF-16 site such that the byte have a dangerous + // interpretation as ASCII and the user can be lured to using the + // charset menu. + TryChannelCharset(aChannel, charsetSource, charset, executor); } + TryUserForcedCharset(muCV, docShell, charsetSource, charset); + + TryHintCharset(muCV, charsetSource, charset); // XXX mailnews-only + TryParentCharset(docShell, parentDocument, charsetSource, charset); + + if (cachingChan && !urlSpec.IsEmpty()) { + TryCacheCharset(cachingChan, charsetSource, charset); + } + + TryDefaultCharset(muCV, charsetSource, charset); + + TryWeakDocTypeDefault(charsetSource, charset); + bool isPostPage = false; // check if current doc is from POST command nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel)); if (httpChannel) { nsAutoCString methodStr; rv = httpChannel->GetRequestMethod(methodStr); isPostPage = (NS_SUCCEEDED(rv) && methodStr.EqualsLiteral("POST")); @@ -3774,8 +3796,42 @@ nsHTMLDocument::DocSizeOfExcludingThis(n // - mLinks // - mAnchors // - mScripts // - mForms // - mFormControls // - mWyciwygChannel // - mMidasCommandManager } + +bool +nsHTMLDocument::WillIgnoreCharsetOverride() +{ + if (!mIsRegularHTML) { + return true; + } + if (mCharacterSetSource == kCharsetFromByteOrderMark) { + return true; + } + if (!IsAsciiCompatible(mCharacterSet)) { + return true; + } + nsCOMPtr<nsIWyciwygChannel> wyciwyg = do_QueryInterface(mChannel); + if (wyciwyg) { + return true; + } + nsIURI* uri = GetOriginalURI(); + if (uri) { + bool schemeIs = false; + uri->SchemeIs("about", &schemeIs); + if (schemeIs) { + return true; + } + bool isResource; + nsresult rv = NS_URIChainHasFlags(uri, + nsIProtocolHandler::URI_IS_UI_RESOURCE, + &isResource); + if (NS_FAILED(rv) || isResource) { + return true; + } + } + return false; +}
--- a/content/html/document/src/nsHTMLDocument.h +++ b/content/html/document/src/nsHTMLDocument.h @@ -168,16 +168,18 @@ public: return nsDocument::GetElementById(aElementId); } virtual nsXPCClassInfo* GetClassInfo(); virtual void DocSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const; // DocSizeOfIncludingThis is inherited from nsIDocument. + virtual bool WillIgnoreCharsetOverride(); + // WebIDL API void GetDomain(nsAString& aDomain, mozilla::ErrorResult& rv); void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv); void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv); void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv); nsGenericHTMLElement *GetBody(); void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv); Element *GetHead() { return GetHeadElement(); } @@ -291,34 +293,34 @@ protected: /** # of forms in the document, synchronously set */ int32_t mNumForms; static uint32_t gWyciwygSessionCnt; static bool IsAsciiCompatible(const nsACString& aPreferredName); static void TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV, - int32_t& aCharsetSource, - nsACString& aCharset); - static bool TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV, - nsIDocShell* aDocShell, - int32_t& aCharsetSource, - nsACString& aCharset); - static bool TryCacheCharset(nsICachingChannel* aCachingChannel, + int32_t& aCharsetSource, + nsACString& aCharset); + void TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV, + nsIDocShell* aDocShell, + int32_t& aCharsetSource, + nsACString& aCharset); + static void TryCacheCharset(nsICachingChannel* aCachingChannel, int32_t& aCharsetSource, nsACString& aCharset); // aParentDocument could be null. void TryParentCharset(nsIDocShell* aDocShell, - nsIDocument* aParentDocument, - int32_t& charsetSource, nsACString& aCharset); - static void UseWeakDocTypeDefault(int32_t& aCharsetSource, - nsACString& aCharset); - static bool TryDefaultCharset(nsIMarkupDocumentViewer* aMarkupDV, - int32_t& aCharsetSource, - nsACString& aCharset); + nsIDocument* aParentDocument, + int32_t& charsetSource, nsACString& aCharset); + static void TryWeakDocTypeDefault(int32_t& aCharsetSource, + nsACString& aCharset); + static void TryDefaultCharset(nsIMarkupDocumentViewer* aMarkupDV, + int32_t& aCharsetSource, + nsACString& aCharset); // Override so we can munge the charset on our wyciwyg channel as needed. virtual void SetDocumentCharacterSet(const nsACString& aCharSetID); // Tracks if we are currently processing any document.write calls (either // implicit or explicit). Note that if a write call writes out something which // would block the parser, then mWriteLevel will be incorrect until the parser // finishes processing that script.
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -2244,16 +2244,35 @@ nsDocShell::SetFullscreenAllowed(bool aF // across process boundaries. return NS_ERROR_UNEXPECTED; } mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS); return NS_OK; } NS_IMETHODIMP +nsDocShell::GetMayEnableCharacterEncodingMenu(bool* aMayEnableCharacterEncodingMenu) +{ + *aMayEnableCharacterEncodingMenu = false; + if (!mContentViewer) { + return NS_OK; + } + nsIDocument* doc = mContentViewer->GetDocument(); + if (!doc) { + return NS_OK; + } + if (doc->WillIgnoreCharsetOverride()) { + return NS_OK; + } + + *aMayEnableCharacterEncodingMenu = true; + return NS_OK; +} + +NS_IMETHODIMP nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection, nsISimpleEnumerator **outEnum) { NS_ENSURE_ARG_POINTER(outEnum); *outEnum = nullptr; nsRefPtr<nsDocShellEnumerator> docShellEnum; if (aDirection == ENUMERATE_FORWARDS) docShellEnum = new nsDocShellForwardsEnumerator;
--- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -34,17 +34,17 @@ interface nsISHEntry; interface nsILayoutHistoryState; interface nsISecureBrowserUI; interface nsIDOMStorage; interface nsIPrincipal; interface nsIWebBrowserPrint; interface nsIVariant; interface nsIPrivacyTransitionObserver; -[scriptable, builtinclass, uuid(008f5c86-b915-458c-aaf1-78de3b484e68)] +[scriptable, builtinclass, uuid(ca15d803-1330-4154-b3f9-063fb2b443e7)] interface nsIDocShell : nsISupports { /** * Loads a given URI. This will give priority to loading the requested URI * in the object implementing this interface. If it can't be loaded here * however, the URL dispatcher will go through its normal process of content * loading. * @@ -578,17 +578,17 @@ interface nsIDocShell : nsISupports /** * In a child docshell, this is the charset of the parent docshell */ attribute nsIAtom parentCharset; /* * In a child docshell, this is the source of parentCharset - * @see nsIParser + * @see nsCharsetSource.h */ attribute int32_t parentCharsetSource; /** * Add an observer to the list of parties to be notified when this docshell's * private browsing status is changed. |obs| must support weak references. */ void addWeakPrivacyTransitionObserver(in nsIPrivacyTransitionObserver obs); @@ -720,9 +720,15 @@ interface nsIDocShell : nsISupports * to propagate the value of the cross process parent's iframe's * "allowfullscreen" attribute to the child process. Setting * fullscreenAllowed on docshells which aren't content boundaries throws an * exception. */ [infallible] readonly attribute boolean fullscreenAllowed; void setFullscreenAllowed(in boolean allowed); + + /** + * Indicates whether the UI may enable the character encoding menu. The UI + * must disable the menu when this property is false. + */ + [infallible] readonly attribute boolean mayEnableCharacterEncodingMenu; };
--- a/docshell/base/nsIMarkupDocumentViewer.idl +++ b/docshell/base/nsIMarkupDocumentViewer.idl @@ -41,29 +41,30 @@ interface nsIMarkupDocumentViewer : nsIS /** Disable entire author style level (including HTML presentation hints) */ attribute boolean authorStyleDisabled; /* XXX Comment here! */ attribute ACString defaultCharacterSet; - /* - XXX Comment here! - */ + /** + * XXX comm-central only: bug 829543. Not the Character Encoding menu in + * browser! + */ attribute ACString forceCharacterSet; - /* - XXX Comment here! - */ + /** + * XXX comm-central only: bug 829543. + */ attribute ACString hintCharacterSet; - /* - XXX Comment here! - */ + /** + * XXX comm-central only: bug 829543. + */ attribute int32_t hintCharacterSetSource; /* character set from prev document */ attribute ACString prevDocCharacterSet; //void GetCharacterSetHint(in wstring hintCharset, in int32_t charsetSource);
--- a/docshell/test/browser/browser_bug134911.js +++ b/docshell/test/browser/browser_bug134911.js @@ -2,17 +2,17 @@ const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059"; const enteredText1="The quick brown fox jumps over the lazy dog"; const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1"; function test() { waitForExplicitFinish(); - var rootDir = getRootDirectory(gTestPath); + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html"); gBrowser.selectedBrowser.addEventListener("load", afterOpen, true); } function afterOpen() { gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true); gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);