blob: 3edaf8bdbdeff5f219637adcd75f4a2a12a693f7 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/path_service.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/extensions/extension_action_test_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "chrome/test/permissions/permission_request_manager_test_api.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/embedder_support/switches.h"
#include "components/permissions/permissions_client.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "components/permissions/test/permission_request_observer.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/content_mock_cert_verifier.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/permissions_test_utils.h"
#include "content/public/test/prerender_test_util.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/options_page_info.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/permissions_info.h"
#include "extensions/test/result_catcher.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "url/gurl.h"
namespace {
// Helper for CreateBlobURL() and CreateFilesystemURL().
// ASSERT_* macros can only be used in functions returning void.
void AssertResultIsString(const content::EvalJsResult& result) {
// Verify no error.
ASSERT_TRUE(result.is_ok());
// We could use result.value.is_string(), but this logs the actual type in
// case of mismatch.
std::ignore = result.ExtractString();
}
// Creates a blob containing dummy HTML, then returns its URL.
// Executes javascript to do so in `rfh`.
GURL CreateBlobURL(content::RenderFrameHost* rfh) {
content::EvalJsResult result = content::EvalJs(rfh, R"(
const blob = new Blob(["foo"], {type: "text/html"});
URL.createObjectURL(blob)
)");
AssertResultIsString(result);
GURL blob_url = GURL(result.ExtractString());
EXPECT_TRUE(blob_url.SchemeIsBlob());
return blob_url;
}
// Writes some dummy HTML to a file, then returns its `filesystem:` URL.
// Executes javascript to do so in `rfh`, which must not be nullptr.
GURL CreateFilesystemURL(content::RenderFrameHost* rfh) {
content::EvalJsResult result = content::EvalJs(rfh, R"(
// It seems anonymous async functions are not available yet, so we cannot
// use an immediately-invoked function expression.
async function run() {
const fs = await new Promise((resolve, reject) => {
window.webkitRequestFileSystem(window.TEMPORARY, 1024, resolve,
reject);
});
const file = await new Promise((resolve, reject) => {
fs.root.getFile('hello.html', {create: true}, resolve, reject);
});
const writer = await new Promise((resolve, reject) => {
file.createWriter(resolve, reject);
});
await new Promise((resolve) => {
writer.onwriteend = resolve;
writer.write(new Blob(["foo"], {type: "text/html"}));
});
return file.toURL();
}
run()
)");
AssertResultIsString(result);
GURL fs_url = GURL(result.ExtractString());
EXPECT_TRUE(fs_url.SchemeIsFileSystem());
return fs_url;
}
GURL CreateFileURL(const base::FilePath::CharType file_name[] =
FILE_PATH_LITERAL("title1.html")) {
GURL file_url =
ui_test_utils::GetTestUrl(base::FilePath(), base::FilePath(file_name));
EXPECT_EQ(url::kFileScheme, file_url.scheme());
return file_url;
}
// Return the active RenderFrameHost loaded in the last iframe in |parent_rfh|.
content::RenderFrameHost* LastChild(content::RenderFrameHost* parent_rfh) {
int child_end = 0;
while (ChildFrameAt(parent_rfh, child_end))
child_end++;
if (child_end == 0)
return nullptr;
return ChildFrameAt(parent_rfh, child_end - 1);
}
// Create an <iframe> inside |parent_rfh|, and navigate it toward |url|.
// |permission_policy| can be used to set permission policy to the iframe.
// For instance:
// ```
// child = CreateIframe(parent, url, "geolocation *; camera *");
// ```
// This returns the new RenderFrameHost associated with new document created in
// the iframe.
content::RenderFrameHost* CreateIframe(
content::RenderFrameHost* parent_rfh,
const GURL& url,
const std::string& permission_policy = "") {
EXPECT_EQ(
"iframe loaded",
content::EvalJs(parent_rfh, content::JsReplace(R"(
new Promise((resolve) => {
const iframe = document.createElement("iframe");
iframe.src = $1;
iframe.allow = $2;
iframe.onload = _ => { resolve("iframe loaded"); };
document.body.appendChild(iframe);
}))",
url, permission_policy)));
return LastChild(parent_rfh);
}
content::WebContents* OpenPopup(Browser* browser, const GURL& url) {
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::ExecuteScriptAsync(
contents, content::JsReplace("window.open($1, '', '[]');", url));
Browser* popup = ui_test_utils::WaitForBrowserToOpen();
EXPECT_NE(popup, browser);
content::WebContents* popup_contents =
popup->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(WaitForRenderFrameReady(popup_contents->GetPrimaryMainFrame()));
WaitForLoadStop(popup_contents);
return popup_contents;
}
constexpr char kCheckNotifications[] = R"(
new Promise(async resolve => {
const PermissionStatus =
await navigator.permissions.query({name: 'notifications'});
resolve(PermissionStatus.state === 'granted');
})
)";
constexpr char kRequestNotifications[] = R"(
new Promise(resolve => {
Notification.requestPermission().then(function (permission) {
resolve(permission)
});
})
)";
constexpr char kCheckGeolocation[] = R"(
new Promise(async resolve => {
const PermissionStatus =
await navigator.permissions.query({name: 'geolocation'});
resolve(PermissionStatus.state === 'granted');
})
)";
constexpr char kRequestGeolocation[] = R"(
new Promise(resolve => {
navigator.geolocation.getCurrentPosition(
() => resolve('granted'),
() => resolve('denied')
);
})
)";
constexpr char kCheckCamera[] = R"(
new Promise(async resolve => {
const PermissionStatus =
await navigator.permissions.query({name: 'camera'});
resolve(PermissionStatus.state === 'granted');
})
)";
constexpr char kRequestCamera[] = R"(
new Promise(async resolve => {
var constraints = { video: true };
window.focus();
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
resolve('granted');
} catch(error) {
resolve('denied')
}
})
)";
constexpr char kRequestMicrophone[] = R"(
new Promise(async resolve => {
var constraints = { audio: true };
window.focus();
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
resolve('granted');
} catch(error) {
resolve('denied')
}
})
)";
constexpr char kCheckMicrophone[] = R"(
new Promise(async resolve => {
const PermissionStatus =
await navigator.permissions.query({name: 'microphone'});
resolve(PermissionStatus.state === 'granted');
})
)";
constexpr char kCheckClipboardRead[] = R"(
new Promise(async resolve => {
const PermissionStatus =
await navigator.permissions.query({name: 'clipboard-read'});
resolve(PermissionStatus.state === 'granted');
})
)";
constexpr char kRequestClipboardRead[] = R"(
new Promise(async resolve => {
try {
const read_promise = await navigator.clipboard.readText();
resolve('granted');
} catch(error) {
resolve('denied');
}
})
)";
constexpr char kCheckClipboardWrite[] = R"(
new Promise(async resolve => {
const PermissionStatus =
await navigator.permissions.query({name: 'clipboard-write'});
resolve(PermissionStatus.state === 'granted');
})
)";
constexpr char kRequestClipboardWrite[] = R"(
new Promise(async resolve => {
try {
const write_promise = await navigator.clipboard.writeText("texts");
resolve('granted');
} catch(error) {
resolve('denied');
}
})
)";
constexpr char kIframePolicy[] = "geolocation *; camera *";
void VerifyPermissionsAllowed(content::RenderFrameHost* main_rfh,
const std::string& request_permission_script,
const std::string& check_permission_script) {
ASSERT_EQ(false, content::EvalJs(main_rfh, check_permission_script));
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(
content::WebContents::FromRenderFrameHost(main_rfh));
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
// Move the web contents to the foreground.
main_rfh->GetView()->Focus();
ASSERT_TRUE(main_rfh->GetView()->HasFocus());
EXPECT_EQ("granted", content::EvalJs(main_rfh, request_permission_script));
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
// Disable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::NONE);
EXPECT_EQ(true, content::EvalJs(main_rfh, check_permission_script));
}
// `test_rfh` is either an embedded iframe or an external popup window.
void VerifyPermission(content::WebContents* opener_or_embedder_contents,
content::RenderFrameHost* test_rfh,
const std::string& request_permission_script,
const std::string& check_permission_script) {
content::RenderFrameHost* opener_rfh =
opener_or_embedder_contents->GetPrimaryMainFrame();
ASSERT_EQ(false, content::EvalJs(opener_rfh, check_permission_script));
ASSERT_EQ(false, content::EvalJs(test_rfh, check_permission_script));
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(
opener_or_embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
// Move the web contents to the foreground.
opener_rfh->GetView()->Focus();
ASSERT_TRUE(opener_rfh->GetView()->HasFocus());
// Request permission on the opener or embedder contents.
EXPECT_EQ("granted", content::EvalJs(opener_rfh, request_permission_script));
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
// Disable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::NONE);
EXPECT_EQ(true, content::EvalJs(opener_rfh, check_permission_script));
// Verify permissions on the test RFH.
EXPECT_EQ(true, content::EvalJs(test_rfh, check_permission_script));
// Request permission on the test RFH.
test_rfh->GetView()->Focus();
ASSERT_TRUE(test_rfh->GetView()->HasFocus());
EXPECT_EQ("granted", content::EvalJs(test_rfh, request_permission_script));
// There should not be the 2nd prompt.
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
}
void VerifyPermissionsDeniedForFencedFrame(
content::WebContents* embedder_contents,
content::RenderFrameHost* fenced_rfh,
const std::string& request_permission_script,
const std::string& check_permission_script,
bool default_granted) {
// If granted by default, permission prompt factory will not receive requests.
int request_count = default_granted ? 0 : 1;
content::RenderFrameHost* embedder_main_rfh =
embedder_contents->GetPrimaryMainFrame();
ASSERT_EQ(content::EvalJs(embedder_main_rfh, check_permission_script),
default_granted);
ASSERT_EQ(false, content::EvalJs(fenced_rfh, check_permission_script));
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
// Move the web contents to the foreground.
embedder_main_rfh->GetView()->Focus();
ASSERT_TRUE(embedder_main_rfh->GetView()->HasFocus());
// Request permission on the embedder contents.
EXPECT_EQ("granted",
content::EvalJs(embedder_main_rfh, request_permission_script));
EXPECT_EQ(request_count, bubble_factory->TotalRequestCount());
// Disable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::NONE);
EXPECT_EQ(true, content::EvalJs(embedder_main_rfh, check_permission_script));
// MPArch RFH is not allowed to verify permissions.
EXPECT_EQ(false, content::EvalJs(fenced_rfh, check_permission_script));
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
// Request permission on the test RFH.
fenced_rfh->GetView()->Focus();
ASSERT_TRUE(fenced_rfh->GetView()->HasFocus());
EXPECT_EQ("denied", content::EvalJs(fenced_rfh, request_permission_script));
// There should not be the 2nd prompt.
EXPECT_EQ(request_count, bubble_factory->TotalRequestCount());
}
void VerifyPermissionsDeniedForFencedFrame(
content::WebContents* embedder_contents,
content::RenderFrameHost* fenced_rfh) {
const struct {
std::string check_permission;
std::string request_permission;
bool default_granted = false;
} kTests[] = {
{kCheckNotifications, kRequestNotifications},
{kCheckGeolocation, kRequestGeolocation},
{kCheckCamera, kRequestCamera},
{kCheckClipboardRead, kRequestClipboardRead},
{kCheckClipboardWrite, kRequestClipboardWrite, /*default_granted=*/true},
};
for (const auto& test : kTests) {
VerifyPermissionsDeniedForFencedFrame(
embedder_contents, fenced_rfh, test.request_permission,
test.check_permission, test.default_granted);
}
}
// getUserMedia requires focus. It should be verified only on a popup window.
void VerifyPopupWindowGetUserMedia(content::WebContents* opener_contents,
content::WebContents* popup_contents) {
content::RenderFrameHost* opener_rfh = opener_contents->GetPrimaryMainFrame();
content::RenderFrameHost* popup_rfh = popup_contents->GetPrimaryMainFrame();
ASSERT_EQ(false, content::EvalJs(opener_rfh, kCheckCamera));
ASSERT_EQ(false, content::EvalJs(popup_rfh, kCheckCamera));
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(popup_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
// Move the web contents to the foreground.
popup_rfh->GetView()->Focus();
ASSERT_TRUE(popup_rfh->GetView()->HasFocus());
// Request permission on the popup RenderFrameHost.
EXPECT_EQ("granted", content::EvalJs(popup_rfh, kRequestCamera));
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
// Disable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::NONE);
EXPECT_EQ(true, content::EvalJs(popup_rfh, kCheckCamera));
EXPECT_EQ(true, content::EvalJs(opener_rfh, kCheckCamera));
}
void VerifyPermissionsAllowed(content::RenderFrameHost* rfh) {
const struct {
std::string check_permission;
std::string request_permission;
} kTests[] = {
{kCheckNotifications, kRequestNotifications},
{kCheckGeolocation, kRequestGeolocation},
{kCheckCamera, kRequestCamera},
};
for (const auto& test : kTests) {
VerifyPermissionsAllowed(rfh, test.request_permission,
test.check_permission);
}
}
void VerifyPermissionsExceptGetUserMedia(
content::WebContents* opener_or_embedder_contents,
content::RenderFrameHost* test_rfh) {
const struct {
std::string check_permission;
std::string request_permission;
} kTests[] = {
{kCheckNotifications, kRequestNotifications},
{kCheckGeolocation, kRequestGeolocation},
};
for (const auto& test : kTests) {
VerifyPermission(opener_or_embedder_contents, test_rfh,
test.request_permission, test.check_permission);
}
}
void VerifyPermissionsAllPermissions(
content::WebContents* opener_or_embedder_contents,
content::RenderFrameHost* test_rfh) {
const struct {
std::string check_permission;
std::string request_permission;
} kTests[] = {
{kCheckNotifications, kRequestNotifications},
{kCheckCamera, kRequestCamera},
{kCheckGeolocation, kRequestGeolocation},
};
for (const auto& test : kTests) {
VerifyPermission(opener_or_embedder_contents, test_rfh,
test.request_permission, test.check_permission);
}
}
void VerifyPermissionsForFile(content::RenderFrameHost* rfh,
bool expect_granted) {
const struct {
std::string check_permission;
std::string request_permission;
} kTests[] = {
{kCheckNotifications, kRequestNotifications},
{kCheckCamera, kRequestCamera},
{kCheckGeolocation, kRequestGeolocation},
};
for (const auto& test : kTests) {
ASSERT_EQ(false, content::EvalJs(rfh, test.check_permission));
EXPECT_EQ(expect_granted ? "granted" : "denied",
content::EvalJs(rfh, test.request_permission));
ASSERT_EQ(false, content::EvalJs(rfh, test.check_permission));
}
}
// Tests of permissions behavior for an inheritance and embedding of an
// origin.
class PermissionsSecurityModelInteractiveUITest
: public InteractiveBrowserTest {
public:
PermissionsSecurityModelInteractiveUITest() {
geolocation_overrider_ =
std::make_unique<device::ScopedGeolocationOverrider>(0, 0);
}
PermissionsSecurityModelInteractiveUITest(
const PermissionsSecurityModelInteractiveUITest&) = delete;
PermissionsSecurityModelInteractiveUITest& operator=(
const PermissionsSecurityModelInteractiveUITest&) = delete;
~PermissionsSecurityModelInteractiveUITest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
embedder_support::kDisablePopupBlocking);
}
private:
std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
};
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
EmbedIframeAboutBlank) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/iframe_about_blank.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::RenderFrameHost* about_blank_iframe =
content::FrameMatchingPredicate(
main_rfh->GetPage(), base::BindRepeating(&content::FrameMatchesName,
"about_blank_iframe"));
ASSERT_TRUE(about_blank_iframe);
VerifyPermissionsAllPermissions(embedder_contents, about_blank_iframe);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
WindowOpenAboutBlank) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* opener_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(opener_contents);
content::WebContents* popup_contents =
OpenPopup(browser(), GURL("about:blank"));
ASSERT_TRUE(popup_contents);
VerifyPermissionsExceptGetUserMedia(opener_contents,
popup_contents->GetPrimaryMainFrame());
VerifyPopupWindowGetUserMedia(opener_contents, popup_contents);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
WindowOpenAboutBlankToUseQuiet) {
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kEnableQuietNotificationPermissionUi, true);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* opener_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(opener_contents);
content::WebContents* popup_contents =
OpenPopup(browser(), GURL("about:blank"));
ASSERT_TRUE(popup_contents);
VerifyPermissionsExceptGetUserMedia(opener_contents,
popup_contents->GetPrimaryMainFrame());
VerifyPopupWindowGetUserMedia(opener_contents, popup_contents);
}
// `about:srcdoc` supports only embedder WebContents, hence no test for opener.
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
EmbedIframeSrcDoc) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/iframe_srcdoc.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::RenderFrameHost* srcdoc_iframe = content::FrameMatchingPredicate(
main_rfh->GetPage(),
base::BindRepeating(&content::FrameMatchesName, "srcdoc_iframe"));
ASSERT_TRUE(srcdoc_iframe);
VerifyPermissionsAllPermissions(embedder_contents, srcdoc_iframe);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
EmbedIframeBlob) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/iframe_blob.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::RenderFrameHost* blob_iframe_rfh = content::FrameMatchingPredicate(
main_rfh->GetPage(),
base::BindRepeating(&content::FrameMatchesName, "blob_iframe"));
ASSERT_TRUE(blob_iframe_rfh);
EXPECT_TRUE(blob_iframe_rfh->GetLastCommittedURL().SchemeIsBlob());
VerifyPermissionsAllPermissions(embedder_contents, blob_iframe_rfh);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
WindowOpenBlob) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* opener_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(opener_contents);
content::WebContents* blob_popup_contents =
OpenPopup(browser(), CreateBlobURL(main_rfh));
ASSERT_TRUE(blob_popup_contents);
EXPECT_TRUE(blob_popup_contents->GetLastCommittedURL().SchemeIsBlob());
VerifyPermissionsExceptGetUserMedia(
opener_contents, blob_popup_contents->GetPrimaryMainFrame());
VerifyPopupWindowGetUserMedia(opener_contents, blob_popup_contents);
}
// Renderer navigation for "filesystem:" is not allowed.
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
WindowOpenFileSystemRendererNavigationNotAllowed) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* opener_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(opener_contents);
content::WebContents* popup_iframe =
OpenPopup(browser(), CreateFilesystemURL(main_rfh));
ASSERT_TRUE(popup_iframe);
// Not allowed to navigate top frame to filesystem URL.
EXPECT_EQ("", popup_iframe->GetLastCommittedURL().scheme());
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
WindowOpenFileSystemBrowserNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* opener_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(opener_contents);
GURL fs_url = CreateFilesystemURL(main_rfh);
content::WebContents* popup_iframe_web_contents =
OpenPopup(browser(), fs_url);
ASSERT_TRUE(popup_iframe_web_contents);
EXPECT_EQ("", popup_iframe_web_contents->GetLastCommittedURL().scheme());
content::RenderFrameHost* popup_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
chrome::FindBrowserWithTab(popup_iframe_web_contents), fs_url, 1);
EXPECT_TRUE(popup_rfh->GetLastCommittedURL().SchemeIsFileSystem());
VerifyPermissionsExceptGetUserMedia(opener_contents, popup_rfh);
VerifyPopupWindowGetUserMedia(
opener_contents, content::WebContents::FromRenderFrameHost(popup_rfh));
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
TopIframeFile) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedder_contents);
EXPECT_FALSE(embedder_contents->GetLastCommittedURL().SchemeIsFile());
main_rfh = ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), CreateFileURL(), 1);
EXPECT_TRUE(main_rfh->GetLastCommittedURL().SchemeIsFile());
EXPECT_TRUE(main_rfh->GetLastCommittedOrigin().GetURL().SchemeIsFile());
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/true);
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::DENY_ALL);
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/false);
}
// Permissions granted for a file should not leak to another file.
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
PermissionDoesNotLeakToAnotherFile) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedder_contents);
EXPECT_FALSE(embedder_contents->GetLastCommittedURL().SchemeIsFile());
GURL file1_url = CreateFileURL();
main_rfh = ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), file1_url, 1);
EXPECT_TRUE(main_rfh->GetLastCommittedURL().SchemeIsFile());
EXPECT_TRUE(main_rfh->GetLastCommittedOrigin().GetURL().SchemeIsFile());
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/true);
GURL file2_url = CreateFileURL(FILE_PATH_LITERAL("title2.html"));
main_rfh = ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), file2_url, 1);
EXPECT_TRUE(main_rfh->GetLastCommittedURL().SchemeIsFile());
EXPECT_TRUE(main_rfh->GetLastCommittedOrigin().GetURL().SchemeIsFile());
EXPECT_EQ(file2_url.spec(), main_rfh->GetLastCommittedURL().spec());
EXPECT_NE(file1_url.spec(), file2_url.spec());
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::DENY_ALL);
// Permission is failed because it is another file.
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/false);
}
// Flaky - https://crbug.com/1289985
#if BUILDFLAG(IS_WIN)
#define MAYBE_UniversalAccessFromFileUrls UniversalAccessFromFileUrls
#else
#define MAYBE_UniversalAccessFromFileUrls DISABLED_UniversalAccessFromFileUrls
#endif
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
MAYBE_UniversalAccessFromFileUrls) {
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedder_contents);
// Activate the preference to allow universal access from file URLs.
blink::web_pref::WebPreferences prefs =
embedder_contents->GetOrCreateWebPreferences();
prefs.allow_universal_access_from_file_urls = true;
embedder_contents->SetWebPreferences(prefs);
const GURL url(embedded_test_server()->GetURL("/empty.html"));
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url,
1);
ASSERT_TRUE(main_rfh);
EXPECT_FALSE(main_rfh->GetLastCommittedURL().SchemeIsFile());
main_rfh = ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), CreateFileURL(), 1);
EXPECT_TRUE(main_rfh->GetLastCommittedURL().SchemeIsFile());
EXPECT_TRUE(main_rfh->GetLastCommittedOrigin().GetURL().SchemeIsFile());
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/true);
content::EvalJsResult result = content::EvalJs(
embedder_contents, "history.pushState({}, {}, 'https://chromium.org');");
EXPECT_TRUE(result.is_ok());
EXPECT_EQ("https://chromium.org/", main_rfh->GetLastCommittedURL().spec());
EXPECT_TRUE(main_rfh->GetLastCommittedOrigin().GetURL().SchemeIsFile());
// `https://chromium.org` is used for permissions verification.
#if BUILDFLAG(IS_ANDROID)
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/false);
#else
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/true);
#endif
}
// Verifies that permissions are not supported for file:/// with changed URL to
// `about:blank`.
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
UniversalAccessFromFileUrlsAboutBlank) {
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedder_contents);
// Activate the preference to allow universal access from file URLs.
blink::web_pref::WebPreferences prefs =
embedder_contents->GetOrCreateWebPreferences();
prefs.allow_universal_access_from_file_urls = true;
embedder_contents->SetWebPreferences(prefs);
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), CreateFileURL(), 1);
ASSERT_TRUE(main_rfh);
EXPECT_TRUE(main_rfh->GetLastCommittedURL().SchemeIsFile());
EXPECT_TRUE(main_rfh->GetLastCommittedOrigin().GetURL().SchemeIsFile());
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/true);
content::EvalJsResult result = content::EvalJs(
embedder_contents, "history.pushState({}, {}, 'about:blank');");
EXPECT_TRUE(result.is_ok());
EXPECT_EQ("about:blank", main_rfh->GetLastCommittedURL().spec());
EXPECT_TRUE(main_rfh->GetLastCommittedURL().IsAboutBlank());
#if BUILDFLAG(IS_ANDROID)
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/false);
#else
VerifyPermissionsForFile(main_rfh, /*expect_granted=*/true);
#endif
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
PermissionRequestOnNtpUseDseOrigin) {
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedder_contents);
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), GURL(chrome::kChromeUINewTabURL), 1);
content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
ASSERT_TRUE(main_rfh);
EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
embedder_contents->GetLastCommittedURL());
EXPECT_EQ(GURL(chrome::kChromeUINewTabPageURL),
main_rfh->GetLastCommittedOrigin().GetURL());
EXPECT_EQ(false, content::EvalJs(main_rfh, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
EXPECT_EQ("granted",
content::EvalJs(main_rfh, kRequestMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::NONE);
EXPECT_EQ(true, content::EvalJs(main_rfh, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
MicActivityIndicatorOnNtpUseDseOrigin) {
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedder_contents);
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), GURL(chrome::kChromeUINewTabURL), 1);
content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
ASSERT_TRUE(main_rfh);
EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
embedder_contents->GetLastCommittedURL());
EXPECT_EQ(GURL(chrome::kChromeUINewTabPageURL),
main_rfh->GetLastCommittedOrigin().GetURL());
EXPECT_EQ(false, content::EvalJs(main_rfh, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
permissions::PermissionRequestManager* permission_request_manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(
permission_request_manager);
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
EXPECT_EQ("granted",
content::EvalJs(main_rfh, kRequestMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Mic request from an NTP will be changed to DSE origin.
EXPECT_TRUE(
bubble_factory->RequestOriginSeen(GURL("https://www.google.com")));
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::NONE);
EXPECT_EQ(true, content::EvalJs(main_rfh, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
content_settings::PageSpecificContentSettings* page_content_settings =
content_settings::PageSpecificContentSettings::GetForFrame(main_rfh);
// Media stream origin on NTP should equal to DSE.
EXPECT_EQ(page_content_settings->media_stream_access_origin(),
GURL("https://www.google.com"));
}
// Test that a permission prompt bubble will be shown on NTP despite the empty
// address bar.
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelInteractiveUITest,
PermissionRequestOnNtpIsNotAutoIgnored) {
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedder_contents);
content::RenderFrameHost* main_rfh =
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), GURL(chrome::kChromeUINewTabURL), 1);
content::WebContents::FromRenderFrameHost(main_rfh)->Focus();
ASSERT_TRUE(main_rfh);
EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
embedder_contents->GetLastCommittedURL());
EXPECT_EQ(GURL(chrome::kChromeUINewTabPageURL),
main_rfh->GetLastCommittedOrigin().GetURL());
EXPECT_EQ(false, content::EvalJs(main_rfh, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
auto* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
permissions::PermissionRequestObserver observer(embedder_contents);
EXPECT_FALSE(manager->IsRequestInProgress());
EXPECT_TRUE(content::ExecJs(
main_rfh, kRequestMicrophone,
content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
// Wait until a permission request is shown.
observer.Wait();
EXPECT_TRUE(manager->IsRequestInProgress());
EXPECT_TRUE(observer.request_shown());
manager->Accept();
EXPECT_EQ(true, content::EvalJs(main_rfh, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
}
class PermissionsSecurityModelHTTPS
: public PermissionsSecurityModelInteractiveUITest {
public:
PermissionsSecurityModelHTTPS()
: https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
~PermissionsSecurityModelHTTPS() override = default;
PermissionsSecurityModelHTTPS(const PermissionsSecurityModelHTTPS&) = delete;
PermissionsSecurityModelHTTPS& operator=(
const PermissionsSecurityModelHTTPS&) = delete;
void SetUpCommandLine(base::CommandLine* command_line) override {
PermissionsSecurityModelInteractiveUITest::SetUpCommandLine(command_line);
mock_cert_verifier_.SetUpCommandLine(command_line);
}
void SetUpInProcessBrowserTestFixture() override {
PermissionsSecurityModelInteractiveUITest::
SetUpInProcessBrowserTestFixture();
mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
}
void TearDownInProcessBrowserTestFixture() override {
PermissionsSecurityModelInteractiveUITest::
TearDownInProcessBrowserTestFixture();
mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
}
void SetUpOnMainThread() override {
mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
host_resolver()->AddRule("*", "127.0.0.1");
https_test_server_.ServeFilesFromDirectory(
base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
PermissionsSecurityModelInteractiveUITest::SetUpOnMainThread();
ASSERT_TRUE(GetHttpsServer()->Start());
}
protected:
GURL GetURL(const std::string& hostname, const std::string& relative_url) {
return https_test_server_.GetURL(hostname, relative_url);
}
net::EmbeddedTestServer* GetHttpsServer() { return &https_test_server_; }
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
// Navigate the main frame toward |url|, returns the new RenderFrameHost.
content::RenderFrameHost* NavigateAndCheckPermissionState(GURL url) {
url::Origin origin = url::Origin::Create(url);
content::WebContents* embedder_contents = GetWebContents();
EXPECT_TRUE(content::NavigateToURL(embedder_contents, url));
content::RenderFrameHost* main_rfh =
embedder_contents->GetPrimaryMainFrame();
EXPECT_EQ(origin, main_rfh->GetLastCommittedOrigin());
// By default permissions are not allowed.
CheckPermissionState(main_rfh, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false,
/*camera_allowed=*/false);
return main_rfh;
}
void CheckPermissionState(content::RenderFrameHost* rfh,
bool notifications_allowed,
bool geolocation_allowed,
bool camera_allowed) {
EXPECT_EQ(geolocation_allowed, content::EvalJs(rfh, kCheckGeolocation));
EXPECT_EQ(notifications_allowed, content::EvalJs(rfh, kCheckNotifications));
EXPECT_EQ(camera_allowed, content::EvalJs(rfh, kCheckCamera));
}
GURL GetMainFrameURL() { return GetURL("a.test", "/title1.html"); }
GURL GetChildFrameURL() { return GetURL("b.test", "/title1.html"); }
void RequestPermissions(content::RenderFrameHost* rfh, bool expected) {
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(
GetWebContents());
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
std::string expected_result = expected ? "granted" : "denied";
EXPECT_EQ(expected_result, content::EvalJs(rfh, kRequestGeolocation));
EXPECT_EQ(expected_result, content::EvalJs(rfh, kRequestCamera));
// Notifications permission cannot be granted for an embedded cross-origin
// iframe.
bool expected_notifications =
expected && (rfh->GetLastCommittedOrigin() !=
url::Origin::Create(GetChildFrameURL()));
expected_result = expected_notifications ? "granted" : "denied";
EXPECT_EQ(expected_result, content::EvalJs(rfh, kRequestNotifications));
CheckPermissionState(rfh, expected_notifications, expected, expected);
}
void RequestPermissionAndGrant(content::RenderFrameHost* rfh,
std::string request_script) {
auto* manager = permissions::PermissionRequestManager::FromWebContents(
GetWebContents());
permissions::PermissionRequestObserver observer(GetWebContents());
EXPECT_FALSE(manager->IsRequestInProgress());
EXPECT_TRUE(content::ExecJs(
rfh, request_script,
content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
// Wait until a permission request is shown.
observer.Wait();
EXPECT_TRUE(manager->IsRequestInProgress());
EXPECT_TRUE(observer.request_shown());
manager->Accept();
}
private:
content::ContentMockCertVerifier mock_cert_verifier_;
net::EmbeddedTestServer https_test_server_;
};
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS, MainFrameTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
VerifyPermissionsAllowed(main_rfh);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS,
OverridesForDevToolsTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
content::PermissionController* permission_controller =
main_rfh->GetBrowserContext()->GetPermissionController();
url::Origin origin = url::Origin::Create(GetMainFrameURL());
SetPermissionControllerOverrideForDevTools(
permission_controller, origin, blink::PermissionType::GEOLOCATION,
blink::mojom::PermissionStatus::GRANTED);
CheckPermissionState(main_rfh, /*notifications_allowed=*/false,
/*geolocation_allowed=*/true, /*camera_allowed=*/false);
SetPermissionControllerOverrideForDevTools(
permission_controller, origin, blink::PermissionType::VIDEO_CAPTURE,
blink::mojom::PermissionStatus::GRANTED);
CheckPermissionState(main_rfh, /*notifications_allowed=*/false,
/*geolocation_allowed=*/true, /*camera_allowed=*/true);
SetPermissionControllerOverrideForDevTools(
permission_controller, origin, blink::PermissionType::NOTIFICATIONS,
blink::mojom::PermissionStatus::GRANTED);
CheckPermissionState(main_rfh, /*notifications_allowed=*/true,
/*geolocation_allowed=*/true, /*camera_allowed=*/true);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS,
TopFramePermissionRequest) {
base::HistogramTester histograms;
content::WebContents* web_contents = GetWebContents();
EXPECT_TRUE(content::NavigateToURL(web_contents, GetMainFrameURL()));
RequestPermissionAndGrant(web_contents->GetPrimaryMainFrame(),
kRequestGeolocation);
histograms.ExpectUniqueSample("Permissions.Request.SameOrigin.MainFrame",
blink::PermissionType::GEOLOCATION, 1);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS,
SubFrameSameOriginPermissionRequest) {
base::HistogramTester histograms;
content::WebContents* web_contents = GetWebContents();
EXPECT_TRUE(content::NavigateToURL(web_contents, GetMainFrameURL()));
content::RenderFrameHost* sameorigin_subframe = CreateIframe(
web_contents->GetPrimaryMainFrame(), GetMainFrameURL(), kIframePolicy);
ASSERT_TRUE(sameorigin_subframe);
RequestPermissionAndGrant(sameorigin_subframe, kRequestGeolocation);
histograms.ExpectUniqueSample("Permissions.Request.SameOrigin.SubFrame",
blink::PermissionType::GEOLOCATION, 1);
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS,
SubFrameCrossOriginPermissionRequest) {
base::HistogramTester histograms;
content::WebContents* web_contents = GetWebContents();
EXPECT_TRUE(content::NavigateToURL(web_contents, GetMainFrameURL()));
content::RenderFrameHost* crossorigin_subframe = CreateIframe(
web_contents->GetPrimaryMainFrame(), GetChildFrameURL(), kIframePolicy);
ASSERT_TRUE(crossorigin_subframe);
RequestPermissionAndGrant(crossorigin_subframe, kRequestGeolocation);
histograms.ExpectUniqueSample("Permissions.Request.CrossOrigin",
blink::PermissionType::GEOLOCATION, 1);
}
// Tests multiple layers of embedded iframes a.com(b.com(a.com)).
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS,
DeepSubFrameCrossOriginPermissionRequest) {
base::HistogramTester histograms;
content::WebContents* web_contents = GetWebContents();
EXPECT_TRUE(content::NavigateToURL(web_contents, GetMainFrameURL()));
content::RenderFrameHost* crossorigin_subframe = CreateIframe(
web_contents->GetPrimaryMainFrame(), GetChildFrameURL(), kIframePolicy);
ASSERT_TRUE(crossorigin_subframe);
content::RenderFrameHost* crossorigin_sub_subframe =
CreateIframe(crossorigin_subframe, GetMainFrameURL(), kIframePolicy);
ASSERT_TRUE(crossorigin_sub_subframe);
EXPECT_TRUE(
crossorigin_sub_subframe->GetLastCommittedOrigin().IsSameOriginWith(
web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin()));
RequestPermissionAndGrant(crossorigin_sub_subframe, kRequestGeolocation);
histograms.ExpectUniqueSample("Permissions.Request.CrossOrigin",
blink::PermissionType::GEOLOCATION, 1);
}
IN_PROC_BROWSER_TEST_F(
PermissionsSecurityModelHTTPS,
MainFrameAndCrossOriginIframeWithoutPermissionsPolicyTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
content::RenderFrameHost* subframe =
CreateIframe(main_rfh, GetChildFrameURL());
ASSERT_TRUE(subframe);
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
HostContentSettingsMap* HCSM = HostContentSettingsMapFactory::GetForProfile(
main_rfh->GetBrowserContext());
HCSM->SetContentSettingDefaultScope(GetChildFrameURL(), GetChildFrameURL(),
ContentSettingsType::GEOLOCATION,
CONTENT_SETTING_ALLOW);
HCSM->SetContentSettingDefaultScope(GetMainFrameURL(), GetMainFrameURL(),
ContentSettingsType::GEOLOCATION,
CONTENT_SETTING_ALLOW);
HCSM->SetContentSettingDefaultScope(GetChildFrameURL(), GetChildFrameURL(),
ContentSettingsType::MEDIASTREAM_CAMERA,
CONTENT_SETTING_ALLOW);
HCSM->SetContentSettingDefaultScope(GetMainFrameURL(), GetMainFrameURL(),
ContentSettingsType::MEDIASTREAM_CAMERA,
CONTENT_SETTING_ALLOW);
CheckPermissionState(main_rfh, /*notifications_allowed=*/false,
/*geolocation_allowed=*/true, /*camera_allowed=*/true);
// Geolocation and Camera are not allowed for `subframe` because it is a
// cross-origin child iframe in `main_rfh`.
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
HCSM->SetContentSettingDefaultScope(GetChildFrameURL(), GetChildFrameURL(),
ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_ALLOW);
CheckPermissionState(main_rfh, /*notifications_allowed=*/false,
/*geolocation_allowed=*/true, /*camera_allowed=*/true);
// Notifications permission is allowed for `subframe` even despite it being a
// cross-origin child iframe in `main_rfh`.
CheckPermissionState(subframe, /*notifications_allowed=*/true,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
HCSM->SetContentSettingDefaultScope(GetMainFrameURL(), GetMainFrameURL(),
ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_ALLOW);
CheckPermissionState(main_rfh, /*notifications_allowed=*/true,
/*geolocation_allowed=*/true, /*camera_allowed=*/true);
CheckPermissionState(subframe, /*notifications_allowed=*/true,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
}
// Preconditions: The embedded cross-origin iframe has no permissions policy.
// Permissions requested from both the main frame and the embedded cross-origin
// iframe.
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS,
RequestPermissionsOnlyFromMainFrameTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
content::RenderFrameHost* subframe =
CreateIframe(main_rfh, GetChildFrameURL());
ASSERT_TRUE(subframe);
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
RequestPermissions(main_rfh, true);
RequestPermissions(subframe, false);
}
// Preconditions: The embedded cross-origin iframe has no permissions policy.
// Permissions requested only from the embedded cross-origin iframe.
IN_PROC_BROWSER_TEST_F(
PermissionsSecurityModelHTTPS,
RequestPermissionsOnlyFromCrossOriginIframeWithoutPermissionsPolicyTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
content::RenderFrameHost* subframe =
CreateIframe(main_rfh, GetChildFrameURL());
ASSERT_TRUE(subframe);
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
RequestPermissions(subframe, false);
CheckPermissionState(main_rfh, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
}
// Preconditions: The embedded cross-origin iframe has permissions policy.
// Permissions requested only from the embedded cross-origin iframe.
IN_PROC_BROWSER_TEST_F(
PermissionsSecurityModelHTTPS,
RequestPermissionsOnlyFromCrossOriginIframeWithPermissionsPolicyTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
content::RenderFrameHost* subframe =
CreateIframe(main_rfh, GetChildFrameURL(), kIframePolicy);
ASSERT_TRUE(subframe);
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
RequestPermissions(subframe, true);
}
// Preconditions: The embedded cross-origin iframe has permissions policy.
// Permissions requested from the main frame only.
IN_PROC_BROWSER_TEST_F(
PermissionsSecurityModelHTTPS,
RequestPermissionsOnlyFromMainFrameCrossOriginIframeWithPermissionsPolicyTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
content::RenderFrameHost* subframe =
CreateIframe(main_rfh, GetChildFrameURL(), kIframePolicy);
ASSERT_TRUE(subframe);
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
RequestPermissions(main_rfh, true);
// Notifications permission is not allowed for an embedded cross-origin
// iframe.
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/true, /*camera_allowed=*/true);
}
// Preconditions: The embedded cross-origin iframe has permissions policy.
// Permissions requested from both the main frame and the embedded cross-origin
// iframe.
IN_PROC_BROWSER_TEST_F(
PermissionsSecurityModelHTTPS,
RequestPermissionsFromBothMainframeAndCrossOriginIframeWithPermissionsPolicyTest) {
content::RenderFrameHost* main_rfh =
NavigateAndCheckPermissionState(GetMainFrameURL());
content::RenderFrameHost* subframe =
CreateIframe(main_rfh, GetChildFrameURL(), kIframePolicy);
ASSERT_TRUE(subframe);
CheckPermissionState(subframe, /*notifications_allowed=*/false,
/*geolocation_allowed=*/false, /*camera_allowed=*/false);
RequestPermissions(main_rfh, true);
RequestPermissions(subframe, true);
}
class PermissionsRequestedFromFencedFrameTest
: public PermissionsSecurityModelInteractiveUITest {
public:
PermissionsRequestedFromFencedFrameTest() = default;
~PermissionsRequestedFromFencedFrameTest() override = default;
PermissionsRequestedFromFencedFrameTest(
const PermissionsRequestedFromFencedFrameTest&) = delete;
PermissionsRequestedFromFencedFrameTest& operator=(
const PermissionsRequestedFromFencedFrameTest&) = delete;
private:
base::test::ScopedFeatureList scoped_feature_list_;
protected:
content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
return fenced_frame_helper_;
}
private:
content::test::FencedFrameTestHelper fenced_frame_helper_;
};
IN_PROC_BROWSER_TEST_F(PermissionsRequestedFromFencedFrameTest,
PermissionsRequestedFromFencedFrameTest) {
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Load a fenced frame.
GURL fenced_frame_url =
embedded_test_server()->GetURL("/fenced_frames/title1.html");
content::RenderFrameHost* fenced_frame_host =
fenced_frame_test_helper().CreateFencedFrame(
web_contents->GetPrimaryMainFrame(), fenced_frame_url);
ASSERT_TRUE(fenced_frame_host);
VerifyPermissionsDeniedForFencedFrame(web_contents, fenced_frame_host);
}
class PermissionRequestWithPrerendererTest
: public PermissionsSecurityModelInteractiveUITest {
public:
PermissionRequestWithPrerendererTest()
: prerender_helper_(base::BindRepeating(
&PermissionRequestWithPrerendererTest::GetActiveWebContents,
base::Unretained(this))) {}
~PermissionRequestWithPrerendererTest() override = default;
PermissionRequestWithPrerendererTest(
const PermissionRequestWithPrerendererTest&) = delete;
PermissionRequestWithPrerendererTest& operator=(
const PermissionRequestWithPrerendererTest&) = delete;
void SetUp() override {
prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
PermissionsSecurityModelInteractiveUITest::SetUp();
}
content::WebContents* GetActiveWebContents() {
return chrome_test_utils::GetActiveWebContents(this);
}
content::test::PrerenderTestHelper& prerender_helper() {
return prerender_helper_;
}
private:
content::test::PrerenderTestHelper prerender_helper_;
};
IN_PROC_BROWSER_TEST_F(PermissionRequestWithPrerendererTest,
PermissionsRequestedFromPrerendererTest) {
ASSERT_TRUE(embedded_test_server()->Start());
// Navigate to an initial page.
GURL url = embedded_test_server()->GetURL("/empty.html");
ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
EXPECT_FALSE(
GetActiveWebContents()
->GetPrimaryMainFrame()
->IsInactiveAndDisallowActivation(
content::DisallowActivationReasonId::kRequestPermission));
// Start a prerender.
GURL prerender_url =
embedded_test_server()->GetURL("/prerenderer_geolocation_test.html");
prerender_helper().AddPrerender(prerender_url);
content::FrameTreeNodeId host_id =
prerender_helper().AddPrerender(prerender_url);
content::RenderFrameHost* prerender_render_frame_host =
prerender_helper().GetPrerenderedMainFrameHost(host_id);
ASSERT_TRUE(prerender_render_frame_host);
// The main frame of an outer document is not a prerenderer.
EXPECT_FALSE(
GetActiveWebContents()
->GetPrimaryMainFrame()
->IsInactiveAndDisallowActivation(
content::DisallowActivationReasonId::kRequestPermission));
// The main frame of a newly created frame tree is a prerenderer. It is
// inactive, all permission requests should be automatically denied.
// (crbug.com/1126305): Do not use RFH::IsInactiveAndDisallowActivation() as
// it will stop prerendering process.
EXPECT_EQ(prerender_render_frame_host->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kPrerendering);
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(
GetActiveWebContents());
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
EXPECT_EQ(
true,
content::ExecJs(
prerender_render_frame_host, "accessGeolocation();",
content::EvalJsOptions::EXECUTE_SCRIPT_NO_USER_GESTURE |
content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
// Run a event loop so the page can fail the test.
EXPECT_TRUE(content::ExecJs(prerender_render_frame_host, "runLoop();"));
// Avoid race conditions, which can lead to a situation where we check for a
// permission prompt before it was shown.
base::RunLoop().RunUntilIdle();
// `accessGeolocation` will request Geolocation permission which should be
// automatically accepted, but it will not happen here, because the
// permissions API is deferred in Prerenderer.
EXPECT_EQ(0, bubble_factory->TotalRequestCount());
// Activate the prerenderer.
prerender_helper().NavigatePrimaryPage(prerender_url);
EXPECT_EQ(prerender_render_frame_host->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kActive);
// Wait for the completion of `accessGeolocation`.
EXPECT_EQ(true, content::EvalJs(prerender_render_frame_host, "result;"));
// Check the event sequence seen in the prerendered page.
content::EvalJsResult results =
content::EvalJs(prerender_render_frame_host, "eventsSeen");
std::vector<std::string> eventsSeen;
const base::Value::List& results_list = results.ExtractList();
for (const auto& result : results_list) {
eventsSeen.push_back(result.GetString());
}
EXPECT_THAT(eventsSeen, testing::ElementsAreArray(
{"accessGeolocation (prerendering: true)",
"prerenderingchange (prerendering: false)",
"getCurrentPosition (prerendering: false)"}));
// Wait until a permission prompt is resolved.
base::RunLoop().RunUntilIdle();
// After the prerenderer activation, a deferred permission request will be
// displayed.
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
}
IN_PROC_BROWSER_TEST_F(PermissionsSecurityModelHTTPS,
PermissionsRequestedFromBFCacheTest) {
GURL url_a = GetURL("a.test", "/title1.html");
GURL url_b = GetURL("b.test", "/title1.html");
url::Origin origin_a = url::Origin::Create(url_a);
url::Origin origin_b = url::Origin::Create(url_b);
content::WebContents* embedder_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// 1) Navigate to A.
EXPECT_TRUE(content::NavigateToURL(embedder_contents, url_a));
content::RenderFrameHost* rfh_a = embedder_contents->GetPrimaryMainFrame();
ASSERT_TRUE(rfh_a);
EXPECT_EQ(origin_a, rfh_a->GetLastCommittedOrigin());
// Currently active RFH is not `kInBackForwardCache`.
EXPECT_EQ(rfh_a->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kActive);
content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
// 2) Navigate to B.
EXPECT_TRUE(content::NavigateToURL(embedder_contents, url_b));
content::RenderFrameHost* rfh_b = embedder_contents->GetPrimaryMainFrame();
ASSERT_TRUE(rfh_b);
EXPECT_EQ(origin_b, rfh_b->GetLastCommittedOrigin());
// Currently active RFH is not `kInBackForwardCache`.
EXPECT_EQ(rfh_b->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kActive);
// `rfh_a` is not deleted because it is in bfcahce.
EXPECT_FALSE(delete_observer_rfh_a.deleted());
// `rfh_a` is no longer `kActive` but `kInBackForwardCache`.
EXPECT_EQ(rfh_a->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kInBackForwardCache);
// 3) Verify that `HistoryGoBack` restores previously cached `rfh_a` and does
// not create a new one.
EXPECT_NE(rfh_a, embedder_contents->GetPrimaryMainFrame());
// After `HistoryGoBack` `rfh_a` should be moved back from the BFCache.
ASSERT_TRUE(HistoryGoBack(embedder_contents));
EXPECT_EQ(rfh_a, embedder_contents->GetPrimaryMainFrame());
EXPECT_EQ(rfh_a->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kActive);
EXPECT_TRUE(content::NavigateToURL(embedder_contents, url_b));
// `rfh_a` is not deleted because it is in bfcahce.
EXPECT_FALSE(delete_observer_rfh_a.deleted());
// `rfh_a` is no longer `kActive` but `kInBackForwardCache`.
EXPECT_EQ(rfh_a->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kInBackForwardCache);
// 4) Verify and request permissions. The main frame works as expected but
// permission verification fails on `rfh_a`. `rfh_a` gets evicted from the
// BFCache.
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(embedder_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
EXPECT_EQ(false, content::EvalJs(embedder_contents->GetPrimaryMainFrame(),
kCheckGeolocation));
EXPECT_EQ("granted", content::EvalJs(embedder_contents->GetPrimaryMainFrame(),
kRequestGeolocation));
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
// Run JavaScript on a page in the back-forward cache. The page should be
// evicted. As the frame is deleted, ExecJs returns false without executing.
// Run without user gesture to prevent UpdateUserActivationState message
// being sent back to browser.
EXPECT_FALSE(content::ExecJs(rfh_a, kRequestGeolocation));
// No permission prompt bubble has been shown for `rfh_a`.
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
// RenderFrameHost A is evicted from the BackForwardCache:
delete_observer_rfh_a.WaitUntilDeleted();
// `rfh_a` is deleted because it was evicted.
EXPECT_TRUE(delete_observer_rfh_a.deleted());
// 5) Go back to a.test and verify that it has no permissions.
ASSERT_TRUE(HistoryGoBack(embedder_contents));
content::RenderFrameHost* rfh_a_2 = embedder_contents->GetPrimaryMainFrame();
ASSERT_TRUE(rfh_a_2);
EXPECT_EQ(origin_a, rfh_a_2->GetLastCommittedOrigin());
// Verify that `a.test` has no granted Geolocation permission despite it being
// requested above.
EXPECT_EQ(false, content::EvalJs(rfh_a_2, kCheckGeolocation));
}
class PermissionRequestFromExtension : public extensions::ExtensionApiTest {
public:
PermissionRequestFromExtension() {
geolocation_overrider_ =
std::make_unique<device::ScopedGeolocationOverrider>(0, 0);
}
~PermissionRequestFromExtension() override = default;
PermissionRequestFromExtension(const PermissionRequestFromExtension&) =
delete;
PermissionRequestFromExtension& operator=(
const PermissionRequestFromExtension&) = delete;
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->ServeFilesFromDirectory(
base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
ExtensionApiTest::SetUpOnMainThread();
}
protected:
GURL GetTestServerInsecureUrl(const std::string& path) {
GURL url = embedded_test_server()->GetURL(path);
GURL::Replacements replace_host_and_scheme;
replace_host_and_scheme.SetHostStr("a.test");
replace_host_and_scheme.SetSchemeStr("http");
url = url.ReplaceComponents(replace_host_and_scheme);
return url;
}
void EnsurePopupActive() {
auto test_util = ExtensionActionTestHelper::Create(browser());
EXPECT_TRUE(test_util->HasPopup());
ASSERT_NO_FATAL_FAILURE(test_util->WaitForPopup());
EXPECT_TRUE(test_util->HasPopup());
}
// Open an extension popup by clicking the browser action button associated
// with `id`.
content::WebContents* OpenPopupViaToolbar(const std::string& id) {
EXPECT_FALSE(id.empty());
content::CreateAndLoadWebContentsObserver popup_observer;
ExtensionActionTestHelper::Create(browser())->Press(id);
content::WebContents* popup = popup_observer.Wait();
EnsurePopupActive();
return popup;
}
void VerifyExtensionsPopupPage(std::string extension_path) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(extension_path));
ASSERT_TRUE(extension);
// Open a popup with the extension.
content::WebContents* extension_popup =
OpenPopupViaToolbar(extension->id());
ASSERT_TRUE(extension_popup);
// Wait for all JS tests to resolve their promises.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
// Showing permission prompts is not allowed on the extension's popup page.
EXPECT_EQ(0, bubble_factory->TotalRequestCount());
}
void VerifyExtensionsOptionsPage(
std::string extension_path,
int shown_prompts,
permissions::PermissionRequestManager::AutoResponseType type =
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(type);
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(extension_path));
ASSERT_TRUE(extension);
ASSERT_TRUE(extensions::OptionsPageInfo::HasOptionsPage(extension));
GURL options_url = extensions::OptionsPageInfo::GetOptionsPage(extension);
EXPECT_TRUE(
extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser()));
// Opening the options page should take the new tab and use it, so we should
// have only one tab, and it should be open to the options page.
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents()));
EXPECT_EQ(options_url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
// Wait for all JS tests to resolve their promises.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
// Prompts for: Notifications, Geolocation, Camera, Microphone.
EXPECT_EQ(shown_prompts, bubble_factory->TotalRequestCount());
}
private:
std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
};
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
EmbeddedIframeWithPermissionsTest) {
ASSERT_TRUE(StartEmbeddedTestServer());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/embedded_into_iframe/has_permissions"));
// Permissions work differently if they are not declared in an extension's
// manifest.
EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::kGeolocation));
EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::kNotifications));
GURL url = GetTestServerInsecureUrl("/extensions/test_file.html?succeed");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::RenderFrameHost* main_rfh = web_contents->GetPrimaryMainFrame();
ASSERT_TRUE(main_rfh);
content::RenderFrameHost* iframe_with_embedded_extension =
content::FrameMatchingPredicate(
main_rfh->GetPage(),
base::BindRepeating(&content::FrameMatchesName,
"iframe_with_embedded_extension"));
ASSERT_TRUE(iframe_with_embedded_extension);
// Notifications are enabled by default if the Notifications permission is
// declared in an extension's manifest.
EXPECT_EQ(true,
content::EvalJs(iframe_with_embedded_extension, kCheckNotifications,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ(false,
content::EvalJs(iframe_with_embedded_extension, kCheckGeolocation,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ("granted",
content::EvalJs(iframe_with_embedded_extension, kRequestGeolocation,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Despite Geolocation being granted above, its state is `prompt`.
EXPECT_EQ(false,
content::EvalJs(iframe_with_embedded_extension, kCheckGeolocation,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// There was no permission prompt shown.
EXPECT_EQ(0, bubble_factory->TotalRequestCount());
// Microphone is disabled by default.
EXPECT_EQ(false,
content::EvalJs(iframe_with_embedded_extension, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ("granted",
content::EvalJs(iframe_with_embedded_extension, kRequestMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Microphone is enabled.
EXPECT_EQ(true,
content::EvalJs(iframe_with_embedded_extension, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Camera is disabled by default.
EXPECT_EQ(false, content::EvalJs(iframe_with_embedded_extension, kCheckCamera,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ("granted",
content::EvalJs(iframe_with_embedded_extension, kRequestCamera,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Camera is enabled.
EXPECT_EQ(true, content::EvalJs(iframe_with_embedded_extension, kCheckCamera,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Only Camera and Microphone will show a prompt on permission request.
EXPECT_EQ(2, bubble_factory->TotalRequestCount());
EXPECT_TRUE(bubble_factory->RequestOriginSeen(
iframe_with_embedded_extension->GetLastCommittedOrigin().GetURL()));
EXPECT_TRUE(iframe_with_embedded_extension->GetLastCommittedOrigin()
.GetURL()
.SchemeIs(extensions::kExtensionScheme));
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
EmbeddedIframeWithNoPermissionsTest) {
ASSERT_TRUE(StartEmbeddedTestServer());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/embedded_into_iframe/no_permissions"));
EXPECT_FALSE(extension->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::kGeolocation));
EXPECT_FALSE(extension->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::kNotifications));
GURL url = GetTestServerInsecureUrl("/extensions/test_file.html?succeed");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::RenderFrameHost* main_rfh = web_contents->GetPrimaryMainFrame();
ASSERT_TRUE(main_rfh);
content::RenderFrameHost* iframe_with_embedded_extension =
content::FrameMatchingPredicate(
main_rfh->GetPage(),
base::BindRepeating(&content::FrameMatchesName,
"iframe_with_embedded_extension"));
ASSERT_TRUE(iframe_with_embedded_extension);
EXPECT_TRUE(iframe_with_embedded_extension->GetLastCommittedOrigin()
.GetURL()
.SchemeIs(extensions::kExtensionScheme));
// Notification permission is disabled if 'notification' is not declared in an
// extension's manifest.
EXPECT_EQ(false,
content::EvalJs(iframe_with_embedded_extension, kCheckNotifications,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ(false,
content::EvalJs(iframe_with_embedded_extension, kCheckGeolocation,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ("granted",
content::EvalJs(iframe_with_embedded_extension, kRequestGeolocation,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ(true,
content::EvalJs(iframe_with_embedded_extension, kCheckGeolocation,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// A permission prompt is shown.
EXPECT_EQ(1, bubble_factory->TotalRequestCount());
// Microphone is disabled by default.
EXPECT_EQ(false,
content::EvalJs(iframe_with_embedded_extension, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ("granted",
content::EvalJs(iframe_with_embedded_extension, kRequestMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Microphone is enabled.
EXPECT_EQ(true,
content::EvalJs(iframe_with_embedded_extension, kCheckMicrophone,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Camera is disabled by default.
EXPECT_EQ(false, content::EvalJs(iframe_with_embedded_extension, kCheckCamera,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
EXPECT_EQ("granted",
content::EvalJs(iframe_with_embedded_extension, kRequestCamera,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Camera is enabled.
EXPECT_EQ(true, content::EvalJs(iframe_with_embedded_extension, kCheckCamera,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1));
// Geolocation, Camera and Microphone will show a prompt on permission
// request.
EXPECT_EQ(3, bubble_factory->TotalRequestCount());
}
// `host` has all needed permissions, hence an extension can use them.
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
ContentScriptAllowedTest) {
ASSERT_TRUE(StartEmbeddedTestServer());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
GURL url = embedded_test_server()->GetURL("/extensions/test_file.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::RenderFrameHost* main_rfh = web_contents->GetPrimaryMainFrame();
ASSERT_TRUE(main_rfh);
// Allow permissions on the main frame, so they became available for an
// extension.
VerifyPermissionsAllowed(main_rfh);
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/request_from_content_script_allowed"));
ASSERT_TRUE(extension);
// Another navigation to activate the extension.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
// `host` does not have needed permissions, hence an extension can request them.
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
ContentScriptPromptTest) {
ASSERT_TRUE(StartEmbeddedTestServer());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
GURL url = embedded_test_server()->GetURL("/extensions/test_file.html");
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/request_from_content_script_prompt"));
ASSERT_TRUE(extension);
// Another navigation to activate the extension.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
// The above loaded extension requests Notifications, Geolocation, Camera,
// Microphone.
EXPECT_EQ(4, bubble_factory->TotalRequestCount());
}
// Permissions requests are not allowed. All permissions denied.
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
ContentScriptDeniedTest) {
ASSERT_TRUE(StartEmbeddedTestServer());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/request_from_content_script_not_allowed"));
ASSERT_TRUE(extension);
GURL url = GetTestServerInsecureUrl("/extensions/test_file.html");
// Another navigation to activate the extension.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
// No permission prompts has been shown.
EXPECT_EQ(0, bubble_factory->TotalRequestCount());
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
BackgroundV3HasPermissionsTest) {
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/request_from_background_v3/has_permissions"));
ASSERT_TRUE(extension);
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
BackgroundV3NoPermissionsTest) {
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/request_from_background_v3/has_permissions"));
ASSERT_TRUE(extension);
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
BackgroundV2HasPermissionsTest) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/request_from_background_v2/has_permissions"));
ASSERT_TRUE(extension);
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
// No permission prompt has been shown.
EXPECT_EQ(0, bubble_factory->TotalRequestCount());
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
BackgroundV2NoPermissionsTest) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
// Enable auto-accept of a permission request.
bubble_factory->set_response_type(
permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(
"permissions_test/request_from_background_v2/no_permissions"));
ASSERT_TRUE(extension);
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
// No permission prompt has been shown.
EXPECT_EQ(0, bubble_factory->TotalRequestCount());
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
PopupPageNoPermissonsV2Test) {
VerifyExtensionsPopupPage(
"permissions_test/request_from_popup_v2/no_permissions");
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
PopupPageHasPermissonsV2Test) {
VerifyExtensionsPopupPage(
"permissions_test/request_from_popup_v2/has_permissions");
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
PopupPageNoPermissonsV3Test) {
VerifyExtensionsPopupPage(
"permissions_test/request_from_popup_v3/no_permissions");
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
PopupPageHasPermissonsV3Test) {
VerifyExtensionsPopupPage(
"permissions_test/request_from_popup_v3/has_permissions");
}
// crbug.com/1356314 Failed on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_OptionsPageNoPermissonsV2Test \
DISABLED_OptionsPageNoPermissonsV2Test
#else
#define MAYBE_OptionsPageNoPermissonsV2Test OptionsPageNoPermissonsV2Test
#endif
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
MAYBE_OptionsPageNoPermissonsV2Test) {
VerifyExtensionsOptionsPage(
"permissions_test/request_from_options_v2/no_permissions",
/*shown_prompts=*/4);
}
// crbug.com/1356314 Failed on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_OptionsPageHasPermissonsV2Test \
DISABLED_OptionsPageHasPermissonsV2Test
#else
#define MAYBE_OptionsPageHasPermissonsV2Test OptionsPageHasPermissonsV2Test
#endif
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
MAYBE_OptionsPageHasPermissonsV2Test) {
VerifyExtensionsOptionsPage(
"permissions_test/request_from_options_v2/has_permissions",
/*shown_prompts=*/2);
}
// crbug.com/1356314 Failed on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_OptionsPageNoPermissonsV3Test \
DISABLED_OptionsPageNoPermissonsV3Test
#else
#define MAYBE_OptionsPageNoPermissonsV3Test OptionsPageNoPermissonsV3Test
#endif
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
MAYBE_OptionsPageNoPermissonsV3Test) {
VerifyExtensionsOptionsPage(
"permissions_test/request_from_options_v3/no_permissions",
/*shown_prompts=*/4);
}
// crbug.com/1356314 Failed on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_OptionsPageHasPermissonsV3Test \
DISABLED_OptionsPageHasPermissonsV3Test
#else
#define MAYBE_OptionsPageHasPermissonsV3Test OptionsPageHasPermissonsV3Test
#endif
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
MAYBE_OptionsPageHasPermissonsV3Test) {
VerifyExtensionsOptionsPage(
"permissions_test/request_from_options_v3/has_permissions",
/*shown_prompts=*/2);
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
OptionsPageHasPermissonsV3NegativeTest) {
VerifyExtensionsOptionsPage(
"permissions_test/request_from_options_v3/has_permissions_negative",
/*shown_prompts=*/2,
permissions::PermissionRequestManager::AutoResponseType::DENY_ALL);
}
IN_PROC_BROWSER_TEST_F(PermissionRequestFromExtension,
ExtensionAccessToCSPSandboxedFrameTest) {
ASSERT_TRUE(StartEmbeddedTestServer());
GURL url = embedded_test_server()->GetURL(
"example.com", "/extensions/page_with_sandbox_csp.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
extensions::ResultCatcher catcher;
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("sandbox_csp"));
ASSERT_TRUE(extension);
// Open a popup with the extension.
content::WebContents* extension_popup = OpenPopupViaToolbar(extension->id());
ASSERT_TRUE(extension_popup);
// Wait for all JS tests to resolve their promises.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
} // anonymous namespace