From bebfb567e8a04d7b6f269622871dda31258d556b Mon Sep 17 00:00:00 2001 From: Aaron Klotz Date: Tue, 5 May 2020 18:43:56 +0000 Subject: [PATCH] Bug 1635173: Add GeckoSessionTestRule.getSessionPid() to junit; r=agi,geckoview-reviewers To support this API, we do the following: Additions to `GeckoSessionTestRule`: * We add an overload to `webExtensionApiCall` that accepts a `GeckoSession` argument. Instead of sending the message to the extension's background script, this overload instead sends the message to the port for the session's content script. * We add `GeckoSessionTestRule.getSessionPid()` which uses the new `webExtensionApiCall` overload. Moving other existing APIs over to use the new overload is out of scope for this bug and should be done in follow-ups. Additions to the `test-support` extension: * We modify the content script to receive an API call message from the native port. It then forwards the request up to the background script. By doing it this way, the background script will receive the message along with the WebExtension `Tab` object belonging to the sender; * The background script's message handler merges the tab into the arguments to the API as a `tab` property; * The background script then calls the API and the result is returned to the harness using the native port, just like the normal implementation; * We add a `GetPidForTab` API for resolving top-level PIDs from a webext tab id. Differential Revision: https://phabricator.services.mozilla.com/D73723 --- .../web_extensions/test-support/background.js | 12 ++++++++ .../web_extensions/test-support/test-api.js | 9 ++++++ .../test-support/test-schema.json | 12 ++++++++ .../test-support/test-support.js | 19 +++++++++---- .../test/rule/GeckoSessionTestRule.java | 28 +++++++++++++++++-- 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js index 578e451064cb..28f0268666fc 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js @@ -17,6 +17,9 @@ const APIS = { GetLinkColor({ uri, selector }) { return browser.test.getLinkColor(uri, selector); }, + GetPidForTab({ tab }) { + return browser.test.getPidForTab(tab.id); + }, GetPrefs({ prefs }) { return browser.test.getPrefs(prefs); }, @@ -39,6 +42,15 @@ port.onMessage.addListener(async message => { apiCall(message, impl); }); +browser.runtime.onConnect.addListener(contentPort => { + contentPort.onMessage.addListener(message => { + message.args.tab = contentPort.sender.tab; + + const impl = APIS[message.type]; + apiCall(message, impl); + }); +}); + function apiCall(message, impl) { const { id, args } = message; sendResponse(id, impl(args)); diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js index e0e9f49ffff7..7155ae029fed 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js @@ -4,6 +4,9 @@ "use strict"; +const { E10SUtils } = ChromeUtils.import( + "resource://gre/modules/E10SUtils.jsm" +); const { Preferences } = ChromeUtils.import( "resource://gre/modules/Preferences.jsm" ); @@ -114,6 +117,12 @@ this.test = class extends ExtensionAPI { return Services.locale.requestedLocales; }, + async getPidForTab(tabId) { + const tab = context.extension.tabManager.get(tabId); + const pids = E10SUtils.getBrowserPids(tab.browser); + return pids[0]; + }, + async addHistogram(id, value) { return Services.telemetry.getHistogramById(id).add(value); }, diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json index ecb3a6124571..3e7ad703f655 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json @@ -132,6 +132,18 @@ "name": "resolution" } ] + }, + { + "name": "getPidForTab", + "type": "function", + "async": true, + "description": "Gets the top-level pid belonging to tabId.", + "parameters": [ + { + "type": "number", + "name": "tabId" + } + ] } ] } diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js index c68bcd59e2bf..c0f073e81da4 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js @@ -2,13 +2,19 @@ * 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/. */ -let port = null; +let backgroundPort = null; +let nativePort = null; window.addEventListener("pageshow", () => { - port = browser.runtime.connectNative("browser"); + backgroundPort = browser.runtime.connect(); + nativePort = browser.runtime.connectNative("browser"); - port.onMessage.addListener(message => { - if (message.eval) { + nativePort.onMessage.addListener(message => { + if (message.type) { + // This is a session-specific webExtensionApiCall. + // Forward to the background script. + backgroundPort.postMessage(message); + } else if (message.eval) { try { // Using eval here is the whole point of this WebExtension so we can // safely ignore the eslint warning. @@ -28,7 +34,7 @@ window.addEventListener("pageshow", () => { } function sendSyncResponse(id, response, exception) { - port.postMessage({ + nativePort.postMessage({ id, response: JSON.stringify(response), exception: exception && exception.toString(), @@ -37,5 +43,6 @@ window.addEventListener("pageshow", () => { }); window.addEventListener("pagehide", () => { - port.disconnect(); + backgroundPort.disconnect(); + nativePort.disconnect(); }); diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java index 30822c93f0ee..1d2af3cdbaec 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java @@ -1877,6 +1877,11 @@ public class GeckoSessionTestRule implements TestRule { return waitForMessage(id); } + public int getSessionPid(final @NonNull GeckoSession session) { + final Double dblPid = (Double) webExtensionApiCall(session, "GetPidForTab", null); + return dblPid.intValue(); + } + private Object waitForMessage(String id) { UiThreadUtils.waitForCondition(() -> mPendingMessages.containsKey(id), mTimeoutMillis); @@ -2107,11 +2112,22 @@ public class GeckoSessionTestRule implements TestRule { }); } - private Object webExtensionApiCall(final String apiName, SetArgs argsSetter) { + private Object webExtensionApiCall(final @NonNull String apiName, final @NonNull SetArgs argsSetter) { + return webExtensionApiCall(null, apiName, argsSetter); + } + + private Object webExtensionApiCall(final GeckoSession session, final @NonNull String apiName, + final @NonNull SetArgs argsSetter) { // Ensure background script is connected UiThreadUtils.waitForCondition(() -> RuntimeCreator.backgroundPort() != null, mTimeoutMillis); + if (session != null) { + // Ensure content script is connected + UiThreadUtils.waitForCondition(() -> mPorts.get(session) != null, + mTimeoutMillis); + } + final String id = UUID.randomUUID().toString(); final JSONObject message = new JSONObject(); @@ -2129,7 +2145,15 @@ public class GeckoSessionTestRule implements TestRule { throw new RuntimeException(ex); } - RuntimeCreator.backgroundPort().postMessage(message); + if (session == null) { + RuntimeCreator.backgroundPort().postMessage(message); + } else { + // We post the message using session's port instead of the background port. By routing + // the message through the extension's content script, we are able to obtain and attach + // the session's WebExtension tab as a `tab` argument to the API. + mPorts.get(session).postMessage(message); + } + return waitForMessage(id); }