Title: [218910] trunk/Source/WebCore
Revision
218910
Author
rn...@webkit.org
Date
2017-06-28 19:54:00 -0700 (Wed, 28 Jun 2017)

Log Message

Safari's Speedometer score massively regresses when accessibility is enabled
https://bugs.webkit.org/show_bug.cgi?id=173912

Reviewed by Chris Fleizach.

The bug was caused by HTMLTextFormControlElement::setInnerTextValue triggering a synchronous layout
via constructing VisiblePosition when the accessibility tree is present.

Added AXObjectCache::postTextReplacementNotificationForTextControl which avoids the construction of
VisiblePosition and other means of triggering a synchronous layout. This patch also fixes a subtle bug
that HTMLTextFormControlElement was creating TextMarkerData with axID set to that of the text control
element instead of the root editable element inside its shadow tree even though the typing command uses
axID of the root editable element. While I couldn't find any user-visible behavioral change from this
code change, new code is more self-consistent.

Also added LayoutDisallowedScope which asserts that no synchronous layout happens in setInnerTextValue
so that we don't introduce a new performance regression like this in the future.

No new tests. Existing tests in accessibility directory covers this.

* CMakeLists.txt: Added LayoutDisallowedScope.cpp.
* WebCore.xcodeproj/project.pbxproj: Ditto.

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::postTextReplacementNotificationForTextControl): Added.
(WebCore::AXObjectCache::textMarkerDataForVisiblePosition): Modernized. Returns optional<TextMarkerData>
instead of taking TextMarkerData as an out-argument, and returning with axID of 0.
(WebCore::AXObjectCache::textMarkerDataForFirstPositionInTextControl): Added. This specialized version
constructs TextMarkerData for the first position inside the editable region in a text control without
triggering a synchronous layout.

* accessibility/AXObjectCache.h:
(WebCore::TextMarkerData): Initialize each member automatically.
(WebCore::AXObjectCache::postTextReplacementNotificationForTextControl):

* accessibility/ios/AXObjectCacheIOS.mm:
(WebCore::AXObjectCache::postTextReplacementPlatformNotificationForTextControl): Added.

* accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
(+[WebAccessibilityTextMarker textMarkerWithVisiblePosition:cache:]):

* accessibility/mac/AXObjectCacheMac.mm:
(WebCore::addTextMarkerFor): Extracted from textReplacementChangeDictionary. Added a new variant which
takes a text form control instead.
(WebCore::textReplacementChangeDictionary): Templatized this function to either take VisiblePosition
and call textMarkerForVisiblePosition or take HTMLTextFormControlElement and call
textMarkerForFirstPositionInTextControl.
(WebCore::postUserInfoForChanges): Extracted from postTextReplacementPlatformNotification.
(WebCore::AXObjectCache::postTextReplacementPlatformNotification): 
(WebCore::AXObjectCache::postTextReplacementPlatformNotificationForTextControl): Added.

* accessibility/mac/WebAccessibilityObjectWrapperBase.h:
* accessibility/mac/WebAccessibilityObjectWrapperMac.h:

* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(textMarkerForVisiblePosition):
(-[WebAccessibilityObjectWrapper textMarkerForFirstPositionInTextControl:]): Added.

* dom/Document.cpp:
(WebCore::Document::updateLayout): Assert that LayoutDisallowedScope is not in the stack frame.

* html/HTMLTextFormControlElement.cpp:
(WebCore::HTMLTextFormControlElement::setInnerTextValue): Call postTextReplacementNotificationForTextControl
to avoid triggering a synchronous layout. Also create LayoutDisallowedScope to avoid a similar performance
regression from being introduced in the future in this function. Finally, made innerText a RefPtr for extra
safety since we're using it after updating the DOM tree.

* rendering/LayoutDisallowedScope.cpp: Added.
* rendering/LayoutDisallowedScope.h: Added.
(WebCore::LayoutDisallowedScope::LayoutDisallowedScope):
(WebCore::LayoutDisallowedScope::~LayoutDisallowedScope):
(WebCore::LayoutDisallowedScope::isLayoutAllowed):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/CMakeLists.txt (218909 => 218910)


--- trunk/Source/WebCore/CMakeLists.txt	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/CMakeLists.txt	2017-06-29 02:54:00 UTC (rev 218910)
@@ -2516,6 +2516,7 @@
     rendering/InlineFlowBox.cpp
     rendering/InlineIterator.cpp
     rendering/InlineTextBox.cpp
+    rendering/LayoutDisallowedScope.cpp
     rendering/LayoutRepainter.cpp
     rendering/LayoutState.cpp
     rendering/OrderIterator.cpp

Modified: trunk/Source/WebCore/ChangeLog (218909 => 218910)


--- trunk/Source/WebCore/ChangeLog	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/ChangeLog	2017-06-29 02:54:00 UTC (rev 218910)
@@ -1,3 +1,78 @@
+2017-06-28  Ryosuke Niwa  <rn...@webkit.org>
+
+        Safari's Speedometer score massively regresses when accessibility is enabled
+        https://bugs.webkit.org/show_bug.cgi?id=173912
+
+        Reviewed by Chris Fleizach.
+
+        The bug was caused by HTMLTextFormControlElement::setInnerTextValue triggering a synchronous layout
+        via constructing VisiblePosition when the accessibility tree is present.
+
+        Added AXObjectCache::postTextReplacementNotificationForTextControl which avoids the construction of
+        VisiblePosition and other means of triggering a synchronous layout. This patch also fixes a subtle bug
+        that HTMLTextFormControlElement was creating TextMarkerData with axID set to that of the text control
+        element instead of the root editable element inside its shadow tree even though the typing command uses
+        axID of the root editable element. While I couldn't find any user-visible behavioral change from this
+        code change, new code is more self-consistent.
+
+        Also added LayoutDisallowedScope which asserts that no synchronous layout happens in setInnerTextValue
+        so that we don't introduce a new performance regression like this in the future.
+
+        No new tests. Existing tests in accessibility directory covers this.
+
+        * CMakeLists.txt: Added LayoutDisallowedScope.cpp.
+        * WebCore.xcodeproj/project.pbxproj: Ditto.
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::postTextReplacementNotificationForTextControl): Added.
+        (WebCore::AXObjectCache::textMarkerDataForVisiblePosition): Modernized. Returns optional<TextMarkerData>
+        instead of taking TextMarkerData as an out-argument, and returning with axID of 0.
+        (WebCore::AXObjectCache::textMarkerDataForFirstPositionInTextControl): Added. This specialized version
+        constructs TextMarkerData for the first position inside the editable region in a text control without
+        triggering a synchronous layout.
+
+        * accessibility/AXObjectCache.h:
+        (WebCore::TextMarkerData): Initialize each member automatically.
+        (WebCore::AXObjectCache::postTextReplacementNotificationForTextControl):
+
+        * accessibility/ios/AXObjectCacheIOS.mm:
+        (WebCore::AXObjectCache::postTextReplacementPlatformNotificationForTextControl): Added.
+
+        * accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
+        (+[WebAccessibilityTextMarker textMarkerWithVisiblePosition:cache:]):
+
+        * accessibility/mac/AXObjectCacheMac.mm:
+        (WebCore::addTextMarkerFor): Extracted from textReplacementChangeDictionary. Added a new variant which
+        takes a text form control instead.
+        (WebCore::textReplacementChangeDictionary): Templatized this function to either take VisiblePosition
+        and call textMarkerForVisiblePosition or take HTMLTextFormControlElement and call
+        textMarkerForFirstPositionInTextControl.
+        (WebCore::postUserInfoForChanges): Extracted from postTextReplacementPlatformNotification.
+        (WebCore::AXObjectCache::postTextReplacementPlatformNotification): 
+        (WebCore::AXObjectCache::postTextReplacementPlatformNotificationForTextControl): Added.
+
+        * accessibility/mac/WebAccessibilityObjectWrapperBase.h:
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.h:
+
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (textMarkerForVisiblePosition):
+        (-[WebAccessibilityObjectWrapper textMarkerForFirstPositionInTextControl:]): Added.
+
+        * dom/Document.cpp:
+        (WebCore::Document::updateLayout): Assert that LayoutDisallowedScope is not in the stack frame.
+
+        * html/HTMLTextFormControlElement.cpp:
+        (WebCore::HTMLTextFormControlElement::setInnerTextValue): Call postTextReplacementNotificationForTextControl
+        to avoid triggering a synchronous layout. Also create LayoutDisallowedScope to avoid a similar performance
+        regression from being introduced in the future in this function. Finally, made innerText a RefPtr for extra
+        safety since we're using it after updating the DOM tree.
+
+        * rendering/LayoutDisallowedScope.cpp: Added.
+        * rendering/LayoutDisallowedScope.h: Added.
+        (WebCore::LayoutDisallowedScope::LayoutDisallowedScope):
+        (WebCore::LayoutDisallowedScope::~LayoutDisallowedScope):
+        (WebCore::LayoutDisallowedScope::isLayoutAllowed):
+
 2017-06-27  Myles C. Maxfield  <mmaxfi...@apple.com>
 
         [iOS] Cannot italicize or bold text rendered with text styles

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (218909 => 218910)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-06-29 02:54:00 UTC (rev 218910)
@@ -4240,6 +4240,7 @@
 		9BC6C21C13CCC97B008E0337 /* HTMLTextFormControlElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BC6C21A13CCC97B008E0337 /* HTMLTextFormControlElement.cpp */; };
 		9BD0BF9312A42BF50072FD43 /* ScopedEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD0BF9112A42BF50072FD43 /* ScopedEventQueue.h */; };
 		9BD0BF9412A42BF50072FD43 /* ScopedEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD0BF9212A42BF50072FD43 /* ScopedEventQueue.cpp */; };
+		9BD1F6821F046310001C9CDD /* LayoutDisallowedScope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD1F6811F046310001C9CDD /* LayoutDisallowedScope.cpp */; };
 		9BD4E9161C462872005065BC /* JSCustomElementInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD4E9141C462872005065BC /* JSCustomElementInterface.cpp */; };
 		9BD4E9171C462872005065BC /* JSCustomElementInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD4E9151C462872005065BC /* JSCustomElementInterface.h */; };
 		9BD4E91A1C462CFC005065BC /* CustomElementRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD4E9181C462CFC005065BC /* CustomElementRegistry.cpp */; };
@@ -12527,6 +12528,8 @@
 		9BC6C21A13CCC97B008E0337 /* HTMLTextFormControlElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLTextFormControlElement.cpp; sourceTree = "<group>"; };
 		9BD0BF9112A42BF50072FD43 /* ScopedEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScopedEventQueue.h; sourceTree = "<group>"; };
 		9BD0BF9212A42BF50072FD43 /* ScopedEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScopedEventQueue.cpp; sourceTree = "<group>"; };
+		9BD1F6801F0462B8001C9CDD /* LayoutDisallowedScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LayoutDisallowedScope.h; sourceTree = "<group>"; };
+		9BD1F6811F046310001C9CDD /* LayoutDisallowedScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutDisallowedScope.cpp; sourceTree = "<group>"; };
 		9BD4E9141C462872005065BC /* JSCustomElementInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCustomElementInterface.cpp; sourceTree = "<group>"; };
 		9BD4E9151C462872005065BC /* JSCustomElementInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCustomElementInterface.h; sourceTree = "<group>"; };
 		9BD4E9181C462CFC005065BC /* CustomElementRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CustomElementRegistry.cpp; sourceTree = "<group>"; };
@@ -25408,6 +25411,8 @@
 				BCEA481A097D93020094C9E4 /* InlineTextBox.cpp */,
 				BCEA481B097D93020094C9E4 /* InlineTextBox.h */,
 				580371631A66F1D300BAF519 /* LayerFragment.h */,
+				9BD1F6811F046310001C9CDD /* LayoutDisallowedScope.cpp */,
+				9BD1F6801F0462B8001C9CDD /* LayoutDisallowedScope.h */,
 				A120ACA113F9984600FE4AC7 /* LayoutRepainter.cpp */,
 				A120ACA013F9983700FE4AC7 /* LayoutRepainter.h */,
 				2D9066040BE141D400956998 /* LayoutState.cpp */,
@@ -33762,6 +33767,7 @@
 				97AABD1814FA09D5007457AE /* ThreadableWebSocketChannelClientWrapper.cpp in Sources */,
 				51DF6D800B92A18E00C2DC85 /* ThreadCheck.mm in Sources */,
 				0F6383DD18615B29003E5DB5 /* ThreadedScrollingTree.cpp in Sources */,
+				9BD1F6821F046310001C9CDD /* LayoutDisallowedScope.cpp in Sources */,
 				E1FF57A60F01256B00891EBB /* ThreadGlobalData.cpp in Sources */,
 				185BCF280F3279CE000EA262 /* ThreadTimers.cpp in Sources */,
 				7AA3A699194A64E7001CBD24 /* TileController.cpp in Sources */,

Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (218909 => 218910)


--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp	2017-06-29 02:54:00 UTC (rev 218910)
@@ -74,6 +74,7 @@
 #include "HTMLLabelElement.h"
 #include "HTMLMeterElement.h"
 #include "HTMLNames.h"
+#include "HTMLTextFormControlElement.h"
 #include "InlineElementBox.h"
 #include "MathMLElement.h"
 #include "Page.h"
@@ -93,6 +94,7 @@
 #include "SVGElement.h"
 #include "ScrollView.h"
 #include "TextBoundaries.h"
+#include "TextControlInnerElements.h"
 #include "TextIterator.h"
 #include <wtf/DataLog.h>
 
@@ -1293,6 +1295,25 @@
 #endif
 }
 
+void AXObjectCache::postTextReplacementNotificationForTextControl(HTMLTextFormControlElement& textControl, const String& deletedText, const String& insertedText)
+{
+    stopCachingComputedObjectAttributes();
+
+    AccessibilityObject* object = getOrCreate(&textControl);
+#if PLATFORM(COCOA)
+    if (object) {
+        if (enqueuePasswordValueChangeNotification(object))
+            return;
+        object = object->observableObject();
+    }
+
+    postTextReplacementPlatformNotificationForTextControl(object, deletedText, insertedText, textControl);
+#else
+    nodeTextChangePlatformNotification(object, textChangeForEditType(AXTextEditTypeDelete), 0, deletedText);
+    nodeTextChangePlatformNotification(object, textChangeForEditType(AXTextEditTypeInsert), 0, insertedText);
+#endif
+}
+
 bool AXObjectCache::enqueuePasswordValueChangeNotification(AccessibilityObject* object)
 {
     if (!isPasswordFieldOrContainedByPasswordField(object))
@@ -2083,47 +2104,75 @@
     return this->getOrCreate(domNode);
 }
 
-void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
+std::optional<TextMarkerData> AXObjectCache::textMarkerDataForVisiblePosition(const VisiblePosition& visiblePos)
 {
-    // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
-    // This also allows callers to check for failure by looking at textMarkerData upon return.
-    memset(&textMarkerData, 0, sizeof(TextMarkerData));
-    
     if (visiblePos.isNull())
-        return;
-    
+        return std::nullopt;
+
     Position deepPos = visiblePos.deepEquivalent();
     Node* domNode = deepPos.deprecatedNode();
     ASSERT(domNode);
     if (!domNode)
-        return;
-    
+        return std::nullopt;
+
     if (is<HTMLInputElement>(*domNode) && downcast<HTMLInputElement>(*domNode).isPasswordField())
-        return;
-    
+        return std::nullopt;
+
     // If the visible position has an anchor type referring to a node other than the anchored node, we should
     // set the text marker data with CharacterOffset so that the offset will correspond to the node.
     CharacterOffset characterOffset = characterOffsetFromVisiblePosition(visiblePos);
     if (deepPos.anchorType() == Position::PositionIsAfterAnchor || deepPos.anchorType() == Position::PositionIsAfterChildren) {
+        TextMarkerData textMarkerData;
         textMarkerDataForCharacterOffset(textMarkerData, characterOffset);
-        return;
+        return textMarkerData;
     }
-    
+
     // find or create an accessibility object for this node
     AXObjectCache* cache = domNode->document().axObjectCache();
     RefPtr<AccessibilityObject> obj = cache->getOrCreate(domNode);
-    
+
+    TextMarkerData textMarkerData;
     textMarkerData.axID = obj.get()->axObjectID();
     textMarkerData.node = domNode;
     textMarkerData.offset = deepPos.deprecatedEditingOffset();
     textMarkerData.affinity = visiblePos.affinity();
-    
+
     textMarkerData.characterOffset = characterOffset.offset;
     textMarkerData.characterStartIndex = characterOffset.startIndex;
-    
+
     cache->setNodeInUse(domNode);
+
+    return textMarkerData;
 }
 
+// This function exits as a performance optimization to avoid a synchronous layout.
+std::optional<TextMarkerData> AXObjectCache::textMarkerDataForFirstPositionInTextControl(HTMLTextFormControlElement& textControl)
+{
+    TextControlInnerTextElement* innerTextElement = textControl.innerTextElement();
+    if (!innerTextElement)
+        return std::nullopt;
+
+    if (is<HTMLInputElement>(textControl) && downcast<HTMLInputElement>(textControl).isPasswordField())
+        return std::nullopt;
+
+    Position firstPosition = firstPositionInNode(innerTextElement);
+    Node* firstChild = innerTextElement->firstChild();
+    if (!firstChild)
+        firstChild = innerTextElement;
+    ContainerNode* editingHost = highestEditableRoot(firstPosition);
+
+    AXObjectCache* cache = textControl.document().axObjectCache();
+    RefPtr<AccessibilityObject> obj = cache->getOrCreate(editingHost);
+
+    TextMarkerData textMarkerData;
+    textMarkerData.axID = obj.get()->axObjectID();
+    textMarkerData.node = firstChild;
+
+    cache->setNodeInUse(&textControl);
+
+    return textMarkerData;
+}
+
 CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset& characterOffset, bool ignoreNextNodeStart)
 {
     if (characterOffset.isNull())

Modified: trunk/Source/WebCore/accessibility/AXObjectCache.h (218909 => 218910)


--- trunk/Source/WebCore/accessibility/AXObjectCache.h	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.h	2017-06-29 02:54:00 UTC (rev 218910)
@@ -41,6 +41,7 @@
 
 class Document;
 class HTMLAreaElement;
+class HTMLTextFormControlElement;
 class Node;
 class Page;
 class RenderBlock;
@@ -51,13 +52,13 @@
 class Widget;
 
 struct TextMarkerData {
-    AXID axID;
-    Node* node;
-    int offset;
-    int characterStartIndex;
-    int characterOffset;
-    bool ignored;
-    EAffinity affinity;
+    AXID axID { 0 };
+    Node* node { nullptr };
+    int offset { 0 };
+    int characterStartIndex { 0 };
+    int characterOffset { 0 };
+    bool ignored { false };
+    EAffinity affinity { DOWNSTREAM };
 };
 
 struct CharacterOffset {
@@ -212,7 +213,8 @@
     AccessibilityObject* objectFromAXID(AXID id) const { return m_objects.get(id); }
 
     // Text marker utilities.
-    void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&);
+    std::optional<TextMarkerData> textMarkerDataForVisiblePosition(const VisiblePosition&);
+    std::optional<TextMarkerData> textMarkerDataForFirstPositionInTextControl(HTMLTextFormControlElement&);
     void textMarkerDataForCharacterOffset(TextMarkerData&, const CharacterOffset&);
     void textMarkerDataForNextCharacterOffset(TextMarkerData&, const CharacterOffset&);
     void textMarkerDataForPreviousCharacterOffset(TextMarkerData&, const CharacterOffset&);
@@ -300,6 +302,7 @@
 
     void postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&);
     void postTextReplacementNotification(Node*, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition&);
+    void postTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String& deletedText, const String& insertedText);
     void postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&);
     void postTextStateChangeNotification(const Position&, const AXTextStateChangeIntent&, const VisibleSelection&);
     void postLiveRegionChangeNotification(AccessibilityObject*);
@@ -337,6 +340,7 @@
 #if PLATFORM(COCOA)
     void postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
     void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&);
+    void postTextReplacementPlatformNotificationForTextControl(AccessibilityObject*, const String& deletedText, const String& insertedText, HTMLTextFormControlElement&);
     void postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&);
 #else
     static AXTextChange textChangeForEditType(AXTextEditType);
@@ -499,6 +503,7 @@
 inline void AXObjectCache::postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&) { }
 inline void AXObjectCache::postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&) { }
 inline void AXObjectCache::postTextReplacementNotification(Node*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postTextReplacementNotificationForTextControl(HTMLTextFormControl&, const String&, const String&) { }
 inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget, PostType) { }
 inline void AXObjectCache::postNotification(RenderObject*, AXNotification, PostTarget, PostType) { }
 inline void AXObjectCache::postNotification(Node*, AXNotification, PostTarget, PostType) { }

Modified: trunk/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm (218909 => 218910)


--- trunk/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm	2017-06-29 02:54:00 UTC (rev 218910)
@@ -113,6 +113,11 @@
     postPlatformNotification(object, AXValueChanged);
 }
 
+void AXObjectCache::postTextReplacementPlatformNotificationForTextControl(AccessibilityObject* object, const String&, const String&, HTMLTextFormControlElement&)
+{
+    postPlatformNotification(object, AXValueChanged);
+}
+
 void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* axFrameObject, AXLoadingEvent loadingEvent)
 {
     if (!axFrameObject)

Modified: trunk/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm (218909 => 218910)


--- trunk/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm	2017-06-29 02:54:00 UTC (rev 218910)
@@ -180,10 +180,10 @@
 
 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache
 {
-    TextMarkerData textMarkerData;
-    cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
-    
-    return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
+    auto textMarkerData = cache->textMarkerDataForVisiblePosition(visiblePos);
+    if (!textMarkerData)
+        return nil;
+    return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData.value() cache:cache] autorelease];
 }
 
 + (WebAccessibilityTextMarker *)textMarkerWithCharacterOffset:(CharacterOffset&)characterOffset cache:(AXObjectCache*)cache

Modified: trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm (218909 => 218910)


--- trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm	2017-06-29 02:54:00 UTC (rev 218910)
@@ -407,8 +407,23 @@
     [userInfo release];
 }
 
-static NSDictionary *textReplacementChangeDictionary(AccessibilityObject* object, AXTextEditType type, const String& string, const VisiblePosition& position)
+static void addTextMarkerFor(NSMutableDictionary* change, AccessibilityObject& object, const VisiblePosition& position)
 {
+    if (position.isNull())
+        return;
+    if (id textMarker = [object.wrapper() textMarkerForVisiblePosition:position])
+        [change setObject:textMarker forKey:NSAccessibilityTextChangeValueStartMarker];
+}
+
+static void addTextMarkerFor(NSMutableDictionary* change, AccessibilityObject& object, HTMLTextFormControlElement& textControl)
+{
+    if (id textMarker = [object.wrapper() textMarkerForFirstPositionInTextControl:textControl])
+        [change setObject:textMarker forKey:NSAccessibilityTextChangeValueStartMarker];
+}
+
+template <typename TextMarkerTargetType>
+static NSDictionary *textReplacementChangeDictionary(AccessibilityObject& object, AXTextEditType type, const String& string, TextMarkerTargetType& markerTarget)
+{
     NSString *text = (NSString *)string;
     NSUInteger length = [text length];
     if (!length)
@@ -420,10 +435,7 @@
         text = [text substringToIndex:AXValueChangeTruncationLength];
     }
     [change setObject:text forKey:NSAccessibilityTextChangeValue];
-    if (position.isNotNull()) {
-        if (id textMarker = [object->wrapper() textMarkerForVisiblePosition:position])
-            [change setObject:textMarker forKey:NSAccessibilityTextChangeValueStartMarker];
-    }
+    addTextMarkerFor(change, object, markerTarget);
     return [change autorelease];
 }
 
@@ -435,6 +447,23 @@
     postTextReplacementPlatformNotification(object, AXTextEditTypeUnknown, emptyString(), type, text, position);
 }
 
+static void postUserInfoForChanges(AccessibilityObject& rootWebArea, AccessibilityObject& object, NSMutableArray* changes)
+{
+    NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:4];
+    [userInfo setObject:@(platformChangeTypeForWebCoreChangeType(AXTextStateChangeTypeEdit)) forKey:NSAccessibilityTextStateChangeTypeKey];
+    if (changes.count)
+        [userInfo setObject:changes forKey:NSAccessibilityTextChangeValues];
+
+    if (id wrapper = object.wrapper())
+        [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
+
+    AXPostNotificationWithUserInfo(rootWebArea.wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+    if (rootWebArea.wrapper() != object.wrapper())
+        AXPostNotificationWithUserInfo(object.wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+
+    [userInfo release];
+}
+
 void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject* object, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition& position)
 {
     if (!object)
@@ -443,26 +472,30 @@
     if (!object)
         return;
 
-    NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithCapacity:4];
-    [userInfo setObject:@(platformChangeTypeForWebCoreChangeType(AXTextStateChangeTypeEdit)) forKey:NSAccessibilityTextStateChangeTypeKey];
-
     NSMutableArray *changes = [[NSMutableArray alloc] initWithCapacity:2];
-    if (NSDictionary *change = textReplacementChangeDictionary(object, deletionType, deletedText, position))
+    if (NSDictionary *change = textReplacementChangeDictionary(*object, deletionType, deletedText, position))
         [changes addObject:change];
-    if (NSDictionary *change = textReplacementChangeDictionary(object, insertionType, insertedText, position))
+    if (NSDictionary *change = textReplacementChangeDictionary(*object, insertionType, insertedText, position))
         [changes addObject:change];
-    if (changes.count)
-        [userInfo setObject:changes forKey:NSAccessibilityTextChangeValues];
+    postUserInfoForChanges(*rootWebArea(), *object, changes);
     [changes release];
+}
 
-    if (id wrapper = object->wrapper())
-        [userInfo setObject:wrapper forKey:NSAccessibilityTextChangeElement];
+void AXObjectCache::postTextReplacementPlatformNotificationForTextControl(AccessibilityObject* object, const String& deletedText, const String& insertedText, HTMLTextFormControlElement& textControl)
+{
+    if (!object)
+        object = rootWebArea();
 
-    AXPostNotificationWithUserInfo(rootWebArea()->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
-    if (rootWebArea()->wrapper() != object->wrapper())
-        AXPostNotificationWithUserInfo(object->wrapper(), NSAccessibilityValueChangedNotification, userInfo);
+    if (!object)
+        return;
 
-    [userInfo release];
+    NSMutableArray *changes = [[NSMutableArray alloc] initWithCapacity:2];
+    if (NSDictionary *change = textReplacementChangeDictionary(*object, AXTextEditTypeDelete, deletedText, textControl))
+        [changes addObject:change];
+    if (NSDictionary *change = textReplacementChangeDictionary(*object, AXTextEditTypeInsert, insertedText, textControl))
+        [changes addObject:change];
+    postUserInfoForChanges(*rootWebArea(), *object, changes);
+    [changes release];
 }
 
 void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* axFrameObject, AXLoadingEvent loadingEvent)

Modified: trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.h (218909 => 218910)


--- trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.h	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.h	2017-06-29 02:54:00 UTC (rev 218910)
@@ -37,6 +37,7 @@
 struct AccessibilitySearchCriteria;
 class IntRect;
 class FloatPoint;
+class HTMLTextFormControlElement;
 class Path;
 class VisiblePosition;
 }

Modified: trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.h (218909 => 218910)


--- trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.h	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.h	2017-06-29 02:54:00 UTC (rev 218910)
@@ -34,6 +34,7 @@
 
 - (id)textMarkerRangeFromVisiblePositions:(const WebCore::VisiblePosition&)startPosition endPosition:(const WebCore::VisiblePosition&)endPosition;
 - (id)textMarkerForVisiblePosition:(const WebCore::VisiblePosition&)visiblePos;
+- (id)textMarkerForFirstPositionInTextControl:(WebCore::HTMLTextFormControlElement&)textControl;
 
 // When a plugin uses a WebKit control to act as a surrogate view (e.g. PDF use WebKit to create text fields).
 - (id)associatedPluginParent;

Modified: trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm (218909 => 218910)


--- trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm	2017-06-29 02:54:00 UTC (rev 218910)
@@ -681,13 +681,12 @@
 static id textMarkerForVisiblePosition(AXObjectCache* cache, const VisiblePosition& visiblePos)
 {
     ASSERT(cache);
-    
-    TextMarkerData textMarkerData;
-    cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
-    if (!textMarkerData.axID)
+
+    auto textMarkerData = cache->textMarkerDataForVisiblePosition(visiblePos);
+    if (!textMarkerData)
         return nil;
-    
-    return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData, sizeof(textMarkerData)));
+
+    return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData.value(), sizeof(textMarkerData.value())));
 }
 
 - (id)textMarkerForVisiblePosition:(const VisiblePosition &)visiblePos
@@ -695,6 +694,19 @@
     return textMarkerForVisiblePosition(m_object->axObjectCache(), visiblePos);
 }
 
+- (id)textMarkerForFirstPositionInTextControl:(HTMLTextFormControlElement &)textControl
+{
+    auto *cache = m_object->axObjectCache();
+    if (!cache)
+        return nil;
+
+    auto textMarkerData = cache->textMarkerDataForFirstPositionInTextControl(textControl);
+    if (!textMarkerData)
+        return nil;
+
+    return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData.value(), sizeof(textMarkerData.value())));
+}
+
 static VisiblePosition visiblePositionForTextMarker(AXObjectCache* cache, CFTypeRef textMarker)
 {
     ASSERT(cache);

Modified: trunk/Source/WebCore/dom/Document.cpp (218909 => 218910)


--- trunk/Source/WebCore/dom/Document.cpp	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/dom/Document.cpp	2017-06-29 02:54:00 UTC (rev 218910)
@@ -107,6 +107,7 @@
 #include "JSLazyEventListener.h"
 #include "KeyboardEvent.h"
 #include "Language.h"
+#include "LayoutDisallowedScope.h"
 #include "LoaderStrategy.h"
 #include "Logging.h"
 #include "MainFrame.h"
@@ -1914,6 +1915,7 @@
 
 void Document::updateLayout()
 {
+    ASSERT(LayoutDisallowedScope::isLayoutAllowed());
     ASSERT(isMainThread());
 
     FrameView* frameView = view();

Modified: trunk/Source/WebCore/html/HTMLTextFormControlElement.cpp (218909 => 218910)


--- trunk/Source/WebCore/html/HTMLTextFormControlElement.cpp	2017-06-29 02:46:11 UTC (rev 218909)
+++ trunk/Source/WebCore/html/HTMLTextFormControlElement.cpp	2017-06-29 02:54:00 UTC (rev 218910)
@@ -40,6 +40,7 @@
 #include "HTMLInputElement.h"
 #include "HTMLNames.h"
 #include "HTMLParserIdioms.h"
+#include "LayoutDisallowedScope.h"
 #include "Logging.h"
 #include "NoEventDispatchAssertion.h"
 #include "NodeTraversal.h"
@@ -549,7 +550,8 @@
 
 void HTMLTextFormControlElement::setInnerTextValue(const String& value)
 {
-    TextControlInnerTextElement* innerText = innerTextElement();
+    LayoutDisallowedScope layoutDisallowedScope(LayoutDisallowedScope::Reason::PerformanceOptimization);
+    RefPtr<TextControlInnerTextElement> innerText = innerTextElement();
     if (!innerText)
         return;
 
@@ -577,7 +579,7 @@
 #if HAVE(ACCESSIBILITY) && PLATFORM(COCOA)
         if (textIsChanged && renderer()) {
             if (AXObjectCache* cache = document().existingAXObjectCache())
-                cache->postTextReplacementNotification(this, AXTextEditTypeDelete, previousValue, AXTextEditTypeInsert, value, VisiblePosition(Position(this, Position::PositionIsBeforeAnchor)));
+                cache->postTextReplacementNotificationForTextControl(*this, previousValue, value);
         }
 #endif
     }

Added: trunk/Source/WebCore/rendering/LayoutDisallowedScope.cpp (0 => 218910)


--- trunk/Source/WebCore/rendering/LayoutDisallowedScope.cpp	                        (rev 0)
+++ trunk/Source/WebCore/rendering/LayoutDisallowedScope.cpp	2017-06-29 02:54:00 UTC (rev 218910)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LayoutDisallowedScope.h"
+
+namespace WebCore {
+
+#if !ASSERT_DISABLED
+
+LayoutDisallowedScope* LayoutDisallowedScope::s_currentAssertion = nullptr;
+
+#endif
+
+}

Added: trunk/Source/WebCore/rendering/LayoutDisallowedScope.h (0 => 218910)


--- trunk/Source/WebCore/rendering/LayoutDisallowedScope.h	                        (rev 0)
+++ trunk/Source/WebCore/rendering/LayoutDisallowedScope.h	2017-06-29 02:54:00 UTC (rev 218910)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+#if !ASSERT_DISABLED
+
+class LayoutDisallowedScope {
+public:
+    enum class Reason { PerformanceOptimization };
+    LayoutDisallowedScope(Reason)
+        : m_previousAssertion(s_currentAssertion)
+    {
+        s_currentAssertion = this;
+    }
+
+    ~LayoutDisallowedScope()
+    {
+        s_currentAssertion = m_previousAssertion;
+    }
+
+    static bool isLayoutAllowed() { return !s_currentAssertion; }
+
+private:
+    LayoutDisallowedScope* m_previousAssertion;
+    static LayoutDisallowedScope* s_currentAssertion;
+};
+
+#else
+
+class LayoutDisallowedScope {
+public:
+    enum class Reason { PerformanceOptimization };
+    LayoutDisallowedScope(Reason) { }
+    static bool isLayoutAllowed() { return true; }
+};
+
+#endif
+
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to