Bug 1329093 - Part 4: stylo: Delay SVG mapped attr resolution till later; draft
authorManish Goregaokar <manishearth@gmail.com>
Wed, 22 Feb 2017 17:19:04 -0800
changeset 493433 c17d0b31e1869dea857160cad0ba485ba02a3106
parent 492144 63bc02cec0abc1e5fadd09e4defcdbe18cc4760e
child 493434 bbd3d0adbbe7240439f3fb2d647e86245a8afef9
push id47759
push userbmo:manishearth@gmail.com
push dateFri, 03 Mar 2017 22:54:21 +0000
bugs1329093
milestone54.0a1
Bug 1329093 - Part 4: stylo: Delay SVG mapped attr resolution till later; MozReview-Commit-ID: 2GvHPg1egjS
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/svg/crashtests/1329093-1.html
dom/svg/crashtests/crashtests.list
dom/svg/nsSVGElement.cpp
layout/style/ServoStyleSet.cpp
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6009,16 +6009,40 @@ nsDocument::IsWebComponentsEnabled(nsPID
 
     return perm == nsIPermissionManager::ALLOW_ACTION;
   }
 
   return false;
 }
 
 void
+nsDocument::ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
+{
+  mLazySVGPresElements.PutEntry(aSVG);
+}
+
+void
+nsDocument::UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
+{
+  if (auto entry = mLazySVGPresElements.GetEntry(aSVG)) {
+    mLazySVGPresElements.RemoveEntry(entry);
+  }
+}
+
+void
+nsDocument::ResolveScheduledSVGPresAttrs()
+{
+  for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) {
+    nsSVGElement* svg = iter.Get()->GetKey();
+    svg->UpdateContentDeclarationBlock(StyleBackendType::Servo);
+  }
+  mLazySVGPresElements.Clear();
+}
+
+void
 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
                             const ElementRegistrationOptions& aOptions,
                             JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& rv)
 {
   RefPtr<CustomElementRegistry> registry(GetCustomElementRegistry());
   if (!registry) {
     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -793,16 +793,19 @@ public:
   virtual void RemoveIntersectionObserver(
     mozilla::dom::DOMIntersectionObserver* aObserver) override;
   virtual void UpdateIntersectionObservations() override;
   virtual void ScheduleIntersectionObserverNotification() override;
   virtual void NotifyIntersectionObservers() override;
 
   virtual void NotifyLayerManagerRecreated() override;
 
+  virtual void ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
+  virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
+  virtual void ResolveScheduledSVGPresAttrs() override;
 
 private:
   void AddOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet);
   nsRadioGroupStruct* GetRadioGroupInternal(const nsAString& aName) const;
   void SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages);
 
 public:
   // nsIDOMNode
@@ -1627,16 +1630,21 @@ private:
   nsCOMPtr<nsIDocument> mMasterDocument;
   RefPtr<mozilla::dom::ImportManager> mImportManager;
   nsTArray<nsCOMPtr<nsINode> > mSubImportLinks;
 
   // Set to true when the document is possibly controlled by the ServiceWorker.
   // Used to prevent multiple requests to ServiceWorkerManager.
   bool mMaybeServiceWorkerControlled;
 
+  // We lazily calculate declaration blocks for SVG elements
+  // with mapped attributes in Servo mode. This list contains all elements which
+  // need lazy resolution
+  nsTHashtable<nsPtrHashKey<nsSVGElement> > mLazySVGPresElements;
+
 #ifdef DEBUG
 public:
   bool mWillReparent;
 #endif
 };
 
 class nsDocumentOnStack
 {
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -88,16 +88,17 @@ class nsIStreamListener;
 class nsIStructuredCloneContainer;
 class nsIURI;
 class nsIVariant;
 class nsViewManager;
 class nsPresContext;
 class nsRange;
 class nsScriptLoader;
 class nsSMILAnimationController;
+class nsSVGElement;
 class nsTextNode;
 class nsWindowSizes;
 class nsDOMCaretPosition;
 class nsViewportInfo;
 class nsIGlobalObject;
 struct nsCSSSelectorList;
 
 namespace mozilla {
@@ -1004,16 +1005,30 @@ public:
       return parentTimeStamp;
     }
 
     return mPageUnloadingEventTimeStamp;
   }
 
   virtual void NotifyLayerManagerRecreated() = 0;
 
+  /**
+   * Add an SVG element to the list of elements that need
+   * their mapped attributes resolved to a Servo declaration block.
+   *
+   * These are weak pointers, please manually unschedule them when an element
+   * is removed.
+   */
+  virtual void ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) = 0;
+  // Unschedule an element scheduled by ScheduleFrameRequestCallback (e.g. for when it is destroyed)
+  virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) = 0;
+
+  // Resolve all SVG pres attrs scheduled in ScheduleSVGForPresAttrEvaluation
+  virtual void ResolveScheduledSVGPresAttrs() = 0;
+
 protected:
   virtual Element *GetRootElementInternal() const = 0;
 
   void SetPageUnloadingEventTimeStamp()
   {
     MOZ_ASSERT(!mPageUnloadingEventTimeStamp);
     mPageUnloadingEventTimeStamp = mozilla::TimeStamp::NowLoRes();
   }
new file mode 100644
--- /dev/null
+++ b/dom/svg/crashtests/1329093-1.html
@@ -0,0 +1,12 @@
+<html class="reftest-wait">
+
+<head>
+<script>
+setTimeout('document.documentElement.className = ""', 500);
+</script>
+</head>
+<body>
+Loading the below iframe should not crash Firefox in Stylo mode.
+<iframe style="display: none" src='data:text/html,<svg height="100" width="100"><circle cx="50" cy="50" r="40" stroke="yellow" stroke-width="2" fill="green"/> </svg>'></iframe>
+</body>
+</html>
\ No newline at end of file
--- a/dom/svg/crashtests/crashtests.list
+++ b/dom/svg/crashtests/crashtests.list
@@ -83,8 +83,9 @@ load 1282985-1.svg
 load zero-size-image.svg
 load 1322286.html
 load 1329849-1.svg
 load 1329849-2.svg
 load 1329849-3.svg
 load 1329849-4.svg
 load 1329849-5.svg
 load 1329849-6.svg
+load 1329093-1.html
--- a/dom/svg/nsSVGElement.cpp
+++ b/dom/svg/nsSVGElement.cpp
@@ -92,16 +92,17 @@ nsSVGEnumMapping nsSVGElement::sSVGUnitT
 
 nsSVGElement::nsSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsSVGElementBase(aNodeInfo)
 {
 }
 
 nsSVGElement::~nsSVGElement()
 {
+  OwnerDoc()->UnscheduleSVGForPresAttrEvaluation(this);
 }
 
 JSObject*
 nsSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return SVGElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -298,19 +299,18 @@ nsSVGElement::AfterSetAttr(int32_t aName
              "Unexpected use of nsMappedAttributes within SVG");
 
   // If this is an svg presentation attribute we need to map it into
   // the content declaration block.
   // XXX For some reason incremental mapping doesn't work, so for now
   // just delete the style rule and lazily reconstruct it as needed).
   if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
     mContentDeclarationBlock = nullptr;
-    // TODO we should be doing this lazily by caching these on the styleset
     if (OwnerDoc()->GetStyleBackendType() == StyleBackendType::Servo) {
-      UpdateContentDeclarationBlock(StyleBackendType::Servo);
+      OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
     }
   }
 
   if (IsEventAttributeName(aName) && aValue) {
     MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
                "Expected string value for script body");
     nsresult rv = SetEventHandler(GetEventNameForAttr(aName),
                                   aValue->GetStringValue());
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -181,16 +181,18 @@ ServoStyleSet::GetContext(already_AddRef
 }
 
 void
 ServoStyleSet::ResolveMappedAttrDeclarationBlocks()
 {
   if (nsHTMLStyleSheet* sheet = mPresContext->Document()->GetAttributeStyleSheet()) {
     sheet->CalculateMappedServoDeclarations();
   }
+
+  mPresContext->Document()->ResolveScheduledSVGPresAttrs();
 }
 
 bool
 ServoStyleSet::PrepareAndTraverseSubtree(RawGeckoElementBorrowed aRoot,
                                          mozilla::TraversalRootBehavior aRootBehavior) {
   ResolveMappedAttrDeclarationBlocks();
 
   // Get the Document's root element to ensure that the cache is valid before