910e62b5创建于 1月15日历史提交
// Copyright 2020 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/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/test/ssl_test_utils.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"

namespace {

constexpr const char kImagePath[] = "/ssl/google_files/logo.gif";

// Returns a path string that points to a page with the
// "REPLACE_WITH_HOST_AND_PORT" string replaced with |host_port_pair|.
// The page at |original_path| should contain the string
// "REPLACE_WITH_HOST_AND_PORT".
std::string GetPathWithHostAndPortReplaced(
    const std::string& original_path,
    const net::HostPortPair& host_port_pair) {
  base::StringPairs replacement_text = {
      {"REPLACE_WITH_HOST_AND_PORT", host_port_pair.ToString()}};
  LOG(ERROR) << "host_port_pair.ToString() " << host_port_pair.ToString();
  return net::test_server::GetFilePathWithReplacements(original_path,
                                                       replacement_text);
}

// Tries to load an image at |image_url| and returns whether or not it loaded
// successfully.
//
// The image could fail to load because it was blocked from being loaded or
// because |image_url| doesn't exist. Therefore, it failing to load is not a
// reliable indicator of insecure content being blocked. Users of the function
// should check the state of security indicators.
bool TryToLoadImage(const content::ToRenderFrameHost& adapter,
                    const GURL& image_url) {
  const std::string script = base::StringPrintf(
      "let i = document.createElement('img');"
      "document.body.appendChild(i);"
      "new Promise(resolve => {"
      "  i.addEventListener('load', () => resolve(true));"
      "  i.addEventListener('error', () => resolve(false));"
      "  i.src = '%s';"
      "});",
      image_url.spec().c_str());

  return content::EvalJs(adapter, script).ExtractBool();
}

}  // anonymous namespace

namespace web_app {

class PWAMixedContentBrowserTest : public WebAppBrowserTestBase {
 public:
  GURL GetMixedContentAppURL() {
    return https_server()->GetURL("app.com",
                                  "/ssl/page_displays_insecure_content.html");
  }

  // This URL is on app.com, and the page contains a secure iframe that points
  // to foo.com/simple.html.
  GURL GetSecureIFrameAppURL() {
    net::HostPortPair host_port_pair = net::HostPortPair::FromURL(
        https_server()->GetURL("foo.com", "/simple.html"));
    const std::string path = GetPathWithHostAndPortReplaced(
        "/ssl/page_with_cross_site_frame.html", host_port_pair);

    return https_server()->GetURL("app.com", path);
  }
};

class PWAMixedContentBrowserTestWithAutoupgradesDisabled
    : public PWAMixedContentBrowserTest {
 public:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    PWAMixedContentBrowserTest::SetUpCommandLine(command_line);
    feature_list.InitAndDisableFeature(
        blink::features::kMixedContentAutoupgrade);
  }

 private:
  base::test::ScopedFeatureList feature_list;
};

// Tests that creating a shortcut app but not installing a PWA is available for
// a non-installable site.
IN_PROC_BROWSER_TEST_F(PWAMixedContentBrowserTest,
                       ShortcutMenuOptionsForNonInstallableSite) {
  EXPECT_FALSE(
      NavigateAndAwaitInstallabilityCheck(browser(), GetMixedContentAppURL()));

  EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, browser()), kEnabled);

  EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kEnabled);
}

// Tests that mixed content is loaded inside PWA windows.
IN_PROC_BROWSER_TEST_F(PWAMixedContentBrowserTestWithAutoupgradesDisabled,
                       MixedContentInPWA) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL app_url = GetMixedContentAppURL();
  const webapps::AppId app_id = InstallPWA(app_url);
  Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id);
  CHECK(app_browser);
  web_app::CheckMixedContentLoaded(app_browser);
}

// Tests that when calling OpenInChrome, mixed content can be loaded in the new
// tab.
IN_PROC_BROWSER_TEST_F(PWAMixedContentBrowserTestWithAutoupgradesDisabled,
                       MixedContentOpenInChrome) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL app_url = GetMixedContentAppURL();
  const webapps::AppId app_id = InstallPWA(app_url);
  Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id);

  // Mixed content should be able to load in web app windows.
  CheckMixedContentLoaded(app_browser);

  chrome::OpenInChrome(app_browser);
  ASSERT_EQ(browser(), chrome::FindLastActive());
  ASSERT_EQ(GetMixedContentAppURL(), browser()
                                         ->tab_strip_model()
                                         ->GetActiveWebContents()
                                         ->GetLastCommittedURL());

  // The WebContents is just reparented, so mixed content is still loaded.
  CheckMixedContentLoaded(browser());
  EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
            kEnabled);

  ui_test_utils::UrlLoadObserver url_observer(GetMixedContentAppURL());
  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  url_observer.Wait();

  // After reloading, mixed content should successfully load.
  CheckMixedContentLoaded(browser());
  EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
            kEnabled);
  EXPECT_NE(ReparentWebAppForActiveTab(browser()), nullptr);
}

// Tests that when calling ReparentWebContentsIntoAppBrowser, mixed
// content cannot be loaded in the new app window.
IN_PROC_BROWSER_TEST_F(PWAMixedContentBrowserTestWithAutoupgradesDisabled,
                       MixedContentReparentWebContentsIntoAppBrowser) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL app_url = GetMixedContentAppURL();
  const webapps::AppId app_id = InstallPWA(app_url);

  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), GetMixedContentAppURL()));
  content::WebContents* tab_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_EQ(tab_contents->GetLastCommittedURL(), GetMixedContentAppURL());

  // A regular tab should be able to load mixed content.
  CheckMixedContentLoaded(browser());
  EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
            kEnabled);

  BrowserWindowInterface* app_browser =
      ReparentWebContentsIntoAppBrowser(tab_contents, app_id);

  ASSERT_NE(app_browser, browser());
  ASSERT_EQ(GetMixedContentAppURL(), app_browser->GetFeatures()
                                         .tab_strip_model()
                                         ->GetActiveWebContents()
                                         ->GetLastCommittedURL());

  // After reparenting, the WebContents should still have its mixed content
  // loaded.
  CheckMixedContentLoaded(app_browser->GetBrowserForMigrationOnly());

  ui_test_utils::UrlLoadObserver url_observer(GetMixedContentAppURL());
  chrome::Reload(app_browser->GetBrowserForMigrationOnly(),
                 WindowOpenDisposition::CURRENT_TAB);
  url_observer.Wait();

  // Mixed content should be able to load in web app windows.
  CheckMixedContentLoaded(app_browser->GetBrowserForMigrationOnly());
}

// Tests that mixed content is not loaded inside iframes in PWA windows.
IN_PROC_BROWSER_TEST_F(PWAMixedContentBrowserTest, IFrameMixedContentInPWA) {
  const GURL app_url = GetSecureIFrameAppURL();
  const webapps::AppId app_id = InstallPWA(app_url);
  Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id);

  CheckMixedContentFailedToLoad(app_browser);
}

// Tests that iframes can't dynamically load mixed content in a PWA window, when
// the iframe was created in a regular tab.
IN_PROC_BROWSER_TEST_F(
    PWAMixedContentBrowserTestWithAutoupgradesDisabled,
    IFrameDynamicMixedContentInPWAReparentWebContentsIntoAppBrowser) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL app_url = GetSecureIFrameAppURL();
  const webapps::AppId app_id = InstallPWA(app_url);

  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
  CheckMixedContentFailedToLoad(browser());

  BrowserWindowInterface* const app_browser = ReparentWebContentsIntoAppBrowser(
      browser()->tab_strip_model()->GetActiveWebContents(), app_id);
  CheckMixedContentFailedToLoad(app_browser->GetBrowserForMigrationOnly());

  // Change the mixed content to be acceptable.
  content::RenderFrameHost* main_frame = app_browser->GetFeatures()
                                             .tab_strip_model()
                                             ->GetActiveWebContents()
                                             ->GetPrimaryMainFrame();
  content::RenderFrameHost* iframe = content::ChildFrameAt(main_frame, 0);
  EXPECT_TRUE(TryToLoadImage(
      iframe, embedded_test_server()->GetURL("foo.com", kImagePath)));

  CheckMixedContentLoaded(app_browser->GetBrowserForMigrationOnly());
}

// Tests that iframes can't dynamically load mixed content in a regular browser
// tab, when the iframe was created in a PWA window.
// https://crbug.com/1087382: Flaky on Windows, CrOS and ASAN
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || defined(ADDRESS_SANITIZER)
#define MAYBE_IFrameDynamicMixedContentInPWAOpenInChrome \
  DISABLED_IFrameDynamicMixedContentInPWAOpenInChrome
#else
#define MAYBE_IFrameDynamicMixedContentInPWAOpenInChrome \
  IFrameDynamicMixedContentInPWAOpenInChrome
#endif
IN_PROC_BROWSER_TEST_F(PWAMixedContentBrowserTestWithAutoupgradesDisabled,
                       MAYBE_IFrameDynamicMixedContentInPWAOpenInChrome) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL app_url = GetSecureIFrameAppURL();
  const webapps::AppId app_id = InstallPWA(app_url);
  Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id);

  chrome::OpenInChrome(app_browser);

  content::RenderFrameHost* main_frame = browser()
                                             ->tab_strip_model()
                                             ->GetActiveWebContents()
                                             ->GetPrimaryMainFrame();
  content::RenderFrameHost* iframe = content::ChildFrameAt(main_frame, 0);

  EXPECT_TRUE(TryToLoadImage(
      iframe, embedded_test_server()->GetURL("foo.com", kImagePath)));

  CheckMixedContentLoaded(browser());
}

}  // namespace web_app