Bug 234628 part 2 - Make manual encoding overrides not apply to internal URL schemes or to docs declared as UTF-16 on the HTTP level; make docshells able to report when the encoding menu should be disabled. r=bzbarsky.
authorHenri Sivonen <hsivonen@iki.fi>
Fri, 18 Jan 2013 16:27:03 +0200
changeset 119257 010f52e1cf84d3d89faf3985c7e79c4ff06a33a7
parent 119256 b814f5638b20c2bdd44e135b22717026f269c901
child 119258 2aa7bb583a6d1a21f5b082df8ea49d19dda45320
push id24195
push userMs2ger@gmail.com
push dateSat, 19 Jan 2013 16:10:11 +0000
treeherdermozilla-central@02e12a80aef9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs234628
milestone21.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
Bug 234628 part 2 - Make manual encoding overrides not apply to internal URL schemes or to docs declared as UTF-16 on the HTTP level; make docshells able to report when the encoding menu should be disabled. r=bzbarsky.
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/html/document/src/nsHTMLDocument.cpp
content/html/document/src/nsHTMLDocument.h
docshell/base/nsDocShell.cpp
docshell/base/nsIDocShell.idl
docshell/base/nsIMarkupDocumentViewer.idl
docshell/test/browser/browser_bug134911.js
--- 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);