зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1562844 - GeckoView webextensions tabs and webnavigation listeners support r=robwu,rpl,snorp
This changes provide basic support for webextenion tabs and webNavigation listeners by implementing missing objects on which Fennec implementation was relying. Differential Revision: https://phabricator.services.mozilla.com/D36575 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
16f6820be1
Коммит
3aafa3960e
|
@ -36,6 +36,11 @@ let tabListener = {
|
|||
let { BrowserApp } = browser.ownerGlobal;
|
||||
let nativeTab = BrowserApp.getTabForBrowser(browser);
|
||||
|
||||
// Ignore initial about:blank
|
||||
if (!request && this.initializingTabs.has(nativeTab)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we are certain that the first page in the tab was loaded.
|
||||
this.initializingTabs.delete(nativeTab);
|
||||
|
||||
|
|
|
@ -8,6 +8,12 @@ ChromeUtils.defineModuleGetter(
|
|||
"resource://gre/modules/PrivateBrowsingUtils.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"GeckoViewTabBridge",
|
||||
"resource://gre/modules/GeckoViewTab.jsm"
|
||||
);
|
||||
|
||||
/* globals EventDispatcher */
|
||||
var { EventDispatcher } = ChromeUtils.import(
|
||||
"resource://gre/modules/Messaging.jsm"
|
||||
|
@ -93,6 +99,23 @@ class BrowserProgressListener {
|
|||
}
|
||||
}
|
||||
|
||||
const PROGRESS_LISTENER_FLAGS =
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_ALL | Ci.nsIWebProgress.NOTIFY_LOCATION;
|
||||
|
||||
class GeckoViewProgressListenerWrapper {
|
||||
constructor(window, listener) {
|
||||
this.listener = new BrowserProgressListener(
|
||||
window.BrowserApp.selectedBrowser,
|
||||
listener,
|
||||
PROGRESS_LISTENER_FLAGS
|
||||
);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.listener.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles wrapping a tab progress listener in browser-specific
|
||||
* BrowserProgressListener instances, an attaching them to each tab in a given
|
||||
|
@ -103,15 +126,12 @@ class BrowserProgressListener {
|
|||
* @param {object} listener
|
||||
* The tab progress listener to wrap.
|
||||
*/
|
||||
class ProgressListenerWrapper {
|
||||
class FennecProgressListenerWrapper {
|
||||
constructor(window, listener) {
|
||||
this.window = window;
|
||||
this.listener = listener;
|
||||
this.listeners = new WeakMap();
|
||||
|
||||
this.flags =
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_ALL | Ci.nsIWebProgress.NOTIFY_LOCATION;
|
||||
|
||||
for (let nativeTab of this.window.BrowserApp.tabs) {
|
||||
this.addBrowserProgressListener(nativeTab.browser);
|
||||
}
|
||||
|
@ -178,6 +198,10 @@ class ProgressListenerWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
const ProgressListenerWrapper = Services.androidBridge.isFennec
|
||||
? FennecProgressListenerWrapper
|
||||
: GeckoViewProgressListenerWrapper;
|
||||
|
||||
class WindowTracker extends WindowTrackerBase {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
@ -189,6 +213,10 @@ class WindowTracker extends WindowTrackerBase {
|
|||
return Services.wm.getMostRecentWindow(WINDOW_TYPE);
|
||||
}
|
||||
|
||||
get topNonPBWindow() {
|
||||
return Services.wm.getMostRecentNonPBWindow(WINDOW_TYPE);
|
||||
}
|
||||
|
||||
isBrowserWindow(window) {
|
||||
let { documentElement } = window.document;
|
||||
return documentElement.getAttribute("windowtype") === WINDOW_TYPE;
|
||||
|
@ -255,7 +283,80 @@ global.makeGlobalEvent = function makeGlobalEvent(
|
|||
}).api();
|
||||
};
|
||||
|
||||
class TabTracker extends TabTrackerBase {
|
||||
class GeckoViewTabTracker extends TabTrackerBase {
|
||||
init() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
this.initialized = true;
|
||||
|
||||
windowTracker.addOpenListener(window => {
|
||||
const nativeTab = window.BrowserApp.selectedTab;
|
||||
this.emit("tab-created", { nativeTab });
|
||||
});
|
||||
|
||||
windowTracker.addCloseListener(window => {
|
||||
const nativeTab = window.BrowserApp.selectedTab;
|
||||
const { windowId, tabId } = this.getBrowserData(
|
||||
window.BrowserApp.selectedBrowser
|
||||
);
|
||||
this.emit("tab-removed", {
|
||||
nativeTab,
|
||||
tabId,
|
||||
windowId,
|
||||
// In GeckoView, it is not meaningful to speak of "window closed", because a tab is a window.
|
||||
// Until we have a meaningful way to group tabs (and close multiple tabs at once),
|
||||
// let's use isWindowClosing: false
|
||||
isWindowClosing: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getId(nativeTab) {
|
||||
return nativeTab.id;
|
||||
}
|
||||
|
||||
getTab(id, default_ = undefined) {
|
||||
const windowId = GeckoViewTabBridge.tabIdToWindowId(id);
|
||||
const win = windowTracker.getWindow(windowId, null, false);
|
||||
|
||||
if (win && win.BrowserApp) {
|
||||
let nativeTab = win.BrowserApp.selectedTab;
|
||||
if (nativeTab) {
|
||||
return nativeTab;
|
||||
}
|
||||
}
|
||||
|
||||
if (default_ !== undefined) {
|
||||
return default_;
|
||||
}
|
||||
throw new ExtensionError(`Invalid tab ID: ${id}`);
|
||||
}
|
||||
|
||||
getBrowserData(browser) {
|
||||
const window = browser.ownerGlobal;
|
||||
if (!window.BrowserApp) {
|
||||
return {
|
||||
tabId: -1,
|
||||
windowId: -1,
|
||||
};
|
||||
}
|
||||
return {
|
||||
tabId: this.getId(window.BrowserApp.selectedTab),
|
||||
windowId: windowTracker.getId(window),
|
||||
};
|
||||
}
|
||||
|
||||
get activeTab() {
|
||||
let win = windowTracker.topWindow;
|
||||
if (win && win.BrowserApp) {
|
||||
return win.BrowserApp.selectedTab;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class FennecTabTracker extends TabTrackerBase {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
|
@ -486,7 +587,11 @@ class TabTracker extends TabTrackerBase {
|
|||
}
|
||||
|
||||
windowTracker = new WindowTracker();
|
||||
tabTracker = new TabTracker();
|
||||
if (Services.androidBridge.isFennec) {
|
||||
tabTracker = new FennecTabTracker();
|
||||
} else {
|
||||
tabTracker = new GeckoViewTabTracker();
|
||||
}
|
||||
|
||||
Object.assign(global, { tabTracker, windowTracker });
|
||||
|
||||
|
|
|
@ -17,21 +17,18 @@ tags = webextensions
|
|||
[test_ext_downloads_saveAs.html]
|
||||
skip-if = !is_fennec # times out
|
||||
[test_ext_tab_runtimeConnect.html]
|
||||
skip-if = !is_fennec # times out
|
||||
skip-if = !is_fennec # times out; bug 1534640 webextension url
|
||||
[test_ext_tabs_captureVisibleTab.html]
|
||||
[test_ext_tabs_create.html]
|
||||
skip-if = !is_fennec # times out; bug 1507167
|
||||
skip-if = !is_fennec # times out; bug 1507167; bug 1534640 webextension url
|
||||
[test_ext_tabs_events.html]
|
||||
skip-if = !is_fennec # times out
|
||||
[test_ext_tabs_executeScript.html]
|
||||
skip-if = !is_fennec # tabs.query returns empty list
|
||||
[test_ext_tabs_executeScript_bad.html]
|
||||
skip-if = true # Currently fails in emulator runs
|
||||
[test_ext_tabs_executeScript_good.html]
|
||||
[test_ext_tabs_executeScript_no_create.html]
|
||||
skip-if = !is_fennec # depends on bug 1539144
|
||||
[test_ext_tabs_executeScript_runAt.html]
|
||||
skip-if = !is_fennec # tabs.query returns empty list
|
||||
[test_ext_tabs_get.html]
|
||||
[test_ext_tabs_getCurrent.html]
|
||||
skip-if = !is_fennec # times out
|
||||
[test_ext_tabs_insertCSS.html]
|
||||
|
@ -42,10 +39,8 @@ skip-if = !is_fennec # times out
|
|||
[test_ext_tabs_reload_bypass_cache.html]
|
||||
skip-if = !is_fennec # times out
|
||||
[test_ext_tabs_onUpdated.html]
|
||||
skip-if = !is_fennec # times out
|
||||
[test_ext_tabs_query.html]
|
||||
skip-if = !is_fennec # tabs.onCreated not working, test uses BrowserApp.addTab.
|
||||
[test_ext_tabs_sendMessage.html]
|
||||
skip-if = !is_fennec # times out
|
||||
[test_ext_tabs_update_url.html]
|
||||
skip-if = !is_fennec # tabs.update rejects any call.
|
||||
skip-if = !is_fennec # bug 1534640 webextension url
|
||||
[test_ext_webNavigation_onCommitted.html]
|
||||
|
|
|
@ -151,6 +151,11 @@ add_task(async function testTabRemovalEvent() {
|
|||
});
|
||||
|
||||
add_task(async function testTabActivationEvent() {
|
||||
if (!SpecialPowers.Services.androidBridge.isFennec) {
|
||||
// TODO bug 1565536: tabs.onActivated is not supported in GeckoView.
|
||||
info("skipping testTabActivationEvent");
|
||||
return;
|
||||
}
|
||||
async function background() {
|
||||
function makeExpectable() {
|
||||
let expectation = null, resolver = null;
|
||||
|
|
|
@ -22,6 +22,11 @@ add_task(async function testExecuteScript() {
|
|||
async function background() {
|
||||
try {
|
||||
let [tab] = await browser.tabs.query({active: true, currentWindow: true});
|
||||
// TODO bug 1565536: tab.active is broken in GeckoView.
|
||||
if (!SpecialPowers.Services.androidBridge.isFennec) {
|
||||
browser.test.assertEq(undefined, tab, "currentWindow's tab is not active (bug 1565536)");
|
||||
[tab] = await browser.tabs.query({currentWindow: true});
|
||||
}
|
||||
let frames = await browser.webNavigation.getAllFrames({tabId: tab.id});
|
||||
|
||||
browser.test.log(`FRAMES: ${frames[1].frameId} ${JSON.stringify(frames)}\n`);
|
||||
|
|
|
@ -48,6 +48,11 @@ add_task(async function testExecuteScript() {
|
|||
|
||||
try {
|
||||
[tab] = await browser.tabs.query({active: true, currentWindow: true});
|
||||
// TODO bug 1565536: tab.active is broken in GeckoView.
|
||||
if (!SpecialPowers.Services.androidBridge.isFennec) {
|
||||
browser.test.assertEq(undefined, tab, "currentWindow's tab is not active (bug 1565536)");
|
||||
[tab] = await browser.tabs.query({currentWindow: true});
|
||||
}
|
||||
|
||||
let success = false;
|
||||
for (let tries = 0; !success && tries < MAX_TRIES; tries++) {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tabs get Test</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
add_task(async function() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["tabs"],
|
||||
},
|
||||
async background() {
|
||||
const tab1 = await browser.tabs.create({});
|
||||
const tab2 = await browser.tabs.create({});
|
||||
browser.test.assertEq(tab1.id, (await browser.tabs.get(tab1.id)).id, "tabs.get should return tab with given id");
|
||||
browser.test.assertEq(tab2.id, (await browser.tabs.get(tab2.id)).id, "tabs.get should return tab with given id");
|
||||
await browser.tabs.remove(tab1.id);
|
||||
await browser.tabs.remove(tab2.id);
|
||||
browser.test.notifyPass("tabs.get");
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitFinish("tabs.get");
|
||||
await extension.unload();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -27,9 +27,15 @@ add_task(async function test_onUpdated() {
|
|||
let pageURL = "http://mochi.test:8888/tests/mobile/android/components/extensions/test/mochitest/context_tabs_onUpdated_page.html";
|
||||
|
||||
let expectedSequence = [
|
||||
{status: "loading"},
|
||||
{status: "loading", url: pageURL},
|
||||
{status: "complete"},
|
||||
];
|
||||
|
||||
if (SpecialPowers.Services.androidBridge.isFennec) {
|
||||
// Fennec does not fire initial loading event - Firefox and GeckoView does
|
||||
expectedSequence.shift();
|
||||
}
|
||||
let collectedSequence = [];
|
||||
|
||||
let tabId;
|
||||
|
|
|
@ -16,6 +16,11 @@ var {BrowserActions} = SpecialPowers.Cu.import("resource://gre/modules/BrowserAc
|
|||
var {Services} = SpecialPowers.Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
add_task(async function test_query_highlighted() {
|
||||
if (!SpecialPowers.Services.androidBridge.isFennec) {
|
||||
// GeckoView does not support extension popups
|
||||
info('skipping test_query_highlighted');
|
||||
return;
|
||||
}
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"],
|
||||
|
@ -100,11 +105,10 @@ add_task(async function test_query_index() {
|
|||
},
|
||||
});
|
||||
|
||||
const {BrowserApp} = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
await extension.startup();
|
||||
let tab = BrowserApp.addTab("http://example.com/");
|
||||
const win = window.open("http://example.com");
|
||||
await extension.awaitFinish("tabs.query");
|
||||
BrowserApp.closeTab(tab);
|
||||
win.close();
|
||||
await extension.unload();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebNavigation onCommitted Test</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
add_task(async function() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["webNavigation", "tabs"],
|
||||
},
|
||||
async background() {
|
||||
const url = "http://mochi.test:8888/";
|
||||
const [tab, tabDetails] = await Promise.all([
|
||||
browser.tabs.create({url}),
|
||||
new Promise(resolve => {
|
||||
browser.webNavigation.onCommitted.addListener(details => {
|
||||
if (details.url === "about:blank") {
|
||||
// skip initial about:blank
|
||||
return;
|
||||
}
|
||||
resolve(details);
|
||||
});
|
||||
}),
|
||||
]);
|
||||
|
||||
browser.test.assertEq(url, tabDetails.url, "webNavigation.onCommitted detects correct url");
|
||||
browser.test.assertEq(tab.id, tabDetails.tabId, "webNavigation.onCommitted fire for proper tabId");
|
||||
await browser.tabs.remove(tab.id);
|
||||
browser.test.notifyPass("webNavigation.onCommitted");
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitFinish("webNavigation.onCommitted");
|
||||
await extension.unload();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -198,7 +198,7 @@ public class TestRunnerActivity extends Activity {
|
|||
@Override
|
||||
public GeckoResult<AllowOrDeny> onCloseTab(WebExtension source, GeckoSession session) {
|
||||
closeSession(session);
|
||||
return GeckoResult.ALLOW;
|
||||
return GeckoResult.fromValue(AllowOrDeny.ALLOW);
|
||||
}
|
||||
});
|
||||
sRuntime.setDelegate(() -> {
|
||||
|
|
|
@ -172,7 +172,7 @@ public class GeckoViewActivity extends AppCompatActivity {
|
|||
public GeckoResult<AllowOrDeny> onCloseTab(WebExtension source, GeckoSession session) {
|
||||
TabSession tabSession = mTabSessionManager.getSession(session);
|
||||
closeTab(tabSession);
|
||||
return GeckoResult.ALLOW;
|
||||
return GeckoResult.fromValue(AllowOrDeny.ALLOW);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -65,15 +65,6 @@ class BrowserAppShim {
|
|||
return this.selectedBrowser;
|
||||
}
|
||||
|
||||
// ext-tabs calls tabListener.initTabReady(); which rely on deck when initializing ProgressListeners.
|
||||
// Deck will be removed by https://phabricator.services.mozilla.com/D36575.
|
||||
get deck() {
|
||||
return {
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
};
|
||||
}
|
||||
|
||||
static getBrowserApp(window) {
|
||||
let { BrowserApp } = window;
|
||||
|
||||
|
|
|
@ -141,6 +141,7 @@ class WebNavigationEventManager extends EventManager {
|
|||
if (
|
||||
chromeWin &&
|
||||
chromeWin.gBrowser &&
|
||||
chromeWin.gBrowserInit &&
|
||||
chromeWin.gBrowserInit.isAdoptingTab() &&
|
||||
chromeWin.gBrowser.selectedBrowser === data.browser
|
||||
) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче