mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add support for loading extensions (issue #1947)
- Add CefRequestContext::LoadExtension, CefExtension, CefExtensionHandler and related methods/interfaces. - Add chrome://extensions-support that lists supported Chrome APIs. - Add CefBrowserHost::SetAutoResizeEnabled and CefDisplayHandler::OnAutoResize to support browser resize based on preferred web contents size. - views: Add support for custom CefMenuButton popups. - cefclient: Run with `--load-extension=set_page_color` command-line flag for an extension loading example. Add `--use-views` on Windows and Linux for an even better example.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#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/resource_util.h"
|
||||
#include "tests/shared/common/client_switches.h"
|
||||
|
||||
@@ -18,10 +19,12 @@ namespace client {
|
||||
|
||||
namespace {
|
||||
|
||||
class ClientRequestContextHandler : public CefRequestContextHandler {
|
||||
class ClientRequestContextHandler : public CefRequestContextHandler,
|
||||
public CefExtensionHandler {
|
||||
public:
|
||||
ClientRequestContextHandler() {}
|
||||
|
||||
// CefRequestContextHandler methods:
|
||||
bool OnBeforePluginLoad(const CefString& mime_type,
|
||||
const CefString& plugin_url,
|
||||
bool is_main_frame,
|
||||
@@ -38,14 +41,73 @@ class ClientRequestContextHandler : public CefRequestContextHandler {
|
||||
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) {
|
||||
: terminate_when_all_windows_closed_(terminate_when_all_windows_closed),
|
||||
image_cache_(new ImageCache) {
|
||||
CefRefPtr<CefCommandLine> command_line =
|
||||
CefCommandLine::GetGlobalCommandLine();
|
||||
DCHECK(command_line.get());
|
||||
@@ -61,17 +123,13 @@ RootWindowManager::~RootWindowManager() {
|
||||
}
|
||||
|
||||
scoped_refptr<RootWindow> RootWindowManager::CreateRootWindow(
|
||||
bool with_controls,
|
||||
bool with_osr,
|
||||
const CefRect& bounds,
|
||||
const std::string& url) {
|
||||
const RootWindowConfig& config) {
|
||||
CefBrowserSettings settings;
|
||||
MainContext::Get()->PopulateBrowserSettings(&settings);
|
||||
|
||||
scoped_refptr<RootWindow> root_window =
|
||||
RootWindow::Create(MainContext::Get()->UseViews());
|
||||
root_window->Init(this, with_controls, with_osr, bounds, settings,
|
||||
url.empty() ? MainContext::Get()->GetMainURL() : url);
|
||||
root_window->Init(this, config, settings);
|
||||
|
||||
// Store a reference to the root window on the main thread.
|
||||
OnRootWindowCreated(root_window);
|
||||
@@ -101,8 +159,60 @@ scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsPopup(
|
||||
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 NULL;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
int browser_id) const {
|
||||
REQUIRE_MAIN_THREAD();
|
||||
|
||||
RootWindowSet::const_iterator it = root_windows_.begin();
|
||||
@@ -114,6 +224,16 @@ scoped_refptr<RootWindow> RootWindowManager::GetWindowForBrowser(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -130,6 +250,29 @@ void RootWindowManager::CloseAllWindows(bool force) {
|
||||
(*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()) {
|
||||
@@ -140,6 +283,26 @@ void RootWindowManager::OnRootWindowCreated(
|
||||
}
|
||||
|
||||
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(
|
||||
@@ -180,14 +343,10 @@ CefRefPtr<CefRequestContext> RootWindowManager::GetRequestContext(
|
||||
return shared_request_context_;
|
||||
}
|
||||
|
||||
CefRefPtr<CefImage> RootWindowManager::GetDefaultWindowIcon() {
|
||||
scoped_refptr<ImageCache> RootWindowManager::GetImageCache() {
|
||||
REQUIRE_MAIN_THREAD();
|
||||
|
||||
if (!default_window_icon_) {
|
||||
// Create the Image and load resources at different scale factors.
|
||||
default_window_icon_ = LoadImageIcon("window_icon");
|
||||
}
|
||||
return default_window_icon_;
|
||||
return image_cache_;
|
||||
}
|
||||
|
||||
void RootWindowManager::OnTest(RootWindow* root_window, int test_id) {
|
||||
@@ -210,10 +369,62 @@ void RootWindowManager::OnRootWindowDestroyed(RootWindow* root_window) {
|
||||
if (it != root_windows_.end())
|
||||
root_windows_.erase(it);
|
||||
|
||||
if (root_window == active_root_window_) {
|
||||
active_root_window_ = NULL;
|
||||
|
||||
base::AutoLock lock_scope(active_browser_lock_);
|
||||
active_browser_ = NULL;
|
||||
}
|
||||
|
||||
if (terminate_when_all_windows_closed_ && root_windows_.empty()) {
|
||||
// Quit the main message loop after all windows have closed.
|
||||
MainMessageLoop::Get()->Quit();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
Reference in New Issue
Block a user