// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "weblayer/shell/browser/shell.h" #include #include #include #include "base/command_line.h" #include "base/macros.h" #include "base/no_destructor.h" #include "base/run_loop.h" #include "base/strings/string_util.h" #include "build/build_config.h" #include "url/gurl.h" #include "weblayer/browser/browser_impl.h" #include "weblayer/browser/browser_list.h" #include "weblayer/browser/profile_impl.h" #include "weblayer/browser/tab_impl.h" #include "weblayer/public/navigation_controller.h" namespace weblayer { // Null until/unless the default main message loop is running. base::NoDestructor g_quit_main_message_loop; const int kDefaultTestWindowWidthDip = 1000; const int kDefaultTestWindowHeightDip = 700; std::vector Shell::windows_; Shell::Shell(std::unique_ptr browser) : browser_(std::move(browser)), window_(nullptr) { windows_.push_back(this); if (tab()) { tab()->AddObserver(this); tab()->GetNavigationController()->AddObserver(this); #if !defined(OS_ANDROID) // Android does this in Java. // TODO: how will tests work with this on android? can we get to the // concrete type? static_cast(tab())->profile()->SetDownloadDelegate(this); #endif } } Shell::~Shell() { if (tab()) { tab()->GetNavigationController()->RemoveObserver(this); tab()->RemoveObserver(this); } PlatformCleanUp(); for (size_t i = 0; i < windows_.size(); ++i) { if (windows_[i] == this) { windows_.erase(windows_.begin() + i); break; } } // Always destroy WebContents before calling PlatformExit(). WebContents // destruction sequence may depend on the resources destroyed in // PlatformExit() (e.g. the display::Screen singleton). browser_.reset(); if (windows_.empty()) { PlatformExit(); if (*g_quit_main_message_loop) std::move(*g_quit_main_message_loop).Run(); } } Shell* Shell::CreateShell(std::unique_ptr browser, const gfx::Size& initial_size) { Shell* shell = new Shell(std::move(browser)); shell->PlatformCreateWindow(initial_size.width(), initial_size.height()); shell->PlatformSetContents(); shell->PlatformResizeSubViews(); return shell; } void Shell::CloseAllWindows() { std::vector open_windows(windows_); for (size_t i = 0; i < open_windows.size(); ++i) open_windows[i]->Close(); // Pump the message loop to allow window teardown tasks to run. base::RunLoop().RunUntilIdle(); } void Shell::SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure) { *g_quit_main_message_loop = std::move(quit_closure); } Tab* Shell::tab() { if (!browser()) return nullptr; CHECK(!browser()->GetTabs().empty()); return browser()->GetTabs()[0]; } Browser* Shell::browser() { #if defined(OS_ANDROID) // TODO(jam): this won't work if we need more than one Shell in a test. const auto& browsers = BrowserList::GetInstance()->browsers(); if (browsers.empty()) return nullptr; return *(browsers.begin()); #else return browser_.get(); #endif } void Shell::Initialize() { PlatformInitialize(GetShellDefaultSize()); } void Shell::DisplayedUrlChanged(const GURL& url) { PlatformSetAddressBarURL(url); } void Shell::LoadStateChanged(bool is_loading, bool to_different_document) { NavigationController* navigation_controller = tab()->GetNavigationController(); PlatformEnableUIControl(STOP_BUTTON, is_loading && to_different_document); // TODO(estade): These should be updated in callbacks that correspond to the // back/forward list changing, such as NavigationEntriesDeleted. PlatformEnableUIControl(BACK_BUTTON, navigation_controller->CanGoBack()); PlatformEnableUIControl(FORWARD_BUTTON, navigation_controller->CanGoForward()); } void Shell::LoadProgressChanged(double progress) { PlatformSetLoadProgress(progress); } bool Shell::InterceptDownload(const GURL& url, const std::string& user_agent, const std::string& content_disposition, const std::string& mime_type, int64_t content_length) { return false; } void Shell::AllowDownload(Tab* tab, const GURL& url, const std::string& request_method, base::Optional request_initiator, AllowDownloadCallback callback) { std::move(callback).Run(true); } gfx::Size Shell::AdjustWindowSize(const gfx::Size& initial_size) { if (!initial_size.IsEmpty()) return initial_size; return GetShellDefaultSize(); } #if defined(OS_ANDROID) Shell* Shell::CreateNewWindow(const GURL& url, const gfx::Size& initial_size) { // On Android, the browser is owned by the Java side. return CreateNewWindowWithBrowser(nullptr, url, initial_size); } #else Shell* Shell::CreateNewWindow(Profile* web_profile, const GURL& url, const gfx::Size& initial_size) { auto browser = Browser::Create(web_profile, nullptr); browser->CreateTab(); return CreateNewWindowWithBrowser(std::move(browser), url, initial_size); } #endif Shell* Shell::CreateNewWindowWithBrowser(std::unique_ptr browser, const GURL& url, const gfx::Size& initial_size) { Shell* shell = CreateShell(std::move(browser), AdjustWindowSize(initial_size)); if (!url.is_empty()) shell->LoadURL(url); return shell; } void Shell::LoadURL(const GURL& url) { tab()->GetNavigationController()->Navigate(url); } void Shell::GoBackOrForward(int offset) { if (offset == -1) tab()->GetNavigationController()->GoBack(); else if (offset == 1) tab()->GetNavigationController()->GoForward(); } void Shell::Reload() { tab()->GetNavigationController()->Reload(); } void Shell::ReloadBypassingCache() {} void Shell::Stop() { tab()->GetNavigationController()->Stop(); } gfx::Size Shell::GetShellDefaultSize() { static gfx::Size default_shell_size; if (!default_shell_size.IsEmpty()) return default_shell_size; default_shell_size = gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip); return default_shell_size; } } // namespace weblayer