Repository: electron/electron
Pull request: fix: use requesting frame origin in permission helper and device choosers

#### Description of Change

`WebContentsPermissionHelper::RequestPermission` was passing `web_contents_->GetLastCommittedURL()` as the requesting origin instead of the actual requesting frame's origin. When a subframe initiates a permission request, this meant the top-level page's URL was forwarded rather than the subframe's. The HID, USB, and Serial device choosers had the same pattern — they keyed grants to the primary main frame's origin instead of the requesting frame's.

This PR switches all of these paths to use `requesting_frame->GetLastCommittedOrigin()` so the origin matches the frame that actually initiated the request.

No API changes — `details.requestingUrl` is unchanged (it was already derived from the requesting RFH in `ElectronPermissionManager`).

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included
- [ ] `npm test` passes
- [x] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fixed an issue where permission and device-chooser handlers received the top-level page origin instead of the requesting subframe's origin.

Changed files:
filename: shell/browser/hid/hid_chooser_controller.cc
patch: @@ -87,13 +87,9 @@ HidChooserController::HidChooserController(
       exclusion_filters_(std::move(exclusion_filters)),
       callback_(std::move(callback)),
       initiator_document_(render_frame_host->GetWeakDocumentPtr()),
-      origin_(content::WebContents::FromRenderFrameHost(render_frame_host)
-                  ->GetPrimaryMainFrame()
-                  ->GetLastCommittedOrigin()),
+      origin_(render_frame_host->GetLastCommittedOrigin()),
       hid_delegate_(hid_delegate),
       render_frame_host_id_(render_frame_host->GetGlobalId()) {
-  // The use above of GetMainFrame is safe as content::HidService instances are
-  // not created for fenced frames.
   DCHECK(!render_frame_host->IsNestedWithinFencedFrame());
 
   chooser_context_ = HidChooserContextFactory::GetForBrowserContext(

filename: shell/browser/serial/electron_serial_delegate.cc
patch: @@ -52,25 +52,21 @@ bool ElectronSerialDelegate::CanRequestPortPermission(
   auto* permission_helper =
       WebContentsPermissionHelper::FromWebContents(web_contents);
   return permission_helper->CheckSerialAccessPermission(
-      web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin());
+      frame->GetLastCommittedOrigin());
 }
 
 bool ElectronSerialDelegate::HasPortPermission(
     content::RenderFrameHost* frame,
     const device::mojom::SerialPortInfo& port) {
-  auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
   return GetChooserContext(frame)->HasPortPermission(
-      web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(), port,
-      frame);
+      frame->GetLastCommittedOrigin(), port, frame);
 }
 
 void ElectronSerialDelegate::RevokePortPermissionWebInitiated(
     content::RenderFrameHost* frame,
     const base::UnguessableToken& token) {
-  auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
   return GetChooserContext(frame)->RevokePortPermissionWebInitiated(
-      web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(), token,
-      frame);
+      frame->GetLastCommittedOrigin(), token, frame);
 }
 
 const device::mojom::SerialPortInfo* ElectronSerialDelegate::GetPortInfo(

filename: shell/browser/serial/serial_chooser_controller.cc
patch: @@ -125,7 +125,7 @@ SerialChooserController::SerialChooserController(
           std::move(allowed_bluetooth_service_class_ids)),
       callback_(std::move(callback)),
       initiator_document_(render_frame_host->GetWeakDocumentPtr()) {
-  origin_ = web_contents_->GetPrimaryMainFrame()->GetLastCommittedOrigin();
+  origin_ = render_frame_host->GetLastCommittedOrigin();
 
   chooser_context_ = SerialChooserContextFactory::GetForBrowserContext(
                          web_contents_->GetBrowserContext())

filename: shell/browser/usb/usb_chooser_controller.cc
patch: @@ -43,7 +43,7 @@ UsbChooserController::UsbChooserController(
     : WebContentsObserver(web_contents),
       options_(std::move(options)),
       callback_(std::move(callback)),
-      origin_(render_frame_host->GetMainFrame()->GetLastCommittedOrigin()),
+      origin_(render_frame_host->GetLastCommittedOrigin()),
       usb_delegate_(usb_delegate),
       render_frame_host_id_(render_frame_host->GetGlobalId()) {
   chooser_context_ = UsbChooserContextFactory::GetForBrowserContext(

filename: shell/browser/web_contents_permission_helper.cc
patch: @@ -219,7 +219,7 @@ void WebContentsPermissionHelper::RequestPermission(
     base::DictValue details) {
   auto* permission_manager = static_cast<ElectronPermissionManager*>(
       web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
-  auto origin = web_contents_->GetLastCommittedURL();
+  auto origin = requesting_frame->GetLastCommittedOrigin().GetURL();
   permission_manager->RequestPermissionWithDetails(
       content::PermissionDescriptorUtil::
           CreatePermissionDescriptorForPermissionType(permission),