tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 50c4de911c377283c861efd035067bd5e313e6bc
parent 7a607605563fcfb274fc87e56ff933d9308a601b
Author: Henrik Skupin <mail@hskupin.info>
Date:   Tue, 16 Dec 2025 12:03:47 +0000

Bug 1851788 - [webdriver-bidi] Enhance "browsingContext.getTree" command for chrome scope when root parameter is set. r=Sasha,jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D275812

Diffstat:
Mremote/webdriver-bidi/modules/RootBiDiModule.sys.mjs | 23++++++++++++++---------
Mremote/webdriver-bidi/modules/root/browsingContext.sys.mjs | 38+++++++++++++++++++++++++-------------
Mtesting/web-platform/mozilla/meta/webdriver/bidi/browsing_context/get_tree/moz_scope.py.ini | 4++++
Atesting/web-platform/mozilla/meta/webdriver/bidi/browsing_context/get_tree/root.py.ini | 4++++
Atesting/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/conftest.py | 9+++++++++
Atesting/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/invalid.py | 12++++++++++++
Mtesting/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/moz_scope.py | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Atesting/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/root.py | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 229 insertions(+), 53 deletions(-)

diff --git a/remote/webdriver-bidi/modules/RootBiDiModule.sys.mjs b/remote/webdriver-bidi/modules/RootBiDiModule.sys.mjs @@ -7,9 +7,8 @@ import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs", error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", - MozContextScope: - "chrome://remote/content/webdriver-bidi/modules/root/browsingContext.sys.mjs", NavigableManager: "chrome://remote/content/shared/NavigableManager.sys.mjs", WindowGlobalMessageHandler: "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs", @@ -47,8 +46,9 @@ export class RootBiDiModule extends Module { * @param {string} navigableId * Unique id of the browsing context. * @param {object=} options - * @param {MozContextScope=} options.scope - * Scope of the browsing context. Defaults to "content". + * @param {boolean=} options.supportsChromeScope + * If set to `true` chrome browsing contexts are supported + * for the BiDi command. Defaults to `false`. * * @returns {BrowsingContext|null} * The browsing context, or null if `navigableId` is null. @@ -57,7 +57,7 @@ export class RootBiDiModule extends Module { * If the browsing context cannot be found. */ _getNavigable(navigableId, options = {}) { - const { scope = lazy.MozContextScope.CONTENT } = options; + const { supportsChromeScope = false } = options; if (navigableId === null) { // The WebDriver BiDi specification expects `null` to be @@ -65,11 +65,16 @@ export class RootBiDiModule extends Module { return null; } - let context = lazy.NavigableManager.getBrowsingContextById(navigableId); + const context = lazy.NavigableManager.getBrowsingContextById(navigableId); - // Only allow to retrieve contexts for chrome windows if explicitly allowed. - if (context && !context.isContent && scope != lazy.MozContextScope.CHROME) { - context = null; + if (context && !context.isContent) { + lazy.assert.hasSystemAccess(); + + if (!supportsChromeScope) { + throw new lazy.error.UnsupportedOperationError( + "The command does not support browsing contexts in privileged (chrome) scope" + ); + } } if (context === null) { diff --git a/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs b/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs @@ -842,7 +842,8 @@ class BrowsingContextModule extends RootBiDiModule { * @param {string=} options.root * Id of the root browsing context. * @param {MozContextScope=} options."moz:scope" - * The scope to retrieve browsing contexts from. + * The scope from which browsing contexts are retrieved. This + * parameter cannot be used when a root browsing context is specified. * * @returns {BrowsingContextGetTreeResult} * Tree of browsing context information. @@ -853,7 +854,7 @@ class BrowsingContextModule extends RootBiDiModule { const { maxDepth = null, root: rootId = null, - "moz:scope": scope = MozContextScope.CONTENT, + "moz:scope": scope = null, } = options; if (maxDepth !== null) { @@ -863,16 +864,18 @@ class BrowsingContextModule extends RootBiDiModule { ); } - const contextScopes = Object.values(MozContextScope); - lazy.assert.that( - _scope => contextScopes.includes(_scope), - `Expected "moz:scope" to be one of ${contextScopes}, ` + - lazy.pprint`got ${scope}` - )(scope); - - if (scope != MozContextScope.CONTENT) { - // By default only content browsing contexts are allowed. - lazy.assert.hasSystemAccess(); + if (scope !== null) { + const contextScopes = Object.values(MozContextScope); + lazy.assert.that( + _scope => contextScopes.includes(_scope), + `Expected "moz:scope" to be one of ${contextScopes}, ` + + lazy.pprint`got ${scope}` + )(scope); + + if (scope != MozContextScope.CONTENT) { + // By default only content browsing contexts are allowed. + lazy.assert.hasSystemAccess(); + } } let contexts; @@ -884,7 +887,15 @@ class BrowsingContextModule extends RootBiDiModule { lazy.pprint`Expected "root" to be a string, got ${rootId}` ); - contexts = [this._getNavigable(rootId, { scope })]; + if (scope) { + // At the moment we only allow to set a specific scope + // when querying at the top-level. + throw new lazy.error.InvalidArgumentError( + `"root" and "moz:scope" are mutual exclusive` + ); + } + + contexts = [this._getNavigable(rootId, { supportsChromeScope: true })]; } else { switch (scope) { case MozContextScope.CHROME: { @@ -2585,6 +2596,7 @@ class BrowsingContextModule extends RootBiDiModule { * @param {number=} options.maxDepth * Depth of the browsing context tree to traverse. If not specified * the whole tree is returned. + * * @returns {BrowsingContextInfo} * The information about the browsing context. */ diff --git a/testing/web-platform/mozilla/meta/webdriver/bidi/browsing_context/get_tree/moz_scope.py.ini b/testing/web-platform/mozilla/meta/webdriver/bidi/browsing_context/get_tree/moz_scope.py.ini @@ -11,3 +11,7 @@ [test_custom_chrome_window_with_iframes] disabled: if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1762066 + + [test_child_context_without_chrome_scope] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1762066 diff --git a/testing/web-platform/mozilla/meta/webdriver/bidi/browsing_context/get_tree/root.py.ini b/testing/web-platform/mozilla/meta/webdriver/bidi/browsing_context/get_tree/root.py.ini @@ -0,0 +1,4 @@ +[root.py] + [test_custom_chrome_window] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1762066 diff --git a/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/conftest.py b/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/conftest.py @@ -0,0 +1,9 @@ +import pytest + + +@pytest.fixture +def browser_chrome_url(current_session): + if current_session.capabilities["platformName"] == "android": + return "chrome://geckoview/content/geckoview.xhtml" + else: + return "chrome://browser/content/browser.xhtml" diff --git a/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/invalid.py b/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/invalid.py @@ -0,0 +1,12 @@ +import pytest +from webdriver.bidi import error + +pytestmark = pytest.mark.asyncio + + +@pytest.mark.allow_system_access +async def test_mutual_exclusive_with_scope(bidi_session, top_context): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.browsing_context.get_tree( + root=top_context["context"], _extension_params={"moz:scope": "chrome"} + ) diff --git a/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/moz_scope.py b/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/moz_scope.py @@ -5,14 +5,6 @@ from webdriver.bidi import error pytestmark = pytest.mark.asyncio -@pytest.fixture -def browser_chrome_url(current_session): - if current_session.capabilities["platformName"] == "android": - return "chrome://geckoview/content/geckoview.xhtml" - else: - return "chrome://browser/content/browser.xhtml" - - async def test_without_system_access(bidi_session): with pytest.raises(error.UnsupportedOperationException): await bidi_session.browsing_context.get_tree( @@ -74,26 +66,30 @@ async def test_custom_chrome_window_without_iframes( # Retrieve all browsing contexts for the custom chrome window parent_contexts = await bidi_session.browsing_context.get_tree( - root=new_window.id, _extension_params={"moz:scope": "chrome"} + max_depth=None, _extension_params={"moz:scope": "chrome"} ) - assert len(parent_contexts) == 1 + assert len(parent_contexts) == 2 + + filtered_contexts = [ + context for context in parent_contexts if context["context"] == new_window.id + ] + + assert len(filtered_contexts) == 1 assert_browsing_context( - parent_contexts[0], - None, + filtered_contexts[0], + new_window.id, children=0, parent=None, url=chrome_url, client_window=None, ) - assert len(parent_contexts) == 1 + assert filtered_contexts[0]["clientWindow"] != top_context["clientWindow"] - assert parent_contexts[0]["clientWindow"] != top_context["clientWindow"] - - assert parent_contexts[0]["moz:scope"] == "chrome" - assert parent_contexts[0]["moz:name"] == "null" + assert filtered_contexts[0]["moz:scope"] == "chrome" + assert filtered_contexts[0]["moz:name"] == "null" @pytest.mark.allow_system_access @@ -114,23 +110,31 @@ async def test_custom_chrome_window_with_iframes( # Retrieve all browsing contexts for the custom chrome window parent_contexts = await bidi_session.browsing_context.get_tree( - root=new_window.id, _extension_params={"moz:scope": "chrome"} + _extension_params={"moz:scope": "chrome"} ) + assert len(parent_contexts) == 2 + + filtered_contexts = [ + context for context in parent_contexts if context["context"] == new_window.id + ] + + assert len(filtered_contexts) == 1 + assert_browsing_context( - parent_contexts[0], - None, + filtered_contexts[0], + new_window.id, children=2, parent=None, url=chrome_url, client_window=None, ) - assert parent_contexts[0]["clientWindow"] != top_context["clientWindow"] + assert filtered_contexts[0]["clientWindow"] != top_context["clientWindow"] - assert parent_contexts[0]["moz:scope"] == "chrome" - assert parent_contexts[0]["moz:name"] == "null" + assert filtered_contexts[0]["moz:scope"] == "chrome" + assert filtered_contexts[0]["moz:name"] == "null" - iframes = parent_contexts[0]["children"] + iframes = filtered_contexts[0]["children"] # First iframe has no children assert_browsing_context( @@ -139,9 +143,9 @@ async def test_custom_chrome_window_with_iframes( children=0, parent_expected=False, url=iframe_url, - client_window=parent_contexts[0]["clientWindow"], + client_window=filtered_contexts[0]["clientWindow"], ) - assert iframes[0]["context"] != parent_contexts[0]["context"] + assert iframes[0]["context"] != filtered_contexts[0]["context"] assert iframes[0]["moz:scope"] == "chrome" assert iframes[0]["moz:name"] == "iframe" @@ -153,9 +157,9 @@ async def test_custom_chrome_window_with_iframes( children=1, parent_expected=False, url=nested_iframe_url, - client_window=parent_contexts[0]["clientWindow"], + client_window=filtered_contexts[0]["clientWindow"], ) - assert iframes[1]["context"] != parent_contexts[0]["context"] + assert iframes[1]["context"] != filtered_contexts[0]["context"] assert iframes[1]["context"] != iframes[0]["context"] assert iframes[1]["moz:scope"] == "chrome" @@ -170,12 +174,58 @@ async def test_custom_chrome_window_with_iframes( children=0, parent_expected=False, url=iframe_url, - client_window=parent_contexts[0]["clientWindow"], + client_window=filtered_contexts[0]["clientWindow"], ) - assert iframes[1]["context"] != parent_contexts[0]["context"] + assert iframes[1]["context"] != filtered_contexts[0]["context"] assert iframes[1]["context"] != iframes[0]["context"] assert nested_iframes[0]["context"] != iframes[1]["context"] - assert nested_iframes[0]["context"] != parent_contexts[0]["context"] + assert nested_iframes[0]["context"] != filtered_contexts[0]["context"] assert nested_iframes[0]["moz:scope"] == "chrome" assert nested_iframes[0]["moz:name"] == "iframe" + + +@pytest.mark.allow_system_access +async def test_child_context_without_chrome_scope( + bidi_session, default_chrome_handler, new_chrome_window +): + # Bug 1762066: Skip this test on Android, as it currently causes the browser to crash + # when attempting to register a chrome handler for files that cannot be found. + # Since we cannot disable the test via the manifest, we return early instead. + if bidi_session.capabilities["platformName"] == "android": + return + + chrome_url = f"{default_chrome_handler}test.xhtml" + iframe_url = f"{default_chrome_handler}test_iframe.xhtml" + + new_window = new_chrome_window(chrome_url) + + # Retrieve all browsing contexts for the custom chrome window + top_level_contexts = await bidi_session.browsing_context.get_tree( + root=new_window.id, max_depth=1 + ) + + assert len(top_level_contexts) == 1 + + assert_browsing_context( + top_level_contexts[0], + new_window.id, + children=2, + parent=None, + url=chrome_url, + client_window=None, + ) + + iframes = top_level_contexts[0]["children"] + + assert len(iframes) == 2 + + # Check that child chrome browsing context are still returned + assert_browsing_context( + iframes[0], + None, + children=None, + parent_expected=False, + url=iframe_url, + client_window=top_level_contexts[0]["clientWindow"], + ) diff --git a/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/root.py b/testing/web-platform/mozilla/tests/webdriver/bidi/browsing_context/get_tree/root.py @@ -0,0 +1,80 @@ +import pytest +from tests.bidi.browsing_context import assert_browsing_context + +pytestmark = pytest.mark.asyncio + + +@pytest.mark.allow_system_access +async def test_custom_chrome_window( + bidi_session, default_chrome_handler, new_chrome_window +): + # Bug 1762066: Skip this test on Android, as it currently causes the browser to crash + # when attempting to register a chrome handler for files that cannot be found. + # Since we cannot disable the test via the manifest, we return early instead. + if bidi_session.capabilities["platformName"] == "android": + return + + chrome_url = f"{default_chrome_handler}test.xhtml" + iframe_url = f"{default_chrome_handler}test_iframe.xhtml" + nested_iframe_url = f"{default_chrome_handler}test_nested_iframe.xhtml" + + new_window = new_chrome_window(chrome_url) + + # Retrieve all browsing contexts for the custom chrome window + top_level_contexts = await bidi_session.browsing_context.get_tree( + root=new_window.id, max_depth=1 + ) + + assert len(top_level_contexts) == 1 + + assert_browsing_context( + top_level_contexts[0], + new_window.id, + children=2, + parent=None, + url=chrome_url, + client_window=None, + ) + + iframes = top_level_contexts[0]["children"] + + # Retrieve all browsing contexts for the second iframe + iframe_contexts = await bidi_session.browsing_context.get_tree( + root=iframes[1]["context"] + ) + + assert len(iframe_contexts) == 1 + + assert_browsing_context( + iframe_contexts[0], + iframes[1]["context"], + children=1, + parent=top_level_contexts[0]["context"], + url=nested_iframe_url, + client_window=top_level_contexts[0]["clientWindow"], + ) + + assert iframe_contexts[0]["context"] != top_level_contexts[0]["context"] + assert iframe_contexts[0]["context"] == iframes[1]["context"] + + assert iframe_contexts[0]["moz:scope"] == "chrome" + assert iframe_contexts[0]["moz:name"] == "iframe-nested" + + nested_iframes = iframe_contexts[0]["children"] + + # Check the first nested iframe + assert_browsing_context( + nested_iframes[0], + None, + children=0, + parent_expected=False, + url=iframe_url, + client_window=top_level_contexts[0]["clientWindow"], + ) + assert iframes[1]["context"] != top_level_contexts[0]["context"] + assert iframes[1]["context"] != iframes[0]["context"] + assert nested_iframes[0]["context"] != iframes[1]["context"] + assert nested_iframes[0]["context"] != top_level_contexts[0]["context"] + + assert nested_iframes[0]["moz:scope"] == "chrome" + assert nested_iframes[0]["moz:name"] == "iframe"