summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/outOfProcess
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/base/content/test/outOfProcess/browser.ini15
-rw-r--r--browser/base/content/test/outOfProcess/browser_basic_outofprocess.js149
-rw-r--r--browser/base/content/test/outOfProcess/browser_controller.js127
-rw-r--r--browser/base/content/test/outOfProcess/browser_promisefocus.js262
-rw-r--r--browser/base/content/test/outOfProcess/file_base.html5
-rw-r--r--browser/base/content/test/outOfProcess/file_frame1.html5
-rw-r--r--browser/base/content/test/outOfProcess/file_frame2.html11
-rw-r--r--browser/base/content/test/outOfProcess/file_innerframe.html3
-rw-r--r--browser/base/content/test/outOfProcess/head.js85
9 files changed, 662 insertions, 0 deletions
diff --git a/browser/base/content/test/outOfProcess/browser.ini b/browser/base/content/test/outOfProcess/browser.ini
new file mode 100644
index 0000000000..de8ad0cb8b
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/browser.ini
@@ -0,0 +1,15 @@
+[DEFAULT]
+support-files =
+ file_base.html
+ file_frame1.html
+ file_frame2.html
+ file_innerframe.html
+ head.js
+
+[browser_basic_outofprocess.js]
+[browser_controller.js]
+skip-if =
+ os == "linux" && bits == 64 # Bug 1663506
+ os == "mac" && debug # Bug 1663506
+ os == "win" && bits == 64 # Bug 1663506
+[browser_promisefocus.js]
diff --git a/browser/base/content/test/outOfProcess/browser_basic_outofprocess.js b/browser/base/content/test/outOfProcess/browser_basic_outofprocess.js
new file mode 100644
index 0000000000..50914a286c
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/browser_basic_outofprocess.js
@@ -0,0 +1,149 @@
+/**
+ * Verify that the colors were set properly. This has the effect of
+ * verifying that the processes are assigned for child frames correctly.
+ */
+async function verifyBaseFrameStructure(
+ browsingContexts,
+ testname,
+ expectedHTML
+) {
+ function checkColorAndText(bc, desc, expectedColor, expectedText) {
+ return SpecialPowers.spawn(
+ bc,
+ [expectedColor, expectedText, desc],
+ (expectedColorChild, expectedTextChild, descChild) => {
+ Assert.equal(
+ content.document.documentElement.style.backgroundColor,
+ expectedColorChild,
+ descChild + " color"
+ );
+ Assert.equal(
+ content.document.getElementById("insertPoint").innerHTML,
+ expectedTextChild,
+ descChild + " text"
+ );
+ }
+ );
+ }
+
+ let useOOPFrames = gFissionBrowser;
+
+ is(
+ browsingContexts.length,
+ TOTAL_FRAME_COUNT,
+ "correct number of browsing contexts"
+ );
+ await checkColorAndText(
+ browsingContexts[0],
+ testname + " base",
+ "white",
+ expectedHTML.next().value
+ );
+ await checkColorAndText(
+ browsingContexts[1],
+ testname + " frame 1",
+ useOOPFrames ? "seashell" : "white",
+ expectedHTML.next().value
+ );
+ await checkColorAndText(
+ browsingContexts[2],
+ testname + " frame 1-1",
+ useOOPFrames ? "seashell" : "white",
+ expectedHTML.next().value
+ );
+ await checkColorAndText(
+ browsingContexts[3],
+ testname + " frame 2",
+ useOOPFrames ? "lightcyan" : "white",
+ expectedHTML.next().value
+ );
+ await checkColorAndText(
+ browsingContexts[4],
+ testname + " frame 2-1",
+ useOOPFrames ? "seashell" : "white",
+ expectedHTML.next().value
+ );
+ await checkColorAndText(
+ browsingContexts[5],
+ testname + " frame 2-2",
+ useOOPFrames ? "lightcyan" : "white",
+ expectedHTML.next().value
+ );
+ await checkColorAndText(
+ browsingContexts[6],
+ testname + " frame 2-3",
+ useOOPFrames ? "palegreen" : "white",
+ expectedHTML.next().value
+ );
+ await checkColorAndText(
+ browsingContexts[7],
+ testname + " frame 2-4",
+ "white",
+ expectedHTML.next().value
+ );
+}
+
+/**
+ * Test setting up all of the frames where a string of markup is passed
+ * to initChildFrames.
+ */
+add_task(async function test_subframes_string() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ OOP_BASE_PAGE_URI
+ );
+
+ const markup = "<p>Text</p>";
+
+ let browser = tab.linkedBrowser;
+ let browsingContexts = await initChildFrames(browser, markup);
+
+ function* getExpectedHTML() {
+ for (let c = 1; c <= TOTAL_FRAME_COUNT; c++) {
+ yield markup;
+ }
+ ok(false, "Frame count does not match actual number of frames");
+ }
+ await verifyBaseFrameStructure(browsingContexts, "string", getExpectedHTML());
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+/**
+ * Test setting up all of the frames where a function that returns different markup
+ * is passed to initChildFrames.
+ */
+add_task(async function test_subframes_function() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ OOP_BASE_PAGE_URI
+ );
+ let browser = tab.linkedBrowser;
+
+ let counter = 0;
+ let browsingContexts = await initChildFrames(
+ browser,
+ function (browsingContext) {
+ return "<p>Text " + ++counter + "</p>";
+ }
+ );
+
+ is(
+ counter,
+ TOTAL_FRAME_COUNT,
+ "insert HTML function called the correct number of times"
+ );
+
+ function* getExpectedHTML() {
+ for (let c = 1; c <= TOTAL_FRAME_COUNT; c++) {
+ yield "<p>Text " + c + "</p>";
+ }
+ }
+ await verifyBaseFrameStructure(
+ browsingContexts,
+ "function",
+ getExpectedHTML()
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/outOfProcess/browser_controller.js b/browser/base/content/test/outOfProcess/browser_controller.js
new file mode 100644
index 0000000000..f9d9ca8c93
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/browser_controller.js
@@ -0,0 +1,127 @@
+function checkCommandState(testid, undoEnabled, copyEnabled, deleteEnabled) {
+ is(
+ !document.getElementById("cmd_undo").hasAttribute("disabled"),
+ undoEnabled,
+ testid + " undo"
+ );
+ is(
+ !document.getElementById("cmd_copy").hasAttribute("disabled"),
+ copyEnabled,
+ testid + " copy"
+ );
+ is(
+ !document.getElementById("cmd_delete").hasAttribute("disabled"),
+ deleteEnabled,
+ testid + " delete"
+ );
+}
+
+function keyAndUpdate(key, eventDetails, updateEventsCount) {
+ let updatePromise = BrowserTestUtils.waitForEvent(
+ window,
+ "commandupdate",
+ false,
+ () => {
+ return --updateEventsCount == 0;
+ }
+ );
+ EventUtils.synthesizeKey(key, eventDetails);
+ return updatePromise;
+}
+
+add_task(async function test_controllers_subframes() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ OOP_BASE_PAGE_URI
+ );
+ let browser = tab.linkedBrowser;
+ let browsingContexts = await initChildFrames(
+ browser,
+ "<input id='input'><br><br>"
+ );
+
+ gURLBar.focus();
+
+ for (let stepNum = 0; stepNum < browsingContexts.length; stepNum++) {
+ await keyAndUpdate(stepNum > 0 ? "VK_TAB" : "VK_F6", {}, 6);
+
+ // Since focus may be switching into a separate process here,
+ // need to wait for the focus to have been updated.
+ await SpecialPowers.spawn(browsingContexts[stepNum], [], () => {
+ return ContentTaskUtils.waitForCondition(
+ () => content.browsingContext.isActive && content.document.hasFocus()
+ );
+ });
+
+ // Force the UI to update on platforms that don't
+ // normally do so until menus are opened.
+ if (AppConstants.platform != "macosx") {
+ goUpdateGlobalEditMenuItems(true);
+ }
+
+ await SpecialPowers.spawn(browsingContexts[stepNum], [], () => {
+ // Both the tab key and document navigation with F6 will focus
+ // the root of the document within the frame.
+ let document = content.document;
+ Assert.equal(
+ document.activeElement,
+ document.documentElement,
+ "root focused"
+ );
+ });
+ // XXX Currently, Copy is always enabled when the root (not an editor element)
+ // is focused. Possibly that should only be true if a listener is present?
+ checkCommandState("step " + stepNum + " root focused", false, true, false);
+
+ // Tab to the textbox.
+ await keyAndUpdate("VK_TAB", {}, 1);
+
+ if (AppConstants.platform != "macosx") {
+ goUpdateGlobalEditMenuItems(true);
+ }
+
+ await SpecialPowers.spawn(browsingContexts[stepNum], [], () => {
+ Assert.equal(
+ content.document.activeElement,
+ content.document.getElementById("input"),
+ "input focused"
+ );
+ });
+ checkCommandState(
+ "step " + stepNum + " input focused",
+ false,
+ false,
+ false
+ );
+
+ // Type into the textbox.
+ await keyAndUpdate("a", {}, 1);
+ checkCommandState("step " + stepNum + " typed", true, false, false);
+
+ await SpecialPowers.spawn(browsingContexts[stepNum], [], () => {
+ Assert.equal(
+ content.document.activeElement,
+ content.document.getElementById("input"),
+ "input focused"
+ );
+ });
+
+ // Select all text; this causes the Copy and Delete commands to be enabled.
+ await keyAndUpdate("a", { accelKey: true }, 1);
+ if (AppConstants.platform != "macosx") {
+ goUpdateGlobalEditMenuItems(true);
+ }
+
+ checkCommandState("step " + stepNum + " selected", true, true, true);
+
+ // Now make sure that the text is selected.
+ await SpecialPowers.spawn(browsingContexts[stepNum], [], () => {
+ let input = content.document.getElementById("input");
+ Assert.equal(input.value, "a", "text matches");
+ Assert.equal(input.selectionStart, 0, "selectionStart matches");
+ Assert.equal(input.selectionEnd, 1, "selectionEnd matches");
+ });
+ }
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/outOfProcess/browser_promisefocus.js b/browser/base/content/test/outOfProcess/browser_promisefocus.js
new file mode 100644
index 0000000000..9018c0f0ae
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/browser_promisefocus.js
@@ -0,0 +1,262 @@
+// Opens another window and switches focus between them.
+add_task(async function test_window_focus() {
+ let window2 = await BrowserTestUtils.openNewBrowserWindow();
+ ok(!document.hasFocus(), "hasFocus after open second window");
+ ok(window2.document.hasFocus(), "hasFocus after open second window");
+ is(
+ Services.focus.activeWindow,
+ window2,
+ "activeWindow after open second window"
+ );
+ is(
+ Services.focus.focusedWindow,
+ window2,
+ "focusedWindow after open second window"
+ );
+
+ await SimpleTest.promiseFocus(window);
+ ok(document.hasFocus(), "hasFocus after promiseFocus on window");
+ ok(!window2.document.hasFocus(), "hasFocus after promiseFocus on window");
+ is(
+ Services.focus.activeWindow,
+ window,
+ "activeWindow after promiseFocus on window"
+ );
+ is(
+ Services.focus.focusedWindow,
+ window,
+ "focusedWindow after promiseFocus on window"
+ );
+
+ await SimpleTest.promiseFocus(window2);
+ ok(!document.hasFocus(), "hasFocus after promiseFocus on second window");
+ ok(
+ window2.document.hasFocus(),
+ "hasFocus after promiseFocus on second window"
+ );
+ is(
+ Services.focus.activeWindow,
+ window2,
+ "activeWindow after promiseFocus on second window"
+ );
+ is(
+ Services.focus.focusedWindow,
+ window2,
+ "focusedWindow after promiseFocus on second window"
+ );
+
+ await BrowserTestUtils.closeWindow(window2);
+
+ // If the window is already focused, this should just return.
+ await SimpleTest.promiseFocus(window);
+ await SimpleTest.promiseFocus(window);
+});
+
+// Opens two tabs and ensures that focus can be switched to the browser.
+add_task(async function test_tab_focus() {
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,<input>"
+ );
+
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,<input>"
+ );
+
+ gURLBar.focus();
+
+ await SimpleTest.promiseFocus(tab2.linkedBrowser);
+ is(
+ document.activeElement,
+ tab2.linkedBrowser,
+ "Browser is focused after promiseFocus"
+ );
+
+ await SpecialPowers.spawn(tab1.linkedBrowser, [], () => {
+ Assert.equal(
+ Services.focus.activeBrowsingContext,
+ null,
+ "activeBrowsingContext in child process in hidden tab"
+ );
+ Assert.equal(
+ Services.focus.focusedWindow,
+ null,
+ "focusedWindow in child process in hidden tab"
+ );
+ Assert.ok(
+ !content.document.hasFocus(),
+ "hasFocus in child process in hidden tab"
+ );
+ });
+
+ await SpecialPowers.spawn(tab2.linkedBrowser, [], () => {
+ Assert.equal(
+ Services.focus.activeBrowsingContext,
+ content.browsingContext,
+ "activeBrowsingContext in child process in visible tab"
+ );
+ Assert.equal(
+ Services.focus.focusedWindow,
+ content.window,
+ "focusedWindow in child process in visible tab"
+ );
+ Assert.ok(
+ content.document.hasFocus(),
+ "hasFocus in child process in visible tab"
+ );
+ });
+
+ BrowserTestUtils.removeTab(tab1);
+ BrowserTestUtils.removeTab(tab2);
+});
+
+// Opens a document with a nested hierarchy of frames using initChildFrames and
+// focuses each child iframe in turn.
+add_task(async function test_subframes_focus() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ OOP_BASE_PAGE_URI
+ );
+
+ const markup = "<input>";
+
+ let browser = tab.linkedBrowser;
+ let browsingContexts = await initChildFrames(browser, markup);
+
+ for (let blurSubframe of [true, false]) {
+ for (let index = browsingContexts.length - 1; index >= 0; index--) {
+ let bc = browsingContexts[index];
+
+ // Focus each browsing context in turn. Do this twice, once when the window
+ // is not already focused, and once when it is already focused.
+ for (let step = 0; step < 2; step++) {
+ let desc =
+ "within child frame " +
+ index +
+ " step " +
+ step +
+ " blur subframe " +
+ blurSubframe +
+ " ";
+
+ info(desc + "start");
+ await SimpleTest.promiseFocus(bc, false, blurSubframe);
+
+ let expectedFocusedBC = bc;
+ // Becuase we are iterating backwards through the iframes, when we get to a frame
+ // that contains the iframe we just tested, focusing it will keep the child
+ // iframe focused as well, so we need to account for this when verifying which
+ // child iframe is focused. For the root frame (index 0), the iframe nested
+ // two items down will actually be focused.
+ // If blurSubframe is true however, the iframe focus in the parent will be cleared,
+ // so the focused window should be the parent instead.
+ if (!blurSubframe) {
+ if (index == 0) {
+ expectedFocusedBC = browsingContexts[index + 2];
+ } else if (index == 3 || index == 1) {
+ expectedFocusedBC = browsingContexts[index + 1];
+ }
+ }
+ is(
+ Services.focus.focusedContentBrowsingContext,
+ expectedFocusedBC,
+ desc +
+ " focusedContentBrowsingContext" +
+ ":: " +
+ Services.focus.focusedContentBrowsingContext?.id +
+ "," +
+ expectedFocusedBC?.id
+ );
+
+ // If the processes don't match, then the child iframe is an out-of-process iframe.
+ let oop =
+ expectedFocusedBC.currentWindowGlobal.osPid !=
+ bc.currentWindowGlobal.osPid;
+ await SpecialPowers.spawn(
+ bc,
+ [
+ index,
+ desc,
+ expectedFocusedBC != bc ? expectedFocusedBC : null,
+ oop,
+ ],
+ (num, descChild, childBC, isOop) => {
+ Assert.equal(
+ Services.focus.activeBrowsingContext,
+ content.browsingContext.top,
+ descChild + "activeBrowsingContext"
+ );
+ Assert.ok(
+ content.document.hasFocus(),
+ descChild + "hasFocus: " + content.browsingContext.id
+ );
+
+ // If a child browsing context is expected to be focused, the focusedWindow
+ // should be set to that instead and the active element should be an iframe.
+ // Otherwise, the focused window should be this window, and the active
+ // element should be the document's body element.
+ if (childBC) {
+ // The frame structure is:
+ // A1
+ // -> B
+ // -> A2
+ // where A and B are two processes. The frame A2 starts out focused. When B is
+ // focused, A1's focus is updated correctly.
+
+ // In Fission mode, childBC.window returns a non-null proxy even if OOP
+ if (isOop) {
+ Assert.equal(
+ Services.focus.focusedWindow,
+ null,
+ descChild + "focusedWindow"
+ );
+ Assert.ok(!childBC.docShell, descChild + "childBC.docShell");
+ } else {
+ Assert.equal(
+ Services.focus.focusedWindow,
+ childBC.window,
+ descChild + "focusedWindow"
+ );
+ }
+ Assert.equal(
+ content.document.activeElement.localName,
+ "iframe",
+ descChild + "activeElement"
+ );
+ } else {
+ Assert.equal(
+ Services.focus.focusedWindow,
+ content.window,
+ descChild + "focusedWindow"
+ );
+ Assert.equal(
+ content.document.activeElement,
+ content.document.body,
+ descChild + "activeElement"
+ );
+ }
+ }
+ );
+ }
+ }
+ }
+
+ // Focus the top window without blurring the browser.
+ await SimpleTest.promiseFocus(window, false, false);
+ is(
+ document.activeElement.localName,
+ "browser",
+ "focus after blurring browser blur subframe false"
+ );
+
+ // Now, focus the top window, blurring the browser.
+ await SimpleTest.promiseFocus(window, false, true);
+ is(
+ document.activeElement,
+ document.body,
+ "focus after blurring browser blur subframe true"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/outOfProcess/file_base.html b/browser/base/content/test/outOfProcess/file_base.html
new file mode 100644
index 0000000000..03f0731a8e
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/file_base.html
@@ -0,0 +1,5 @@
+<html><body>
+<div id="insertPoint"></div>
+<iframe src="https://www.mozilla.org:443/browser/browser/base/content/test/outOfProcess/file_frame1.html" width="320" height="700" style="border: 1px solid black;"></iframe></body>
+<iframe src="https://test1.example.org:443/browser/browser/base/content/test/outOfProcess/file_frame2.html" width="320" height="700" style="border: 1px solid black;"></iframe></body>
+</html>
diff --git a/browser/base/content/test/outOfProcess/file_frame1.html b/browser/base/content/test/outOfProcess/file_frame1.html
new file mode 100644
index 0000000000..d39e970c0f
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/file_frame1.html
@@ -0,0 +1,5 @@
+<html><body>
+<div id="insertPoint"></div>
+Same domain:<br>
+<iframe src="file_innerframe.html" width="300" height="100" style="border: 1px solid black;"></iframe></body>
+</html>
diff --git a/browser/base/content/test/outOfProcess/file_frame2.html b/browser/base/content/test/outOfProcess/file_frame2.html
new file mode 100644
index 0000000000..f0bc91ba20
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/file_frame2.html
@@ -0,0 +1,11 @@
+<html><body>
+<div id="insertPoint"></div>
+Same domain as to the left:<br>
+<iframe src="https://www.mozilla.org:443/browser/browser/base/content/test/outOfProcess/file_innerframe.html" width="300" height="100" style="border: 1px solid black;"></iframe></body>
+Same domain as parent:<br>
+<iframe src="https://test1.example.org:443/browser/browser/base/content/test/outOfProcess/file_innerframe.html" width="300" height="100" style="border: 1px solid black;"></iframe></body>
+Different domain:<br>
+<iframe src="https://w3c-test.org:443/browser/browser/base/content/test/outOfProcess/file_innerframe.html" width="300" height="100" style="border: 1px solid black;"></iframe></body>
+Same as top-level domain:<br>
+<iframe src="https://example.com/browser/browser/base/content/test/outOfProcess/file_innerframe.html" width="300" height="100" style="border: 1px solid black;"></iframe></body>
+</html>
diff --git a/browser/base/content/test/outOfProcess/file_innerframe.html b/browser/base/content/test/outOfProcess/file_innerframe.html
new file mode 100644
index 0000000000..23c516232c
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/file_innerframe.html
@@ -0,0 +1,3 @@
+<html><body>
+<div id="insertPoint"></div>
+</html>
diff --git a/browser/base/content/test/outOfProcess/head.js b/browser/base/content/test/outOfProcess/head.js
new file mode 100644
index 0000000000..230e2e2cbc
--- /dev/null
+++ b/browser/base/content/test/outOfProcess/head.js
@@ -0,0 +1,85 @@
+const OOP_BASE_PAGE_URI =
+ "https://example.com/browser/browser/base/content/test/outOfProcess/file_base.html";
+
+// The number of frames and subframes that exist for the basic OOP test. If frames are
+// modified within file_base.html, update this value.
+const TOTAL_FRAME_COUNT = 8;
+
+// The frames are assigned different colors based on their process ids. If you add a
+// frame you might need to add more colors to this list.
+const FRAME_COLORS = ["white", "seashell", "lightcyan", "palegreen"];
+
+/**
+ * Set up a set of child frames for the given browser for testing
+ * out of process frames. 'OOP_BASE_PAGE_URI' is the base page and subframes
+ * contain pages from the same or other domains.
+ *
+ * @param browser browser containing frame hierarchy to set up
+ * @param insertHTML HTML or function that returns what to insert into each frame
+ * @returns array of all browsing contexts in depth-first order
+ *
+ * This function adds a browsing context and process id label to each
+ * child subframe. It also sets the background color of each frame to
+ * different colors based on the process id. The browser_basic_outofprocess.js
+ * test verifies these colors to ensure that the frame/process hierarchy
+ * has been set up as expected. Colors are used to help people visualize
+ * the process setup.
+ *
+ * The insertHTML argument may be either a fixed string of HTML to insert
+ * into each subframe, or a function that returns the string to insert. The
+ * function takes one argument, the browsing context being processed.
+ */
+async function initChildFrames(browser, insertHTML) {
+ let colors = FRAME_COLORS.slice();
+ let colorMap = new Map();
+
+ let browsingContexts = [];
+
+ async function processBC(bc) {
+ browsingContexts.push(bc);
+
+ let pid = bc.currentWindowGlobal.osPid;
+ let ident = "BrowsingContext: " + bc.id + "\nProcess: " + pid;
+
+ let color = colorMap.get(pid);
+ if (!color) {
+ if (!colors.length) {
+ ok(false, "ran out of available colors");
+ }
+
+ color = colors.shift();
+ colorMap.set(pid, color);
+ }
+
+ let insertHTMLString = insertHTML;
+ if (typeof insertHTML == "function") {
+ insertHTMLString = insertHTML(bc);
+ }
+
+ await SpecialPowers.spawn(
+ bc,
+ [ident, color, insertHTMLString],
+ (identChild, colorChild, insertHTMLChild) => {
+ let root = content.document.documentElement;
+ root.style = "background-color: " + colorChild;
+
+ let pre = content.document.createElement("pre");
+ pre.textContent = identChild;
+ root.insertBefore(pre, root.firstChild);
+
+ if (insertHTMLChild) {
+ // eslint-disable-next-line no-unsanitized/property
+ content.document.getElementById("insertPoint").innerHTML =
+ insertHTMLChild;
+ }
+ }
+ );
+
+ for (let childBC of bc.children) {
+ await processBC(childBC);
+ }
+ }
+ await processBC(browser.browsingContext);
+
+ return browsingContexts;
+}