<!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>[186165] 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/186165">186165</a></dd>
<dt>Author</dt> <dd>zalan@apple.com</dd>
<dt>Date</dt> <dd>2015-06-30 21:30:52 -0700 (Tue, 30 Jun 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Frame flattening: Hit-testing an iframe could end up destroying the associated inline tree context.
https://bugs.webkit.org/show_bug.cgi?id=146447
rdar://problem/20613501

Reviewed by Simon Fraser.

This patch ensures that the render tree associated with the document on which
the hit-test is initiated does not get laid out, unless it was directly mutated prior to the hittest.

Hit-test requirements:
1. A clean the render tree before hit-testing gets propagated to the renderers.
Document::updateLayout() ensures it by calling both updateStyleIfNeeded() and layout() not only on the current tree, but also
on the ancestors if needed.

2. No render tree mutation while hit-testing the renderers.

When an iframe is being hit-tested, this hit-test could bubble down to the child frame's render view.
In order to ensure #1, we call Document::updateLayout() on the current (subframe) document.
If updateStyleIfNeeded() mutates the render tree, we mark it dirty for layout(). However frame flattening also
marks the parent renderer (RenderIFrame) dirty.
While calling layout() to clean the current render tree, we end up laying out the parent tree too.
Laying out the parent tree could end up destroying the inline tree context from where the
hittest just bubbled down. (InlineFlowBox -&gt; RenderWidget -&gt; RenderView).

This patch protects the render tree from such unintentional inline tree mutation during hittesting.
After the initial layout we set a layout disallow flag on the frame view to defer subsequent layouts.
This patch only changes behavior when frame flattening is enabled, but in future we may always want to enable this.

Source/WebCore:

Test: fast/frames/flattening/hittest-iframe-while-style-changes-crash.html

* page/FrameView.cpp:
(WebCore::FrameView::layout):
(WebCore::FrameView::startLayoutAtMainFrameViewIfNeeded): Deleted. -&gt; Assertion in no longer valid.
* page/FrameView.h:
* rendering/RenderView.cpp:
(WebCore::FrameFlatteningLayoutDisallower::FrameFlatteningLayoutDisallower):
(WebCore::FrameFlatteningLayoutDisallower::~FrameFlatteningLayoutDisallower):
(WebCore::RenderView::hitTest): Protect the render tree from subsequent layouts.

LayoutTests:

* fast/frames/flattening/hittest-iframe-while-style-changes-crash-expected.txt: Added.
* fast/frames/flattening/hittest-iframe-while-style-changes-crash.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorepageFrameViewcpp">trunk/Source/WebCore/page/FrameView.cpp</a></li>
<li><a href="#trunkSourceWebCorepageFrameViewh">trunk/Source/WebCore/page/FrameView.h</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderViewcpp">trunk/Source/WebCore/rendering/RenderView.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastframesflatteninghittestiframewhilestylechangescrashexpectedtxt">trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastframesflatteninghittestiframewhilestylechangescrashhtml">trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (186164 => 186165)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-07-01 02:39:50 UTC (rev 186164)
+++ trunk/LayoutTests/ChangeLog        2015-07-01 04:30:52 UTC (rev 186165)
</span><span class="lines">@@ -1,3 +1,36 @@
</span><ins>+2015-06-30  Zalan Bujtas  &lt;zalan@apple.com&gt;
+
+        Frame flattening: Hit-testing an iframe could end up destroying the associated inline tree context.
+        https://bugs.webkit.org/show_bug.cgi?id=146447
+        rdar://problem/20613501
+
+        Reviewed by Simon Fraser.
+
+        This patch ensures that the render tree associated with the document on which
+        the hit-test is initiated does not get laid out, unless it was directly mutated prior to the hittest.
+
+        Hit-test requirements:
+        1. A clean the render tree before hit-testing gets propagated to the renderers.
+        Document::updateLayout() ensures it by calling both updateStyleIfNeeded() and layout() not only on the current tree, but also
+        on the ancestors if needed.
+
+        2. No render tree mutation while hit-testing the renderers.
+
+        When an iframe is being hit-tested, this hit-test could bubble down to the child frame's render view.
+        In order to ensure #1, we call Document::updateLayout() on the current (subframe) document.
+        If updateStyleIfNeeded() mutates the render tree, we mark it dirty for layout(). However frame flattening also
+        marks the parent renderer (RenderIFrame) dirty.
+        While calling layout() to clean the current render tree, we end up laying out the parent tree too.
+        Laying out the parent tree could end up destroying the inline tree context from where the
+        hittest just bubbled down. (InlineFlowBox -&gt; RenderWidget -&gt; RenderView).
+
+        This patch protects the render tree from such unintentional inline tree mutation during hittesting.
+        After the initial layout we set a layout disallow flag on the frame view to defer subsequent layouts.
+        This patch only changes behavior when frame flattening is enabled, but in future we may always want to enable this.
+
+        * fast/frames/flattening/hittest-iframe-while-style-changes-crash-expected.txt: Added.
+        * fast/frames/flattening/hittest-iframe-while-style-changes-crash.html: Added.
+
</ins><span class="cx"> 2015-06-30  Andy VanWagoner  &lt;thetalecrafter@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Implement ECMAScript Internationalization API
</span></span></pre></div>
<a id="trunkLayoutTestsfastframesflatteninghittestiframewhilestylechangescrashexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash-expected.txt (0 => 186165)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash-expected.txt        2015-07-01 04:30:52 UTC (rev 186165)
</span><span class="lines">@@ -0,0 +1 @@
</span><ins>+Pass if no crash or assert in debug. 
</ins></span></pre></div>
<a id="trunkLayoutTestsfastframesflatteninghittestiframewhilestylechangescrashhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash.html (0 => 186165)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash.html                                (rev 0)
+++ trunk/LayoutTests/fast/frames/flattening/hittest-iframe-while-style-changes-crash.html        2015-07-01 04:30:52 UTC (rev 186165)
</span><span class="lines">@@ -0,0 +1,27 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;title&gt;This tests that hittesting an iframe when frame flattening is on does not crash.&lt;/title&gt;
+&lt;script&gt;
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+if (window.internals)
+    internals.settings.setFrameFlatteningEnabled(true);
+
+function runTest() {
+    setTimeout(function() {
+        document.getElementById('clickonthis').contentDocument.getElementById('foo').style.display = &quot;none&quot;;
+        if (window.internals)
+            internals.nodesFromRect(document, 100, 100, 0, 0, 0, 0, false, false, true);
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }, 0);
+}
+&lt;/script&gt;
+&lt;body&gt;
+Pass if no crash or assert in debug.
+&lt;iframe onload=&quot;runTest()&quot; id=clickonthis src=&quot;data:text/html,&lt;div id=foo&gt;foobar&lt;/div&gt;&quot;&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (186164 => 186165)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-07-01 02:39:50 UTC (rev 186164)
+++ trunk/Source/WebCore/ChangeLog        2015-07-01 04:30:52 UTC (rev 186165)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2015-06-30  Zalan Bujtas  &lt;zalan@apple.com&gt;
+
+        Frame flattening: Hit-testing an iframe could end up destroying the associated inline tree context.
+        https://bugs.webkit.org/show_bug.cgi?id=146447
+        rdar://problem/20613501
+
+        Reviewed by Simon Fraser.
+
+        This patch ensures that the render tree associated with the document on which
+        the hit-test is initiated does not get laid out, unless it was directly mutated prior to the hittest.
+
+        Hit-test requirements:
+        1. A clean the render tree before hit-testing gets propagated to the renderers.
+        Document::updateLayout() ensures it by calling both updateStyleIfNeeded() and layout() not only on the current tree, but also
+        on the ancestors if needed.
+
+        2. No render tree mutation while hit-testing the renderers.
+
+        When an iframe is being hit-tested, this hit-test could bubble down to the child frame's render view.
+        In order to ensure #1, we call Document::updateLayout() on the current (subframe) document.
+        If updateStyleIfNeeded() mutates the render tree, we mark it dirty for layout(). However frame flattening also
+        marks the parent renderer (RenderIFrame) dirty.
+        While calling layout() to clean the current render tree, we end up laying out the parent tree too.
+        Laying out the parent tree could end up destroying the inline tree context from where the
+        hittest just bubbled down. (InlineFlowBox -&gt; RenderWidget -&gt; RenderView).
+
+        This patch protects the render tree from such unintentional inline tree mutation during hittesting.
+        After the initial layout we set a layout disallow flag on the frame view to defer subsequent layouts.
+        This patch only changes behavior when frame flattening is enabled, but in future we may always want to enable this.
+
+        Test: fast/frames/flattening/hittest-iframe-while-style-changes-crash.html
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::layout):
+        (WebCore::FrameView::startLayoutAtMainFrameViewIfNeeded): Deleted. -&gt; Assertion in no longer valid.
+        * page/FrameView.h:
+        * rendering/RenderView.cpp:
+        (WebCore::FrameFlatteningLayoutDisallower::FrameFlatteningLayoutDisallower):
+        (WebCore::FrameFlatteningLayoutDisallower::~FrameFlatteningLayoutDisallower):
+        (WebCore::RenderView::hitTest): Protect the render tree from subsequent layouts.
+
</ins><span class="cx"> 2015-06-30  Andy VanWagoner  &lt;thetalecrafter@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Implement ECMAScript Internationalization API
</span></span></pre></div>
<a id="trunkSourceWebCorepageFrameViewcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/FrameView.cpp (186164 => 186165)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/FrameView.cpp        2015-07-01 02:39:50 UTC (rev 186164)
+++ trunk/Source/WebCore/page/FrameView.cpp        2015-07-01 04:30:52 UTC (rev 186165)
</span><span class="lines">@@ -1140,6 +1140,9 @@
</span><span class="cx">     if (isInLayout())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><ins>+    if (layoutDisallowed())
+        return;
+
</ins><span class="cx">     // Protect the view from being deleted during layout (in recalcStyle).
</span><span class="cx">     Ref&lt;FrameView&gt; protect(*this);
</span><span class="cx"> 
</span><span class="lines">@@ -3763,9 +3766,6 @@
</span><span class="cx">         parentView = parentView-&gt;parentFrameView();
</span><span class="cx"> 
</span><span class="cx">     parentView-&gt;layout(allowSubtree);
</span><del>-
-    RenderElement* root = m_layoutRoot ? m_layoutRoot : frame().document()-&gt;renderView();
-    ASSERT_UNUSED(root, !root-&gt;needsLayout());
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void FrameView::updateControlTints()
</span></span></pre></div>
<a id="trunkSourceWebCorepageFrameViewh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/FrameView.h (186164 => 186165)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/FrameView.h        2015-07-01 02:39:50 UTC (rev 186164)
+++ trunk/Source/WebCore/page/FrameView.h        2015-07-01 04:30:52 UTC (rev 186165)
</span><span class="lines">@@ -365,6 +365,10 @@
</span><span class="cx"> 
</span><span class="cx">     bool isInChildFrameWithFrameFlattening() const;
</span><span class="cx"> 
</span><ins>+    void startDisallowingLayout() { ++m_layoutDisallowed; }
+    void endDisallowingLayout() { ASSERT(m_layoutDisallowed &gt; 0); --m_layoutDisallowed; }
+    bool layoutDisallowed() const { return m_layoutDisallowed; }
+
</ins><span class="cx">     static double currentPaintTimeStamp() { return sCurrentPaintTimeStamp; } // returns 0 if not painting
</span><span class="cx">     
</span><span class="cx">     WEBCORE_EXPORT void updateLayoutAndStyleIfNeededRecursive();
</span><span class="lines">@@ -736,6 +740,7 @@
</span><span class="cx"> 
</span><span class="cx">     unsigned m_deferSetNeedsLayoutCount;
</span><span class="cx">     bool m_setNeedsLayoutWasDeferred;
</span><ins>+    int m_layoutDisallowed { 0 };
</ins><span class="cx"> 
</span><span class="cx">     RefPtr&lt;Node&gt; m_nodeToDraw;
</span><span class="cx">     PaintBehavior m_paintBehavior;
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderViewcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderView.cpp (186164 => 186165)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderView.cpp        2015-07-01 02:39:50 UTC (rev 186164)
+++ trunk/Source/WebCore/rendering/RenderView.cpp        2015-07-01 04:30:52 UTC (rev 186165)
</span><span class="lines">@@ -54,6 +54,26 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+struct FrameFlatteningLayoutDisallower {
+    FrameFlatteningLayoutDisallower(FrameView&amp; frameView)
+        : m_frameView(frameView)
+        , m_disallowLayout(frameView.frame().settings().frameFlatteningEnabled())
+    {
+        if (m_disallowLayout)
+            m_frameView.startDisallowingLayout();
+    }
+
+    ~FrameFlatteningLayoutDisallower()
+    {
+        if (m_disallowLayout)
+            m_frameView.endDisallowingLayout();
+    }
+
+private:
+    FrameView&amp; m_frameView;
+    bool m_disallowLayout { false };
+};
+
</ins><span class="cx"> struct SelectionIterator {
</span><span class="cx">     RenderObject* m_current;
</span><span class="cx">     Vector&lt;RenderMultiColumnSpannerPlaceholder*&gt; m_spannerStack;
</span><span class="lines">@@ -176,6 +196,8 @@
</span><span class="cx"> {
</span><span class="cx">     document().updateLayout();
</span><span class="cx"> 
</span><ins>+    FrameFlatteningLayoutDisallower disallower(frameView());
+
</ins><span class="cx">     if (layer()-&gt;hitTest(request, location, result))
</span><span class="cx">         return true;
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>