Bug 1455649 - DocumentL10n, part 2 - Extend nsIDocument to use DocumentL10n. draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Mon, 25 Jun 2018 10:03:30 -0700
changeset 826337 1d1ac90ae9d0a59829fd66aa9da436a150360c70
parent 826336 1dd0a9fba23044279ba5d80e57c85b7312d20794
child 826338 abdfcf5b962b7beced7b2df3207e845b621252f7
push id118298
push userbmo:gandalf@aviary.pl
push dateFri, 03 Aug 2018 17:04:21 +0000
bugs1455649
milestone63.0a1
Bug 1455649 - DocumentL10n, part 2 - Extend nsIDocument to use DocumentL10n. Extend nsIDocument to store DocumentL10n instance for documents which can be localized. MozReview-Commit-ID: APXbRPHZxAg
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/webidl/Document.webidl
xpcom/ds/nsGkAtomList.h
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -206,16 +206,17 @@
 #include "nsWrapperCacheInlines.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/dom/AnimatableBinding.h"
 #include "mozilla/dom/AnonymousContent.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/ClientState.h"
 #include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/DocumentL10n.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/HTMLBodyElement.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/NodeFilterBinding.h"
 #include "mozilla/OwningNonNull.h"
@@ -1372,16 +1373,17 @@ nsIDocument::nsIDocument()
     mCharacterSet(WINDOWS_1252_ENCODING),
     mCharacterSetSource(0),
     mParentDocument(nullptr),
     mCachedRootElement(nullptr),
     mNodeInfoManager(nullptr),
 #ifdef DEBUG
     mStyledLinksCleared(false),
 #endif
+    mDocumentL10n(nullptr),
     mBidiEnabled(false),
     mMathMLEnabled(false),
     mIsInitialDocumentInWindow(false),
     mIgnoreDocGroupMismatches(false),
     mLoadedAsData(false),
     mLoadedAsInteractiveData(false),
     mMayStartLayout(true),
     mHaveFiredTitleChange(false),
@@ -1548,16 +1550,18 @@ nsIDocument::ClearAllBoxObjects()
   }
 }
 
 nsIDocument::~nsIDocument()
 {
   MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
              "must not have media query lists left");
 
+  mDocumentL10n = nullptr;
+
   if (mNodeInfoManager) {
     mNodeInfoManager->DropDocumentReference();
   }
 
   if (mDocGroup) {
     mDocGroup->RemoveDocument(this);
   }
 
@@ -1871,16 +1875,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
   }
 
   // Traverse all nsIDocument pointer members.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n)
 
   // Traverse all nsDocument nsCOMPtrs.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
@@ -2024,16 +2029,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n);
 
   tmp->mParentDocument = nullptr;
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
 
   tmp->ClearAllBoxObjects();
@@ -2222,16 +2228,44 @@ nsIDocument::Reset(nsIChannel* aChannel,
       mDocumentBaseURI = baseURI;
       mChromeXHRDocBaseURI = nullptr;
     }
   }
 
   mChannel = aChannel;
 }
 
+bool
+PrincipalAllowsL10n(nsIPrincipal* principal) {
+  // Fast track privileged contexts
+  if (nsContentUtils::IsSystemPrincipal(principal)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = principal->GetURI(getter_AddRefs(uri));
+  if (NS_FAILED(rv) || !uri) {
+    return false;
+  }
+
+  bool isAbout;
+  rv = uri->SchemeIs("about", &isAbout);
+  if (NS_FAILED(rv) || (!isAbout)) {
+    return false;
+  }
+
+  bool isNonWeb;
+  rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DANGEROUS_TO_LOAD, &isNonWeb);
+  if (NS_FAILED(rv) || !isNonWeb) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 nsIDocument::ResetToURI(nsIURI* aURI,
                         nsILoadGroup* aLoadGroup,
                         nsIPrincipal* aPrincipal)
 {
   MOZ_ASSERT(aURI, "Null URI passed to ResetToURI");
 
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
@@ -3302,16 +3336,99 @@ nsIDocument::GetAllowPlugins()
   FlashClassification classification = DocumentFlashClassification();
   if (classification == FlashClassification::Denied) {
     return false;
   }
 
   return true;
 }
 
+DocumentL10n*
+nsIDocument::GetLocalization(bool initialize)
+{
+  if (!mDocumentL10n && initialize) {
+    auto l10n = new DocumentL10n(this);
+    if (l10n) {
+      mDocumentL10n = l10n;
+    }
+  }
+  return mDocumentL10n;
+}
+
+already_AddRefed<DocumentL10n>
+nsIDocument::GetL10n(ErrorResult& aRv)
+{
+  auto l10n = GetLocalization(false);
+  if (!l10n) {
+    /* aRv.Throw(NS_ERROR_FAILURE); */
+    return nullptr;
+  }
+
+  RefPtr<DocumentL10n> refL10n(l10n);
+  return refL10n.forget();
+}
+
+bool
+nsDocument::DocumentSupportsL10n(JSContext* aCx, JSObject* aObject)
+{
+  return PrincipalAllowsL10n(nsContentUtils::SubjectPrincipal(aCx));
+}
+
+void
+nsIDocument::LocalizationLinkAdded(Element* aLinkElement)
+{
+  nsString href;
+  aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
+  auto l10n = GetLocalization(false);
+  if (l10n) {
+    AutoTArray<nsString, 1> resourceIds;
+    resourceIds.AppendElement(href);
+    l10n->AddResourceIds(resourceIds);
+  } else {
+    mL10nResources.AppendElement(href);
+  }
+}
+
+void
+nsIDocument::LocalizationLinkRemoved(Element* aLinkElement)
+{
+  nsString href;
+  aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
+  auto l10n = GetLocalization(false);
+  if (l10n) {
+    AutoTArray<nsString, 1> resourceIds;
+    resourceIds.AppendElement(href);
+    uint32_t remaining = l10n->RemoveResourceIds(resourceIds);
+    if (remaining == 0) {
+      mDocumentL10n = nullptr;
+    }
+  }
+}
+
+void
+nsIDocument::OnL10nResourceContainerParsed()
+{
+  if (!mL10nResources.IsEmpty()) {
+    auto l10n = GetLocalization(true);
+    if (l10n) {
+      l10n->AddResourceIds(mL10nResources);
+      mL10nResources.Clear();
+    }
+  }
+}
+
+void
+nsIDocument::OnDocumentParsed()
+{
+  auto l10n = GetLocalization(false);
+  if (l10n) {
+    l10n->OnDocumentParsed();
+  }
+}
+
 bool
 nsDocument::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   return nsContentUtils::IsSystemCaller(aCx) ||
          nsContentUtils::AnimationsAPICoreEnabled();
 }
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -153,16 +153,17 @@ public:
                                      nsILoadGroup* aLoadGroup,
                                      nsISupports* aContainer,
                                      nsIStreamListener **aDocListener,
                                      bool aReset = true,
                                      nsIContentSink* aContentSink = nullptr) override = 0;
 
   virtual void StopDocumentLoad() override;
 
+  static bool DocumentSupportsL10n(JSContext* aCx, JSObject* aObject);
   static bool IsWebAnimationsEnabled(JSContext* aCx, JSObject* aObject);
   static bool IsWebAnimationsEnabled(mozilla::dom::CallerType aCallerType);
   static bool IsWebAnimationsGetAnimationsEnabled(JSContext* aCx,
                                                   JSObject* aObject);
   static bool AreWebAnimationsImplicitKeyframesEnabled(JSContext* aCx,
                                                        JSObject* aObject);
   static bool AreWebAnimationsTimelinesEnabled(JSContext* aCx,
                                                JSObject* aObject);
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -142,16 +142,17 @@ class AnonymousContent;
 class Attr;
 class BoxObject;
 class ClientInfo;
 class ClientState;
 class CDATASection;
 class Comment;
 struct CustomElementDefinition;
 class DocGroup;
+class DocumentL10n;
 class DocumentFragment;
 class DocumentTimeline;
 class DocumentType;
 class DOMImplementation;
 class DOMIntersectionObserver;
 class DOMStringList;
 class Element;
 struct ElementCreationOptions;
@@ -3176,16 +3177,22 @@ public:
     CreateAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aQualifiedName,
                       mozilla::ErrorResult& rv);
   void GetInputEncoding(nsAString& aInputEncoding) const;
   already_AddRefed<mozilla::dom::Location> GetLocation() const;
   void GetReferrer(nsAString& aReferrer) const;
   void GetLastModified(nsAString& aLastModified) const;
   void GetReadyState(nsAString& aReadyState) const;
+  void LocalizationLinkAdded(Element* aLinkElement);
+  void LocalizationLinkRemoved(Element* aLinkElement);
+  mozilla::dom::DocumentL10n* GetLocalization(bool initialize);
+  void OnL10nResourceContainerParsed();
+  void OnDocumentParsed();
+  already_AddRefed<mozilla::dom::DocumentL10n> GetL10n(ErrorResult& aRv);
 
   void GetTitle(nsAString& aTitle);
   void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv);
   void GetDir(nsAString& aDirection) const;
   void SetDir(const nsAString& aDirection);
   nsIHTMLCollection* Images();
   nsIHTMLCollection* Embeds();
   nsIHTMLCollection* Plugins()
@@ -3858,16 +3865,19 @@ protected:
   // Last time this document or a one of its sub-documents was focused.  If
   // focus has never occurred then mLastFocusTime.IsNull() will be true.
   mozilla::TimeStamp mLastFocusTime;
 
   mozilla::EventStates mDocumentState;
 
   RefPtr<mozilla::dom::Promise> mReadyForIdle;
 
+  RefPtr<mozilla::dom::DocumentL10n> mDocumentL10n;
+  nsTArray<nsString> mL10nResources;
+
   // True if BIDI is enabled.
   bool mBidiEnabled : 1;
   // True if a MathML element has ever been owned by this document.
   bool mMathMLEnabled : 1;
 
   // True if this document is the initial document for a window.  This should
   // basically be true only for documents that exist in newly-opened windows or
   // documents created to satisfy a GetDocument() on a window when there's no
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -488,16 +488,20 @@ enum FlashClassification {
   "allowed",        // Site is on the Flash whitelist
   "denied"          // Site is on the Flash blacklist
 };
 partial interface Document {
   [ChromeOnly]
   readonly attribute FlashClassification documentFlashClassification;
 };
 
+partial interface Document {
+  [Throws, Func="nsDocument::DocumentSupportsL10n"] readonly attribute DocumentL10n? l10n;
+};
+
 Document implements XPathEvaluator;
 Document implements GlobalEventHandlers;
 Document implements DocumentAndElementEventHandlers;
 Document implements TouchEventHandlers;
 Document implements ParentNode;
 Document implements OnErrorEventHandlerForNodes;
 Document implements GeometryUtils;
 Document implements FontFaceSource;
--- a/xpcom/ds/nsGkAtomList.h
+++ b/xpcom/ds/nsGkAtomList.h
@@ -543,16 +543,17 @@ GK_ATOM(left, "left")
 GK_ATOM(leftmargin, "leftmargin")
 GK_ATOM(legend, "legend")
 GK_ATOM(length, "length")
 GK_ATOM(letterValue, "letter-value")
 GK_ATOM(level, "level")
 GK_ATOM(li, "li")
 GK_ATOM(line, "line")
 GK_ATOM(link, "link")
+GK_ATOM(linkset, "linkset")
 //GK_ATOM(list, "list")  # "list" is present below
 GK_ATOM(listbox, "listbox")
 GK_ATOM(listener, "listener")
 GK_ATOM(listheader, "listheader")
 GK_ATOM(listing, "listing")
 GK_ATOM(listitem, "listitem")
 GK_ATOM(load, "load")
 GK_ATOM(triggeringprincipal, "triggeringprincipal")