Bug 1480206 - Move XULDocument popup attributes to Document. r=bz

Allows top level non-XUL documents to share this code. Three tests had to
be adjusted to account for the attributes being chrome only now and not
available to content privilege XUL. In two tests, the values attributes
are now simply undefined. The crashtest was converted to a chrome
mochitest to preserve what it was testing.

MozReview-Commit-ID: 99w9Ax4et3C

--HG--
rename : dom/base/crashtests/473284.xul => dom/base/test/chrome/test_bug473284.xul
extra : rebase_source : 924d34a88fe8a48d766f78b02e64275f6e7cdc2b
This commit is contained in:
Brendan Dahl 2018-08-06 10:52:53 -07:00
Родитель 20a322176f
Коммит da472b2bfe
11 изменённых файлов: 155 добавлений и 149 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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<nsPIWindowRoot>
nsIDocument::GetWindowRoot()
{
if (!mDocumentContainer) {
return nullptr;
}
// XXX It's unclear why this can't just use GetWindow().
nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow();
return piWin ? piWin->GetTopWindowRoot() : nullptr;
}
already_AddRefed<nsINode>
nsIDocument::GetPopupNode()
{
nsCOMPtr<nsINode> node;
nsCOMPtr<nsPIWindowRoot> 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<nsPIWindowRoot> 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<nsINode>
nsIDocument::GetTooltipNode()
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm) {
nsCOMPtr<nsINode> node = pm->GetLastTriggerTooltipNode(this);
if (node) {
return node.forget();
}
}
return nullptr;
}
nsIHTMLCollection*
nsIDocument::Children()
{

Просмотреть файл

@ -1383,6 +1383,11 @@ protected:
*/
void ClearStaleServoData();
/**
* Returns the top window root from the outer window.
*/
already_AddRefed<nsPIWindowRoot> GetWindowRoot();
private:
class SelectorCacheKey
{
@ -3336,6 +3341,13 @@ public:
mozilla::dom::Promise* GetDocumentReadyForIdle(mozilla::ErrorResult& aRv);
already_AddRefed<nsINode> GetPopupNode();
void SetPopupNode(nsINode* aNode);
nsINode* GetPopupRangeParent(ErrorResult& aRv);
int32_t GetPopupRangeOffset(ErrorResult& aRv);
already_AddRefed<nsINode> GetTooltipNode();
void SetTooltipNode(nsINode* aNode) { /* do nothing */ }
// ParentNode
nsIHTMLCollection* Children();
uint32_t ChildElementCount();

Просмотреть файл

@ -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]

Просмотреть файл

@ -1,5 +1,11 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=473284
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
class="reftest-wait"
onload="
var result = '';
try {
@ -86,8 +92,14 @@ try {
result += '.';
}
document.documentElement.textContent = result == '.23.56.89.bc' ? 'PASSED' : 'FAILED';
if (document.documentElement.textContent == 'PASSED') {
document.documentElement.removeAttribute('class');
}
"/>
is(result, '.23.56.89.bc', 'The correct assignments throw.');
">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=473284"
target="_blank">Mozilla Bug 473284</a>
</body>
</window>

Просмотреть файл

@ -406,6 +406,21 @@ partial interface Document {
// "document_idle" webextension script injection point.
[ChromeOnly, Throws]
readonly attribute Promise<Document> 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 {

Просмотреть файл

@ -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;

Просмотреть файл

@ -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<nsINode>
XULDocument::GetPopupNode()
{
nsCOMPtr<nsINode> node;
nsCOMPtr<nsPIWindowRoot> 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<nsPIWindowRoot> 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<nsINode>
XULDocument::GetTooltipNode()
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm) {
nsCOMPtr<nsINode> 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<nsPIWindowRoot>
XULDocument::GetWindowRoot()
{
if (!mDocumentContainer) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow();
return piWin ? piWin->GetTopWindowRoot() : nullptr;
}
bool
XULDocument::IsDocumentRightToLeft()
{

Просмотреть файл

@ -137,12 +137,6 @@ public:
void TraceProtos(JSTracer* aTrc);
// WebIDL API
already_AddRefed<nsINode> GetPopupNode();
void SetPopupNode(nsINode* aNode);
nsINode* GetPopupRangeParent(ErrorResult& aRv);
int32_t GetPopupRangeOffset(ErrorResult& aRv);
already_AddRefed<nsINode> GetTooltipNode();
void SetTooltipNode(nsINode* aNode) { /* do nothing */ }
nsIDOMXULCommandDispatcher* GetCommandDispatcher() const
{
return mCommandDispatcher;
@ -190,8 +184,6 @@ protected:
Element* aListener,
nsAtom* aAttr);
already_AddRefed<nsPIWindowRoot> GetWindowRoot();
static void DirectionChanged(const char* aPrefName, XULDocument* aData);
// pseudo constants

Просмотреть файл

@ -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

Просмотреть файл

@ -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();