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
This commit is contained in:
Aaron Klotz 2020-05-05 18:43:56 +00:00
parent 2ce561dd99
commit bebfb567e8
5 changed files with 72 additions and 8 deletions

View File

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

View File

@ -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);
},

View File

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

View File

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

View File

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