AppUiTestDelegate.sys.mjs (4129B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const lazy = {}; 6 7 ChromeUtils.defineESModuleGetters(lazy, { 8 EventDispatcher: "resource://gre/modules/Messaging.sys.mjs", 9 GeckoViewTabBridge: "resource://gre/modules/GeckoViewTab.sys.mjs", 10 mobileWindowTracker: "resource://gre/modules/GeckoViewWebExtension.sys.mjs", 11 }); 12 13 const TEST_SUPPORT_EXTENSION_ID = "test-runner-support@tests.mozilla.org"; 14 15 /** 16 * The implementation of AppUiTestDelegate. All implementations need to be kept 17 * in sync. For details, see: 18 * testing/specialpowers/content/AppTestDelegateParent.sys.mjs 19 * 20 * This implementation mostly forwards calls to TestRunnerApiEngine in 21 * mobile/android/test_runner/src/main/java/org/mozilla/geckoview/test_runner/TestRunnerApiEngine.java 22 */ 23 class Delegate { 24 _sendMessageToApp(data) { 25 // "GeckoView:WebExtension:Message" with the "nativeApp" property set is a 26 // message usually emitted by the runtime.sendNativeMessage implementation. 27 // 28 // Although a dummy extension with ID TEST_SUPPORT_EXTENSION_ID is installed 29 // by TestRunnerActivity, the sendNativeMessage API is not used directly. 30 // Instead, we forge a message in the same (internal) format here. 31 // 32 // The message is ultimately received and handled by TestRunnerApiEngine at 33 // mobile/android/test_runner/src/main/java/org/mozilla/geckoview/test_runner/TestRunnerApiEngine.java 34 const message = { 35 type: "GeckoView:WebExtension:Message", 36 sender: { 37 envType: "addon_child", 38 url: "test-runner-support:///", 39 }, 40 data, 41 extensionId: TEST_SUPPORT_EXTENSION_ID, 42 nativeApp: "test-runner-support", 43 }; 44 45 return lazy.EventDispatcher.instance.sendRequestForResult(message); 46 } 47 48 clickPageAction(window, extensionId) { 49 return this._sendMessageToApp({ type: "clickPageAction", extensionId }); 50 } 51 52 clickBrowserAction(window, extensionId) { 53 return this._sendMessageToApp({ type: "clickBrowserAction", extensionId }); 54 } 55 56 closePageAction(window, extensionId) { 57 return this._sendMessageToApp({ type: "closePageAction", extensionId }); 58 } 59 60 closeBrowserAction(window, extensionId) { 61 return this._sendMessageToApp({ type: "closeBrowserAction", extensionId }); 62 } 63 64 awaitExtensionPanel(window, extensionId) { 65 return this._sendMessageToApp({ type: "awaitExtensionPanel", extensionId }); 66 } 67 68 async removeTab(tab) { 69 const window = tab.browser.ownerGlobal; 70 await lazy.GeckoViewTabBridge.closeTab({ 71 window, 72 extensionId: TEST_SUPPORT_EXTENSION_ID, 73 }); 74 } 75 76 async openNewForegroundTab(window, url, waitForLoad = true) { 77 const tab = await lazy.GeckoViewTabBridge.createNewTab({ 78 extensionId: TEST_SUPPORT_EXTENSION_ID, 79 createProperties: { 80 url, 81 active: true, 82 }, 83 }); 84 85 const { browser } = tab; 86 const triggeringPrincipal = 87 Services.scriptSecurityManager.createContentPrincipal( 88 Services.io.newURI(url), 89 {} 90 ); 91 92 browser.fixupAndLoadURIString(url, { 93 loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_NONE, 94 triggeringPrincipal, 95 }); 96 97 const newWindow = browser.ownerGlobal; 98 lazy.mobileWindowTracker.setTabActive(newWindow, true); 99 100 if (!waitForLoad) { 101 return tab; 102 } 103 104 return new Promise(resolve => { 105 const listener = ev => { 106 const { browsingContext, internalURL } = ev.detail; 107 108 // Sometimes we arrive here without an internalURL. If that's the 109 // case, just keep waiting until we get one. 110 if (!internalURL || internalURL == "about:blank") { 111 return; 112 } 113 114 // Ignore subframes 115 if (browsingContext !== browsingContext.top) { 116 return; 117 } 118 119 resolve(tab); 120 browser.removeEventListener("AppTestDelegate:load", listener, true); 121 }; 122 browser.addEventListener("AppTestDelegate:load", listener, true); 123 }); 124 } 125 } 126 127 export var AppUiTestDelegate = new Delegate();