Diff
Modified: trunk/LayoutTests/ChangeLog (209627 => 209628)
--- trunk/LayoutTests/ChangeLog 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/LayoutTests/ChangeLog 2016-12-09 22:06:29 UTC (rev 209628)
@@ -1,3 +1,31 @@
+2016-12-09 Ryosuke Niwa <rn...@webkit.org>
+
+ document.webkitFullscreenElement leaks elements inside a shadow tree
+ https://bugs.webkit.org/show_bug.cgi?id=158471
+
+ Reviewed by Chris Dumez.
+
+ Added tests for calling webkitFullscreenElement and webkitCurrentFullScreenElement on a fullscreened element
+ to make sure they return the shadow host instead.
+
+ Also added two unrelated test cases for temporal regressions I introduced while working on this patch.
+
+ Skip the fullscreen tests on iOS WK2 since eventSender doesn't work there.
+
+ * fast/shadow-dom/activeElement-for-focused-element-in-another-shadow-expected.txt: Added.
+ * fast/shadow-dom/activeElement-for-focused-element-in-another-shadow.html: Added.
+ * fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content-expected.txt: Added.
+ * fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content.html: Added.
+ * fast/shadow-dom/fullscreen-in-shadow-fullscreenElement-expected.txt: Added.
+ * fast/shadow-dom/fullscreen-in-shadow-fullscreenElement.html: Added.
+ * fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement-expected.txt: Added.
+ * fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement.html: Added.
+ * fast/shadow-dom/fullscreen-in-slot-fullscreenElement-expected.txt: Added.
+ * fast/shadow-dom/fullscreen-in-slot-fullscreenElement.html: Added.
+ * fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement-expected.txt: Added.
+ * fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement.html: Added.
+ * platform/ios-simulator-wk2/TestExpectations:
+
2016-12-09 Chris Dumez <cdu...@apple.com>
[Cocoa] Validation message for required checkbox doesn’t conform the the Apple Style Guide
Added: trunk/LayoutTests/fast/shadow-dom/activeElement-for-focused-element-in-another-shadow-expected.txt (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/activeElement-for-focused-element-in-another-shadow-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/activeElement-for-focused-element-in-another-shadow-expected.txt 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,4 @@
+
+PASS ShadowRoot.activeElement must return null the focused element is in another shadow tree when both shadow roots are open.
+PASS ShadowRoot.activeElement must return null the focused element is in another shadow tree when both shadow roots are closed.
+
Added: trunk/LayoutTests/fast/shadow-dom/activeElement-for-focused-element-in-another-shadow.html (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/activeElement-for-focused-element-in-another-shadow.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/activeElement-for-focused-element-in-another-shadow.html 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="ShadowRoot's ">
+<link rel="help" href=""
+<script src=""
+<script src=""
+<script src=""
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+function testActiveElementWithFocusedElementInAnotherShadow(mode) {
+ test(function () {
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ const shadowRoot = host.attachShadow({'mode': mode});
+ const input = document.createElement('input');
+ shadowRoot.appendChild(input);
+ assert_equals(shadowRoot.activeElement, null);
+ input.focus();
+ assert_equals(shadowRoot.activeElement, input);
+ assert_equals(document.activeElement, host);
+
+ const anotherHost = document.createElement('div');
+ document.body.appendChild(anotherHost);
+ const anotherShadowRoot = anotherHost.attachShadow({'mode': mode});
+ const anotherInput = document.createElement('input');
+ anotherShadowRoot.appendChild(anotherInput);
+
+ assert_equals(anotherShadowRoot.activeElement, null);
+ assert_equals(shadowRoot.activeElement, input);
+ assert_equals(document.activeElement, host);
+
+ anotherInput.focus();
+ assert_equals(anotherShadowRoot.activeElement, anotherInput);
+ assert_equals(shadowRoot.activeElement, null);
+ assert_equals(document.activeElement, anotherHost);
+
+ }, 'ShadowRoot.activeElement must return null the focused element is in another shadow tree when both shadow roots are ' + mode + '.');
+}
+
+testActiveElementWithFocusedElementInAnotherShadow('open');
+testActiveElementWithFocusedElementInAnotherShadow('closed');
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content-expected.txt (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content-expected.txt 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,12 @@
+Test that calling blur() on a shadow host of a focused element clears the focus.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS input = shadowRoot.querySelector("input"); input.focus(); shadowRoot.activeElement is input
+PASS document.activeElement is shadowHost
+PASS shadowHost.blur(); shadowRoot.activeElement is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content.html (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content.html 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="host"></div>
+<script src=""
+<script>
+
+description('Test that calling blur() on a shadow host of a focused element clears the focus.');
+
+let shadowHost = document.getElementById('host');
+let shadowRoot = shadowHost.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = `<input>`;
+
+shouldBe('input = shadowRoot.querySelector("input"); input.focus(); shadowRoot.activeElement', 'input');
+shouldBe('document.activeElement', 'shadowHost');
+shouldBe('shadowHost.blur(); shadowRoot.activeElement', 'null');
+
+var successfullyParsed = true;
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-fullscreenElement-expected.txt (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-fullscreenElement-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-fullscreenElement-expected.txt 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,10 @@
+Test that webkitFullscreenElement retargets the fullscreen element inside a shadow tree.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.webkitFullscreenElement is shadowHost
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Enter fullscreen
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-fullscreenElement.html (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-fullscreenElement.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-fullscreenElement.html 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="host"></div>
+<button>Enter fullscreen</button>
+<script src=""
+<script>
+
+description('Test that webkitFullscreenElement retargets the fullscreen element inside a shadow tree.');
+
+let shadowHost = document.getElementById('host');
+let shadowRoot = shadowHost.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<span>full screen content</span>';
+
+function goFullscreen() {
+ shadowRoot.querySelector('span').webkitRequestFullscreen();
+ setTimeout(function () {
+ if (done)
+ return;
+
+ testFailed('webkitfullscreenchange was not fired');
+ finishJSTest();
+ }, 2000);
+}
+
+let done = false;
+function finalizeTest() {
+ if (done)
+ return;
+ done = true;
+
+ shouldBe('document.webkitFullscreenElement', 'shadowHost');
+ finishJSTest();
+}
+
+shadowRoot.addEventListener('webkitfullscreenchange', finalizeTest);
+document.addEventListener('fullscreenchange', finalizeTest); // Standard fullscreenchange only fires at document level.
+
+let button = document.querySelector('button');
+button._onclick_ = goFullscreen;
+
+if (window.eventSender) {
+ jsTestIsAsync = true;
+ eventSender.mouseMoveTo(button.offsetLeft + 5, button.offsetTop + 5);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+}
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement-expected.txt (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement-expected.txt 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,10 @@
+Test that webkitCurrentFullScreenElement retargets the fullscreen element inside a shadow tree.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.webkitCurrentFullScreenElement is shadowHost
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Enter fullscreen
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement.html (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement.html 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="host"></div>
+<button>Enter fullscreen</button>
+<script src=""
+<script>
+
+description('Test that webkitCurrentFullScreenElement retargets the fullscreen element inside a shadow tree.');
+
+let shadowHost = document.getElementById('host');
+let shadowRoot = shadowHost.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<span>full screen content</span>';
+
+function goFullscreen() {
+ shadowRoot.querySelector('span').webkitRequestFullScreen();
+ setTimeout(function () {
+ if (done)
+ return;
+
+ testFailed('webkitfullscreenchange was not fired');
+ finishJSTest();
+ }, 2000);
+}
+
+let done = false;
+function finalizeTest() {
+ if (done)
+ return;
+ done = true;
+
+ shouldBe('document.webkitCurrentFullScreenElement', 'shadowHost');
+ finishJSTest();
+}
+
+shadowRoot.addEventListener('webkitfullscreenchange', finalizeTest);
+document.addEventListener('fullscreenchange', finalizeTest); // Standard fullscreenchange only fires at document level.
+
+let button = document.querySelector('button');
+button._onclick_ = goFullscreen;
+
+if (window.eventSender) {
+ jsTestIsAsync = true;
+ eventSender.mouseMoveTo(button.offsetLeft + 5, button.offsetTop + 5);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+}
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-fullscreenElement-expected.txt (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-fullscreenElement-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-fullscreenElement-expected.txt 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,11 @@
+Test that webkitFullscreenElement retargets the fullscreen element assigned to a slot.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.webkitFullscreenElement is document.querySelector("section")
+PASS successfullyParsed is true
+
+TEST COMPLETE
+full screen content
+Enter fullscreen
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-fullscreenElement.html (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-fullscreenElement.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-fullscreenElement.html 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="host"><section>full screen content</section></div>
+<button>Enter fullscreen</button>
+<script src=""
+<script>
+
+description('Test that webkitFullscreenElement retargets the fullscreen element assigned to a slot.');
+
+let shadowHost = document.getElementById('host');
+let shadowRoot = shadowHost.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<span><slot></slot></span>';
+
+function goFullscreen() {
+ document.querySelector('section').webkitRequestFullscreen();
+ setTimeout(function () {
+ if (done)
+ return;
+
+ testFailed('webkitfullscreenchange was not fired');
+ finishJSTest();
+ }, 2000);
+}
+
+let done = false;
+function finalizeTest() {
+ if (done)
+ return;
+ done = true;
+
+ shouldBe('document.webkitFullscreenElement', 'document.querySelector("section")');
+ finishJSTest();
+}
+
+shadowRoot.addEventListener('webkitfullscreenchange', finalizeTest);
+document.addEventListener('fullscreenchange', finalizeTest); // Standard fullscreenchange only fires at document level.
+
+let button = document.querySelector('button');
+button._onclick_ = goFullscreen;
+
+if (window.eventSender) {
+ jsTestIsAsync = true;
+ eventSender.mouseMoveTo(button.offsetLeft + 5, button.offsetTop + 5);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+}
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement-expected.txt (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement-expected.txt 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,11 @@
+Test that webkitCurrentFullScreenElement retargets the fullscreen element assigned to a slot.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.webkitCurrentFullScreenElement is document.querySelector("section")
+PASS successfullyParsed is true
+
+TEST COMPLETE
+full screen content
+Enter fullscreen
Added: trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement.html (0 => 209628)
--- trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement.html 2016-12-09 22:06:29 UTC (rev 209628)
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="host"><section>full screen content</section></div>
+<button>Enter fullscreen</button>
+<script src=""
+<script>
+
+description('Test that webkitCurrentFullScreenElement retargets the fullscreen element assigned to a slot.');
+
+let shadowHost = document.getElementById('host');
+let shadowRoot = shadowHost.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<span><slot></slot></span>';
+
+function goFullscreen() {
+ document.querySelector('section').webkitRequestFullScreen();
+ setTimeout(function () {
+ if (done)
+ return;
+
+ testFailed('webkitfullscreenchange was not fired');
+ finishJSTest();
+ }, 2000);
+}
+
+let done = false;
+function finalizeTest() {
+ if (done)
+ return;
+ done = true;
+
+ shouldBe('document.webkitCurrentFullScreenElement', 'document.querySelector("section")');
+ finishJSTest();
+}
+
+shadowRoot.addEventListener('webkitfullscreenchange', finalizeTest);
+document.addEventListener('fullscreenchange', finalizeTest); // Standard fullscreenchange only fires at document level.
+
+let button = document.querySelector('button');
+button._onclick_ = goFullscreen;
+
+if (window.eventSender) {
+ jsTestIsAsync = true;
+ eventSender.mouseMoveTo(button.offsetLeft + 5, button.offsetTop + 5);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+}
+
+</script>
+</body>
+</html>
Modified: trunk/LayoutTests/platform/ios-simulator-wk2/TestExpectations (209627 => 209628)
--- trunk/LayoutTests/platform/ios-simulator-wk2/TestExpectations 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/LayoutTests/platform/ios-simulator-wk2/TestExpectations 2016-12-09 22:06:29 UTC (rev 209628)
@@ -1778,6 +1778,10 @@
imported/blink/editing/selection/selectstart-event-crash.html [ Skip ]
fast/dom/Window/post-message-user-action.html [ Skip ]
fast/shadow-dom/click-text-inside-linked-slot.html [ Skip ]
+fast/shadow-dom/fullscreen-in-shadow-fullscreenElement.html
+fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement.html
+fast/shadow-dom/fullscreen-in-slot-fullscreenElement.html
+fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement.html
# No touch events
http/tests/contentdispositionattachmentsandbox/referer-header-stripped-with-meta-referer-always.html [ Skip ]
Modified: trunk/Source/WebCore/ChangeLog (209627 => 209628)
--- trunk/Source/WebCore/ChangeLog 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/ChangeLog 2016-12-09 22:06:29 UTC (rev 209628)
@@ -1,3 +1,73 @@
+2016-12-09 Ryosuke Niwa <rn...@webkit.org>
+
+ document.webkitFullscreenElement leaks elements inside a shadow tree
+ https://bugs.webkit.org/show_bug.cgi?id=158471
+
+ Reviewed by Chris Dumez.
+
+ Fixed the bug by calling the newly added ancestorElementInThisScope in webkitCurrentFullScreenElementForBindings
+ and webkitFullscreenElementForBinding.
+
+ The specification (https://fullscreen.spec.whatwg.org/#dom-document-fullscreenelement) uses "the result of
+ retargeting fullscreen element" and returns null if the result is not in the same tree as the context object.
+
+ This is equivalent to the algorithm implemented by ancestorElementInThisScope. Observe that the retargeting
+ algorithm (https://dom.spec.whatwg.org/#retarget) finds the lowest common tree scope of the retargetee and
+ the context object. There are two cases to consider.
+
+ 1. The context object's tree scope is the lowest common tree scope: In this case, an ancestor shadow host or
+ the retargetee itself is in this tree scope. It's sufficient traverse every shadow host to find the one that
+ resides in the same tree scope as the context object. This is precisely what ancestorElementInThisScope does.
+
+ 2. The context object's tree scope is not the lowest common tree scope: In this case, the context object is
+ inside a shadow tree whose ancestor shadow host is in the lowest common tree scope. In this case, retargeting
+ algorithm finds a node which is not in the same tree as the context object. Thus, the result is null.
+ ancestorElementInThisScope traveres ancestor shadow hosts and returns null if no shadow host's tree scope
+ matches that of the context object's tree scope. Thus, it would return null in this case as desired.
+
+ Also renamed TreeScope::focusedElement to focusedElementInScope for clarity since Document which inherits
+ from TreeScope also has a distinct member function named focusedElement called by TreeScope::focusedElement,
+ and used ancestorElementInThisScope since it uses the same algorithm.
+
+ Tests: fast/shadow-dom/activeElement-for-focused-element-in-another-shadow.html
+ fast/shadow-dom/blur-on-shadow-host-with-focused-shadow-content.html
+ fast/shadow-dom/fullscreen-in-shadow-fullscreenElement.html
+ fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement.html
+ fast/shadow-dom/fullscreen-in-slot-fullscreenElement.html
+ fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement.html
+
+ * dom/Document.cpp:
+ (WebCore::Document::removeFocusedNodeOfSubtree):
+ (WebCore::Document::activeElement):
+ * dom/Document.h:
+ (WebCore::Document::webkitCurrentFullScreenElementForBindings): Added.
+ (WebCore::Document::webkitFullscreenElementForBindings): Added.
+ * dom/Document.idl:
+ * dom/Element.cpp:
+ (WebCore::Element::blur):
+ * dom/ShadowRoot.h:
+ (WebCore::ShadowRoot::activeElement):
+ * dom/TreeScope.cpp:
+ (WebCore::TreeScope::ancestorNodeInThisScope): Renamed from ancestorInThisScope for clarity.
+ (WebCore::TreeScope::ancestorElementInThisScope):
+ (WebCore::TreeScope::focusedElementInScope): Renamed from focusedElement to disambiguate it from Document's
+ focusedElement.
+ * dom/TreeScope.h:
+ * editing/VisibleSelection.cpp:
+ (WebCore::adjustPositionForEnd):
+ (WebCore::adjustPositionForStart):
+ * editing/htmlediting.cpp:
+ (WebCore::comparePositions):
+ (WebCore::firstEditablePositionAfterPositionInRoot):
+ (WebCore::lastEditablePositionBeforePositionInRoot):
+ * page/DOMSelection.cpp:
+ (WebCore::selectionShadowAncestor):
+ (WebCore::DOMSelection::shadowAdjustedNode):
+ (WebCore::DOMSelection::shadowAdjustedOffset):
+ * rendering/HitTestResult.cpp:
+ (WebCore::HitTestResult::addNodeToRectBasedTestResult): Added a FIXME here since this is clearly wrong for
+ shadow trees created by author scripts.
+
2016-12-09 Geoffrey Garen <gga...@apple.com>
TextPosition and OrdinalNumber should be more like idiomatic numbers
Modified: trunk/Source/WebCore/dom/Document.cpp (209627 => 209628)
--- trunk/Source/WebCore/dom/Document.cpp 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/dom/Document.cpp 2016-12-09 22:06:29 UTC (rev 209628)
@@ -3540,7 +3540,7 @@
if (!m_focusedElement || pageCacheState() != NotInPageCache) // If the document is in the page cache, then we don't need to clear out the focused node.
return;
- Element* focusedElement = node.treeScope().focusedElement();
+ Element* focusedElement = node.treeScope().focusedElementInScope();
if (!focusedElement)
return;
@@ -6771,7 +6771,7 @@
Element* Document::activeElement()
{
- if (Element* element = treeScope().focusedElement())
+ if (Element* element = treeScope().focusedElementInScope())
return element;
return bodyOrFrameset();
}
Modified: trunk/Source/WebCore/dom/Document.h (209627 => 209628)
--- trunk/Source/WebCore/dom/Document.h 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/dom/Document.h 2016-12-09 22:06:29 UTC (rev 209628)
@@ -1088,7 +1088,8 @@
bool webkitIsFullScreen() const { return m_fullScreenElement.get(); }
bool webkitFullScreenKeyboardInputAllowed() const { return m_fullScreenElement.get() && m_areKeysEnabledInFullScreen; }
Element* webkitCurrentFullScreenElement() const { return m_fullScreenElement.get(); }
-
+ Element* webkitCurrentFullScreenElementForBindings() const { return ancestorElementInThisScope(webkitCurrentFullScreenElement()); }
+
enum FullScreenCheckType {
EnforceIFrameAllowFullScreenRequirement,
ExemptIFrameAllowFullScreenRequirement,
@@ -1115,6 +1116,7 @@
WEBCORE_EXPORT bool webkitFullscreenEnabled() const;
Element* webkitFullscreenElement() const { return !m_fullScreenElementStack.isEmpty() ? m_fullScreenElementStack.last().get() : nullptr; }
+ Element* webkitFullscreenElementForBindings() const { return ancestorElementInThisScope(webkitFullscreenElement()); }
WEBCORE_EXPORT void webkitExitFullscreen();
#endif
Modified: trunk/Source/WebCore/dom/Document.idl (209627 => 209628)
--- trunk/Source/WebCore/dom/Document.idl 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/dom/Document.idl 2016-12-09 22:06:29 UTC (rev 209628)
@@ -143,12 +143,12 @@
// Mozilla version
readonly attribute boolean webkitIsFullScreen;
readonly attribute boolean webkitFullScreenKeyboardInputAllowed;
- readonly attribute Element webkitCurrentFullScreenElement;
+ [ImplementedAs=webkitCurrentFullScreenElementForBindings] readonly attribute Element webkitCurrentFullScreenElement;
void webkitCancelFullScreen();
// W3C version
readonly attribute boolean webkitFullscreenEnabled;
- readonly attribute Element? webkitFullscreenElement;
+ [ImplementedAs=webkitFullscreenElementForBindings] readonly attribute Element? webkitFullscreenElement;
void webkitExitFullscreen();
#endif
Modified: trunk/Source/WebCore/dom/Element.cpp (209627 => 209628)
--- trunk/Source/WebCore/dom/Element.cpp 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/dom/Element.cpp 2016-12-09 22:06:29 UTC (rev 209628)
@@ -2438,7 +2438,7 @@
void Element::blur()
{
cancelFocusAppearanceUpdate();
- if (treeScope().focusedElement() == this) {
+ if (treeScope().focusedElementInScope() == this) {
if (Frame* frame = document().frame())
frame->page()->focusController().setFocusedElement(0, frame);
else
Modified: trunk/Source/WebCore/dom/ShadowRoot.h (209627 => 209628)
--- trunk/Source/WebCore/dom/ShadowRoot.h 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/dom/ShadowRoot.h 2016-12-09 22:06:29 UTC (rev 209628)
@@ -110,7 +110,7 @@
inline Element* ShadowRoot::activeElement() const
{
- return treeScope().focusedElement();
+ return treeScope().focusedElementInScope();
}
inline ShadowRoot* Node::shadowRoot() const
Modified: trunk/Source/WebCore/dom/TreeScope.cpp (209627 => 209628)
--- trunk/Source/WebCore/dom/TreeScope.cpp 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/dom/TreeScope.cpp 2016-12-09 22:06:29 UTC (rev 209628)
@@ -195,7 +195,7 @@
return *shadowRootInLowestCommonTreeScope.host();
}
-Node* TreeScope::ancestorInThisScope(Node* node) const
+Node* TreeScope::ancestorNodeInThisScope(Node* node) const
{
for (; node; node = node->shadowHost()) {
if (&node->treeScope() == this)
@@ -206,6 +206,17 @@
return nullptr;
}
+Element* TreeScope::ancestorElementInThisScope(Element* element) const
+{
+ for (; element; element = element->shadowHost()) {
+ if (&element->treeScope() == this)
+ return element;
+ if (!element->isInShadowTree())
+ return nullptr;
+ }
+ return nullptr;
+}
+
void TreeScope::addImageMap(HTMLMapElement& imageMap)
{
AtomicStringImpl* name = imageMap.getName().impl();
@@ -364,7 +375,7 @@
return nullptr;
}
-Element* TreeScope::focusedElement()
+Element* TreeScope::focusedElementInScope()
{
Document& document = m_rootNode.document();
Element* element = document.focusedElement();
@@ -371,21 +382,8 @@
if (!element && document.page())
element = focusedFrameOwnerElement(document.page()->focusController().focusedFrame(), document.frame());
- if (!element)
- return nullptr;
- TreeScope* treeScope = &element->treeScope();
- RELEASE_ASSERT(&document == &treeScope->documentScope());
- while (treeScope != this && treeScope != &document) {
- auto& rootNode = treeScope->rootNode();
- if (is<ShadowRoot>(rootNode))
- element = downcast<ShadowRoot>(rootNode).host();
- else
- return nullptr;
- treeScope = &element->treeScope();
- }
- if (this != treeScope)
- return nullptr;
- return element;
+
+ return ancestorElementInThisScope(element);
}
static void listTreeScopes(Node* node, Vector<TreeScope*, 5>& treeScopes)
Modified: trunk/Source/WebCore/dom/TreeScope.h (209627 => 209628)
--- trunk/Source/WebCore/dom/TreeScope.h 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/dom/TreeScope.h 2016-12-09 22:06:29 UTC (rev 209628)
@@ -51,7 +51,7 @@
TreeScope* parentTreeScope() const { return m_parentTreeScope; }
void setParentTreeScope(TreeScope&);
- Element* focusedElement();
+ Element* focusedElementInScope();
WEBCORE_EXPORT Element* getElementById(const AtomicString&) const;
WEBCORE_EXPORT Element* getElementById(const String&) const;
const Vector<Element*>* getAllElementsById(const AtomicString&) const;
@@ -72,7 +72,8 @@
// https://dom.spec.whatwg.org/#retarget
Node& retargetToScope(Node&) const;
- Node* ancestorInThisScope(Node*) const;
+ Node* ancestorNodeInThisScope(Node*) const;
+ WEBCORE_EXPORT Element* ancestorElementInThisScope(Element*) const;
void addImageMap(HTMLMapElement&);
void removeImageMap(HTMLMapElement&);
Modified: trunk/Source/WebCore/editing/VisibleSelection.cpp (209627 => 209628)
--- trunk/Source/WebCore/editing/VisibleSelection.cpp 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/editing/VisibleSelection.cpp 2016-12-09 22:06:29 UTC (rev 209628)
@@ -473,7 +473,7 @@
ASSERT(¤tPosition.containerNode()->treeScope() != &treeScope);
- if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.containerNode())) {
+ if (Node* ancestor = treeScope.ancestorNodeInThisScope(currentPosition.containerNode())) {
if (ancestor->contains(startContainerNode))
return positionAfterNode(ancestor);
return positionBeforeNode(ancestor);
@@ -491,7 +491,7 @@
ASSERT(¤tPosition.containerNode()->treeScope() != &treeScope);
- if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.containerNode())) {
+ if (Node* ancestor = treeScope.ancestorNodeInThisScope(currentPosition.containerNode())) {
if (ancestor->contains(endContainerNode))
return positionBeforeNode(ancestor);
return positionAfterNode(ancestor);
Modified: trunk/Source/WebCore/editing/htmlediting.cpp (209627 => 209628)
--- trunk/Source/WebCore/editing/htmlediting.cpp 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/editing/htmlediting.cpp 2016-12-09 22:06:29 UTC (rev 209628)
@@ -83,12 +83,12 @@
if (!commonScope)
return 0;
- Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
+ Node* nodeA = commonScope->ancestorNodeInThisScope(a.containerNode());
ASSERT(nodeA);
bool hasDescendentA = nodeA != a.containerNode();
int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
- Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
+ Node* nodeB = commonScope->ancestorNodeInThisScope(b.containerNode());
ASSERT(nodeB);
bool hasDescendentB = nodeB != b.containerNode();
int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
@@ -292,7 +292,7 @@
Position candidate = position;
if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
- auto* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(position.deprecatedNode());
+ auto* shadowAncestor = highestRoot->treeScope().ancestorNodeInThisScope(position.deprecatedNode());
if (!shadowAncestor)
return { };
@@ -320,7 +320,7 @@
Position candidate = position;
if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
- auto* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(position.deprecatedNode());
+ auto* shadowAncestor = highestRoot->treeScope().ancestorNodeInThisScope(position.deprecatedNode());
if (!shadowAncestor)
return { };
Modified: trunk/Source/WebCore/page/DOMSelection.cpp (209627 => 209628)
--- trunk/Source/WebCore/page/DOMSelection.cpp 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/page/DOMSelection.cpp 2016-12-09 22:06:29 UTC (rev 209628)
@@ -49,7 +49,7 @@
if (!node->isInShadowTree())
return nullptr;
// FIXME: Unclear on why this needs to be the possibly null frame.document() instead of the never null node->document().
- return frame.document()->ancestorInThisScope(node);
+ return frame.document()->ancestorNodeInThisScope(node);
}
DOMSelection::DOMSelection(Frame& frame)
@@ -436,7 +436,7 @@
return nullptr;
auto* containerNode = position.containerNode();
- auto* adjustedNode = m_frame->document()->ancestorInThisScope(containerNode);
+ auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode);
if (!adjustedNode)
return nullptr;
@@ -452,7 +452,7 @@
return 0;
auto* containerNode = position.containerNode();
- auto* adjustedNode = m_frame->document()->ancestorInThisScope(containerNode);
+ auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode);
if (!adjustedNode)
return 0;
Modified: trunk/Source/WebCore/rendering/HitTestResult.cpp (209627 => 209628)
--- trunk/Source/WebCore/rendering/HitTestResult.cpp 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebCore/rendering/HitTestResult.cpp 2016-12-09 22:06:29 UTC (rev 209628)
@@ -668,8 +668,9 @@
if (!node)
return true;
+ // FIXME: This moves out of a author shadow tree.
if (request.disallowsUserAgentShadowContent())
- node = node->document().ancestorInThisScope(node);
+ node = node->document().ancestorNodeInThisScope(node);
mutableRectBasedTestResult().add(node);
@@ -688,8 +689,9 @@
if (!node)
return true;
+ // FIXME: This moves out of a author shadow tree.
if (request.disallowsUserAgentShadowContent())
- node = node->document().ancestorInThisScope(node);
+ node = node->document().ancestorNodeInThisScope(node);
mutableRectBasedTestResult().add(node);
Modified: trunk/Source/WebKit/mac/ChangeLog (209627 => 209628)
--- trunk/Source/WebKit/mac/ChangeLog 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebKit/mac/ChangeLog 2016-12-09 22:06:29 UTC (rev 209628)
@@ -1,3 +1,16 @@
+2016-12-09 Ryosuke Niwa <rn...@webkit.org>
+
+ document.webkitFullscreenElement leaks elements inside a shadow tree
+ https://bugs.webkit.org/show_bug.cgi?id=158471
+
+ Reviewed by Chris Dumez.
+
+ Use the API for bindings to avoid exposing nodes inside a shadow tree.
+
+ * DOM/DOMDocument.mm:
+ (-[DOMDocument webkitCurrentFullScreenElement]):
+ (-[DOMDocument webkitFullscreenElement]):
+
2016-12-09 Beth Dakin <bda...@apple.com>
Password fields should not show the emoji button in TouchBar
Modified: trunk/Source/WebKit/mac/DOM/DOMDocument.mm (209627 => 209628)
--- trunk/Source/WebKit/mac/DOM/DOMDocument.mm 2016-12-09 22:04:14 UTC (rev 209627)
+++ trunk/Source/WebKit/mac/DOM/DOMDocument.mm 2016-12-09 22:06:29 UTC (rev 209628)
@@ -361,7 +361,7 @@
- (DOMElement *)webkitCurrentFullScreenElement
{
WebCore::JSMainThreadNullState state;
- return kit(WTF::getPtr(IMPL->webkitCurrentFullScreenElement()));
+ return kit(WTF::getPtr(IMPL->webkitCurrentFullScreenElementForBindings()));
}
- (BOOL)webkitFullscreenEnabled
@@ -373,7 +373,7 @@
- (DOMElement *)webkitFullscreenElement
{
WebCore::JSMainThreadNullState state;
- return kit(WTF::getPtr(IMPL->webkitFullscreenElement()));
+ return kit(WTF::getPtr(IMPL->webkitFullscreenElementForBindings()));
}
#endif