|
@@ -0,0 +1,193 @@
|
|
|
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
+From: Dave Tapuska <[email protected]>
|
|
|
+Date: Thu, 10 Mar 2022 02:35:27 +0000
|
|
|
+Subject: Do not expose inner WebContents on scripting/getAllFrames.
|
|
|
+
|
|
|
+Inner WebContents shouldn't be exposed for executeScript or getAllFrames
|
|
|
+APIs. This is consistent with the API before crrev.com/f894f106
|
|
|
+and crrev.com/c8de3b0a.
|
|
|
+
|
|
|
+BUG=1301320,1261261
|
|
|
+
|
|
|
+(cherry picked from commit 5c4e043324b3afd1be673ae2c0a5c00845bb0e86)
|
|
|
+
|
|
|
+Change-Id: I86a5b09aa44c48319b7dd0a10e5442b8c803d4e5
|
|
|
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3497565
|
|
|
+Reviewed-by: Devlin Cronin <[email protected]>
|
|
|
+Reviewed-by: Kevin McNee <[email protected]>
|
|
|
+Commit-Queue: Dave Tapuska <[email protected]>
|
|
|
+Cr-Original-Commit-Position: refs/heads/main@{#977769}
|
|
|
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3514993
|
|
|
+Cr-Commit-Position: refs/branch-heads/4758@{#1244}
|
|
|
+Cr-Branched-From: 4a2cf4baf90326df19c3ee70ff987960d59a386e-refs/heads/main@{#950365}
|
|
|
+
|
|
|
+diff --git a/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
|
|
|
+index e6d0aa8331816e4d2fe3e21fcede6b11b5ab00b2..37a35fda1ba5bda086764d0c1f8ee31f0d70f653 100644
|
|
|
+--- a/chrome/browser/extensions/api/scripting/scripting_apitest.cc
|
|
|
++++ b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
|
|
|
+@@ -83,6 +83,15 @@ IN_PROC_BROWSER_TEST_F(ScriptingAPITest, SubFramesTests) {
|
|
|
+ ASSERT_TRUE(RunExtensionTest("scripting/sub_frames")) << message_;
|
|
|
+ }
|
|
|
+
|
|
|
++// Test validating we don't insert content into nested WebContents.
|
|
|
++IN_PROC_BROWSER_TEST_F(ScriptingAPITest, NestedWebContents) {
|
|
|
++ OpenURLInCurrentTab(
|
|
|
++ embedded_test_server()->GetURL("a.com", "/page_with_embedded_pdf.html"));
|
|
|
++
|
|
|
++ // From there, the test continues in the JS.
|
|
|
++ ASSERT_TRUE(RunExtensionTest("scripting/nested_web_contents")) << message_;
|
|
|
++}
|
|
|
++
|
|
|
+ IN_PROC_BROWSER_TEST_F(ScriptingAPITest, CSSInjection) {
|
|
|
+ OpenURLInCurrentTab(
|
|
|
+ embedded_test_server()->GetURL("example.com", "/simple.html"));
|
|
|
+diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
|
|
|
+index d18d496e9626990dc633b207a0c7482c026e6ed7..0486c08505e8f2f57229a775ada456ecf1a30a56 100644
|
|
|
+--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
|
|
|
++++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
|
|
|
+@@ -511,14 +511,21 @@ ExtensionFunction::ResponseAction WebNavigationGetAllFramesFunction::Run() {
|
|
|
+ std::vector<GetAllFrames::Results::DetailsType> result_list;
|
|
|
+
|
|
|
+ web_contents->ForEachFrame(base::BindRepeating(
|
|
|
+- [](std::vector<GetAllFrames::Results::DetailsType>& result_list,
|
|
|
++ [](content::WebContents* web_contents,
|
|
|
++ std::vector<GetAllFrames::Results::DetailsType>& result_list,
|
|
|
+ content::RenderFrameHost* render_frame_host) {
|
|
|
++ // Don't expose inner WebContents for the getFrames API.
|
|
|
++ if (content::WebContents::FromRenderFrameHost(render_frame_host) !=
|
|
|
++ web_contents) {
|
|
|
++ return content::RenderFrameHost::FrameIterationAction::kSkipChildren;
|
|
|
++ }
|
|
|
++
|
|
|
+ auto* navigation_state =
|
|
|
+ FrameNavigationState::GetForCurrentDocument(render_frame_host);
|
|
|
+
|
|
|
+ if (!navigation_state ||
|
|
|
+ !FrameNavigationState::IsValidUrl(navigation_state->GetUrl())) {
|
|
|
+- return;
|
|
|
++ return content::RenderFrameHost::FrameIterationAction::kContinue;
|
|
|
+ }
|
|
|
+
|
|
|
+ GetAllFrames::Results::DetailsType frame;
|
|
|
+@@ -529,8 +536,9 @@ ExtensionFunction::ResponseAction WebNavigationGetAllFramesFunction::Run() {
|
|
|
+ frame.process_id = render_frame_host->GetProcess()->GetID();
|
|
|
+ frame.error_occurred = navigation_state->GetErrorOccurredInFrame();
|
|
|
+ result_list.push_back(std::move(frame));
|
|
|
++ return content::RenderFrameHost::FrameIterationAction::kContinue;
|
|
|
+ },
|
|
|
+- std::ref(result_list)));
|
|
|
++ web_contents, std::ref(result_list)));
|
|
|
+
|
|
|
+ return RespondNow(ArgumentList(GetAllFrames::Results::Create(result_list)));
|
|
|
+ }
|
|
|
+diff --git a/chrome/test/data/extensions/api_test/scripting/nested_web_contents/manifest.json b/chrome/test/data/extensions/api_test/scripting/nested_web_contents/manifest.json
|
|
|
+new file mode 100644
|
|
|
+index 0000000000000000000000000000000000000000..1ee28c4149223ce82a7563270ee13c7f7c01d4a9
|
|
|
+--- /dev/null
|
|
|
++++ b/chrome/test/data/extensions/api_test/scripting/nested_web_contents/manifest.json
|
|
|
+@@ -0,0 +1,8 @@
|
|
|
++{
|
|
|
++ "name": "Scripting API Test",
|
|
|
++ "manifest_version": 3,
|
|
|
++ "version": "0.1",
|
|
|
++ "permissions": ["scripting", "tabs", "webNavigation"],
|
|
|
++ "background": {"service_worker": "worker.js"},
|
|
|
++ "host_permissions": ["http://a.com/*", "http://b.com/*"]
|
|
|
++}
|
|
|
+diff --git a/chrome/test/data/extensions/api_test/scripting/nested_web_contents/worker.js b/chrome/test/data/extensions/api_test/scripting/nested_web_contents/worker.js
|
|
|
+new file mode 100644
|
|
|
+index 0000000000000000000000000000000000000000..cea1287ece0a7397ac4cab3919ee2e7c12b02da8
|
|
|
+--- /dev/null
|
|
|
++++ b/chrome/test/data/extensions/api_test/scripting/nested_web_contents/worker.js
|
|
|
+@@ -0,0 +1,41 @@
|
|
|
++// Copyright 2022 The Chromium Authors. All rights reserved.
|
|
|
++// Use of this source code is governed by a BSD-style license that can be
|
|
|
++// found in the LICENSE file.
|
|
|
++
|
|
|
++function injectedFunction() {
|
|
|
++ return location.pathname;
|
|
|
++}
|
|
|
++
|
|
|
++// Returns the single tab matching the given `query`.
|
|
|
++async function getSingleTab(query) {
|
|
|
++ const tabs = await chrome.tabs.query(query);
|
|
|
++ chrome.test.assertEq(1, tabs.length);
|
|
|
++ return tabs[0];
|
|
|
++}
|
|
|
++
|
|
|
++chrome.test.runTests([
|
|
|
++ async function nestedWebContents() {
|
|
|
++ const query = {url: 'http://a.com/*'};
|
|
|
++ let tab = await getSingleTab(query);
|
|
|
++ // There should be exactly 2 frames.
|
|
|
++ let frames = await chrome.webNavigation.getAllFrames({tabId: tab.id});
|
|
|
++ chrome.test.assertEq(2, frames.length);
|
|
|
++
|
|
|
++ // There should be exactly 2 results from executeScript.
|
|
|
++ let results = await chrome.scripting.executeScript({
|
|
|
++ target: {
|
|
|
++ tabId: tab.id,
|
|
|
++ allFrames: true,
|
|
|
++ },
|
|
|
++ func: injectedFunction,
|
|
|
++ });
|
|
|
++
|
|
|
++ // We see two frames here, the main frame and one for the embed. We should
|
|
|
++ // *not* see the third "embed within the embed" created by the PDF
|
|
|
++ // reader.
|
|
|
++ chrome.test.assertEq(2, results.length);
|
|
|
++ chrome.test.assertEq('/page_with_embedded_pdf.html', results[0].result);
|
|
|
++ chrome.test.assertEq('/pdf/test.pdf', results[1].result);
|
|
|
++ chrome.test.succeed();
|
|
|
++ },
|
|
|
++]);
|
|
|
+diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc
|
|
|
+index b5b8c8d4b31e3e9bdd7d96c79cc361ace45182fd..69cea43df78ae2d314bd7634c5eb6112f9dcedea 100644
|
|
|
+--- a/extensions/browser/script_executor.cc
|
|
|
++++ b/extensions/browser/script_executor.cc
|
|
|
+@@ -90,24 +90,32 @@ class Handler : public content::WebContentsObserver {
|
|
|
+ // `pending_render_frames_` and add them if they are alive (and not already
|
|
|
+ // contained in `pending_frames`).
|
|
|
+ if (scope == ScriptExecutor::INCLUDE_SUB_FRAMES) {
|
|
|
+- auto append_frame =
|
|
|
+- [](std::vector<content::RenderFrameHost*>* pending_frames,
|
|
|
+- content::RenderFrameHost* frame) {
|
|
|
+- if (!frame->IsRenderFrameLive() ||
|
|
|
+- base::Contains(*pending_frames, frame)) {
|
|
|
+- return;
|
|
|
+- }
|
|
|
+-
|
|
|
+- pending_frames->push_back(frame);
|
|
|
+- };
|
|
|
++ auto append_frame = [](content::WebContents* web_contents,
|
|
|
++ std::vector<content::RenderFrameHost*>*
|
|
|
++ pending_frames,
|
|
|
++ content::RenderFrameHost* frame) {
|
|
|
++ // Avoid inner web contents. If we need to execute scripts on
|
|
|
++ // inner WebContents this class needs to be updated.
|
|
|
++ // See crbug.com/1301320.
|
|
|
++ if (content::WebContents::FromRenderFrameHost(frame) != web_contents) {
|
|
|
++ return content::RenderFrameHost::FrameIterationAction::kSkipChildren;
|
|
|
++ }
|
|
|
++ if (!frame->IsRenderFrameLive() ||
|
|
|
++ base::Contains(*pending_frames, frame)) {
|
|
|
++ return content::RenderFrameHost::FrameIterationAction::kContinue;
|
|
|
++ }
|
|
|
++
|
|
|
++ pending_frames->push_back(frame);
|
|
|
++ return content::RenderFrameHost::FrameIterationAction::kContinue;
|
|
|
++ };
|
|
|
+
|
|
|
+ // We iterate over the requested frames. Note we can't use an iterator
|
|
|
+ // as the for loop will mutate `pending_render_frames_`.
|
|
|
+ size_t requested_frame_count = pending_render_frames_.size();
|
|
|
+ for (size_t i = 0; i < requested_frame_count; ++i) {
|
|
|
+- auto* frame = pending_render_frames_.at(i);
|
|
|
+- frame->ForEachRenderFrameHost(
|
|
|
+- base::BindRepeating(append_frame, &pending_render_frames_));
|
|
|
++ pending_render_frames_.at(i)->ForEachRenderFrameHost(
|
|
|
++ base::BindRepeating(append_frame, web_contents,
|
|
|
++ &pending_render_frames_));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|