| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/test/test_switches.h" |
| #include "base/test/values_test_util.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/devtools/devtools_window.h" |
| #include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_util.h" |
| #include "chrome/browser/extensions/unpacked_installer.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/infobars/content/content_infobar_manager.h" |
| #include "components/infobars/core/infobar.h" |
| #include "components/infobars/core/infobar_delegate.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/ssl_status.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "extensions/browser/extension_host.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/process_manager.h" |
| #include "extensions/browser/test_extension_registry_observer.h" |
| #include "extensions/common/manifest_handlers/background_info.h" |
| #include "extensions/test/extension_test_message_listener.h" |
| #include "net/base/ip_address.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/ssl/ssl_cipher_suite_names.h" |
| #include "net/ssl/ssl_config.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/ssl/ssl_server_config.h" |
| #include "printing/buildflags/buildflags.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/boringssl/src/include/openssl/ssl.h" |
| #include "url/origin.h" |
| |
| using DevToolsProtocolTest = DevToolsProtocolTestBase; |
| using testing::AllOf; |
| using testing::Contains; |
| using testing::Eq; |
| using testing::Not; |
| |
| namespace { |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, |
| VisibleSecurityStateChangedNeutralState) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| |
| Attach(); |
| SendCommandAsync("Security.enable"); |
| base::Value::Dict params = |
| WaitForNotification("Security.visibleSecurityStateChanged", true); |
| |
| std::string* security_state = |
| params.FindStringByDottedPath("visibleSecurityState.securityState"); |
| ASSERT_TRUE(security_state); |
| EXPECT_EQ(std::string("neutral"), *security_state); |
| EXPECT_FALSE(params.FindStringByDottedPath( |
| "visibleSecurityState.certificateSecurityState")); |
| EXPECT_FALSE( |
| params.FindStringByDottedPath("visibleSecurityState.safetyTipInfo")); |
| const base::Value* security_state_issue_ids = |
| params.FindByDottedPath("visibleSecurityState.securityStateIssueIds"); |
| EXPECT_TRUE(std::find(security_state_issue_ids->GetListDeprecated().begin(), |
| security_state_issue_ids->GetListDeprecated().end(), |
| base::Value("scheme-is-not-cryptographic")) != |
| security_state_issue_ids->GetListDeprecated().end()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, CreateDeleteContext) { |
| AttachToBrowserTarget(); |
| for (int i = 0; i < 2; i++) { |
| const base::Value::Dict* result = |
| SendCommandSync("Target.createBrowserContext"); |
| std::string context_id = *result->FindString("browserContextId"); |
| |
| base::Value::Dict params; |
| params.Set("url", "about:blank"); |
| params.Set("browserContextId", context_id); |
| SendCommandSync("Target.createTarget", std::move(params)); |
| |
| params = base::Value::Dict(); |
| params.Set("browserContextId", context_id); |
| SendCommandSync("Target.disposeBrowserContext", std::move(params)); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, |
| NewTabPageInCreatedContextDoesNotCrash) { |
| AttachToBrowserTarget(); |
| const base::Value::Dict* result = |
| SendCommandSync("Target.createBrowserContext"); |
| std::string context_id = *result->FindString("browserContextId"); |
| |
| base::Value::Dict params; |
| params.Set("url", chrome::kChromeUINewTabURL); |
| params.Set("browserContextId", context_id); |
| content::WebContentsAddedObserver observer; |
| SendCommandSync("Target.createTarget", std::move(params)); |
| content::WebContents* wc = observer.GetWebContents(); |
| ASSERT_TRUE(content::WaitForLoadStop(wc)); |
| EXPECT_EQ(chrome::kChromeUINewTabURL, wc->GetLastCommittedURL().spec()); |
| |
| // Should not crash by this point. |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, |
| InputDispatchEventsToCorrectTarget) { |
| Attach(); |
| |
| std::string setup_logging = R"( |
| window.logs = []; |
| ['dragenter', 'keydown', 'mousedown', 'mouseenter', 'mouseleave', |
| 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'click', 'touchcancel', |
| 'touchend', 'touchmove', 'touchstart', |
| ].forEach((event) => |
| window.addEventListener(event, (e) => logs.push(e.type)));)"; |
| content::WebContents* target_web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| content::WebContents* other_web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_TRUE( |
| content::EvalJs(target_web_contents, setup_logging).error.empty()); |
| EXPECT_TRUE(content::EvalJs(other_web_contents, setup_logging).error.empty()); |
| |
| base::Value::Dict params; |
| params.Set("button", "left"); |
| params.Set("clickCount", 1); |
| params.Set("x", 100); |
| params.Set("y", 250); |
| params.Set("clickCount", 1); |
| |
| params.Set("type", "mousePressed"); |
| SendCommandSync("Input.dispatchMouseEvent", params.Clone()); |
| |
| params.Set("type", "mouseMoved"); |
| params.Set("y", 270); |
| SendCommandSync("Input.dispatchMouseEvent", params.Clone()); |
| |
| params.Set("type", "mouseReleased"); |
| SendCommandSync("Input.dispatchMouseEvent", std::move(params)); |
| |
| params = base::Value::Dict(); |
| params.Set("x", 100); |
| params.Set("y", 250); |
| params.Set("type", "dragEnter"); |
| params.SetByDottedPath("data.dragOperationsMask", 1); |
| params.SetByDottedPath("data.items", base::Value::List()); |
| SendCommandSync("Input.dispatchDragEvent", std::move(params)); |
| |
| params = base::Value::Dict(); |
| params.Set("x", 100); |
| params.Set("y", 250); |
| SendCommandSync("Input.synthesizeTapGesture", std::move(params)); |
| |
| params = base::Value::Dict(); |
| params.Set("type", "keyDown"); |
| params.Set("key", "a"); |
| SendCommandSync("Input.dispatchKeyEvent", std::move(params)); |
| |
| content::EvalJsResult main_target_events = |
| content::EvalJs(target_web_contents, "logs.join(' ')"); |
| content::EvalJsResult other_target_events = |
| content::EvalJs(other_web_contents, "logs.join(' ')"); |
| // mouse events might happen in the other_target if the real mouse pointer |
| // happens to be over the browser window |
| EXPECT_THAT( |
| base::SplitString(main_target_events.ExtractString(), " ", |
| base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL), |
| AllOf(Contains("mouseover"), Contains("mousedown"), Contains("mousemove"), |
| Contains("mouseup"), Contains("click"), Contains("dragenter"), |
| Contains("keydown"))); |
| EXPECT_THAT(base::SplitString(other_target_events.ExtractString(), " ", |
| base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL), |
| AllOf(Not(Contains("click")), Not(Contains("dragenter")), |
| Not(Contains("keydown")))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, |
| NoInputEventsSentToBrowserWhenDisallowed) { |
| SetIsTrusted(false); |
| Attach(); |
| |
| base::Value::Dict params; |
| params.Set("type", "rawKeyDown"); |
| params.Set("key", "F12"); |
| params.Set("windowsVirtualKeyCode", 123); |
| params.Set("nativeVirtualKeyCode", 123); |
| SendCommandSync("Input.dispatchKeyEvent", std::move(params)); |
| |
| EXPECT_EQ(nullptr, DevToolsWindow::FindDevToolsWindow(agent_host_.get())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| DevToolsProtocolTest, |
| NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation) { |
| GURL url("invalid.scheme:for-sure"); |
| ui_test_utils::AllBrowserTabAddedWaiter tab_added_waiter; |
| |
| content::WebContents* web_contents = |
| browser()->OpenURL(content::OpenURLParams( |
| url, content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui::PAGE_TRANSITION_TYPED, false)); |
| tab_added_waiter.Wait(); |
| // WaitForLoadStop() checks for the existence of the last committed |
| // NavigationEntry, which will only be there if we have initial |
| // NavigationEntries. |
| ASSERT_EQ(WaitForLoadStop(web_contents), |
| blink::features::IsInitialNavigationEntryEnabled()); |
| content::NavigationController& navigation_controller = |
| web_contents->GetController(); |
| content::NavigationEntry* pending_entry = |
| navigation_controller.GetPendingEntry(); |
| ASSERT_NE(nullptr, pending_entry); |
| EXPECT_EQ(url, pending_entry->GetURL()); |
| |
| EXPECT_EQ(pending_entry, navigation_controller.GetVisibleEntry()); |
| agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(web_contents); |
| agent_host_->AttachClient(this); |
| SendCommandSync("Page.enable"); |
| |
| // Ensure that a failed pending entry is cleared when the DevTools protocol |
| // attaches, so that any modified page content is not attributed to the failed |
| // URL. (crbug/1192417) |
| EXPECT_EQ(nullptr, navigation_controller.GetPendingEntry()); |
| if (blink::features::IsInitialNavigationEntryEnabled()) { |
| EXPECT_EQ(GURL(""), navigation_controller.GetVisibleEntry()->GetURL()); |
| } else { |
| EXPECT_EQ(nullptr, navigation_controller.GetVisibleEntry()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, |
| NoPendingUrlShownForPageNavigateFromChromeExtension) { |
| GURL url("https://example.com"); |
| // DevTools protocol use cases that have an initiator origin (e.g., for |
| // extensions) should use renderer-initiated navigations and be subject to URL |
| // spoof defenses. |
| SetNavigationInitiatorOrigin( |
| url::Origin::Create(GURL("chrome-extension://abc123/"))); |
| |
| // Attach DevTools and start a navigation but don't wait for it to finish. |
| Attach(); |
| SendCommandSync("Page.enable"); |
| base::Value::Dict params; |
| params.Set("url", url.spec()); |
| SendCommandAsync("Page.navigate", std::move(params)); |
| content::NavigationController& navigation_controller = |
| web_contents()->GetController(); |
| content::NavigationEntry* pending_entry = |
| navigation_controller.GetPendingEntry(); |
| ASSERT_NE(nullptr, pending_entry); |
| EXPECT_EQ(url, pending_entry->GetURL()); |
| |
| // Attaching the DevTools protocol to the initial empty document of a new tab |
| // should prevent the pending URL from being visible, since the protocol |
| // allows modifying the initial empty document in a way that could be useful |
| // for URL spoofs. |
| EXPECT_NE(pending_entry, navigation_controller.GetVisibleEntry()); |
| EXPECT_NE(nullptr, navigation_controller.GetPendingEntry()); |
| EXPECT_EQ(GURL("about:blank"), |
| navigation_controller.GetVisibleEntry()->GetURL()); |
| } |
| |
| class DevToolsProtocolTest_AppId : public DevToolsProtocolTest { |
| public: |
| DevToolsProtocolTest_AppId() { |
| scoped_feature_list_.InitAndEnableFeature( |
| blink::features::kWebAppEnableManifestId); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest_AppId, ReturnsManifestAppId) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| const GURL url(embedded_test_server()->GetURL( |
| "/banners/manifest_test_page.html?manifest=manifest_with_id.json")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| Attach(); |
| |
| const base::Value::Dict* result = SendCommandSync("Page.getAppId"); |
| EXPECT_EQ(*result->FindString("appId"), |
| embedded_test_server()->GetURL("/some_id")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest_AppId, |
| ReturnsStartUrlAsManifestAppIdIfNotSet) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| const GURL url( |
| embedded_test_server()->GetURL("/web_apps/no_service_worker.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| Attach(); |
| |
| const base::Value::Dict* result = SendCommandSync("Page.getAppId"); |
| EXPECT_EQ(*result->FindString("appId"), |
| embedded_test_server()->GetURL("/web_apps/no_service_worker.html")); |
| EXPECT_EQ(*result->FindString("recommendedId"), |
| "/web_apps/no_service_worker.html"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest_AppId, ReturnsNoAppIdIfNoManifest) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| const GURL url(embedded_test_server()->GetURL("/empty.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| Attach(); |
| |
| const base::Value::Dict* result = SendCommandSync("Page.getAppId"); |
| EXPECT_FALSE(result->Find("appId")); |
| EXPECT_FALSE(result->Find("recommendedId")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, VisibleSecurityStateSecureState) { |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( |
| browser(), https_server.GetURL("/title1.html"), 1); |
| content::NavigationEntry* entry = |
| web_contents()->GetController().GetLastCommittedEntry(); |
| ASSERT_TRUE(entry); |
| |
| // Extract SSL status data from the navigation entry. |
| scoped_refptr<net::X509Certificate> page_cert = entry->GetSSL().certificate; |
| ASSERT_TRUE(page_cert); |
| |
| int ssl_version = |
| net::SSLConnectionStatusToVersion(entry->GetSSL().connection_status); |
| const char* page_protocol; |
| net::SSLVersionToString(&page_protocol, ssl_version); |
| |
| const char* page_key_exchange_str; |
| const char* page_cipher; |
| const char* page_mac; |
| bool is_aead; |
| bool is_tls13; |
| uint16_t page_cipher_suite = |
| net::SSLConnectionStatusToCipherSuite(entry->GetSSL().connection_status); |
| net::SSLCipherSuiteToStrings(&page_key_exchange_str, &page_cipher, &page_mac, |
| &is_aead, &is_tls13, page_cipher_suite); |
| std::string page_key_exchange; |
| if (page_key_exchange_str) |
| page_key_exchange = page_key_exchange_str; |
| |
| const char* page_key_exchange_group = |
| SSL_get_curve_name(entry->GetSSL().key_exchange_group); |
| |
| std::string page_subject_name; |
| std::string page_issuer_name; |
| double page_valid_from = 0.0; |
| double page_valid_to = 0.0; |
| if (entry->GetSSL().certificate) { |
| page_subject_name = entry->GetSSL().certificate->subject().common_name; |
| page_issuer_name = entry->GetSSL().certificate->issuer().common_name; |
| page_valid_from = entry->GetSSL().certificate->valid_start().ToDoubleT(); |
| page_valid_to = entry->GetSSL().certificate->valid_expiry().ToDoubleT(); |
| } |
| |
| std::string page_certificate_network_error; |
| if (net::IsCertStatusError(entry->GetSSL().cert_status)) { |
| page_certificate_network_error = net::ErrorToString( |
| net::MapCertStatusToNetError(entry->GetSSL().cert_status)); |
| } |
| |
| bool page_certificate_has_weak_signature = |
| (entry->GetSSL().cert_status & net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM); |
| |
| bool page_certificate_has_sha1_signature_present = |
| (entry->GetSSL().cert_status & net::CERT_STATUS_SHA1_SIGNATURE_PRESENT); |
| |
| int status = net::ObsoleteSSLStatus(entry->GetSSL().connection_status, |
| entry->GetSSL().peer_signature_algorithm); |
| bool page_modern_ssl = status == net::OBSOLETE_SSL_NONE; |
| bool page_obsolete_ssl_protocol = status & net::OBSOLETE_SSL_MASK_PROTOCOL; |
| bool page_obsolete_ssl_key_exchange = |
| status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE; |
| bool page_obsolete_ssl_cipher = status & net::OBSOLETE_SSL_MASK_CIPHER; |
| bool page_obsolete_ssl_signature = status & net::OBSOLETE_SSL_MASK_SIGNATURE; |
| |
| Attach(); |
| SendCommandAsync("Security.enable"); |
| auto has_certificate = [](const base::Value::Dict& params) { |
| return params.FindListByDottedPath( |
| "visibleSecurityState.certificateSecurityState.certificate") != |
| nullptr; |
| }; |
| base::Value::Dict params = |
| WaitForMatchingNotification("Security.visibleSecurityStateChanged", |
| base::BindRepeating(has_certificate)); |
| |
| // Verify that the visibleSecurityState payload matches the SSL status data. |
| std::string* security_state = |
| params.FindStringByDottedPath("visibleSecurityState.securityState"); |
| ASSERT_TRUE(security_state); |
| ASSERT_EQ(std::string("secure"), *security_state); |
| |
| base::Value* certificate_security_state = |
| params.FindByDottedPath("visibleSecurityState.certificateSecurityState"); |
| ASSERT_TRUE(certificate_security_state); |
| |
| std::string* protocol = |
| certificate_security_state->FindStringPath("protocol"); |
| ASSERT_TRUE(protocol); |
| ASSERT_EQ(*protocol, page_protocol); |
| |
| std::string* key_exchange = |
| certificate_security_state->FindStringPath("keyExchange"); |
| ASSERT_TRUE(key_exchange); |
| ASSERT_EQ(*key_exchange, page_key_exchange); |
| |
| std::string* key_exchange_group = |
| certificate_security_state->FindStringPath("keyExchangeGroup"); |
| if (key_exchange_group) { |
| ASSERT_EQ(*key_exchange_group, page_key_exchange_group); |
| } |
| |
| std::string* mac = certificate_security_state->FindStringPath("mac"); |
| if (mac) { |
| ASSERT_EQ(*mac, page_mac); |
| } |
| |
| std::string* cipher = certificate_security_state->FindStringPath("cipher"); |
| ASSERT_TRUE(cipher); |
| ASSERT_EQ(*cipher, page_cipher); |
| |
| std::string* subject_name = |
| certificate_security_state->FindStringPath("subjectName"); |
| ASSERT_TRUE(subject_name); |
| ASSERT_EQ(*subject_name, page_subject_name); |
| |
| std::string* issuer = certificate_security_state->FindStringPath("issuer"); |
| ASSERT_TRUE(issuer); |
| ASSERT_EQ(*issuer, page_issuer_name); |
| |
| auto valid_from = certificate_security_state->FindDoublePath("validFrom"); |
| ASSERT_TRUE(valid_from); |
| ASSERT_EQ(*valid_from, page_valid_from); |
| |
| auto valid_to = certificate_security_state->FindDoublePath("validTo"); |
| ASSERT_TRUE(valid_to); |
| ASSERT_EQ(*valid_to, page_valid_to); |
| |
| std::string* certificate_network_error = |
| certificate_security_state->FindStringPath("certificateNetworkError"); |
| if (certificate_network_error) { |
| ASSERT_EQ(*certificate_network_error, page_certificate_network_error); |
| } |
| |
| auto certificate_has_weak_signature = |
| certificate_security_state->FindBoolPath("certificateHasWeakSignature"); |
| ASSERT_TRUE(certificate_has_weak_signature); |
| ASSERT_EQ(*certificate_has_weak_signature, |
| page_certificate_has_weak_signature); |
| |
| auto certificate_has_sha1_signature_present = |
| certificate_security_state->FindBoolPath("certificateHasSha1Signature"); |
| ASSERT_TRUE(certificate_has_sha1_signature_present); |
| ASSERT_EQ(*certificate_has_sha1_signature_present, |
| page_certificate_has_sha1_signature_present); |
| |
| auto modern_ssl = certificate_security_state->FindBoolPath("modernSSL"); |
| ASSERT_TRUE(modern_ssl); |
| ASSERT_EQ(*modern_ssl, page_modern_ssl); |
| |
| auto obsolete_ssl_protocol = |
| certificate_security_state->FindBoolPath("obsoleteSslProtocol"); |
| ASSERT_TRUE(obsolete_ssl_protocol); |
| ASSERT_EQ(*obsolete_ssl_protocol, page_obsolete_ssl_protocol); |
| |
| auto obsolete_ssl_key_exchange = |
| certificate_security_state->FindBoolPath("obsoleteSslKeyExchange"); |
| ASSERT_TRUE(obsolete_ssl_key_exchange); |
| ASSERT_EQ(*obsolete_ssl_key_exchange, page_obsolete_ssl_key_exchange); |
| |
| auto obsolete_ssl_cipher = |
| certificate_security_state->FindBoolPath("obsoleteSslCipher"); |
| ASSERT_TRUE(obsolete_ssl_cipher); |
| ASSERT_EQ(*obsolete_ssl_cipher, page_obsolete_ssl_cipher); |
| |
| auto obsolete_ssl_signature = |
| certificate_security_state->FindBoolPath("obsoleteSslSignature"); |
| ASSERT_TRUE(obsolete_ssl_signature); |
| ASSERT_EQ(*obsolete_ssl_signature, page_obsolete_ssl_signature); |
| |
| const base::Value* certificate_value = |
| certificate_security_state->FindListPath("certificate"); |
| std::vector<std::string> der_certs; |
| for (const auto& cert : certificate_value->GetListDeprecated()) { |
| std::string decoded; |
| ASSERT_TRUE(base::Base64Decode(cert.GetString(), &decoded)); |
| der_certs.push_back(decoded); |
| } |
| std::vector<base::StringPiece> cert_string_piece; |
| for (const auto& str : der_certs) { |
| cert_string_piece.push_back(str); |
| } |
| |
| // Check that the certificateSecurityState.certificate matches. |
| net::SHA256HashValue page_cert_chain_fingerprint = |
| page_cert->CalculateChainFingerprint256(); |
| scoped_refptr<net::X509Certificate> certificate = |
| net::X509Certificate::CreateFromDERCertChain(cert_string_piece); |
| ASSERT_TRUE(certificate); |
| EXPECT_EQ(page_cert_chain_fingerprint, |
| certificate->CalculateChainFingerprint256()); |
| const base::Value* security_state_issue_ids = |
| params.FindByDottedPath("visibleSecurityState.securityStateIssueIds"); |
| EXPECT_EQ(security_state_issue_ids->GetListDeprecated().size(), 0u); |
| |
| EXPECT_FALSE(params.FindByDottedPath("visibleSecurityState.safetyTipInfo")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, |
| AutomationOverrideShowsAndRemovesInfoBar) { |
| Attach(); |
| auto* manager = infobars::ContentInfoBarManager::FromWebContents( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| { |
| base::Value::Dict params; |
| params.Set("enabled", true); |
| SendCommandSync("Emulation.setAutomationOverride", std::move(params)); |
| } |
| EXPECT_EQ(static_cast<int>(manager->infobar_count()), 1); |
| { |
| base::Value::Dict params; |
| params.Set("enabled", false); |
| SendCommandSync("Emulation.setAutomationOverride", std::move(params)); |
| } |
| EXPECT_EQ(static_cast<int>(manager->infobar_count()), 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, |
| AutomationOverrideAddsOneInfoBarOnly) { |
| Attach(); |
| auto* manager = infobars::ContentInfoBarManager::FromWebContents( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| { |
| base::Value::Dict params; |
| params.Set("enabled", true); |
| SendCommandSync("Emulation.setAutomationOverride", std::move(params)); |
| } |
| EXPECT_EQ(static_cast<int>(manager->infobar_count()), 1); |
| { |
| base::Value::Dict params; |
| params.Set("enabled", true); |
| SendCommandSync("Emulation.setAutomationOverride", std::move(params)); |
| } |
| EXPECT_EQ(static_cast<int>(manager->infobar_count()), 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, UntrustedClient) { |
| std::unique_ptr<base::Value::Dict> params(new base::Value::Dict()); |
| SetIsTrusted(false); |
| Attach(); |
| EXPECT_FALSE(SendCommandSync("HeapProfiler.enable")); // Implemented in V8 |
| EXPECT_FALSE(SendCommandSync("LayerTree.enable")); // Implemented in blink |
| EXPECT_FALSE(SendCommandSync( |
| "Memory.prepareForLeakDetection")); // Implemented in content |
| EXPECT_FALSE(SendCommandSync("Cast.enable")); // Implemented in content |
| } |
| |
| class ExtensionProtocolTest : public DevToolsProtocolTest { |
| protected: |
| void SetUpOnMainThread() override { |
| DevToolsProtocolTest::SetUpOnMainThread(); |
| Profile* profile = browser()->profile(); |
| extension_service_ = |
| extensions::ExtensionSystem::Get(profile)->extension_service(); |
| extension_registry_ = extensions::ExtensionRegistry::Get(profile); |
| } |
| |
| content::WebContents* web_contents() override { |
| return background_web_contents_; |
| } |
| |
| const extensions::Extension* LoadExtension(base::FilePath extension_path) { |
| extensions::TestExtensionRegistryObserver observer(extension_registry_); |
| ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED"); |
| extensions::UnpackedInstaller::Create(extension_service_) |
| ->Load(extension_path); |
| observer.WaitForExtensionLoaded(); |
| const extensions::Extension* extension = nullptr; |
| for (const auto& enabled_extension : |
| extension_registry_->enabled_extensions()) { |
| if (enabled_extension->path() == extension_path) { |
| extension = enabled_extension.get(); |
| break; |
| } |
| } |
| CHECK(extension) << "Failed to find loaded extension " << extension_path; |
| auto* process_manager = |
| extensions::ProcessManager::Get(browser()->profile()); |
| if (extensions::BackgroundInfo::IsServiceWorkerBased(extension)) { |
| EXPECT_TRUE(activated_listener.WaitUntilSatisfied()); |
| auto worker_ids = |
| process_manager->GetServiceWorkersForExtension(extension->id()); |
| CHECK_EQ(1lu, worker_ids.size()); |
| } else { |
| extensions::ExtensionHost* host = |
| process_manager->GetBackgroundHostForExtension(extension->id()); |
| background_web_contents_ = host->host_contents(); |
| } |
| |
| return extension; |
| } |
| |
| void ReloadExtension(const std::string extension_id) { |
| extensions::TestExtensionRegistryObserver observer(extension_registry_); |
| extension_service_->ReloadExtension(extension_id); |
| observer.WaitForExtensionLoaded(); |
| } |
| |
| private: |
| extensions::ExtensionService* extension_service_; |
| extensions::ExtensionRegistry* extension_registry_; |
| content::WebContents* background_web_contents_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionProtocolTest, ReloadTracedExtension) { |
| base::FilePath extension_path = |
| base::PathService::CheckedGet(chrome::DIR_TEST_DATA) |
| .AppendASCII("devtools") |
| .AppendASCII("extensions") |
| .AppendASCII("simple_background_page"); |
| auto* extension = LoadExtension(extension_path); |
| ASSERT_TRUE(extension); |
| Attach(); |
| ReloadExtension(extension->id()); |
| base::Value::Dict params; |
| params.Set("categories", "-*"); |
| SendCommandSync("Tracing.start", std::move(params)); |
| SendCommandAsync("Tracing.end"); |
| WaitForNotification("Tracing.tracingComplete", true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionProtocolTest, |
| DISABLED_ReloadServiceWorkerExtension) { |
| base::FilePath extension_path = |
| base::PathService::CheckedGet(chrome::DIR_TEST_DATA) |
| .AppendASCII("devtools") |
| .AppendASCII("extensions") |
| .AppendASCII("service_worker"); |
| std::string extension_id; |
| { |
| // `extension` is stale after reload. |
| auto* extension = LoadExtension(extension_path); |
| ASSERT_THAT(extension, testing::NotNull()); |
| extension_id = extension->id(); |
| } |
| AttachToBrowserTarget(); |
| const base::Value::Dict* result = SendCommandSync("Target.getTargets"); |
| |
| std::string target_id; |
| base::Value ext_target; |
| for (const auto& target : *result->FindList("targetInfos")) { |
| if (*target.FindStringKey("type") == "service_worker") { |
| ext_target = target.Clone(); |
| break; |
| } |
| } |
| { |
| base::Value::Dict params; |
| params.Set("targetId", *ext_target.FindStringKey("targetId")); |
| params.Set("waitForDebuggerOnStart", false); |
| SendCommandSync("Target.autoAttachRelated", std::move(params)); |
| } |
| ReloadExtension(extension_id); |
| base::Value::Dict attached = |
| WaitForNotification("Target.attachedToTarget", true); |
| base::Value* targetInfo = attached.Find("targetInfo"); |
| ASSERT_THAT(targetInfo, testing::NotNull()); |
| EXPECT_THAT(*targetInfo, base::test::DictionaryHasValue( |
| "type", base::Value("service_worker"))); |
| EXPECT_THAT(*targetInfo, base::test::DictionaryHasValue( |
| "url", *ext_target.FindKey("url"))); |
| EXPECT_THAT(attached.FindBool("waitingForDebugger"), |
| testing::Optional(false)); |
| |
| { |
| base::Value::Dict params; |
| params.Set("targetId", *targetInfo->FindStringKey("targetId")); |
| params.Set("waitForDebuggerOnStart", false); |
| SendCommandSync("Target.autoAttachRelated", std::move(params)); |
| } |
| auto detached = WaitForNotification("Target.detachedFromTarget", true); |
| EXPECT_THAT(*detached.FindString("sessionId"), Eq("sessionId")); |
| } |
| |
| } // namespace |