зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central a=merge
This commit is contained in:
Коммит
22aa841729
|
@ -39,9 +39,10 @@ class ChildDevToolsPanel extends ExtensionCommon.EventEmitter {
|
|||
this.id = id;
|
||||
this._panelContext = null;
|
||||
|
||||
this.mm = context.messageManager;
|
||||
this.mm.addMessageListener("Extension:DevToolsPanelShown", this);
|
||||
this.mm.addMessageListener("Extension:DevToolsPanelHidden", this);
|
||||
this.conduit = context.openConduit(this, {
|
||||
id: this.id,
|
||||
recv: ["PanelHidden", "PanelShown"],
|
||||
});
|
||||
}
|
||||
|
||||
get panelContext() {
|
||||
|
@ -69,29 +70,11 @@ class ChildDevToolsPanel extends ExtensionCommon.EventEmitter {
|
|||
return null;
|
||||
}
|
||||
|
||||
receiveMessage({ name, data }) {
|
||||
// Filter out any message that is not related to the id of this
|
||||
// toolbox panel.
|
||||
if (!data || data.toolboxPanelId !== this.id) {
|
||||
recvPanelShown() {
|
||||
// Ignore received call before the panel context exist.
|
||||
if (!this.panelContext || !this.panelContext.contentWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case "Extension:DevToolsPanelShown":
|
||||
// Filter out *Shown message received while the panel context do not yet
|
||||
// exist.
|
||||
if (!this.panelContext || !this.panelContext.contentWindow) {
|
||||
return;
|
||||
}
|
||||
this.onParentPanelShown();
|
||||
break;
|
||||
case "Extension:DevToolsPanelHidden":
|
||||
this.onParentPanelHidden();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onParentPanelShown() {
|
||||
const { document } = this.panelContext.contentWindow;
|
||||
|
||||
// Ensure that the onShown event is fired when the panel document has
|
||||
|
@ -101,7 +84,7 @@ class ChildDevToolsPanel extends ExtensionCommon.EventEmitter {
|
|||
});
|
||||
}
|
||||
|
||||
onParentPanelHidden() {
|
||||
recvPanelHidden() {
|
||||
this.emit("hidden");
|
||||
}
|
||||
|
||||
|
@ -140,9 +123,7 @@ class ChildDevToolsPanel extends ExtensionCommon.EventEmitter {
|
|||
}
|
||||
|
||||
close() {
|
||||
this.mm.removeMessageListener("Extension:DevToolsPanelShown", this);
|
||||
this.mm.removeMessageListener("Extension:DevToolsPanelHidden", this);
|
||||
|
||||
this.conduit.close();
|
||||
this._panelContext = null;
|
||||
this.context = null;
|
||||
}
|
||||
|
@ -166,50 +147,23 @@ class ChildDevToolsInspectorSidebar extends ExtensionCommon.EventEmitter {
|
|||
|
||||
this.id = id;
|
||||
|
||||
this.mm = context.messageManager;
|
||||
this.mm.addMessageListener("Extension:DevToolsInspectorSidebarShown", this);
|
||||
this.mm.addMessageListener(
|
||||
"Extension:DevToolsInspectorSidebarHidden",
|
||||
this
|
||||
);
|
||||
this.conduit = context.openConduit(this, {
|
||||
id: this.id,
|
||||
recv: ["InspectorSidebarHidden", "InspectorSidebarShown"],
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
this.mm.removeMessageListener(
|
||||
"Extension:DevToolsInspectorSidebarShown",
|
||||
this
|
||||
);
|
||||
this.mm.removeMessageListener(
|
||||
"Extension:DevToolsInspectorSidebarHidden",
|
||||
this
|
||||
);
|
||||
|
||||
this.conduit.close();
|
||||
this.content = null;
|
||||
}
|
||||
|
||||
receiveMessage({ name, data }) {
|
||||
// Filter out any message that is not related to the id of this
|
||||
// toolbox panel.
|
||||
if (!data || data.inspectorSidebarId !== this.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case "Extension:DevToolsInspectorSidebarShown":
|
||||
this.onParentSidebarShown();
|
||||
break;
|
||||
case "Extension:DevToolsInspectorSidebarHidden":
|
||||
this.onParentSidebarHidden();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onParentSidebarShown() {
|
||||
recvInspectorSidebarShown() {
|
||||
// TODO: wait and emit sidebar contentWindow once sidebar.setPage is supported.
|
||||
this.emit("shown");
|
||||
}
|
||||
|
||||
onParentSidebarHidden() {
|
||||
recvInspectorSidebarHidden() {
|
||||
this.emit("hidden");
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@ var { ExtensionParent } = ChromeUtils.import(
|
|||
"resource://gre/modules/ExtensionParent.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"BroadcastConduit",
|
||||
"resource://gre/modules/ConduitsParent.jsm"
|
||||
);
|
||||
|
||||
var { IconDetails, watchExtensionProxyContextLoad } = ExtensionParent;
|
||||
|
||||
var { promiseDocumentLoaded } = ExtensionUtils;
|
||||
|
@ -120,6 +126,11 @@ class ParentDevToolsPanel extends BaseDevToolsPanel {
|
|||
|
||||
this.context.callOnClose(this);
|
||||
|
||||
this.conduit = new BroadcastConduit(this, {
|
||||
id: `${this.id}-parent`,
|
||||
send: ["PanelHidden", "PanelShown"],
|
||||
});
|
||||
|
||||
this.onToolboxPanelSelect = this.onToolboxPanelSelect.bind(this);
|
||||
this.onToolboxHostWillChange = this.onToolboxHostWillChange.bind(this);
|
||||
this.onToolboxHostChanged = this.onToolboxHostChanged.bind(this);
|
||||
|
@ -201,12 +212,7 @@ class ParentDevToolsPanel extends BaseDevToolsPanel {
|
|||
// Fires a panel.onHidden event before destroying the browser element because
|
||||
// the toolbox hosts is changing.
|
||||
if (this.visible) {
|
||||
this.context.parentMessageManager.sendAsyncMessage(
|
||||
"Extension:DevToolsPanelHidden",
|
||||
{
|
||||
toolboxPanelId: this.id,
|
||||
}
|
||||
);
|
||||
this.conduit.sendPanelHidden(this.id);
|
||||
}
|
||||
|
||||
this.destroyBrowserElement();
|
||||
|
@ -222,13 +228,7 @@ class ParentDevToolsPanel extends BaseDevToolsPanel {
|
|||
// object to the extension page that has created the devtools panel).
|
||||
if (this.visible) {
|
||||
await this.waitTopLevelContext;
|
||||
|
||||
this.context.parentMessageManager.sendAsyncMessage(
|
||||
"Extension:DevToolsPanelShown",
|
||||
{
|
||||
toolboxPanelId: this.id,
|
||||
}
|
||||
);
|
||||
this.conduit.sendPanelShown(this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -243,16 +243,11 @@ class ParentDevToolsPanel extends BaseDevToolsPanel {
|
|||
|
||||
if (!this.visible && id === this.id) {
|
||||
this.visible = true;
|
||||
this.conduit.sendPanelShown(this.id);
|
||||
} else if (this.visible && id !== this.id) {
|
||||
this.visible = false;
|
||||
this.conduit.sendPanelHidden(this.id);
|
||||
}
|
||||
|
||||
const extensionMessage = `Extension:DevToolsPanel${
|
||||
this.visible ? "Shown" : "Hidden"
|
||||
}`;
|
||||
this.context.parentMessageManager.sendAsyncMessage(extensionMessage, {
|
||||
toolboxPanelId: this.id,
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
|
@ -262,6 +257,8 @@ class ParentDevToolsPanel extends BaseDevToolsPanel {
|
|||
throw new Error("Unable to destroy a closed devtools panel");
|
||||
}
|
||||
|
||||
this.conduit.close();
|
||||
|
||||
// Explicitly remove the panel if it is registered and the toolbox is not
|
||||
// closing itself.
|
||||
if (this.panelAdded && toolbox.isToolRegistered(this.id)) {
|
||||
|
@ -360,6 +357,11 @@ class ParentDevToolsInspectorSidebar extends BaseDevToolsPanel {
|
|||
|
||||
this.context.callOnClose(this);
|
||||
|
||||
this.conduit = new BroadcastConduit(this, {
|
||||
id: `${this.id}-parent`,
|
||||
send: ["InspectorSidebarHidden", "InspectorSidebarShown"],
|
||||
});
|
||||
|
||||
this.onSidebarSelect = this.onSidebarSelect.bind(this);
|
||||
this.onSidebarCreated = this.onSidebarCreated.bind(this);
|
||||
this.onExtensionPageMount = this.onExtensionPageMount.bind(this);
|
||||
|
@ -393,6 +395,8 @@ class ParentDevToolsInspectorSidebar extends BaseDevToolsPanel {
|
|||
throw new Error("Unable to close a destroyed DevToolsSelectionObserver");
|
||||
}
|
||||
|
||||
this.conduit.close();
|
||||
|
||||
if (this.extensionSidebar) {
|
||||
this.extensionSidebar.off(
|
||||
"extension-page-mount",
|
||||
|
@ -476,20 +480,10 @@ class ParentDevToolsInspectorSidebar extends BaseDevToolsPanel {
|
|||
|
||||
if (!this.visible && id === this.id) {
|
||||
this.visible = true;
|
||||
this.context.parentMessageManager.sendAsyncMessage(
|
||||
"Extension:DevToolsInspectorSidebarShown",
|
||||
{
|
||||
inspectorSidebarId: this.id,
|
||||
}
|
||||
);
|
||||
this.conduit.sendInspectorSidebarShown(this.id);
|
||||
} else if (this.visible && id !== this.id) {
|
||||
this.visible = false;
|
||||
this.context.parentMessageManager.sendAsyncMessage(
|
||||
"Extension:DevToolsInspectorSidebarHidden",
|
||||
{
|
||||
inspectorSidebarId: this.id,
|
||||
}
|
||||
);
|
||||
this.conduit.sendInspectorSidebarHidden(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,8 +114,7 @@ support-files = !/browser/components/places/tests/browser/head.js
|
|||
[browser_ext_devtools_inspectedWindow_eval_bindings.js]
|
||||
[browser_ext_devtools_inspectedWindow_reload.js]
|
||||
[browser_ext_devtools_network.js]
|
||||
fail-if = fission
|
||||
skip-if = os == 'linux' || (os == 'mac' && debug) || (debug && os == 'win' && bits == 64) # Bug1570478
|
||||
skip-if = fission || os == 'linux' || (os == 'mac' && debug) || (debug && os == 'win' && bits == 64) # Bug1570478
|
||||
[browser_ext_devtools_page.js]
|
||||
[browser_ext_devtools_page_incognito.js]
|
||||
[browser_ext_devtools_panel.js]
|
||||
|
|
|
@ -22,12 +22,10 @@ add_task(async function testExecuteScriptAtOnUpdated() {
|
|||
let ignore = false;
|
||||
let url;
|
||||
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (
|
||||
url &&
|
||||
changeInfo.status === "loading" &&
|
||||
tab.url === url &&
|
||||
!ignore
|
||||
) {
|
||||
if (ignore) {
|
||||
return;
|
||||
}
|
||||
if (url && changeInfo.status === "loading" && tab.url === url) {
|
||||
ignore = true;
|
||||
browser.tabs
|
||||
.executeScript(tabId, {
|
||||
|
|
|
@ -18,12 +18,6 @@ ChromeUtils.defineModuleGetter(
|
|||
"resource://gre/modules/ExtensionCommon.jsm"
|
||||
);
|
||||
|
||||
const { PromiseTestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/PromiseTestUtils.jsm"
|
||||
);
|
||||
|
||||
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
|
||||
|
||||
add_task(async function test_delete() {
|
||||
function background() {
|
||||
let historyClearedCount = 0;
|
||||
|
@ -502,7 +496,7 @@ add_task(async function test_add_url() {
|
|||
});
|
||||
|
||||
add_task(async function test_get_visits() {
|
||||
function background() {
|
||||
async function background() {
|
||||
const TEST_DOMAIN = "http://example.com/";
|
||||
const FIRST_DATE = Date.now();
|
||||
const INITIAL_DETAILS = {
|
||||
|
@ -513,7 +507,7 @@ add_task(async function test_get_visits() {
|
|||
|
||||
let visitIds = new Set();
|
||||
|
||||
function checkVisit(visit, expected) {
|
||||
async function checkVisit(visit, expected) {
|
||||
visitIds.add(visit.visitId);
|
||||
browser.test.assertEq(
|
||||
expected.visitTime,
|
||||
|
@ -525,67 +519,52 @@ add_task(async function test_get_visits() {
|
|||
visit.transition,
|
||||
"visit has the correct transition"
|
||||
);
|
||||
browser.history.search({ text: expected.url }).then(results => {
|
||||
// all results will have the same id, so we only need to use the first one
|
||||
browser.test.assertEq(
|
||||
results[0].id,
|
||||
visit.id,
|
||||
"visit has the correct id"
|
||||
);
|
||||
});
|
||||
let results = await browser.history.search({ text: expected.url });
|
||||
// all results will have the same id, so we only need to use the first one
|
||||
browser.test.assertEq(
|
||||
results[0].id,
|
||||
visit.id,
|
||||
"visit has the correct id"
|
||||
);
|
||||
}
|
||||
|
||||
let details = Object.assign({}, INITIAL_DETAILS);
|
||||
|
||||
browser.history
|
||||
.addUrl(details)
|
||||
.then(() => {
|
||||
return browser.history.getVisits({ url: details.url });
|
||||
})
|
||||
.then(results => {
|
||||
browser.test.assertEq(
|
||||
1,
|
||||
results.length,
|
||||
"the expected number of visits were returned"
|
||||
);
|
||||
checkVisit(results[0], details);
|
||||
details.url = `${TEST_DOMAIN}/1/`;
|
||||
return browser.history.addUrl(details);
|
||||
})
|
||||
.then(() => {
|
||||
return browser.history.getVisits({ url: details.url });
|
||||
})
|
||||
.then(results => {
|
||||
browser.test.assertEq(
|
||||
1,
|
||||
results.length,
|
||||
"the expected number of visits were returned"
|
||||
);
|
||||
checkVisit(results[0], details);
|
||||
details.visitTime = FIRST_DATE - 1000;
|
||||
details.transition = "typed";
|
||||
return browser.history.addUrl(details);
|
||||
})
|
||||
.then(() => {
|
||||
return browser.history.getVisits({ url: details.url });
|
||||
})
|
||||
.then(results => {
|
||||
browser.test.assertEq(
|
||||
2,
|
||||
results.length,
|
||||
"the expected number of visits were returned"
|
||||
);
|
||||
checkVisit(results[0], INITIAL_DETAILS);
|
||||
checkVisit(results[1], details);
|
||||
})
|
||||
.then(() => {
|
||||
browser.test.assertEq(
|
||||
3,
|
||||
visitIds.size,
|
||||
"each visit has a unique visitId"
|
||||
);
|
||||
browser.test.notifyPass("get-visits");
|
||||
});
|
||||
await browser.history.addUrl(details);
|
||||
let results = await browser.history.getVisits({ url: details.url });
|
||||
|
||||
browser.test.assertEq(
|
||||
1,
|
||||
results.length,
|
||||
"the expected number of visits were returned"
|
||||
);
|
||||
await checkVisit(results[0], details);
|
||||
|
||||
details.url = `${TEST_DOMAIN}/1/`;
|
||||
await browser.history.addUrl(details);
|
||||
|
||||
results = await browser.history.getVisits({ url: details.url });
|
||||
browser.test.assertEq(
|
||||
1,
|
||||
results.length,
|
||||
"the expected number of visits were returned"
|
||||
);
|
||||
await checkVisit(results[0], details);
|
||||
|
||||
details.visitTime = FIRST_DATE - 1000;
|
||||
details.transition = "typed";
|
||||
await browser.history.addUrl(details);
|
||||
results = await browser.history.getVisits({ url: details.url });
|
||||
|
||||
browser.test.assertEq(
|
||||
2,
|
||||
results.length,
|
||||
"the expected number of visits were returned"
|
||||
);
|
||||
await checkVisit(results[0], INITIAL_DETAILS);
|
||||
await checkVisit(results[1], details);
|
||||
browser.test.assertEq(3, visitIds.size, "each visit has a unique visitId");
|
||||
await browser.test.notifyPass("get-visits");
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
next="importItems">
|
||||
<description control="profiles">&selectProfile.label;</description>
|
||||
|
||||
<radiogroup id="profiles" align="left"/>
|
||||
<radiogroup id="profiles" align="start"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="importItems" pageid="importItems" label="&importItems.title;"
|
||||
|
@ -70,20 +70,20 @@
|
|||
oncommand="MigrationWizard.onImportItemCommand();">
|
||||
<description control="dataSources">&importItems.label;</description>
|
||||
|
||||
<vbox id="dataSources" style="overflow: auto; -moz-appearance: listbox" align="left" flex="1" role="group"/>
|
||||
<vbox id="dataSources" style="overflow: auto; -moz-appearance: listbox" align="start" flex="1" role="group"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="migrating" pageid="migrating" label="&migrating.title;"
|
||||
next="done">
|
||||
<description control="migratingItems">&migrating.label;</description>
|
||||
|
||||
<vbox id="migratingItems" style="overflow: auto;" align="left" role="group"/>
|
||||
<vbox id="migratingItems" style="overflow: auto;" align="start" role="group"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="done" pageid="done" label="&done.title;">
|
||||
<description control="doneItems">&done.label;</description>
|
||||
|
||||
<vbox id="doneItems" style="overflow: auto;" align="left" role="group"/>
|
||||
<vbox id="doneItems" style="overflow: auto;" align="start" role="group"/>
|
||||
</wizardpage>
|
||||
|
||||
</wizard>
|
||||
|
|
|
@ -44,6 +44,7 @@ Please note that some targeting attributes require stricter controls on the tele
|
|||
* [platformName](#platformname)
|
||||
* [personalizedCfrScores](#personalizedcfrscores)
|
||||
* [personalizedCfrThreshold](#personalizedcfrthreshold)
|
||||
* [messageImpressions](#messageimpressions)
|
||||
|
||||
## Detailed usage
|
||||
|
||||
|
@ -615,4 +616,23 @@ See more in [CFR Machine Learning Experiment](https://bugzilla.mozilla.org/show_
|
|||
|
||||
```
|
||||
declare const personalizedCfrThreshold = float;
|
||||
```
|
||||
```
|
||||
|
||||
### `messageImpressions`
|
||||
|
||||
Dictionary that maps message ids to impression timestamps. Timestamps are stored in
|
||||
consecutive order. Can be used to detect first impression of a message, number of
|
||||
impressions. Can be used in targeting to show a message if another message has been
|
||||
seen.
|
||||
Impressions are used for frequency capping so we only store them if the message has
|
||||
`frequency` configured.
|
||||
Impressions for badges might not work as expected: we add a badge for every opened
|
||||
window so the number of impressions stored might be higher than expected. Additionally
|
||||
not all badges have `frequency` cap so `messageImpressions` might not be defined.
|
||||
Badge impressions should not be used for targeting.
|
||||
|
||||
#### Definition
|
||||
|
||||
```
|
||||
declare const messageImpressions: { [key: string]: Array<UnixEpochNumber> };
|
||||
```
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Guide to targeting with JEXL
|
||||
|
||||
For a more in-depth explanation of JEXL syntax you can read the [Normady project docs](https://normandy.readthedocs.io/en/stable/user/filters.html#jexl-basics).
|
||||
For a more in-depth explanation of JEXL syntax you can read the [Normady project docs](https://mozilla.github.io/normandy/user/filters.html?highlight=jexl).
|
||||
|
||||
### How to write JEXL targeting expressions
|
||||
A message needs to contain the `targeting` property (JEXL string) which is evaluated against the provided attributes.
|
||||
|
|
|
@ -265,6 +265,11 @@ class _ToolbarBadgeHub {
|
|||
toolbarbutton.addEventListener("keypress", this.removeAllNotifications);
|
||||
this.state = { notification: { id: message.id } };
|
||||
|
||||
// Impression should be added when the badge becomes visible
|
||||
this._addImpression(message);
|
||||
// Send a telemetry ping when adding the notification badge
|
||||
this.sendUserEventTelemetry("IMPRESSION", message);
|
||||
|
||||
return toolbarbutton;
|
||||
}
|
||||
|
||||
|
@ -278,11 +283,6 @@ class _ToolbarBadgeHub {
|
|||
return;
|
||||
}
|
||||
|
||||
// Impression should be added when the badge becomes visible
|
||||
this._addImpression(message);
|
||||
// Send a telemetry ping when adding the notification badge
|
||||
this.sendUserEventTelemetry("IMPRESSION", message);
|
||||
|
||||
EveryWindow.registerCallback(
|
||||
this.id,
|
||||
win => {
|
||||
|
|
|
@ -74,7 +74,11 @@ describe("ASRTargeting docs", () => {
|
|||
// whitelist includes targeting attributes that are not implemented by
|
||||
// ASRTargetingAttributes. For example trigger context passed to the evaluation
|
||||
// context in when a trigger runs or ASRouter state used in the evaluation.
|
||||
const whitelist = ["personalizedCfrThreshold", "personalizedCfrScores"];
|
||||
const whitelist = [
|
||||
"personalizedCfrThreshold",
|
||||
"personalizedCfrScores",
|
||||
"messageImpressions",
|
||||
];
|
||||
for (const targetingParam of DOCS_TARGETING_HEADINGS.filter(
|
||||
doc => !whitelist.includes(doc)
|
||||
)) {
|
||||
|
|
|
@ -206,7 +206,11 @@ describe("ToolbarBadgeHub", () => {
|
|||
describe("addToolbarNotification", () => {
|
||||
let target;
|
||||
let fakeDocument;
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
await instance.init(sandbox.stub().resolves(), {
|
||||
addImpression: fakeAddImpression,
|
||||
dispatch: fakeDispatch,
|
||||
});
|
||||
fakeDocument = {
|
||||
getElementById: sandbox.stub().returns(fakeElement),
|
||||
createElement: sandbox.stub().returns(fakeElement),
|
||||
|
@ -214,6 +218,9 @@ describe("ToolbarBadgeHub", () => {
|
|||
};
|
||||
target = { ...fakeWindow, browser: { ownerDocument: fakeDocument } };
|
||||
});
|
||||
afterEach(() => {
|
||||
instance.uninit();
|
||||
});
|
||||
it("shouldn't do anything if target element is not found", () => {
|
||||
fakeDocument.getElementById.returns(null);
|
||||
instance.addToolbarNotification(target, fxaMessage);
|
||||
|
@ -296,6 +303,23 @@ describe("ToolbarBadgeHub", () => {
|
|||
whatsnewMessage.content.badgeDescription.string_id
|
||||
);
|
||||
});
|
||||
it("should add an impression for the message", () => {
|
||||
instance.addToolbarNotification(target, whatsnewMessage);
|
||||
|
||||
assert.calledOnce(instance._addImpression);
|
||||
assert.calledWithExactly(instance._addImpression, whatsnewMessage);
|
||||
});
|
||||
it("should send an impression ping", async () => {
|
||||
sandbox.stub(instance, "sendUserEventTelemetry");
|
||||
instance.addToolbarNotification(target, whatsnewMessage);
|
||||
|
||||
assert.calledOnce(instance.sendUserEventTelemetry);
|
||||
assert.calledWithExactly(
|
||||
instance.sendUserEventTelemetry,
|
||||
"IMPRESSION",
|
||||
whatsnewMessage
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("registerBadgeNotificationListener", () => {
|
||||
let msg_no_delay;
|
||||
|
@ -317,12 +341,6 @@ describe("ToolbarBadgeHub", () => {
|
|||
afterEach(() => {
|
||||
instance.uninit();
|
||||
});
|
||||
it("should add an impression for the message", () => {
|
||||
instance.registerBadgeNotificationListener(msg_no_delay);
|
||||
|
||||
assert.calledOnce(instance._addImpression);
|
||||
assert.calledWithExactly(instance._addImpression, msg_no_delay);
|
||||
});
|
||||
it("should register a callback that adds/removes the notification", () => {
|
||||
instance.registerBadgeNotificationListener(msg_no_delay);
|
||||
|
||||
|
@ -356,18 +374,6 @@ describe("ToolbarBadgeHub", () => {
|
|||
assert.calledOnce(instance.removeToolbarNotification);
|
||||
assert.calledWithExactly(instance.removeToolbarNotification, fakeElement);
|
||||
});
|
||||
it("should send an impression", async () => {
|
||||
sandbox.stub(instance, "sendUserEventTelemetry");
|
||||
|
||||
instance.registerBadgeNotificationListener(msg_no_delay);
|
||||
|
||||
assert.calledOnce(instance.sendUserEventTelemetry);
|
||||
assert.calledWithExactly(
|
||||
instance.sendUserEventTelemetry,
|
||||
"IMPRESSION",
|
||||
msg_no_delay
|
||||
);
|
||||
});
|
||||
it("should unregister notifications when forcing a badge via devtools", () => {
|
||||
instance.registerBadgeNotificationListener(msg_no_delay, { force: true });
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
</tree>
|
||||
</vbox>
|
||||
|
||||
<hbox class="actionButtons" align="right" flex="1">
|
||||
<hbox class="actionButtons" align="end" flex="1">
|
||||
<button oncommand="window.close();" icon="close"
|
||||
data-l10n-id="blocklist-button-cancel"/>
|
||||
<button id="btnApplyChanges" oncommand="gBlocklistManager.onApplyChanges();" icon="save"
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</vbox>
|
||||
</vbox>
|
||||
|
||||
<hbox class="actionButtons" align="right" flex="1">
|
||||
<hbox class="actionButtons" align="end" flex="1">
|
||||
<button id="cancelButton" icon="close"
|
||||
data-l10n-id="clear-site-data-cancel"/>
|
||||
<button id="clearButton" icon="save"
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
<html:input id="networkProxyHTTP" type="text" style="-moz-box-flex: 1;"
|
||||
preference="network.proxy.http"/>
|
||||
<label data-l10n-id="connection-proxy-http-port" control="networkProxyHTTP_Port" />
|
||||
<html:input id="networkProxyHTTP_Port" class="proxy-port-input" hidespinbuttons="true" type="number" min="0" max="65535"
|
||||
<html:input id="networkProxyHTTP_Port" class="proxy-port-input input-number-mozbox" hidespinbuttons="true" type="number" min="0" max="65535"
|
||||
preference="network.proxy.http_port"/>
|
||||
</hbox>
|
||||
<hbox/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<hbox align="center">
|
||||
<html:input id="networkProxySSL" type="text" style="-moz-box-flex: 1;" preference="network.proxy.ssl"/>
|
||||
<label data-l10n-id="connection-proxy-ssl-port" control="networkProxySSL_Port" />
|
||||
<html:input id="networkProxySSL_Port" class="proxy-port-input" hidespinbuttons="true" type="number" min="0" max="65535" size="5"
|
||||
<html:input id="networkProxySSL_Port" class="proxy-port-input input-number-mozbox" hidespinbuttons="true" type="number" min="0" max="65535" size="5"
|
||||
preference="network.proxy.ssl_port"/>
|
||||
</hbox>
|
||||
<hbox pack="end">
|
||||
|
@ -96,7 +96,7 @@
|
|||
<hbox align="center">
|
||||
<html:input id="networkProxyFTP" type="text" style="-moz-box-flex: 1;" preference="network.proxy.ftp"/>
|
||||
<label data-l10n-id="connection-proxy-ftp-port" control="networkProxyFTP_Port"/>
|
||||
<html:input id="networkProxyFTP_Port" class="proxy-port-input" hidespinbuttons="true" type="number" min="0" max="65535" size="5"
|
||||
<html:input id="networkProxyFTP_Port" class="proxy-port-input input-number-mozbox" hidespinbuttons="true" type="number" min="0" max="65535" size="5"
|
||||
preference="network.proxy.ftp_port"/>
|
||||
</hbox>
|
||||
<hbox pack="end">
|
||||
|
@ -105,7 +105,7 @@
|
|||
<hbox align="center">
|
||||
<html:input id="networkProxySOCKS" type="text" style="-moz-box-flex: 1;" preference="network.proxy.socks"/>
|
||||
<label data-l10n-id="connection-proxy-socks-port" control="networkProxySOCKS_Port"/>
|
||||
<html:input id="networkProxySOCKS_Port" class="proxy-port-input" hidespinbuttons="true" type="number" min="0" max="65535" size="5"
|
||||
<html:input id="networkProxySOCKS_Port" class="proxy-port-input input-number-mozbox" hidespinbuttons="true" type="number" min="0" max="65535" size="5"
|
||||
preference="network.proxy.socks_port"/>
|
||||
</hbox>
|
||||
<spacer/>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<hbox class="actionButtons" align="right" flex="1">
|
||||
<hbox class="actionButtons" align="end" flex="1">
|
||||
<button id="btnApplyChanges" disabled="true" oncommand="gContainersManager.onApplyChanges();" icon="save"
|
||||
data-l10n-id="containers-button-done"/>
|
||||
</hbox>
|
||||
|
|
|
@ -65,7 +65,7 @@ let gContainersPane = {
|
|||
let containerButtons = document.createXULElement("hbox");
|
||||
containerButtons.className = "container-buttons";
|
||||
containerButtons.setAttribute("flex", 1);
|
||||
containerButtons.setAttribute("align", "right");
|
||||
containerButtons.setAttribute("align", "end");
|
||||
item.appendChild(containerButtons);
|
||||
|
||||
let prefsButton = document.createXULElement("button");
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
onselect="gPermissionManager.onPermissionSelect();"/>
|
||||
</vbox>
|
||||
|
||||
<hbox class="actionButtons" align="left" flex="1">
|
||||
<hbox class="actionButtons" align="start" flex="1">
|
||||
<button id="removePermission" disabled="true"
|
||||
data-l10n-id="permissions-remove"
|
||||
icon="remove"
|
||||
|
@ -70,7 +70,7 @@
|
|||
oncommand="gPermissionManager.onAllPermissionsDelete();"/>
|
||||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
<hbox class="actionButtons" align="right" flex="1">
|
||||
<hbox class="actionButtons" align="end" flex="1">
|
||||
<button oncommand="window.close();" icon="close"
|
||||
data-l10n-id="permissions-button-cancel" />
|
||||
<button id="btnApplyChanges" oncommand="gPermissionManager.onApplyChanges();" icon="save"
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
onselect="gSitePermissionsManager.onPermissionSelect();"/>
|
||||
</vbox>
|
||||
|
||||
<hbox class="actionButtons" align="left" flex="1">
|
||||
<hbox class="actionButtons" align="start" flex="1">
|
||||
<button id="removePermission" disabled="true"
|
||||
data-l10n-id="permissions-remove"
|
||||
icon="remove"
|
||||
|
@ -79,7 +79,7 @@
|
|||
class="extension-controlled-button accessory-button"
|
||||
data-l10n-id="disable-extension"/>
|
||||
</hbox>
|
||||
<hbox class="actionButtons" align="right" flex="1">
|
||||
<hbox class="actionButtons" align="end" flex="1">
|
||||
<button oncommand="window.close();" icon="close" id="cancel"
|
||||
data-l10n-id="permissions-button-cancel" />
|
||||
<button id="btnApplyChanges" oncommand="gSitePermissionsManager.onApplyChanges();" icon="save"
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
#ifdef XP_MACOSX
|
||||
<separator/>
|
||||
|
||||
<hbox align="right">
|
||||
<hbox align="end">
|
||||
<button id="setDesktopBackground"
|
||||
label="&setDesktopBackground.title;"
|
||||
oncommand="gSetBackground.setDesktopBackground();"/>
|
||||
|
|
|
@ -296,6 +296,13 @@ class TouchBarHelper {
|
|||
layoutItems.appendElement(input);
|
||||
}
|
||||
|
||||
// Every input must be updated at least once so that all assets (titles,
|
||||
// icons) are loaded. We keep track of which inputs haven't updated and
|
||||
// run an update on them after the first location change.
|
||||
this._inputsNotUpdated = new Set(Object.keys(kBuiltInInputs));
|
||||
// This is a temporary workaround until bug 1596723 is resolved.
|
||||
this._inputsNotUpdated.delete("SearchPopover");
|
||||
|
||||
return layoutItems;
|
||||
}
|
||||
|
||||
|
@ -356,6 +363,9 @@ class TouchBarHelper {
|
|||
kBuiltInInputs[inputName].localTitle = result; // Cache result.
|
||||
// Checking TouchBarHelper.window since this callback can fire after all windows are closed.
|
||||
if (TouchBarHelper.window) {
|
||||
if (this._inputsNotUpdated) {
|
||||
this._inputsNotUpdated.delete(inputName);
|
||||
}
|
||||
gTouchBarUpdater.updateTouchBarInputs(TouchBarHelper.baseWindow, [
|
||||
item,
|
||||
]);
|
||||
|
@ -367,22 +377,23 @@ class TouchBarHelper {
|
|||
|
||||
/**
|
||||
* Fetches a specific Touch Bar Input by name and updates it on the Touch Bar.
|
||||
* @param {string} inputName
|
||||
* A key to a value in the kBuiltInInputs object in this file.
|
||||
* @param {...*} [otherInputs] (optional)
|
||||
* Additional keys to values in the kBuiltInInputs object in this file.
|
||||
* @param {...*} inputNames
|
||||
* A key/keys to a value/values in the kBuiltInInputs object in this file.
|
||||
*/
|
||||
_updateTouchBarInputs(...inputNames) {
|
||||
if (!TouchBarHelper.window) {
|
||||
if (!TouchBarHelper.window || !inputNames.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let inputs = [];
|
||||
for (let inputName of inputNames) {
|
||||
for (let inputName of new Set(inputNames)) {
|
||||
let input = this.getTouchBarInput(inputName);
|
||||
if (!input) {
|
||||
continue;
|
||||
}
|
||||
if (this._inputsNotUpdated) {
|
||||
this._inputsNotUpdated.delete(inputName);
|
||||
}
|
||||
inputs.push(input);
|
||||
}
|
||||
|
||||
|
@ -419,7 +430,12 @@ class TouchBarHelper {
|
|||
.canGoBack;
|
||||
kBuiltInInputs.Forward.disabled = !TouchBarHelper.window.gBrowser
|
||||
.canGoForward;
|
||||
this._updateTouchBarInputs("ReaderView", "Back", "Forward");
|
||||
this._updateTouchBarInputs(
|
||||
"ReaderView",
|
||||
"Back",
|
||||
"Forward",
|
||||
...this._inputsNotUpdated
|
||||
);
|
||||
break;
|
||||
case "bookmark-icon-updated":
|
||||
data == "starred"
|
||||
|
|
|
@ -9,7 +9,6 @@ const { XPCOMUtils } = ChromeUtils.import(
|
|||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
|
||||
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
|
||||
Preferences: "resource://gre/modules/Preferences.jsm",
|
||||
ProfileAge: "resource://gre/modules/ProfileAge.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
ResetProfile: "resource://gre/modules/ResetProfile.jsm",
|
||||
|
@ -28,12 +27,6 @@ XPCOMUtils.defineLazyServiceGetter(
|
|||
"nsIUpdateManager"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(
|
||||
this,
|
||||
"defaultPreferences",
|
||||
() => new Preferences({ defaultBranch: true })
|
||||
);
|
||||
|
||||
function updateStateIs(prefix) {
|
||||
let update = updateManager.activeUpdate;
|
||||
return !!(update && update.state.startsWith(prefix));
|
||||
|
@ -44,10 +37,6 @@ this.experiments_urlbar = class extends ExtensionAPI {
|
|||
return {
|
||||
experiments: {
|
||||
urlbar: {
|
||||
engagementTelemetry: this._getDefaultSettingsAPI(
|
||||
"browser.urlbar.eventTelemetry.enabled"
|
||||
),
|
||||
|
||||
isBrowserShowingNotification() {
|
||||
let window = BrowserWindowTracker.getTopWindow();
|
||||
|
||||
|
@ -134,10 +123,6 @@ this.experiments_urlbar = class extends ExtensionAPI {
|
|||
return (await age.firstUse) || age.created;
|
||||
},
|
||||
|
||||
openViewOnFocus: this._getDefaultSettingsAPI(
|
||||
"browser.urlbar.openViewOnFocus"
|
||||
),
|
||||
|
||||
restartBrowser() {
|
||||
// Notify all windows that an application quit has been requested.
|
||||
let cancelQuit = Cc[
|
||||
|
@ -173,40 +158,4 @@ this.experiments_urlbar = class extends ExtensionAPI {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
onShutdown() {
|
||||
// Reset the default prefs. This is necessary because
|
||||
// ExtensionPreferencesManager doesn't properly reset prefs set on the
|
||||
// default branch. See bug 1586543, bug 1578513, bug 1578508.
|
||||
if (this._initialDefaultPrefs) {
|
||||
for (let [pref, value] of this._initialDefaultPrefs.entries()) {
|
||||
defaultPreferences.set(pref, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getDefaultSettingsAPI(pref) {
|
||||
return {
|
||||
get: details => {
|
||||
return { value: Preferences.get(pref) };
|
||||
},
|
||||
set: details => {
|
||||
if (!this._initialDefaultPrefs) {
|
||||
this._initialDefaultPrefs = new Map();
|
||||
}
|
||||
if (!this._initialDefaultPrefs.has(pref)) {
|
||||
this._initialDefaultPrefs.set(pref, defaultPreferences.get(pref));
|
||||
}
|
||||
defaultPreferences.set(pref, details.value);
|
||||
return true;
|
||||
},
|
||||
clear: details => {
|
||||
if (this._initialDefaultPrefs && this._initialDefaultPrefs.has(pref)) {
|
||||
defaultPreferences.set(pref, this._initialDefaultPrefs.get(pref));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,9 +9,7 @@ support-files =
|
|||
../schema.json
|
||||
head.js
|
||||
|
||||
[browser_ext_urlbar_engagementTelemetry.js]
|
||||
[browser_ext_urlbar_isBrowserShowingNotification.js]
|
||||
[browser_ext_urlbar_isBrowserUpdateReadyToInstall.js]
|
||||
[browser_ext_urlbar_lastBrowserUpdateDate.js]
|
||||
[browser_ext_urlbar_openViewOnFocus.js]
|
||||
[browser_ext_urlbar_resetBrowser.js]
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* global browser */
|
||||
|
||||
// This tests the browser.experiments.urlbar.engagementTelemetry WebExtension
|
||||
// Experiment API.
|
||||
|
||||
"use strict";
|
||||
|
||||
add_settings_tasks("browser.urlbar.eventTelemetry.enabled", () => {
|
||||
browser.test.onMessage.addListener(async (method, arg) => {
|
||||
let result = await browser.experiments.urlbar.engagementTelemetry[method](
|
||||
arg
|
||||
);
|
||||
browser.test.sendMessage("done", result);
|
||||
});
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* global browser */
|
||||
|
||||
// This tests the browser.experiments.urlbar.openViewOnFocus WebExtension
|
||||
// Experiment API.
|
||||
|
||||
"use strict";
|
||||
|
||||
add_settings_tasks("browser.urlbar.openViewOnFocus", () => {
|
||||
browser.test.onMessage.addListener(async (method, arg) => {
|
||||
let result = await browser.experiments.urlbar.openViewOnFocus[method](arg);
|
||||
browser.test.sendMessage("done", result);
|
||||
});
|
||||
});
|
|
@ -25,10 +25,6 @@ Services.scriptloader.loadSubScript(
|
|||
this
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Preferences: "resource://gre/modules/Preferences.jsm",
|
||||
});
|
||||
|
||||
const SCHEMA_BASENAME = "schema.json";
|
||||
const SCRIPT_BASENAME = "api.js";
|
||||
|
||||
|
@ -68,140 +64,3 @@ async function loadExtension(background) {
|
|||
await ext.startup();
|
||||
return ext;
|
||||
}
|
||||
|
||||
function add_settings_tasks(prefName, background) {
|
||||
let defaultPreferences = new Preferences({ defaultBranch: true });
|
||||
|
||||
let originalValue = defaultPreferences.get(prefName);
|
||||
registerCleanupFunction(() => {
|
||||
defaultPreferences.set(prefName, originalValue);
|
||||
});
|
||||
|
||||
add_task(async function get() {
|
||||
let ext = await loadExtension(background);
|
||||
|
||||
defaultPreferences.set(prefName, false);
|
||||
ext.sendMessage("get", {});
|
||||
let result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result.value, false);
|
||||
|
||||
defaultPreferences.set(prefName, true);
|
||||
ext.sendMessage("get", {});
|
||||
result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result.value, true);
|
||||
|
||||
await ext.unload();
|
||||
});
|
||||
|
||||
add_task(async function set() {
|
||||
let ext = await loadExtension(background);
|
||||
|
||||
defaultPreferences.set(prefName, false);
|
||||
ext.sendMessage("set", { value: true });
|
||||
let result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result, true);
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), true);
|
||||
|
||||
ext.sendMessage("set", { value: false });
|
||||
result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result, true);
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), false);
|
||||
|
||||
await ext.unload();
|
||||
});
|
||||
|
||||
add_task(async function clear() {
|
||||
// no set()
|
||||
defaultPreferences.set(prefName, false);
|
||||
let ext = await loadExtension(background);
|
||||
ext.sendMessage("clear", {});
|
||||
let result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result, false);
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), false);
|
||||
await ext.unload();
|
||||
|
||||
// false -> true
|
||||
defaultPreferences.set(prefName, false);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: true });
|
||||
await ext.awaitMessage("done");
|
||||
ext.sendMessage("clear", {});
|
||||
result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result, true);
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), false);
|
||||
await ext.unload();
|
||||
|
||||
// true -> false
|
||||
defaultPreferences.set(prefName, true);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: false });
|
||||
await ext.awaitMessage("done");
|
||||
ext.sendMessage("clear", {});
|
||||
result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result, true);
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), true);
|
||||
await ext.unload();
|
||||
|
||||
// false -> false
|
||||
defaultPreferences.set(prefName, false);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: false });
|
||||
await ext.awaitMessage("done");
|
||||
ext.sendMessage("clear", {});
|
||||
result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result, true);
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), false);
|
||||
await ext.unload();
|
||||
|
||||
// true -> true
|
||||
defaultPreferences.set(prefName, true);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: true });
|
||||
await ext.awaitMessage("done");
|
||||
ext.sendMessage("clear", {});
|
||||
result = await ext.awaitMessage("done");
|
||||
Assert.strictEqual(result, true);
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), true);
|
||||
await ext.unload();
|
||||
});
|
||||
|
||||
add_task(async function shutdown() {
|
||||
// no set()
|
||||
defaultPreferences.set(prefName, false);
|
||||
let ext = await loadExtension(background);
|
||||
await ext.unload();
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), false);
|
||||
|
||||
// false -> true
|
||||
defaultPreferences.set(prefName, false);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: true });
|
||||
await ext.awaitMessage("done");
|
||||
await ext.unload();
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), false);
|
||||
|
||||
// true -> false
|
||||
defaultPreferences.set(prefName, true);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: false });
|
||||
await ext.awaitMessage("done");
|
||||
await ext.unload();
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), true);
|
||||
|
||||
// false -> false
|
||||
defaultPreferences.set(prefName, false);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: false });
|
||||
await ext.awaitMessage("done");
|
||||
await ext.unload();
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), false);
|
||||
|
||||
// true -> true
|
||||
defaultPreferences.set(prefName, true);
|
||||
ext = await loadExtension(background);
|
||||
ext.sendMessage("set", { value: true });
|
||||
await ext.awaitMessage("done");
|
||||
await ext.unload();
|
||||
Assert.strictEqual(defaultPreferences.get(prefName), true);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,16 +2,6 @@
|
|||
{
|
||||
"namespace": "experiments.urlbar",
|
||||
"description": "APIs supporting urlbar experiments",
|
||||
"properties": {
|
||||
"engagementTelemetry": {
|
||||
"$ref": "types.Setting",
|
||||
"description": "Enables or disables the engagement telemetry for the current browser session."
|
||||
},
|
||||
"openViewOnFocus": {
|
||||
"$ref": "types.Setting",
|
||||
"description": "Enables or disables the open-view-on-focus mode for the current browser session."
|
||||
}
|
||||
},
|
||||
"functions": [
|
||||
{
|
||||
"name": "isBrowserShowingNotification",
|
||||
|
|
|
@ -137,19 +137,33 @@
|
|||
.urlbarView:not(.megabar) .urlbarView-row,
|
||||
.urlbarView.megabar .urlbarView-row-inner {
|
||||
border-radius: 2px;
|
||||
padding-block: 6px;
|
||||
padding-inline-start: calc(@urlbarViewItemPaddingStart@);
|
||||
}
|
||||
|
||||
.urlbarView:not(.megabar) .urlbarView-row:not([type=tip]),
|
||||
.urlbarView.megabar .urlbarView-row-inner {
|
||||
padding-block: 6px;
|
||||
}
|
||||
|
||||
.urlbarView:not(.megabar) .urlbarView-row {
|
||||
padding-inline-start: calc(var(--item-padding-start, calc(5px + @urlbarViewFaviconOffset@)) - @urlbarViewFaviconOffset@);
|
||||
padding-inline-end: var(--item-padding-end, 5px);
|
||||
}
|
||||
|
||||
:root[uidensity=touch] .urlbarView-row {
|
||||
:root[uidensity=touch] .urlbarView-row:not([type=tip]) {
|
||||
padding-block: 11px;
|
||||
}
|
||||
|
||||
.urlbarView:not(.megabar) .urlbarView-row[type=tip] {
|
||||
padding-block: 24px;
|
||||
}
|
||||
|
||||
.urlbarView.megabar .urlbarView-row[type=tip] {
|
||||
padding-block: 18px;
|
||||
/* Compensating for the 4px focus ring on tip buttons. */
|
||||
padding-inline-end: calc(@urlbarViewItemPaddingStart@ + 4px);
|
||||
}
|
||||
|
||||
.urlbarView-row-inner {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
@ -251,11 +265,6 @@
|
|||
background-image: url(chrome://browser/skin/tab.svg);
|
||||
}
|
||||
|
||||
.urlbarView-row[type=tip] {
|
||||
padding-block: 24px;
|
||||
padding-inline-end: @urlbarViewPaddingInline@;
|
||||
}
|
||||
|
||||
.urlbarView-row[type=tip]:not(:last-child) {
|
||||
border-bottom: 1px solid var(--panel-separator-color);
|
||||
margin-bottom: 4px;
|
||||
|
|
|
@ -34,6 +34,12 @@ def main(argv):
|
|||
|
||||
sandbox = ConfigureSandbox(config, os.environ, argv)
|
||||
|
||||
clobber_file = 'CLOBBER'
|
||||
if not os.path.exists(clobber_file):
|
||||
# Simply touch the file.
|
||||
with open(clobber_file, 'a'):
|
||||
pass
|
||||
|
||||
if os.environ.get('MOZ_CONFIGURE_TRACE'):
|
||||
sandbox._logger.setLevel(TRACE)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ add_task(async function() {
|
|||
const uri = Services.io.newFileURI(dir);
|
||||
const tab = await addJsonViewTab(uri.spec);
|
||||
|
||||
// perform sanity checks for URI and pricnipals in loadInfo
|
||||
// perform sanity checks for URI and principals in loadInfo
|
||||
await ContentTask.spawn(tab.linkedBrowser, {TEST_JSON_FILE}, async function ({TEST_JSON_FILE}) { // eslint-disable-line
|
||||
const channel = content.docShell.currentDocumentChannel;
|
||||
const channelURI = channel.URI.spec;
|
||||
|
|
|
@ -455,7 +455,11 @@ async function getBackgroundColor({ rawNode: node, walker }) {
|
|||
// - not element node
|
||||
// - more than one child
|
||||
// Avoid calculating bounds and creating doc walker by returning early.
|
||||
if (node.nodeType != Node.ELEMENT_NODE || node.children.length > 0) {
|
||||
if (
|
||||
node.nodeType != Node.ELEMENT_NODE ||
|
||||
node.childNodes.length > 1 ||
|
||||
!node.firstChild
|
||||
) {
|
||||
return {
|
||||
value: colorUtils.colorToRGBA(
|
||||
getClosestBackgroundColor(node),
|
||||
|
|
|
@ -118,6 +118,7 @@ skip-if = true # Bug 1593562
|
|||
[browser_inspector-search.js]
|
||||
[browser_inspector-shadow.js]
|
||||
[browser_inspector-traversal.js]
|
||||
[browser_inspector-utils.js]
|
||||
[browser_layout_getGrids.js]
|
||||
[browser_layout_simple.js]
|
||||
[browser_markers-cycle-collection.js]
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from inspector-helpers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/server/tests/browser/inspector-helpers.js",
|
||||
this
|
||||
);
|
||||
|
||||
const COLOR_WHITE = [255, 255, 255, 1];
|
||||
|
||||
add_task(async function loadNewChild() {
|
||||
const { walker } = await initInspectorFront(
|
||||
`data:text/html,<style>body{color:red;background-color:white;}body::before{content:"test";}</style>`
|
||||
);
|
||||
|
||||
const body = await walker.querySelector(walker.rootNode, "body");
|
||||
const color = await body.getBackgroundColor();
|
||||
Assert.deepEqual(
|
||||
color.value,
|
||||
COLOR_WHITE,
|
||||
"Background color is calculated correctly for an element with a pseudo child."
|
||||
);
|
||||
});
|
|
@ -483,6 +483,10 @@ bool WebGLContext::CreateAndInitGL(
|
|||
flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
|
||||
}
|
||||
|
||||
if (StaticPrefs::webgl_cgl_multithreaded()) {
|
||||
flags |= gl::CreateContextFlags::PREFER_MULTITHREADED;
|
||||
}
|
||||
|
||||
if (IsWebGL2()) {
|
||||
flags |= gl::CreateContextFlags::PREFER_ES3;
|
||||
} else {
|
||||
|
|
|
@ -52,8 +52,6 @@ class GLContextCGL : public GLContext {
|
|||
|
||||
virtual GLenum GetPreferredARGB32Format() const override;
|
||||
|
||||
virtual bool IsDoubleBuffered() const override;
|
||||
|
||||
virtual bool SwapBuffers() override;
|
||||
|
||||
virtual void GetWSIInfo(nsCString* const out) const override;
|
||||
|
|
|
@ -21,10 +21,6 @@
|
|||
|
||||
#include <OpenGL/OpenGL.h>
|
||||
|
||||
// When running inside a VM, creating an accelerated OpenGL context usually
|
||||
// fails. Uncomment this line to emulate that behavior.
|
||||
// #define EMULATE_VM
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
|
@ -45,25 +41,14 @@ class CGLLibrary {
|
|||
}
|
||||
}
|
||||
|
||||
const char* db = PR_GetEnv("MOZ_CGL_DB");
|
||||
if (db) {
|
||||
mUseDoubleBufferedWindows = *db != '0';
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UseDoubleBufferedWindows() const {
|
||||
MOZ_ASSERT(mInitialized);
|
||||
return mUseDoubleBufferedWindows;
|
||||
}
|
||||
|
||||
const auto& Library() const { return mOGLLibrary; }
|
||||
|
||||
private:
|
||||
bool mInitialized = false;
|
||||
bool mUseDoubleBufferedWindows = true;
|
||||
PRLibrary* mOGLLibrary = nullptr;
|
||||
};
|
||||
|
||||
|
@ -111,8 +96,6 @@ bool GLContextCGL::IsCurrentImpl() const { return [NSOpenGLContext currentContex
|
|||
|
||||
GLenum GLContextCGL::GetPreferredARGB32Format() const { return LOCAL_GL_BGRA; }
|
||||
|
||||
bool GLContextCGL::IsDoubleBuffered() const { return sCGLLibrary.UseDoubleBufferedWindows(); }
|
||||
|
||||
bool GLContextCGL::SwapBuffers() {
|
||||
AUTO_PROFILER_LABEL("GLContextCGL::SwapBuffers", GRAPHICS);
|
||||
|
||||
|
@ -135,28 +118,6 @@ already_AddRefed<GLContext> GLContextProviderCGL::CreateWrappingExisting(void*,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = {
|
||||
NSOpenGLPFAAllowOfflineRenderers, 0};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered_accel[] = {
|
||||
NSOpenGLPFAAccelerated, NSOpenGLPFAAllowOfflineRenderers, 0};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered[] = {
|
||||
NSOpenGLPFAAllowOfflineRenderers, NSOpenGLPFADoubleBuffer, 0};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel[] = {
|
||||
NSOpenGLPFAAccelerated, NSOpenGLPFAAllowOfflineRenderers, NSOpenGLPFADoubleBuffer, 0};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel_webrender[] = {
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFAAllowOfflineRenderers,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAOpenGLProfile,
|
||||
NSOpenGLProfileVersion3_2Core,
|
||||
NSOpenGLPFADepthSize,
|
||||
24,
|
||||
0};
|
||||
|
||||
static NSOpenGLContext* CreateWithFormat(const NSOpenGLPixelFormatAttribute* attribs) {
|
||||
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
|
||||
if (!format) {
|
||||
|
@ -173,55 +134,15 @@ static NSOpenGLContext* CreateWithFormat(const NSOpenGLPixelFormatAttribute* att
|
|||
|
||||
already_AddRefed<GLContext> GLContextProviderCGL::CreateForCompositorWidget(
|
||||
CompositorWidget* aCompositorWidget, bool aWebRender, bool aForceAccelerated) {
|
||||
if (!aCompositorWidget) {
|
||||
MOZ_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
return CreateForWindow(aCompositorWidget->RealWidget(), aWebRender, aForceAccelerated);
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderCGL::CreateForWindow(nsIWidget* aWidget,
|
||||
bool aWebRender,
|
||||
bool aForceAccelerated) {
|
||||
if (!sCGLLibrary.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef EMULATE_VM
|
||||
CreateContextFlags flags = CreateContextFlags::ALLOW_OFFLINE_RENDERER;
|
||||
if (aForceAccelerated) {
|
||||
return nullptr;
|
||||
flags |= CreateContextFlags::FORCE_ENABLE_HARDWARE;
|
||||
}
|
||||
#endif
|
||||
|
||||
const NSOpenGLPixelFormatAttribute* attribs;
|
||||
SurfaceCaps caps = SurfaceCaps::ForRGBA();
|
||||
if (sCGLLibrary.UseDoubleBufferedWindows()) {
|
||||
if (aWebRender) {
|
||||
MOZ_RELEASE_ASSERT(aForceAccelerated,
|
||||
"At the moment, aForceAccelerated is always true if aWebRender is true. "
|
||||
"If this changes, please update the code here.");
|
||||
attribs = kAttribs_doubleBuffered_accel_webrender;
|
||||
caps.depth = true;
|
||||
} else {
|
||||
attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel : kAttribs_doubleBuffered;
|
||||
}
|
||||
} else {
|
||||
attribs = aForceAccelerated ? kAttribs_singleBuffered_accel : kAttribs_singleBuffered;
|
||||
if (!aWebRender) {
|
||||
flags |= CreateContextFlags::REQUIRE_COMPAT_PROFILE;
|
||||
}
|
||||
NSOpenGLContext* context = CreateWithFormat(attribs);
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GLContextCGL> glContext = new GLContextCGL(CreateContextFlags::NONE, caps, context, false);
|
||||
|
||||
if (!glContext->Init()) {
|
||||
glContext = nullptr;
|
||||
[context release];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return glContext.forget();
|
||||
nsCString failureUnused;
|
||||
return CreateHeadless(flags, &failureUnused);
|
||||
}
|
||||
|
||||
static already_AddRefed<GLContextCGL> CreateOffscreenFBOContext(CreateContextFlags flags) {
|
||||
|
@ -243,7 +164,7 @@ static already_AddRefed<GLContextCGL> CreateOffscreenFBOContext(CreateContextFla
|
|||
attribs.push_back(NSOpenGLPFAAllowOfflineRenderers);
|
||||
}
|
||||
|
||||
if (StaticPrefs::gl_require_hardware()) {
|
||||
if (flags & CreateContextFlags::FORCE_ENABLE_HARDWARE) {
|
||||
attribs.push_back(NSOpenGLPFAAccelerated);
|
||||
}
|
||||
|
||||
|
@ -267,7 +188,7 @@ static already_AddRefed<GLContextCGL> CreateOffscreenFBOContext(CreateContextFla
|
|||
|
||||
RefPtr<GLContextCGL> glContext = new GLContextCGL(flags, SurfaceCaps::Any(), context, true);
|
||||
|
||||
if (StaticPrefs::gl_multithreaded()) {
|
||||
if (flags & CreateContextFlags::PREFER_MULTITHREADED) {
|
||||
CGLEnable(glContext->GetCGLContext(), kCGLCEMPEngine);
|
||||
}
|
||||
return glContext.forget();
|
||||
|
|
|
@ -170,19 +170,14 @@ already_AddRefed<GLContext> GLContextProviderEAGL::CreateForCompositorWidget(
|
|||
MOZ_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
return CreateForWindow(aCompositorWidget->RealWidget(), aWebRender, aForceAccelerated);
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget,
|
||||
bool aWebRender,
|
||||
bool aForceAccelerated) {
|
||||
RefPtr<GLContext> glContext =
|
||||
CreateEAGLContext(CreateContextFlags::NONE, false, GetGlobalContextEAGL());
|
||||
if (!glContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) {
|
||||
if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aCompositorWidget->RealWidget())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -904,13 +904,6 @@ already_AddRefed<GLContext> GLContextProviderEGL::CreateForCompositorWidget(
|
|||
return GLContextEGLFactory::Create(window, aWebRender);
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderEGL::CreateForWindow(
|
||||
nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated) {
|
||||
MOZ_ASSERT(aWidget);
|
||||
return GLContextEGLFactory::Create(
|
||||
GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget), aWebRender);
|
||||
}
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
EGLSurface GLContextEGL::CreateCompatibleSurface(void* aWindow) {
|
||||
if (mConfig == EGL_NO_CONFIG) {
|
||||
|
|
|
@ -759,15 +759,6 @@ already_AddRefed<GLContext> GLContextProviderGLX::CreateForCompositorWidget(
|
|||
aWebRender, aForceAccelerated);
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderGLX::CreateForWindow(
|
||||
nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated) {
|
||||
Display* display =
|
||||
(Display*)aWidget->GetNativeData(NS_NATIVE_COMPOSITOR_DISPLAY);
|
||||
Window window = GET_NATIVE_WINDOW(aWidget);
|
||||
|
||||
return CreateForWidget(display, window, aWebRender, aForceAccelerated);
|
||||
}
|
||||
|
||||
static bool ChooseConfig(GLXLibrary* glx, Display* display, int screen,
|
||||
const SurfaceCaps& minCaps,
|
||||
ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
|
||||
|
|
|
@ -43,33 +43,6 @@ class GL_CONTEXT_PROVIDER_NAME {
|
|||
mozilla::widget::CompositorWidget* aCompositorWidget, bool aWebRender,
|
||||
bool aForceAccelerated);
|
||||
|
||||
/**
|
||||
* Create a context that renders to the surface of the widget that is
|
||||
* passed in. The context is always created with an RGB pixel format,
|
||||
* with no alpha, depth or stencil. If any of those features are needed,
|
||||
* either use a framebuffer, or use CreateOffscreen.
|
||||
*
|
||||
* This context will attempt to share resources with all other window
|
||||
* contexts. As such, it's critical that resources allocated that are not
|
||||
* needed by other contexts be deleted before the context is destroyed.
|
||||
*
|
||||
* The GetSharedContext() method will return non-null if sharing
|
||||
* was successful.
|
||||
*
|
||||
* Note: a context created for a widget /must not/ hold a strong
|
||||
* reference to the widget; otherwise a cycle can be created through
|
||||
* a GL layer manager.
|
||||
*
|
||||
* @param aWidget Widget whose surface to create a context for
|
||||
* @param aWebRender If the compositor is a WebRender compositor
|
||||
* @param aForceAccelerated true if only accelerated contexts are allowed
|
||||
*
|
||||
* @return Context to use for the window
|
||||
*/
|
||||
static already_AddRefed<GLContext> CreateForWindow(nsIWidget* aWidget,
|
||||
bool aWebRender,
|
||||
bool aForceAccelerated);
|
||||
|
||||
/**
|
||||
* Create a context for offscreen rendering. The target of this
|
||||
* context should be treated as opaque -- it might be a FBO, or a
|
||||
|
|
|
@ -16,11 +16,6 @@ already_AddRefed<GLContext> GLContextProviderNull::CreateForCompositorWidget(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderNull::CreateForWindow(
|
||||
nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderNull::CreateWrappingExisting(
|
||||
void*, void*) {
|
||||
return nullptr;
|
||||
|
|
|
@ -463,13 +463,6 @@ already_AddRefed<GLContext> GLContextProviderWGL::CreateForCompositorWidget(
|
|||
.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderWGL::CreateForWindow(
|
||||
nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated) {
|
||||
return CreateForWidget((HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW),
|
||||
aWebRender, aForceAccelerated)
|
||||
.forget();
|
||||
}
|
||||
|
||||
/*static*/
|
||||
already_AddRefed<GLContext> GLContextProviderWGL::CreateHeadless(
|
||||
const CreateContextFlags flags, nsACString* const out_failureId) {
|
||||
|
|
|
@ -40,17 +40,6 @@ already_AddRefed<GLContext> GLContextProviderWayland::CreateForCompositorWidget(
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext> GLContextProviderWayland::CreateForWindow(
|
||||
nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated) {
|
||||
if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
|
||||
return sGLContextProviderGLX.CreateForWindow(aWidget, aWebRender,
|
||||
aForceAccelerated);
|
||||
} else {
|
||||
return sGLContextProviderEGL.CreateForWindow(aWidget, aWebRender,
|
||||
aForceAccelerated);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/
|
||||
already_AddRefed<GLContext> GLContextProviderWayland::CreateHeadless(
|
||||
CreateContextFlags flags, nsACString* const out_failureId) {
|
||||
|
|
|
@ -44,6 +44,7 @@ enum class CreateContextFlags : uint16_t {
|
|||
HIGH_POWER = 1 << 6,
|
||||
PROVOKING_VERTEX_DONT_CARE = 1 << 7,
|
||||
PREFER_EXACT_VERSION = 1 << 8,
|
||||
PREFER_MULTITHREADED = 1 << 9,
|
||||
};
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CreateContextFlags)
|
||||
|
||||
|
|
|
@ -589,15 +589,9 @@ void CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget) {
|
|||
// Matrix to transform (0, 0, aWidth, aHeight) to viewport space (-1.0, 1.0,
|
||||
// 2, 2) and flip the contents.
|
||||
Matrix viewMatrix;
|
||||
if (mGLContext->IsOffscreen() && !gIsGtest) {
|
||||
// In case of rendering via GL Offscreen context, disable Y-Flipping
|
||||
viewMatrix.PreTranslate(-1.0, -1.0);
|
||||
viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
|
||||
} else {
|
||||
viewMatrix.PreTranslate(-1.0, 1.0);
|
||||
viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
|
||||
viewMatrix.PreScale(1.0f, -1.0f);
|
||||
}
|
||||
viewMatrix.PreTranslate(-1.0, 1.0);
|
||||
viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
|
||||
viewMatrix.PreScale(1.0f, -1.0f);
|
||||
|
||||
MOZ_ASSERT(mCurrentRenderTarget, "No destination");
|
||||
// If we're drawing directly to the window then we want to offset
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "GLContext.h"
|
||||
#include "GLContextProvider.h"
|
||||
#include "mozilla/StaticPrefs_gfx.h"
|
||||
#include "mozilla/webrender/RenderThread.h"
|
||||
#include "mozilla/widget/CompositorWidget.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -17,9 +18,11 @@ namespace wr {
|
|||
/* static */
|
||||
UniquePtr<RenderCompositor> RenderCompositorOGL::Create(
|
||||
RefPtr<widget::CompositorWidget>&& aWidget) {
|
||||
RefPtr<gl::GLContext> gl;
|
||||
gl = gl::GLContextProvider::CreateForCompositorWidget(
|
||||
aWidget, /* aWebRender */ true, /* aForceAccelerated */ true);
|
||||
RefPtr<gl::GLContext> gl = RenderThread::Get()->SharedGL();
|
||||
if (!gl) {
|
||||
gl = gl::GLContextProvider::CreateForCompositorWidget(
|
||||
aWidget, /* aWebRender */ true, /* aForceAccelerated */ true);
|
||||
}
|
||||
if (!gl || !gl->MakeCurrent()) {
|
||||
gfxCriticalNote << "Failed GL context creation for WebRender: "
|
||||
<< gfx::hexa(gl.get());
|
||||
|
|
|
@ -952,6 +952,16 @@ static already_AddRefed<gl::GLContext> CreateGLContextEGL() {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
static already_AddRefed<gl::GLContext> CreateGLContextCGL() {
|
||||
nsCString failureUnused;
|
||||
return gl::GLContextProvider::CreateHeadless(
|
||||
gl::CreateContextFlags::ALLOW_OFFLINE_RENDERER |
|
||||
gl::CreateContextFlags::FORCE_ENABLE_HARDWARE,
|
||||
&failureUnused);
|
||||
}
|
||||
#endif
|
||||
|
||||
static already_AddRefed<gl::GLContext> CreateGLContext() {
|
||||
#ifdef XP_WIN
|
||||
if (gfx::gfxVars::UseWebRenderANGLE()) {
|
||||
|
@ -966,8 +976,11 @@ static already_AddRefed<gl::GLContext> CreateGLContext() {
|
|||
return CreateGLContextEGL();
|
||||
}
|
||||
#endif
|
||||
// We currently only support a shared GLContext
|
||||
// with ANGLE.
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
return CreateGLContextCGL();
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="left">
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="start">
|
||||
<menulist style="color: transparent">
|
||||
<menupopup>
|
||||
<menuitem value="1" label="short" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="left">
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="start">
|
||||
<menulist style="color: transparent">
|
||||
<menupopup>
|
||||
<menuitem value="1" label="long item" />
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
]]>
|
||||
</style>
|
||||
<stack>
|
||||
<vbox align="left">
|
||||
<vbox align="start">
|
||||
<menulist>
|
||||
<menupopup>
|
||||
<menuitem value="1" label="long item" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<window class="reftest-wait"
|
||||
align="left"
|
||||
align="start"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<html:style type="text/css">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<window class="reftest-wait"
|
||||
align="left"
|
||||
align="start"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<html:style type="text/css">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="left">
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="start">
|
||||
<hbox style="font: 40px monospace;" align="baseline">
|
||||
<label value="lowercase"/>
|
||||
<label value="SMALLCAPS" style="font-size: 32px;"/>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="left">
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="start">
|
||||
<hbox style="font: 40px monospace;">
|
||||
<label value="lowercase" style="font-variant: normal;"/>
|
||||
<label value="smallcaps" style="font-variant: small-caps;"/>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!-- bug 749658 - The cropped label should *not* cause a scroll bar to appear.
|
||||
If it does, the scrollbars in the testcase and reference will differ
|
||||
because the total scroll-overflow extent will be different. -->
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="left">
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="start">
|
||||
<hbox width="150px" style="overflow: scroll">
|
||||
<label value="A very long label for a small item"
|
||||
width="120px" crop="right"/>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!-- bug 749658 - The cropped label should *not* cause a scroll bar to appear.
|
||||
If it does, the scrollbars in the testcase and reference will differ
|
||||
because the total scroll-overflow extent will be different. -->
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="left">
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="start">
|
||||
<hbox width="150px" style="overflow: scroll">
|
||||
<label value="A very long label for a small item that should not trigger a scroll bar"
|
||||
width="120px" crop="right"/>
|
||||
|
|
|
@ -231,44 +231,10 @@ void nsBoxFrame::CacheAttributes() {
|
|||
}
|
||||
|
||||
bool nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign) {
|
||||
if (!GetContent() || !GetContent()->IsElement()) return false;
|
||||
if (!GetContent()) return false;
|
||||
|
||||
Element* element = GetContent()->AsElement();
|
||||
// XXXdwh Everything inside this if statement is deprecated code.
|
||||
static Element::AttrValuesArray alignStrings[] = {nsGkAtoms::left,
|
||||
nsGkAtoms::right, nullptr};
|
||||
static const Halignment alignValues[] = {hAlign_Left, hAlign_Right};
|
||||
int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
|
||||
alignStrings, eCaseMatters);
|
||||
if (index >= 0) {
|
||||
aHalign = alignValues[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now that the deprecated stuff is out of the way, we move on to check the
|
||||
// appropriate attribute. For horizontal boxes, we are checking the PACK
|
||||
// attribute. For vertical boxes we are checking the ALIGN attribute.
|
||||
nsAtom* attrName = IsXULHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align;
|
||||
static Element::AttrValuesArray strings[] = {
|
||||
nsGkAtoms::_empty, nsGkAtoms::start, nsGkAtoms::center, nsGkAtoms::end,
|
||||
nullptr};
|
||||
static const Halignment values[] = {hAlign_Left /*not used*/, hAlign_Left,
|
||||
hAlign_Center, hAlign_Right};
|
||||
index = element->FindAttrValueIn(kNameSpaceID_None, attrName, strings,
|
||||
eCaseMatters);
|
||||
|
||||
if (index == Element::ATTR_VALUE_NO_MATCH) {
|
||||
// The attr was present but had a nonsensical value. Revert to the default.
|
||||
return false;
|
||||
}
|
||||
if (index > 0) {
|
||||
aHalign = values[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now that we've checked for the attribute it's time to check CSS. For
|
||||
// horizontal boxes we're checking PACK. For vertical boxes we are checking
|
||||
// ALIGN.
|
||||
// For horizontal boxes we're checking PACK. For vertical boxes we are
|
||||
// checking ALIGN.
|
||||
const nsStyleXUL* boxInfo = StyleXUL();
|
||||
if (IsXULHorizontal()) {
|
||||
switch (boxInfo->mBoxPack) {
|
||||
|
@ -304,46 +270,9 @@ bool nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign) {
|
|||
}
|
||||
|
||||
bool nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign) {
|
||||
if (!GetContent() || !GetContent()->IsElement()) return false;
|
||||
|
||||
Element* element = GetContent()->AsElement();
|
||||
|
||||
static Element::AttrValuesArray valignStrings[] = {
|
||||
nsGkAtoms::top, nsGkAtoms::baseline, nsGkAtoms::middle, nsGkAtoms::bottom,
|
||||
nullptr};
|
||||
static const Valignment valignValues[] = {vAlign_Top, vAlign_BaseLine,
|
||||
vAlign_Middle, vAlign_Bottom};
|
||||
int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign,
|
||||
valignStrings, eCaseMatters);
|
||||
if (index >= 0) {
|
||||
aValign = valignValues[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now that the deprecated stuff is out of the way, we move on to check the
|
||||
// appropriate attribute. For horizontal boxes, we are checking the ALIGN
|
||||
// attribute. For vertical boxes we are checking the PACK attribute.
|
||||
nsAtom* attrName = IsXULHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack;
|
||||
static Element::AttrValuesArray strings[] = {
|
||||
nsGkAtoms::_empty, nsGkAtoms::start, nsGkAtoms::center,
|
||||
nsGkAtoms::baseline, nsGkAtoms::end, nullptr};
|
||||
static const Valignment values[] = {vAlign_Top /*not used*/, vAlign_Top,
|
||||
vAlign_Middle, vAlign_BaseLine,
|
||||
vAlign_Bottom};
|
||||
index = element->FindAttrValueIn(kNameSpaceID_None, attrName, strings,
|
||||
eCaseMatters);
|
||||
if (index == Element::ATTR_VALUE_NO_MATCH) {
|
||||
// The attr was present but had a nonsensical value. Revert to the default.
|
||||
return false;
|
||||
}
|
||||
if (index > 0) {
|
||||
aValign = values[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now that we've checked for the attribute it's time to check CSS. For
|
||||
// horizontal boxes we're checking ALIGN. For vertical boxes we are checking
|
||||
// PACK.
|
||||
if (!GetContent()) return false;
|
||||
// For horizontal boxes we're checking ALIGN. For vertical boxes we are
|
||||
// checking PACK.
|
||||
const nsStyleXUL* boxInfo = StyleXUL();
|
||||
if (IsXULHorizontal()) {
|
||||
switch (boxInfo->mBoxAlign) {
|
||||
|
@ -385,25 +314,12 @@ void nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal) {
|
|||
// see if we are a vertical or horizontal box.
|
||||
if (!GetContent()) return;
|
||||
|
||||
// Check the style system first.
|
||||
const nsStyleXUL* boxInfo = StyleXUL();
|
||||
if (boxInfo->mBoxOrient == StyleBoxOrient::Horizontal) {
|
||||
aIsHorizontal = true;
|
||||
} else {
|
||||
aIsHorizontal = false;
|
||||
}
|
||||
|
||||
// Now see if we have an attribute. The attribute overrides
|
||||
// the style system value.
|
||||
if (!GetContent()->IsElement()) return;
|
||||
|
||||
static Element::AttrValuesArray strings[] = {nsGkAtoms::vertical,
|
||||
nsGkAtoms::horizontal, nullptr};
|
||||
int32_t index = GetContent()->AsElement()->FindAttrValueIn(
|
||||
kNameSpaceID_None, nsGkAtoms::orient, strings, eCaseMatters);
|
||||
if (index >= 0) {
|
||||
aIsHorizontal = index == 1;
|
||||
}
|
||||
}
|
||||
|
||||
void nsBoxFrame::GetInitialDirection(bool& aIsNormal) {
|
||||
|
@ -461,18 +377,6 @@ bool nsBoxFrame::GetInitialEqualSize(bool& aEqualSize) {
|
|||
bool nsBoxFrame::GetInitialAutoStretch(bool& aStretch) {
|
||||
if (!GetContent()) return false;
|
||||
|
||||
// Check the align attribute.
|
||||
if (GetContent()->IsElement()) {
|
||||
static Element::AttrValuesArray strings[] = {nsGkAtoms::_empty,
|
||||
nsGkAtoms::stretch, nullptr};
|
||||
int32_t index = GetContent()->AsElement()->FindAttrValueIn(
|
||||
kNameSpaceID_None, nsGkAtoms::align, strings, eCaseMatters);
|
||||
if (index != Element::ATTR_MISSING && index != 0) {
|
||||
aStretch = index == 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the CSS box-align property.
|
||||
const nsStyleXUL* boxInfo = StyleXUL();
|
||||
aStretch = (boxInfo->mBoxAlign == StyleBoxAlign::Stretch);
|
||||
|
|
|
@ -381,10 +381,6 @@ nsresult nsScrollbarFrame::CreateAnonymousContent(
|
|||
nodeInfoManager->GetNodeInfo(nsGkAtoms::thumb, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient, false);
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::align,
|
||||
NS_LITERAL_STRING("center"), false);
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::pack,
|
||||
NS_LITERAL_STRING("center"), false);
|
||||
mSlider->AppendChildTo(mThumb, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -520,8 +520,10 @@ function startup() {
|
|||
},
|
||||
{
|
||||
name: "GeckoViewContentBlocking",
|
||||
onEnable: {
|
||||
onInit: {
|
||||
resource: "resource://gre/modules/GeckoViewContentBlocking.jsm",
|
||||
},
|
||||
onEnable: {
|
||||
frameScript:
|
||||
"chrome://geckoview/content/GeckoViewContentBlockingChild.js",
|
||||
},
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.mozilla.geckoview.ContentBlocking
|
|||
import org.mozilla.geckoview.ContentBlockingController
|
||||
import org.mozilla.geckoview.GeckoSession
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
|
||||
import org.mozilla.geckoview.test.util.Callbacks
|
||||
import org.junit.Assume.assumeThat
|
||||
|
||||
|
@ -146,16 +147,19 @@ class ContentBlockingControllerTest : BaseSessionTest() {
|
|||
sessionRule.runtime.contentBlockingController.clearExceptionList()
|
||||
}
|
||||
|
||||
@Ignore //Bug 1581657 - ignore test to reduce frequent failures
|
||||
@Test
|
||||
fun getLog() {
|
||||
val category = ContentBlocking.AntiTracking.TEST
|
||||
sessionRule.runtime.settings.contentBlocking.setAntiTracking(category)
|
||||
sessionRule.session.settings.useTrackingProtection = true
|
||||
sessionRule.session.loadTestPath(TRACKERS_PATH)
|
||||
|
||||
|
||||
sessionRule.waitForPageStop()
|
||||
|
||||
sessionRule.waitUntilCalled(object : Callbacks.ContentBlockingDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onContentBlocked(session: GeckoSession,
|
||||
event: ContentBlocking.BlockEvent) {
|
||||
}
|
||||
})
|
||||
|
||||
sessionRule.waitForResult(sessionRule.runtime.contentBlockingController.getLog(sessionRule.session).accept {
|
||||
assertThat("Log must not be null", it, Matchers.notNullValue())
|
||||
|
|
|
@ -3713,18 +3713,6 @@
|
|||
value: false
|
||||
mirror: always
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
- name: gl.multithreaded
|
||||
type: RelaxedAtomicBool
|
||||
value: true
|
||||
mirror: always
|
||||
#endif
|
||||
|
||||
- name: gl.require-hardware
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
- name: gl.use-tls-is-current
|
||||
type: RelaxedAtomicInt32
|
||||
value: 0
|
||||
|
@ -7587,7 +7575,6 @@
|
|||
#endif
|
||||
mirror: always
|
||||
|
||||
|
||||
- name: webgl.all-angle-options
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
|
@ -7617,6 +7604,11 @@
|
|||
value: true
|
||||
mirror: always
|
||||
|
||||
- name: webgl.cgl.multithreaded
|
||||
type: RelaxedAtomicBool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
- name: webgl.default-antialias
|
||||
type: RelaxedAtomicBool
|
||||
value: @IS_NOT_ANDROID@
|
||||
|
|
|
@ -2051,7 +2051,7 @@ bool NS_IsSameSiteForeign(nsIChannel* aChannel, nsIURI* aHostURI) {
|
|||
if (loadInfo->GetExternalContentPolicyType() ==
|
||||
nsIContentPolicy::TYPE_DOCUMENT) {
|
||||
// for loads of TYPE_DOCUMENT we query the hostURI from the
|
||||
// triggeringPricnipal which returns the URI of the document that caused the
|
||||
// triggeringPrincipal which returns the URI of the document that caused the
|
||||
// navigation.
|
||||
loadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(uri));
|
||||
} else {
|
||||
|
|
|
@ -166,20 +166,6 @@ class Clobberer(object):
|
|||
paths = self.collect_subdirs(cargo_path, {'incremental', })
|
||||
self.delete_dirs(cargo_path, paths)
|
||||
|
||||
def ensure_objdir_state(self):
|
||||
"""Ensure the CLOBBER file in the objdir exists.
|
||||
|
||||
This is called as part of the build to ensure the clobber information
|
||||
is configured properly for the objdir.
|
||||
"""
|
||||
if not os.path.exists(self.topobjdir):
|
||||
os.makedirs(self.topobjdir)
|
||||
|
||||
if not os.path.exists(self.obj_clobber):
|
||||
# Simply touch the file.
|
||||
with open(self.obj_clobber, 'a'):
|
||||
pass
|
||||
|
||||
def maybe_do_clobber(self, cwd, allow_auto=False, fh=sys.stderr):
|
||||
"""Perform a clobber if it is required. Maybe.
|
||||
|
||||
|
@ -199,7 +185,6 @@ class Clobberer(object):
|
|||
|
||||
if not self.clobber_needed():
|
||||
print('Clobber not needed.', file=fh)
|
||||
self.ensure_objdir_state()
|
||||
return False, False, None
|
||||
|
||||
# So a clobber is needed. We only perform a clobber if we are
|
||||
|
@ -223,7 +208,6 @@ class Clobberer(object):
|
|||
print('Automatically clobbering %s' % objdir, file=fh)
|
||||
try:
|
||||
self.remove_objdir(False)
|
||||
self.ensure_objdir_state()
|
||||
print('Successfully completed auto clobber.', file=fh)
|
||||
return True, True, None
|
||||
except (IOError) as error:
|
||||
|
|
|
@ -64,14 +64,13 @@ class TestClobberer(unittest.TestCase):
|
|||
c = Clobberer(self.get_topsrcdir(), tmp)
|
||||
self.assertFalse(c.clobber_needed())
|
||||
|
||||
# Side-effect is topobjdir is created with CLOBBER file touched.
|
||||
required, performed, reason = c.maybe_do_clobber(os.getcwd(), True)
|
||||
self.assertFalse(required)
|
||||
self.assertFalse(performed)
|
||||
self.assertIsNone(reason)
|
||||
|
||||
self.assertTrue(os.path.isdir(tmp))
|
||||
self.assertTrue(os.path.exists(os.path.join(tmp, 'CLOBBER')))
|
||||
self.assertFalse(os.path.isdir(tmp))
|
||||
self.assertFalse(os.path.exists(os.path.join(tmp, 'CLOBBER')))
|
||||
|
||||
def test_objdir_no_clobber_file(self):
|
||||
"""If CLOBBER does not exist in topobjdir, treat as empty."""
|
||||
|
@ -84,7 +83,7 @@ class TestClobberer(unittest.TestCase):
|
|||
self.assertFalse(performed)
|
||||
self.assertIsNone(reason)
|
||||
|
||||
self.assertTrue(os.path.exists(os.path.join(c.topobjdir, 'CLOBBER')))
|
||||
self.assertFalse(os.path.exists(os.path.join(c.topobjdir, 'CLOBBER')))
|
||||
|
||||
def test_objdir_clobber_newer(self):
|
||||
"""If CLOBBER in topobjdir is newer, do nothing."""
|
||||
|
@ -121,9 +120,7 @@ class TestClobberer(unittest.TestCase):
|
|||
self.assertTrue(performed)
|
||||
|
||||
self.assertFalse(os.path.exists(dummy_path))
|
||||
self.assertTrue(os.path.exists(c.obj_clobber))
|
||||
self.assertGreaterEqual(os.path.getmtime(c.obj_clobber),
|
||||
os.path.getmtime(c.src_clobber))
|
||||
self.assertFalse(os.path.exists(c.obj_clobber))
|
||||
|
||||
def test_objdir_is_srcdir(self):
|
||||
"""If topobjdir is the topsrcdir, refuse to clobber."""
|
||||
|
|
|
@ -23,12 +23,15 @@ const {
|
|||
class Page extends ContentProcessDomain {
|
||||
constructor(session) {
|
||||
super(session);
|
||||
|
||||
this.enabled = false;
|
||||
this.lifecycleEnabled = false;
|
||||
|
||||
this.onFrameNavigated = this.onFrameNavigated.bind(this);
|
||||
}
|
||||
|
||||
destructor() {
|
||||
this.setLifecycleEventsEnabled({ enabled: false });
|
||||
this.disable();
|
||||
|
||||
super.destructor();
|
||||
|
@ -44,6 +47,9 @@ class Page extends ContentProcessDomain {
|
|||
this.chromeEventHandler.addEventListener("DOMContentLoaded", this, {
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
this.chromeEventHandler.addEventListener("pagehide", this, {
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
this.chromeEventHandler.addEventListener("pageshow", this, {
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
|
@ -57,6 +63,9 @@ class Page extends ContentProcessDomain {
|
|||
this.chromeEventHandler.removeEventListener("DOMContentLoaded", this, {
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
this.chromeEventHandler.removeEventListener("pagehide", this, {
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
this.chromeEventHandler.removeEventListener("pageshow", this, {
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
|
@ -115,10 +124,22 @@ class Page extends ContentProcessDomain {
|
|||
};
|
||||
}
|
||||
|
||||
setLifecycleEventsEnabled() {}
|
||||
addScriptToEvaluateOnNewDocument() {}
|
||||
createIsolatedWorld() {}
|
||||
|
||||
/**
|
||||
* Controls whether page will emit lifecycle events.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {boolean} options.enabled
|
||||
* If true, starts emitting lifecycle events.
|
||||
*/
|
||||
setLifecycleEventsEnabled(options) {
|
||||
const { enabled } = options;
|
||||
|
||||
this.lifecycleEnabled = enabled;
|
||||
}
|
||||
|
||||
url() {
|
||||
return this.content.location.href;
|
||||
}
|
||||
|
@ -136,8 +157,16 @@ class Page extends ContentProcessDomain {
|
|||
});
|
||||
}
|
||||
|
||||
emitLifecycleEvent(frameId, loaderId, name, timestamp) {
|
||||
if (this.lifecycleEnabled) {
|
||||
this.emit("Page.lifecycleEvent", { frameId, loaderId, name, timestamp });
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent({ type, target }) {
|
||||
if (target.defaultView != this.content) {
|
||||
const isFrame = target.defaultView != this.content;
|
||||
|
||||
if (isFrame) {
|
||||
// Ignore iframes for now
|
||||
return;
|
||||
}
|
||||
|
@ -149,13 +178,44 @@ class Page extends ContentProcessDomain {
|
|||
switch (type) {
|
||||
case "DOMContentLoaded":
|
||||
this.emit("Page.domContentEventFired", { timestamp });
|
||||
if (!isFrame) {
|
||||
this.emitLifecycleEvent(
|
||||
frameId,
|
||||
/* loaderId */ null,
|
||||
"DOMContentLoaded",
|
||||
timestamp
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "pagehide":
|
||||
// Maybe better to bound to "unload" once we can register for this event
|
||||
this.emit("Page.frameStartedLoading", { frameId });
|
||||
if (!isFrame) {
|
||||
this.emitLifecycleEvent(
|
||||
frameId,
|
||||
/* loaderId */ null,
|
||||
"init",
|
||||
timestamp
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "pageshow":
|
||||
this.emit("Page.loadEventFired", { timestamp });
|
||||
if (!isFrame) {
|
||||
this.emitLifecycleEvent(
|
||||
frameId,
|
||||
/* loaderId */ null,
|
||||
"load",
|
||||
timestamp
|
||||
);
|
||||
}
|
||||
|
||||
// XXX this should most likely be sent differently
|
||||
this.emit("Page.navigatedWithinDocument", { frameId, url });
|
||||
this.emit("Page.frameStoppedLoading", { frameId });
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,3 +178,12 @@ function getContentProperty(prop) {
|
|||
_prop => content[_prop]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new promise, which resolves after ms have been elapsed
|
||||
*/
|
||||
function timeoutPromise(ms) {
|
||||
return new Promise(resolve => {
|
||||
window.setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,5 +14,6 @@ support-files =
|
|||
[browser_javascriptDialog_confirm.js]
|
||||
[browser_javascriptDialog_otherTarget.js]
|
||||
[browser_javascriptDialog_prompt.js]
|
||||
[browser_lifecycleEvent.js]
|
||||
[browser_reload.js]
|
||||
[browser_runtimeEvents.js]
|
||||
|
|
|
@ -42,11 +42,12 @@ add_task(async function(client) {
|
|||
}
|
||||
// Record all Page events that we assert in this test
|
||||
function recordPromises() {
|
||||
recordPromise("frameStoppedLoading", Page.frameStoppedLoading());
|
||||
recordPromise("navigatedWithinDocument", Page.navigatedWithinDocument());
|
||||
recordPromise("frameStartedLoading", Page.frameStartedLoading());
|
||||
recordPromise("frameNavigated", Page.frameNavigated());
|
||||
recordPromise("domContentEventFired", Page.domContentEventFired());
|
||||
recordPromise("loadEventFired", Page.loadEventFired());
|
||||
recordPromise("frameNavigated", Page.frameNavigated());
|
||||
recordPromise("navigatedWithinDocument", Page.navigatedWithinDocument());
|
||||
recordPromise("frameStoppedLoading", Page.frameStoppedLoading());
|
||||
}
|
||||
|
||||
info("Test Page.navigate");
|
||||
|
@ -107,6 +108,7 @@ async function assertNavigationEvents({ url, frameId }) {
|
|||
|
||||
// Assert the order in which they resolved
|
||||
const expectedResolutions = [
|
||||
"frameStartedLoading",
|
||||
"frameNavigated",
|
||||
"domContentEventFired",
|
||||
"loadEventFired",
|
||||
|
@ -120,6 +122,13 @@ async function assertNavigationEvents({ url, frameId }) {
|
|||
);
|
||||
|
||||
// Now assert the data exposed by each of these events
|
||||
const frameStartedLoading = resolutions.get("frameStartedLoading");
|
||||
is(
|
||||
frameStartedLoading.frameId,
|
||||
frameId,
|
||||
"frameStartedLoading frameId is the same one"
|
||||
);
|
||||
|
||||
const frameNavigated = resolutions.get("frameNavigated");
|
||||
ok(
|
||||
!frameNavigated.frame.parentId,
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test the Page lifecycle events
|
||||
|
||||
const DOC = toDataURL("default-test-page");
|
||||
|
||||
add_task(async function noInitialEvents({ Page }) {
|
||||
await Page.enable();
|
||||
info("Page domain has been enabled");
|
||||
|
||||
const promise = recordPromises(Page, ["init", "DOMContentLoaded", "load"]);
|
||||
info("Lifecycle events are not enabled");
|
||||
|
||||
let pageLoaded = Page.loadEventFired();
|
||||
const { frameId } = await Page.navigate({ url: DOC });
|
||||
await pageLoaded;
|
||||
info("A new page has been loaded");
|
||||
|
||||
await assertNavigationLifecycleEvents({ promise, frameId, timeout: 1000 });
|
||||
});
|
||||
|
||||
add_task(async function noEventsAfterDisable({ Page }) {
|
||||
await Page.enable();
|
||||
info("Page domain has been enabled");
|
||||
|
||||
await Page.setLifecycleEventsEnabled({ enabled: true });
|
||||
await Page.setLifecycleEventsEnabled({ enabled: false });
|
||||
const promise = recordPromises(Page, ["init", "DOMContentLoaded", "load"]);
|
||||
info("Lifecycle events are not enabled");
|
||||
|
||||
let pageLoaded = Page.loadEventFired();
|
||||
const { frameId } = await Page.navigate({ url: DOC });
|
||||
await pageLoaded;
|
||||
info("A new page has been loaded");
|
||||
|
||||
await assertNavigationLifecycleEvents({ promise, frameId, timeout: 1000 });
|
||||
});
|
||||
|
||||
add_task(async function navigateEvents({ Page }) {
|
||||
await Page.enable();
|
||||
info("Page domain has been enabled");
|
||||
|
||||
await Page.setLifecycleEventsEnabled({ enabled: true });
|
||||
const promise = recordPromises(Page, ["init", "DOMContentLoaded", "load"]);
|
||||
info("Lifecycle events have been enabled");
|
||||
|
||||
let pageLoaded = Page.loadEventFired();
|
||||
const { frameId } = await Page.navigate({ url: DOC });
|
||||
await pageLoaded;
|
||||
info("A new page has been loaded");
|
||||
|
||||
await assertNavigationLifecycleEvents({ promise, frameId });
|
||||
});
|
||||
|
||||
add_task(async function navigateEventsOnReload({ Page }) {
|
||||
await Page.enable();
|
||||
info("Page domain has been enabled");
|
||||
|
||||
let pageLoaded = Page.loadEventFired();
|
||||
const { frameId } = await Page.navigate({ url: DOC });
|
||||
await pageLoaded;
|
||||
info("Initial page has been loaded");
|
||||
|
||||
await Page.setLifecycleEventsEnabled({ enabled: true });
|
||||
const promise = recordPromises(Page, ["init", "DOMContentLoaded", "load"]);
|
||||
info("Lifecycle events have been enabled");
|
||||
|
||||
pageLoaded = Page.loadEventFired();
|
||||
await Page.reload();
|
||||
await pageLoaded;
|
||||
info("The page has been reloaded");
|
||||
|
||||
await assertNavigationLifecycleEvents({ promise, frameId });
|
||||
});
|
||||
|
||||
add_task(async function navigateEventsOnNavigateToSameURL({ Page }) {
|
||||
await Page.enable();
|
||||
info("Page domain has been enabled");
|
||||
|
||||
let pageLoaded = Page.loadEventFired();
|
||||
const { frameId } = await Page.navigate({ url: DOC });
|
||||
await pageLoaded;
|
||||
info("Initial page has been loaded");
|
||||
|
||||
await Page.setLifecycleEventsEnabled({ enabled: true });
|
||||
const promise = recordPromises(Page, ["init", "DOMContentLoaded", "load"]);
|
||||
info("Lifecycle events have been enabled");
|
||||
|
||||
pageLoaded = Page.loadEventFired();
|
||||
await Page.navigate({ url: DOC });
|
||||
await pageLoaded;
|
||||
info("The page has been reloaded");
|
||||
|
||||
await assertNavigationLifecycleEvents({ promise, frameId });
|
||||
});
|
||||
|
||||
function recordPromises(Page, names) {
|
||||
return new Promise(resolve => {
|
||||
const resolutions = new Map();
|
||||
|
||||
const unsubscribe = Page.lifecycleEvent(event => {
|
||||
info(`Received Page.lifecycleEvent for ${event.name}`);
|
||||
resolutions.set(event.name, event);
|
||||
|
||||
if (event.name == names[names.length - 1]) {
|
||||
unsubscribe();
|
||||
resolve(resolutions);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function assertNavigationLifecycleEvents({ promise, frameId, timeout }) {
|
||||
// Wait for all the promises to resolve
|
||||
const promises = [promise];
|
||||
|
||||
if (timeout) {
|
||||
promises.push(timeoutPromise(timeout));
|
||||
}
|
||||
|
||||
const resolutions = await Promise.race(promises);
|
||||
|
||||
if (timeout) {
|
||||
is(resolutions, undefined, "No lifecycle events have been recorded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Assert the order in which they resolved
|
||||
const expectedResolutions = ["init", "DOMContentLoaded", "load"];
|
||||
Assert.deepEqual(
|
||||
[...resolutions.keys()],
|
||||
expectedResolutions,
|
||||
"Received various lifecycle events in the expected order"
|
||||
);
|
||||
|
||||
// Now assert the data exposed by each of these events
|
||||
const frameStartedLoading = resolutions.get("init");
|
||||
is(frameStartedLoading.frameId, frameId, "init frameId is the same one");
|
||||
|
||||
const DOMContentLoaded = resolutions.get("DOMContentLoaded");
|
||||
is(
|
||||
DOMContentLoaded.frameId,
|
||||
frameId,
|
||||
"DOMContentLoaded frameId is the same one"
|
||||
);
|
||||
|
||||
const load = resolutions.get("load");
|
||||
is(load.frameId, frameId, "load frameId is the same one");
|
||||
}
|
|
@ -109,7 +109,9 @@ run-sequentially = hardcoded ports
|
|||
# this test doesn't apply.
|
||||
skip-if = toolkit == 'android'
|
||||
[test_delegated_credentials.js]
|
||||
run-sequentially = hardcoded ports
|
||||
[test_delegated_credentials_weak.js]
|
||||
run-sequentially = hardcoded ports
|
||||
[test_der.js]
|
||||
[test_enterprise_roots.js]
|
||||
# This feature is implemented for Windows and OS X. However, we don't currently
|
||||
|
|
|
@ -356,7 +356,7 @@ android-api-16-gcp/debug:
|
|||
platform: android-4-0-armv7-api16/debug
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android/releng.manifest"
|
||||
|
@ -380,7 +380,7 @@ android-x86-gcp/opt:
|
|||
platform: android-4-2-x86/opt
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android-x86/releng.manifest"
|
||||
|
@ -403,7 +403,7 @@ android-api-16-gcp/opt:
|
|||
platform: android-4-0-armv7-api16/opt
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android/releng.manifest"
|
||||
|
@ -426,7 +426,7 @@ android-aarch64-gcp/opt:
|
|||
platform: android-5-0-aarch64/opt
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android/releng.manifest"
|
||||
|
@ -449,7 +449,7 @@ android-aarch64-gcp/debug:
|
|||
platform: android-5-0-aarch64/debug
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android/releng.manifest"
|
||||
|
@ -472,7 +472,7 @@ android-x86_64-gcp/opt:
|
|||
platform: android-5-0-x86_64/opt
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android-x86/releng.manifest"
|
||||
|
@ -495,7 +495,7 @@ android-x86_64-gcp/debug:
|
|||
platform: android-5-0-x86_64/debug
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android-x86/releng.manifest"
|
||||
|
|
|
@ -1101,49 +1101,6 @@ linux64-tup/opt:
|
|||
- linux64-nasm
|
||||
- linux64-node
|
||||
|
||||
linux64-ccov/debug:
|
||||
description: "Linux64-CCov Debug"
|
||||
index:
|
||||
product: firefox
|
||||
job-name: linux64-ccov-debug
|
||||
treeherder:
|
||||
platform: linux64-ccov/debug
|
||||
symbol: B
|
||||
tier: 2
|
||||
run-on-projects: ['mozilla-central', 'try']
|
||||
worker-type: b-linux
|
||||
worker:
|
||||
artifacts:
|
||||
- name: public/code-coverage-grcov.zip
|
||||
path: /builds/worker/workspace/build/src/obj-firefox/code-coverage-grcov.zip
|
||||
type: file
|
||||
max-run-time: 7200
|
||||
env:
|
||||
FORCE_GCC: '1'
|
||||
RUSTC_BOOTSTRAP: '1'
|
||||
run:
|
||||
using: mozharness
|
||||
actions: [get-secrets, build]
|
||||
config:
|
||||
- builds/releng_base_firefox.py
|
||||
- builds/releng_base_linux_64_builds.py
|
||||
script: "mozharness/scripts/fx_desktop_build.py"
|
||||
secrets: true
|
||||
custom-build-variant-cfg: code-coverage-debug
|
||||
mozconfig-variant: code-coverage-debug
|
||||
tooltool-downloads: public
|
||||
need-xvfb: true
|
||||
fetches:
|
||||
toolchain:
|
||||
- linux64-clang-9
|
||||
- linux64-rust
|
||||
- linux64-gcc
|
||||
- linux64-grcov
|
||||
- linux64-cbindgen
|
||||
- linux64-sccache
|
||||
- linux64-nasm
|
||||
- linux64-node
|
||||
|
||||
linux64-ccov/opt:
|
||||
description: "Linux64-CCov Opt"
|
||||
index:
|
||||
|
@ -1268,7 +1225,7 @@ linux-gcp/debug:
|
|||
platform: linux32/debug
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
docker-image: {in-tree: debian7-i386-build}
|
||||
max-run-time: 5400
|
||||
|
@ -1308,7 +1265,7 @@ linux-gcp/opt:
|
|||
platform: linux32/opt
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
docker-image: {in-tree: debian7-i386-build}
|
||||
max-run-time: 7200
|
||||
|
@ -1347,7 +1304,7 @@ linux64-gcp/debug:
|
|||
platform: linux64/debug
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
max-run-time: 5400
|
||||
env:
|
||||
|
@ -1386,7 +1343,7 @@ linux64-gcp/opt:
|
|||
platform: linux64/opt
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
max-run-time: 7200
|
||||
env:
|
||||
|
|
|
@ -395,7 +395,7 @@ macosx64-gcp/debug:
|
|||
platform: osx-cross/debug
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
max-run-time: 3600
|
||||
env:
|
||||
|
@ -438,7 +438,7 @@ macosx64-gcp/opt:
|
|||
platform: osx-cross/opt
|
||||
symbol: Bg
|
||||
tier: 3
|
||||
worker-type: gce/gecko-{level}-b-linux
|
||||
worker-type: b-linux-gcp
|
||||
worker:
|
||||
max-run-time: 7200
|
||||
env:
|
||||
|
|
|
@ -118,15 +118,6 @@ linux64-asan-qr/opt:
|
|||
test-sets:
|
||||
- linux-qr-smoketests
|
||||
|
||||
linux64-ccov/debug:
|
||||
build-platform: linux64-ccov/debug
|
||||
test-sets:
|
||||
- common-tests
|
||||
- web-platform-tests
|
||||
- awsy
|
||||
- talos
|
||||
- ccov-code-coverage-tests
|
||||
|
||||
linux64-ccov/opt:
|
||||
build-platform: linux64-ccov/opt
|
||||
test-sets:
|
||||
|
|
|
@ -2102,7 +2102,7 @@ var BrowserTestUtils = {
|
|||
|
||||
/**
|
||||
* Opens a tab with a given uri and params object. If the params object is not set
|
||||
* or the params parameter does not include a triggeringPricnipal then this function
|
||||
* or the params parameter does not include a triggeringPrincipal then this function
|
||||
* provides a params object using the systemPrincipal as the default triggeringPrincipal.
|
||||
*
|
||||
* @param {xul:tabbrowser} tabbrowser
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* This @file implements the child side of Conduits, an abstraction over
|
||||
* Fission IPC for extension API subject. See {@link ConduitsParent.jsm}
|
||||
* for more details about the overall design.
|
||||
*/
|
||||
|
||||
const EXPORTED_SYMBOLS = ["BaseConduit", "ConduitsChild"];
|
||||
|
||||
/**
|
||||
* Base class for both child (Point) and parent (Broadcast) side of conduits,
|
||||
* handles setting up send/receive method stubs.
|
||||
*/
|
||||
class BaseConduit {
|
||||
/**
|
||||
* @param {object} subject
|
||||
* @param {ConduitAddress} address
|
||||
*/
|
||||
constructor(subject, address) {
|
||||
this.subject = subject;
|
||||
this.address = address;
|
||||
this.id = address.id;
|
||||
|
||||
for (let name of address.send || []) {
|
||||
this[`send${name}`] = this._send.bind(this, name, false);
|
||||
}
|
||||
for (let name of address.query || []) {
|
||||
this[`query${name}`] = this._send.bind(this, name, true);
|
||||
}
|
||||
|
||||
this.recv = new Map();
|
||||
for (let name of address.recv || []) {
|
||||
let method = this.subject[`recv${name}`];
|
||||
if (!method) {
|
||||
throw new Error(`recv${name} not found for conduit ${this.id}`);
|
||||
}
|
||||
this.recv.set(name, method.bind(this.subject));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal, partially @abstract, uses the actor to send the message/query.
|
||||
* @param {string} method
|
||||
* @param {boolean} query Flag indicating a response is expected.
|
||||
* @param {JSWindowActor} actor
|
||||
* @param {{arg: object, sender: ConduitID}} data
|
||||
* @returns {Promise?}
|
||||
*/
|
||||
_send(method, query, actor, data) {
|
||||
if (query) {
|
||||
return actor.sendQuery(method, data);
|
||||
}
|
||||
actor.sendAsyncMessage(method, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal, calls the specific recvX method based on the message.
|
||||
* @param {string} name Message/method name.
|
||||
* @param {object} arg Message data, the one and only method argument.
|
||||
* @param {object} meta Metadata about the method call.
|
||||
*/
|
||||
async _recv(name, arg, meta) {
|
||||
let method = this.recv.get(name);
|
||||
if (!method) {
|
||||
throw new Error(`recv${name} not found for conduit ${this.id}`);
|
||||
}
|
||||
try {
|
||||
return await method(arg, meta);
|
||||
} catch (e) {
|
||||
if (meta.query) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Child side conduit, can only send/receive point-to-point messages via the
|
||||
* one specific ConduitsChild actor.
|
||||
*/
|
||||
class PointConduit extends BaseConduit {
|
||||
constructor(subject, address, actor) {
|
||||
super(subject, address);
|
||||
this.actor = actor;
|
||||
this.actor.sendAsyncMessage("ConduitOpened", { arg: address });
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal, sends messages via the actor, used by sendX stubs.
|
||||
* @param {string} method
|
||||
* @param {boolean} query
|
||||
* @param {object?} arg
|
||||
* @returns {Promise?}
|
||||
*/
|
||||
_send(method, query, arg = {}) {
|
||||
if (!this.actor) {
|
||||
throw new Error(`send${method} on closed conduit ${this.id}`);
|
||||
}
|
||||
let sender = this.id;
|
||||
return super._send(method, query, this.actor, { arg, query, sender });
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the conduit from further IPC, notifies the parent side by default.
|
||||
* @param {boolean} silent
|
||||
*/
|
||||
close(silent = false) {
|
||||
let { actor } = this;
|
||||
if (actor) {
|
||||
this.actor = null;
|
||||
actor.conduits.delete(this.id);
|
||||
if (!silent) {
|
||||
actor.sendAsyncMessage("ConduitClosed", { arg: this.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the child side of the Conduits actor, manages conduit lifetimes.
|
||||
*/
|
||||
class ConduitsChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
this.conduits = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Public entry point a child-side subject uses to open a conduit.
|
||||
* @param {object} subject
|
||||
* @param {ConduitAddress} address
|
||||
* @returns {PointConduit}
|
||||
*/
|
||||
openConduit(subject, address) {
|
||||
let conduit = new PointConduit(subject, address, this);
|
||||
this.conduits.set(conduit.id, conduit);
|
||||
return conduit;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSWindowActor method, routes the message to the target subject.
|
||||
* @returns {Promise?}
|
||||
*/
|
||||
receiveMessage({ name, data: { target, arg, query, sender } }) {
|
||||
let conduit = this.conduits.get(target);
|
||||
if (!conduit) {
|
||||
throw new Error(`${name} for closed conduit ${target}: ${uneval(arg)}`);
|
||||
}
|
||||
return conduit._recv(name, arg, { sender, query, actor: this });
|
||||
}
|
||||
|
||||
/**
|
||||
* JSWindowActor method, called before actor is destroyed.
|
||||
* Parent side will get the same call, so just silently close all conduits.
|
||||
*/
|
||||
willDestroy() {
|
||||
for (let conduit of this.conduits.values()) {
|
||||
conduit.close(true);
|
||||
}
|
||||
this.conduits.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSWindowActor method, ensure cleanup (see bug 1596187).
|
||||
*/
|
||||
didDestroy() {
|
||||
this.willDestroy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
/* 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";
|
||||
|
||||
const EXPORTED_SYMBOLS = ["BroadcastConduit", "ConduitsParent"];
|
||||
|
||||
/**
|
||||
* This @file implements the parent side of Conduits, an abstraction over
|
||||
* Fission IPC for extension Contexts, API managers, Ports/Messengers, and
|
||||
* other types of "subjects" participating in implementation of extension APIs.
|
||||
*
|
||||
* Additionally, knowledge about conduits from all child processes is gathered
|
||||
* here, and used together with the full CanonicalBrowsingContext tree to route
|
||||
* IPC messages and queries directly to the right subjects.
|
||||
*
|
||||
* Each Conduit is tied to one subject, attached to a ConduitAddress descriptor,
|
||||
* and exposes an API for sending/receiving via an actor, or multiple actors in
|
||||
* case of the parent process.
|
||||
*
|
||||
* @typedef {number|string} ConduitID
|
||||
*
|
||||
* @typedef {object} ConduitAddress
|
||||
* @prop {ConduitID} id Globally unique across all processes.
|
||||
* @prop {string[]} [recv]
|
||||
* @prop {string[]} [send]
|
||||
* @prop {string[]} [query]
|
||||
* Lists of recvX, sendX, and queryX methods this subject will use.
|
||||
*
|
||||
* @example:
|
||||
*
|
||||
* init(actor) {
|
||||
* this.conduit = actor.openConduit(this, {
|
||||
* id: this.id,
|
||||
* recv: ["recvAddNumber"],
|
||||
* send: ["sendNumberUpdate"],
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* recvAddNumber({ num }, { actor, sender }) {
|
||||
* num += 1;
|
||||
* this.conduit.sendNumberUpdate(sender.id, { num });
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
const {
|
||||
ExtensionUtils: { DefaultMap, DefaultWeakMap },
|
||||
} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
const { BaseConduit } = ChromeUtils.import(
|
||||
"resource://gre/modules/ConduitsChild.jsm"
|
||||
);
|
||||
|
||||
/**
|
||||
* Internal, keeps track of all parent and remote (child) conduits.
|
||||
*/
|
||||
const Hub = {
|
||||
/** @type Map<ConduitID, ConduitAddress> Info about all child conduits. */
|
||||
remotes: new Map(),
|
||||
|
||||
/** @type Map<ConduitID, BroadcastConduit> All open parent conduits. */
|
||||
conduits: new Map(),
|
||||
|
||||
/** @type Map<string, BroadcastConduit> Parent conduits by recvMethod. */
|
||||
byMethod: new Map(),
|
||||
|
||||
/** @type WeakMap<ConduitsParent, Set<ConduitAddress>> Conduits by actor. */
|
||||
byActor: new DefaultWeakMap(() => new Set()),
|
||||
|
||||
/** @type Map<ConduitID, Set<BroadcastConduit>> */
|
||||
onRemoteClosed: new DefaultMap(() => new Set()),
|
||||
|
||||
/**
|
||||
* Save info about a new parent conduit, register it as a global listener.
|
||||
* @param {BroadcastConduit} conduit
|
||||
*/
|
||||
openConduit(conduit) {
|
||||
this.conduits.set(conduit.id, conduit);
|
||||
for (let name of conduit.address.recv || []) {
|
||||
if (this.byMethod.get(name)) {
|
||||
// For now, we only allow one parent conduit handling each recv method.
|
||||
throw new Error(`Duplicate BroadcastConduit method name recv${name}`);
|
||||
}
|
||||
this.byMethod.set(name, conduit);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanup.
|
||||
* @param {BroadcastConduit} conduit
|
||||
*/
|
||||
closeConduit({ id, address }) {
|
||||
this.conduits.delete(id);
|
||||
for (let name of address.recv || []) {
|
||||
this.byMethod.remove(name);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save info about a new remote conduit.
|
||||
* @param {ConduitAddress} address
|
||||
* @param {ConduitsParent} actor
|
||||
*/
|
||||
recvConduitOpened(address, actor) {
|
||||
address.actor = actor;
|
||||
this.remotes.set(address.id, address);
|
||||
this.byActor.get(actor).add(address);
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies listeners and cleans up after the remote conduit is closed.
|
||||
* @param {ConduitAddress} remote
|
||||
*/
|
||||
recvConduitClosed(remote) {
|
||||
this.remotes.delete(remote.id);
|
||||
this.byActor.get(remote.actor).delete(remote);
|
||||
|
||||
remote.actor = null;
|
||||
for (let conduit of this.onRemoteClosed.get(remote.id)) {
|
||||
conduit.subject.recvConduitClosed(remote);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Close all remote conduits when the actor goes away.
|
||||
* @param {ConduitsParent} actor
|
||||
*/
|
||||
actorClosed(actor) {
|
||||
for (let remote of this.byActor.get(actor)) {
|
||||
this.recvConduitClosed(remote);
|
||||
}
|
||||
this.byActor.delete(actor);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Parent side conduit, registers as a global listeners for certain messages,
|
||||
* and can target specific child conduits when sending.
|
||||
*/
|
||||
class BroadcastConduit extends BaseConduit {
|
||||
/**
|
||||
* @param {object} subject
|
||||
* @param {ConduitAddress} address
|
||||
*/
|
||||
constructor(subject, address) {
|
||||
super(subject, address);
|
||||
|
||||
this.open = true;
|
||||
Hub.openConduit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal, sends a message to a specific conduit, used by sendX stubs.
|
||||
* @param {string} method
|
||||
* @param {boolean} query
|
||||
* @param {ConduitID} target
|
||||
* @param {object?} arg
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
_send(method, query, target, arg = {}) {
|
||||
if (!this.open) {
|
||||
throw new Error(`send${method} on closed conduit ${this.id}`);
|
||||
}
|
||||
let sender = this.address;
|
||||
let { actor } = Hub.remotes.get(target);
|
||||
return super._send(method, query, actor, { target, arg, query, sender });
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the subject wants to listen for the specific conduit closing.
|
||||
* The method `recvConduitClosed(address)` will be called.
|
||||
* @param {ConduitID} target
|
||||
*/
|
||||
reportOnClosed(target) {
|
||||
Hub.onRemoteClosed.get(target).add(this);
|
||||
}
|
||||
|
||||
async close() {
|
||||
this.open = false;
|
||||
Hub.closeConduit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the parent side of the Conduits actor.
|
||||
*/
|
||||
class ConduitsParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSWindowActor method, routes the message to the target subject.
|
||||
* @returns {Promise?}
|
||||
*/
|
||||
receiveMessage({ name, data: { arg, query, sender } }) {
|
||||
switch (name) {
|
||||
case "ConduitOpened":
|
||||
return Hub.recvConduitOpened(arg, this);
|
||||
case "ConduitClosed":
|
||||
return Hub.recvConduitClosed(Hub.remotes.get(arg));
|
||||
}
|
||||
|
||||
let conduit = Hub.byMethod.get(name);
|
||||
if (!conduit) {
|
||||
throw new Error(`Parent conduit for recv${name} not found`);
|
||||
}
|
||||
|
||||
sender = Hub.remotes.get(sender);
|
||||
return conduit._recv(name, arg, { actor: this, query, sender });
|
||||
}
|
||||
|
||||
/**
|
||||
* JSWindowActor method, called before actor is destroyed.
|
||||
*/
|
||||
willDestroy() {
|
||||
Hub.actorClosed(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSWindowActor method, ensure cleanup (see bug 1596187).
|
||||
*/
|
||||
didDestroy() {
|
||||
this.willDestroy();
|
||||
}
|
||||
}
|
|
@ -1072,7 +1072,7 @@ class ProxyAPIImplementation extends SchemaAPIInterface {
|
|||
map.ids.set(id, listener);
|
||||
map.listeners.set(listener, id);
|
||||
|
||||
this.childApiManager.messageManager.sendAsyncMessage("API:AddListener", {
|
||||
this.childApiManager.conduit.sendAddListener({
|
||||
childId: this.childApiManager.id,
|
||||
listenerId: id,
|
||||
path: this.path,
|
||||
|
@ -1092,7 +1092,7 @@ class ProxyAPIImplementation extends SchemaAPIInterface {
|
|||
map.ids.delete(id);
|
||||
map.removedIds.add(id);
|
||||
|
||||
this.childApiManager.messageManager.sendAsyncMessage("API:RemoveListener", {
|
||||
this.childApiManager.conduit.sendRemoveListener({
|
||||
childId: this.childApiManager.id,
|
||||
listenerId: id,
|
||||
path: this.path,
|
||||
|
@ -1173,10 +1173,18 @@ class ChildAPIManager {
|
|||
|
||||
this.id = `${context.extension.id}.${context.contextId}`;
|
||||
|
||||
MessageChannel.addListener(messageManager, "API:RunListener", this);
|
||||
messageManager.addMessageListener("API:CallResult", this);
|
||||
this.conduit = context.openConduit(this, {
|
||||
id: this.id,
|
||||
send: ["CreateProxyContext", "APICall", "AddListener", "RemoveListener"],
|
||||
recv: ["CallResult", "RunListener"],
|
||||
});
|
||||
|
||||
this.messageFilterStrict = { childId: this.id };
|
||||
this.conduit.sendCreateProxyContext({
|
||||
childId: this.id,
|
||||
extensionId: context.extension.id,
|
||||
principal: context.principal,
|
||||
...contextData,
|
||||
});
|
||||
|
||||
this.listeners = new DefaultMap(() => ({
|
||||
ids: new Map(),
|
||||
|
@ -1187,15 +1195,6 @@ class ChildAPIManager {
|
|||
// Map[callId -> Deferred]
|
||||
this.callPromises = new Map();
|
||||
|
||||
let params = {
|
||||
childId: this.id,
|
||||
extensionId: context.extension.id,
|
||||
principal: context.principal,
|
||||
};
|
||||
Object.assign(params, contextData);
|
||||
|
||||
this.messageManager.sendAsyncMessage("API:CreateProxyContext", params);
|
||||
|
||||
this.permissionsChangedCallbacks = new Set();
|
||||
this.updatePermissions = null;
|
||||
if (this.context.extension.optionalPermissions.length) {
|
||||
|
@ -1217,50 +1216,42 @@ class ChildAPIManager {
|
|||
this.schema.inject(obj, this);
|
||||
}
|
||||
|
||||
receiveMessage({ name, messageName, data }) {
|
||||
if (data.childId != this.id) {
|
||||
return;
|
||||
recvCallResult(data) {
|
||||
let deferred = this.callPromises.get(data.callId);
|
||||
this.callPromises.delete(data.callId);
|
||||
if ("error" in data) {
|
||||
deferred.reject(data.error);
|
||||
} else {
|
||||
let result = data.result.deserialize(this.context.cloneScope);
|
||||
|
||||
deferred.resolve(new NoCloneSpreadArgs(result));
|
||||
}
|
||||
}
|
||||
|
||||
switch (name || messageName) {
|
||||
case "API:RunListener":
|
||||
let map = this.listeners.get(data.path);
|
||||
let listener = map.ids.get(data.listenerId);
|
||||
recvRunListener(data) {
|
||||
let map = this.listeners.get(data.path);
|
||||
let listener = map.ids.get(data.listenerId);
|
||||
|
||||
if (listener) {
|
||||
let args = data.args.deserialize(this.context.cloneScope);
|
||||
let fire = () => this.context.applySafeWithoutClone(listener, args);
|
||||
return Promise.resolve(
|
||||
data.handlingUserInput
|
||||
? withHandlingUserInput(this.context.contentWindow, fire)
|
||||
: fire()
|
||||
).then(result => {
|
||||
if (result !== undefined) {
|
||||
return new StructuredCloneHolder(result, this.context.cloneScope);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
if (listener) {
|
||||
let args = data.args.deserialize(this.context.cloneScope);
|
||||
let fire = () => this.context.applySafeWithoutClone(listener, args);
|
||||
return Promise.resolve(
|
||||
data.handlingUserInput
|
||||
? withHandlingUserInput(this.context.contentWindow, fire)
|
||||
: fire()
|
||||
).then(result => {
|
||||
if (result !== undefined) {
|
||||
return new StructuredCloneHolder(result, this.context.cloneScope);
|
||||
}
|
||||
if (!map.removedIds.has(data.listenerId)) {
|
||||
Services.console.logStringMessage(
|
||||
`Unknown listener at childId=${data.childId} path=${
|
||||
data.path
|
||||
} listenerId=${data.listenerId}\n`
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "API:CallResult":
|
||||
let deferred = this.callPromises.get(data.callId);
|
||||
if ("error" in data) {
|
||||
deferred.reject(data.error);
|
||||
} else {
|
||||
let result = data.result.deserialize(this.context.cloneScope);
|
||||
|
||||
deferred.resolve(new NoCloneSpreadArgs(result));
|
||||
}
|
||||
this.callPromises.delete(data.callId);
|
||||
break;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (!map.removedIds.has(data.listenerId)) {
|
||||
Services.console.logStringMessage(
|
||||
`Unknown listener at childId=${data.childId} path=${
|
||||
data.path
|
||||
} listenerId=${data.listenerId}\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1271,11 +1262,7 @@ class ChildAPIManager {
|
|||
* @param {Array} args The parameters for the function.
|
||||
*/
|
||||
callParentFunctionNoReturn(path, args) {
|
||||
this.messageManager.sendAsyncMessage("API:Call", {
|
||||
childId: this.id,
|
||||
path,
|
||||
args,
|
||||
});
|
||||
this.conduit.sendAPICall({ childId: this.id, path, args });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1299,7 +1286,8 @@ class ChildAPIManager {
|
|||
// logged the api_call. Flag it so the parent doesn't log again.
|
||||
let { alreadyLogged = true } = options;
|
||||
|
||||
this.messageManager.sendAsyncMessage("API:Call", {
|
||||
// TODO: conduit.queryAPICall()
|
||||
this.conduit.sendAPICall({
|
||||
childId: this.id,
|
||||
callId,
|
||||
path,
|
||||
|
@ -1335,11 +1323,8 @@ class ChildAPIManager {
|
|||
}
|
||||
|
||||
close() {
|
||||
this.messageManager.sendAsyncMessage("API:CloseProxyContext", {
|
||||
childId: this.id,
|
||||
});
|
||||
this.messageManager.removeMessageListener("API:CallResult", this);
|
||||
MessageChannel.removeListener(this.messageManager, "API:RunListener", this);
|
||||
// Reports CONDUIT_CLOSED on the parent side.
|
||||
this.conduit.close();
|
||||
|
||||
if (this.updatePermissions) {
|
||||
this.context.extension.off("add-permissions", this.updatePermissions);
|
||||
|
|
|
@ -489,6 +489,11 @@ class BaseContext {
|
|||
return this.extension.canAccessWindow(window);
|
||||
}
|
||||
|
||||
openConduit(subject, address) {
|
||||
let actor = this.contentWindow.getWindowGlobalChild().getActor("Conduits");
|
||||
return actor.openConduit(subject, address);
|
||||
}
|
||||
|
||||
setContentWindow(contentWindow) {
|
||||
if (!this.canAccessWindow(contentWindow)) {
|
||||
throw new Error(
|
||||
|
|
|
@ -24,6 +24,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
|
||||
BroadcastConduit: "resource://gre/modules/ConduitsParent.jsm",
|
||||
DeferredTask: "resource://gre/modules/DeferredTask.jsm",
|
||||
E10SUtils: "resource://gre/modules/E10SUtils.jsm",
|
||||
ExtensionData: "resource://gre/modules/Extension.jsm",
|
||||
|
@ -901,13 +902,15 @@ ParentAPIManager = {
|
|||
proxyContexts: new Map(),
|
||||
|
||||
init() {
|
||||
// TODO: Bug 1595186 - remove/replace all usage of MessageManager below.
|
||||
Services.obs.addObserver(this, "message-manager-close");
|
||||
|
||||
Services.mm.addMessageListener("API:CreateProxyContext", this);
|
||||
Services.mm.addMessageListener("API:CloseProxyContext", this, true);
|
||||
Services.mm.addMessageListener("API:Call", this);
|
||||
Services.mm.addMessageListener("API:AddListener", this);
|
||||
Services.mm.addMessageListener("API:RemoveListener", this);
|
||||
this.conduit = new BroadcastConduit(this, {
|
||||
id: "ParentAPIManager",
|
||||
recv: ["CreateProxyContext", "APICall", "AddListener", "RemoveListener"],
|
||||
send: ["CallResult"],
|
||||
query: ["RunListener"],
|
||||
});
|
||||
},
|
||||
|
||||
attachMessageManager(extension, processMessageManager) {
|
||||
|
@ -945,32 +948,9 @@ ParentAPIManager = {
|
|||
}
|
||||
},
|
||||
|
||||
receiveMessage({ name, data, target }) {
|
||||
try {
|
||||
switch (name) {
|
||||
case "API:CreateProxyContext":
|
||||
this.createProxyContext(data, target);
|
||||
break;
|
||||
|
||||
case "API:CloseProxyContext":
|
||||
this.closeProxyContext(data.childId);
|
||||
break;
|
||||
|
||||
case "API:Call":
|
||||
this.call(data, target);
|
||||
break;
|
||||
|
||||
case "API:AddListener":
|
||||
this.addListener(data, target);
|
||||
break;
|
||||
|
||||
case "API:RemoveListener":
|
||||
this.removeListener(data);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
recvCreateProxyContext(data, { actor, sender }) {
|
||||
this.conduit.reportOnClosed(sender.id);
|
||||
this.createProxyContext(data, actor.browsingContext.top.embedderElement);
|
||||
},
|
||||
|
||||
createProxyContext(data, target) {
|
||||
|
@ -1036,6 +1016,10 @@ ParentAPIManager = {
|
|||
this.proxyContexts.set(childId, context);
|
||||
},
|
||||
|
||||
recvConduitClosed(sender) {
|
||||
this.closeProxyContext(sender.id);
|
||||
},
|
||||
|
||||
closeProxyContext(childId) {
|
||||
let context = this.proxyContexts.get(childId);
|
||||
if (context) {
|
||||
|
@ -1079,8 +1063,9 @@ ParentAPIManager = {
|
|||
}
|
||||
},
|
||||
|
||||
async call(data, target) {
|
||||
async recvAPICall(data, { actor }) {
|
||||
let context = this.getContextById(data.childId);
|
||||
let target = actor.browsingContext.top.embedderElement;
|
||||
if (context.parentMessageManager !== target.messageManager) {
|
||||
throw new Error("Got message on unexpected message manager");
|
||||
}
|
||||
|
@ -1094,16 +1079,12 @@ ParentAPIManager = {
|
|||
return;
|
||||
}
|
||||
|
||||
context.parentMessageManager.sendAsyncMessage(
|
||||
"API:CallResult",
|
||||
Object.assign(
|
||||
{
|
||||
childId: data.childId,
|
||||
callId: data.callId,
|
||||
},
|
||||
result
|
||||
)
|
||||
);
|
||||
this.conduit.sendCallResult(data.childId, {
|
||||
childId: data.childId,
|
||||
callId: data.callId,
|
||||
path: data.path,
|
||||
...result,
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
|
@ -1143,47 +1124,39 @@ ParentAPIManager = {
|
|||
}
|
||||
},
|
||||
|
||||
async addListener(data, target) {
|
||||
async recvAddListener(data, { actor }) {
|
||||
let context = this.getContextById(data.childId);
|
||||
let target = actor.browsingContext.top.embedderElement;
|
||||
if (context.parentMessageManager !== target.messageManager) {
|
||||
throw new Error("Got message on unexpected message manager");
|
||||
}
|
||||
|
||||
let { childId } = data;
|
||||
let handlingUserInput = false;
|
||||
let lowPriority = data.path.startsWith("webRequest.");
|
||||
|
||||
function listener(...listenerArgs) {
|
||||
return context
|
||||
.sendMessage(
|
||||
context.parentMessageManager,
|
||||
"API:RunListener",
|
||||
{
|
||||
childId,
|
||||
handlingUserInput,
|
||||
listenerId: data.listenerId,
|
||||
path: data.path,
|
||||
get args() {
|
||||
return new StructuredCloneHolder(listenerArgs);
|
||||
},
|
||||
},
|
||||
{
|
||||
lowPriority,
|
||||
recipient: { childId },
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
let rv = result && result.deserialize(global);
|
||||
ExtensionActivityLog.log(
|
||||
context.extension.id,
|
||||
context.viewType,
|
||||
"api_event",
|
||||
data.path,
|
||||
{ args: listenerArgs, result: rv }
|
||||
);
|
||||
return rv;
|
||||
});
|
||||
}
|
||||
// TODO: Bug 1587058 - Redesign webRequest event coelescing.
|
||||
// let lowPriority = data.path.startsWith("webRequest.");
|
||||
|
||||
let listener = async (...listenerArgs) => {
|
||||
let result = await this.conduit.queryRunListener(childId, {
|
||||
childId,
|
||||
handlingUserInput,
|
||||
listenerId: data.listenerId,
|
||||
path: data.path,
|
||||
get args() {
|
||||
return new StructuredCloneHolder(listenerArgs);
|
||||
},
|
||||
});
|
||||
let rv = result && result.deserialize(global);
|
||||
ExtensionActivityLog.log(
|
||||
context.extension.id,
|
||||
context.viewType,
|
||||
"api_event",
|
||||
data.path,
|
||||
{ args: listenerArgs, result: rv }
|
||||
);
|
||||
return rv;
|
||||
};
|
||||
|
||||
context.listenerProxies.set(data.listenerId, listener);
|
||||
|
||||
|
@ -1215,7 +1188,7 @@ ParentAPIManager = {
|
|||
);
|
||||
},
|
||||
|
||||
async removeListener(data) {
|
||||
async recvRemoveListener(data) {
|
||||
let context = this.getContextById(data.childId);
|
||||
let listener = context.listenerProxies.get(data.listenerId);
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ with Files('**'):
|
|||
BUG_COMPONENT = ('WebExtensions', 'General')
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'ConduitsChild.jsm',
|
||||
'ConduitsParent.jsm',
|
||||
'Extension.jsm',
|
||||
'ExtensionActions.jsm',
|
||||
'ExtensionActivityLog.jsm',
|
||||
|
|
|
@ -30,7 +30,7 @@ customElements.define(
|
|||
<toolbarbutton id="print-preview-navigateHome" class="print-preview-navigate-button tabbable" oncommand="parentNode.navigate(0, 0, 'home');" data-l10n-id="printpreview-homearrow"/>
|
||||
<toolbarbutton id="print-preview-navigatePrevious" class="print-preview-navigate-button tabbable" oncommand="parentNode.navigate(-1, 0, 0);" data-l10n-id="printpreview-previousarrow"/>
|
||||
<hbox align="center" pack="center">
|
||||
<html:input id="print-preview-pageNumber" hidespinbuttons="true" type="number" value="1" min="1"/>
|
||||
<html:input id="print-preview-pageNumber" class="input-number-mozbox" hidespinbuttons="true" type="number" value="1" min="1"/>
|
||||
<label data-l10n-id="printpreview-of"/>
|
||||
<label id="print-preview-totalPages" value="1"/>
|
||||
</hbox>
|
||||
|
|
|
@ -595,6 +595,7 @@ var PrintUtils = {
|
|||
is: "printpreview-toolbar",
|
||||
});
|
||||
printPreviewTB.setAttribute("fullscreentoolbar", true);
|
||||
printPreviewTB.setAttribute("flex", "1");
|
||||
printPreviewTB.id = "print-preview-toolbar";
|
||||
|
||||
let navToolbox = this._listener.getNavToolbox();
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
}
|
||||
|
||||
/* Rules required for style caching of anonymous content scrollbar parts */
|
||||
|
||||
scrollcorner, resizer, scrollbar, scrollbarbutton, slider {
|
||||
/* All scrollbar parts must not inherit any properties from the scrollable
|
||||
* element (except for visibility and pointer-events), for the anonymous
|
||||
|
@ -90,10 +89,19 @@ scrollcorner, resizer, scrollbar, scrollbarbutton, slider {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
scrollbar[orient="vertical"],
|
||||
slider[orient="vertical"],
|
||||
thumb[orient="vertical"] {
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
thumb {
|
||||
/* Prevent -moz-user-modify declaration from designmode.css having an
|
||||
* effect. */
|
||||
-moz-user-modify: initial;
|
||||
|
||||
-moz-box-align: center;
|
||||
-moz-box-pack: center;
|
||||
}
|
||||
|
||||
/* There are other rules that set direction and cursor on scrollbar,
|
||||
|
|
|
@ -569,26 +569,24 @@ tabmodalprompt {
|
|||
display: none;
|
||||
}
|
||||
|
||||
[orient="vertical"] { -moz-box-orient: vertical !important; }
|
||||
[orient="horizontal"] { -moz-box-orient: horizontal !important; }
|
||||
|
||||
[align="start"] { -moz-box-align: start !important; }
|
||||
[align="center"] { -moz-box-align: center !important; }
|
||||
[align="end"] { -moz-box-align: end !important; }
|
||||
[align="baseline"] { -moz-box-align: baseline !important; }
|
||||
[align="stretch"] { -moz-box-align: stretch !important; }
|
||||
|
||||
[pack="start"] { -moz-box-pack: start !important; }
|
||||
[pack="center"] { -moz-box-pack: center !important; }
|
||||
[pack="end"] { -moz-box-pack: end !important; }
|
||||
|
||||
@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
|
||||
/* Support common XUL attributes in the emulated flex mode so we can
|
||||
test the browser in this configuration without mass-changing existing
|
||||
markup and CSS. */
|
||||
[orient="vertical"] { -moz-box-orient: vertical; }
|
||||
[orient="horizontal"] { -moz-box-orient: horizontal; }
|
||||
|
||||
[align="start"] { -moz-box-align: start; }
|
||||
[align="center"] { -moz-box-align: center; }
|
||||
[align="end"] { -moz-box-align: end; }
|
||||
[align="baseline"] { -moz-box-align: baseline; }
|
||||
[align="stretch"] { -moz-box-align: stretch; }
|
||||
|
||||
[pack="start"] { -moz-box-pack: start; }
|
||||
[pack="center"] { -moz-box-pack: center; }
|
||||
[pack="end"] { -moz-box-pack: end; }
|
||||
|
||||
/* This isn't a real solution for [flex] and [ordinal], but it covers enough
|
||||
cases to render the browser chrome. If we get attr() in Bug 435426 this could
|
||||
work for all cases. */
|
||||
cases to render the browser chrome for us to test emulated flex mode without
|
||||
mass-changing existing markup and CSS.
|
||||
If we get attr() in Bug 435426 this could work for all cases. */
|
||||
[flex="1"] { -moz-box-flex: 1; }
|
||||
[flex="2"] { -moz-box-flex: 2; }
|
||||
[flex="3"] { -moz-box-flex: 3; }
|
||||
|
|
|
@ -113,9 +113,9 @@ def main():
|
|||
# in front of symbols.mozilla.org has a 300 second timeout, so we'll use that.
|
||||
timeout=(10, 300),
|
||||
**zip_arg)
|
||||
# 500 is likely to be a transient failure.
|
||||
# 429 or any 5XX is likely to be a transient failure.
|
||||
# Break out for success or other error codes.
|
||||
if r.status_code < 500:
|
||||
if r.ok or (r.status_code < 500 and r.status_code != 429):
|
||||
break
|
||||
print_error(r)
|
||||
except requests.exceptions.RequestException as e:
|
||||
|
|
|
@ -105,6 +105,18 @@ let ACTORS = {
|
|||
allFrames: true,
|
||||
},
|
||||
|
||||
Conduits: {
|
||||
parent: {
|
||||
moduleURI: "resource://gre/modules/ConduitsParent.jsm",
|
||||
},
|
||||
|
||||
child: {
|
||||
moduleURI: "resource://gre/modules/ConduitsChild.jsm",
|
||||
},
|
||||
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
DateTimePicker: {
|
||||
parent: {
|
||||
moduleURI: "resource://gre/actors/DateTimePickerParent.jsm",
|
||||
|
|
|
@ -6,30 +6,29 @@
|
|||
== Styles used by the input[type="number"] element.
|
||||
======================================================================= */
|
||||
|
||||
@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
||||
@namespace html url("http://www.w3.org/1999/xhtml");
|
||||
@namespace url("http://www.w3.org/1999/xhtml");
|
||||
|
||||
html|input[type="number"] {
|
||||
input[type="number"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
html|input[type="number"][hidespinbuttons="true"] {
|
||||
input[type="number"][hidespinbuttons="true"] {
|
||||
-moz-appearance: textfield !important;
|
||||
}
|
||||
|
||||
/* input[type=number] uses display: flex; by default which is incompatible with XUL flexbox
|
||||
Forcing XUL flexbox allows changing the size of the input. */
|
||||
xul|*:root html|input[type="number"],
|
||||
xul|*:root html|input[type="number"]::-moz-number-wrapper {
|
||||
.input-number-mozbox,
|
||||
.input-number-mozbox::-moz-number-wrapper {
|
||||
display: -moz-box;
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
xul|*:root html|input[type="number"]::-moz-number-wrapper,
|
||||
xul|*:root html|input[type="number"]::-moz-number-text {
|
||||
.input-number-mozbox::-moz-number-wrapper,
|
||||
.input-number-mozbox::-moz-number-text {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
xul|*:root html|input[type="number"]::-moz-number-wrapper {
|
||||
.input-number-mozbox::-moz-number-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -27,10 +27,6 @@
|
|||
|
||||
#define LOG(args) MOZ_LOG(mLog, mozilla::LogLevel::Debug, args)
|
||||
|
||||
#ifdef __MINGW32__
|
||||
# define ASSOCF_NONE 0x0
|
||||
#endif
|
||||
|
||||
// helper methods: forward declarations...
|
||||
static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType,
|
||||
nsString& aFileExtension);
|
||||
|
@ -304,16 +300,10 @@ nsresult nsOSHelperAppService::GetDefaultAppInfo(
|
|||
nsCOMPtr<nsILocalFileWin> lf = new nsLocalFile();
|
||||
rv = lf->InitWithCommandLine(handlerCommand);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
lf.forget(aDefaultApplication);
|
||||
|
||||
wchar_t friendlyName[1024];
|
||||
DWORD friendlyNameSize = 1024;
|
||||
HRESULT hr = AssocQueryString(ASSOCF_NONE, ASSOCSTR_FRIENDLYAPPNAME,
|
||||
PromiseFlatString(aAppInfo).get(), NULL,
|
||||
friendlyName, &friendlyNameSize);
|
||||
if (SUCCEEDED(hr) && friendlyNameSize > 1) {
|
||||
aDefaultDescription.Assign(friendlyName, friendlyNameSize - 1);
|
||||
}
|
||||
// The "FileDescription" field contains the actual name of the application.
|
||||
lf->GetVersionInfoField("FileDescription", aDefaultDescription);
|
||||
lf.forget(aDefaultApplication);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,14 @@ nsTouchBarUpdater::UpdateTouchBarInputs(nsIBaseWindow* aWindow,
|
|||
continue;
|
||||
}
|
||||
|
||||
NSTouchBarItemIdentifier newIdentifier = [TouchBarInput nativeIdentifierWithXPCOM:input];
|
||||
// We don't support updating the Share scrubber since it's a special
|
||||
// Apple-made component that behaves differently from the other inputs.
|
||||
if ([newIdentifier isEqualToString:[TouchBarInput nativeIdentifierWithType:@"scrubber"
|
||||
withKey:@"share"]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TouchBarInput* convertedInput = [[TouchBarInput alloc] initWithXPCOM:input];
|
||||
[(nsTouchBar*)cocoaWin.touchBar updateItem:convertedInput];
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче