<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[180867] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/180867">180867</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2015-03-01 11:52:21 -0800 (Sun, 01 Mar 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>isContentEditable shouldn't trigger synchronous style recalc in most cases
https://bugs.webkit.org/show_bug.cgi?id=129034

Reviewed by Antti Koivisto.

Source/WebCore:

Avoid style recalc inside isContentEditable when the document doesn't contain -webkit-user-modify or
-webkit-user-select: all. Instead, compute the value from contenteditable attributes in ancestors.
However, still compute the editability from the style tree when it's up-to-date in order to avoid
repeatedly walking up the DOM tree in a hot code path inside editing.

Test: fast/dom/HTMLElement/dynamic-editability-change.html

* css/CSSGrammar.y.in: No need to pass in &quot;true&quot; as we never call this function with false.
* css/CSSParser.cpp:
(WebCore::isValidKeywordPropertyAndValue): Calls parserSetUsesStyleBasedEditability as needed.
(WebCore::parseKeywordValue): Passes around StyleSheetContents*.
(WebCore::CSSParser::parseValue): Ditto.
(WebCore::CSSParser::parseFont): Ditto.

* css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::StyleSheetContents): Initializes and copies m_usesStyleBasedEditability.

* css/StyleSheetContents.h:
(WebCore::StyleSheetContents::parserSetUsesRemUnits): Removed the argument since it was always true.
(WebCore::StyleSheetContents::parserSetUsesStyleBasedEditability): Added.
(WebCore::StyleSheetContents::usesStyleBasedEditability): Added.

* dom/Document.cpp:
(WebCore::Document::recalcStyle): Added a FIXME as well as a comment explaining why we don't call
setUsesStyleBasedEditability. Since Node::computeEditability triggers style recalc only when the flag
is set to true, it's too late to update the flag here.
(WebCore::Document::updateStyleIfNeeded): Uses a newly extracted needsStyleRecalc.
(WebCore::Document::updateBaseURL): Preserves m_usesStyleBasedEditability as well as m_usesRemUnit.
(WebCore::Document::usesStyleBasedEditability): Added. Returns true when inline style declarations or
any active stylesheet uses -webkit-user-modify or -webkit-user-select: all. Flushing pending stylesheet
changes here is fine because the alternative is to trigger a full blown style recalc.

* dom/Document.h:
(WebCore::Document::needsStyleRecalc): Added. Extracted from updateStyleIfNeeded.

* dom/DocumentStyleSheetCollection.cpp:
(WebCore::DocumentStyleSheetCollection::DocumentStyleSheetCollection):
(WebCore::styleSheetsUseRemUnits): Deleted.
(WebCore::DocumentStyleSheetCollection::updateActiveStyleSheets): Updates m_usesStyleBasedEditability
as well as m_usesRemUnit.

* dom/DocumentStyleSheetCollection.h:
(WebCore::DocumentStyleSheetCollection::usesStyleBasedEditability): Added.
(WebCore::DocumentStyleSheetCollection::setUsesStyleBasedEditability): Added.

* dom/Node.cpp:
(WebCore::computeEditabilityFromComputedStyle): Extracted from computeEditability.
(WebCore::Node::computeEditability): When the style recalc is requested and the render tree is dirty,
check if the document uses any CSS property that can affect the editability of elements. If it doesn't,
compute the editability from contenteditable attributes in the anchors via matchesReadWritePseudoClass.
Continue to use the style-based computation when the render tree isn't dirty to avoid the tree walk.

* html/HTMLElement.cpp:
(WebCore::HTMLElement::editabilityFromContentEditableAttr): Extracted from matchesReadWritePseudoClass
to be called in Node::computeEditability. Also made it return Editability instead of boolean.
(WebCore::HTMLElement::matchesReadWritePseudoClass):
* html/HTMLElement.h:

LayoutTests:

Added a regression test to update the editability of elements dynamically. Also rebaselined
tests per style recalc timing changes.

* fast/dom/HTMLElement/dynamic-editability-change-expected.txt: Added.
* fast/dom/HTMLElement/dynamic-editability-change.html: Added.
* platform/mac/editing/execCommand/5142012-1-expected.txt: anonymous render block differences.
* platform/mac/editing/execCommand/nsresponder-outdent-expected.txt: Ditto.
* platform/mac/editing/inserting/insert-at-end-02-expected.txt: Empty render text differences.
* platform/mac/editing/pasteboard/4989774-expected.txt: Ditto.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformmaceditingexecCommand51420121expectedtxt">trunk/LayoutTests/platform/mac/editing/execCommand/5142012-1-expected.txt</a></li>
<li><a href="#trunkLayoutTestsplatformmaceditingexecCommandnsresponderoutdentexpectedtxt">trunk/LayoutTests/platform/mac/editing/execCommand/nsresponder-outdent-expected.txt</a></li>
<li><a href="#trunkLayoutTestsplatformmaceditinginsertinginsertatend02expectedtxt">trunk/LayoutTests/platform/mac/editing/inserting/insert-at-end-02-expected.txt</a></li>
<li><a href="#trunkLayoutTestsplatformmaceditingpasteboard4989774expectedtxt">trunk/LayoutTests/platform/mac/editing/pasteboard/4989774-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorecssCSSGrammaryin">trunk/Source/WebCore/css/CSSGrammar.y.in</a></li>
<li><a href="#trunkSourceWebCorecssCSSParsercpp">trunk/Source/WebCore/css/CSSParser.cpp</a></li>
<li><a href="#trunkSourceWebCorecssStyleSheetContentscpp">trunk/Source/WebCore/css/StyleSheetContents.cpp</a></li>
<li><a href="#trunkSourceWebCorecssStyleSheetContentsh">trunk/Source/WebCore/css/StyleSheetContents.h</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCoredomDocumentStyleSheetCollectioncpp">trunk/Source/WebCore/dom/DocumentStyleSheetCollection.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumentStyleSheetCollectionh">trunk/Source/WebCore/dom/DocumentStyleSheetCollection.h</a></li>
<li><a href="#trunkSourceWebCoredomNodecpp">trunk/Source/WebCore/dom/Node.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLElementcpp">trunk/Source/WebCore/html/HTMLElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLElementh">trunk/Source/WebCore/html/HTMLElement.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastdomHTMLElementdynamiceditabilitychangeexpectedtxt">trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastdomHTMLElementdynamiceditabilitychangehtml">trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/LayoutTests/ChangeLog        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2015-03-01  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        isContentEditable shouldn't trigger synchronous style recalc in most cases
+        https://bugs.webkit.org/show_bug.cgi?id=129034
+
+        Reviewed by Antti Koivisto.
+
+        Added a regression test to update the editability of elements dynamically. Also rebaselined
+        tests per style recalc timing changes.
+
+        * fast/dom/HTMLElement/dynamic-editability-change-expected.txt: Added.
+        * fast/dom/HTMLElement/dynamic-editability-change.html: Added.
+        * platform/mac/editing/execCommand/5142012-1-expected.txt: anonymous render block differences.
+        * platform/mac/editing/execCommand/nsresponder-outdent-expected.txt: Ditto.
+        * platform/mac/editing/inserting/insert-at-end-02-expected.txt: Empty render text differences.
+        * platform/mac/editing/pasteboard/4989774-expected.txt: Ditto.
+
</ins><span class="cx"> 2015-03-01  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Win] Document some more debug assertions.
</span></span></pre></div>
<a id="trunkLayoutTestsfastdomHTMLElementdynamiceditabilitychangeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change-expected.txt (0 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change-expected.txt        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+This test updates contenteditable content attribute, contentEditable IDL property, and -webkit-user-modify CSS property dynamically.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable is false
+PASS $(&quot;div&quot;).contentEditable = &quot;true&quot;; $(&quot;div&quot;).isContentEditable is true
+PASS newDoc(&quot;&lt;div contenteditable=true&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;div&quot;).contentEditable = &quot;false&quot;; $(&quot;div&quot;).isContentEditable is false
+PASS newDoc(&quot;&lt;div contenteditable=plaintext-only&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;div&quot;).contentEditable = &quot;false&quot;; $(&quot;div&quot;).isContentEditable is false
+
+Style rules
+PASS newDoc(&quot;&lt;div&gt;&lt;/div&gt;&lt;style&gt; div { -webkit-user-modify: read-write; } &lt;/style&gt;&quot;); $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;style&quot;).textContent = &quot;&quot;; $(&quot;div&quot;).isContentEditable is false
+PASS $(&quot;style&quot;).textContent = &quot;* { -webkit-user-modify: read-write-plaintext-only; }&quot;; $(&quot;div&quot;).isContentEditable is true
+PASS newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); style = element(&quot;style&quot;, &quot;* { -webkit-user-modify: read-write }&quot;); $(&quot;div&quot;).isContentEditable is false
+PASS $(&quot;body&quot;).appendChild(style); $(&quot;div&quot;).isContentEditable is true
+PASS newDoc(&quot;&lt;div&gt;&lt;/div&gt;&lt;style&gt;&lt;/style&gt;&quot;); $(&quot;div&quot;).isContentEditable is false
+PASS $(&quot;style&quot;).sheet.insertRule(&quot;* { -webkit-user-modify: read-write; }&quot;); $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;style&quot;).sheet.insertRule(&quot;* { -webkit-user-modify: read-only !important; }&quot;); $(&quot;div&quot;).isContentEditable is false
+
+Inline styles
+PASS newDoc(&quot;&lt;div style='-webkit-user-modify:read-write'&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;head&quot;).innerHTML = &quot;&lt;base href='http://localhost/'&gt;&quot;; $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;div&quot;).style.webkitUserModify = &quot;&quot;; $(&quot;div&quot;).isContentEditable is false
+PASS newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).style.webkitUserModify = &quot;read-write&quot;; $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;div&quot;).setAttribute(&quot;style&quot;, &quot;&quot;); $(&quot;div&quot;).isContentEditable is false
+PASS newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable is false
+PASS $(&quot;div&quot;).setAttribute(&quot;style&quot;, &quot;-webkit-user-modify: read-write&quot;); $(&quot;div&quot;).isContentEditable is true
+PASS $(&quot;div&quot;).setAttribute(&quot;style&quot;, &quot;-webkit-user-modify: read-only&quot;); $(&quot;div&quot;).isContentEditable is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastdomHTMLElementdynamiceditabilitychangehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change.html (0 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change.html                                (rev 0)
+++ trunk/LayoutTests/fast/dom/HTMLElement/dynamic-editability-change.html        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -0,0 +1,61 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+
+function newDoc(markup) {
+    var iframe = document.createElement('iframe');
+    document.body.appendChild(iframe);
+    iframe.contentDocument.documentElement.innerHTML = '&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;/body&gt;';
+    iframe.contentDocument.body.innerHTML = markup;
+    window.doc = iframe.contentDocument;
+    window.$ = iframe.contentDocument.querySelector.bind(iframe.contentDocument);
+}
+
+function element(name, markup) {
+    var element = doc.createElement(name);
+    element.textContent = markup;
+    return element;
+}
+
+description('This test updates contenteditable content attribute, contentEditable IDL property, and -webkit-user-modify CSS property dynamically.');
+
+shouldBeFalse('newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('$(&quot;div&quot;).contentEditable = &quot;true&quot;; $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('newDoc(&quot;&lt;div contenteditable=true&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('$(&quot;div&quot;).contentEditable = &quot;false&quot;; $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('newDoc(&quot;&lt;div contenteditable=plaintext-only&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('$(&quot;div&quot;).contentEditable = &quot;false&quot;; $(&quot;div&quot;).isContentEditable');
+
+debug('');
+debug('Style rules');
+shouldBeTrue('newDoc(&quot;&lt;div&gt;&lt;/div&gt;&lt;style&gt; div { -webkit-user-modify: read-write; } &lt;/style&gt;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('$(&quot;style&quot;).textContent = &quot;&quot;; $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('$(&quot;style&quot;).textContent = &quot;* { -webkit-user-modify: read-write-plaintext-only; }&quot;; $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); style = element(&quot;style&quot;, &quot;* { -webkit-user-modify: read-write }&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('$(&quot;body&quot;).appendChild(style); $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('newDoc(&quot;&lt;div&gt;&lt;/div&gt;&lt;style&gt;&lt;/style&gt;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('$(&quot;style&quot;).sheet.insertRule(&quot;* { -webkit-user-modify: read-write; }&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('$(&quot;style&quot;).sheet.insertRule(&quot;* { -webkit-user-modify: read-only !important; }&quot;); $(&quot;div&quot;).isContentEditable');
+
+debug('');
+debug('Inline styles');
+shouldBeTrue('newDoc(&quot;&lt;div style=\'-webkit-user-modify:read-write\'&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('$(&quot;head&quot;).innerHTML = &quot;&lt;base href=\'http://localhost/\'&gt;&quot;; $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('$(&quot;div&quot;).style.webkitUserModify = &quot;&quot;; $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).style.webkitUserModify = &quot;read-write&quot;; $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('$(&quot;div&quot;).setAttribute(&quot;style&quot;, &quot;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('newDoc(&quot;&lt;div&gt;&lt;/div&gt;&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeTrue('$(&quot;div&quot;).setAttribute(&quot;style&quot;, &quot;-webkit-user-modify: read-write&quot;); $(&quot;div&quot;).isContentEditable');
+shouldBeFalse('$(&quot;div&quot;).setAttribute(&quot;style&quot;, &quot;-webkit-user-modify: read-only&quot;); $(&quot;div&quot;).isContentEditable');
+
+var iframes = document.querySelectorAll('iframe');
+for (var i = 0; i &lt; iframes.length; i++)
+    document.body.removeChild(iframes[i]);
+
+var successfullyParsed = true;
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformmaceditingexecCommand51420121expectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/editing/execCommand/5142012-1-expected.txt (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/editing/execCommand/5142012-1-expected.txt        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/LayoutTests/platform/mac/editing/execCommand/5142012-1-expected.txt        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -9,6 +9,7 @@
</span><span class="cx">           text run at (457,0) width 324: &quot;There shouldn't be any links *inside the selection*&quot;
</span><span class="cx">           text run at (0,18) width 43: &quot;below.&quot;
</span><span class="cx">       RenderBlock {DIV} at (0,52) size 784x36
</span><ins>+        RenderBlock (anonymous) at (0,0) size 784x0
</ins><span class="cx">         RenderBlock {DIV} at (0,0) size 784x18
</span><span class="cx">           RenderInline {A} at (0,0) size 24x18 [color=#0000EE]
</span><span class="cx">             RenderText {#text} at (0,0) size 24x18
</span><span class="lines">@@ -16,7 +17,6 @@
</span><span class="cx">           RenderInline {SPAN} at (0,0) size 13x18
</span><span class="cx">             RenderText {#text} at (23,0) size 13x18
</span><span class="cx">               text run at (23,0) width 13: &quot;lo&quot;
</span><del>-        RenderBlock (anonymous) at (0,18) size 784x0
</del><span class="cx">         RenderBlock {DIV} at (0,18) size 784x18
</span><span class="cx">           RenderInline {SPAN} at (0,0) size 16x18
</span><span class="cx">             RenderText {#text} at (0,0) size 16x18
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmaceditingexecCommandnsresponderoutdentexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/editing/execCommand/nsresponder-outdent-expected.txt (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/editing/execCommand/nsresponder-outdent-expected.txt        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/LayoutTests/platform/mac/editing/execCommand/nsresponder-outdent-expected.txt        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -14,6 +14,7 @@
</span><span class="cx">           text run at (0,0) width 201: &quot;This tests the outdent: method. &quot;
</span><span class="cx">           text run at (200,0) width 257: &quot;You should see an undented 'foo' below.&quot;
</span><span class="cx">       RenderBlock {DIV} at (0,34) size 784x18
</span><del>-        RenderText {#text} at (0,0) size 22x18
-          text run at (0,0) width 22: &quot;foo&quot;
</del><ins>+        RenderBlock (anonymous) at (0,0) size 784x18
+          RenderText {#text} at (0,0) size 22x18
+            text run at (0,0) width 22: &quot;foo&quot;
</ins><span class="cx"> caret: position 0 of child 0 {#text} of child 2 {DIV} of body
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmaceditinginsertinginsertatend02expectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/editing/inserting/insert-at-end-02-expected.txt (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/editing/inserting/insert-at-end-02-expected.txt        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/LayoutTests/platform/mac/editing/inserting/insert-at-end-02-expected.txt        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -29,4 +29,5 @@
</span><span class="cx">         RenderBlock (anonymous) at (2,46) size 780x18
</span><span class="cx">           RenderText {#text} at (0,0) size 8x18
</span><span class="cx">             text run at (0,0) width 8: &quot;x&quot;
</span><ins>+          RenderText {#text} at (0,0) size 0x0
</ins><span class="cx"> caret: position 1 of child 5 {#text} of child 5 {DIV} of body
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmaceditingpasteboard4989774expectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/editing/pasteboard/4989774-expected.txt (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/editing/pasteboard/4989774-expected.txt        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/LayoutTests/platform/mac/editing/pasteboard/4989774-expected.txt        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -12,4 +12,6 @@
</span><span class="cx">         text run at (634,103) width 102: &quot;You should see&quot;
</span><span class="cx">         text run at (735,103) width 5: &quot; &quot;
</span><span class="cx">         text run at (0,121) width 364: &quot;several pictures above all in the same line/paragraph.&quot;
</span><ins>+      RenderText {#text} at (0,0) size 0x0
+      RenderText {#text} at (0,0) size 0x0
</ins><span class="cx"> caret: position 164 of child 4 {#text} of body
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/ChangeLog        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -1,3 +1,68 @@
</span><ins>+2015-03-01  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        isContentEditable shouldn't trigger synchronous style recalc in most cases
+        https://bugs.webkit.org/show_bug.cgi?id=129034
+
+        Reviewed by Antti Koivisto.
+
+        Avoid style recalc inside isContentEditable when the document doesn't contain -webkit-user-modify or
+        -webkit-user-select: all. Instead, compute the value from contenteditable attributes in ancestors.
+        However, still compute the editability from the style tree when it's up-to-date in order to avoid
+        repeatedly walking up the DOM tree in a hot code path inside editing.
+
+        Test: fast/dom/HTMLElement/dynamic-editability-change.html
+
+        * css/CSSGrammar.y.in: No need to pass in &quot;true&quot; as we never call this function with false.
+        * css/CSSParser.cpp:
+        (WebCore::isValidKeywordPropertyAndValue): Calls parserSetUsesStyleBasedEditability as needed.
+        (WebCore::parseKeywordValue): Passes around StyleSheetContents*.
+        (WebCore::CSSParser::parseValue): Ditto.
+        (WebCore::CSSParser::parseFont): Ditto.
+
+        * css/StyleSheetContents.cpp:
+        (WebCore::StyleSheetContents::StyleSheetContents): Initializes and copies m_usesStyleBasedEditability.
+
+        * css/StyleSheetContents.h:
+        (WebCore::StyleSheetContents::parserSetUsesRemUnits): Removed the argument since it was always true.
+        (WebCore::StyleSheetContents::parserSetUsesStyleBasedEditability): Added.
+        (WebCore::StyleSheetContents::usesStyleBasedEditability): Added.
+
+        * dom/Document.cpp:
+        (WebCore::Document::recalcStyle): Added a FIXME as well as a comment explaining why we don't call
+        setUsesStyleBasedEditability. Since Node::computeEditability triggers style recalc only when the flag
+        is set to true, it's too late to update the flag here.
+        (WebCore::Document::updateStyleIfNeeded): Uses a newly extracted needsStyleRecalc.
+        (WebCore::Document::updateBaseURL): Preserves m_usesStyleBasedEditability as well as m_usesRemUnit.
+        (WebCore::Document::usesStyleBasedEditability): Added. Returns true when inline style declarations or
+        any active stylesheet uses -webkit-user-modify or -webkit-user-select: all. Flushing pending stylesheet
+        changes here is fine because the alternative is to trigger a full blown style recalc.
+
+        * dom/Document.h:
+        (WebCore::Document::needsStyleRecalc): Added. Extracted from updateStyleIfNeeded.
+
+        * dom/DocumentStyleSheetCollection.cpp:
+        (WebCore::DocumentStyleSheetCollection::DocumentStyleSheetCollection):
+        (WebCore::styleSheetsUseRemUnits): Deleted.
+        (WebCore::DocumentStyleSheetCollection::updateActiveStyleSheets): Updates m_usesStyleBasedEditability
+        as well as m_usesRemUnit.
+
+        * dom/DocumentStyleSheetCollection.h:
+        (WebCore::DocumentStyleSheetCollection::usesStyleBasedEditability): Added.
+        (WebCore::DocumentStyleSheetCollection::setUsesStyleBasedEditability): Added.
+
+        * dom/Node.cpp:
+        (WebCore::computeEditabilityFromComputedStyle): Extracted from computeEditability.
+        (WebCore::Node::computeEditability): When the style recalc is requested and the render tree is dirty,
+        check if the document uses any CSS property that can affect the editability of elements. If it doesn't,
+        compute the editability from contenteditable attributes in the anchors via matchesReadWritePseudoClass.
+        Continue to use the style-based computation when the render tree isn't dirty to avoid the tree walk.
+
+        * html/HTMLElement.cpp:
+        (WebCore::HTMLElement::editabilityFromContentEditableAttr): Extracted from matchesReadWritePseudoClass
+        to be called in Node::computeEditability. Also made it return Editability instead of boolean.
+        (WebCore::HTMLElement::matchesReadWritePseudoClass):
+        * html/HTMLElement.h:
+
</ins><span class="cx"> 2015-03-01  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Win] Unreviewed build fix.
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSGrammaryin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSGrammar.y.in (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/CSSGrammar.y.in        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/css/CSSGrammar.y.in        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -1700,7 +1700,7 @@
</span><span class="cx">       $$.fValue = $1;
</span><span class="cx">       $$.unit = CSSPrimitiveValue::CSS_REMS;
</span><span class="cx">       if (parser-&gt;m_styleSheet)
</span><del>-          parser-&gt;m_styleSheet-&gt;parserSetUsesRemUnits(true);
</del><ins>+          parser-&gt;m_styleSheet-&gt;parserSetUsesRemUnits();
</ins><span class="cx">   }
</span><span class="cx">   | CHS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CHS; }
</span><span class="cx">   | VW { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_VW; }
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSParser.cpp (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/CSSParser.cpp        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/css/CSSParser.cpp        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -640,7 +640,7 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int valueID, const CSSParserContext&amp; parserContext)
</del><ins>+static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int valueID, const CSSParserContext&amp; parserContext, StyleSheetContents* styleSheetContents)
</ins><span class="cx"> {
</span><span class="cx">     if (!valueID)
</span><span class="cx">         return false;
</span><span class="lines">@@ -1015,12 +1015,20 @@
</span><span class="cx">             return true;
</span><span class="cx">         break;
</span><span class="cx">     case CSSPropertyWebkitUserModify: // read-only | read-write
</span><del>-        if (valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly)
</del><ins>+        if (valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly) {
+            if (styleSheetContents)
+                styleSheetContents-&gt;parserSetUsesStyleBasedEditability();
</ins><span class="cx">             return true;
</span><ins>+        }
</ins><span class="cx">         break;
</span><span class="cx">     case CSSPropertyWebkitUserSelect: // auto | none | text | all
</span><del>-        if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll)
</del><ins>+        if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText)
</ins><span class="cx">             return true;
</span><ins>+        if (valueID == CSSValueAll) {
+            if (styleSheetContents)
+                styleSheetContents-&gt;parserSetUsesStyleBasedEditability();
+            return true;
+        }
</ins><span class="cx">         break;
</span><span class="cx">     case CSSPropertyWebkitWritingMode:
</span><span class="cx">         if (valueID &gt;= CSSValueHorizontalTb &amp;&amp; valueID &lt;= CSSValueHorizontalBt)
</span><span class="lines">@@ -1176,7 +1184,7 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String&amp; string, bool important, const CSSParserContext&amp; parserContext)
</del><ins>+static bool parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String&amp; string, bool important, const CSSParserContext&amp; parserContext, StyleSheetContents* styleSheetContents)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(!string.isEmpty());
</span><span class="cx"> 
</span><span class="lines">@@ -1203,7 +1211,7 @@
</span><span class="cx">         value = cssValuePool().createInheritedValue();
</span><span class="cx">     else if (valueID == CSSValueInitial)
</span><span class="cx">         value = cssValuePool().createExplicitInitialValue();
</span><del>-    else if (isValidKeywordPropertyAndValue(propertyId, valueID, parserContext))
</del><ins>+    else if (isValidKeywordPropertyAndValue(propertyId, valueID, parserContext, styleSheetContents))
</ins><span class="cx">         value = cssValuePool().createIdentifierValue(valueID);
</span><span class="cx">     else
</span><span class="cx">         return false;
</span><span class="lines">@@ -1308,7 +1316,7 @@
</span><span class="cx">         context.mode = cssParserMode;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (parseKeywordValue(declaration, propertyID, string, important, context))
</del><ins>+    if (parseKeywordValue(declaration, propertyID, string, important, context, contextStyleSheet))
</ins><span class="cx">         return true;
</span><span class="cx">     if (parseTranslateTransformValue(declaration, propertyID, string, important))
</span><span class="cx">         return true;
</span><span class="lines">@@ -1886,7 +1894,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (isKeywordPropertyID(propId)) {
</span><del>-        if (!isValidKeywordPropertyAndValue(propId, id, m_context))
</del><ins>+        if (!isValidKeywordPropertyAndValue(propId, id, m_context, m_styleSheet))
</ins><span class="cx">             return false;
</span><span class="cx">         if (m_valueList-&gt;next() &amp;&amp; !inShorthand())
</span><span class="cx">             return false;
</span><span class="lines">@@ -6297,7 +6305,7 @@
</span><span class="cx">     bool fontWeightParsed = false;
</span><span class="cx">     CSSParserValue* value;
</span><span class="cx">     while ((value = m_valueList-&gt;current())) {
</span><del>-        if (!fontStyleParsed &amp;&amp; isValidKeywordPropertyAndValue(CSSPropertyFontStyle, value-&gt;id, m_context)) {
</del><ins>+        if (!fontStyleParsed &amp;&amp; isValidKeywordPropertyAndValue(CSSPropertyFontStyle, value-&gt;id, m_context, m_styleSheet)) {
</ins><span class="cx">             addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(value-&gt;id), important);
</span><span class="cx">             fontStyleParsed = true;
</span><span class="cx">         } else if (!fontVariantParsed &amp;&amp; (value-&gt;id == CSSValueNormal || value-&gt;id == CSSValueSmallCaps)) {
</span></span></pre></div>
<a id="trunkSourceWebCorecssStyleSheetContentscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/StyleSheetContents.cpp (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/StyleSheetContents.cpp        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/css/StyleSheetContents.cpp        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -64,6 +64,7 @@
</span><span class="cx">     , m_hasSyntacticallyValidCSSHeader(true)
</span><span class="cx">     , m_didLoadErrorOccur(false)
</span><span class="cx">     , m_usesRemUnits(false)
</span><ins>+    , m_usesStyleBasedEditability(false)
</ins><span class="cx">     , m_isMutable(false)
</span><span class="cx">     , m_isInMemoryCache(false)
</span><span class="cx">     , m_parserContext(context)
</span><span class="lines">@@ -83,6 +84,7 @@
</span><span class="cx">     , m_hasSyntacticallyValidCSSHeader(o.m_hasSyntacticallyValidCSSHeader)
</span><span class="cx">     , m_didLoadErrorOccur(false)
</span><span class="cx">     , m_usesRemUnits(o.m_usesRemUnits)
</span><ins>+    , m_usesStyleBasedEditability(o.m_usesStyleBasedEditability)
</ins><span class="cx">     , m_isMutable(false)
</span><span class="cx">     , m_isInMemoryCache(false)
</span><span class="cx">     , m_parserContext(o.m_parserContext)
</span></span></pre></div>
<a id="trunkSourceWebCorecssStyleSheetContentsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/StyleSheetContents.h (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/StyleSheetContents.h        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/css/StyleSheetContents.h        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -91,7 +91,8 @@
</span><span class="cx">     void parserAddNamespace(const AtomicString&amp; prefix, const AtomicString&amp; uri);
</span><span class="cx">     void parserAppendRule(PassRefPtr&lt;StyleRuleBase&gt;);
</span><span class="cx">     void parserSetEncodingFromCharsetRule(const String&amp; encoding); 
</span><del>-    void parserSetUsesRemUnits(bool b) { m_usesRemUnits = b; }
</del><ins>+    void parserSetUsesRemUnits() { m_usesRemUnits = true; }
+    void parserSetUsesStyleBasedEditability() { m_usesStyleBasedEditability = true; }
</ins><span class="cx"> 
</span><span class="cx">     void clearRules();
</span><span class="cx"> 
</span><span class="lines">@@ -117,6 +118,7 @@
</span><span class="cx">     StyleRuleBase* ruleAt(unsigned index) const;
</span><span class="cx"> 
</span><span class="cx">     bool usesRemUnits() const { return m_usesRemUnits; }
</span><ins>+    bool usesStyleBasedEditability() const { return m_usesStyleBasedEditability; }
</ins><span class="cx"> 
</span><span class="cx">     unsigned estimatedSizeInBytes() const;
</span><span class="cx">     
</span><span class="lines">@@ -159,6 +161,7 @@
</span><span class="cx">     bool m_hasSyntacticallyValidCSSHeader : 1;
</span><span class="cx">     bool m_didLoadErrorOccur : 1;
</span><span class="cx">     bool m_usesRemUnits : 1;
</span><ins>+    bool m_usesStyleBasedEditability : 1;
</ins><span class="cx">     bool m_isMutable : 1;
</span><span class="cx">     bool m_isInMemoryCache : 1;
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/dom/Document.cpp        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -1747,8 +1747,11 @@
</span><span class="cx"> 
</span><span class="cx">     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(*this);
</span><span class="cx"> 
</span><ins>+    // FIXME: We never reset this flags.
</ins><span class="cx">     if (m_elementSheet &amp;&amp; m_elementSheet-&gt;contents().usesRemUnits())
</span><span class="cx">         m_styleSheetCollection.setUsesRemUnit(true);
</span><ins>+    // We don't call setUsesStyleBasedEditability here because the whole point of the flag is to avoid style recalc.
+    // i.e. updating the flag here would be too late.
</ins><span class="cx"> 
</span><span class="cx">     m_inStyleRecalc = true;
</span><span class="cx">     {
</span><span class="lines">@@ -1808,7 +1811,7 @@
</span><span class="cx">     if (m_optimizedStyleSheetUpdateTimer.isActive())
</span><span class="cx">         styleResolverChanged(RecalcStyleIfNeeded);
</span><span class="cx"> 
</span><del>-    if ((!m_pendingStyleRecalcShouldForce &amp;&amp; !childNeedsStyleRecalc()) || inPageCache())
</del><ins>+    if (!needsStyleRecalc())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     recalcStyle(Style::NoChange);
</span><span class="lines">@@ -2683,9 +2686,13 @@
</span><span class="cx">         // Element sheet is silly. It never contains anything.
</span><span class="cx">         ASSERT(!m_elementSheet-&gt;contents().ruleCount());
</span><span class="cx">         bool usesRemUnits = m_elementSheet-&gt;contents().usesRemUnits();
</span><ins>+        bool usesStyleBasedEditability = m_elementSheet-&gt;contents().usesStyleBasedEditability();
</ins><span class="cx">         m_elementSheet = CSSStyleSheet::createInline(*this, m_baseURL);
</span><span class="cx">         // FIXME: So we are not really the parser. The right fix is to eliminate the element sheet completely.
</span><del>-        m_elementSheet-&gt;contents().parserSetUsesRemUnits(usesRemUnits);
</del><ins>+        if (usesRemUnits)
+            m_elementSheet-&gt;contents().parserSetUsesRemUnits();
+        if (usesStyleBasedEditability)
+            m_elementSheet-&gt;contents().parserSetUsesStyleBasedEditability();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!equalIgnoringFragmentIdentifier(oldBaseURL, m_baseURL)) {
</span><span class="lines">@@ -2852,6 +2859,19 @@
</span><span class="cx">     return *m_elementSheet;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool Document::usesStyleBasedEditability() const
+{
+    if (m_elementSheet &amp;&amp; m_elementSheet-&gt;contents().usesStyleBasedEditability())
+        return true;
+
+    ASSERT(!m_renderView || !m_renderView-&gt;frameView().isPainting());
+    ASSERT(!m_inStyleRecalc);
+
+    auto&amp; collection = document().styleSheetCollection();
+    collection.flushPendingUpdates();
+    return collection.usesStyleBasedEditability();
+}
+
</ins><span class="cx"> void Document::processHttpEquiv(const String&amp; equiv, const String&amp; content)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!equiv.isNull() &amp;&amp; !content.isNull());
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/dom/Document.h        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -601,6 +601,7 @@
</span><span class="cx"> 
</span><span class="cx">     void recalcStyle(Style::Change = Style::NoChange);
</span><span class="cx">     WEBCORE_EXPORT void updateStyleIfNeeded();
</span><ins>+    bool needsStyleRecalc() const { return !inPageCache() &amp;&amp; (m_pendingStyleRecalcShouldForce || childNeedsStyleRecalc() || m_optimizedStyleSheetUpdateTimer.isActive()); }
</ins><span class="cx"> 
</span><span class="cx">     WEBCORE_EXPORT void updateLayout();
</span><span class="cx">     
</span><span class="lines">@@ -693,6 +694,7 @@
</span><span class="cx">     Frame* findUnsafeParentScrollPropagationBoundary();
</span><span class="cx"> 
</span><span class="cx">     CSSStyleSheet&amp; elementSheet();
</span><ins>+    bool usesStyleBasedEditability() const;
</ins><span class="cx">     
</span><span class="cx">     virtual Ref&lt;DocumentParser&gt; createParser();
</span><span class="cx">     DocumentParser* parser() const { return m_parser.get(); }
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentStyleSheetCollectioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/DocumentStyleSheetCollection.cpp (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/DocumentStyleSheetCollection.cpp        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/dom/DocumentStyleSheetCollection.cpp        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -60,6 +60,7 @@
</span><span class="cx">     , m_usesFirstLineRules(false)
</span><span class="cx">     , m_usesFirstLetterRules(false)
</span><span class="cx">     , m_usesRemUnits(false)
</span><ins>+    , m_usesStyleBasedEditability(false)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -390,15 +391,6 @@
</span><span class="cx">     requiresFullStyleRecalc = false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool styleSheetsUseRemUnits(const Vector&lt;RefPtr&lt;CSSStyleSheet&gt;&gt;&amp; sheets)
-{
-    for (unsigned i = 0; i &lt; sheets.size(); ++i) {
-        if (sheets[i]-&gt;contents().usesRemUnits())
-            return true;
-    }
-    return false;
-}
-
</del><span class="cx"> static void filterEnabledNonemptyCSSStyleSheets(Vector&lt;RefPtr&lt;CSSStyleSheet&gt;&gt;&amp; result, const Vector&lt;RefPtr&lt;StyleSheet&gt;&gt;&amp; sheets)
</span><span class="cx"> {
</span><span class="cx">     for (unsigned i = 0; i &lt; sheets.size(); ++i) {
</span><span class="lines">@@ -457,7 +449,12 @@
</span><span class="cx">     m_activeAuthorStyleSheets.swap(activeCSSStyleSheets);
</span><span class="cx">     m_styleSheetsForStyleSheetList.swap(activeStyleSheets);
</span><span class="cx"> 
</span><del>-    m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets);
</del><ins>+    for (const auto&amp; sheet : m_activeAuthorStyleSheets) {
+        if (sheet-&gt;contents().usesRemUnits())
+            m_usesRemUnits = true;
+        if (sheet-&gt;contents().usesStyleBasedEditability())
+            m_usesStyleBasedEditability = true;
+    }
</ins><span class="cx">     m_pendingUpdateType = NoUpdate;
</span><span class="cx"> 
</span><span class="cx">     return requiresFullStyleRecalc;
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentStyleSheetCollectionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/DocumentStyleSheetCollection.h (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/DocumentStyleSheetCollection.h        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/dom/DocumentStyleSheetCollection.h        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -105,6 +105,8 @@
</span><span class="cx">     bool usesFirstLetterRules() const { return m_usesFirstLetterRules; }
</span><span class="cx">     bool usesRemUnits() const { return m_usesRemUnits; }
</span><span class="cx">     void setUsesRemUnit(bool b) { m_usesRemUnits = b; }
</span><ins>+    bool usesStyleBasedEditability() { return m_usesStyleBasedEditability; }
+    void setUsesStyleBasedEditability(bool b) { m_usesStyleBasedEditability = b; }
</ins><span class="cx"> 
</span><span class="cx">     void combineCSSFeatureFlags();
</span><span class="cx">     void resetCSSFeatureFlags();
</span><span class="lines">@@ -157,6 +159,7 @@
</span><span class="cx">     bool m_usesFirstLineRules;
</span><span class="cx">     bool m_usesFirstLetterRules;
</span><span class="cx">     bool m_usesRemUnits;
</span><ins>+    bool m_usesStyleBasedEditability;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Node.cpp (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Node.cpp        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/dom/Node.cpp        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -71,6 +71,7 @@
</span><span class="cx"> #include &quot;Settings.h&quot;
</span><span class="cx"> #include &quot;StorageEvent.h&quot;
</span><span class="cx"> #include &quot;StyleResolver.h&quot;
</span><ins>+#include &quot;StyleSheetContents.h&quot;
</ins><span class="cx"> #include &quot;TemplateContentDocumentFragment.h&quot;
</span><span class="cx"> #include &quot;TextEvent.h&quot;
</span><span class="cx"> #include &quot;TouchEvent.h&quot;
</span><span class="lines">@@ -553,24 +554,13 @@
</span><span class="cx">         document().page()-&gt;inspectorController().inspect(this);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-Node::Editability Node::computeEditability(UserSelectAllTreatment treatment, ShouldUpdateStyle shouldUpdateStyle) const
</del><ins>+static Node::Editability computeEditabilityFromComputedStyle(const Node&amp; startNode, Node::UserSelectAllTreatment treatment)
</ins><span class="cx"> {
</span><del>-    if (shouldUpdateStyle == ShouldUpdateStyle::Update)
-        document().updateStyleIfNeeded();
-
-    if (!document().hasLivingRenderTree())
-        return Editability::ReadOnly;
-    if (document().frame() &amp;&amp; document().frame()-&gt;page() &amp;&amp; document().frame()-&gt;page()-&gt;isEditable() &amp;&amp; !containingShadowRoot())
-        return Editability::CanEditRichly;
-
-    if (isPseudoElement())
-        return Editability::ReadOnly;
-
</del><span class="cx">     // Ideally we'd call ASSERT(!needsStyleRecalc()) here, but
</span><span class="cx">     // ContainerNode::setFocus() calls setNeedsStyleRecalc(), so the assertion
</span><span class="cx">     // would fire in the middle of Document::setFocusedElement().
</span><span class="cx"> 
</span><del>-    for (const Node* node = this; node; node = node-&gt;parentNode()) {
</del><ins>+    for (const Node* node = &amp;startNode; node; node = node-&gt;parentNode()) {
</ins><span class="cx">         RenderStyle* style = node-&gt;isDocumentNode() ? node-&gt;renderStyle() : const_cast&lt;Node*&gt;(node)-&gt;computedStyle();
</span><span class="cx">         if (!style)
</span><span class="cx">             continue;
</span><span class="lines">@@ -579,23 +569,39 @@
</span><span class="cx"> #if ENABLE(USERSELECT_ALL)
</span><span class="cx">         // Elements with user-select: all style are considered atomic
</span><span class="cx">         // therefore non editable.
</span><del>-        if (treatment == UserSelectAllIsAlwaysNonEditable &amp;&amp; style-&gt;userSelect() == SELECT_ALL)
-            return Editability::ReadOnly;
</del><ins>+        if (treatment == Node::UserSelectAllIsAlwaysNonEditable &amp;&amp; style-&gt;userSelect() == SELECT_ALL)
+            return Node::Editability::ReadOnly;
</ins><span class="cx"> #else
</span><span class="cx">         UNUSED_PARAM(treatment);
</span><span class="cx"> #endif
</span><span class="cx">         switch (style-&gt;userModify()) {
</span><span class="cx">         case READ_ONLY:
</span><del>-            return Editability::ReadOnly;
</del><ins>+            return Node::Editability::ReadOnly;
</ins><span class="cx">         case READ_WRITE:
</span><del>-            return Editability::CanEditRichly;
</del><ins>+            return Node::Editability::CanEditRichly;
</ins><span class="cx">         case READ_WRITE_PLAINTEXT_ONLY:
</span><del>-            return Editability::CanEditPlainText;
</del><ins>+            return Node::Editability::CanEditPlainText;
</ins><span class="cx">         }
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><ins>+        return Node::Editability::ReadOnly;
+    }
+    return Node::Editability::ReadOnly;
+}
+
+Node::Editability Node::computeEditability(UserSelectAllTreatment treatment, ShouldUpdateStyle shouldUpdateStyle) const
+{
+    if (!document().hasLivingRenderTree() || isPseudoElement())
</ins><span class="cx">         return Editability::ReadOnly;
</span><ins>+
+    if (document().frame() &amp;&amp; document().frame()-&gt;page() &amp;&amp; document().frame()-&gt;page()-&gt;isEditable() &amp;&amp; !containingShadowRoot())
+        return Editability::CanEditRichly;
+
+    if (shouldUpdateStyle == ShouldUpdateStyle::Update &amp;&amp; document().needsStyleRecalc()) {
+        if (!document().usesStyleBasedEditability())
+            return HTMLElement::editabilityFromContentEditableAttr(*this);
+        document().updateStyleIfNeeded();
</ins><span class="cx">     }
</span><del>-    return Editability::ReadOnly;
</del><ins>+    return computeEditabilityFromComputedStyle(*this, treatment);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> RenderBox* Node::renderBox() const
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLElement.cpp (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLElement.cpp        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/html/HTMLElement.cpp        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -367,30 +367,37 @@
</span><span class="cx">         map.add(customTable[i].attributeName.localName().impl(), customTable[i].eventName);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool HTMLElement::matchesReadWritePseudoClass() const
</del><ins>+Node::Editability HTMLElement::editabilityFromContentEditableAttr(const Node&amp; node)
</ins><span class="cx"> {
</span><del>-    const Element* currentElement = this;
</del><ins>+    const Node* currentNode = &amp;node;
</ins><span class="cx">     do {
</span><del>-        if (is&lt;HTMLElement&gt;(*currentElement)) {
-            switch (contentEditableType(downcast&lt;HTMLElement&gt;(*currentElement))) {
</del><ins>+        if (is&lt;HTMLElement&gt;(*currentNode)) {
+            switch (contentEditableType(downcast&lt;HTMLElement&gt;(*currentNode))) {
</ins><span class="cx">             case ContentEditableType::True:
</span><ins>+                return Node::Editability::CanEditRichly;
</ins><span class="cx">             case ContentEditableType::PlaintextOnly:
</span><del>-                return true;
</del><ins>+                return Node::Editability::CanEditPlainText;
</ins><span class="cx">             case ContentEditableType::False:
</span><del>-                return false;
</del><ins>+                return Node::Editability::ReadOnly;
</ins><span class="cx">             case ContentEditableType::Inherit:
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">         }
</span><del>-        currentElement = currentElement-&gt;parentElement();
-    } while (currentElement);
</del><ins>+        currentNode = currentNode-&gt;parentNode();
+    } while (currentNode);
</ins><span class="cx"> 
</span><del>-    const Document&amp; document = this-&gt;document();
</del><ins>+    const Document&amp; document = node.document();
</ins><span class="cx">     if (is&lt;HTMLDocument&gt;(document))
</span><del>-        return downcast&lt;HTMLDocument&gt;(document).inDesignMode();
-    return false;
</del><ins>+        return downcast&lt;HTMLDocument&gt;(document).inDesignMode() ? Node::Editability::CanEditRichly : Node::Editability::ReadOnly;
+
+    return Node::Editability::ReadOnly;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool HTMLElement::matchesReadWritePseudoClass() const
+{
+    return editabilityFromContentEditableAttr(*this) != Editability::ReadOnly;
+}
+
</ins><span class="cx"> void HTMLElement::parseAttribute(const QualifiedName&amp; name, const AtomicString&amp; value)
</span><span class="cx"> {
</span><span class="cx">     if (name == HTMLNames::idAttr || name == HTMLNames::classAttr || name == HTMLNames::styleAttr)
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLElement.h (180866 => 180867)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLElement.h        2015-03-01 19:48:55 UTC (rev 180866)
+++ trunk/Source/WebCore/html/HTMLElement.h        2015-03-01 19:52:21 UTC (rev 180867)
</span><span class="lines">@@ -61,6 +61,8 @@
</span><span class="cx">     String contentEditable() const;
</span><span class="cx">     void setContentEditable(const String&amp;, ExceptionCode&amp;);
</span><span class="cx"> 
</span><ins>+    static Editability editabilityFromContentEditableAttr(const Node&amp;);
+
</ins><span class="cx">     virtual bool draggable() const;
</span><span class="cx">     void setDraggable(bool);
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>