Chrome · Browser
CVE-2026-106413
Race in Browser
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/webshare/mac/sharing_service_operation.mm |
modified | |
ifchrome/browser/webshare/share_service_impl.cc |
modified | |
RenderFrameHostchrome/browser/webshare/share_service_impl.h |
modified | |
WebContentschrome/browser/webshare/share_service_impl.h |
modified | |
TEST_Fchrome/browser/webshare/chromeos/sharesheet_client_unittest.cc |
modified | |
TEST_Fchrome/browser/webshare/mac/sharing_service_operation_unittest.cc |
modified |
Files Changed
chrome/browser/webshare/share_service_unittest.ccchrome/browser/webshare/win/share_operation_unittest.ccchrome/browser/webshare/share_service_impl.ccchrome/browser/webshare/chromeos/sharesheet_client.ccchrome/browser/webshare/win/share_operation.ccchrome/browser/webshare/mac/sharing_service_operation.mmchrome/browser/webshare/share_service_impl.hchrome/browser/webshare/chromeos/sharesheet_client_unittest.ccchrome/browser/webshare/BUILD.gnchrome/browser/webshare/mac/sharing_service_operation_unittest.cc
Patch
From 6fcc843a0e21159a85a919c37b8daef3f5ab595f Mon Sep 17 00:00:00 2001
From: Marijn Kruisselbrink <mek@chromium.org>
Date: Fri, 11 Sep 2026 15:26:46 -0700
Subject: [PATCH] Ensure Web Share checks tab activation and visibility before showing UI
Web Share checked tab activation only at the beginning of the share
operation. On ChromeOS and macOS, file streaming from the renderer is
asynchronous, allowing a renderer to pace or stall the transfer until
the user switches tabs or windows.
Additionally code on all platforms only checked for tab activation,
and not tab visibility, meaning that a tab could be occluded but still
trigger a web share dialog.
This change adds ShareServiceImpl::IsWebContentsForegroundAndVisible()
to verify that the WebContents is visible (not minimized or occluded)
and that the tab is active in its window. This check is run in common
desktop code at Share() entry and after Safe Browsing checks, and
is re-checked in ChromeOS and macOS implementations immediately before
displaying the system share sheet / picker once file storage finishes.
Bug: 553336689
Change-Id: I6e48fb8dc60b2f4104d08d0af22b04b406632f2b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8391861
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: Daniel Murphy <dmurph@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1696659}
---
diff --git a/chrome/browser/webshare/chromeos/sharesheet_client.cc b/chrome/browser/webshare/chromeos/sharesheet_client.cc
index 2e35bf6..5d7e80a6 100644
--- a/chrome/browser/webshare/chromeos/sharesheet_client.cc
+++ b/chrome/browser/webshare/chromeos/sharesheet_client.cc
@@ -30,7 +30,6 @@
#include "components/prefs/pref_service.h"
#include "components/services/app_service/public/cpp/intent.h"
#include "components/services/app_service/public/cpp/intent_util.h"
-#include "components/tabs/public/tab_interface.h"
#include "components/visibility_timer/visibility_timer_tab_helper.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
@@ -147,10 +146,8 @@
return;
}
- // If the tab is no longer active, return permission denied.
- tabs::TabInterface* tab_interface =
- tabs::TabInterface::MaybeGetFromContents(web_contents());
- if (tab_interface && !tab_interface->IsActivated()) {
+ // If the tab is no longer active or visible, return permission denied.
+ if (!ShareServiceImpl::IsWebContentsForegroundAndVisible(web_contents())) {
std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED);
return;
}
@@ -255,8 +252,14 @@
if (!current_share_.has_value())
return;
- if (!web_contents() || error != blink::mojom::ShareError::OK) {
- std::move(current_share_->callback).Run(error);
+ // Re-check here rather than aborting immediately on visibility changes so
+ // transient tab switches or occlusions do not cancel in-flight shares.
+ if (!web_contents() || error != blink::mojom::ShareError::OK ||
+ !ShareServiceImpl::IsWebContentsForegroundAndVisible(web_contents())) {
+ std::move(current_share_->callback)
+ .Run(error != blink::mojom::ShareError::OK
+ ? error
+ : blink::mojom::ShareError::PERMISSION_DENIED);
ScheduleSharedFileDirectoryDeletion(std::move(current_share_->file_paths),
base::Minutes(0));
current_share_ = std::nullopt;
diff --git a/chrome/browser/webshare/mac/sharing_service_operation.mm b/chrome/browser/webshare/mac/sharing_service_operation.mm
index 3e90569d..17671143 100644
--- a/chrome/browser/webshare/mac/sharing_service_operation.mm
+++ b/chrome/browser/webshare/mac/sharing_service_operation.mm
@@ -21,7 +21,6 @@
#include "chrome/browser/webshare/prepare_subdirectory_task.h"
#include "chrome/browser/webshare/share_service_impl.h"
#include "chrome/browser/webshare/store_files_task.h"
-#include "components/tabs/public/tab_interface.h"
#include "components/visibility_timer/visibility_timer_tab_helper.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_widget_host_view.h"
@@ -90,10 +89,9 @@
return;
}
- // If the tab is no longer active, return permission denied.
- tabs::TabInterface* tab_interface =
- tabs::TabInterface::MaybeGetFromContents(web_contents_.get());
- if (tab_interface && !tab_interface->IsActivated()) {
+ // If the tab is no longer active or visible, return permission denied.
+ if (!ShareServiceImpl::IsWebContentsForegroundAndVisible(
+ web_contents_.get())) {
std::move(callback_).Run(blink::mojom::ShareError::PERMISSION_DENIED);
return;
}
@@ -166,10 +164,16 @@
}
void SharingServiceOperation::OnStoreFiles(blink::mojom::ShareError error) {
- if (!web_contents_ || error != blink::mojom::ShareError::OK) {
+ // Re-check here rather than aborting immediately on visibility changes so
+ // transient tab switches or occlusions do not cancel in-flight shares.
+ if (!web_contents_ || error != blink::mojom::ShareError::OK ||
+ !ShareServiceImpl::IsWebContentsForegroundAndVisible(
+ web_contents_.get())) {
PrepareDirectoryTask::ScheduleSharedFileDeletion(std::move(file_paths_),
base::Minutes(0));
- std::move(callback_).Run(error);
+ std::move(callback_).Run(error != blink::mojom::ShareError::OK
+ ? error
+ : blink::mojom::ShareError::PERMISSION_DENIED);
return;
}
diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc
index 1750add..e17b981 100644
--- a/chrome/browser/webshare/share_service_impl.cc
+++ b/chrome/browser/webshare/share_service_impl.cc
@@ -19,6 +19,8 @@
#include "chrome/browser/bad_message.h"
#include "chrome/browser/browser_process.h"
#include "components/safe_browsing/buildflags.h"
+#include "components/tabs/public/tab_interface.h"
+#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
@@ -173,6 +175,23 @@
return true;
}
+// static
+bool ShareServiceImpl::IsWebContentsForegroundAndVisible(
+ content::WebContents* web_contents) {
+ if (!web_contents) {
+ return false;
+ }
+ if (web_contents->GetVisibility() != content::Visibility::VISIBLE) {
+ return false;
+ }
+ tabs::TabInterface* tab_interface =
+ tabs::TabInterface::MaybeGetFromContents(web_contents);
+ if (tab_interface && !tab_interface->IsActivated()) {
+ return false;
+ }
+ return true;
+}
+
void ShareServiceImpl::Share(const std::string& title,
const std::string& text,
const GURL& share_url,
@@ -196,8 +215,8 @@
content::WebContents* const web_contents =
content::WebContents::FromRenderFrameHost(&render_frame_host());
- if (!web_contents) {
- VLOG(1) << "Cannot share after navigating away";
+ if (!web_contents || !IsWebContentsForegroundAndVisible(web_contents)) {
+ VLOG(1) << "Cannot share if tab is not active and visible";
std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED);
return;
}
@@ -280,8 +299,8 @@
content::WebContents* const web_contents =
content::WebContents::FromRenderFrameHost(&render_frame_host());
- if (!web_contents) {
- VLOG(1) << "Cannot share after navigating away";
+ if (!web_contents || !IsWebContentsForegroundAndVisible(web_contents)) {
+ VLOG(1) << "Cannot share if tab is not active and visible";
std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED);
return;
}
diff --git a/chrome/browser/webshare/share_service_impl.h b/chrome/browser/webshare/share_service_impl.h
index 6d9a75e..12de0a3 100644
--- a/chrome/browser/webshare/share_service_impl.h
+++ b/chrome/browser/webshare/share_service_impl.h
@@ -29,6 +29,7 @@
namespace content {
class RenderFrameHost;
+class WebContents;
}
enum class WebShareMethod { kShare = 0, kMaxValue = kShare };
@@ -52,6 +53,11 @@
static bool IsDangerousFilename(const base::FilePath& path);
static bool IsDangerousMimeType(std::string_view content_type);
+ // Returns true if `web_contents` is visible and, if it is a tab, the tab is
+ // active in its window.
+ static bool IsWebContentsForegroundAndVisible(
+ content::WebContents* web_contents);
+
// blink::mojom::ShareService:
void Share(const std::string& title,
const std::string& text,
diff --git a/chrome/browser/webshare/win/share_operation.cc b/chrome/browser/webshare/win/share_operation.cc
index 6d0b7548..4fb95b8 100644
--- a/chrome/browser/webshare/win/share_operation.cc
+++ b/chrome/browser/webshare/win/share_operation.cc
@@ -26,7 +26,6 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/webshare/share_service_impl.h"
#include "chrome/browser/webshare/win/show_share_ui_for_window_operation.h"
-#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
@@ -403,10 +402,9 @@
return;
}
- // If the tab is no longer active, return permission denied.
- tabs::TabInterface* tab_interface =
- tabs::TabInterface::MaybeGetFromContents(web_contents_.get());
- if (tab_interface && !tab_interface->IsActivated()) {
+ // If the tab is no longer active or visible, return permission denied.
+ if (!ShareServiceImpl::IsWebContentsForegroundAndVisible(
+ web_contents_.get())) {
Complete(blink::mojom::ShareError::PERMISSION_DENIED);
return;
}
diff --git a/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc b/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc
index b9ff3e2..e7c86c0 100644
--- a/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc
+++ b/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc
@@ -170,4 +170,112 @@
EXPECT_FALSE(base::PathExists(second_file));
}
+TEST_F(SharesheetClientUnitTest, TestHiddenWebContentsBlocked) {
+ web_contents()->WasHidden();
+ SharesheetClient sharesheet_client(web_contents());
+
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharesheetClientUnitTest, TestHiddenDuringStoreFilesBlocked) {
+ SharesheetClient sharesheet_client(web_contents());
+ const base::FilePath share_cache_dir =
+ file_manager::util::GetShareCacheFilePath(profile());
+ const base::FilePath first_file =
+ share_cache_dir.AppendASCII(".WebShare/share1/first.txt");
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+ files.push_back(
+ blink::mojom::SharedFile::New(*base::SafeBaseName::Create(first_file),
+ blink::mojom::SerializedBlob::New()));
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ // Hide the WebContents while file storage tasks are in flight.
+ web_contents()->WasHidden();
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharesheetClientUnitTest, TestOccludedWebContentsBlocked) {
+ web_contents()->WasOccluded();
+ SharesheetClient sharesheet_client(web_contents());
+
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharesheetClientUnitTest, TestOccludedDuringStoreFilesBlocked) {
+ SharesheetClient sharesheet_client(web_contents());
+ const base::FilePath share_cache_dir =
+ file_manager::util::GetShareCacheFilePath(profile());
+ const base::FilePath first_file =
+ share_cache_dir.AppendASCII(".WebShare/share1/first.txt");
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+ files.push_back(
+ blink::mojom::SharedFile::New(*base::SafeBaseName::Create(first_file),
+ blink::mojom::SerializedBlob::New()));
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ // Occlude the WebContents while file storage tasks are in flight.
+ web_contents()->WasOccluded();
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
} // namespace webshare
diff --git a/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc b/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc
index f15a807..602df1cc 100644
--- a/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc
+++ b/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/webshare/mac/sharing_service_operation.h"
+#include "base/files/safe_base_name.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
@@ -106,4 +107,108 @@
EXPECT_EQ(error, blink::mojom::ShareError::OK);
}
+TEST_F(SharingServiceOperationUnitTest, TestHiddenWebContentsBlocked) {
+ web_contents()->WasHidden();
+
+ const std::string title = "Title";
+ const std::string text = "Text";
+ const GURL url("https://example.com");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ SharingServiceOperation sharing_service_operation(
+ title, text, url, std::move(files), web_contents());
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+
+ sharing_service_operation.Share(base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharingServiceOperationUnitTest, TestHiddenDuringStoreFilesBlocked) {
+ const std::string title = "Title";
+ const std::string text = "Text";
+ const GURL url("https://example.com");
+ std::vector<blink::mojom::SharedFilePtr> files;
+ files.push_back(blink::mojom::SharedFile::New(
+ *base::SafeBaseName::Create(base::FilePath::FromASCII("test.txt")),
+ blink::mojom::SerializedBlob::New()));
+
+ SharingServiceOperation sharing_service_operation(
+ title, text, url, std::move(files), web_contents());
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+
+ sharing_service_operation.Share(base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc b/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc
index b9ff3e2..e7c86c0 100644
--- a/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc
+++ b/chrome/browser/webshare/chromeos/sharesheet_client_unittest.cc
@@ -170,4 +170,112 @@
EXPECT_FALSE(base::PathExists(second_file));
}
+TEST_F(SharesheetClientUnitTest, TestHiddenWebContentsBlocked) {
+ web_contents()->WasHidden();
+ SharesheetClient sharesheet_client(web_contents());
+
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharesheetClientUnitTest, TestHiddenDuringStoreFilesBlocked) {
+ SharesheetClient sharesheet_client(web_contents());
+ const base::FilePath share_cache_dir =
+ file_manager::util::GetShareCacheFilePath(profile());
+ const base::FilePath first_file =
+ share_cache_dir.AppendASCII(".WebShare/share1/first.txt");
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+ files.push_back(
+ blink::mojom::SharedFile::New(*base::SafeBaseName::Create(first_file),
+ blink::mojom::SerializedBlob::New()));
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ // Hide the WebContents while file storage tasks are in flight.
+ web_contents()->WasHidden();
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharesheetClientUnitTest, TestOccludedWebContentsBlocked) {
+ web_contents()->WasOccluded();
+ SharesheetClient sharesheet_client(web_contents());
+
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharesheetClientUnitTest, TestOccludedDuringStoreFilesBlocked) {
+ SharesheetClient sharesheet_client(web_contents());
+ const base::FilePath share_cache_dir =
+ file_manager::util::GetShareCacheFilePath(profile());
+ const base::FilePath first_file =
+ share_cache_dir.AppendASCII(".WebShare/share1/first.txt");
+ const std::string title = "Subject";
+ const std::string text = "Message";
+ const GURL share_url("https://example.com/");
+ std::vector<blink::mojom::SharedFilePtr> files;
+ files.push_back(
+ blink::mojom::SharedFile::New(*base::SafeBaseName::Create(first_file),
+ blink::mojom::SerializedBlob::New()));
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+ sharesheet_client.Share(
+ title, text, share_url, std::move(files),
+ base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ // Occlude the WebContents while file storage tasks are in flight.
+ web_contents()->WasOccluded();
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
} // namespace webshare
diff --git a/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc b/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc
index f15a807..602df1cc 100644
--- a/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc
+++ b/chrome/browser/webshare/mac/sharing_service_operation_unittest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/webshare/mac/sharing_service_operation.h"
+#include "base/files/safe_base_name.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
@@ -106,4 +107,108 @@
EXPECT_EQ(error, blink::mojom::ShareError::OK);
}
+TEST_F(SharingServiceOperationUnitTest, TestHiddenWebContentsBlocked) {
+ web_contents()->WasHidden();
+
+ const std::string title = "Title";
+ const std::string text = "Text";
+ const GURL url("https://example.com");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ SharingServiceOperation sharing_service_operation(
+ title, text, url, std::move(files), web_contents());
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+
+ sharing_service_operation.Share(base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharingServiceOperationUnitTest, TestHiddenDuringStoreFilesBlocked) {
+ const std::string title = "Title";
+ const std::string text = "Text";
+ const GURL url("https://example.com");
+ std::vector<blink::mojom::SharedFilePtr> files;
+ files.push_back(blink::mojom::SharedFile::New(
+ *base::SafeBaseName::Create(base::FilePath::FromASCII("test.txt")),
+ blink::mojom::SerializedBlob::New()));
+
+ SharingServiceOperation sharing_service_operation(
+ title, text, url, std::move(files), web_contents());
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+
+ sharing_service_operation.Share(base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ // Hide the WebContents while file storage tasks are in flight.
+ web_contents()->WasHidden();
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharingServiceOperationUnitTest, TestOccludedWebContentsBlocked) {
+ web_contents()->WasOccluded();
+
+ const std::string title = "Title";
+ const std::string text = "Text";
+ const GURL url("https://example.com");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ SharingServiceOperation sharing_service_operation(
+ title, text, url, std::move(files), web_contents());
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+
+ sharing_service_operation.Share(base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
+TEST_F(SharingServiceOperationUnitTest, TestOccludedDuringStoreFilesBlocked) {
+ const std::string title = "Title";
+ const std::string text = "Text";
+ const GURL url("https://example.com");
+ std::vector<blink::mojom::SharedFilePtr> files;
+ files.push_back(blink::mojom::SharedFile::New(
+ *base::SafeBaseName::Create(base::FilePath::FromASCII("test.txt")),
+ blink::mojom::SerializedBlob::New()));
+
+ SharingServiceOperation sharing_service_operation(
+ title, text, url, std::move(files), web_contents());
+
+ base::RunLoop run_loop;
+ blink::mojom::ShareError error = blink::mojom::ShareError::INTERNAL_ERROR;
+
+ sharing_service_operation.Share(base::BindLambdaForTesting(
+ [&run_loop, &error](blink::mojom::ShareError in_error) {
+ error = in_error;
+ run_loop.Quit();
+ }));
+
+ // Occlude the WebContents while file storage tasks are in flight.
+ web_contents()->WasOccluded();
+
+ run_loop.Run();
+ EXPECT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+}
+
} // namespace webshare
diff --git a/chrome/browser/webshare/share_service_unittest.cc b/chrome/browser/webshare/share_service_unittest.cc
index 338ef6b7..d464e51 100644
--- a/chrome/browser/webshare/share_service_unittest.cc
+++ b/chrome/browser/webshare/share_service_unittest.cc
@@ -20,6 +20,7 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
#include "content/public/test/test_renderer_host.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_impl.h"
@@ -242,6 +243,24 @@
EXPECT_EQ(ShareError::OK, ShareGeneratedFileData(".pdf", "application/pdf"));
}
+TEST_F(ShareServiceUnitTest, HiddenWebContentsBlocked) {
+ web_contents()->WasHidden();
+ EXPECT_EQ(ShareError::PERMISSION_DENIED,
+ ShareGeneratedFileData(".txt", "text/plain"));
+
+ web_contents()->WasShown();
+ EXPECT_EQ(ShareError::OK, ShareGeneratedFileData(".txt", "text/plain"));
+}
+
+TEST_F(ShareServiceUnitTest, OccludedWebContentsBlocked) {
+ web_contents()->WasOccluded();
+ EXPECT_EQ(ShareError::PERMISSION_DENIED,
+ ShareGeneratedFileData(".txt", "text/plain"));
+
+ web_contents()->WasShown();
+ EXPECT_EQ(ShareError::OK, ShareGeneratedFileData(".txt", "text/plain"));
+}
+
#if BUILDFLAG(IS_WIN)
TEST_F(ShareServiceUnitTest, ReservedNames) {
EXPECT_TRUE(IsDangerousFilename(FILE_PATH_LITERAL("CON")));
diff --git a/chrome/browser/webshare/win/share_operation_unittest.cc b/chrome/browser/webshare/win/share_operation_unittest.cc
index 1ba705f..e5b26de 100644
--- a/chrome/browser/webshare/win/share_operation_unittest.cc
+++ b/chrome/browser/webshare/win/share_operation_unittest.cc
@@ -466,4 +466,34 @@
kMaxSharedFileBytesForTest + 1);
}
+TEST_F(ShareOperationUnitTest, TestHiddenWebContentsBlocked) {
+ web_contents()->WasHidden();
+
+ base::RunLoop run_loop;
+ std::vector<blink::mojom::SharedFilePtr> files;
+ ShareOperation operation{"", "", GURL(), web_contents()};
+ operation.Run(
+ std::move(files),
+ base::BindLambdaForTesting([&run_loop](blink::mojom::ShareError error) {
+ ASSERT_EQ(error, blink::mojom::ShareError::PERMISSION_DENIED);
+ run_loop.Quit();
+ }));
+ run_loop.Run();
+}
+
+TEST_F(ShareOperationUnitTest, TestOccludedWebContentsBlocked) {
+ web_contents()->WasOccluded();
... (truncated)
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page