Title: [137370] trunk/Source/WebCore
Revision
137370
Author
[email protected]
Date
2012-12-11 15:33:49 -0800 (Tue, 11 Dec 2012)

Log Message

Mail hangs when resizing the font size of a large RTL text
https://bugs.webkit.org/show_bug.cgi?id=104643

Reviewed by Enrica Casucci.

The bug was caused by ApplyStyleCommand::applyInlineStyleToNodeRange obtaining computed styles while
removing and adding nodes. Fixed the slowness by breaking it into three phases:
1. Split the range into contiguous inline runs, and determine whether styles need to be removed or applied.
2. Remove any conflicting styles, and insert dummy elements at positions where inline styles ought to be
computed as needed.
3. Remove the dummy elements and apply ilnine styles as needed.

No new tests are added since there is no behavior change. This is just a performance improvement. 

* editing/ApplyStyleCommand.cpp:
(WebCore::InlineRunToApplyStyle::InlineRunToApplyStyle):
(WebCore::InlineRunToApplyStyle::startAndEndAreStillInDocument):
(WebCore::ApplyStyleCommand::applyInlineStyleToNodeRange):
(WebCore::ApplyStyleCommand::shouldApplyInlineStyleToRun):
(WebCore::ApplyStyleCommand::removeConflictingInlineStyleFromRun):
(WebCore::ApplyStyleCommand::addInlineStyleIfNeeded):
(WebCore::ApplyStyleCommand::positionToComputeInlineStyleChange):
(WebCore::ApplyStyleCommand::applyInlineStyleChange):
* editing/ApplyStyleCommand.h:
* editing/EditingStyle.h:
(WebCore::StyleChange::StyleChange):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (137369 => 137370)


--- trunk/Source/WebCore/ChangeLog	2012-12-11 23:32:33 UTC (rev 137369)
+++ trunk/Source/WebCore/ChangeLog	2012-12-11 23:33:49 UTC (rev 137370)
@@ -1,3 +1,32 @@
+2012-12-11  Ryosuke Niwa  <[email protected]>
+
+        Mail hangs when resizing the font size of a large RTL text
+        https://bugs.webkit.org/show_bug.cgi?id=104643
+
+        Reviewed by Enrica Casucci.
+
+        The bug was caused by ApplyStyleCommand::applyInlineStyleToNodeRange obtaining computed styles while
+        removing and adding nodes. Fixed the slowness by breaking it into three phases:
+        1. Split the range into contiguous inline runs, and determine whether styles need to be removed or applied.
+        2. Remove any conflicting styles, and insert dummy elements at positions where inline styles ought to be
+        computed as needed.
+        3. Remove the dummy elements and apply ilnine styles as needed.
+
+        No new tests are added since there is no behavior change. This is just a performance improvement. 
+
+        * editing/ApplyStyleCommand.cpp:
+        (WebCore::InlineRunToApplyStyle::InlineRunToApplyStyle):
+        (WebCore::InlineRunToApplyStyle::startAndEndAreStillInDocument):
+        (WebCore::ApplyStyleCommand::applyInlineStyleToNodeRange):
+        (WebCore::ApplyStyleCommand::shouldApplyInlineStyleToRun):
+        (WebCore::ApplyStyleCommand::removeConflictingInlineStyleFromRun):
+        (WebCore::ApplyStyleCommand::addInlineStyleIfNeeded):
+        (WebCore::ApplyStyleCommand::positionToComputeInlineStyleChange):
+        (WebCore::ApplyStyleCommand::applyInlineStyleChange):
+        * editing/ApplyStyleCommand.h:
+        * editing/EditingStyle.h:
+        (WebCore::StyleChange::StyleChange):
+
 2012-12-11  Xianzhu Wang  <[email protected]>
 
         Incorrect position of layers for fixed position elements when page is scaled smaller than viewport

Modified: trunk/Source/WebCore/editing/ApplyStyleCommand.cpp (137369 => 137370)


--- trunk/Source/WebCore/editing/ApplyStyleCommand.cpp	2012-12-11 23:32:33 UTC (rev 137369)
+++ trunk/Source/WebCore/editing/ApplyStyleCommand.cpp	2012-12-11 23:33:49 UTC (rev 137370)
@@ -716,11 +716,36 @@
     return false;
 }
 
+struct InlineRunToApplyStyle {
+    InlineRunToApplyStyle(Node* start, Node* end, Node* pastEndNode)
+        : start(start)
+        , end(end)
+        , pastEndNode(pastEndNode)
+    {
+        ASSERT(start->parentNode() == end->parentNode());
+    }
+
+    bool startAndEndAreStillInDocument()
+    {
+        return start && end && start->inDocument() && end->inDocument();
+    }
+
+    RefPtr<Node> start;
+    RefPtr<Node> end;
+    RefPtr<Node> pastEndNode;
+    Position positionForStyleComputation;
+    RefPtr<Node> dummyElement;
+    StyleChange change;
+};
+
 void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, PassRefPtr<Node> startNode, PassRefPtr<Node> pastEndNode)
 {
     if (m_removeOnly)
         return;
 
+    document()->updateLayoutIgnorePendingStylesheets();
+
+    Vector<InlineRunToApplyStyle> runs;
     RefPtr<Node> node = startNode;
     for (RefPtr<Node> next; node && node != pastEndNode; node = next) {
         next = NodeTraversal::next(node.get());
@@ -755,8 +780,8 @@
             }
         }
 
-        RefPtr<Node> runStart = node;
-        RefPtr<Node> runEnd = node;
+        Node* runStart = node.get();
+        Node* runEnd = node.get();
         Node* sibling = node->nextSibling();
         while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode.get())
                && (!isBlock(sibling) || sibling->hasTagName(brTag))
@@ -764,12 +789,32 @@
             runEnd = sibling;
             sibling = runEnd->nextSibling();
         }
-        next = NodeTraversal::nextSkippingChildren(runEnd.get());
+        next = NodeTraversal::nextSkippingChildren(runEnd);
 
-        if (!removeStyleFromRunBeforeApplyingStyle(style, runStart, runEnd))
+        Node* pastEndNode = NodeTraversal::nextSkippingChildren(runEnd);
+        if (!shouldApplyInlineStyleToRun(style, runStart, pastEndNode))
             continue;
-        addInlineStyleIfNeeded(style, runStart.get(), runEnd.get(), AddStyledElement);
+
+        runs.append(InlineRunToApplyStyle(runStart, runEnd, pastEndNode));
     }
+
+    for (size_t i = 0; i < runs.size(); i++) {
+        removeConflictingInlineStyleFromRun(style, runs[i].start, runs[i].end, runs[i].pastEndNode);
+        runs[i].positionForStyleComputation = positionToComputeInlineStyleChange(runs[i].start, runs[i].dummyElement);
+    }
+
+    document()->updateLayoutIgnorePendingStylesheets();
+
+    for (size_t i = 0; i < runs.size(); i++)
+        runs[i].change = StyleChange(style, runs[i].positionForStyleComputation);
+
+    for (size_t i = 0; i < runs.size(); i++) {
+        InlineRunToApplyStyle& run = runs[i];
+        if (run.dummyElement)
+            removeNode(run.dummyElement);
+        if (run.startAndEndAreStillInDocument())
+            applyInlineStyleChange(run.start.release(), run.end.release(), run.change, AddStyledElement);
+    }
 }
 
 bool ApplyStyleCommand::isStyledInlineElementToRemove(Element* element) const
@@ -778,24 +823,25 @@
         || (m_isInlineElementToRemoveFunction && m_isInlineElementToRemoveFunction(element));
 }
 
-bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(EditingStyle* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd)
+bool ApplyStyleCommand::shouldApplyInlineStyleToRun(EditingStyle* style, Node* runStart, Node* pastEndNode)
 {
-    ASSERT(runStart && runEnd && runStart->parentNode() == runEnd->parentNode());
-    RefPtr<Node> pastEndNode = NodeTraversal::nextSkippingChildren(runEnd.get());
-    bool needToApplyStyle = false;
-    for (Node* node = runStart.get(); node && node != pastEndNode.get(); node = NodeTraversal::next(node)) {
+    ASSERT(style && runStart);
+
+    for (Node* node = runStart; node && node != pastEndNode; node = NodeTraversal::next(node)) {
         if (node->childNodeCount())
             continue;
         // We don't consider m_isInlineElementToRemoveFunction here because we never apply style when m_isInlineElementToRemoveFunction is specified
-        if (!style->styleIsPresentInComputedStyleOfNode(node)
-            || (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(node), m_styledInlineElement->tagQName()))) {
-            needToApplyStyle = true;
-            break;
-        }
+        if (!style->styleIsPresentInComputedStyleOfNode(node))
+            return true;
+        if (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(node), m_styledInlineElement->tagQName()))
+            return true;
     }
-    if (!needToApplyStyle)
-        return false;
+    return false;
+}
 
+void ApplyStyleCommand::removeConflictingInlineStyleFromRun(EditingStyle* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd, PassRefPtr<Node> pastEndNode)
+{
+    ASSERT(runStart && runEnd);
     RefPtr<Node> next = runStart;
     for (RefPtr<Node> node = next; node && node->inDocument() && node != pastEndNode; node = next) {
         if (editingIgnoresContent(node.get())) {
@@ -818,8 +864,6 @@
                 runEnd = nextSibling ? nextSibling->previousSibling() : parent->lastChild();
         }
     }
-
-    return true;
 }
 
 bool ApplyStyleCommand::removeInlineStyleFromElement(EditingStyle* style, PassRefPtr<HTMLElement> element, InlineStyleRemovalMode mode, EditingStyle* extractedStyle)
@@ -1338,23 +1382,36 @@
 {
     if (!passedStart || !passedEnd || !passedStart->inDocument() || !passedEnd->inDocument())
         return;
-    RefPtr<Node> startNode = passedStart;
-    RefPtr<Node> endNode = passedEnd;
 
+    RefPtr<Node> start = passedStart;
+    RefPtr<Node> dummyElement;
+    StyleChange styleChange(style, positionToComputeInlineStyleChange(start, dummyElement));
+
+    if (dummyElement)
+        removeNode(dummyElement);
+
+    applyInlineStyleChange(start, passedEnd, styleChange, addStyledElement);
+}
+
+Position ApplyStyleCommand::positionToComputeInlineStyleChange(PassRefPtr<Node> startNode, RefPtr<Node>& dummyElement)
+{
     // It's okay to obtain the style at the startNode because we've removed all relevant styles from the current run.
-    RefPtr<HTMLElement> dummyElement;
     Position positionForStyleComparison;
     if (!startNode->isElementNode()) {
         dummyElement = createStyleSpanElement(document());
         insertNodeAt(dummyElement, positionBeforeNode(startNode.get()));
-        positionForStyleComparison = positionBeforeNode(dummyElement.get());
-    } else
-        positionForStyleComparison = firstPositionInOrBeforeNode(startNode.get());
+        return positionBeforeNode(dummyElement.get());
+    }
 
-    StyleChange styleChange(style, positionForStyleComparison);
+    return firstPositionInOrBeforeNode(startNode.get());
+}
 
-    if (dummyElement)
-        removeNode(dummyElement);
+void ApplyStyleCommand::applyInlineStyleChange(PassRefPtr<Node> passedStart, PassRefPtr<Node> passedEnd, StyleChange& styleChange, EAddStyledElement addStyledElement)
+{
+    RefPtr<Node> startNode = passedStart;
+    RefPtr<Node> endNode = passedEnd;
+    ASSERT(startNode->inDocument());
+    ASSERT(endNode->inDocument());
 
     // Find appropriate font and span elements top-down.
     HTMLElement* fontContainer = 0;

Modified: trunk/Source/WebCore/editing/ApplyStyleCommand.h (137369 => 137370)


--- trunk/Source/WebCore/editing/ApplyStyleCommand.h	2012-12-11 23:32:33 UTC (rev 137369)
+++ trunk/Source/WebCore/editing/ApplyStyleCommand.h	2012-12-11 23:33:49 UTC (rev 137370)
@@ -76,7 +76,8 @@
 
     // style-removal helpers
     bool isStyledInlineElementToRemove(Element*) const;
-    bool removeStyleFromRunBeforeApplyingStyle(EditingStyle*, RefPtr<Node>& runStart, RefPtr<Node>& runEnd);
+    bool shouldApplyInlineStyleToRun(EditingStyle*, Node* runStart, Node* pastEndNode);
+    void removeConflictingInlineStyleFromRun(EditingStyle*, RefPtr<Node>& runStart, RefPtr<Node>& runEnd, PassRefPtr<Node> pastEndNode);
     bool removeInlineStyleFromElement(EditingStyle*, PassRefPtr<HTMLElement>, InlineStyleRemovalMode = RemoveIfNeeded, EditingStyle* extractedStyle = 0);
     inline bool shouldRemoveInlineStyleFromElement(EditingStyle* style, HTMLElement* element) {return removeInlineStyleFromElement(style, element, RemoveNone);}
     void replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*&);
@@ -97,6 +98,8 @@
     void applyInlineStyleToNodeRange(EditingStyle*, PassRefPtr<Node> startNode, PassRefPtr<Node> pastEndNode);
     void addBlockStyle(const StyleChange&, HTMLElement*);
     void addInlineStyleIfNeeded(EditingStyle*, PassRefPtr<Node> start, PassRefPtr<Node> end, EAddStyledElement = AddStyledElement);
+    Position positionToComputeInlineStyleChange(PassRefPtr<Node>, RefPtr<Node>& dummyElement);
+    void applyInlineStyleChange(PassRefPtr<Node> startNode, PassRefPtr<Node> endNode, StyleChange&, EAddStyledElement);
     void splitTextAtStart(const Position& start, const Position& end);
     void splitTextAtEnd(const Position& start, const Position& end);
     void splitTextElementAtStart(const Position& start, const Position& end);

Modified: trunk/Source/WebCore/editing/EditingStyle.h (137369 => 137370)


--- trunk/Source/WebCore/editing/EditingStyle.h	2012-12-11 23:32:33 UTC (rev 137369)
+++ trunk/Source/WebCore/editing/EditingStyle.h	2012-12-11 23:33:49 UTC (rev 137370)
@@ -173,6 +173,15 @@
 
 class StyleChange {
 public:
+    StyleChange()
+        : m_applyBold(false)
+        , m_applyItalic(false)
+        , m_applyUnderline(false)
+        , m_applyLineThrough(false)
+        , m_applySubscript(false)
+        , m_applySuperscript(false)
+    { }
+
     StyleChange(EditingStyle*, const Position&);
 
     String cssStyle() const { return m_cssStyle; }
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to