author | Olli Pettay <Olli.Pettay@helsinki.fi> |
Sat, 14 Jul 2018 04:48:19 +0300 | |
changeset 484401 | 015a00cd67680e33731ef61931b87afb6c66d9bb |
parent 484400 | 4afbbc6154255f6f5dc1151a5a2cd1ea674e3915 |
child 484402 | 50a16618593df5b2dd4a3cceaedc9472741b91ef |
push id | 1815 |
push user | ffxbld-merge |
push date | Mon, 15 Oct 2018 10:40:45 +0000 |
treeherder | mozilla-release@18d4c09e9378 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mrbkap |
bugs | 1475485 |
milestone | 63.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/docshell/base/nsDocShellTreeOwner.cpp +++ b/docshell/base/nsDocShellTreeOwner.cpp @@ -1213,17 +1213,17 @@ ChromeTooltipListener::MouseMove(Event* if (mTooltipTimer) { mTooltipTimer->Cancel(); } if (!mShowingTooltip && !mTooltipShownOnce) { nsIEventTarget* target = nullptr; - nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetTarget(); + nsCOMPtr<EventTarget> eventTarget = aMouseEvent->GetComposedTarget(); if (eventTarget) { mPossibleTooltipNode = do_QueryInterface(eventTarget); nsCOMPtr<nsIGlobalObject> global(eventTarget->GetOwnerGlobal()); if (global) { target = global->EventTargetFor(TaskCategory::UI); } } @@ -1312,16 +1312,22 @@ ChromeTooltipListener::HideTooltip() // -- the x/y coordinates of the mouse (mMouseClientY, mMouseClientX) // -- the dom node the user hovered over (mPossibleTooltipNode) void ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer, void* aChromeTooltipListener) { auto self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener); if (self && self->mPossibleTooltipNode) { + if (!self->mPossibleTooltipNode->IsInComposedDoc()) { + // release tooltip target if there is one, NO MATTER WHAT + self->mPossibleTooltipNode = nullptr; + return; + } + // The actual coordinates we want to put the tooltip at are relative to the // toplevel docshell of our mWebBrowser. We know what the screen // coordinates of the mouse event were, which means we just need the screen // coordinates of the docshell. Unfortunately, there is no good way to // find those short of groveling for the presentation in that docshell and // finding the screen coords of its toplevel widget... nsCOMPtr<nsIDocShell> docShell = do_GetInterface(static_cast<nsIWebBrowser*>(self->mWebBrowser));
--- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -261,16 +261,20 @@ partial interface Element { [BinaryName="shadowRootByMode", Func="nsDocument::IsShadowDOMEnabled"] readonly attribute ShadowRoot? shadowRoot; [ChromeOnly, Func="nsDocument::IsShadowDOMEnabled", BinaryName="shadowRoot"] readonly attribute ShadowRoot? openOrClosedShadowRoot; [BinaryName="assignedSlotByMode", Func="nsDocument::IsShadowDOMEnabled"] readonly attribute HTMLSlotElement? assignedSlot; + + [ChromeOnly, BinaryName="assignedSlot", Func="nsDocument::IsShadowDOMEnabled"] + readonly attribute HTMLSlotElement? openOrClosedAssignedSlot; + [CEReactions, Unscopable, SetterThrows, Func="nsDocument::IsShadowDOMEnabled"] attribute DOMString slot; }; Element implements ChildNode; Element implements NonDocumentTypeChildNode; Element implements ParentNode; Element implements Animatable;
--- a/dom/webidl/Text.webidl +++ b/dom/webidl/Text.webidl @@ -16,11 +16,14 @@ interface Text : CharacterData { Text splitText(unsigned long offset); [Throws] readonly attribute DOMString wholeText; }; partial interface Text { [BinaryName="assignedSlotByMode", Func="nsTextNode::IsShadowDOMEnabled"] readonly attribute HTMLSlotElement? assignedSlot; + + [ChromeOnly, BinaryName="assignedSlot", Func="nsTextNode::IsShadowDOMEnabled"] + readonly attribute HTMLSlotElement? openOrClosedAssignedSlot; }; Text implements GeometryUtils;
--- a/layout/xul/nsXULTooltipListener.cpp +++ b/layout/xul/nsXULTooltipListener.cpp @@ -97,17 +97,23 @@ nsXULTooltipListener::MouseOut(Event* aE return; #endif #ifdef MOZ_XUL // check to see if the mouse left the targetNode, and if so, // hide the tooltip if (currentTooltip) { // which node did the mouse leave? - nsCOMPtr<nsINode> targetNode = do_QueryInterface(aEvent->GetTarget()); + EventTarget* eventTarget = aEvent->GetComposedTarget(); + nsCOMPtr<nsIContent> content = do_QueryInterface(eventTarget); + if (content && !content->GetContainingShadow()) { + eventTarget = aEvent->GetTarget(); + } + + nsCOMPtr<nsINode> targetNode = do_QueryInterface(eventTarget); nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) { nsCOMPtr<nsINode> tooltipNode = pm->GetLastTriggerTooltipNode(currentTooltip->GetComposedDoc()); if (tooltipNode == targetNode) { // if the target node is the current tooltip target node, the mouse // left the node the tooltip appeared on, so close the tooltip. @@ -168,17 +174,21 @@ nsXULTooltipListener::MouseMove(Event* a // so that the delay is from when the mouse stops moving, not when it enters // the node. KillTooltipTimer(); // If the mouse moves while the tooltip is up, hide it. If nothing is // showing and the tooltip hasn't been displayed since the mouse entered // the node, then start the timer to show the tooltip. if (!currentTooltip && !mTooltipShownOnce) { - nsCOMPtr<EventTarget> eventTarget = aEvent->GetTarget(); + nsCOMPtr<EventTarget> eventTarget = aEvent->GetComposedTarget(); + nsCOMPtr<nsIContent> content = do_QueryInterface(eventTarget); + if (content && !content->GetContainingShadow()) { + eventTarget = aEvent->GetTarget(); + } // don't show tooltips attached to elements outside of a menu popup // when hovering over an element inside it. The popupsinherittooltip // attribute may be used to disable this behaviour, which is useful for // large menu hierarchies such as bookmarks. if (!sourceContent->IsElement() || !sourceContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::popupsinherittooltip,
--- a/toolkit/components/tooltiptext/TooltipTextProvider.js +++ b/toolkit/components/tooltiptext/TooltipTextProvider.js @@ -4,21 +4,20 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); function TooltipTextProvider() {} TooltipTextProvider.prototype = { getNodeText(tipElement, textOut, directionOut) { - // Don't show the tooltip if the tooltip node is a document, browser, or disconnected. + // Don't show the tooltip if the tooltip node is a document or browser. + // Caller should ensure the node is in (composed) document. if (!tipElement || !tipElement.ownerDocument || - tipElement.localName == "browser" || - (tipElement.ownerDocument.compareDocumentPosition(tipElement) & - tipElement.ownerDocument.DOCUMENT_POSITION_DISCONNECTED)) { + tipElement.localName == "browser") { return false; } var defView = tipElement.ownerGlobal; // XXX Work around bug 350679: // "Tooltips can be fired in documents with no view". if (!defView) return false; @@ -118,17 +117,28 @@ TooltipTextProvider.prototype = { break; } } } usedTipElement = tipElement; } - tipElement = tipElement.parentNode; + let parent = tipElement.parentNode; + if (defView.ShadowRoot && + parent instanceof defView.ShadowRoot) { + tipElement = parent.host; + } else { + let slot = tipElement.openOrClosedAssignedSlot; + if (slot) { + tipElement = slot; + } else { + tipElement = parent; + } + } } return [titleText, XLinkTitleText, SVGTitleText, XULtooltiptextText].some(function(t) { if (t && /\S/.test(t)) { // Make CRLF and CR render one line break each. textOut.value = t.replace(/\r\n?/g, "\n"); if (usedTipElement) {
--- a/toolkit/components/tooltiptext/tests/browser.ini +++ b/toolkit/components/tooltiptext/tests/browser.ini @@ -1,8 +1,9 @@ [browser_bug329212.js] support-files = title_test.svg [browser_bug331772_xul_tooltiptext_in_html.js] support-files = xul_tooltiptext.xhtml [browser_bug561623.js] [browser_bug581947.js] [browser_input_file_tooltips.js] skip-if = os == 'win' && os_version == '10.0' # Permafail on Win 10 (bug 1400368) +[browser_shadow_dom_tooltip.js]
new file mode 100644 --- /dev/null +++ b/toolkit/components/tooltiptext/tests/browser_shadow_dom_tooltip.js @@ -0,0 +1,131 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +add_task(async function setup() { + await SpecialPowers.pushPrefEnv( + {"set": [["ui.tooltipDelay", 0], + ["dom.webcomponents.shadowdom.enabled", true]]}); +}); + +add_task(async function test_title_in_shadow_dom() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + + info("Moving mouse out of the way."); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 300, 300); + + info("creating host"); + await ContentTask.spawn(tab.linkedBrowser, {}, async function() { + let doc = content.document; + let host = doc.createElement("div"); + doc.body.appendChild(host); + host.setAttribute("style", "position: absolute; top: 0; left: 0;"); + var sr = host.attachShadow({ mode: "closed" }); + sr.innerHTML = "<div title='shadow' style='width: 200px; height: 200px;'>shadow</div>"; + }); + + let awaitTooltipOpen = new Promise(resolve => { + let tooltipId = Services.appinfo.browserTabsRemoteAutostart ? + "remoteBrowserTooltip" : + "aHTMLTooltip"; + let tooltip = document.getElementById(tooltipId); + tooltip.addEventListener("popupshown", function(event) { + resolve(event.target); + }, {once: true}); + }); + info("Initial mouse move"); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5); + info("Waiting"); + await new Promise(resolve => setTimeout(resolve, 400)); + info("Second mouse move"); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5); + info("Waiting for tooltip to open"); + let tooltip = await awaitTooltipOpen; + + is(tooltip.getAttribute("label"), "shadow", "tooltip label should match expectation"); + + info("Closing tab"); + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_title_in_light_dom() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + + info("Moving mouse out of the way."); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 300, 300); + + info("creating host"); + await ContentTask.spawn(tab.linkedBrowser, {}, async function() { + let doc = content.document; + let host = doc.createElement("div"); + host.title = "light"; + doc.body.appendChild(host); + host.setAttribute("style", "position: absolute; top: 0; left: 0;"); + var sr = host.attachShadow({ mode: "closed" }); + sr.innerHTML = "<div style='width: 200px; height: 200px;'>shadow</div>"; + }); + + let awaitTooltipOpen = new Promise(resolve => { + let tooltipId = Services.appinfo.browserTabsRemoteAutostart ? + "remoteBrowserTooltip" : + "aHTMLTooltip"; + let tooltip = document.getElementById(tooltipId); + tooltip.addEventListener("popupshown", function(event) { + resolve(event.target); + }, {once: true}); + }); + info("Initial mouse move"); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5); + info("Waiting"); + await new Promise(resolve => setTimeout(resolve, 400)); + info("Second mouse move"); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5); + info("Waiting for tooltip to open"); + let tooltip = await awaitTooltipOpen; + + is(tooltip.getAttribute("label"), "light", "tooltip label should match expectation"); + + info("Closing tab"); + BrowserTestUtils.removeTab(tab); +}); + + +add_task(async function test_title_through_slot() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + + info("Moving mouse out of the way."); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 300, 300); + + info("creating host"); + await ContentTask.spawn(tab.linkedBrowser, {}, async function() { + let doc = content.document; + let host = doc.createElement("div"); + host.title = "light"; + host.innerHTML = "<div style='width: 200px; height: 200px;'>light</div>" + doc.body.appendChild(host); + host.setAttribute("style", "position: absolute; top: 0; left: 0;"); + var sr = host.attachShadow({ mode: "closed" }); + sr.innerHTML = "<div title='shadow' style='width: 200px; height: 200px;'><slot></slot></div>"; + }); + + let awaitTooltipOpen = new Promise(resolve => { + let tooltipId = Services.appinfo.browserTabsRemoteAutostart ? + "remoteBrowserTooltip" : + "aHTMLTooltip"; + let tooltip = document.getElementById(tooltipId); + tooltip.addEventListener("popupshown", function(event) { + resolve(event.target); + }, {once: true}); + }); + info("Initial mouse move"); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5); + info("Waiting"); + await new Promise(resolve => setTimeout(resolve, 400)); + info("Second mouse move"); + await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5); + info("Waiting for tooltip to open"); + let tooltip = await awaitTooltipOpen; + + is(tooltip.getAttribute("label"), "shadow", "tooltip label should match expectation"); + + info("Closing tab"); + BrowserTestUtils.removeTab(tab); +});