mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			460 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			460 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright (c) 2015 The Chromium Embedded Framework 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 "tests/cefclient/browser/root_window_manager.h"
 | 
						|
 | 
						|
#include <sstream>
 | 
						|
 | 
						|
#include "include/base/cef_bind.h"
 | 
						|
#include "include/base/cef_logging.h"
 | 
						|
#include "include/wrapper/cef_helpers.h"
 | 
						|
#include "tests/cefclient/browser/main_context.h"
 | 
						|
#include "tests/cefclient/browser/test_runner.h"
 | 
						|
#include "tests/shared/browser/extension_util.h"
 | 
						|
#include "tests/shared/browser/file_util.h"
 | 
						|
#include "tests/shared/browser/resource_util.h"
 | 
						|
#include "tests/shared/common/client_switches.h"
 | 
						|
 | 
						|
namespace client {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class ClientRequestContextHandler : public CefRequestContextHandler,
 | 
						|
                                    public CefExtensionHandler {
 | 
						|
 public:
 | 
						|
  ClientRequestContextHandler() {}
 | 
						|
 | 
						|
  // CefRequestContextHandler methods:
 | 
						|
  bool OnBeforePluginLoad(const CefString& mime_type,
 | 
						|
                          const CefString& plugin_url,
 | 
						|
                          bool is_main_frame,
 | 
						|
                          const CefString& top_origin_url,
 | 
						|
                          CefRefPtr<CefWebPluginInfo> plugin_info,
 | 
						|
                          PluginPolicy* plugin_policy) OVERRIDE {
 | 
						|
    // Always allow the PDF plugin to load.
 | 
						|
    if (*plugin_policy != PLUGIN_POLICY_ALLOW &&
 | 
						|
        mime_type == "application/pdf") {
 | 
						|
      *plugin_policy = PLUGIN_POLICY_ALLOW;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  void OnRequestContextInitialized(
 | 
						|
      CefRefPtr<CefRequestContext> request_context) OVERRIDE {
 | 
						|
    CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
    CefRefPtr<CefCommandLine> command_line =
 | 
						|
        CefCommandLine::GetGlobalCommandLine();
 | 
						|
    if (command_line->HasSwitch(switches::kLoadExtension)) {
 | 
						|
      if (MainContext::Get()
 | 
						|
              ->GetRootWindowManager()
 | 
						|
              ->request_context_per_browser()) {
 | 
						|
        // The example extension loading implementation requires all browsers to
 | 
						|
        // share the same request context.
 | 
						|
        LOG(ERROR)
 | 
						|
            << "Cannot mix --load-extension and --request-context-per-browser";
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      // Load one or more extension paths specified on the command-line and
 | 
						|
      // delimited with semicolon.
 | 
						|
      const std::string& extension_path =
 | 
						|
          command_line->GetSwitchValue(switches::kLoadExtension);
 | 
						|
      if (!extension_path.empty()) {
 | 
						|
        std::string part;
 | 
						|
        std::istringstream f(extension_path);
 | 
						|
        while (getline(f, part, ';')) {
 | 
						|
          if (!part.empty())
 | 
						|
            extension_util::LoadExtension(request_context, part, this);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // CefExtensionHandler methods:
 | 
						|
  void OnExtensionLoaded(CefRefPtr<CefExtension> extension) OVERRIDE {
 | 
						|
    CEF_REQUIRE_UI_THREAD();
 | 
						|
    MainContext::Get()->GetRootWindowManager()->AddExtension(extension);
 | 
						|
  }
 | 
						|
 | 
						|
  CefRefPtr<CefBrowser> GetActiveBrowser(CefRefPtr<CefExtension> extension,
 | 
						|
                                         CefRefPtr<CefBrowser> browser,
 | 
						|
                                         bool include_incognito) OVERRIDE {
 | 
						|
    CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
    // Return the browser for the active/foreground window.
 | 
						|
    CefRefPtr<CefBrowser> active_browser =
 | 
						|
        MainContext::Get()->GetRootWindowManager()->GetActiveBrowser();
 | 
						|
    if (!active_browser) {
 | 
						|
      LOG(WARNING)
 | 
						|
          << "No active browser available for extension "
 | 
						|
          << browser->GetHost()->GetExtension()->GetIdentifier().ToString();
 | 
						|
    } else {
 | 
						|
      // The active browser should not be hosting an extension.
 | 
						|
      DCHECK(!active_browser->GetHost()->GetExtension());
 | 
						|
    }
 | 
						|
    return active_browser;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  IMPLEMENT_REFCOUNTING(ClientRequestContextHandler);
 | 
						|
  DISALLOW_COPY_AND_ASSIGN(ClientRequestContextHandler);
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
RootWindowManager::RootWindowManager(bool terminate_when_all_windows_closed)
 | 
						|
    : terminate_when_all_windows_closed_(terminate_when_all_windows_closed) {
 | 
						|
  CefRefPtr<CefCommandLine> command_line =
 | 
						|
      CefCommandLine::GetGlobalCommandLine();
 | 
						|
  DCHECK(command_line.get());
 | 
						|
  request_context_per_browser_ =
 | 
						|
      command_line->HasSwitch(switches::kRequestContextPerBrowser);
 | 
						|
  request_context_shared_cache_ =
 | 
						|
      command_line->HasSwitch(switches::kRequestContextSharedCache);
 | 
						|
}
 | 
						|
 | 
						|
RootWindowManager::~RootWindowManager() {
 | 
						|
  // All root windows should already have been destroyed.
 | 
						|
  DCHECK(root_windows_.empty());
 | 
						|
}
 | 
						|
 | 
						|
scoped_refptr<RootWindow> RootWindowManager::CreateRootWindow(
 | 
						|
    const RootWindowConfig& config) {
 | 
						|
  CefBrowserSettings settings;
 | 
						|
  MainContext::Get()->PopulateBrowserSettings(&settings);
 | 
						|
 | 
						|
  scoped_refptr<RootWindow> root_window =
 | 
						|
      RootWindow::Create(MainContext::Get()->UseViews());
 | 
						|
  root_window->Init(this, config, settings);
 | 
						|
 | 
						|
  // Store a reference to the root window on the main thread.
 | 
						|
  OnRootWindowCreated(root_window);
 | 
						|
 | 
						|
  return root_window;
 | 
						|
}
 | 
						|
 | 
						|
scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsPopup(
 | 
						|
    bool with_controls,
 | 
						|
    bool with_osr,
 | 
						|
    const CefPopupFeatures& popupFeatures,
 | 
						|
    CefWindowInfo& windowInfo,
 | 
						|
    CefRefPtr<CefClient>& client,
 | 
						|
    CefBrowserSettings& settings) {
 | 
						|
  CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
  if (!temp_window_) {
 | 
						|
    // TempWindow must be created on the UI thread.
 | 
						|
    temp_window_.reset(new TempWindow());
 | 
						|
  }
 | 
						|
 | 
						|
  MainContext::Get()->PopulateBrowserSettings(&settings);
 | 
						|
 | 
						|
  scoped_refptr<RootWindow> root_window =
 | 
						|
      RootWindow::Create(MainContext::Get()->UseViews());
 | 
						|
  root_window->InitAsPopup(this, with_controls, with_osr, popupFeatures,
 | 
						|
                           windowInfo, client, settings);
 | 
						|
 | 
						|
  // Store a reference to the root window on the main thread.
 | 
						|
  OnRootWindowCreated(root_window);
 | 
						|
 | 
						|
  return root_window;
 | 
						|
}
 | 
						|
 | 
						|
scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsExtension(
 | 
						|
    CefRefPtr<CefExtension> extension,
 | 
						|
    const CefRect& source_bounds,
 | 
						|
    CefRefPtr<CefWindow> parent_window,
 | 
						|
    const base::Closure& close_callback,
 | 
						|
    bool with_controls,
 | 
						|
    bool with_osr) {
 | 
						|
  const std::string& extension_url = extension_util::GetExtensionURL(extension);
 | 
						|
  if (extension_url.empty()) {
 | 
						|
    NOTREACHED() << "Extension cannot be loaded directly.";
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // Create an initially hidden browser window that loads the extension URL.
 | 
						|
  // We'll show the window when the desired size becomes available via
 | 
						|
  // ClientHandler::OnAutoResize.
 | 
						|
  RootWindowConfig config;
 | 
						|
  config.with_controls = with_controls;
 | 
						|
  config.with_osr = with_osr;
 | 
						|
  config.with_extension = true;
 | 
						|
  config.initially_hidden = true;
 | 
						|
  config.source_bounds = source_bounds;
 | 
						|
  config.parent_window = parent_window;
 | 
						|
  config.close_callback = close_callback;
 | 
						|
  config.url = extension_url;
 | 
						|
  return CreateRootWindow(config);
 | 
						|
}
 | 
						|
 | 
						|
bool RootWindowManager::HasRootWindowAsExtension(
 | 
						|
    CefRefPtr<CefExtension> extension) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  RootWindowSet::const_iterator it = root_windows_.begin();
 | 
						|
  for (; it != root_windows_.end(); ++it) {
 | 
						|
    const RootWindow* root_window = (*it);
 | 
						|
    if (!root_window->WithExtension())
 | 
						|
      continue;
 | 
						|
 | 
						|
    CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
 | 
						|
    if (!browser)
 | 
						|
      continue;
 | 
						|
 | 
						|
    CefRefPtr<CefExtension> browser_extension =
 | 
						|
        browser->GetHost()->GetExtension();
 | 
						|
    DCHECK(browser_extension);
 | 
						|
    if (browser_extension->GetIdentifier() == extension->GetIdentifier())
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
scoped_refptr<RootWindow> RootWindowManager::GetWindowForBrowser(
 | 
						|
    int browser_id) const {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  RootWindowSet::const_iterator it = root_windows_.begin();
 | 
						|
  for (; it != root_windows_.end(); ++it) {
 | 
						|
    CefRefPtr<CefBrowser> browser = (*it)->GetBrowser();
 | 
						|
    if (browser.get() && browser->GetIdentifier() == browser_id)
 | 
						|
      return *it;
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
scoped_refptr<RootWindow> RootWindowManager::GetActiveRootWindow() const {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
  return active_root_window_;
 | 
						|
}
 | 
						|
 | 
						|
CefRefPtr<CefBrowser> RootWindowManager::GetActiveBrowser() const {
 | 
						|
  base::AutoLock lock_scope(active_browser_lock_);
 | 
						|
  return active_browser_;
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::CloseAllWindows(bool force) {
 | 
						|
  if (!CURRENTLY_ON_MAIN_THREAD()) {
 | 
						|
    // Execute this method on the main thread.
 | 
						|
    MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::CloseAllWindows,
 | 
						|
                                 base::Unretained(this), force));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (root_windows_.empty())
 | 
						|
    return;
 | 
						|
 | 
						|
  // Use a copy of |root_windows_| because the original set may be modified
 | 
						|
  // in OnRootWindowDestroyed while iterating.
 | 
						|
  RootWindowSet root_windows = root_windows_;
 | 
						|
 | 
						|
  RootWindowSet::const_iterator it = root_windows.begin();
 | 
						|
  for (; it != root_windows.end(); ++it)
 | 
						|
    (*it)->Close(force);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::AddExtension(CefRefPtr<CefExtension> extension) {
 | 
						|
  if (!CURRENTLY_ON_MAIN_THREAD()) {
 | 
						|
    // Execute this method on the main thread.
 | 
						|
    MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::AddExtension,
 | 
						|
                                 base::Unretained(this), extension));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Don't track extensions that can't be loaded directly.
 | 
						|
  if (extension_util::GetExtensionURL(extension).empty())
 | 
						|
    return;
 | 
						|
 | 
						|
  // Don't add the same extension multiple times.
 | 
						|
  ExtensionSet::const_iterator it = extensions_.begin();
 | 
						|
  for (; it != extensions_.end(); ++it) {
 | 
						|
    if ((*it)->GetIdentifier() == extension->GetIdentifier())
 | 
						|
      return;
 | 
						|
  }
 | 
						|
 | 
						|
  extensions_.insert(extension);
 | 
						|
  NotifyExtensionsChanged();
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::OnRootWindowCreated(
 | 
						|
    scoped_refptr<RootWindow> root_window) {
 | 
						|
  if (!CURRENTLY_ON_MAIN_THREAD()) {
 | 
						|
    // Execute this method on the main thread.
 | 
						|
    MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::OnRootWindowCreated,
 | 
						|
                                 base::Unretained(this), root_window));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  root_windows_.insert(root_window);
 | 
						|
  if (!root_window->WithExtension()) {
 | 
						|
    root_window->OnExtensionsChanged(extensions_);
 | 
						|
 | 
						|
    if (root_windows_.size() == 1U) {
 | 
						|
      // The first non-extension root window should be considered the active
 | 
						|
      // window.
 | 
						|
      OnRootWindowActivated(root_window);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::NotifyExtensionsChanged() {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  RootWindowSet::const_iterator it = root_windows_.begin();
 | 
						|
  for (; it != root_windows_.end(); ++it) {
 | 
						|
    RootWindow* root_window = *it;
 | 
						|
    if (!root_window->WithExtension())
 | 
						|
      root_window->OnExtensionsChanged(extensions_);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
CefRefPtr<CefRequestContext> RootWindowManager::GetRequestContext(
 | 
						|
    RootWindow* root_window) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (request_context_per_browser_) {
 | 
						|
    // Create a new request context for each browser.
 | 
						|
    CefRequestContextSettings settings;
 | 
						|
 | 
						|
    CefRefPtr<CefCommandLine> command_line =
 | 
						|
        CefCommandLine::GetGlobalCommandLine();
 | 
						|
    if (command_line->HasSwitch(switches::kCachePath)) {
 | 
						|
      if (request_context_shared_cache_) {
 | 
						|
        // Give each browser the same cache path. The resulting context objects
 | 
						|
        // will share the same storage internally.
 | 
						|
        CefString(&settings.cache_path) =
 | 
						|
            command_line->GetSwitchValue(switches::kCachePath);
 | 
						|
      } else {
 | 
						|
        // Give each browser a unique cache path. This will create completely
 | 
						|
        // isolated context objects.
 | 
						|
        std::stringstream ss;
 | 
						|
        ss << command_line->GetSwitchValue(switches::kCachePath).ToString()
 | 
						|
           << file_util::kPathSep << time(NULL);
 | 
						|
        CefString(&settings.cache_path) = ss.str();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return CefRequestContext::CreateContext(settings,
 | 
						|
                                            new ClientRequestContextHandler);
 | 
						|
  }
 | 
						|
 | 
						|
  // All browsers will share the global request context.
 | 
						|
  if (!shared_request_context_.get()) {
 | 
						|
    shared_request_context_ = CefRequestContext::CreateContext(
 | 
						|
        CefRequestContext::GetGlobalContext(), new ClientRequestContextHandler);
 | 
						|
  }
 | 
						|
  return shared_request_context_;
 | 
						|
}
 | 
						|
 | 
						|
scoped_refptr<ImageCache> RootWindowManager::GetImageCache() {
 | 
						|
  CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
  if (!image_cache_) {
 | 
						|
    image_cache_ = new ImageCache;
 | 
						|
  }
 | 
						|
  return image_cache_;
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::OnTest(RootWindow* root_window, int test_id) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  test_runner::RunTest(root_window->GetBrowser(), test_id);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::OnExit(RootWindow* root_window) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  CloseAllWindows(false);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::OnRootWindowDestroyed(RootWindow* root_window) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  RootWindowSet::iterator it = root_windows_.find(root_window);
 | 
						|
  DCHECK(it != root_windows_.end());
 | 
						|
  if (it != root_windows_.end())
 | 
						|
    root_windows_.erase(it);
 | 
						|
 | 
						|
  if (root_window == active_root_window_) {
 | 
						|
    active_root_window_ = nullptr;
 | 
						|
 | 
						|
    base::AutoLock lock_scope(active_browser_lock_);
 | 
						|
    active_browser_ = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (terminate_when_all_windows_closed_ && root_windows_.empty()) {
 | 
						|
    // All windows have closed. Clean up on the UI thread.
 | 
						|
    CefPostTask(TID_UI, base::Bind(&RootWindowManager::CleanupOnUIThread,
 | 
						|
                                   base::Unretained(this)));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::OnRootWindowActivated(RootWindow* root_window) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (root_window->WithExtension()) {
 | 
						|
    // We don't want extension apps to become the active RootWindow.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (root_window == active_root_window_)
 | 
						|
    return;
 | 
						|
 | 
						|
  active_root_window_ = root_window;
 | 
						|
 | 
						|
  {
 | 
						|
    base::AutoLock lock_scope(active_browser_lock_);
 | 
						|
    // May be NULL at this point, in which case we'll make the association in
 | 
						|
    // OnBrowserCreated.
 | 
						|
    active_browser_ = active_root_window_->GetBrowser();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::OnBrowserCreated(RootWindow* root_window,
 | 
						|
                                         CefRefPtr<CefBrowser> browser) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (root_window == active_root_window_) {
 | 
						|
    base::AutoLock lock_scope(active_browser_lock_);
 | 
						|
    active_browser_ = browser;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::CreateExtensionWindow(
 | 
						|
    CefRefPtr<CefExtension> extension,
 | 
						|
    const CefRect& source_bounds,
 | 
						|
    CefRefPtr<CefWindow> parent_window,
 | 
						|
    const base::Closure& close_callback,
 | 
						|
    bool with_osr) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (!HasRootWindowAsExtension(extension)) {
 | 
						|
    CreateRootWindowAsExtension(extension, source_bounds, parent_window,
 | 
						|
                                close_callback, false, with_osr);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowManager::CleanupOnUIThread() {
 | 
						|
  CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
  if (temp_window_) {
 | 
						|
    // TempWindow must be destroyed on the UI thread.
 | 
						|
    temp_window_.reset(nullptr);
 | 
						|
  }
 | 
						|
 | 
						|
  if (image_cache_) {
 | 
						|
    image_cache_ = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // Quit the main message loop.
 | 
						|
  MainMessageLoop::Get()->Quit();
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace client
 |