Bug 1545699 - Track editor stylesheets better. r=masayuki
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 19 Apr 2019 16:34:36 +0200
changeset 530112 9d3956ea78a8ddb36e0ee683e9f4ef7cc604e7c9
parent 530111 24ad41213ce44bef9fe3d7f8af56ad9ca38e941f
child 530113 c08ca0ff0c98cdec6f6a70bdd9e7c610e5e39f95
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1545699
milestone68.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 1545699 - Track editor stylesheets better. r=masayuki Replacing the whole set of user-agent stylesheets seems a bit overkill. Differential Revision: https://phabricator.services.mozilla.com/D28212
dom/base/Document.cpp
dom/base/Document.h
dom/html/nsHTMLDocument.cpp
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/nsIPresShell.h
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1208,16 +1208,18 @@ Document::Document(const char* aContentT
       mEncodingMenuDisabled(false),
       mIsSVGGlyphsDocument(false),
       mInDestructor(false),
       mIsGoingAway(false),
       mInXBLUpdate(false),
       mNeedsReleaseAfterStackRefCntRelease(false),
       mStyleSetFilled(false),
       mQuirkSheetAdded(false),
+      mContentEditableSheetAdded(false),
+      mDesignModeSheetAdded(false),
       mSSApplicableStateNotificationPending(false),
       mMayHaveTitleElement(false),
       mDOMLoadingSet(false),
       mDOMInteractiveSet(false),
       mDOMCompleteSet(false),
       mAutoFocusFired(false),
       mScrolledToRefAlready(false),
       mChangeScrollPosWhenScrollingToRef(false),
@@ -2396,16 +2398,62 @@ void Document::FillStyleSetUserAndUAShee
 
 void Document::FillStyleSet() {
   MOZ_ASSERT(!mStyleSetFilled);
   FillStyleSetUserAndUASheets();
   FillStyleSetDocumentSheets();
   mStyleSetFilled = true;
 }
 
+void Document::RemoveContentEditableStyleSheets() {
+  MOZ_ASSERT(IsHTMLOrXHTML());
+
+  auto* cache = nsLayoutStylesheetCache::Singleton();
+  bool changed = false;
+  if (mDesignModeSheetAdded) {
+    mStyleSet->RemoveStyleSheet(StyleOrigin::UserAgent, cache->DesignModeSheet());
+    mDesignModeSheetAdded = false;
+    changed = true;
+  }
+  if (mContentEditableSheetAdded) {
+    mStyleSet->RemoveStyleSheet(StyleOrigin::UserAgent, cache->ContentEditableSheet());
+    mContentEditableSheetAdded = false;
+    changed = true;
+  }
+  if (changed) {
+    MOZ_ASSERT(mStyleSetFilled);
+    ApplicableStylesChanged();
+  }
+}
+
+void Document::AddContentEditableStyleSheetsToStyleSet(bool aDesignMode) {
+  MOZ_ASSERT(IsHTMLOrXHTML());
+  MOZ_DIAGNOSTIC_ASSERT(mStyleSetFilled, "Caller should ensure we're being rendered");
+
+  auto* cache = nsLayoutStylesheetCache::Singleton();
+  bool changed = false;
+  if (!mContentEditableSheetAdded) {
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->ContentEditableSheet());
+    mContentEditableSheetAdded = true;
+    changed = true;
+  }
+  if (mDesignModeSheetAdded != aDesignMode) {
+    if (mDesignModeSheetAdded) {
+      mStyleSet->RemoveStyleSheet(StyleOrigin::UserAgent, cache->DesignModeSheet());
+    } else {
+      mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->DesignModeSheet());
+    }
+    mDesignModeSheetAdded = !mDesignModeSheetAdded;
+    changed = true;
+  }
+  if (changed) {
+    ApplicableStylesChanged();
+  }
+}
+
 void Document::FillStyleSetDocumentSheets() {
   MOZ_ASSERT(mStyleSet->SheetCount(StyleOrigin::Author) == 0,
              "Style set already has document sheets?");
 
   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
     if (sheet->IsApplicable()) {
       mStyleSet->AddDocStyleSheet(sheet);
     }
@@ -4003,16 +4051,18 @@ void Document::DeletePresShell() {
   UpdateFrameRequestCallbackSchedulingState(oldPresShell);
 
   ClearStaleServoData();
   AssertNoStaleServoDataIn(*this);
 
   mStyleSet->ShellDetachedFromDocument();
   mStyleSetFilled = false;
   mQuirkSheetAdded = false;
+  mContentEditableSheetAdded = false;
+  mDesignModeSheetAdded = false;
 }
 
 void Document::SetBFCacheEntry(nsIBFCacheEntry* aEntry) {
   MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!");
 
   if (mPresShell) {
     if (aEntry) {
       mPresShell->StopObservingRefreshDriver();
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3820,18 +3820,21 @@ class Document : public nsINode,
       const nsTArray<RefPtr<StyleSheet>>& aSheets, StyleOrigin);
   void ResetStylesheetsToURI(nsIURI* aURI);
   void FillStyleSet();
   void FillStyleSetUserAndUASheets();
   void FillStyleSetDocumentSheets();
   void CompatibilityModeChanged();
   bool NeedsQuirksSheet() const {
     // SVG documents never load quirk.css.
+    // FIXME(emilio): Can SVG documents be in quirks mode anyway?
     return mCompatMode == eCompatibility_NavQuirks && !IsSVGDocument();
   }
+  void AddContentEditableStyleSheetsToStyleSet(bool aDesignMode);
+  void RemoveContentEditableStyleSheets();
   void AddStyleSheetToStyleSets(StyleSheet* aSheet);
   void RemoveStyleSheetFromStyleSets(StyleSheet* aSheet);
   void NotifyStyleSheetAdded(StyleSheet* aSheet, bool aDocumentSheet);
   void NotifyStyleSheetRemoved(StyleSheet* aSheet, bool aDocumentSheet);
   void NotifyStyleSheetApplicableStateChanged();
   // Just like EnableStyleSheetsForSet, but doesn't check whether
   // aSheetSet is null and allows the caller to control whether to set
   // aSheetSet as the preferred set in the CSSLoader.
@@ -4215,16 +4218,22 @@ class Document : public nsINode,
   bool mNeedsReleaseAfterStackRefCntRelease : 1;
 
   // Whether we have filled our style set with all the stylesheets.
   bool mStyleSetFilled : 1;
 
   // Whether we have a quirks mode stylesheet in the style set.
   bool mQuirkSheetAdded : 1;
 
+  // Whether we have a contenteditable.css stylesheet in the style set.
+  bool mContentEditableSheetAdded : 1;
+
+  // Whether we have a designmode.css stylesheet in the style set.
+  bool mDesignModeSheetAdded : 1;
+
   // Keeps track of whether we have a pending
   // 'style-sheet-applicable-state-changed' notification.
   bool mSSApplicableStateNotificationPending : 1;
 
   // True if this document has ever had an HTML or SVG <title> element
   // bound to it
   bool mMayHaveTitleElement : 1;
 
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1707,35 +1707,18 @@ static void NotifyEditableStateChange(ns
       child->AsElement()->UpdateState(true);
     }
     NotifyEditableStateChange(child, aDocument);
   }
 }
 
 void nsHTMLDocument::TearingDownEditor() {
   if (IsEditingOn()) {
-    EditingState oldState = mEditingState;
     mEditingState = eTearingDown;
-
-    RefPtr<PresShell> presShell = GetPresShell();
-    if (!presShell) {
-      return;
-    }
-
-    nsTArray<RefPtr<StyleSheet>> agentSheets;
-    presShell->GetAgentStyleSheets(agentSheets);
-
-    auto cache = nsLayoutStylesheetCache::Singleton();
-
-    agentSheets.RemoveElement(cache->ContentEditableSheet());
-    if (oldState == eDesignMode)
-      agentSheets.RemoveElement(cache->DesignModeSheet());
-
-    presShell->SetAgentStyleSheets(agentSheets);
-    ApplicableStylesChanged();
+    RemoveContentEditableStyleSheets();
   }
 }
 
 nsresult nsHTMLDocument::TurnEditingOff() {
   NS_ASSERTION(mEditingState != eOff, "Editing is already off.");
 
   nsPIDOMWindowOuter* window = GetWindow();
   if (!window) return NS_ERROR_FAILURE;
@@ -1859,54 +1842,32 @@ nsresult nsHTMLDocument::EditingStateCha
 
   {
     EditingState oldState = mEditingState;
     nsAutoEditingState push(this, eSettingUp);
 
     RefPtr<PresShell> presShell = GetPresShell();
     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 
+    MOZ_ASSERT(mStyleSetFilled);
+
     // Before making this window editable, we need to modify UA style sheet
     // because new style may change whether focused element will be focusable
     // or not.
-    nsTArray<RefPtr<StyleSheet>> agentSheets;
-    rv = presShell->GetAgentStyleSheets(agentSheets);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    auto cache = nsLayoutStylesheetCache::Singleton();
-
-    StyleSheet* contentEditableSheet = cache->ContentEditableSheet();
-
-    if (!agentSheets.Contains(contentEditableSheet)) {
-      agentSheets.AppendElement(contentEditableSheet);
-    }
+    AddContentEditableStyleSheetsToStyleSet(designMode);
 
     // Should we update the editable state of all the nodes in the document? We
     // need to do this when the designMode value changes, as that overrides
     // specific states on the elements.
+    updateState = designMode || oldState == eDesignMode;
     if (designMode) {
       // designMode is being turned on (overrides contentEditable).
-      StyleSheet* designModeSheet = cache->DesignModeSheet();
-      if (!agentSheets.Contains(designModeSheet)) {
-        agentSheets.AppendElement(designModeSheet);
-      }
-
-      updateState = true;
       spellRecheckAll = oldState == eContentEditable;
-    } else if (oldState == eDesignMode) {
-      // designMode is being turned off (contentEditable is still on).
-      agentSheets.RemoveElement(cache->DesignModeSheet());
-      updateState = true;
     }
 
-    rv = presShell->SetAgentStyleSheets(agentSheets);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    ApplicableStylesChanged();
-
     // Adjust focused element with new style but blur event shouldn't be fired
     // until mEditingState is modified with newState.
     nsAutoScriptBlocker scriptBlocker;
     if (designMode) {
       nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
       nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
           window, nsFocusManager::eOnlyCurrentWindow,
           getter_AddRefs(focusedWindow));
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -8820,38 +8820,16 @@ void PresShell::RespectDisplayportSuppre
     }
   }
 }
 
 bool PresShell::IsDisplayportSuppressed() {
   return sDisplayPortSuppressionRespected && mActiveSuppressDisplayport > 0;
 }
 
-nsresult PresShell::GetAgentStyleSheets(nsTArray<RefPtr<StyleSheet>>& aSheets) {
-  aSheets.Clear();
-  int32_t sheetCount = StyleSet()->SheetCount(StyleOrigin::UserAgent);
-
-  if (!aSheets.SetCapacity(sheetCount, fallible)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  for (int32_t i = 0; i < sheetCount; ++i) {
-    StyleSheet* sheet = StyleSet()->SheetAt(StyleOrigin::UserAgent, i);
-    aSheets.AppendElement(sheet);
-  }
-
-  return NS_OK;
-}
-
-nsresult PresShell::SetAgentStyleSheets(
-    const nsTArray<RefPtr<StyleSheet>>& aSheets) {
-  StyleSet()->ReplaceSheets(StyleOrigin::UserAgent, aSheets);
-  return NS_OK;
-}
-
 nsresult PresShell::AddOverrideStyleSheet(StyleSheet* aSheet) {
   StyleSet()->AppendStyleSheet(aSheet->GetOrigin(), aSheet);
   return NS_OK;
 }
 
 nsresult PresShell::RemoveOverrideStyleSheet(StyleSheet* aSheet) {
   StyleSet()->RemoveStyleSheet(aSheet->GetOrigin(), aSheet);
   return NS_OK;
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -110,22 +110,18 @@ class PresShell final : public nsIPresSh
   RectVisibility GetRectVisibility(nsIFrame* aFrame, const nsRect& aRect,
                                    nscoord aMinTwips) const override;
 
   nsresult CaptureHistoryState(
       nsILayoutHistoryState** aLayoutHistoryState) override;
 
   void UnsuppressPainting() override;
 
-  nsresult GetAgentStyleSheets(nsTArray<RefPtr<StyleSheet>>& aSheets) override;
-  nsresult SetAgentStyleSheets(
-      const nsTArray<RefPtr<StyleSheet>>& aSheets) override;
-
-  nsresult AddOverrideStyleSheet(StyleSheet* aSheet) override;
-  nsresult RemoveOverrideStyleSheet(StyleSheet* aSheet) override;
+  nsresult AddOverrideStyleSheet(StyleSheet*) override;
+  nsresult RemoveOverrideStyleSheet(StyleSheet*) override;
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult HandleEventWithTarget(
       WidgetEvent* aEvent, nsIFrame* aFrame, nsIContent* aContent,
       nsEventStatus* aEventStatus, bool aIsHandlingNativeEvent = false,
       nsIContent** aTargetContent = nullptr,
       nsIContent* aOverrideClickTarget = nullptr) override {
     MOZ_ASSERT(aEvent);
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -867,28 +867,16 @@ class nsIPresShell : public nsStubDocume
   virtual void ResumePainting() = 0;
 
   /**
    * Unsuppress painting.
    */
   virtual void UnsuppressPainting() = 0;
 
   /**
-   * Get the set of agent style sheets for this presentation
-   */
-  virtual nsresult GetAgentStyleSheets(
-      nsTArray<RefPtr<mozilla::StyleSheet>>& aSheets) = 0;
-
-  /**
-   * Replace the set of agent style sheets
-   */
-  virtual nsresult SetAgentStyleSheets(
-      const nsTArray<RefPtr<mozilla::StyleSheet>>& aSheets) = 0;
-
-  /**
    * Add an override style sheet for this presentation
    */
   virtual nsresult AddOverrideStyleSheet(mozilla::StyleSheet* aSheet) = 0;
 
   /**
    * Remove an override style sheet
    */
   virtual nsresult RemoveOverrideStyleSheet(mozilla::StyleSheet* aSheet) = 0;
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -579,38 +579,16 @@ void ServoStyleSet::RemoveStyleSheet(Sty
   Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), aSheet);
   SetStylistStyleSheetsDirty();
 
   if (mStyleRuleMap) {
     mStyleRuleMap->SheetRemoved(*aSheet);
   }
 }
 
-void ServoStyleSet::ReplaceSheets(
-    StyleOrigin aOrigin, const nsTArray<RefPtr<StyleSheet>>& aNewSheets) {
-  // Gecko uses a two-dimensional array keyed by sheet type, whereas Servo
-  // stores a flattened list. This makes ReplaceSheets a pretty clunky thing
-  // to express. If the need ever arises, we can easily make this more efficent,
-  // probably by aligning the representations better between engines.
-
-  SetStylistStyleSheetsDirty();
-
-  mStyleRuleMap = nullptr;
-
-  // Remove all the existing sheets first.
-  for (size_t count = SheetCount(aOrigin); count--;) {
-    RemoveStyleSheet(aOrigin, SheetAt(aOrigin, count));
-  }
-
-  // Add in all the new sheets.
-  for (auto& sheet : aNewSheets) {
-    AppendStyleSheet(aOrigin, sheet);
-  }
-}
-
 void ServoStyleSet::InsertStyleSheetBefore(Origin aOrigin,
                                            StyleSheet* aNewSheet,
                                            StyleSheet* aReferenceSheet) {
   MOZ_ASSERT(aNewSheet);
   MOZ_ASSERT(aReferenceSheet);
   MOZ_ASSERT(aNewSheet->IsApplicable());
   MOZ_ASSERT(aNewSheet != aReferenceSheet, "Can't place sheet before itself.");
   MOZ_ASSERT(aNewSheet->RawContents(),
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -201,17 +201,16 @@ class ServoStyleSet {
   already_AddRefed<ComputedStyle> ResolveXULTreePseudoStyle(
       dom::Element* aParentElement, nsCSSAnonBoxPseudoStaticAtom* aPseudoTag,
       ComputedStyle* aParentStyle, const AtomArray& aInputWord);
 #endif
 
   // manage the set of style sheets in the style set
   void AppendStyleSheet(Origin, StyleSheet*);
   void RemoveStyleSheet(Origin, StyleSheet*);
-  void ReplaceSheets(Origin, const nsTArray<RefPtr<StyleSheet>>& aNewSheets);
   void InsertStyleSheetBefore(Origin, StyleSheet*, StyleSheet* aReferenceSheet);
 
   size_t SheetCount(Origin) const;
   StyleSheet* SheetAt(Origin, size_t aIndex) const;
 
   void AppendAllNonDocumentAuthorSheets(nsTArray<StyleSheet*>& aArray) const;
 
   void RemoveDocStyleSheet(StyleSheet* aSheet) {