diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index 725144c1a4aad..5695b40d3f84f 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -49,6 +49,10 @@ Disables the disk cache for HTTP requests. Disable HTTP/2 and SPDY/3.1 protocols. +### --disable-geolocation _macOS_ + +Disables the Geolocation API. Permission requests for geolocation will be denied internally regardless of the decision made by a handler set via `session.setPermissionRequestHandler`. This functionality is currently implemented only for macOS. Has no effect on other platforms. + ### --disable-renderer-backgrounding Prevents Chromium from lowering the priority of invisible pages' renderer diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index e96a8962cb0b3..1d08c82680506 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -884,6 +884,24 @@ void Session::SetPermissionRequestHandler(v8::Local val, blink::PermissionType permission_type, ElectronPermissionManager::StatusCallback callback, const base::Value& details) { +#if (BUILDFLAG(IS_MAC)) + if (permission_type == blink::PermissionType::GEOLOCATION) { + if (ElectronPermissionManager:: + IsGeolocationDisabledViaCommandLine()) { + auto original_callback = std::move(callback); + callback = base::BindOnce( + [](ElectronPermissionManager::StatusCallback callback, + content::PermissionResult /*ignored_result*/) { + // Always deny regardless of what + // content::PermissionResult is passed here + std::move(callback).Run(content::PermissionResult( + blink::mojom::PermissionStatus::DENIED, + content::PermissionStatusSource::UNSPECIFIED)); + }, + std::move(original_callback)); + } + } +#endif handler->Run(web_contents, permission_type, std::move(callback), details); }, diff --git a/shell/browser/electron_browser_main_parts_mac.mm b/shell/browser/electron_browser_main_parts_mac.mm index c7c093010ef0b..2a4238dec285c 100644 --- a/shell/browser/electron_browser_main_parts_mac.mm +++ b/shell/browser/electron_browser_main_parts_mac.mm @@ -11,6 +11,7 @@ #include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h" #include "services/device/public/cpp/geolocation/system_geolocation_source_apple.h" #include "shell/browser/browser_process_impl.h" +#include "shell/browser/electron_permission_manager.h" #include "shell/browser/mac/electron_application.h" #include "shell/browser/mac/electron_application_delegate.h" #include "ui/base/l10n/l10n_util_mac.h" @@ -32,7 +33,13 @@ setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"]; - if (!device::GeolocationSystemPermissionManager::GetInstance()) { + const bool geolocationDisabled = + ElectronPermissionManager::IsGeolocationDisabledViaCommandLine(); + + // Check if geolocation api is NOT disabled via command line before + // CreateGeolocationSystemPermissionManager is called + if (!geolocationDisabled && + !device::GeolocationSystemPermissionManager::GetInstance()) { device::GeolocationSystemPermissionManager::SetInstance( device::SystemGeolocationSourceApple:: CreateGeolocationSystemPermissionManager()); diff --git a/shell/browser/electron_permission_manager.cc b/shell/browser/electron_permission_manager.cc index a58b3a696302b..c50c76789bbc9 100644 --- a/shell/browser/electron_permission_manager.cc +++ b/shell/browser/electron_permission_manager.cc @@ -8,6 +8,7 @@ #include #include +#include "base/command_line.h" #include "base/containers/to_vector.h" #include "base/values.h" #include "content/browser/permissions/permission_util.h" // nogncheck @@ -146,6 +147,17 @@ void ElectronPermissionManager::SetBluetoothPairingHandler( bluetooth_pairing_handler_ = handler; } +// static +bool ElectronPermissionManager::IsGeolocationDisabledViaCommandLine() { +// Remove platform check once flag is extended to other platforms +#if BUILDFLAG(IS_MAC) + auto* command_line = base::CommandLine::ForCurrentProcess(); + return command_line->HasSwitch("disable-geolocation"); +#else + return false; +#endif +} + bool ElectronPermissionManager::HasPermissionRequestHandler() const { return !request_handler_.is_null(); } @@ -220,9 +232,16 @@ void ElectronPermissionManager::RequestPermissionsWithDetails( ->GrantSendMidiSysExMessage( render_frame_host->GetProcess()->GetDeprecatedID()); } else if (permission_type == blink::PermissionType::GEOLOCATION) { - ElectronBrowserMainParts::Get() - ->GetGeolocationControl() - ->UserDidOptIntoLocationServices(); + if (IsGeolocationDisabledViaCommandLine()) { + results.push_back(content::PermissionResult( + blink::mojom::PermissionStatus::DENIED, + content::PermissionStatusSource::UNSPECIFIED)); + continue; + } else { + ElectronBrowserMainParts::Get() + ->GetGeolocationControl() + ->UserDidOptIntoLocationServices(); + } } results.push_back(content::PermissionResult( blink::mojom::PermissionStatus::GRANTED, @@ -331,6 +350,10 @@ bool ElectronPermissionManager::CheckPermissionWithDetails( content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, base::Value::Dict details) const { + if (permission == blink::PermissionType::GEOLOCATION && + IsGeolocationDisabledViaCommandLine()) + return false; + if (check_handler_.is_null()) { if (permission == blink::PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ) { return false; @@ -368,6 +391,10 @@ bool ElectronPermissionManager::CheckDevicePermission( const url::Origin& origin, const base::Value& device, ElectronBrowserContext* browser_context) const { + if (permission == blink::PermissionType::GEOLOCATION && + IsGeolocationDisabledViaCommandLine()) + return false; + if (device_permission_handler_.is_null()) return browser_context->CheckDevicePermission(origin, device, permission); diff --git a/shell/browser/electron_permission_manager.h b/shell/browser/electron_permission_manager.h index 6bdcec371c405..9a0062ef631e5 100644 --- a/shell/browser/electron_permission_manager.h +++ b/shell/browser/electron_permission_manager.h @@ -66,6 +66,8 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate { using BluetoothPairingHandler = base::RepeatingCallback; + static bool IsGeolocationDisabledViaCommandLine(); + void RequestPermissionWithDetails( blink::mojom::PermissionDescriptorPtr permission, content::RenderFrameHost* render_frame_host, diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 4457d2a1bfb8b..515e5ef5af129 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -898,6 +898,72 @@ describe('chromium features', () => { expect(position).to.have.property('coords'); expect(position).to.have.property('timestamp'); }); + + ifdescribe(process.platform === 'darwin')('with --disable-geolocation', () => { + const testSwitchBehavior = (handlerAction: 'allow' | 'deny' | 'none') => async () => { + const rc = await startRemoteControlApp([ + '--disable-geolocation', + `--boot-eval=fixturesPath=${JSON.stringify(fixturesPath)}` + ]); + + const result = await rc.remotely(async (action: typeof handlerAction) => { + const { session, BrowserWindow } = require('electron'); + const path = require('node:path'); + + // Isolate each test's permissions to prevent permission state leaks between the test variations + const testSession = session.fromPartition(`geolocation-disable-${action}`); + + if (action !== 'none') { + // Make the PermissionRequestHandler behave according to action variable passed for this test + testSession.setPermissionRequestHandler((_wc, permission, callback) => { + if (permission === 'geolocation') { + if (action === 'allow') callback(true); + else if (action === 'deny') callback(false); + else callback(false); + } + }); + } + + const w = new BrowserWindow({ + show: false, + webPreferences: { + session: testSession, + nodeIntegration: true, + contextIsolation: false + } + }); + + await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); + + const permissionState = await w.webContents.executeJavaScript(` + navigator.permissions.query({ name: 'geolocation' }) + .then(status => status.state) + .catch(() => 'error') + `); + + const geoResult = await w.webContents.executeJavaScript(` + new Promise(resolve => { + navigator.geolocation.getCurrentPosition( + () => resolve('allowed'), + err => resolve(err.code) + ); + }) + `); + + return { permissionState, geoResult }; + }, handlerAction); + + // Always expect status to be denied regardless of the decision made by a handler set via `session.setPermissionRequestHandler` + expect(result.permissionState).to.equal('denied', `Unexpected permission state for ${handlerAction} handler`); + + // 1 = PERMISSION_DENIED + expect(result.geoResult).to.equal(1, `Unexpected API result for ${handlerAction} handler`); + }; + + it('denies geolocation when permission request handler would allow', testSwitchBehavior('allow')); + it('denies geolocation when permission request handler would deny', testSwitchBehavior('deny')); + it('denies geolocation with no permission request handler', testSwitchBehavior('none')); + }); }); describe('File System API,', () => {