diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 3f1a50d542ee..7519dc026251 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -74,7 +74,6 @@ skip load 458637-1.html # sporadically times out (bug 473680) load 462947.html load 467392.html load 472593-1.html -load 473284.xul load 474041-1.svg load 476526.html load 483818-1.html diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index edf89d128b6a..d55d406c4ba7 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -259,6 +259,7 @@ #include "mozilla/dom/MenuBoxObject.h" #include "mozilla/dom/TreeBoxObject.h" #include "nsIXULWindow.h" +#include "nsXULPopupManager.h" #include "nsIDocShellTreeOwner.h" #endif #include "nsIPresShellInlines.h" @@ -10174,6 +10175,112 @@ nsIDocument::MaybeResolveReadyForIdle() } } +static JSObject* +GetScopeObjectOfNode(nsINode* node) +{ + MOZ_ASSERT(node, "Must not be called with null."); + + // Window root occasionally keeps alive a node of a document whose + // window is already dead. If in this brief period someone calls + // GetPopupNode and we return that node, we can end up creating a + // reflector for the node in the wrong global (the current global, + // not the document global, because we won't know what the document + // global is). Returning an orphan node like that to JS would be a + // bug anyway, so to avoid this, let's do the same check as fetching + // GetParentObjet() on the document does to determine the scope and + // if it returns null let's just return null in XULDocument::GetPopupNode. + nsIDocument* doc = node->OwnerDoc(); + MOZ_ASSERT(doc, "This should never happen."); + + nsIGlobalObject* global = doc->GetScopeObject(); + return global ? global->GetGlobalJSObject() : nullptr; +} + + +already_AddRefed +nsIDocument::GetWindowRoot() +{ + if (!mDocumentContainer) { + return nullptr; + } + // XXX It's unclear why this can't just use GetWindow(). + nsCOMPtr piWin = mDocumentContainer->GetWindow(); + return piWin ? piWin->GetTopWindowRoot() : nullptr; +} + +already_AddRefed +nsIDocument::GetPopupNode() +{ + nsCOMPtr node; + nsCOMPtr rootWin = GetWindowRoot(); + if (rootWin) { + node = rootWin->GetPopupNode(); // addref happens here + } + + if (!node) { + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + node = pm->GetLastTriggerPopupNode(this); + } + } + + if (node && GetScopeObjectOfNode(node)) { + return node.forget(); + } + + return nullptr; +} + +void +nsIDocument::SetPopupNode(nsINode* aNode) +{ + nsCOMPtr rootWin = GetWindowRoot(); + if (rootWin) { + rootWin->SetPopupNode(aNode); + } +} + +// Returns the rangeOffset element from the XUL Popup Manager. This is for +// chrome callers only. +nsINode* +nsIDocument::GetPopupRangeParent(ErrorResult& aRv) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return pm->GetMouseLocationParent(); +} + +// Returns the rangeOffset element from the XUL Popup Manager. +int32_t +nsIDocument::GetPopupRangeOffset(ErrorResult& aRv) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) { + aRv.Throw(NS_ERROR_FAILURE); + return 0; + } + + return pm->MouseLocationOffset(); +} + +already_AddRefed +nsIDocument::GetTooltipNode() +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + nsCOMPtr node = pm->GetLastTriggerTooltipNode(this); + if (node) { + return node.forget(); + } + } + + return nullptr; +} + nsIHTMLCollection* nsIDocument::Children() { diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 9e7412d9705f..95b18662885c 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -1383,6 +1383,11 @@ protected: */ void ClearStaleServoData(); + /** + * Returns the top window root from the outer window. + */ + already_AddRefed GetWindowRoot(); + private: class SelectorCacheKey { @@ -3336,6 +3341,13 @@ public: mozilla::dom::Promise* GetDocumentReadyForIdle(mozilla::ErrorResult& aRv); + already_AddRefed GetPopupNode(); + void SetPopupNode(nsINode* aNode); + nsINode* GetPopupRangeParent(ErrorResult& aRv); + int32_t GetPopupRangeOffset(ErrorResult& aRv); + already_AddRefed GetTooltipNode(); + void SetTooltipNode(nsINode* aNode) { /* do nothing */ } + // ParentNode nsIHTMLCollection* Children(); uint32_t ChildElementCount(); diff --git a/dom/base/test/chrome/chrome.ini b/dom/base/test/chrome/chrome.ini index b06e158e1749..0ec09970d99d 100644 --- a/dom/base/test/chrome/chrome.ini +++ b/dom/base/test/chrome/chrome.ini @@ -39,6 +39,7 @@ support-files = ../file_bug357450.js [test_bug429785.xul] [test_bug430050.xul] [test_bug467123.xul] +[test_bug473284.xul] [test_bug549682.xul] skip-if = verify [test_bug571390.xul] diff --git a/dom/base/crashtests/473284.xul b/dom/base/test/chrome/test_bug473284.xul similarity index 67% rename from dom/base/crashtests/473284.xul rename to dom/base/test/chrome/test_bug473284.xul index 2a00d2783c87..940fefa8b5be 100644 --- a/dom/base/crashtests/473284.xul +++ b/dom/base/test/chrome/test_bug473284.xul @@ -1,5 +1,11 @@ + + + + +is(result, '.23.56.89.bc', 'The correct assignments throw.'); +"> + + + + + Mozilla Bug 473284 + + diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index 201fe8023cf9..6022e5f2f9a4 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -406,6 +406,21 @@ partial interface Document { // "document_idle" webextension script injection point. [ChromeOnly, Throws] readonly attribute Promise documentReadyForIdle; + + [ChromeOnly] + attribute Node? popupNode; + + /** + * These attributes correspond to rangeParent and rangeOffset. They will help + * you find where in the DOM the popup is happening. Can be accessed only + * during a popup event. Accessing any other time will be an error. + */ + [Throws, ChromeOnly] + readonly attribute Node? popupRangeParent; + [Throws, ChromeOnly] + readonly attribute long popupRangeOffset; + [ChromeOnly] + attribute Node? tooltipNode; }; dictionary BlockParsingOptions { diff --git a/dom/webidl/XULDocument.webidl b/dom/webidl/XULDocument.webidl index 53038690746d..6b157dec3bb9 100644 --- a/dom/webidl/XULDocument.webidl +++ b/dom/webidl/XULDocument.webidl @@ -9,20 +9,6 @@ interface MozObserver; [Func="IsChromeOrXBL"] interface XULDocument : Document { - attribute Node? popupNode; - - /** - * These attributes correspond to trustedGetPopupNode().rangeOffset and - * rangeParent. They will help you find where in the DOM the popup is - * happening. Can be accessed only during a popup event. Accessing any other - * time will be an error. - */ - [Throws, ChromeOnly] - readonly attribute Node? popupRangeParent; - [Throws, ChromeOnly] - readonly attribute long popupRangeOffset; - - attribute Node? tooltipNode; readonly attribute XULCommandDispatcher? commandDispatcher; diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index ca24322c7b3a..53ac03a332a2 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -1003,113 +1003,6 @@ XULDocument::Persist(Element* aElement, int32_t aNameSpaceID, mLocalStore->SetValue(uri, id, attrstr, valuestr); } -static JSObject* -GetScopeObjectOfNode(nsINode* node) -{ - MOZ_ASSERT(node, "Must not be called with null."); - - // Window root occasionally keeps alive a node of a document whose - // window is already dead. If in this brief period someone calls - // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw, - // because it will not know which scope this node belongs to. Returning - // an orphan node like that to JS would be a bug anyway, so to avoid - // this, let's do the same check as nsNodeSH::PreCreate does to - // determine the scope and if it fails let's just return null in - // XULDocument::GetPopupNode. - nsIDocument* doc = node->OwnerDoc(); - MOZ_ASSERT(doc, "This should never happen."); - - nsIGlobalObject* global = doc->GetScopeObject(); - return global ? global->GetGlobalJSObject() : nullptr; -} - -already_AddRefed -XULDocument::GetPopupNode() -{ - nsCOMPtr node; - nsCOMPtr rootWin = GetWindowRoot(); - if (rootWin) { - node = rootWin->GetPopupNode(); // addref happens here - } - - if (!node) { - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm) { - node = pm->GetLastTriggerPopupNode(this); - } - } - - if (node && nsContentUtils::CanCallerAccess(node) - && GetScopeObjectOfNode(node)) { - return node.forget(); - } - - return nullptr; -} - -void -XULDocument::SetPopupNode(nsINode* aNode) -{ - nsCOMPtr rootWin = GetWindowRoot(); - if (rootWin) { - rootWin->SetPopupNode(aNode); - } -} - -// Returns the rangeOffset element from the XUL Popup Manager. This is for -// chrome callers only. -nsINode* -XULDocument::GetPopupRangeParent(ErrorResult& aRv) -{ - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (!pm) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsINode* rangeParent = pm->GetMouseLocationParent(); - if (rangeParent && !nsContentUtils::CanCallerAccess(rangeParent)) { - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } - - return rangeParent; -} - -// Returns the rangeOffset element from the XUL Popup Manager. We check the -// rangeParent to determine if the caller has rights to access to the data. -int32_t -XULDocument::GetPopupRangeOffset(ErrorResult& aRv) -{ - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (!pm) { - aRv.Throw(NS_ERROR_FAILURE); - return 0; - } - - nsINode* rangeParent = pm->GetMouseLocationParent(); - if (rangeParent && !nsContentUtils::CanCallerAccess(rangeParent)) { - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return 0; - } - - return pm->MouseLocationOffset(); -} - -already_AddRefed -XULDocument::GetTooltipNode() -{ - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm) { - nsCOMPtr node = pm->GetLastTriggerTooltipNode(this); - if (node && nsContentUtils::CanCallerAccess(node)) { - return node.forget(); - } - } - - return nullptr; -} - nsresult XULDocument::AddElementToDocumentPre(Element* aElement) { @@ -2717,17 +2610,6 @@ XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request, return NS_ERROR_UNEXPECTED; } -already_AddRefed -XULDocument::GetWindowRoot() -{ - if (!mDocumentContainer) { - return nullptr; - } - - nsCOMPtr piWin = mDocumentContainer->GetWindow(); - return piWin ? piWin->GetTopWindowRoot() : nullptr; -} - bool XULDocument::IsDocumentRightToLeft() { diff --git a/dom/xul/XULDocument.h b/dom/xul/XULDocument.h index 7a87a11e711d..970274525120 100644 --- a/dom/xul/XULDocument.h +++ b/dom/xul/XULDocument.h @@ -137,12 +137,6 @@ public: void TraceProtos(JSTracer* aTrc); // WebIDL API - already_AddRefed GetPopupNode(); - void SetPopupNode(nsINode* aNode); - nsINode* GetPopupRangeParent(ErrorResult& aRv); - int32_t GetPopupRangeOffset(ErrorResult& aRv); - already_AddRefed GetTooltipNode(); - void SetTooltipNode(nsINode* aNode) { /* do nothing */ } nsIDOMXULCommandDispatcher* GetCommandDispatcher() const { return mCommandDispatcher; @@ -190,8 +184,6 @@ protected: Element* aListener, nsAtom* aAttr); - already_AddRefed GetWindowRoot(); - static void DirectionChanged(const char* aPrefName, XULDocument* aData); // pseudo constants diff --git a/toolkit/content/tests/chrome/popup_trigger.js b/toolkit/content/tests/chrome/popup_trigger.js index c9b537ac3577..1abd36988147 100644 --- a/toolkit/content/tests/chrome/popup_trigger.js +++ b/toolkit/content/tests/chrome/popup_trigger.js @@ -453,7 +453,7 @@ var popupTests = [ var evt = child.createEvent("Event"); evt.initEvent("click", true, true); child.documentElement.dispatchEvent(evt); - is(child.documentElement.getAttribute("data"), "xnull", + is(child.documentElement.getAttribute("data"), "xundefined", "cannot get popupNode from other document"); child.documentElement.setAttribute("data", "none"); // now try again with document.popupNode set explicitly diff --git a/toolkit/content/tests/chrome/window_tooltip.xul b/toolkit/content/tests/chrome/window_tooltip.xul index 0d33515c704f..ea89eebbc53d 100644 --- a/toolkit/content/tests/chrome/window_tooltip.xul +++ b/toolkit/content/tests/chrome/window_tooltip.xul @@ -113,7 +113,7 @@ var popupTests = [ var evt = child.createEvent("Event"); evt.initEvent("click", true, true); child.documentElement.dispatchEvent(evt); - is(child.documentElement.getAttribute("data"), "xnull", + is(child.documentElement.getAttribute("data"), "xundefined", "cannot get tooltipNode from other document"); var buttonrect = document.getElementById("withtooltip").getBoundingClientRect();