зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
cf4fe0aaee
|
@ -511,7 +511,6 @@ module.exports = {
|
|||
"dom/base/test/chrome/test_bug683852.xhtml",
|
||||
"dom/base/test/chrome/test_bug780529.xhtml",
|
||||
"dom/base/test/chrome/test_bug800386.xhtml",
|
||||
"dom/base/test/chrome/test_bug814638.xhtml",
|
||||
"dom/base/test/chrome/test_bug884693.xhtml",
|
||||
"dom/base/test/chrome/test_document-element-inserted.xhtml",
|
||||
"dom/base/test/chrome/test_domparsing.xhtml",
|
||||
|
|
|
@ -710,17 +710,9 @@
|
|||
|
||||
obj = {
|
||||
role: ROLE_SPINBUTTON,
|
||||
interfaces: [ nsIAccessibleValue ],
|
||||
interfaces: [ nsIAccessibleValue, nsIAccessibleText, nsIAccessibleEditableText ],
|
||||
children: [
|
||||
{ role: ROLE_TEXT_LEAF },
|
||||
{
|
||||
role: ROLE_PUSHBUTTON,
|
||||
actions: "press",
|
||||
},
|
||||
{
|
||||
role: ROLE_PUSHBUTTON,
|
||||
actions: "press",
|
||||
},
|
||||
],
|
||||
};
|
||||
testElm("input_number", obj);
|
||||
|
|
|
@ -62,11 +62,7 @@
|
|||
testAccessibleTree("range", accTree);
|
||||
|
||||
// input@type="number"
|
||||
accTree =
|
||||
{ SPINBUTTON: [
|
||||
{ PUSHBUTTON: [ ] },
|
||||
{ PUSHBUTTON: [ ] },
|
||||
] };
|
||||
accTree = { SPINBUTTON: [ ] };
|
||||
testAccessibleTree("number", accTree);
|
||||
|
||||
// output
|
||||
|
|
|
@ -2293,13 +2293,6 @@ pref("devtools.debugger.features.map-await-expression", true);
|
|||
// about:devtools-toolbox tabs unusable by mistake.
|
||||
pref("devtools.popup.disable_autohide", false);
|
||||
|
||||
// Load the DevTools toolbox in a frame with type=content instead of type=chrome
|
||||
// See Bug 1539979 for more details.
|
||||
// We keep the option of running devtools in a chrome frame while we fix racy
|
||||
// tests that started failing when using type=content, but this ultimately
|
||||
// should be removed.
|
||||
pref("devtools.toolbox.content-frame", true);
|
||||
|
||||
// Visibility switch preference for the WhatsNew panel.
|
||||
pref("devtools.whatsnew.enabled", true);
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ body {
|
|||
%endif
|
||||
}
|
||||
|
||||
:root[customize-entered] {
|
||||
:root[customizing] {
|
||||
min-width: -moz-fit-content;
|
||||
}
|
||||
|
||||
|
@ -788,7 +788,6 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks {
|
|||
toolbarpaletteitem[place=toolbar][id^=wrapper-customizableui-special-spring],
|
||||
toolbarspring {
|
||||
-moz-box-flex: 1;
|
||||
min-width: 1px;
|
||||
max-width: 112px;
|
||||
}
|
||||
|
||||
|
|
|
@ -2893,7 +2893,10 @@ function focusAndSelectUrlBar() {
|
|||
// finished leaving customize mode, and the url bar will still be disabled.
|
||||
// We can't focus it when it's disabled, so we need to re-run ourselves when
|
||||
// we've finished leaving customize mode.
|
||||
if (CustomizationHandler.isCustomizing()) {
|
||||
if (
|
||||
CustomizationHandler.isCustomizing() ||
|
||||
CustomizationHandler.isExitingCustomizeMode
|
||||
) {
|
||||
gNavToolbox.addEventListener("aftercustomization", focusAndSelectUrlBar, {
|
||||
once: true,
|
||||
});
|
||||
|
|
|
@ -2634,12 +2634,17 @@
|
|||
this.tabContainer.getAttribute("overflow") != "true" &&
|
||||
this.animationsEnabled;
|
||||
|
||||
// Related tab inherits current tab's user context unless a different
|
||||
// usercontextid is specified
|
||||
if (userContextId == null && openerTab) {
|
||||
userContextId = openerTab.getAttribute("usercontextid") || 0;
|
||||
}
|
||||
|
||||
this.setTabAttributes(t, {
|
||||
animate,
|
||||
noInitialLabel,
|
||||
aURI,
|
||||
userContextId,
|
||||
openerTab,
|
||||
skipBackgroundNotify,
|
||||
pinned,
|
||||
skipAnimation,
|
||||
|
@ -3111,7 +3116,6 @@
|
|||
noInitialLabel,
|
||||
aURI,
|
||||
userContextId,
|
||||
openerTab,
|
||||
skipBackgroundNotify,
|
||||
pinned,
|
||||
skipAnimation,
|
||||
|
@ -3126,12 +3130,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Related tab inherits current tab's user context unless a different
|
||||
// usercontextid is specified
|
||||
if (userContextId == null && openerTab) {
|
||||
userContextId = openerTab.getAttribute("usercontextid") || 0;
|
||||
}
|
||||
|
||||
if (userContextId) {
|
||||
tab.setAttribute("usercontextid", userContextId);
|
||||
ContextualIdentityService.setTabStyle(tab);
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
open_and_focus_helper.html
|
||||
[browser_bug1557041.js]
|
||||
skip-if = os == 'linux' # Bug 1561973
|
||||
[browser_fullscreen_permissions_prompt.js]
|
||||
skip-if = debug && os == 'mac' # Bug 1568570
|
||||
[browser_fullscreen_cross_origin.js]
|
||||
support-files = fullscreen.html fullscreen_frame.html
|
||||
[browser_fullscreen_window_open.js]
|
||||
skip-if = debug && os == 'mac' # Bug 1568570
|
||||
[browser_fullscreen_window_focus.js]
|
||||
skip-if = debug && os == 'mac' # Bug 1568570
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// This test tends to trigger a race in the fullscreen time telemetry,
|
||||
// where the fullscreen enter and fullscreen exit events (which use the
|
||||
// same histogram ID) overlap. That causes TelemetryStopwatch to log an
|
||||
// error.
|
||||
SimpleTest.ignoreAllUncaughtExceptions(true);
|
||||
|
||||
const TEST_URL =
|
||||
"http://example.com/browser/browser/base/content/test/fullscreen/open_and_focus_helper.html";
|
||||
const IFRAME_ID = "testIframe";
|
||||
|
||||
async function testWindowFocus(iframeID) {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
info("Calling window.open()");
|
||||
let popup = await jsWindowOpen(tab.linkedBrowser, iframeID);
|
||||
info("re-focusing main window");
|
||||
await waitForFocus();
|
||||
|
||||
info("Entering full-screen");
|
||||
await changeFullscreen(tab.linkedBrowser, true);
|
||||
|
||||
await testExpectFullScreenExit(tab.linkedBrowser, true, async () => {
|
||||
info("Calling window.focus()");
|
||||
await jsWindowFocus(tab.linkedBrowser, iframeID);
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
popup.close();
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["dom.disable_open_during_load", false], // Allow window.focus calls without user interaction
|
||||
["browser.link.open_newwindow.disabled_in_fullscreen", false],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function test_parentWindowFocus() {
|
||||
return testWindowFocus();
|
||||
});
|
||||
|
||||
add_task(function test_iframeWindowFocus() {
|
||||
return testWindowFocus(IFRAME_ID);
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// This test tends to trigger a race in the fullscreen time telemetry,
|
||||
// where the fullscreen enter and fullscreen exit events (which use the
|
||||
// same histogram ID) overlap. That causes TelemetryStopwatch to log an
|
||||
// error.
|
||||
SimpleTest.ignoreAllUncaughtExceptions(true);
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
|
||||
const TEST_URL =
|
||||
"http://example.com/browser/browser/base/content/test/fullscreen/open_and_focus_helper.html";
|
||||
const IFRAME_ID = "testIframe";
|
||||
|
||||
async function testWindowOpen(iframeID) {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
info("Entering full-screen");
|
||||
await changeFullscreen(tab.linkedBrowser, true);
|
||||
|
||||
let popup;
|
||||
await testExpectFullScreenExit(tab.linkedBrowser, true, async () => {
|
||||
info("Calling window.open()");
|
||||
popup = await jsWindowOpen(tab.linkedBrowser, iframeID);
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
await BrowserTestUtils.closeWindow(popup);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["dom.disable_open_during_load", false], // Allow window.open calls without user interaction
|
||||
["browser.link.open_newwindow.disabled_in_fullscreen", false],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function test_parentWindowOpen() {
|
||||
return testWindowOpen();
|
||||
});
|
||||
|
||||
add_task(function test_iframeWindowOpen() {
|
||||
return testWindowOpen(IFRAME_ID);
|
||||
});
|
|
@ -2,7 +2,6 @@ const { ContentTaskUtils } = ChromeUtils.import(
|
|||
"resource://testing-common/ContentTaskUtils.jsm"
|
||||
);
|
||||
function waitForFullScreenState(browser, state) {
|
||||
info("inside waitforfullscreenstate");
|
||||
return new Promise(resolve => {
|
||||
let eventReceived = false;
|
||||
|
||||
|
@ -15,7 +14,7 @@ function waitForFullScreenState(browser, state) {
|
|||
};
|
||||
Services.obs.addObserver(observe, "fullscreen-painted");
|
||||
|
||||
window.addEventListener(
|
||||
browser.ownerGlobal.addEventListener(
|
||||
`MozDOMFullscreen:${state ? "Entered" : "Exited"}`,
|
||||
() => {
|
||||
eventReceived = true;
|
||||
|
@ -50,3 +49,78 @@ async function changeFullscreen(browser, fullScreenState) {
|
|||
});
|
||||
return fullScreenChange;
|
||||
}
|
||||
|
||||
async function testExpectFullScreenExit(browser, leaveFS, action) {
|
||||
let fsPromise = waitForFullScreenState(browser, !leaveFS);
|
||||
if (leaveFS) {
|
||||
if (action) {
|
||||
await action();
|
||||
}
|
||||
await fsPromise;
|
||||
ok(true, "Should leave full-screen");
|
||||
} else {
|
||||
if (action) {
|
||||
await action();
|
||||
}
|
||||
let result = await Promise.race([
|
||||
fsPromise,
|
||||
new Promise(resolve => {
|
||||
SimpleTest.requestFlakyTimeout("Wait for failure condition");
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
setTimeout(() => resolve(true), 2500);
|
||||
}),
|
||||
]);
|
||||
ok(result, "Should not leave full-screen");
|
||||
}
|
||||
}
|
||||
|
||||
function jsWindowFocus(browser, iframeId) {
|
||||
return ContentTask.spawn(browser, { iframeId }, async args => {
|
||||
let destWin = content;
|
||||
if (args.iframeId) {
|
||||
let iframe = content.document.getElementById(args.iframeId);
|
||||
if (!iframe) {
|
||||
throw new Error("iframe not set");
|
||||
}
|
||||
destWin = iframe.contentWindow;
|
||||
}
|
||||
await content.wrappedJSObject.sendMessage(destWin, "focus");
|
||||
});
|
||||
}
|
||||
|
||||
async function jsWindowOpen(browser, iframeId) {
|
||||
let windowOpened = BrowserTestUtils.waitForNewWindow();
|
||||
ContentTask.spawn(browser, { iframeId }, async args => {
|
||||
let destWin = content;
|
||||
if (args.iframeId) {
|
||||
// Create a cross origin iframe
|
||||
destWin = (await content.wrappedJSObject.createIframe(
|
||||
args.iframeId,
|
||||
true
|
||||
)).contentWindow;
|
||||
}
|
||||
// Send message to either the iframe or the current page to open a popup
|
||||
await content.wrappedJSObject.sendMessage(destWin, "open");
|
||||
});
|
||||
return windowOpened;
|
||||
}
|
||||
|
||||
function waitForFocus(...args) {
|
||||
return new Promise(resolve => SimpleTest.waitForFocus(resolve, ...args));
|
||||
}
|
||||
|
||||
function waitForBrowserWindowActive(win) {
|
||||
return new Promise(resolve => {
|
||||
if (Services.focus.activeWindow == win) {
|
||||
resolve();
|
||||
} else {
|
||||
win.addEventListener(
|
||||
"activate",
|
||||
() => {
|
||||
resolve();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const MY_ORIGIN = window.location.origin;
|
||||
const CROSS_ORIGIN = "https://example.org";
|
||||
|
||||
// Creates an iframe with message channel to trigger window open and focus
|
||||
window.createIframe = function(id, crossOrigin = false) {
|
||||
return new Promise(resolve => {
|
||||
const origin = crossOrigin ? CROSS_ORIGIN : MY_ORIGIN;
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.id = id;
|
||||
iframe.src = origin + window.location.pathname;
|
||||
iframe.onload = () => resolve(iframe);
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
window.sendMessage = function(destWin, msg) {
|
||||
return new Promise(resolve => {
|
||||
let channel = new MessageChannel();
|
||||
channel.port1.onmessage = resolve;
|
||||
destWin.postMessage(msg, "*", [channel.port2]);
|
||||
});
|
||||
}
|
||||
|
||||
window.onMessage = function(event) {
|
||||
let canReply = event.ports && !!event.ports.length;
|
||||
if(event.data === "open") {
|
||||
window.popup = window.open('https://example.com', '', 'top=0,height=1, width=300');
|
||||
if (canReply) event.ports[0].postMessage('opened');
|
||||
} else if(event.data === "focus") {
|
||||
window.popup.focus();
|
||||
if (canReply) event.ports[0].postMessage('focused');
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', window.onMessage);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -775,12 +775,14 @@ async function runUrlbarTest(
|
|||
// So we just whitelist the whole urlbar. We don't check the bottom of
|
||||
// the rect because the result view height varies depending on the
|
||||
// results.
|
||||
// We use floor/ceil because the Urlbar dimensions aren't always
|
||||
// integers.
|
||||
return rects.filter(
|
||||
r =>
|
||||
!(
|
||||
r.x1 >= urlbarRect.left - SHADOW_SIZE &&
|
||||
r.x2 <= urlbarRect.right + SHADOW_SIZE &&
|
||||
r.y1 >= urlbarRect.top - SHADOW_SIZE
|
||||
r.x1 >= Math.floor(urlbarRect.left) - SHADOW_SIZE &&
|
||||
r.x2 <= Math.ceil(urlbarRect.right) + SHADOW_SIZE &&
|
||||
r.y1 >= Math.floor(urlbarRect.top) - SHADOW_SIZE
|
||||
)
|
||||
);
|
||||
},
|
||||
|
@ -800,16 +802,16 @@ async function runUrlbarTest(
|
|||
r =>
|
||||
!// We put text into the urlbar so expect its textbox to change.
|
||||
(
|
||||
(r.x1 >= textBoxRect.left &&
|
||||
r.x2 <= textBoxRect.right &&
|
||||
r.y1 >= textBoxRect.top &&
|
||||
r.y2 <= textBoxRect.bottom) ||
|
||||
(r.x1 >= Math.floor(textBoxRect.left) &&
|
||||
r.x2 <= Math.ceil(textBoxRect.right) &&
|
||||
r.y1 >= Math.floor(textBoxRect.top) &&
|
||||
r.y2 <= Math.ceil(textBoxRect.bottom)) ||
|
||||
// The dropmarker is displayed as active during some of the test.
|
||||
// dropmarkerRect.left isn't always an integer, hence the - 1 and + 1
|
||||
(r.x1 >= dropmarkerRect.left - 1 &&
|
||||
r.x2 <= dropmarkerRect.right + 1 &&
|
||||
r.y1 >= dropmarkerRect.top &&
|
||||
r.y2 <= dropmarkerRect.bottom)
|
||||
// dropmarkerRect.left isn't always an integer.
|
||||
(r.x1 >= Math.floor(dropmarkerRect.left) &&
|
||||
r.x2 <= Math.ceil(dropmarkerRect.right) &&
|
||||
r.y1 >= Math.floor(dropmarkerRect.top) &&
|
||||
r.y2 <= Math.ceil(dropmarkerRect.bottom))
|
||||
)
|
||||
),
|
||||
};
|
||||
|
|
|
@ -23,6 +23,11 @@ add_task(async function() {
|
|||
1,
|
||||
"Related tab (relatedToCurrent) inherits current tab's usercontextid"
|
||||
);
|
||||
is(
|
||||
relatedTab.linkedBrowser.contentPrincipal.userContextId,
|
||||
1,
|
||||
"Related tab's browser actually inherits the current tab's usercontextid"
|
||||
);
|
||||
BrowserTestUtils.removeTab(relatedTab);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
|
@ -35,6 +40,11 @@ add_task(async function() {
|
|||
2,
|
||||
"Related tab (relatedToCurrent) with overridden usercontextid"
|
||||
);
|
||||
is(
|
||||
relatedTab.linkedBrowser.contentPrincipal.userContextId,
|
||||
2,
|
||||
"Related tab's browser actually gets overridden usercontextid"
|
||||
);
|
||||
BrowserTestUtils.removeTab(relatedTab);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
|
@ -51,6 +61,11 @@ add_task(async function() {
|
|||
1,
|
||||
"Related tab (referrer) inherits current tab's usercontextid"
|
||||
);
|
||||
is(
|
||||
relatedTab.linkedBrowser.contentPrincipal.userContextId,
|
||||
1,
|
||||
"Related tab's browser (referrer) actually inherits the current tab's usercontextid"
|
||||
);
|
||||
BrowserTestUtils.removeTab(relatedTab);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
|
@ -68,6 +83,11 @@ add_task(async function() {
|
|||
2,
|
||||
"Related tab (referrer) with overridden usercontextid"
|
||||
);
|
||||
is(
|
||||
relatedTab.linkedBrowser.contentPrincipal.userContextId,
|
||||
2,
|
||||
"Related tab's browser (referrer) actually gets overridden usercontextid"
|
||||
);
|
||||
BrowserTestUtils.removeTab(relatedTab);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
|
|
@ -377,6 +377,8 @@ CustomizeMode.prototype = {
|
|||
|
||||
this._wrapToolbarItemSync(CustomizableUI.AREA_TABSTRIP);
|
||||
|
||||
this.document.documentElement.setAttribute("customizing", true);
|
||||
|
||||
let customizableToolbars = document.querySelectorAll(
|
||||
"toolbar[customizable=true]:not([autohide=true]):not([collapsed=true])"
|
||||
);
|
||||
|
@ -386,8 +388,6 @@ CustomizeMode.prototype = {
|
|||
|
||||
this._updateOverflowPanelArrowOffset();
|
||||
|
||||
await this._doTransition(true);
|
||||
|
||||
// Let everybody in this window know that we're about to customize.
|
||||
CustomizableUI.dispatchToolboxEvent("customizationstarting", {}, window);
|
||||
|
||||
|
@ -489,15 +489,17 @@ CustomizeMode.prototype = {
|
|||
(async () => {
|
||||
await this.depopulatePalette();
|
||||
|
||||
await this._doTransition(false);
|
||||
// We need to set this._customizing to false and remove the `customizing`
|
||||
// attribute before removing the tab or else
|
||||
// XULBrowserWindow.onLocationChange might think that we're still in
|
||||
// customization mode and need to exit it for a second time.
|
||||
this._customizing = false;
|
||||
this.document.documentElement.removeAttribute("customizing");
|
||||
|
||||
if (this.browser.selectedTab == gTab) {
|
||||
if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
|
||||
closeGlobalTab();
|
||||
} else {
|
||||
unregisterGlobalTab();
|
||||
}
|
||||
closeGlobalTab();
|
||||
}
|
||||
|
||||
let customizer = document.getElementById("customization-container");
|
||||
let browser = document.getElementById("browser");
|
||||
customizer.hidden = true;
|
||||
|
@ -525,11 +527,6 @@ CustomizeMode.prototype = {
|
|||
let panelContextMenu = document.getElementById(kPanelItemContextMenu);
|
||||
this._previousPanelContextMenuParent.appendChild(panelContextMenu);
|
||||
|
||||
// We need to set this._customizing to false before removing the tab
|
||||
// or the TabSelect event handler will think that we are exiting
|
||||
// customization mode for a second time.
|
||||
this._customizing = false;
|
||||
|
||||
let customizableToolbars = document.querySelectorAll(
|
||||
"toolbar[customizable=true]:not([autohide=true])"
|
||||
);
|
||||
|
@ -555,40 +552,6 @@ CustomizeMode.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* The customize mode transition has 4 phases when entering:
|
||||
* 1) Pre-customization mode
|
||||
* This is the starting phase of the browser.
|
||||
* 2) LWT swapping
|
||||
* This is where we swap some of the lightweight theme styles in order
|
||||
* to make them work in customize mode. We set/unset a customization-
|
||||
* lwtheme attribute iff we're using a lightweight theme.
|
||||
* 3) customize-entering
|
||||
* This phase is a transition, optimized for smoothness.
|
||||
* 4) customize-entered
|
||||
* After the transition completes, this phase draws all of the
|
||||
* expensive detail that isn't necessary during the second phase.
|
||||
*
|
||||
* Exiting customization mode has a similar set of phases, but in reverse
|
||||
* order - customize-entered, customize-exiting, remove LWT swapping,
|
||||
* pre-customization mode.
|
||||
*
|
||||
* When in the customize-entering, customize-entered, or customize-exiting
|
||||
* phases, there is a "customizing" attribute set on the main-window to simplify
|
||||
* excluding certain styles while in any phase of customize mode.
|
||||
*/
|
||||
_doTransition(aEntering) {
|
||||
let docEl = this.document.documentElement;
|
||||
if (aEntering) {
|
||||
docEl.setAttribute("customizing", true);
|
||||
docEl.setAttribute("customize-entered", true);
|
||||
} else {
|
||||
docEl.removeAttribute("customizing");
|
||||
docEl.removeAttribute("customize-entered");
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* The overflow panel in customize mode should have its arrow pointing
|
||||
* at the overflow button. In order to do this correctly, we pass the
|
||||
|
|
|
@ -245,37 +245,24 @@ function endCustomizing(aWindow = window) {
|
|||
if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
|
||||
return true;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
function onCustomizationEnds() {
|
||||
aWindow.gNavToolbox.removeEventListener(
|
||||
"aftercustomization",
|
||||
onCustomizationEnds
|
||||
);
|
||||
resolve();
|
||||
}
|
||||
aWindow.gNavToolbox.addEventListener(
|
||||
"aftercustomization",
|
||||
onCustomizationEnds
|
||||
);
|
||||
aWindow.gCustomizeMode.exit();
|
||||
});
|
||||
let afterCustomizationPromise = BrowserTestUtils.waitForEvent(
|
||||
aWindow.gNavToolbox,
|
||||
"aftercustomization"
|
||||
);
|
||||
aWindow.gCustomizeMode.exit();
|
||||
return afterCustomizationPromise;
|
||||
}
|
||||
|
||||
function startCustomizing(aWindow = window) {
|
||||
if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
|
||||
return null;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
function onCustomizing() {
|
||||
aWindow.gNavToolbox.removeEventListener(
|
||||
"customizationready",
|
||||
onCustomizing
|
||||
);
|
||||
resolve();
|
||||
}
|
||||
aWindow.gNavToolbox.addEventListener("customizationready", onCustomizing);
|
||||
aWindow.gCustomizeMode.enter();
|
||||
});
|
||||
let customizationReadyPromise = BrowserTestUtils.waitForEvent(
|
||||
aWindow.gNavToolbox,
|
||||
"customizationready"
|
||||
);
|
||||
aWindow.gCustomizeMode.enter();
|
||||
return customizationReadyPromise;
|
||||
}
|
||||
|
||||
function promiseObserverNotified(aTopic) {
|
||||
|
|
|
@ -110,11 +110,11 @@ Event Value
|
|||
``topsites``. To know whether the user actually picked a top site, check
|
||||
check that ``numChars`` == 0. If ``numChars`` > 0, the user initially opened
|
||||
top sites, but then they started typing and confirmed a different result.
|
||||
``returned``. The user abandoned a search, for example by switching to
|
||||
- ``returned``: The user abandoned a search, for example by switching to
|
||||
another tab/window, or focusing something else, then came back to it
|
||||
and continued. We consider a search continued if the user kept at least the
|
||||
first char of the original search string.
|
||||
``restarted``. The user abandoned a search, for example by switching to
|
||||
- ``restarted``: The user abandoned a search, for example by switching to
|
||||
another tab/window, or focusing something else, then came back to it,
|
||||
cleared it and then typed a new string.
|
||||
|
||||
|
|
|
@ -639,7 +639,9 @@ const tests = [
|
|||
|
||||
async function() {
|
||||
info("Retained result: type, blur, focus, confirm.");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.update1", true]],
|
||||
});
|
||||
await promiseAutocompleteResultPopup("search", window, true);
|
||||
await UrlbarTestUtils.promisePopupClose(window, () => {
|
||||
gURLBar.blur();
|
||||
|
@ -651,6 +653,7 @@ const tests = [
|
|||
let promise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
EventUtils.synthesizeKey("VK_RETURN");
|
||||
await promise;
|
||||
await SpecialPowers.popPrefEnv();
|
||||
return [
|
||||
{
|
||||
category: "urlbar",
|
||||
|
@ -700,7 +703,9 @@ const tests = [
|
|||
|
||||
async function() {
|
||||
info("Retained result: type, blur, focus, backspace, type, confirm.");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.update1", true]],
|
||||
});
|
||||
await promiseAutocompleteResultPopup("search", window, true);
|
||||
await UrlbarTestUtils.promisePopupClose(window, () => {
|
||||
gURLBar.blur();
|
||||
|
@ -715,6 +720,7 @@ const tests = [
|
|||
let promise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
EventUtils.synthesizeKey("VK_RETURN");
|
||||
await promise;
|
||||
await SpecialPowers.popPrefEnv();
|
||||
return [
|
||||
{
|
||||
category: "urlbar",
|
||||
|
@ -743,7 +749,9 @@ const tests = [
|
|||
|
||||
async function() {
|
||||
info("Retained result: type, blur, focus, type (overwrite), confirm.");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.update1", true]],
|
||||
});
|
||||
await promiseAutocompleteResultPopup("search", window, true);
|
||||
await UrlbarTestUtils.promisePopupClose(window, () => {
|
||||
gURLBar.blur();
|
||||
|
@ -756,6 +764,7 @@ const tests = [
|
|||
let promise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
EventUtils.synthesizeKey("VK_RETURN");
|
||||
await promise;
|
||||
await SpecialPowers.popPrefEnv();
|
||||
return [
|
||||
{
|
||||
category: "urlbar",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
TOOLTOOL_DIR=${topsrcdir}
|
||||
export MAKECAB=$TOOLTOOL_DIR/makecab.exe
|
||||
export LLVM_CONFIG="${MOZ_FETCHES_DIR}/clang/bin/llvm-config"
|
||||
export DUMP_SYMS="${MOZ_FETCHES_DIR}/dump_syms/dump_syms.exe"
|
||||
|
||||
RUSTC="${MOZ_FETCHES_DIR}/rustc/bin/rustc"
|
||||
CARGO="${MOZ_FETCHES_DIR}/rustc/bin/cargo"
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
|
||||
/* Draw the bottom border of the tabs toolbar when it's not using
|
||||
-moz-appearance: toolbar. */
|
||||
:root:-moz-any([sizemode="fullscreen"],[customize-entered]) #nav-bar:not([tabs-hidden="true"]),
|
||||
:root:-moz-any([sizemode="fullscreen"],[customizing]) #nav-bar:not([tabs-hidden="true"]),
|
||||
:root:not([tabsintitlebar]) #nav-bar:not([tabs-hidden="true"]),
|
||||
#nav-bar:not([tabs-hidden="true"]):-moz-lwtheme {
|
||||
box-shadow: 0 calc(-1 * var(--tabs-navbar-shadow-size)) 0 var(--tabs-border-color);
|
||||
|
|
|
@ -226,7 +226,7 @@
|
|||
|
||||
@keyframes urlbar-grow {
|
||||
0% {
|
||||
transform: scaleX(.98) scaleY(.9);
|
||||
transform: scaleX(.99) scaleY(.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.0);
|
||||
|
|
|
@ -714,6 +714,7 @@ def toolchain_search_path_for(host_or_target):
|
|||
os.path.join(mozbuild_state_path, *rest) for rest in (
|
||||
['clang', 'bin'],
|
||||
['cbindgen'],
|
||||
['dump_syms'],
|
||||
['nasm'],
|
||||
['lucetc'],
|
||||
)]
|
||||
|
|
|
@ -19,6 +19,7 @@ unset RUSTDOC
|
|||
unset RUSTFMT
|
||||
unset CBINDGEN
|
||||
unset MAKECAB
|
||||
unset DUMP_SYMS
|
||||
unset TOOLCHAIN_PREFIX
|
||||
unset BINDGEN_CFLAGS
|
||||
unset LLVM_CONFIG
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
|
||||
export MAKECAB=$TOOLTOOL_DIR/makecab.exe
|
||||
export DUMP_SYMS="${MOZ_FETCHES_DIR}/dump_syms/dump_syms.exe"
|
||||
|
||||
if [ -z "$USE_ARTIFACT" ]; then
|
||||
if [ -n "$TASKCLUSTER_PGO_PROFILE_USE" ]; then
|
||||
|
|
|
@ -419,14 +419,8 @@ function focusTab(tab) {
|
|||
* Create an iframe that can be used to load DevTools via about:devtools-toolbox.
|
||||
*/
|
||||
function createDevToolsFrame(doc, className) {
|
||||
let frame;
|
||||
if (Services.prefs.getBoolPref("devtools.toolbox.content-frame", false)) {
|
||||
frame = doc.createXULElement("browser");
|
||||
frame.setAttribute("type", "content");
|
||||
} else {
|
||||
frame = doc.createXULElement("iframe");
|
||||
}
|
||||
|
||||
const frame = doc.createXULElement("browser");
|
||||
frame.setAttribute("type", "content");
|
||||
frame.flex = 1; // Required to be able to shrink when the window shrinks
|
||||
frame.className = className;
|
||||
frame.tooltip = "aHTMLTooltip";
|
||||
|
|
|
@ -516,7 +516,7 @@ html #webconsole-notificationbox {
|
|||
}
|
||||
|
||||
.eager-evaluation-result .objectBox {
|
||||
height: 15px;
|
||||
height: 18px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -238,7 +238,7 @@ function terminalInputChanged(expression) {
|
|||
let mapped;
|
||||
({ expression, mapped } = await getMappedExpression(hud, expression));
|
||||
|
||||
const { frameActor, webConsoleFront } = webConsoleUI.getFrameActor();
|
||||
const { frameActor, webConsoleFront } = await webConsoleUI.getFrameActor();
|
||||
|
||||
const response = await client.evaluateJSAsync(expression, {
|
||||
frameActor,
|
||||
|
|
|
@ -75,6 +75,7 @@ skip-if = (os == "win" && processor == "aarch64") # disabled on aarch64 due to 1
|
|||
[browser_jsterm_ctrl_key_nav.js]
|
||||
skip-if = os != 'mac' # The tested ctrl+key shortcuts are OSX only
|
||||
[browser_jsterm_document_no_xray.js]
|
||||
[browser_jsterm_eager_evaluation_in_debugger_stackframe.js]
|
||||
[browser_jsterm_eager_evaluation.js]
|
||||
[browser_jsterm_editor.js]
|
||||
[browser_jsterm_editor_disabled_history_nav_with_keyboard.js]
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that eager evaluation works as expected when paused in the debugger.
|
||||
|
||||
const TEST_URI = `data:text/html;charset=utf-8,
|
||||
<script>
|
||||
var x = "global";
|
||||
|
||||
function pauseInDebugger(param) {
|
||||
let x = "local";
|
||||
debugger;
|
||||
}
|
||||
|
||||
</script>
|
||||
`;
|
||||
|
||||
const EAGER_EVALUATION_PREF = "devtools.webconsole.input.eagerEvaluation";
|
||||
|
||||
add_task(async function() {
|
||||
await pushPref(EAGER_EVALUATION_PREF, true);
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
const target = await TargetFactory.forTab(gBrowser.selectedTab);
|
||||
const toolbox = gDevTools.getToolbox(target);
|
||||
|
||||
setInputValue(hud, "x");
|
||||
await waitForEagerEvaluationResult(hud, `"global"`);
|
||||
|
||||
info("Open Debugger");
|
||||
await openDebugger();
|
||||
const dbg = createDebuggerContext(toolbox);
|
||||
|
||||
info("Pause in Debugger");
|
||||
SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
|
||||
content.wrappedJSObject.pauseInDebugger("myParam");
|
||||
});
|
||||
await pauseDebugger(dbg);
|
||||
|
||||
info("Opening Console");
|
||||
await toolbox.selectTool("webconsole");
|
||||
|
||||
info("Check that the parameter is eagerly evaluated as expected");
|
||||
setInputValue(hud, "param");
|
||||
await waitForEagerEvaluationResult(hud, `"myParam"`);
|
||||
|
||||
setInputValue(hud, "x");
|
||||
await waitForEagerEvaluationResult(hud, `"local"`);
|
||||
|
||||
await resume(dbg);
|
||||
});
|
|
@ -511,81 +511,6 @@ function writeFile(fileName, content) {
|
|||
}
|
||||
}
|
||||
|
||||
function connectPipeTracing() {
|
||||
return new TracingTransport(DebuggerServer.connectPipe());
|
||||
}
|
||||
|
||||
function TracingTransport(childTransport) {
|
||||
this.hooks = null;
|
||||
this.child = childTransport;
|
||||
this.child.hooks = this;
|
||||
|
||||
this.expectations = [];
|
||||
this.packets = [];
|
||||
this.checkIndex = 0;
|
||||
}
|
||||
|
||||
TracingTransport.prototype = {
|
||||
// Remove actor names
|
||||
normalize: function(packet) {
|
||||
return JSON.parse(
|
||||
JSON.stringify(packet, (key, value) => {
|
||||
if (key === "to" || key === "from" || key === "actor") {
|
||||
return "<actorid>";
|
||||
}
|
||||
return value;
|
||||
})
|
||||
);
|
||||
},
|
||||
send: function(packet) {
|
||||
this.packets.push({
|
||||
type: "sent",
|
||||
packet: this.normalize(packet),
|
||||
});
|
||||
return this.child.send(packet);
|
||||
},
|
||||
close: function() {
|
||||
return this.child.close();
|
||||
},
|
||||
ready: function() {
|
||||
return this.child.ready();
|
||||
},
|
||||
onPacket: function(packet) {
|
||||
this.packets.push({
|
||||
type: "received",
|
||||
packet: this.normalize(packet),
|
||||
});
|
||||
this.hooks.onPacket(packet);
|
||||
},
|
||||
onClosed: function() {
|
||||
this.hooks.onClosed();
|
||||
},
|
||||
|
||||
expectSend: function(expected) {
|
||||
const packet = this.packets[this.checkIndex++];
|
||||
Assert.equal(packet.type, "sent");
|
||||
deepEqual(packet.packet, this.normalize(expected));
|
||||
},
|
||||
|
||||
expectReceive: function(expected) {
|
||||
const packet = this.packets[this.checkIndex++];
|
||||
Assert.equal(packet.type, "received");
|
||||
deepEqual(packet.packet, this.normalize(expected));
|
||||
},
|
||||
|
||||
// Write your tests, call dumpLog at the end, inspect the output,
|
||||
// then sprinkle the calls through the right places in your test.
|
||||
dumpLog: function() {
|
||||
for (const entry of this.packets) {
|
||||
if (entry.type === "sent") {
|
||||
dumpn("trace.expectSend(" + entry.packet + ");");
|
||||
} else {
|
||||
dumpn("trace.expectReceive(" + entry.packet + ");");
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function StubTransport() {}
|
||||
StubTransport.prototype.ready = function() {};
|
||||
StubTransport.prototype.send = function() {};
|
||||
|
|
|
@ -1,671 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test simple requests using the protocol helpers.
|
||||
*/
|
||||
var protocol = require("devtools/shared/protocol");
|
||||
var { types, Arg, RetVal } = protocol;
|
||||
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
function simpleHello() {
|
||||
return {
|
||||
from: "root",
|
||||
applicationType: "xpcshell-tests",
|
||||
traits: [],
|
||||
};
|
||||
}
|
||||
|
||||
// Predeclaring the actor type so that it can be used in the
|
||||
// implementation of the child actor.
|
||||
types.addActorType("childActor");
|
||||
|
||||
const childSpec = protocol.generateActorSpec({
|
||||
typeName: "childActor",
|
||||
|
||||
events: {
|
||||
event1: {
|
||||
a: Arg(0),
|
||||
b: Arg(1),
|
||||
c: Arg(2),
|
||||
},
|
||||
event2: {
|
||||
a: Arg(0),
|
||||
b: Arg(1),
|
||||
c: Arg(2),
|
||||
},
|
||||
"named-event": {
|
||||
type: "namedEvent",
|
||||
a: Arg(0),
|
||||
b: Arg(1),
|
||||
c: Arg(2),
|
||||
},
|
||||
"object-event": {
|
||||
type: "objectEvent",
|
||||
detail: Arg(0, "childActor#actorid"),
|
||||
},
|
||||
"array-object-event": {
|
||||
type: "arrayObjectEvent",
|
||||
detail: Arg(0, "array:childActor#actorid"),
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
echo: {
|
||||
request: { str: Arg(0) },
|
||||
response: { str: RetVal("string") },
|
||||
},
|
||||
getDetail1: {
|
||||
response: {
|
||||
child: RetVal("childActor#actorid"),
|
||||
},
|
||||
},
|
||||
getDetail2: {
|
||||
response: {
|
||||
child: RetVal("childActor#actorid"),
|
||||
},
|
||||
},
|
||||
getIDDetail: {
|
||||
response: {
|
||||
idDetail: RetVal("childActor#actorid"),
|
||||
},
|
||||
},
|
||||
getIntArray: {
|
||||
request: { inputArray: Arg(0, "array:number") },
|
||||
response: RetVal("array:number"),
|
||||
},
|
||||
getSibling: {
|
||||
request: { id: Arg(0) },
|
||||
response: { sibling: RetVal("childActor") },
|
||||
},
|
||||
emitEvents: {
|
||||
response: { value: RetVal("string") },
|
||||
},
|
||||
release: {
|
||||
release: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
var ChildActor = protocol.ActorClassWithSpec(childSpec, {
|
||||
// Actors returned by this actor should be owned by the root actor.
|
||||
marshallPool: function() {
|
||||
return this.parent();
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return "[ChildActor " + this.childID + "]";
|
||||
},
|
||||
|
||||
initialize: function(conn, id) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.childID = id;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
protocol.Actor.prototype.destroy.call(this);
|
||||
this.destroyed = true;
|
||||
},
|
||||
|
||||
form: function() {
|
||||
return {
|
||||
actor: this.actorID,
|
||||
childID: this.childID,
|
||||
};
|
||||
},
|
||||
|
||||
echo: function(str) {
|
||||
return str;
|
||||
},
|
||||
|
||||
getDetail1: function() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getDetail2: function() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getIDDetail: function() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getIntArray: function(inputArray) {
|
||||
// Test that protocol.js converts an iterator to an array.
|
||||
const f = function*() {
|
||||
for (const i of inputArray) {
|
||||
yield 2 * i;
|
||||
}
|
||||
};
|
||||
return f();
|
||||
},
|
||||
|
||||
getSibling: function(id) {
|
||||
return this.parent().getChild(id);
|
||||
},
|
||||
|
||||
emitEvents: function() {
|
||||
EventEmitter.emit(this, "event1", 1, 2, 3);
|
||||
EventEmitter.emit(this, "event2", 4, 5, 6);
|
||||
EventEmitter.emit(this, "named-event", 1, 2, 3);
|
||||
EventEmitter.emit(this, "object-event", this);
|
||||
EventEmitter.emit(this, "array-object-event", [this]);
|
||||
return "correct response";
|
||||
},
|
||||
|
||||
release: function() {},
|
||||
});
|
||||
|
||||
class ChildFront extends protocol.FrontClassWithSpec(childSpec) {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
|
||||
this.before("event1", this.onEvent1.bind(this));
|
||||
this.before("event2", this.onEvent2a.bind(this));
|
||||
this.on("event2", this.onEvent2b.bind(this));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
marshallPool() {
|
||||
return this.parent();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[child front " + this.childID + "]";
|
||||
}
|
||||
|
||||
form(form) {
|
||||
this.childID = form.childID;
|
||||
}
|
||||
|
||||
onEvent1(a, b, c) {
|
||||
this.event1arg3 = c;
|
||||
}
|
||||
|
||||
onEvent2a(a, b, c) {
|
||||
return Promise.resolve().then(() => {
|
||||
this.event2arg3 = c;
|
||||
});
|
||||
}
|
||||
|
||||
onEvent2b(a, b, c) {
|
||||
this.event2arg2 = b;
|
||||
}
|
||||
}
|
||||
protocol.registerFront(ChildFront);
|
||||
|
||||
types.addDictType("manyChildrenDict", {
|
||||
child5: "childActor",
|
||||
more: "array:childActor",
|
||||
});
|
||||
|
||||
types.addLifetime("temp", "_temporaryHolder");
|
||||
|
||||
const rootSpec = protocol.generateActorSpec({
|
||||
typeName: "root",
|
||||
|
||||
methods: {
|
||||
getChild: {
|
||||
request: { str: Arg(0) },
|
||||
response: { actor: RetVal("childActor") },
|
||||
},
|
||||
getChildren: {
|
||||
request: { ids: Arg(0, "array:string") },
|
||||
response: { children: RetVal("array:childActor") },
|
||||
},
|
||||
getChildren2: {
|
||||
request: { ids: Arg(0, "array:childActor") },
|
||||
response: { children: RetVal("array:childActor") },
|
||||
},
|
||||
getManyChildren: {
|
||||
response: RetVal("manyChildrenDict"),
|
||||
},
|
||||
getTemporaryChild: {
|
||||
request: { id: Arg(0) },
|
||||
response: { child: RetVal("temp:childActor") },
|
||||
},
|
||||
clearTemporaryChildren: {},
|
||||
},
|
||||
});
|
||||
|
||||
var rootActor = null;
|
||||
var RootActor = protocol.ActorClassWithSpec(rootSpec, {
|
||||
toString: function() {
|
||||
return "[root actor]";
|
||||
},
|
||||
|
||||
initialize: function(conn) {
|
||||
rootActor = this;
|
||||
this.actorID = "root";
|
||||
this._children = {};
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
},
|
||||
|
||||
sayHello: simpleHello,
|
||||
|
||||
getChild: function(id) {
|
||||
if (id in this._children) {
|
||||
return this._children[id];
|
||||
}
|
||||
const child = new ChildActor(this.conn, id);
|
||||
this._children[id] = child;
|
||||
return child;
|
||||
},
|
||||
|
||||
getChildren: function(ids) {
|
||||
return ids.map(id => this.getChild(id));
|
||||
},
|
||||
|
||||
getChildren2: function(ids) {
|
||||
const f = function*() {
|
||||
for (const c of ids) {
|
||||
yield c;
|
||||
}
|
||||
};
|
||||
return f();
|
||||
},
|
||||
|
||||
getManyChildren: function() {
|
||||
return {
|
||||
// note that this isn't in the specialization array.
|
||||
foo: "bar",
|
||||
child5: this.getChild("child5"),
|
||||
more: [this.getChild("child6"), this.getChild("child7")],
|
||||
};
|
||||
},
|
||||
|
||||
// This should remind you of a pause actor.
|
||||
getTemporaryChild: function(id) {
|
||||
if (!this._temporaryHolder) {
|
||||
this._temporaryHolder = new protocol.Actor(this.conn);
|
||||
this.manage(this._temporaryHolder);
|
||||
}
|
||||
return new ChildActor(this.conn, id);
|
||||
},
|
||||
|
||||
clearTemporaryChildren: function(id) {
|
||||
if (!this._temporaryHolder) {
|
||||
return;
|
||||
}
|
||||
this._temporaryHolder.destroy();
|
||||
delete this._temporaryHolder;
|
||||
},
|
||||
});
|
||||
|
||||
class RootFront extends protocol.FrontClassWithSpec(rootSpec) {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.actorID = "root";
|
||||
// Root actor owns itself.
|
||||
this.manage(this);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[root front]";
|
||||
}
|
||||
|
||||
getTemporaryChild(id) {
|
||||
if (!this._temporaryHolder) {
|
||||
this._temporaryHolder = new protocol.Front(this.conn);
|
||||
this._temporaryHolder.actorID = this.actorID + "_temp";
|
||||
this.manage(this._temporaryHolder);
|
||||
}
|
||||
return super.getTemporaryChild(id);
|
||||
}
|
||||
|
||||
clearTemporaryChildren() {
|
||||
if (!this._temporaryHolder) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
this._temporaryHolder.destroy();
|
||||
delete this._temporaryHolder;
|
||||
return super.clearTemporaryChildren();
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
DebuggerServer.createRootActor = conn => {
|
||||
return RootActor(conn);
|
||||
};
|
||||
DebuggerServer.init();
|
||||
|
||||
const trace = connectPipeTracing();
|
||||
const client = new DebuggerClient(trace);
|
||||
client.connect().then(([applicationType, traits]) => {
|
||||
trace.expectReceive({
|
||||
from: "<actorid>",
|
||||
applicationType: "xpcshell-tests",
|
||||
traits: [],
|
||||
});
|
||||
Assert.equal(applicationType, "xpcshell-tests");
|
||||
|
||||
const rootFront = new RootFront(client);
|
||||
let childFront = null;
|
||||
|
||||
const expectRootChildren = size => {
|
||||
Assert.equal(rootActor._poolMap.size, size);
|
||||
Assert.equal(rootFront._poolMap.size, size + 1);
|
||||
if (childFront) {
|
||||
Assert.equal(childFront._poolMap.size, 0);
|
||||
}
|
||||
};
|
||||
|
||||
rootFront
|
||||
.getChild("child1")
|
||||
.then(ret => {
|
||||
trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
|
||||
trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
|
||||
|
||||
childFront = ret;
|
||||
Assert.ok(childFront instanceof ChildFront);
|
||||
Assert.equal(childFront.childID, "child1");
|
||||
expectRootChildren(1);
|
||||
})
|
||||
.then(() => {
|
||||
// Request the child again, make sure the same is returned.
|
||||
return rootFront.getChild("child1");
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
|
||||
trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
|
||||
|
||||
expectRootChildren(1);
|
||||
Assert.ok(ret === childFront);
|
||||
})
|
||||
.then(() => {
|
||||
return childFront.echo("hello");
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({ type: "echo", str: "hello", to: "<actorid>" });
|
||||
trace.expectReceive({ str: "hello", from: "<actorid>" });
|
||||
|
||||
Assert.equal(ret, "hello");
|
||||
})
|
||||
.then(() => {
|
||||
return childFront.getDetail1();
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({ type: "getDetail1", to: "<actorid>" });
|
||||
trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
|
||||
Assert.ok(ret === childFront);
|
||||
})
|
||||
.then(() => {
|
||||
return childFront.getDetail2();
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({ type: "getDetail2", to: "<actorid>" });
|
||||
trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
|
||||
Assert.ok(ret === childFront);
|
||||
})
|
||||
.then(() => {
|
||||
return childFront.getIDDetail();
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({ type: "getIDDetail", to: "<actorid>" });
|
||||
trace.expectReceive({
|
||||
idDetail: childFront.actorID,
|
||||
from: "<actorid>",
|
||||
});
|
||||
Assert.ok(ret === childFront);
|
||||
})
|
||||
.then(() => {
|
||||
return childFront.getSibling("siblingID");
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({
|
||||
type: "getSibling",
|
||||
id: "siblingID",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
sibling: { actor: "<actorid>", childID: "siblingID" },
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
expectRootChildren(2);
|
||||
})
|
||||
.then(ret => {
|
||||
return rootFront.getTemporaryChild("temp1").then(temp1 => {
|
||||
trace.expectSend({
|
||||
type: "getTemporaryChild",
|
||||
id: "temp1",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
child: { actor: "<actorid>", childID: "temp1" },
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
// At this point we expect two direct children, plus the temporary holder
|
||||
// which should hold 1 itself.
|
||||
Assert.equal(rootActor._temporaryHolder.__poolMap.size, 1);
|
||||
Assert.equal(rootFront._temporaryHolder.__poolMap.size, 1);
|
||||
|
||||
expectRootChildren(3);
|
||||
return rootFront.getTemporaryChild("temp2").then(temp2 => {
|
||||
trace.expectSend({
|
||||
type: "getTemporaryChild",
|
||||
id: "temp2",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
child: { actor: "<actorid>", childID: "temp2" },
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
// Same amount of direct children, and an extra in the temporary holder.
|
||||
expectRootChildren(3);
|
||||
Assert.equal(rootActor._temporaryHolder.__poolMap.size, 2);
|
||||
Assert.equal(rootFront._temporaryHolder.__poolMap.size, 2);
|
||||
|
||||
// Get the children of the temporary holder...
|
||||
const checkActors = rootActor._temporaryHolder.__poolMap.values();
|
||||
|
||||
// Now release the temporary holders and expect them to drop again.
|
||||
return rootFront.clearTemporaryChildren().then(() => {
|
||||
trace.expectSend({
|
||||
type: "clearTemporaryChildren",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({ from: "<actorid>" });
|
||||
|
||||
expectRootChildren(2);
|
||||
Assert.ok(!rootActor._temporaryHolder);
|
||||
Assert.ok(!rootFront._temporaryHolder);
|
||||
for (const checkActor of checkActors) {
|
||||
Assert.ok(checkActor.destroyed);
|
||||
Assert.ok(checkActor.destroyed);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(ret => {
|
||||
return rootFront.getChildren(["child1", "child2"]);
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({
|
||||
type: "getChildren",
|
||||
ids: ["child1", "child2"],
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
children: [
|
||||
{ actor: "<actorid>", childID: "child1" },
|
||||
{ actor: "<actorid>", childID: "child2" },
|
||||
],
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
expectRootChildren(3);
|
||||
Assert.ok(ret[0] === childFront);
|
||||
Assert.ok(ret[1] !== childFront);
|
||||
Assert.ok(ret[1] instanceof ChildFront);
|
||||
|
||||
// On both children, listen to events. We're only
|
||||
// going to trigger events on the first child, so an event
|
||||
// triggered on the second should cause immediate failures.
|
||||
|
||||
const set = new Set([
|
||||
"event1",
|
||||
"event2",
|
||||
"named-event",
|
||||
"object-event",
|
||||
"array-object-event",
|
||||
]);
|
||||
|
||||
childFront.on("event1", (a, b, c) => {
|
||||
Assert.equal(a, 1);
|
||||
Assert.equal(b, 2);
|
||||
Assert.equal(c, 3);
|
||||
// Verify that the pre-event handler was called.
|
||||
Assert.equal(childFront.event1arg3, 3);
|
||||
set.delete("event1");
|
||||
});
|
||||
childFront.on("event2", (a, b, c) => {
|
||||
Assert.equal(a, 4);
|
||||
Assert.equal(b, 5);
|
||||
Assert.equal(c, 6);
|
||||
// Verify that the async pre-event handler was called,
|
||||
// setting the property before this handler was called.
|
||||
Assert.equal(childFront.event2arg3, 6);
|
||||
// And check that the sync preEvent with the same name is also
|
||||
// executed
|
||||
Assert.equal(childFront.event2arg2, 5);
|
||||
set.delete("event2");
|
||||
});
|
||||
childFront.on("named-event", (a, b, c) => {
|
||||
Assert.equal(a, 1);
|
||||
Assert.equal(b, 2);
|
||||
Assert.equal(c, 3);
|
||||
set.delete("named-event");
|
||||
});
|
||||
childFront.on("object-event", obj => {
|
||||
Assert.ok(obj === childFront);
|
||||
set.delete("object-event");
|
||||
});
|
||||
childFront.on("array-object-event", array => {
|
||||
Assert.ok(array[0] === childFront);
|
||||
set.delete("array-object-event");
|
||||
});
|
||||
|
||||
const fail = function() {
|
||||
do_throw("Unexpected event");
|
||||
};
|
||||
ret[1].on("event1", fail);
|
||||
ret[1].on("event2", fail);
|
||||
ret[1].on("named-event", fail);
|
||||
ret[1].on("object-event", fail);
|
||||
ret[1].on("array-object-event", fail);
|
||||
|
||||
return childFront.emitEvents().then(() => {
|
||||
trace.expectSend({ type: "emitEvents", to: "<actorid>" });
|
||||
trace.expectReceive({
|
||||
type: "event1",
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "event2",
|
||||
a: 4,
|
||||
b: 5,
|
||||
c: 6,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "namedEvent",
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "objectEvent",
|
||||
detail: childFront.actorID,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "arrayObjectEvent",
|
||||
detail: [childFront.actorID],
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({ value: "correct response", from: "<actorid>" });
|
||||
|
||||
Assert.equal(set.size, 0);
|
||||
});
|
||||
})
|
||||
.then(ret => {
|
||||
return rootFront.getManyChildren();
|
||||
})
|
||||
.then(ret => {
|
||||
trace.expectSend({ type: "getManyChildren", to: "<actorid>" });
|
||||
trace.expectReceive({
|
||||
foo: "bar",
|
||||
child5: { actor: "<actorid>", childID: "child5" },
|
||||
more: [
|
||||
{ actor: "<actorid>", childID: "child6" },
|
||||
{ actor: "<actorid>", childID: "child7" },
|
||||
],
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
// Check all the crazy stuff we did in getManyChildren
|
||||
Assert.equal(ret.foo, "bar");
|
||||
Assert.equal(ret.child5.childID, "child5");
|
||||
Assert.equal(ret.more[0].childID, "child6");
|
||||
Assert.equal(ret.more[1].childID, "child7");
|
||||
})
|
||||
.then(() => {
|
||||
// Test accepting a generator.
|
||||
const f = function*() {
|
||||
for (const i of [1, 2, 3, 4, 5]) {
|
||||
yield i;
|
||||
}
|
||||
};
|
||||
return childFront.getIntArray(f());
|
||||
})
|
||||
.then(ret => {
|
||||
Assert.equal(ret.length, 5);
|
||||
const expected = [2, 4, 6, 8, 10];
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
Assert.equal(ret[i], expected[i]);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return rootFront.getChildren(["child1", "child2"]);
|
||||
})
|
||||
.then(ids => {
|
||||
const f = function*() {
|
||||
for (const id of ids) {
|
||||
yield id;
|
||||
}
|
||||
};
|
||||
return rootFront.getChildren2(f());
|
||||
})
|
||||
.then(ret => {
|
||||
Assert.equal(ret.length, 2);
|
||||
Assert.ok(ret[0] === childFront);
|
||||
Assert.ok(ret[1] !== childFront);
|
||||
Assert.ok(ret[1] instanceof ChildFront);
|
||||
})
|
||||
.then(() => {
|
||||
client.close().then(() => {
|
||||
do_test_finished();
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
do_report_unexpected_exception(err, "Failure executing test");
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
|
@ -81,14 +81,6 @@ support-files =
|
|||
[test_nativewrappers.js]
|
||||
[test_nodelistactor.js]
|
||||
[test_format_command.js]
|
||||
[test_protocol_abort.js]
|
||||
[test_protocol_async.js]
|
||||
[test_protocol_children.js]
|
||||
[test_protocol_longstring.js]
|
||||
[test_protocol_simple.js]
|
||||
[test_protocol_stack.js]
|
||||
[test_protocol_unregister.js]
|
||||
[test_protocol_watchFronts.js]
|
||||
[test_breakpoint-01.js]
|
||||
[test_register_actor.js]
|
||||
[test_breakpoint-02.js]
|
||||
|
|
|
@ -18,3 +18,5 @@ DevToolsModules(
|
|||
'types.js',
|
||||
'utils.js',
|
||||
)
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
// Extend from the common devtools xpcshell eslintrc config.
|
||||
"extends": "../../../../.eslintrc.xpcshell.js"
|
||||
};
|
|
@ -0,0 +1,94 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
|
||||
const Services = require("Services");
|
||||
const { DebuggerServer } = require("devtools/server/debugger-server");
|
||||
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
const defer = require("devtools/shared/defer");
|
||||
|
||||
function dumpn(msg) {
|
||||
dump("DBG-TEST: " + msg + "\n");
|
||||
}
|
||||
|
||||
function connectPipeTracing() {
|
||||
return new TracingTransport(DebuggerServer.connectPipe());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock the `Transport` class in order to intercept all the packet
|
||||
* getting in and out and then being able to assert them and dump them.
|
||||
*/
|
||||
function TracingTransport(childTransport) {
|
||||
this.hooks = null;
|
||||
this.child = childTransport;
|
||||
this.child.hooks = this;
|
||||
|
||||
this.expectations = [];
|
||||
this.packets = [];
|
||||
this.checkIndex = 0;
|
||||
}
|
||||
|
||||
TracingTransport.prototype = {
|
||||
// Remove actor names
|
||||
normalize: function(packet) {
|
||||
return JSON.parse(
|
||||
JSON.stringify(packet, (key, value) => {
|
||||
if (key === "to" || key === "from" || key === "actor") {
|
||||
return "<actorid>";
|
||||
}
|
||||
return value;
|
||||
})
|
||||
);
|
||||
},
|
||||
send: function(packet) {
|
||||
this.packets.push({
|
||||
type: "sent",
|
||||
packet: this.normalize(packet),
|
||||
});
|
||||
return this.child.send(packet);
|
||||
},
|
||||
close: function() {
|
||||
return this.child.close();
|
||||
},
|
||||
ready: function() {
|
||||
return this.child.ready();
|
||||
},
|
||||
onPacket: function(packet) {
|
||||
this.packets.push({
|
||||
type: "received",
|
||||
packet: this.normalize(packet),
|
||||
});
|
||||
this.hooks.onPacket(packet);
|
||||
},
|
||||
onClosed: function() {
|
||||
this.hooks.onClosed();
|
||||
},
|
||||
|
||||
expectSend: function(expected) {
|
||||
const packet = this.packets[this.checkIndex++];
|
||||
Assert.equal(packet.type, "sent");
|
||||
deepEqual(packet.packet, this.normalize(expected));
|
||||
},
|
||||
|
||||
expectReceive: function(expected) {
|
||||
const packet = this.packets[this.checkIndex++];
|
||||
Assert.equal(packet.type, "received");
|
||||
deepEqual(packet.packet, this.normalize(expected));
|
||||
},
|
||||
|
||||
// Write your tests, call dumpLog at the end, inspect the output,
|
||||
// then sprinkle the calls through the right places in your test.
|
||||
dumpLog: function() {
|
||||
for (const entry of this.packets) {
|
||||
if (entry.type === "sent") {
|
||||
dumpn("trace.expectSend(" + entry.packet + ");");
|
||||
} else {
|
||||
dumpn("trace.expectReceive(" + entry.packet + ");");
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
|
@ -0,0 +1,714 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test simple requests using the protocol helpers.
|
||||
*/
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const { types, Arg, RetVal } = protocol;
|
||||
|
||||
function simpleHello() {
|
||||
return {
|
||||
from: "root",
|
||||
applicationType: "xpcshell-tests",
|
||||
traits: [],
|
||||
};
|
||||
}
|
||||
|
||||
// Predeclaring the actor type so that it can be used in the
|
||||
// implementation of the child actor.
|
||||
types.addActorType("childActor");
|
||||
types.addActorType("otherChildActor");
|
||||
types.addPolymorphicType("polytype", ["childActor", "otherChildActor"]);
|
||||
|
||||
const childSpec = protocol.generateActorSpec({
|
||||
typeName: "childActor",
|
||||
|
||||
events: {
|
||||
event1: {
|
||||
a: Arg(0),
|
||||
b: Arg(1),
|
||||
c: Arg(2),
|
||||
},
|
||||
event2: {
|
||||
a: Arg(0),
|
||||
b: Arg(1),
|
||||
c: Arg(2),
|
||||
},
|
||||
"named-event": {
|
||||
type: "namedEvent",
|
||||
a: Arg(0),
|
||||
b: Arg(1),
|
||||
c: Arg(2),
|
||||
},
|
||||
"object-event": {
|
||||
type: "objectEvent",
|
||||
detail: Arg(0, "childActor#actorid"),
|
||||
},
|
||||
"array-object-event": {
|
||||
type: "arrayObjectEvent",
|
||||
detail: Arg(0, "array:childActor#actorid"),
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
echo: {
|
||||
request: { str: Arg(0) },
|
||||
response: { str: RetVal("string") },
|
||||
},
|
||||
getDetail1: {
|
||||
response: {
|
||||
child: RetVal("childActor#actorid"),
|
||||
},
|
||||
},
|
||||
getDetail2: {
|
||||
response: {
|
||||
child: RetVal("childActor#actorid"),
|
||||
},
|
||||
},
|
||||
getIDDetail: {
|
||||
response: {
|
||||
idDetail: RetVal("childActor#actorid"),
|
||||
},
|
||||
},
|
||||
getIntArray: {
|
||||
request: { inputArray: Arg(0, "array:number") },
|
||||
response: RetVal("array:number"),
|
||||
},
|
||||
getSibling: {
|
||||
request: { id: Arg(0) },
|
||||
response: { sibling: RetVal("childActor") },
|
||||
},
|
||||
emitEvents: {
|
||||
response: { value: RetVal("string") },
|
||||
},
|
||||
release: {
|
||||
release: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
var ChildActor = protocol.ActorClassWithSpec(childSpec, {
|
||||
// Actors returned by this actor should be owned by the root actor.
|
||||
marshallPool() {
|
||||
return this.parent();
|
||||
},
|
||||
|
||||
toString() {
|
||||
return "[ChildActor " + this.childID + "]";
|
||||
},
|
||||
|
||||
initialize(conn, id) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.childID = id;
|
||||
},
|
||||
|
||||
destroy() {
|
||||
protocol.Actor.prototype.destroy.call(this);
|
||||
this.destroyed = true;
|
||||
},
|
||||
|
||||
form() {
|
||||
return {
|
||||
actor: this.actorID,
|
||||
childID: this.childID,
|
||||
};
|
||||
},
|
||||
|
||||
echo(str) {
|
||||
return str;
|
||||
},
|
||||
|
||||
getDetail1() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getDetail2() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getIDDetail() {
|
||||
return this;
|
||||
},
|
||||
|
||||
getIntArray(inputArray) {
|
||||
// Test that protocol.js converts an iterator to an array.
|
||||
const f = function*() {
|
||||
for (const i of inputArray) {
|
||||
yield 2 * i;
|
||||
}
|
||||
};
|
||||
return f();
|
||||
},
|
||||
|
||||
getSibling(id) {
|
||||
return this.parent().getChild(id);
|
||||
},
|
||||
|
||||
emitEvents() {
|
||||
this.emit("event1", 1, 2, 3);
|
||||
this.emit("event2", 4, 5, 6);
|
||||
this.emit("named-event", 1, 2, 3);
|
||||
this.emit("object-event", this);
|
||||
this.emit("array-object-event", [this]);
|
||||
return "correct response";
|
||||
},
|
||||
|
||||
release() {},
|
||||
});
|
||||
|
||||
class ChildFront extends protocol.FrontClassWithSpec(childSpec) {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
|
||||
this.before("event1", this.onEvent1.bind(this));
|
||||
this.before("event2", this.onEvent2a.bind(this));
|
||||
this.on("event2", this.onEvent2b.bind(this));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
marshallPool() {
|
||||
return this.parent();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[child front " + this.childID + "]";
|
||||
}
|
||||
|
||||
form(form) {
|
||||
this.childID = form.childID;
|
||||
}
|
||||
|
||||
onEvent1(a, b, c) {
|
||||
this.event1arg3 = c;
|
||||
}
|
||||
|
||||
onEvent2a(a, b, c) {
|
||||
return Promise.resolve().then(() => {
|
||||
this.event2arg3 = c;
|
||||
});
|
||||
}
|
||||
|
||||
onEvent2b(a, b, c) {
|
||||
this.event2arg2 = b;
|
||||
}
|
||||
}
|
||||
protocol.registerFront(ChildFront);
|
||||
|
||||
const otherChildSpec = protocol.generateActorSpec({
|
||||
typeName: "otherChildActor",
|
||||
methods: {},
|
||||
events: {},
|
||||
});
|
||||
const OtherChildActor = protocol.ActorClassWithSpec(otherChildSpec, {});
|
||||
class OtherChildFront extends protocol.FrontClassWithSpec(otherChildSpec) {}
|
||||
protocol.registerFront(OtherChildFront);
|
||||
|
||||
types.addDictType("manyChildrenDict", {
|
||||
child5: "childActor",
|
||||
more: "array:childActor",
|
||||
});
|
||||
|
||||
types.addLifetime("temp", "_temporaryHolder");
|
||||
|
||||
const rootSpec = protocol.generateActorSpec({
|
||||
typeName: "root",
|
||||
|
||||
methods: {
|
||||
getChild: {
|
||||
request: { str: Arg(0) },
|
||||
response: { actor: RetVal("childActor") },
|
||||
},
|
||||
getChildren: {
|
||||
request: { ids: Arg(0, "array:string") },
|
||||
response: { children: RetVal("array:childActor") },
|
||||
},
|
||||
getChildren2: {
|
||||
request: { ids: Arg(0, "array:childActor") },
|
||||
response: { children: RetVal("array:childActor") },
|
||||
},
|
||||
getManyChildren: {
|
||||
response: RetVal("manyChildrenDict"),
|
||||
},
|
||||
getTemporaryChild: {
|
||||
request: { id: Arg(0) },
|
||||
response: { child: RetVal("temp:childActor") },
|
||||
},
|
||||
getPolymorphism: {
|
||||
request: { id: Arg(0, "number") },
|
||||
response: { child: RetVal("polytype") },
|
||||
},
|
||||
requestPolymorphism: {
|
||||
request: {
|
||||
id: Arg(0, "number"),
|
||||
actor: Arg(1, "polytype"),
|
||||
},
|
||||
response: { child: RetVal("polytype") },
|
||||
},
|
||||
clearTemporaryChildren: {},
|
||||
},
|
||||
});
|
||||
|
||||
let rootActor = null;
|
||||
const RootActor = protocol.ActorClassWithSpec(rootSpec, {
|
||||
toString() {
|
||||
return "[root actor]";
|
||||
},
|
||||
|
||||
initialize(conn) {
|
||||
rootActor = this;
|
||||
this.actorID = "root";
|
||||
this._children = {};
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
},
|
||||
|
||||
sayHello: simpleHello,
|
||||
|
||||
getChild(id) {
|
||||
if (id in this._children) {
|
||||
return this._children[id];
|
||||
}
|
||||
const child = new ChildActor(this.conn, id);
|
||||
this._children[id] = child;
|
||||
return child;
|
||||
},
|
||||
|
||||
getChildren(ids) {
|
||||
return ids.map(id => this.getChild(id));
|
||||
},
|
||||
|
||||
getChildren2(ids) {
|
||||
const f = function*() {
|
||||
for (const c of ids) {
|
||||
yield c;
|
||||
}
|
||||
};
|
||||
return f();
|
||||
},
|
||||
|
||||
getManyChildren() {
|
||||
return {
|
||||
// note that this isn't in the specialization array.
|
||||
foo: "bar",
|
||||
child5: this.getChild("child5"),
|
||||
more: [this.getChild("child6"), this.getChild("child7")],
|
||||
};
|
||||
},
|
||||
|
||||
// This should remind you of a pause actor.
|
||||
getTemporaryChild(id) {
|
||||
if (!this._temporaryHolder) {
|
||||
this._temporaryHolder = new protocol.Actor(this.conn);
|
||||
this.manage(this._temporaryHolder);
|
||||
}
|
||||
return new ChildActor(this.conn, id);
|
||||
},
|
||||
|
||||
clearTemporaryChildren(id) {
|
||||
if (!this._temporaryHolder) {
|
||||
return;
|
||||
}
|
||||
this._temporaryHolder.destroy();
|
||||
delete this._temporaryHolder;
|
||||
},
|
||||
|
||||
getPolymorphism: function(id) {
|
||||
if (id == 0) {
|
||||
return new ChildActor(this.conn, id);
|
||||
} else if (id == 1) {
|
||||
return new OtherChildActor(this.conn);
|
||||
}
|
||||
throw new Error("Unexpected id");
|
||||
},
|
||||
|
||||
requestPolymorphism: function(id, actor) {
|
||||
if (id == 0 && actor instanceof ChildActor) {
|
||||
return actor;
|
||||
} else if (id == 1 && actor instanceof OtherChildActor) {
|
||||
return actor;
|
||||
}
|
||||
throw new Error("Unexpected id or actor");
|
||||
},
|
||||
});
|
||||
|
||||
class RootFront extends protocol.FrontClassWithSpec(rootSpec) {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.actorID = "root";
|
||||
// Root actor owns itself.
|
||||
this.manage(this);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[root front]";
|
||||
}
|
||||
|
||||
getTemporaryChild(id) {
|
||||
if (!this._temporaryHolder) {
|
||||
this._temporaryHolder = new protocol.Front(this.conn);
|
||||
this._temporaryHolder.actorID = this.actorID + "_temp";
|
||||
this.manage(this._temporaryHolder);
|
||||
}
|
||||
return super.getTemporaryChild(id);
|
||||
}
|
||||
|
||||
clearTemporaryChildren() {
|
||||
if (!this._temporaryHolder) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
this._temporaryHolder.destroy();
|
||||
delete this._temporaryHolder;
|
||||
return super.clearTemporaryChildren();
|
||||
}
|
||||
}
|
||||
|
||||
let rootFront, childFront;
|
||||
function expectRootChildren(size) {
|
||||
Assert.equal(rootActor._poolMap.size, size);
|
||||
Assert.equal(rootFront._poolMap.size, size + 1);
|
||||
if (childFront) {
|
||||
Assert.equal(childFront._poolMap.size, 0);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
DebuggerServer.createRootActor = conn => {
|
||||
return RootActor(conn);
|
||||
};
|
||||
DebuggerServer.init();
|
||||
|
||||
const trace = connectPipeTracing();
|
||||
const client = new DebuggerClient(trace);
|
||||
const [applicationType] = await client.connect();
|
||||
trace.expectReceive({
|
||||
from: "<actorid>",
|
||||
applicationType: "xpcshell-tests",
|
||||
traits: [],
|
||||
});
|
||||
Assert.equal(applicationType, "xpcshell-tests");
|
||||
|
||||
rootFront = new RootFront(client);
|
||||
|
||||
await testSimpleChildren(trace);
|
||||
await testDetail(trace);
|
||||
await testSibling(trace);
|
||||
await testTemporary(trace);
|
||||
await testEvents(trace);
|
||||
await testManyChildren(trace);
|
||||
await testGenerator(trace);
|
||||
await testPolymorphism(trace);
|
||||
|
||||
await client.close();
|
||||
});
|
||||
|
||||
async function testSimpleChildren(trace) {
|
||||
childFront = await rootFront.getChild("child1");
|
||||
trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
|
||||
trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
|
||||
|
||||
Assert.ok(childFront instanceof ChildFront);
|
||||
Assert.equal(childFront.childID, "child1");
|
||||
expectRootChildren(1);
|
||||
|
||||
// Request the child again, make sure the same is returned.
|
||||
let ret = await rootFront.getChild("child1");
|
||||
trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
|
||||
trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
|
||||
|
||||
expectRootChildren(1);
|
||||
Assert.ok(ret === childFront);
|
||||
|
||||
ret = await childFront.echo("hello");
|
||||
trace.expectSend({ type: "echo", str: "hello", to: "<actorid>" });
|
||||
trace.expectReceive({ str: "hello", from: "<actorid>" });
|
||||
|
||||
Assert.equal(ret, "hello");
|
||||
}
|
||||
|
||||
async function testDetail(trace) {
|
||||
let ret = await childFront.getDetail1();
|
||||
trace.expectSend({ type: "getDetail1", to: "<actorid>" });
|
||||
trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
|
||||
Assert.ok(ret === childFront);
|
||||
|
||||
ret = await childFront.getDetail2();
|
||||
trace.expectSend({ type: "getDetail2", to: "<actorid>" });
|
||||
trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
|
||||
Assert.ok(ret === childFront);
|
||||
|
||||
ret = await childFront.getIDDetail();
|
||||
trace.expectSend({ type: "getIDDetail", to: "<actorid>" });
|
||||
trace.expectReceive({
|
||||
idDetail: childFront.actorID,
|
||||
from: "<actorid>",
|
||||
});
|
||||
Assert.ok(ret === childFront);
|
||||
}
|
||||
|
||||
async function testSibling(trace) {
|
||||
await childFront.getSibling("siblingID");
|
||||
trace.expectSend({
|
||||
type: "getSibling",
|
||||
id: "siblingID",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
sibling: { actor: "<actorid>", childID: "siblingID" },
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
expectRootChildren(2);
|
||||
}
|
||||
|
||||
async function testTemporary(trace) {
|
||||
await rootFront.getTemporaryChild("temp1");
|
||||
trace.expectSend({
|
||||
type: "getTemporaryChild",
|
||||
id: "temp1",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
child: { actor: "<actorid>", childID: "temp1" },
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
// At this point we expect two direct children, plus the temporary holder
|
||||
// which should hold 1 itself.
|
||||
Assert.equal(rootActor._temporaryHolder.__poolMap.size, 1);
|
||||
Assert.equal(rootFront._temporaryHolder.__poolMap.size, 1);
|
||||
|
||||
expectRootChildren(3);
|
||||
|
||||
await rootFront.getTemporaryChild("temp2");
|
||||
trace.expectSend({
|
||||
type: "getTemporaryChild",
|
||||
id: "temp2",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
child: { actor: "<actorid>", childID: "temp2" },
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
// Same amount of direct children, and an extra in the temporary holder.
|
||||
expectRootChildren(3);
|
||||
Assert.equal(rootActor._temporaryHolder.__poolMap.size, 2);
|
||||
Assert.equal(rootFront._temporaryHolder.__poolMap.size, 2);
|
||||
|
||||
// Get the children of the temporary holder...
|
||||
const checkActors = rootActor._temporaryHolder.__poolMap.values();
|
||||
|
||||
// Now release the temporary holders and expect them to drop again.
|
||||
await rootFront.clearTemporaryChildren();
|
||||
trace.expectSend({
|
||||
type: "clearTemporaryChildren",
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({ from: "<actorid>" });
|
||||
|
||||
expectRootChildren(2);
|
||||
Assert.ok(!rootActor._temporaryHolder);
|
||||
Assert.ok(!rootFront._temporaryHolder);
|
||||
for (const checkActor of checkActors) {
|
||||
Assert.ok(checkActor.destroyed);
|
||||
Assert.ok(checkActor.destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
async function testEvents(trace) {
|
||||
const ret = await rootFront.getChildren(["child1", "child2"]);
|
||||
trace.expectSend({
|
||||
type: "getChildren",
|
||||
ids: ["child1", "child2"],
|
||||
to: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
children: [
|
||||
{ actor: "<actorid>", childID: "child1" },
|
||||
{ actor: "<actorid>", childID: "child2" },
|
||||
],
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
expectRootChildren(3);
|
||||
Assert.ok(ret[0] === childFront);
|
||||
Assert.ok(ret[1] !== childFront);
|
||||
Assert.ok(ret[1] instanceof ChildFront);
|
||||
|
||||
// On both children, listen to events. We're only
|
||||
// going to trigger events on the first child, so an event
|
||||
// triggered on the second should cause immediate failures.
|
||||
|
||||
const set = new Set([
|
||||
"event1",
|
||||
"event2",
|
||||
"named-event",
|
||||
"object-event",
|
||||
"array-object-event",
|
||||
]);
|
||||
|
||||
childFront.on("event1", (a, b, c) => {
|
||||
Assert.equal(a, 1);
|
||||
Assert.equal(b, 2);
|
||||
Assert.equal(c, 3);
|
||||
// Verify that the pre-event handler was called.
|
||||
Assert.equal(childFront.event1arg3, 3);
|
||||
set.delete("event1");
|
||||
});
|
||||
childFront.on("event2", (a, b, c) => {
|
||||
Assert.equal(a, 4);
|
||||
Assert.equal(b, 5);
|
||||
Assert.equal(c, 6);
|
||||
// Verify that the async pre-event handler was called,
|
||||
// setting the property before this handler was called.
|
||||
Assert.equal(childFront.event2arg3, 6);
|
||||
// And check that the sync preEvent with the same name is also
|
||||
// executed
|
||||
Assert.equal(childFront.event2arg2, 5);
|
||||
set.delete("event2");
|
||||
});
|
||||
childFront.on("named-event", (a, b, c) => {
|
||||
Assert.equal(a, 1);
|
||||
Assert.equal(b, 2);
|
||||
Assert.equal(c, 3);
|
||||
set.delete("named-event");
|
||||
});
|
||||
childFront.on("object-event", obj => {
|
||||
Assert.ok(obj === childFront);
|
||||
set.delete("object-event");
|
||||
});
|
||||
childFront.on("array-object-event", array => {
|
||||
Assert.ok(array[0] === childFront);
|
||||
set.delete("array-object-event");
|
||||
});
|
||||
|
||||
const fail = function() {
|
||||
do_throw("Unexpected event");
|
||||
};
|
||||
ret[1].on("event1", fail);
|
||||
ret[1].on("event2", fail);
|
||||
ret[1].on("named-event", fail);
|
||||
ret[1].on("object-event", fail);
|
||||
ret[1].on("array-object-event", fail);
|
||||
|
||||
await childFront.emitEvents();
|
||||
trace.expectSend({ type: "emitEvents", to: "<actorid>" });
|
||||
trace.expectReceive({
|
||||
type: "event1",
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "event2",
|
||||
a: 4,
|
||||
b: 5,
|
||||
c: 6,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "namedEvent",
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "objectEvent",
|
||||
detail: childFront.actorID,
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({
|
||||
type: "arrayObjectEvent",
|
||||
detail: [childFront.actorID],
|
||||
from: "<actorid>",
|
||||
});
|
||||
trace.expectReceive({ value: "correct response", from: "<actorid>" });
|
||||
|
||||
Assert.equal(set.size, 0);
|
||||
}
|
||||
|
||||
async function testManyChildren(trace) {
|
||||
const ret = await rootFront.getManyChildren();
|
||||
trace.expectSend({ type: "getManyChildren", to: "<actorid>" });
|
||||
trace.expectReceive({
|
||||
foo: "bar",
|
||||
child5: { actor: "<actorid>", childID: "child5" },
|
||||
more: [
|
||||
{ actor: "<actorid>", childID: "child6" },
|
||||
{ actor: "<actorid>", childID: "child7" },
|
||||
],
|
||||
from: "<actorid>",
|
||||
});
|
||||
|
||||
// Check all the crazy stuff we did in getManyChildren
|
||||
Assert.equal(ret.foo, "bar");
|
||||
Assert.equal(ret.child5.childID, "child5");
|
||||
Assert.equal(ret.more[0].childID, "child6");
|
||||
Assert.equal(ret.more[1].childID, "child7");
|
||||
}
|
||||
|
||||
async function testGenerator(trace) {
|
||||
// Test accepting a generator.
|
||||
const f = function*() {
|
||||
for (const i of [1, 2, 3, 4, 5]) {
|
||||
yield i;
|
||||
}
|
||||
};
|
||||
let ret = await childFront.getIntArray(f());
|
||||
Assert.equal(ret.length, 5);
|
||||
const expected = [2, 4, 6, 8, 10];
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
Assert.equal(ret[i], expected[i]);
|
||||
}
|
||||
|
||||
const ids = await rootFront.getChildren(["child1", "child2"]);
|
||||
const f2 = function*() {
|
||||
for (const id of ids) {
|
||||
yield id;
|
||||
}
|
||||
};
|
||||
ret = await rootFront.getChildren2(f2());
|
||||
Assert.equal(ret.length, 2);
|
||||
Assert.ok(ret[0] === childFront);
|
||||
Assert.ok(ret[1] !== childFront);
|
||||
Assert.ok(ret[1] instanceof ChildFront);
|
||||
}
|
||||
|
||||
async function testPolymorphism(trace) {
|
||||
// Check polymorphic types returned by an actor
|
||||
const firstChild = await rootFront.getPolymorphism(0);
|
||||
Assert.ok(firstChild instanceof ChildFront);
|
||||
|
||||
// Check polymorphic types passed to a front
|
||||
const sameFirstChild = await rootFront.requestPolymorphism(0, firstChild);
|
||||
Assert.ok(sameFirstChild instanceof ChildFront);
|
||||
Assert.equal(sameFirstChild, firstChild);
|
||||
|
||||
// Same with the second possible type
|
||||
const secondChild = await rootFront.getPolymorphism(1);
|
||||
Assert.ok(secondChild instanceof OtherChildFront);
|
||||
|
||||
const sameSecondChild = await rootFront.requestPolymorphism(1, secondChild);
|
||||
Assert.ok(sameSecondChild instanceof OtherChildFront);
|
||||
Assert.equal(sameSecondChild, secondChild);
|
||||
|
||||
// Check that any other type is rejected
|
||||
Assert.throws(() => {
|
||||
rootFront.requestPolymorphism(0, null);
|
||||
}, /Was expecting one of these actors 'childActor,otherChildActor' but instead got an empty value/);
|
||||
Assert.throws(() => {
|
||||
rootFront.requestPolymorphism(0, 42);
|
||||
}, /Was expecting one of these actors 'childActor,otherChildActor' but instead got value: '42'/);
|
||||
Assert.throws(() => {
|
||||
rootFront.requestPolymorphism(0, rootFront);
|
||||
}, /Was expecting one of these actors 'childActor,otherChildActor' but instead got an actor of type: 'root'/);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
"use strict";
|
||||
|
||||
const { types } = require("devtools/shared/protocol");
|
||||
|
||||
function run_test() {
|
||||
types.addActorType("myActor1");
|
||||
types.addActorType("myActor2");
|
||||
types.addActorType("myActor3");
|
||||
|
||||
types.addPolymorphicType("ptype1", ["myActor1", "myActor2"]);
|
||||
const ptype1 = types.getType("ptype1");
|
||||
Assert.equal(ptype1.name, "ptype1");
|
||||
Assert.equal(ptype1.category, "polymorphic");
|
||||
|
||||
types.addPolymorphicType("ptype2", ["myActor1", "myActor2", "myActor3"]);
|
||||
const ptype2 = types.getType("ptype2");
|
||||
Assert.equal(ptype2.name, "ptype2");
|
||||
Assert.equal(ptype2.category, "polymorphic");
|
||||
|
||||
// Polymorphic types only accept actor types
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["myActor1", "myActor4"]);
|
||||
Assert.ok(false, "getType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(ex.toString(), "Error: Unknown type: myActor4");
|
||||
}
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["myActor1", "string"]);
|
||||
Assert.ok(false, "getType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'myActor1,string', the type 'string' isn't an actor"
|
||||
);
|
||||
}
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["myActor1", "boolean"]);
|
||||
Assert.ok(false, "getType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'myActor1,boolean', the type 'boolean' isn't an actor"
|
||||
);
|
||||
}
|
||||
|
||||
// Polymorphic types are not compatible with array or nullables
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["array:myActor1", "myActor2"]);
|
||||
Assert.ok(false, "addType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'array:myActor1,myActor2', the type 'array:myActor1' isn't an actor"
|
||||
);
|
||||
}
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["nullable:myActor1", "myActor2"]);
|
||||
Assert.ok(false, "addType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'nullable:myActor1,myActor2', the type 'nullable:myActor1' isn't an actor"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
head = head.js
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android'
|
||||
support-files =
|
||||
|
||||
[test_protocol_abort.js]
|
||||
[test_protocol_async.js]
|
||||
[test_protocol_children.js]
|
||||
[test_protocol_index.js]
|
||||
[test_protocol_longstring.js]
|
||||
[test_protocol_simple.js]
|
||||
[test_protocol_stack.js]
|
||||
[test_protocol_types.js]
|
||||
[test_protocol_unregister.js]
|
||||
[test_protocol_watchFronts.js]
|
|
@ -370,6 +370,68 @@ types.addActorType = function(name) {
|
|||
return type;
|
||||
};
|
||||
|
||||
types.addPolymorphicType = function(name, subtypes) {
|
||||
// Assert that all subtypes are actors, as the marshalling implementation depends on that.
|
||||
for (const subTypeName of subtypes) {
|
||||
const subtype = types.getType(subTypeName);
|
||||
if (subtype.category != "actor") {
|
||||
throw new Error(
|
||||
`In polymorphic type '${subtypes.join(
|
||||
","
|
||||
)}', the type '${subTypeName}' isn't an actor`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return types.addType(name, {
|
||||
category: "polymorphic",
|
||||
read: (value, ctx) => {
|
||||
// `value` is either a string which is an Actor ID or a form object
|
||||
// where `actor` is an actor ID
|
||||
const actorID = typeof value === "string" ? value : value.actor;
|
||||
if (!actorID) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got value: '${value}'`
|
||||
);
|
||||
}
|
||||
|
||||
// Extract the typeName out of the actor ID, which should be composed like this
|
||||
// ${DebuggerServerConnectionPrefix}.${typeName}${Number}
|
||||
const typeName = actorID.match(/\.([a-zA-Z]+)\d+$/)[1];
|
||||
if (!subtypes.includes(typeName)) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got an actor of type: '${typeName}'`
|
||||
);
|
||||
}
|
||||
|
||||
const subtype = types.getType(typeName);
|
||||
return subtype.read(value, ctx);
|
||||
},
|
||||
write: (value, ctx) => {
|
||||
if (!value) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got an empty value.`
|
||||
);
|
||||
}
|
||||
// value is either an `Actor` or a `Front` and both classes exposes a `typeName`
|
||||
const typeName = value.typeName;
|
||||
if (!typeName) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got value: '${value}'. Did you pass a form instead of an Actor?`
|
||||
);
|
||||
}
|
||||
|
||||
if (!subtypes.includes(typeName)) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got an actor of type: '${typeName}'`
|
||||
);
|
||||
}
|
||||
|
||||
const subtype = types.getType(typeName);
|
||||
return subtype.write(value, ctx);
|
||||
},
|
||||
});
|
||||
};
|
||||
types.addNullableType = function(subtype) {
|
||||
subtype = types.getType(subtype);
|
||||
return types.addType("nullable:" + subtype.name, {
|
||||
|
|
|
@ -42,4 +42,3 @@ run-if = nightly_build
|
|||
[test_stack.js]
|
||||
[test_defer.js]
|
||||
[test_executeSoon.js]
|
||||
[test_protocol_index.js]
|
||||
|
|
|
@ -303,29 +303,6 @@ void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
|
|||
// Notify the parent process of the embedding status. We don't need to do
|
||||
// this when clearing our embedder, as we're being destroyed either way.
|
||||
if (aEmbedder) {
|
||||
// If our embedder element is being mutated to a different embedder, and we
|
||||
// have a parent edge, bad things might be happening!
|
||||
//
|
||||
// XXX: This is a workaround to some parent edges not being immutable in the
|
||||
// parent process. It can be fixed once bug 1539979 has been fixed.
|
||||
if (mParent && mEmbedderElement && mEmbedderElement != aEmbedder) {
|
||||
NS_WARNING("Non root content frameLoader swap! This will crash soon!");
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mType == Type::Chrome, "must be chrome");
|
||||
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "must be in parent");
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mGroup->IsContextCached(this),
|
||||
"cannot be in bfcache");
|
||||
|
||||
RefPtr<BrowsingContext> kungFuDeathGrip(this);
|
||||
RefPtr<BrowsingContext> newParent(
|
||||
aEmbedder->OwnerDoc()->GetBrowsingContext());
|
||||
mParent->mChildren.RemoveElement(this);
|
||||
if (newParent) {
|
||||
newParent->mChildren.AppendElement(this);
|
||||
}
|
||||
mParent = newParent;
|
||||
}
|
||||
|
||||
if (nsCOMPtr<nsPIDOMWindowInner> inner =
|
||||
do_QueryInterface(aEmbedder->GetOwnerGlobal())) {
|
||||
SetEmbedderInnerWindowId(inner->WindowID());
|
||||
|
@ -949,7 +926,7 @@ nsresult BrowsingContext::InternalLoad(BrowsingContext* aAccessor,
|
|||
// the same as how the tab first opened.
|
||||
nsCOMPtr<nsPIDOMWindowOuter> domWin = GetDOMWindow();
|
||||
if (isActive && domWin) {
|
||||
nsFocusManager::FocusWindow(domWin);
|
||||
nsFocusManager::FocusWindow(domWin, CallerType::System);
|
||||
}
|
||||
|
||||
// Else we ran out of memory, or were a popup and got blocked,
|
||||
|
@ -1023,11 +1000,11 @@ void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
|
|||
}
|
||||
}
|
||||
|
||||
void BrowsingContext::Focus(ErrorResult& aError) {
|
||||
void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) {
|
||||
if (ContentChild* cc = ContentChild::GetSingleton()) {
|
||||
cc->SendWindowFocus(this);
|
||||
cc->SendWindowFocus(this, aCallerType);
|
||||
} else if (ContentParent* cp = Canonical()->GetContentParent()) {
|
||||
Unused << cp->SendWindowFocus(this);
|
||||
Unused << cp->SendWindowFocus(this, aCallerType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -390,7 +390,7 @@ class BrowsingContext : public nsISupports,
|
|||
ErrorResult& aError);
|
||||
void Close(CallerType aCallerType, ErrorResult& aError);
|
||||
bool GetClosed(ErrorResult&) { return mClosed; }
|
||||
void Focus(ErrorResult& aError);
|
||||
void Focus(CallerType aCallerType, ErrorResult& aError);
|
||||
void Blur(ErrorResult& aError);
|
||||
WindowProxyHolder GetFrames(ErrorResult& aError);
|
||||
int32_t Length() const { return mChildren.Length(); }
|
||||
|
|
|
@ -190,7 +190,7 @@ nsDSURIContentListener::DoContent(const nsACString& aContentType,
|
|||
nsCOMPtr<nsPIDOMWindowOuter> domWindow =
|
||||
mDocShell ? mDocShell->GetWindow() : nullptr;
|
||||
NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
|
||||
domWindow->Focus();
|
||||
domWindow->Focus(mozilla::dom::CallerType::System);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -88,7 +88,7 @@ nsresult DOMImplementation::CreateDocument(const nsAString& aNamespaceURI,
|
|||
rv = NS_NewDOMDocument(getter_AddRefs(doc), aNamespaceURI, aQualifiedName,
|
||||
aDoctype, mDocumentURI, mBaseURI,
|
||||
mOwner->NodePrincipal(), true, scriptHandlingObject,
|
||||
DocumentFlavorLegacyGuess);
|
||||
DocumentFlavorXML);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// When DOMImplementation's createDocument method is invoked with
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/NullPrincipal.h"
|
||||
#include "NullPrincipalURI.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
|
||||
|
@ -250,8 +251,8 @@ already_AddRefed<DOMParser> DOMParser::Constructor(const GlobalObject& aOwner,
|
|||
nsCOMPtr<nsIURI> documentURI;
|
||||
nsIURI* baseURI = nullptr;
|
||||
if (docPrincipal->IsSystemPrincipal()) {
|
||||
docPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
|
||||
docPrincipal->GetURI(getter_AddRefs(documentURI));
|
||||
documentURI = new NullPrincipalURI();
|
||||
docPrincipal = NullPrincipal::Create(OriginAttributes(), documentURI);
|
||||
} else {
|
||||
// Grab document and base URIs off the window our constructor was
|
||||
// called on. Error out if anything untoward happens.
|
||||
|
@ -280,10 +281,9 @@ already_AddRefed<DOMParser> DOMParser::Constructor(const GlobalObject& aOwner,
|
|||
|
||||
// static
|
||||
already_AddRefed<DOMParser> DOMParser::CreateWithoutGlobal(ErrorResult& aRv) {
|
||||
nsCOMPtr<nsIURI> documentURI = new NullPrincipalURI();
|
||||
nsCOMPtr<nsIPrincipal> docPrincipal =
|
||||
NullPrincipal::CreateWithoutOriginAttributes();
|
||||
nsCOMPtr<nsIURI> documentURI;
|
||||
docPrincipal->GetURI(getter_AddRefs(documentURI));
|
||||
NullPrincipal::Create(OriginAttributes(), documentURI);
|
||||
|
||||
if (!documentURI) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
|
|
|
@ -11739,7 +11739,7 @@ class nsAutoFocusEvent : public Runnable {
|
|||
|
||||
FocusOptions options;
|
||||
ErrorResult rv;
|
||||
mElement->Focus(options, rv);
|
||||
mElement->Focus(options, CallerType::System, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
|
|
|
@ -5467,6 +5467,7 @@ enum DocumentFlavor {
|
|||
DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
|
||||
DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
|
||||
DocumentFlavorSVG, // SVGDocument
|
||||
DocumentFlavorXML, // XMLDocument
|
||||
DocumentFlavorPlain, // Just a Document
|
||||
};
|
||||
|
||||
|
|
|
@ -336,7 +336,8 @@ int32_t Element::TabIndex() {
|
|||
return TabIndexDefault();
|
||||
}
|
||||
|
||||
void Element::Focus(const FocusOptions& aOptions, ErrorResult& aError) {
|
||||
void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
|
||||
ErrorResult& aError) {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
// Also other browsers seem to have the hack to not re-focus (and flush) when
|
||||
// the element is already focused.
|
||||
|
@ -348,9 +349,13 @@ void Element::Focus(const FocusOptions& aOptions, ErrorResult& aError) {
|
|||
if (fm->CanSkipFocus(this)) {
|
||||
fm->NeedsFlushBeforeEventHandling(this);
|
||||
} else {
|
||||
aError = fm->SetFocus(
|
||||
this, nsIFocusManager::FLAG_BYELEMENTFOCUS |
|
||||
nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions));
|
||||
uint32_t fmFlags =
|
||||
nsIFocusManager::FLAG_BYELEMENTFOCUS |
|
||||
nsFocusManager::FocusOptionsToFocusManagerFlags(aOptions);
|
||||
if (aCallerType == CallerType::NonSystem) {
|
||||
fmFlags = nsIFocusManager::FLAG_NONSYSTEMCALLER | fmFlags;
|
||||
}
|
||||
aError = fm->SetFocus(this, fmFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,7 +234,8 @@ class Element : public FragmentOrElement {
|
|||
/**
|
||||
* Make focus on this element.
|
||||
*/
|
||||
virtual void Focus(const FocusOptions& aOptions, ErrorResult& aError);
|
||||
virtual void Focus(const FocusOptions& aOptions, const CallerType aCallerType,
|
||||
ErrorResult& aError);
|
||||
|
||||
/**
|
||||
* Show blur and clear focus.
|
||||
|
|
|
@ -4339,7 +4339,8 @@ nsresult nsContentUtils::DispatchChromeEvent(
|
|||
return err.StealNSResult();
|
||||
}
|
||||
|
||||
void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise) {
|
||||
void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
|
||||
CallerType aCallerType) {
|
||||
RefPtr<Element> target = &aFrameElement;
|
||||
bool defaultAction = true;
|
||||
if (aCanRaise) {
|
||||
|
@ -4361,6 +4362,10 @@ void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise) {
|
|||
flags |= nsIFocusManager::FLAG_RAISE;
|
||||
}
|
||||
|
||||
if (aCallerType == CallerType::NonSystem) {
|
||||
flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
|
||||
}
|
||||
|
||||
fm->SetFocus(target, flags);
|
||||
}
|
||||
|
||||
|
|
|
@ -1580,7 +1580,8 @@ class nsContentUtils {
|
|||
* Helper to dispatch a "framefocusrequested" event to chrome, which will only
|
||||
* bring the window to the foreground and switch tabs if aCanRaise is true.
|
||||
*/
|
||||
static void RequestFrameFocus(Element& aFrameElement, bool aCanRaise);
|
||||
static void RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
|
||||
mozilla::dom::CallerType aCallerType);
|
||||
|
||||
/**
|
||||
* This method creates and dispatches a trusted event.
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "mozilla/dom/Selection.h"
|
||||
#include "nsXULPopupManager.h"
|
||||
#include "nsMenuPopupFrame.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
@ -39,6 +40,7 @@
|
|||
#include "nsHTMLDocument.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsRange.h"
|
||||
#include "nsFrameLoaderOwner.h"
|
||||
|
||||
#include "mozilla/AccessibleCaretEventHub.h"
|
||||
#include "mozilla/ContentEvents.h"
|
||||
|
@ -49,6 +51,7 @@
|
|||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "mozilla/dom/BrowserBridgeChild.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
|
@ -59,6 +62,7 @@
|
|||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/StaticPrefs_full_screen_api.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
|
@ -69,10 +73,6 @@
|
|||
# include "nsAccessibilityService.h"
|
||||
#endif
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
# include "nsIScriptError.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::widget;
|
||||
|
@ -351,21 +351,27 @@ nsFocusManager::GetActiveWindow(mozIDOMWindowProxy** aWindow) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsFocusManager::FocusWindow(nsPIDOMWindowOuter* aWindow) {
|
||||
void nsFocusManager::FocusWindow(nsPIDOMWindowOuter* aWindow,
|
||||
CallerType aCallerType) {
|
||||
if (RefPtr<nsFocusManager> fm = sInstance) {
|
||||
fm->SetFocusedWindow(aWindow);
|
||||
fm->SetFocusedWindowWithCallerType(aWindow, aCallerType);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsFocusManager::SetActiveWindow(mozIDOMWindowProxy* aWindow) {
|
||||
return SetActiveWindowWithCallerType(aWindow, CallerType::System);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFocusManager::SetActiveWindow(mozIDOMWindowProxy* aWindow) {
|
||||
nsFocusManager::SetActiveWindowWithCallerType(mozIDOMWindowProxy* aWindow,
|
||||
CallerType aCallerType) {
|
||||
NS_ENSURE_STATE(aWindow);
|
||||
|
||||
// only top-level windows can be made active
|
||||
nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(aWindow);
|
||||
NS_ENSURE_TRUE(piWindow == piWindow->GetPrivateRoot(), NS_ERROR_INVALID_ARG);
|
||||
|
||||
RaiseWindow(piWindow);
|
||||
RaiseWindow(piWindow, aCallerType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -375,8 +381,8 @@ nsFocusManager::GetFocusedWindow(mozIDOMWindowProxy** aFocusedWindow) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsFocusManager::SetFocusedWindow(
|
||||
mozIDOMWindowProxy* aWindowToFocus) {
|
||||
NS_IMETHODIMP nsFocusManager::SetFocusedWindowWithCallerType(
|
||||
mozIDOMWindowProxy* aWindowToFocus, CallerType aCallerType) {
|
||||
LOGFOCUS(("<<SetFocusedWindow begin>>"));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> windowToFocus =
|
||||
|
@ -401,13 +407,18 @@ NS_IMETHODIMP nsFocusManager::SetFocusedWindow(
|
|||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> rootWindow = windowToFocus->GetPrivateRoot();
|
||||
if (rootWindow) RaiseWindow(rootWindow);
|
||||
if (rootWindow) RaiseWindow(rootWindow, aCallerType);
|
||||
|
||||
LOGFOCUS(("<<SetFocusedWindow end>>"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsFocusManager::SetFocusedWindow(
|
||||
mozIDOMWindowProxy* aWindowToFocus) {
|
||||
return SetFocusedWindowWithCallerType(aWindowToFocus, CallerType::System);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFocusManager::GetFocusedElement(Element** aFocusedElement) {
|
||||
RefPtr<Element> focusedElement = mFocusedElement;
|
||||
|
@ -629,7 +640,7 @@ nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow) {
|
|||
// of the child we want. We solve this by calling SetFocus to ensure that
|
||||
// what the focus manager thinks should be the current widget is actually
|
||||
// focused.
|
||||
EnsureCurrentWidgetFocused();
|
||||
EnsureCurrentWidgetFocused(CallerType::System);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -844,7 +855,7 @@ nsFocusManager::WindowShown(mozIDOMWindowProxy* aWindow, bool aNeedsFocus) {
|
|||
// Sometimes, an element in a window can be focused before the window is
|
||||
// visible, which would mean that the widget may not be properly focused.
|
||||
// When the window becomes visible, make sure the right widget is focused.
|
||||
EnsureCurrentWidgetFocused();
|
||||
EnsureCurrentWidgetFocused(CallerType::System);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1058,7 +1069,7 @@ void nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
|
|||
}
|
||||
|
||||
// static
|
||||
void nsFocusManager::EnsureCurrentWidgetFocused() {
|
||||
void nsFocusManager::EnsureCurrentWidgetFocused(CallerType aCallerType) {
|
||||
if (!mFocusedWindow || sTestMode) return;
|
||||
|
||||
// get the main child widget for the focused window and ensure that the
|
||||
|
@ -1080,7 +1091,7 @@ void nsFocusManager::EnsureCurrentWidgetFocused() {
|
|||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
widget->SetFocus(nsIWidget::Raise::No);
|
||||
widget->SetFocus(nsIWidget::Raise::No, aCallerType);
|
||||
}
|
||||
|
||||
void nsFocusManager::ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow,
|
||||
|
@ -1111,6 +1122,37 @@ void nsFocusManager::ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow,
|
|||
});
|
||||
}
|
||||
|
||||
// Retrieves innerWindowId of the window of the last focused element to
|
||||
// log a warning to the website console.
|
||||
void LogWarningFullscreenWindowRaise(Element* aElement) {
|
||||
nsCOMPtr<nsFrameLoaderOwner> frameLoaderOwner(do_QueryInterface(aElement));
|
||||
NS_ENSURE_TRUE_VOID(frameLoaderOwner);
|
||||
|
||||
RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
|
||||
NS_ENSURE_TRUE_VOID(frameLoaderOwner);
|
||||
|
||||
RefPtr<BrowsingContext> browsingContext = frameLoader->GetBrowsingContext();
|
||||
NS_ENSURE_TRUE_VOID(browsingContext);
|
||||
|
||||
WindowGlobalParent* windowGlobalParent =
|
||||
browsingContext->Canonical()->GetCurrentWindowGlobal();
|
||||
NS_ENSURE_TRUE_VOID(windowGlobalParent);
|
||||
|
||||
// Log to console
|
||||
nsAutoString localizedMsg;
|
||||
nsTArray<nsString> params;
|
||||
nsresult rv = nsContentUtils::FormatLocalizedString(
|
||||
nsContentUtils::eDOM_PROPERTIES, "FullscreenExitWindowFocus", params,
|
||||
localizedMsg);
|
||||
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
Unused << nsContentUtils::ReportToConsoleByWindowID(
|
||||
localizedMsg, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"),
|
||||
windowGlobalParent->InnerWindowId(),
|
||||
windowGlobalParent->GetDocumentURI());
|
||||
}
|
||||
|
||||
void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
|
||||
bool aFocusChanged, bool aAdjustWidget) {
|
||||
// if the element is not focusable, just return and leave the focus as is
|
||||
|
@ -1201,6 +1243,20 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
|
|||
isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow);
|
||||
}
|
||||
|
||||
// Exit fullscreen if a website focuses another window
|
||||
if (StaticPrefs::full_screen_api_exit_on_windowRaise() &&
|
||||
!isElementInActiveWindow &&
|
||||
aFlags & (FLAG_RAISE | FLAG_NONSYSTEMCALLER)) {
|
||||
if (Document* doc = mActiveWindow ? mActiveWindow->GetDoc() : nullptr) {
|
||||
if (doc->GetFullscreenElement()) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
LogWarningFullscreenWindowRaise(mFocusedElement);
|
||||
}
|
||||
Document::AsyncExitFullscreen(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
|
||||
// system. We don't control event dispatch to windowed plugins on non-MacOSX,
|
||||
// so we can't display the "Press ESC to leave fullscreen mode" warning on
|
||||
|
@ -1307,7 +1363,10 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
|
|||
if (allowFrameSwitch)
|
||||
newWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
|
||||
|
||||
if (aFlags & FLAG_RAISE) RaiseWindow(newRootWindow);
|
||||
if (aFlags & FLAG_RAISE)
|
||||
RaiseWindow(newRootWindow, aFlags & FLAG_NONSYSTEMCALLER
|
||||
? CallerType::NonSystem
|
||||
: CallerType::System);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1607,7 +1666,7 @@ bool nsFocusManager::Blur(nsPIDOMWindowOuter* aWindowToClear,
|
|||
vm->GetRootWidget(getter_AddRefs(widget));
|
||||
if (widget) {
|
||||
// set focus to the top level window but don't raise it.
|
||||
widget->SetFocus(nsIWidget::Raise::No);
|
||||
widget->SetFocus(nsIWidget::Raise::No, CallerType::System);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1800,7 +1859,10 @@ void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
|||
if (nsViewManager* vm = presShell->GetViewManager()) {
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
vm->GetRootWidget(getter_AddRefs(widget));
|
||||
if (widget) widget->SetFocus(nsIWidget::Raise::No);
|
||||
if (widget)
|
||||
widget->SetFocus(nsIWidget::Raise::No, aFlags & FLAG_NONSYSTEMCALLER
|
||||
? CallerType::NonSystem
|
||||
: CallerType::System);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1854,7 +1916,10 @@ void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
|||
// fired above when aIsNewDocument.
|
||||
if (presShell->GetDocument() == aElement->GetComposedDoc()) {
|
||||
if (aAdjustWidgets && objectFrameWidget && !sTestMode) {
|
||||
objectFrameWidget->SetFocus(nsIWidget::Raise::No);
|
||||
objectFrameWidget->SetFocus(nsIWidget::Raise::No,
|
||||
aFlags & FLAG_NONSYSTEMCALLER
|
||||
? CallerType::NonSystem
|
||||
: CallerType::System);
|
||||
}
|
||||
|
||||
// if the object being focused is a remote browser, activate remote
|
||||
|
@ -1891,7 +1956,9 @@ void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
|||
nsCOMPtr<nsIWidget> widget;
|
||||
vm->GetRootWidget(getter_AddRefs(widget));
|
||||
if (widget) {
|
||||
widget->SetFocus(nsIWidget::Raise::No);
|
||||
widget->SetFocus(nsIWidget::Raise::No, aFlags & FLAG_NONSYSTEMCALLER
|
||||
? CallerType::NonSystem
|
||||
: CallerType::System);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2124,7 +2191,8 @@ void nsFocusManager::ScrollIntoView(PresShell* aPresShell, nsIContent* aContent,
|
|||
}
|
||||
}
|
||||
|
||||
void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow) {
|
||||
void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
|
||||
CallerType aCallerType) {
|
||||
// don't raise windows that are already raised or are in the process of
|
||||
// being lowered
|
||||
if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
|
||||
|
@ -2169,7 +2237,7 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow) {
|
|||
if (nsViewManager* vm = presShell->GetViewManager()) {
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
vm->GetRootWidget(getter_AddRefs(widget));
|
||||
if (widget) widget->SetFocus(nsIWidget::Raise::Yes);
|
||||
if (widget) widget->SetFocus(nsIWidget::Raise::Yes, aCallerType);
|
||||
}
|
||||
#else
|
||||
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin =
|
||||
|
@ -2177,7 +2245,7 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow) {
|
|||
if (treeOwnerAsWin) {
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
|
||||
if (widget) widget->SetFocus(nsIWidget::Raise::Yes);
|
||||
if (widget) widget->SetFocus(nsIWidget::Raise::Yes, aCallerType);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -60,7 +60,8 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
// Simple helper to call SetFocusedWindow on the instance.
|
||||
//
|
||||
// This raises the window and switches to the tab as needed.
|
||||
static void FocusWindow(nsPIDOMWindowOuter* aWindow);
|
||||
static void FocusWindow(nsPIDOMWindowOuter* aWindow,
|
||||
mozilla::dom::CallerType aCallerType);
|
||||
|
||||
static void PrefChanged(const char* aPref, void* aSelf);
|
||||
void PrefChanged(const char* aPref);
|
||||
|
@ -202,7 +203,7 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
* Ensure that the widget associated with the currently focused window is
|
||||
* focused at the widget level.
|
||||
*/
|
||||
void EnsureCurrentWidgetFocused();
|
||||
void EnsureCurrentWidgetFocused(mozilla::dom::CallerType aCallerType);
|
||||
|
||||
/**
|
||||
* Activate or deactivate the window and send the activate/deactivate events.
|
||||
|
@ -350,7 +351,6 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
Document* aDocument, nsISupports* aTarget, uint32_t aFocusMethod,
|
||||
bool aWindowRaised, bool aIsRefocus = false,
|
||||
mozilla::dom::EventTarget* aRelatedTarget = nullptr);
|
||||
|
||||
/**
|
||||
* Fire a focus or blur event at aTarget.
|
||||
*
|
||||
|
@ -397,7 +397,8 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
/**
|
||||
* Raises the top-level window aWindow at the widget level.
|
||||
*/
|
||||
void RaiseWindow(nsPIDOMWindowOuter* aWindow);
|
||||
void RaiseWindow(nsPIDOMWindowOuter* aWindow,
|
||||
mozilla::dom::CallerType aCallerType);
|
||||
|
||||
/**
|
||||
* Updates the caret positon and visibility to match the focus.
|
||||
|
|
|
@ -3451,13 +3451,13 @@ void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
|
|||
aError, );
|
||||
}
|
||||
|
||||
void nsGlobalWindowInner::Focus(ErrorResult& aError) {
|
||||
FORWARD_TO_OUTER_OR_THROW(FocusOuter, (), aError, );
|
||||
void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
|
||||
FORWARD_TO_OUTER_OR_THROW(FocusOuter, (aCallerType), aError, );
|
||||
}
|
||||
|
||||
nsresult nsGlobalWindowInner::Focus() {
|
||||
nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
|
||||
ErrorResult rv;
|
||||
Focus(rv);
|
||||
Focus(aCallerType, rv);
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
|
|
@ -601,8 +601,9 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
|
|||
nsresult Close() override;
|
||||
bool GetClosed(mozilla::ErrorResult& aError);
|
||||
void Stop(mozilla::ErrorResult& aError);
|
||||
void Focus(mozilla::ErrorResult& aError);
|
||||
nsresult Focus() override;
|
||||
void Focus(mozilla::dom::CallerType aCallerType,
|
||||
mozilla::ErrorResult& aError);
|
||||
nsresult Focus(mozilla::dom::CallerType aCallerType) override;
|
||||
void Blur(mozilla::ErrorResult& aError);
|
||||
mozilla::dom::WindowProxyHolder GetFrames(mozilla::ErrorResult& aError);
|
||||
uint32_t Length();
|
||||
|
|
|
@ -4830,7 +4830,7 @@ void nsGlobalWindowOuter::PromptOuter(const nsAString& aMessage,
|
|||
}
|
||||
}
|
||||
|
||||
void nsGlobalWindowOuter::FocusOuter() {
|
||||
void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType) {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (!fm) {
|
||||
return;
|
||||
|
@ -4899,7 +4899,7 @@ void nsGlobalWindowOuter::FocusOuter() {
|
|||
}
|
||||
|
||||
if (Element* frame = parentdoc->FindContentForSubDocument(mDoc)) {
|
||||
nsContentUtils::RequestFrameFocus(*frame, canFocus);
|
||||
nsContentUtils::RequestFrameFocus(*frame, canFocus, aCallerType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -4908,15 +4908,17 @@ void nsGlobalWindowOuter::FocusOuter() {
|
|||
// if there is no parent, this must be a toplevel window, so raise the
|
||||
// window if canFocus is true. If this is a child process, the raise
|
||||
// window request will get forwarded to the parent by the puppet widget.
|
||||
DebugOnly<nsresult> rv = fm->SetActiveWindow(this);
|
||||
DebugOnly<nsresult> rv =
|
||||
fm->SetActiveWindowWithCallerType(this, aCallerType);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv),
|
||||
"SetActiveWindow only fails if passed null or a non-toplevel "
|
||||
"SetActiveWindowWithCallerType only fails if passed null or a "
|
||||
"non-toplevel "
|
||||
"window, which is not the case here.");
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsGlobalWindowOuter::Focus() {
|
||||
FORWARD_TO_INNER(Focus, (), NS_ERROR_UNEXPECTED);
|
||||
nsresult nsGlobalWindowOuter::Focus(CallerType aCallerType) {
|
||||
FORWARD_TO_INNER(Focus, (aCallerType), NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
void nsGlobalWindowOuter::BlurOuter() {
|
||||
|
|
|
@ -542,8 +542,8 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
|
|||
bool GetClosedOuter();
|
||||
bool Closed() override;
|
||||
void StopOuter(mozilla::ErrorResult& aError);
|
||||
void FocusOuter();
|
||||
nsresult Focus() override;
|
||||
void FocusOuter(mozilla::dom::CallerType aCallerType);
|
||||
nsresult Focus(mozilla::dom::CallerType aCallerType) override;
|
||||
void BlurOuter();
|
||||
mozilla::dom::WindowProxyHolder GetFramesOuter();
|
||||
uint32_t Length();
|
||||
|
|
|
@ -544,7 +544,7 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
|
|||
|
||||
virtual bool GetFullScreen() = 0;
|
||||
|
||||
virtual nsresult Focus() = 0;
|
||||
virtual nsresult Focus(mozilla::dom::CallerType aCallerType) = 0;
|
||||
virtual nsresult Close() = 0;
|
||||
|
||||
mozilla::dom::DocGroup* GetDocGroup() const;
|
||||
|
@ -1033,7 +1033,7 @@ class nsPIDOMWindowOuter : public mozIDOMWindowProxy {
|
|||
virtual bool GetFullScreen() = 0;
|
||||
virtual nsresult SetFullScreen(bool aFullscreen) = 0;
|
||||
|
||||
virtual nsresult Focus() = 0;
|
||||
virtual nsresult Focus(mozilla::dom::CallerType aCallerType) = 0;
|
||||
virtual nsresult Close() = 0;
|
||||
|
||||
virtual nsresult MoveBy(int32_t aXDif, int32_t aYDif) = 0;
|
||||
|
|
|
@ -16,10 +16,8 @@ support-files =
|
|||
file_bug1139964.xhtml
|
||||
file_bug1209621.xhtml
|
||||
fileconstructor_file.png
|
||||
frame_bug814638.xhtml
|
||||
frame_custom_element_content.html
|
||||
custom_element_ep.js
|
||||
host_bug814638.xhtml
|
||||
window_nsITextInputProcessor.xhtml
|
||||
title_window.xhtml
|
||||
window_swapFrameLoaders.xhtml
|
||||
|
@ -53,7 +51,6 @@ skip-if = verify
|
|||
[test_bug780199.xhtml]
|
||||
[test_bug780529.xhtml]
|
||||
[test_bug800386.xhtml]
|
||||
[test_bug814638.xhtml]
|
||||
[test_bug816340.xhtml]
|
||||
[test_bug884693.xhtml]
|
||||
[test_bug914381.html]
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=814638
|
||||
-->
|
||||
<window title="Mozilla Bug 814638"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<keyset>
|
||||
<key key="T" modifiers="control" oncommand="receivedKeyEvent()"/>
|
||||
</keyset>
|
||||
|
||||
<iframe flex="1" src="about:mozilla"/>
|
||||
|
||||
</window>
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=814638
|
||||
-->
|
||||
<window title="Mozilla Bug 814638"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<iframe flex="1" src="frame_bug814638.xhtml"/>
|
||||
</window>
|
|
@ -1,63 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=814638
|
||||
-->
|
||||
<window title="Mozilla Bug 814638"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- 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=814638"
|
||||
target="_blank" id="link">Mozilla Bug 814638</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
/** Test for Bug 814638 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function startTest() {
|
||||
let hostURL = "chrome://mochitests/content/chrome/dom/base/test/chrome/host_bug814638.xhtml";
|
||||
let host1 = docShell.rootTreeItem.domWindow.open(hostURL, "_blank", "chrome");
|
||||
let host2 = docShell.rootTreeItem.domWindow.open(hostURL, "_blank", "chrome");
|
||||
|
||||
let isHost1Loaded = isHost2Loaded = false
|
||||
host1.onload = function() {
|
||||
isHost1Loaded = true;
|
||||
if (isHost2Loaded) swapFrames();
|
||||
}
|
||||
host2.onload = function() {
|
||||
isHost2Loaded = true;
|
||||
if (isHost1Loaded) swapFrames();
|
||||
}
|
||||
|
||||
function swapFrames() {
|
||||
let iframe1 = host1.document.querySelector("iframe");
|
||||
let iframe2 = host2.document.querySelector("iframe");
|
||||
iframe2.swapFrameLoaders(iframe1);
|
||||
setTimeout(function() {
|
||||
iframe2.contentWindow.receivedKeyEvent = receivedKeyEvent;
|
||||
let innerIframe2 = iframe2.contentDocument.querySelector("iframe");
|
||||
let e = innerIframe2.contentDocument.createEvent("KeyboardEvent");
|
||||
e.initKeyEvent("keypress", true, true, null, true, false, false, false, 0, "t".charCodeAt(0));
|
||||
innerIframe2.contentDocument.documentElement.dispatchEvent(e);
|
||||
host1.close();
|
||||
host2.close();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function receivedKeyEvent() {
|
||||
ok(true, "Received key event");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
addLoadEvent(startTest);
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
|
@ -20,40 +20,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=450160
|
|||
/** Test for Bug 450160 **/
|
||||
|
||||
|
||||
function testHTMLDocuments(ids, isXHTML) {
|
||||
for (var i = 0; i < ids.length; ++i) {
|
||||
var docType1 =
|
||||
document.implementation.createDocumentType(isXHTML ? "html" : "HTML",
|
||||
ids[i],
|
||||
null);
|
||||
ok(docType1, "No doctype?");
|
||||
ok(docType1.ownerDocument, "docType should have ownerDocument!");
|
||||
var doc1 = document.implementation.createDocument(null, null, docType1);
|
||||
is(docType1.ownerDocument, doc1, "docType should have ownerDocument!");
|
||||
ok(!doc1.documentElement, "Document shouldn't have document element!");
|
||||
is(doc1.body, null, "Shouldn't have .body!");
|
||||
ok(doc1 instanceof HTMLDocument,
|
||||
"Document should be an HTML document!");
|
||||
|
||||
var docType2 =
|
||||
document.implementation.createDocumentType(isXHTML ? "html" : "HTML",
|
||||
ids[i],
|
||||
null);
|
||||
var doc2 = document.implementation.createDocument(
|
||||
"http://www.w3.org/1999/xhtml", "html", docType2);
|
||||
is(docType2.ownerDocument, doc2, "docType should have ownerDocument!");
|
||||
ok(doc2.documentElement, "Document should have document element!");
|
||||
is(doc2.documentElement.localName, "html", "Wrong document element!");
|
||||
is(doc2.body, null, "Shouldn't have .body!");
|
||||
doc2.documentElement.appendChild(doc2.createElement("body"));
|
||||
is(doc2.body, doc2.documentElement.firstChild, "Should have .body!");
|
||||
if (isXHTML) {
|
||||
doc2.body.appendChild(doc2.createElementNS("http://www.w3.org/1999/xhtml", "form"));
|
||||
} else {
|
||||
doc2.body.appendChild(doc2.createElement("form"));
|
||||
}
|
||||
is(doc2.forms.length, 1, "Form wasn't added .forms");
|
||||
}
|
||||
function testHTMLDocument() {
|
||||
var doc = document.implementation.createHTMLDocument();
|
||||
ok(!!doc.documentElement, "Document should have document element!");
|
||||
ok(!!doc.body, "Should have .body!");
|
||||
ok(doc instanceof HTMLDocument,
|
||||
"Document should be an HTML document!");
|
||||
}
|
||||
|
||||
function testSVGDocument() {
|
||||
|
@ -117,21 +89,7 @@ function testNullDocTypeDocument() {
|
|||
is(doc2.documentElement.localName, "FooBar", "Wrong localName!");
|
||||
}
|
||||
|
||||
var htmlPublicIDs =
|
||||
[ "-//W3C//DTD HTML 4.01//EN",
|
||||
"-//W3C//DTD HTML 4.01 Transitional//EN",
|
||||
"-//W3C//DTD HTML 4.01 Frameset//EN",
|
||||
"-//W3C//DTD HTML 4.0//EN",
|
||||
"-//W3C//DTD HTML 4.0 Transitional//EN",
|
||||
"-//W3C//DTD HTML 4.0 Frameset//EN" ];
|
||||
|
||||
var xhtmlPublicIDs =
|
||||
[ "-//W3C//DTD XHTML 1.0 Strict//EN",
|
||||
"-//W3C//DTD XHTML 1.0 Transitional//EN",
|
||||
"-//W3C//DTD XHTML 1.0 Frameset//EN" ];
|
||||
|
||||
testHTMLDocuments(htmlPublicIDs, false);
|
||||
testHTMLDocuments(xhtmlPublicIDs, true);
|
||||
testHTMLDocument();
|
||||
testSVGDocument();
|
||||
testFooBarDocument();
|
||||
testNullDocTypeDocument();
|
||||
|
|
|
@ -23,10 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=562652
|
|||
|
||||
var testCount = 0;
|
||||
function createHTMLDoc() {
|
||||
var dtd = document.implementation.createDocumentType("HTML", "-//W3C//DTD HTML 4.01//EN", null);
|
||||
var d = document.implementation.createDocument(null, null, dtd);
|
||||
d.appendChild(d.createElement("html"));
|
||||
d.documentElement.appendChild(d.createElement("body"));
|
||||
var d = document.implementation.createHTMLDocument();
|
||||
d.body.setAttribute("id", "testtarget");
|
||||
return d;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef _mozilla_dom_BindingIPCUtils_h
|
||||
#define _mozilla_dom_BindingIPCUtils_h
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
|
||||
namespace IPC {
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::CallerType>
|
||||
: public ContiguousEnumSerializerInclusive<
|
||||
mozilla::dom::CallerType, mozilla::dom::CallerType::System,
|
||||
mozilla::dom::CallerType::NonSystem> {};
|
||||
} // namespace IPC
|
||||
#endif // _mozilla_dom_BindingIPCUtils_h
|
|
@ -28,6 +28,7 @@ EXPORTS.mozilla += [
|
|||
EXPORTS.mozilla.dom += [
|
||||
'AtomList.h',
|
||||
'BindingDeclarations.h',
|
||||
'BindingIPCUtils.h',
|
||||
'BindingUtils.h',
|
||||
'CallbackFunction.h',
|
||||
'CallbackInterface.h',
|
||||
|
|
|
@ -26,7 +26,7 @@ WebGLSampler::~WebGLSampler() {
|
|||
static bool ValidateSamplerParameterParams(WebGLContext* webgl, GLenum pname,
|
||||
const FloatOrInt& param) {
|
||||
const auto& paramInt = param.i;
|
||||
|
||||
bool pnameValid = true;
|
||||
switch (pname) {
|
||||
case LOCAL_GL_TEXTURE_MIN_FILTER:
|
||||
switch (paramInt) {
|
||||
|
@ -100,9 +100,22 @@ static bool ValidateSamplerParameterParams(WebGLContext* webgl, GLenum pname,
|
|||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_TEXTURE_MAX_ANISOTROPY:
|
||||
if (webgl->IsExtensionEnabled(
|
||||
WebGLExtensionID::EXT_texture_filter_anisotropic)) {
|
||||
return true;
|
||||
}
|
||||
pnameValid = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
webgl->ErrorInvalidEnumInfo("pname", pname);
|
||||
return false;
|
||||
pnameValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pnameValid) {
|
||||
webgl->ErrorInvalidEnumInfo("pname", pname);
|
||||
return false;
|
||||
}
|
||||
|
||||
webgl->ErrorInvalidEnumInfo("param", paramInt);
|
||||
|
|
|
@ -126,7 +126,8 @@ bool Client::Focused() const {
|
|||
return mData->state().get_IPCClientWindowState().focused();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> Client::Focus(ErrorResult& aRv) {
|
||||
already_AddRefed<Promise> Client::Focus(CallerType aCallerType,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
|
||||
|
@ -149,7 +150,7 @@ already_AddRefed<Promise> Client::Focus(ErrorResult& aRv) {
|
|||
auto holder =
|
||||
MakeRefPtr<DOMMozPromiseRequestHolder<ClientStatePromise>>(mGlobal);
|
||||
|
||||
mHandle->Focus()
|
||||
mHandle->Focus(aCallerType)
|
||||
->Then(
|
||||
mGlobal->EventTargetFor(TaskCategory::Other), __func__,
|
||||
[ipcClientInfo, holder, outerPromise](const ClientState& aResult) {
|
||||
|
|
|
@ -69,7 +69,7 @@ class Client final : public nsISupports, public nsWrapperCache {
|
|||
|
||||
bool Focused() const;
|
||||
|
||||
already_AddRefed<Promise> Focus(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Focus(CallerType aCallerType, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Navigate(const nsAString& aURL, ErrorResult& aRv);
|
||||
|
||||
|
|
|
@ -122,12 +122,12 @@ RefPtr<GenericPromise> ClientHandle::Control(
|
|||
return outerPromise.forget();
|
||||
}
|
||||
|
||||
RefPtr<ClientStatePromise> ClientHandle::Focus() {
|
||||
RefPtr<ClientStatePromise> ClientHandle::Focus(CallerType aCallerType) {
|
||||
RefPtr<ClientStatePromise::Private> outerPromise =
|
||||
new ClientStatePromise::Private(__func__);
|
||||
|
||||
StartOp(
|
||||
ClientFocusArgs(),
|
||||
ClientFocusArgs(aCallerType),
|
||||
[outerPromise](const ClientOpResult& aResult) {
|
||||
outerPromise->Resolve(
|
||||
ClientState::FromIPC(aResult.get_IPCClientState()), __func__);
|
||||
|
|
|
@ -76,7 +76,7 @@ class ClientHandle final : public ClientThing<ClientHandleChild> {
|
|||
// Focus the Client if possible. If successful the promise will resolve with
|
||||
// a new ClientState snapshot after focus has completed. If focusing fails
|
||||
// for any reason then the promise will reject.
|
||||
RefPtr<ClientStatePromise> Focus();
|
||||
RefPtr<ClientStatePromise> Focus(CallerType aCallerType);
|
||||
|
||||
// Send a postMessage() call to the target Client. Currently this only
|
||||
// supports sending from a ServiceWorker source and the MessageEvent is
|
||||
|
|
|
@ -12,6 +12,7 @@ using ClientType from "mozilla/dom/ClientIPCUtils.h";
|
|||
using FrameType from "mozilla/dom/ClientIPCUtils.h";
|
||||
using mozilla::StorageAccess from "mozilla/dom/ClientIPCUtils.h";
|
||||
using VisibilityState from "mozilla/dom/ClientIPCUtils.h";
|
||||
using CallerType from "mozilla/dom/BindingIPCUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -75,6 +76,7 @@ struct ClientControlledArgs
|
|||
|
||||
struct ClientFocusArgs
|
||||
{
|
||||
CallerType callerType;
|
||||
};
|
||||
|
||||
struct ClientNavigateArgs
|
||||
|
|
|
@ -262,7 +262,7 @@ void WaitForLoad(const ClientOpenWindowArgs& aArgs,
|
|||
|
||||
RefPtr<ClientOpPromise::Private> promise = aPromise;
|
||||
|
||||
nsFocusManager::FocusWindow(aOuterWindow);
|
||||
nsFocusManager::FocusWindow(aOuterWindow, CallerType::NonSystem);
|
||||
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL());
|
||||
|
|
|
@ -555,7 +555,7 @@ RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) {
|
|||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsFocusManager::FocusWindow(outer);
|
||||
nsFocusManager::FocusWindow(outer, aArgs.callerType());
|
||||
|
||||
ClientState state;
|
||||
nsresult rv = SnapshotState(&state);
|
||||
|
|
|
@ -1080,7 +1080,7 @@ bool EventStateManager::LookForAccessKeyAndExecute(
|
|||
nsCOMPtr<nsIBrowserChild> child =
|
||||
docShell ? docShell->GetBrowserChild() : nullptr;
|
||||
if (child) {
|
||||
child->SendRequestFocus(false);
|
||||
child->SendRequestFocus(false, CallerType::System);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2932,7 +2932,7 @@ void HTMLInputElement::Blur(ErrorResult& aError) {
|
|||
}
|
||||
|
||||
void HTMLInputElement::Focus(const FocusOptions& aOptions,
|
||||
ErrorResult& aError) {
|
||||
CallerType aCallerType, ErrorResult& aError) {
|
||||
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
|
||||
!IsExperimentalMobileType(mType)) {
|
||||
if (Element* dateTimeBoxElement = GetDateTimeBoxElement()) {
|
||||
|
@ -2944,7 +2944,7 @@ void HTMLInputElement::Focus(const FocusOptions& aOptions,
|
|||
}
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::Focus(aOptions, aError);
|
||||
nsGenericHTMLElement::Focus(aOptions, aCallerType, aError);
|
||||
}
|
||||
|
||||
#if !defined(ANDROID) && !defined(XP_MACOSX)
|
||||
|
@ -3798,7 +3798,8 @@ nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
|||
FocusOptions options;
|
||||
ErrorResult error;
|
||||
|
||||
selectedRadioButton->Focus(options, error);
|
||||
selectedRadioButton->Focus(options, CallerType::System,
|
||||
error);
|
||||
rv = error.StealNSResult();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = DispatchSimulatedClick(selectedRadioButton,
|
||||
|
|
|
@ -142,7 +142,7 @@ class HTMLInputElement final : public TextControlElement,
|
|||
virtual int32_t TabIndexDefault() override;
|
||||
using nsGenericHTMLElement::Focus;
|
||||
virtual void Blur(ErrorResult& aError) override;
|
||||
virtual void Focus(const FocusOptions& aOptions,
|
||||
virtual void Focus(const FocusOptions& aOptions, const CallerType aCallerType,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
// nsINode
|
||||
|
|
|
@ -51,6 +51,7 @@ HTMLFormElement* HTMLLabelElement::GetForm() const {
|
|||
}
|
||||
|
||||
void HTMLLabelElement::Focus(const FocusOptions& aOptions,
|
||||
const CallerType aCallerType,
|
||||
ErrorResult& aError) {
|
||||
// retarget the focus method at the for content
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
|
|
|
@ -44,6 +44,7 @@ class HTMLLabelElement final : public nsGenericHTMLElement {
|
|||
|
||||
using nsGenericHTMLElement::Focus;
|
||||
virtual void Focus(const FocusOptions& aOptions,
|
||||
const mozilla::dom::CallerType aCallerType,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
// nsIContent
|
||||
|
|
|
@ -67,6 +67,7 @@ void HTMLLegendElement::UnbindFromTree(bool aNullParent) {
|
|||
}
|
||||
|
||||
void HTMLLegendElement::Focus(const FocusOptions& aOptions,
|
||||
const mozilla::dom::CallerType aCallerType,
|
||||
ErrorResult& aError) {
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
|
@ -75,7 +76,7 @@ void HTMLLegendElement::Focus(const FocusOptions& aOptions,
|
|||
|
||||
int32_t tabIndex;
|
||||
if (frame->IsFocusable(&tabIndex, false)) {
|
||||
nsGenericHTMLElement::Focus(aOptions, aError);
|
||||
nsGenericHTMLElement::Focus(aOptions, aCallerType, aError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -100,7 +101,7 @@ bool HTMLLegendElement::PerformAccesskey(bool aKeyCausesActivation,
|
|||
FocusOptions options;
|
||||
ErrorResult rv;
|
||||
|
||||
Focus(options, rv);
|
||||
Focus(options, CallerType::System, rv);
|
||||
return NS_SUCCEEDED(rv.StealNSResult());
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ class HTMLLegendElement final : public nsGenericHTMLElement {
|
|||
|
||||
using nsGenericHTMLElement::Focus;
|
||||
virtual void Focus(const FocusOptions& aOptions,
|
||||
const mozilla::dom::CallerType aCallerType,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
virtual bool PerformAccesskey(bool aKeyCausesActivation,
|
||||
|
|
|
@ -28,7 +28,8 @@ SimpleTest.requestFlakyTimeout("untriaged");
|
|||
// run in an iframe, which by default will not have the allowfullscreen
|
||||
// attribute set, so full-screen won't work.
|
||||
var gTestWindows = [
|
||||
{ test: "file_fullscreen-multiple.html" },
|
||||
{ test: "file_fullscreen-multiple.html",
|
||||
prefs: [["full-screen-api.exit-on.windowRaise", false], ["full-screen-api.exit-on.windowOpen", false]] },
|
||||
{ test: "file_fullscreen-rollback.html" },
|
||||
{ test: "file_fullscreen-esc-exit.html" },
|
||||
{ test: "file_fullscreen-denied.html" },
|
||||
|
|
|
@ -10,6 +10,8 @@ interface nsIWebBrowserChrome3;
|
|||
|
||||
webidl ContentFrameMessageManager;
|
||||
|
||||
|
||||
native CallerType(mozilla::dom::CallerType);
|
||||
native CommandsArray(nsTArray<nsCString>);
|
||||
[ref] native CommandsArrayRef(nsTArray<nsCString>);
|
||||
|
||||
|
@ -20,7 +22,7 @@ interface nsIBrowserChild : nsISupports
|
|||
|
||||
attribute nsIWebBrowserChrome3 webBrowserChrome;
|
||||
|
||||
[notxpcom] void sendRequestFocus(in boolean canFocus);
|
||||
[notxpcom] void sendRequestFocus(in boolean canFocus, in CallerType aCallerType);
|
||||
|
||||
[noscript, notxpcom] void enableDisableCommands(in AString action,
|
||||
in CommandsArrayRef enabledCommands,
|
||||
|
|
|
@ -10,6 +10,8 @@ interface mozIDOMWindowProxy;
|
|||
webidl Document;
|
||||
webidl Element;
|
||||
|
||||
native CallerType(mozilla::dom::CallerType);
|
||||
|
||||
/**
|
||||
* The focus manager deals with all focus related behaviour. Only one element
|
||||
* in the entire application may have the focus at a time; this element
|
||||
|
@ -47,9 +49,15 @@ interface nsIFocusManager : nsISupports
|
|||
* focuses the current child window's current element, if any. Setting this
|
||||
* to null or to a non-top-level window throws an NS_ERROR_INVALID_ARG
|
||||
* exception.
|
||||
* The setter for this attribute defaults to CallerType::System.
|
||||
*/
|
||||
attribute mozIDOMWindowProxy activeWindow;
|
||||
|
||||
/**
|
||||
* Setter for activeWindow with CallerType
|
||||
*/
|
||||
[noscript] void SetActiveWindowWithCallerType(in mozIDOMWindowProxy aWindow, in CallerType aCallerType);
|
||||
|
||||
/**
|
||||
* The child window within the activeWindow that is focused. This will
|
||||
* always be activeWindow, a child window of activeWindow or null if no
|
||||
|
@ -59,9 +67,15 @@ interface nsIFocusManager : nsISupports
|
|||
* will actually be set to the child window and the current element within
|
||||
* that set as the focused element. This process repeats downwards until a
|
||||
* non-frame element is found.
|
||||
* The setter for this attribute defaults to CallerType::System.
|
||||
*/
|
||||
attribute mozIDOMWindowProxy focusedWindow;
|
||||
|
||||
/**
|
||||
* Setter for focusedWindow with CallerType
|
||||
*/
|
||||
[noscript] void SetFocusedWindowWithCallerType(in mozIDOMWindowProxy aWindow, in CallerType aCallerType);
|
||||
|
||||
/**
|
||||
* The element that is currently focused. This will always be an element
|
||||
* within the document loaded in focusedWindow or null if no element in that
|
||||
|
@ -176,6 +190,12 @@ interface nsIFocusManager : nsISupports
|
|||
*/
|
||||
const unsigned long FLAG_NOPARENTFRAME = 8;
|
||||
|
||||
/**
|
||||
* This flag is used for window and element focus operations to signal
|
||||
* wether the caller is system or non system.
|
||||
*/
|
||||
const unsigned long FLAG_NONSYSTEMCALLER = 16;
|
||||
|
||||
/**
|
||||
* Focus is changing due to a mouse operation, for instance the mouse was
|
||||
* clicked on an element.
|
||||
|
|
|
@ -117,13 +117,13 @@ IPCResult BrowserBridgeChild::RecvSetLayersId(
|
|||
}
|
||||
|
||||
mozilla::ipc::IPCResult BrowserBridgeChild::RecvRequestFocus(
|
||||
const bool& aCanRaise) {
|
||||
const bool& aCanRaise, const CallerType aCallerType) {
|
||||
// Adapted from BrowserParent
|
||||
RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
|
||||
if (!owner) {
|
||||
return IPC_OK();
|
||||
}
|
||||
nsContentUtils::RequestFrameFocus(*owner, aCanRaise);
|
||||
nsContentUtils::RequestFrameFocus(*owner, aCanRaise, aCallerType);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,8 @@ class BrowserBridgeChild : public PBrowserBridgeChild {
|
|||
mozilla::ipc::IPCResult RecvSetLayersId(
|
||||
const mozilla::layers::LayersId& aLayersId);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise);
|
||||
mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise,
|
||||
const CallerType aCallerType);
|
||||
|
||||
mozilla::ipc::IPCResult RecvMoveFocus(const bool& aForward,
|
||||
const bool& aForDocumentNavigation);
|
||||
|
|
|
@ -2652,7 +2652,7 @@ mozilla::ipc::IPCResult BrowserChild::RecvNavigateByKey(
|
|||
nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result));
|
||||
}
|
||||
|
||||
SendRequestFocus(false);
|
||||
SendRequestFocus(false, CallerType::System);
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
|
@ -2981,8 +2981,8 @@ BrowserChild::SetWebBrowserChrome(nsIWebBrowserChrome3* aWebBrowserChrome) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void BrowserChild::SendRequestFocus(bool aCanFocus) {
|
||||
PBrowserChild::SendRequestFocus(aCanFocus);
|
||||
void BrowserChild::SendRequestFocus(bool aCanFocus, CallerType aCallerType) {
|
||||
PBrowserChild::SendRequestFocus(aCanFocus, aCallerType);
|
||||
}
|
||||
|
||||
void BrowserChild::EnableDisableCommands(
|
||||
|
|
|
@ -2229,10 +2229,11 @@ mozilla::ipc::IPCResult BrowserParent::RecvOnWindowedPluginKeyEvent(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult BrowserParent::RecvRequestFocus(const bool& aCanRaise) {
|
||||
mozilla::ipc::IPCResult BrowserParent::RecvRequestFocus(
|
||||
const bool& aCanRaise, const CallerType aCallerType) {
|
||||
LOGBROWSERFOCUS(("RecvRequestFocus %p, aCanRaise: %d", this, aCanRaise));
|
||||
if (BrowserBridgeParent* bridgeParent = GetBrowserBridgeParent()) {
|
||||
mozilla::Unused << bridgeParent->SendRequestFocus(aCanRaise);
|
||||
mozilla::Unused << bridgeParent->SendRequestFocus(aCanRaise, aCallerType);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -2240,7 +2241,7 @@ mozilla::ipc::IPCResult BrowserParent::RecvRequestFocus(const bool& aCanRaise) {
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
nsContentUtils::RequestFrameFocus(*mFrameElement, aCanRaise);
|
||||
nsContentUtils::RequestFrameFocus(*mFrameElement, aCanRaise, aCallerType);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -3138,7 +3139,7 @@ mozilla::ipc::IPCResult BrowserParent::RecvSetNativeChildOfShareableWindow(
|
|||
|
||||
mozilla::ipc::IPCResult BrowserParent::RecvDispatchFocusToTopLevelWindow() {
|
||||
if (nsCOMPtr<nsIWidget> widget = GetTopLevelWidget()) {
|
||||
widget->SetFocus(nsIWidget::Raise::No);
|
||||
widget->SetFocus(nsIWidget::Raise::No, CallerType::System);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
|
|
@ -424,7 +424,8 @@ class BrowserParent final : public PBrowserParent,
|
|||
mozilla::ipc::IPCResult RecvOnWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise);
|
||||
mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise,
|
||||
const CallerType aCallerType);
|
||||
|
||||
mozilla::ipc::IPCResult RecvLookUpDictionary(
|
||||
const nsString& aText, nsTArray<mozilla::FontRange>&& aFontRangeArray,
|
||||
|
|
|
@ -4047,8 +4047,8 @@ mozilla::ipc::IPCResult ContentChild::RecvWindowClose(BrowsingContext* aContext,
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(
|
||||
BrowsingContext* aContext) {
|
||||
mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(BrowsingContext* aContext,
|
||||
CallerType aCallerType) {
|
||||
if (!aContext) {
|
||||
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
|
||||
("ChildIPC: Trying to send a message to dead or detached context"));
|
||||
|
@ -4062,7 +4062,7 @@ mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(
|
|||
("ChildIPC: Trying to send a message to a context without a window"));
|
||||
return IPC_OK();
|
||||
}
|
||||
nsGlobalWindowOuter::Cast(window)->FocusOuter();
|
||||
nsGlobalWindowOuter::Cast(window)->FocusOuter(aCallerType);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -4213,7 +4213,7 @@ mozilla::ipc::IPCResult ContentChild::RecvInternalLoad(
|
|||
|
||||
if (aTakeFocus) {
|
||||
if (nsCOMPtr<nsPIDOMWindowOuter> domWin = aContext->GetDOMWindow()) {
|
||||
nsFocusManager::FocusWindow(domWin);
|
||||
nsFocusManager::FocusWindow(domWin, CallerType::System);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -751,7 +751,8 @@ class ContentChild final
|
|||
|
||||
mozilla::ipc::IPCResult RecvWindowClose(BrowsingContext* aContext,
|
||||
bool aTrustedCaller);
|
||||
mozilla::ipc::IPCResult RecvWindowFocus(BrowsingContext* aContext);
|
||||
mozilla::ipc::IPCResult RecvWindowFocus(BrowsingContext* aContext,
|
||||
CallerType aCallerType);
|
||||
mozilla::ipc::IPCResult RecvWindowBlur(BrowsingContext* aContext);
|
||||
mozilla::ipc::IPCResult RecvWindowPostMessage(
|
||||
BrowsingContext* aContext, const ClonedMessageData& aMessage,
|
||||
|
|
|
@ -6289,7 +6289,7 @@ mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
|
|||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentParent::RecvWindowFocus(
|
||||
BrowsingContext* aContext) {
|
||||
BrowsingContext* aContext, CallerType aCallerType) {
|
||||
if (!aContext || aContext->IsDiscarded()) {
|
||||
MOZ_LOG(
|
||||
BrowsingContext::GetLog(), LogLevel::Debug,
|
||||
|
@ -6300,7 +6300,7 @@ mozilla::ipc::IPCResult ContentParent::RecvWindowFocus(
|
|||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||
ContentParent* cp = cpm->GetContentProcessById(
|
||||
ContentParentId(aContext->Canonical()->OwnerProcessId()));
|
||||
Unused << cp->SendWindowFocus(aContext);
|
||||
Unused << cp->SendWindowFocus(aContext, aCallerType);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче