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:
Marshall Greenblatt
2017-08-03 18:55:19 -04:00
parent 5b12134a45
commit 9cff99dc4e
178 changed files with 10360 additions and 650 deletions

View File

@@ -83,6 +83,7 @@
'shared_sources_resources',
'cefclient_bundle_resources_mac:MACOSX',
'cefclient_sources_resources',
'cefclient_sources_resources_extensions_set_page_color',
],
}}
@@ -192,7 +193,6 @@ if(OS_MACOSX)
add_dependencies(${CEF_TARGET} libcef_dll_wrapper "${CEF_HELPER_TARGET}")
target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS} "-framework OpenGL")
set_target_properties(${CEF_TARGET} PROPERTIES
RESOURCE "${CEFCLIENT_RESOURCES_SRCS}"
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/Info.plist
)
@@ -214,17 +214,16 @@ if(OS_MACOSX)
# Fix the framework rpath in the main executable.
FIX_MACOSX_MAIN_FRAMEWORK_RPATH(${CEF_TARGET})
if(NOT ${CMAKE_GENERATOR} STREQUAL "Xcode")
# Manually process and copy over resource files.
# The Xcode generator handles this via the set_target_properties RESOURCE directive.
# Remove these prefixes from input file paths.
set(PREFIXES
"resources/mac/"
"resources/"
"../shared/resources/"
)
COPY_MACOSX_RESOURCES("${CEFCLIENT_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
endif()
# Manually process and copy over resource files.
# The Xcode generator can support this via the set_target_properties RESOURCE
# directive but that doesn't properly handle nested resource directories.
# Remove these prefixes from input file paths.
set(PREFIXES
"resources/mac/"
"resources/"
"../shared/resources/"
)
COPY_MACOSX_RESOURCES("${CEFCLIENT_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
endif()

View File

@@ -73,6 +73,11 @@ void BrowserWindow::OnSetFullscreen(bool fullscreen) {
delegate_->OnSetFullscreen(fullscreen);
}
void BrowserWindow::OnAutoResize(const CefSize& new_size) {
REQUIRE_MAIN_THREAD();
delegate_->OnAutoResize(new_size);
}
void BrowserWindow::OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) {

View File

@@ -37,6 +37,9 @@ class BrowserWindow : public ClientHandler::Delegate {
// Set fullscreen mode.
virtual void OnSetFullscreen(bool fullscreen) = 0;
// Auto-resize contents.
virtual void OnAutoResize(const CefSize& new_size) = 0;
// Set the loading state.
virtual void OnSetLoadingState(bool isLoading,
bool canGoBack,
@@ -117,6 +120,7 @@ class BrowserWindow : public ClientHandler::Delegate {
void OnSetAddress(const std::string& url) OVERRIDE;
void OnSetTitle(const std::string& title) OVERRIDE;
void OnSetFullscreen(bool fullscreen) OVERRIDE;
void OnAutoResize(const CefSize& new_size) OVERRIDE;
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;

View File

@@ -20,6 +20,7 @@
#include "tests/cefclient/browser/main_context.h"
#include "tests/cefclient/browser/root_window_manager.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"
@@ -417,6 +418,14 @@ bool ClientHandler::OnConsoleMessage(CefRefPtr<CefBrowser> browser,
return false;
}
bool ClientHandler::OnAutoResize(CefRefPtr<CefBrowser> browser,
const CefSize& new_size) {
CEF_REQUIRE_UI_THREAD();
NotifyAutoResize(new_size);
return true;
}
void ClientHandler::OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
@@ -541,6 +550,19 @@ void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
if (mouse_cursor_change_disabled_)
browser->GetHost()->SetMouseCursorChangeDisabled(true);
if (browser->GetHost()->GetExtension()) {
// Browsers hosting extension apps should auto-resize.
browser->GetHost()->SetAutoResizeEnabled(true, CefSize(20, 20),
CefSize(1000, 1000));
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
if (extension_util::IsInternalExtension(extension->GetPath())) {
// Register the internal handler for extension resources.
extension_util::AddInternalExtensionToResourceManager(extension,
resource_manager_);
}
}
NotifyBrowserCreated(browser);
}
@@ -623,8 +645,11 @@ bool ClientHandler::OnOpenURLFromTab(
target_disposition == WOD_NEW_FOREGROUND_TAB) {
// Handle middle-click and ctrl + left-click by opening the URL in a new
// browser window.
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(
true, is_osr(), CefRect(), target_url);
RootWindowConfig config;
config.with_controls = true;
config.with_osr = is_osr();
config.url = target_url;
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(config);
return true;
}
@@ -867,9 +892,11 @@ void ClientHandler::ShowSSLInformation(CefRefPtr<CefBrowser> browser) {
ss << "</body></html>";
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(
false, is_osr(), CefRect(),
test_runner::GetDataURI(ss.str(), "text/html"));
RootWindowConfig config;
config.with_controls = false;
config.with_osr = is_osr();
config.url = test_runner::GetDataURI(ss.str(), "text/html");
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(config);
}
bool ClientHandler::CreatePopupWindow(CefRefPtr<CefBrowser> browser,
@@ -969,6 +996,18 @@ void ClientHandler::NotifyFullscreen(bool fullscreen) {
delegate_->OnSetFullscreen(fullscreen);
}
void ClientHandler::NotifyAutoResize(const CefSize& new_size) {
if (!CURRENTLY_ON_MAIN_THREAD()) {
// Execute this method on the main thread.
MAIN_POST_CLOSURE(
base::Bind(&ClientHandler::NotifyAutoResize, this, new_size));
return;
}
if (delegate_)
delegate_->OnAutoResize(new_size);
}
void ClientHandler::NotifyLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) {

View File

@@ -58,11 +58,14 @@ class ClientHandler : public CefClient,
virtual void OnSetTitle(const std::string& title) = 0;
// Set the Favicon image.
virtual void OnSetFavicon(CefRefPtr<CefImage> image){};
virtual void OnSetFavicon(CefRefPtr<CefImage> image) {}
// Set fullscreen mode.
virtual void OnSetFullscreen(bool fullscreen) = 0;
// Auto-resize contents.
virtual void OnAutoResize(const CefSize& new_size) = 0;
// Set the loading state.
virtual void OnSetLoadingState(bool isLoading,
bool canGoBack,
@@ -147,6 +150,8 @@ class ClientHandler : public CefClient,
const CefString& message,
const CefString& source,
int line) OVERRIDE;
bool OnAutoResize(CefRefPtr<CefBrowser> browser,
const CefSize& new_size) OVERRIDE;
// CefDownloadHandler methods
void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
@@ -311,6 +316,7 @@ class ClientHandler : public CefClient,
void NotifyTitle(const CefString& title);
void NotifyFavicon(CefRefPtr<CefImage> image);
void NotifyFullscreen(bool fullscreen);
void NotifyAutoResize(const CefSize& new_size);
void NotifyLoadingState(bool isLoading, bool canGoBack, bool canGoForward);
void NotifyDraggableRegions(const std::vector<CefDraggableRegion>& regions);
void NotifyTakeFocus(bool next);

View File

@@ -0,0 +1,306 @@
// Copyright (c) 2017 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/image_cache.h"
#include "tests/shared/browser/file_util.h"
#include "tests/shared/browser/resource_util.h"
namespace client {
namespace {
const char kEmptyId[] = "__empty";
} // namespace
ImageCache::ImageCache() {}
ImageCache::~ImageCache() {
CEF_REQUIRE_UI_THREAD();
}
ImageCache::ImageRep::ImageRep(const std::string& path, float scale_factor)
: path_(path), scale_factor_(scale_factor) {
DCHECK(!path_.empty());
DCHECK_GT(scale_factor_, 0.0f);
}
ImageCache::ImageInfo::ImageInfo(const std::string& id,
const ImageRepSet& reps,
bool internal,
bool force_reload)
: id_(id), reps_(reps), internal_(internal), force_reload_(force_reload) {
#ifndef NDEBUG
DCHECK(!id_.empty());
if (id_ != kEmptyId)
DCHECK(!reps_.empty());
#endif
}
// static
ImageCache::ImageInfo ImageCache::ImageInfo::Empty() {
return ImageInfo(kEmptyId, ImageRepSet(), true, false);
}
// static
ImageCache::ImageInfo ImageCache::ImageInfo::Create1x(
const std::string& id,
const std::string& path_1x,
bool internal) {
ImageRepSet reps;
reps.push_back(ImageRep(path_1x, 1.0f));
return ImageInfo(id, reps, internal, false);
}
// static
ImageCache::ImageInfo ImageCache::ImageInfo::Create2x(
const std::string& id,
const std::string& path_1x,
const std::string& path_2x,
bool internal) {
ImageRepSet reps;
reps.push_back(ImageRep(path_1x, 1.0f));
reps.push_back(ImageRep(path_2x, 2.0f));
return ImageInfo(id, reps, internal, false);
}
// static
ImageCache::ImageInfo ImageCache::ImageInfo::Create2x(const std::string& id) {
return Create2x(id, id + ".1x.png", id + ".2x.png", true);
}
struct ImageCache::ImageContent {
ImageContent() {}
struct RepContent {
RepContent(ImageType type, float scale_factor, const std::string& contents)
: type_(type), scale_factor_(scale_factor), contents_(contents) {}
ImageType type_;
float scale_factor_;
std::string contents_;
};
typedef std::vector<RepContent> RepContentSet;
RepContentSet contents_;
CefRefPtr<CefImage> image_;
};
void ImageCache::LoadImages(const ImageInfoSet& image_info,
const LoadImagesCallback& callback) {
DCHECK(!image_info.empty());
DCHECK(!callback.is_null());
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::Bind(&ImageCache::LoadImages, this, image_info,
callback));
return;
}
ImageSet images;
bool missing_images = false;
ImageInfoSet::const_iterator it = image_info.begin();
for (; it != image_info.end(); ++it) {
const ImageInfo& info = *it;
if (info.id_ == kEmptyId) {
// Image intentionally left empty.
images.push_back(NULL);
continue;
}
ImageMap::iterator it2 = image_map_.find(info.id_);
if (it2 != image_map_.end()) {
if (!info.force_reload_) {
// Image already exists.
images.push_back(it2->second);
continue;
}
// Remove the existing image from the map.
image_map_.erase(it2);
}
// Load the image.
images.push_back(NULL);
if (!missing_images)
missing_images = true;
}
if (missing_images) {
CefPostTask(TID_FILE, base::Bind(&ImageCache::LoadMissing, this, image_info,
images, callback));
} else {
callback.Run(images);
}
}
CefRefPtr<CefImage> ImageCache::GetCachedImage(const std::string& image_id) {
CEF_REQUIRE_UI_THREAD();
DCHECK(!image_id.empty());
ImageMap::const_iterator it = image_map_.find(image_id);
if (it != image_map_.end())
return it->second;
return NULL;
}
// static
ImageCache::ImageType ImageCache::GetImageType(const std::string& path) {
std::string ext = file_util::GetFileExtension(path);
if (ext.empty())
return TYPE_NONE;
std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
if (ext == "png")
return TYPE_PNG;
if (ext == "jpg" || ext == "jpeg")
return TYPE_JPEG;
return TYPE_NONE;
}
void ImageCache::LoadMissing(const ImageInfoSet& image_info,
const ImageSet& images,
const LoadImagesCallback& callback) {
CEF_REQUIRE_FILE_THREAD();
DCHECK_EQ(image_info.size(), images.size());
ImageContentSet contents;
ImageInfoSet::const_iterator it1 = image_info.begin();
ImageSet::const_iterator it2 = images.begin();
for (; it1 != image_info.end() && it2 != images.end(); ++it1, ++it2) {
const ImageInfo& info = *it1;
ImageContent content;
if (*it2 || info.id_ == kEmptyId) {
// Image already exists or is intentionally empty.
content.image_ = *it2;
} else {
LoadImageContents(info, &content);
}
contents.push_back(content);
}
CefPostTask(TID_UI, base::Bind(&ImageCache::UpdateCache, this, image_info,
contents, callback));
}
// static
bool ImageCache::LoadImageContents(const ImageInfo& info,
ImageContent* content) {
CEF_REQUIRE_FILE_THREAD();
ImageRepSet::const_iterator it = info.reps_.begin();
for (; it != info.reps_.end(); ++it) {
const ImageRep& rep = *it;
ImageType rep_type;
std::string rep_contents;
if (!LoadImageContents(rep.path_, info.internal_, &rep_type,
&rep_contents)) {
LOG(ERROR) << "Failed to load image " << info.id_ << " from path "
<< rep.path_;
return false;
}
content->contents_.push_back(
ImageContent::RepContent(rep_type, rep.scale_factor_, rep_contents));
}
return true;
}
// static
bool ImageCache::LoadImageContents(const std::string& path,
bool internal,
ImageType* type,
std::string* contents) {
CEF_REQUIRE_FILE_THREAD();
*type = GetImageType(path);
if (*type == TYPE_NONE)
return false;
if (internal) {
if (!LoadBinaryResource(path.c_str(), *contents))
return false;
} else if (!file_util::ReadFileToString(path, contents)) {
return false;
}
return !contents->empty();
}
void ImageCache::UpdateCache(const ImageInfoSet& image_info,
const ImageContentSet& contents,
const LoadImagesCallback& callback) {
CEF_REQUIRE_UI_THREAD();
DCHECK_EQ(image_info.size(), contents.size());
ImageSet images;
ImageInfoSet::const_iterator it1 = image_info.begin();
ImageContentSet::const_iterator it2 = contents.begin();
for (; it1 != image_info.end() && it2 != contents.end(); ++it1, ++it2) {
const ImageInfo& info = *it1;
const ImageContent& content = *it2;
if (content.image_ || info.id_ == kEmptyId) {
// Image already exists or is intentionally empty.
images.push_back(content.image_);
} else {
CefRefPtr<CefImage> image = CreateImage(info.id_, content);
images.push_back(image);
// Add the image to the map.
image_map_.insert(std::make_pair(info.id_, image));
}
}
callback.Run(images);
}
// static
CefRefPtr<CefImage> ImageCache::CreateImage(const std::string& image_id,
const ImageContent& content) {
CEF_REQUIRE_UI_THREAD();
// Shouldn't be creating an image if one already exists.
DCHECK(!content.image_);
if (content.contents_.empty())
return NULL;
CefRefPtr<CefImage> image = CefImage::CreateImage();
ImageContent::RepContentSet::const_iterator it = content.contents_.begin();
for (; it != content.contents_.end(); ++it) {
const ImageContent::RepContent& rep = *it;
if (rep.type_ == TYPE_PNG) {
if (!image->AddPNG(rep.scale_factor_, rep.contents_.c_str(),
rep.contents_.size())) {
LOG(ERROR) << "Failed to create image " << image_id << " for PNG@"
<< rep.scale_factor_;
return NULL;
}
} else if (rep.type_ == TYPE_JPEG) {
if (!image->AddJPEG(rep.scale_factor_, rep.contents_.c_str(),
rep.contents_.size())) {
LOG(ERROR) << "Failed to create image " << image_id << " for JPG@"
<< rep.scale_factor_;
return NULL;
}
} else {
NOTREACHED();
return NULL;
}
}
return image;
}
} // namespace client

View File

@@ -0,0 +1,127 @@
// Copyright (c) 2017 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.
#ifndef CEF_TESTS_CEFCLIENT_BROWSER_IMAGE_CACHE_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_IMAGE_CACHE_H_
#pragma once
#include <map>
#include <vector>
#include "include/base/cef_bind.h"
#include "include/base/cef_ref_counted.h"
#include "include/cef_image.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
namespace client {
// Simple image caching implementation.
class ImageCache
: public base::RefCountedThreadSafe<ImageCache, CefDeleteOnUIThread> {
public:
ImageCache();
// Image representation at a specific scale factor.
struct ImageRep {
ImageRep(const std::string& path, float scale_factor);
// Full file system path.
std::string path_;
// Image scale factor (usually 1.0f or 2.0f).
float scale_factor_;
};
typedef std::vector<ImageRep> ImageRepSet;
// Unique image that may have multiple representations.
struct ImageInfo {
ImageInfo(const std::string& id,
const ImageRepSet& reps,
bool internal,
bool force_reload);
// Helper for returning an empty image.
static ImageInfo Empty();
// Helpers for creating common representations.
static ImageInfo Create1x(const std::string& id,
const std::string& path_1x,
bool internal);
static ImageInfo Create2x(const std::string& id,
const std::string& path_1x,
const std::string& path_2x,
bool internal);
static ImageInfo Create2x(const std::string& id);
// Image unique ID.
std::string id_;
// Image representations to load.
ImageRepSet reps_;
// True if the image is internal (loaded via LoadBinaryResource).
bool internal_;
// True to force reload.
bool force_reload_;
};
typedef std::vector<ImageInfo> ImageInfoSet;
typedef std::vector<CefRefPtr<CefImage>> ImageSet;
typedef base::Callback<void(const ImageSet& /*images*/)> LoadImagesCallback;
// Loads the images represented by |image_info|. Executes |callback|
// either synchronously or asychronously on the UI thread after completion.
void LoadImages(const ImageInfoSet& image_info,
const LoadImagesCallback& callback);
// Returns an image that has already been cached. Must be called on the
// UI thread.
CefRefPtr<CefImage> GetCachedImage(const std::string& image_id);
private:
// Only allow deletion via scoped_refptr.
friend struct CefDeleteOnThread<TID_UI>;
friend class base::RefCountedThreadSafe<ImageCache, CefDeleteOnUIThread>;
~ImageCache();
enum ImageType {
TYPE_NONE,
TYPE_PNG,
TYPE_JPEG,
};
static ImageType GetImageType(const std::string& path);
struct ImageContent;
typedef std::vector<ImageContent> ImageContentSet;
// Load missing image contents on the FILE thread.
void LoadMissing(const ImageInfoSet& image_info,
const ImageSet& images,
const LoadImagesCallback& callback);
static bool LoadImageContents(const ImageInfo& info, ImageContent* content);
static bool LoadImageContents(const std::string& path,
bool internal,
ImageType* type,
std::string* contents);
// Create missing CefImage representations on the UI thread.
void UpdateCache(const ImageInfoSet& image_info,
const ImageContentSet& contents,
const LoadImagesCallback& callback);
static CefRefPtr<CefImage> CreateImage(const std::string& image_id,
const ImageContent& content);
// Map image ID to image representation. Only accessed on the UI thread.
typedef std::map<std::string, CefRefPtr<CefImage>> ImageMap;
ImageMap image_map_;
};
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_IMAGE_CACHE_H_

View File

@@ -65,6 +65,11 @@
#define IDS_WINDOW_ICON_2X_PNG 1020
#define IDS_XMLHTTPREQUEST_HTML 1021
#define IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG 1030
#define IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON 1031
#define IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML 1032
#define IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS 1033
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED

View File

@@ -18,6 +18,14 @@ int GetResourceId(const char* resource_name) {
{"dialogs.html", IDS_DIALOGS_HTML},
{"draggable.html", IDS_DRAGGABLE_HTML},
{"drm.html", IDS_DRM_HTML},
{"extensions/set_page_color/icon.png",
IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG},
{"extensions/set_page_color/manifest.json",
IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON},
{"extensions/set_page_color/popup.html",
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML},
{"extensions/set_page_color/popup.js",
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS},
{"logo.png", IDS_LOGO_PNG},
{"localstorage.html", IDS_LOCALSTORAGE_HTML},
{"menu_icon.1x.png", IDS_MENU_ICON_1X_PNG},

View File

@@ -9,10 +9,36 @@
namespace client {
RootWindowConfig::RootWindowConfig()
: with_controls(true),
with_osr(false),
with_extension(false),
initially_hidden(false),
url(MainContext::Get()->GetMainURL()) {}
RootWindow::RootWindow() : delegate_(NULL) {}
RootWindow::~RootWindow() {}
// static
scoped_refptr<RootWindow> RootWindow::GetForBrowser(int browser_id) {
return MainContext::Get()->GetRootWindowManager()->GetWindowForBrowser(
browser_id);
}
void RootWindow::OnExtensionsChanged(const ExtensionSet& extensions) {
REQUIRE_MAIN_THREAD();
DCHECK(delegate_);
DCHECK(!WithExtension());
if (extensions.empty())
return;
ExtensionSet::const_iterator it = extensions.begin();
for (; it != extensions.end(); ++it) {
delegate_->CreateExtensionWindow(*it, CefRect(), NULL, base::Closure(),
WithWindowlessRendering());
}
}
} // namespace client

View File

@@ -6,15 +6,58 @@
#define CEF_TESTS_CEFCLIENT_BROWSER_ROOT_WINDOW_H_
#pragma once
#include <set>
#include <string>
#include "include/base/cef_callback_forward.h"
#include "include/base/cef_ref_counted.h"
#include "include/cef_browser.h"
#include "include/views/cef_window.h"
#include "tests/cefclient/browser/client_types.h"
#include "tests/cefclient/browser/image_cache.h"
#include "tests/shared/browser/main_message_loop.h"
namespace client {
// Used to configure how a RootWindow is created.
struct RootWindowConfig {
RootWindowConfig();
// If true the window will show controls.
bool with_controls;
// If true the window will use off-screen rendering.
bool with_osr;
// If true the window is hosting an extension app.
bool with_extension;
// If true the window will be created initially hidden.
bool initially_hidden;
// Requested window position. If |bounds| and |source_bounds| are empty the
// default window size and location will be used.
CefRect bounds;
// Position of the UI element that triggered the window creation. If |bounds|
// is empty and |source_bounds| is non-empty the new window will be positioned
// relative to |source_bounds|. This is currently only implemented for Views-
// based windows when |initially_hidden| is also true.
CefRect source_bounds;
// Parent window. Only used for Views-based windows.
CefRefPtr<CefWindow> parent_window;
// Callback to be executed when the window is closed. Will be executed on the
// main thread. This is currently only implemented for Views-based windows.
base::Closure close_callback;
// Initial URL to load.
std::string url;
};
typedef std::set<CefRefPtr<CefExtension>> ExtensionSet;
// Represents a top-level native window in the browser process. While references
// to this object are thread-safe the methods must be called on the main thread
// unless otherwise indicated.
@@ -30,8 +73,8 @@ class RootWindow
virtual CefRefPtr<CefRequestContext> GetRequestContext(
RootWindow* root_window) = 0;
// Returns the default window icon.
virtual CefRefPtr<CefImage> GetDefaultWindowIcon() = 0;
// Returns the ImageCache.
virtual scoped_refptr<ImageCache> GetImageCache() = 0;
// Called to execute a test. See resource.h for |test_id| values.
virtual void OnTest(RootWindow* root_window, int test_id) = 0;
@@ -42,6 +85,21 @@ class RootWindow
// Called when the RootWindow has been destroyed.
virtual void OnRootWindowDestroyed(RootWindow* root_window) = 0;
// Called when the RootWindow is activated (becomes the foreground window).
virtual void OnRootWindowActivated(RootWindow* root_window) = 0;
// Called when the browser is created for the RootWindow.
virtual void OnBrowserCreated(RootWindow* root_window,
CefRefPtr<CefBrowser> browser) = 0;
// Create a window for |extension|. |source_bounds| are the bounds of the
// UI element, like a button, that triggered the extension.
virtual void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
const CefRect& source_bounds,
CefRefPtr<CefWindow> parent_window,
const base::Closure& close_callback,
bool with_osr) = 0;
protected:
virtual ~Delegate() {}
};
@@ -68,11 +126,8 @@ class RootWindow
// Use RootWindowManager::CreateRootWindow() instead of calling this method
// directly.
virtual void Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& bounds,
const CefBrowserSettings& settings,
const std::string& url) = 0;
const RootWindowConfig& config,
const CefBrowserSettings& settings) = 0;
// Initialize as a popup window. This is used to attach a new native window to
// a single browser instance that will be created later. The native window
@@ -121,12 +176,25 @@ class RootWindow
// Returns the native handle for this window, if any.
virtual ClientWindowHandle GetWindowHandle() const = 0;
// Returns true if this window is using windowless rendering (osr).
virtual bool WithWindowlessRendering() const = 0;
// Returns true if this window is hosting an extension app.
virtual bool WithExtension() const = 0;
// Called when the set of loaded extensions changes. The default
// implementation will create a single window instance for each extension.
virtual void OnExtensionsChanged(const ExtensionSet& extensions);
protected:
// Allow deletion via scoped_refptr only.
friend struct DeleteOnMainThread;
friend class base::RefCountedThreadSafe<RootWindow, DeleteOnMainThread>;
virtual ~RootWindow() {}
RootWindow();
virtual ~RootWindow();
Delegate* delegate_;
};
} // namespace client

View File

@@ -49,9 +49,9 @@ void MaximizeWindow(GtkWindow* window) {
} // namespace
RootWindowGtk::RootWindowGtk()
: delegate_(NULL),
with_controls_(false),
: with_controls_(false),
with_osr_(false),
with_extension_(false),
is_popup_(false),
initialized_(false),
window_(NULL),
@@ -75,29 +75,27 @@ RootWindowGtk::~RootWindowGtk() {
}
void RootWindowGtk::Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& bounds,
const CefBrowserSettings& settings,
const std::string& url) {
const RootWindowConfig& config,
const CefBrowserSettings& settings) {
DCHECK(delegate);
DCHECK(!initialized_);
delegate_ = delegate;
with_controls_ = with_controls;
with_osr_ = with_osr;
start_rect_ = bounds;
with_controls_ = config.with_controls;
with_osr_ = config.with_osr;
with_extension_ = config.with_extension;
start_rect_ = config.bounds;
CreateBrowserWindow(url);
CreateBrowserWindow(config.url);
initialized_ = true;
// Create the native root window on the main thread.
if (CURRENTLY_ON_MAIN_THREAD()) {
CreateRootWindow(settings);
CreateRootWindow(settings, config.initially_hidden);
} else {
MAIN_POST_CLOSURE(
base::Bind(&RootWindowGtk::CreateRootWindow, this, settings));
MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::CreateRootWindow, this,
settings, config.initially_hidden));
}
}
@@ -179,13 +177,7 @@ void RootWindowGtk::SetBounds(int x, int y, size_t width, size_t height) {
else
gtk_window_present(window);
// Retrieve information about the display that contains the window.
GdkScreen* screen = gdk_screen_get_default();
const gint monitor = gdk_screen_get_monitor_at_window(screen, gdk_window);
GdkRectangle rect;
gdk_screen_get_monitor_geometry(screen, monitor, &rect);
gdk_window_move_resize(gdk_window, rect.x, rect.y, rect.width, rect.height);
gdk_window_move_resize(gdk_window, x, y, width, height);
}
void RootWindowGtk::Close(bool force) {
@@ -200,16 +192,18 @@ void RootWindowGtk::Close(bool force) {
void RootWindowGtk::SetDeviceScaleFactor(float device_scale_factor) {
REQUIRE_MAIN_THREAD();
if (browser_window_)
if (browser_window_ && with_osr_)
browser_window_->SetDeviceScaleFactor(device_scale_factor);
}
float RootWindowGtk::GetDeviceScaleFactor() const {
REQUIRE_MAIN_THREAD();
if (browser_window_)
if (browser_window_ && with_osr_)
return browser_window_->GetDeviceScaleFactor();
return 1.0f;
NOTREACHED();
return 0.0f;
}
CefRefPtr<CefBrowser> RootWindowGtk::GetBrowser() const {
@@ -225,6 +219,16 @@ ClientWindowHandle RootWindowGtk::GetWindowHandle() const {
return window_;
}
bool RootWindowGtk::WithWindowlessRendering() const {
REQUIRE_MAIN_THREAD();
return with_osr_;
}
bool RootWindowGtk::WithExtension() const {
REQUIRE_MAIN_THREAD();
return with_extension_;
}
void RootWindowGtk::CreateBrowserWindow(const std::string& startup_url) {
if (with_osr_) {
OsrRenderer::Settings settings = {};
@@ -235,7 +239,8 @@ void RootWindowGtk::CreateBrowserWindow(const std::string& startup_url) {
}
}
void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings) {
void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
bool initially_hidden) {
REQUIRE_MAIN_THREAD();
DCHECK(!window_);
@@ -358,7 +363,9 @@ void RootWindowGtk::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
// For popup browsers create the root window once the browser has been
// created.
if (is_popup_)
CreateRootWindow(CefBrowserSettings());
CreateRootWindow(CefBrowserSettings(), false);
delegate_->OnBrowserCreated(this, browser);
}
void RootWindowGtk::OnBrowserWindowDestroyed() {
@@ -409,6 +416,24 @@ void RootWindowGtk::OnSetFullscreen(bool fullscreen) {
}
}
void RootWindowGtk::OnAutoResize(const CefSize& new_size) {
REQUIRE_MAIN_THREAD();
if (!window_)
return;
GtkWindow* window = GTK_WINDOW(window_);
GdkWindow* gdk_window = gtk_widget_get_window(window_);
// Make sure the window isn't minimized or maximized.
if (IsWindowMaximized(window))
gtk_window_unmaximize(window);
else
gtk_window_present(window);
gdk_window_resize(gdk_window, new_size.width, new_size.height);
}
void RootWindowGtk::OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) {
@@ -440,6 +465,7 @@ gboolean RootWindowGtk::WindowFocusIn(GtkWidget* widget,
RootWindowGtk* self) {
if (event->in && self->browser_window_.get()) {
self->browser_window_->SetFocus(true);
self->delegate_->OnRootWindowActivated(self);
// Return true for a windowed browser so that focus is not passed to GTK.
return self->with_osr_ ? FALSE : TRUE;
}

View File

@@ -26,11 +26,8 @@ class RootWindowGtk : public RootWindow, public BrowserWindow::Delegate {
// RootWindow methods.
void Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& rect,
const CefBrowserSettings& settings,
const std::string& url) OVERRIDE;
const RootWindowConfig& config,
const CefBrowserSettings& settings) OVERRIDE;
void InitAsPopup(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
@@ -46,10 +43,13 @@ class RootWindowGtk : public RootWindow, public BrowserWindow::Delegate {
float GetDeviceScaleFactor() const OVERRIDE;
CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
ClientWindowHandle GetWindowHandle() const OVERRIDE;
bool WithWindowlessRendering() const OVERRIDE;
bool WithExtension() const OVERRIDE;
private:
void CreateBrowserWindow(const std::string& startup_url);
void CreateRootWindow(const CefBrowserSettings& settings);
void CreateRootWindow(const CefBrowserSettings& settings,
bool initially_hidden);
// BrowserWindow::Delegate methods.
void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
@@ -57,6 +57,7 @@ class RootWindowGtk : public RootWindow, public BrowserWindow::Delegate {
void OnSetAddress(const std::string& url) OVERRIDE;
void OnSetTitle(const std::string& title) OVERRIDE;
void OnSetFullscreen(bool fullscreen) OVERRIDE;
void OnAutoResize(const CefSize& new_size) OVERRIDE;
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
@@ -112,9 +113,9 @@ class RootWindowGtk : public RootWindow, public BrowserWindow::Delegate {
// After initialization all members are only accessed on the main thread.
// Members set during initialization.
RootWindow::Delegate* delegate_;
bool with_controls_;
bool with_osr_;
bool with_extension_;
bool is_popup_;
CefRect start_rect_;
scoped_ptr<BrowserWindow> browser_window_;

View File

@@ -35,11 +35,8 @@ class RootWindowMac : public RootWindow, public BrowserWindow::Delegate {
// RootWindow methods.
void Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& rect,
const CefBrowserSettings& settings,
const std::string& url) OVERRIDE;
const RootWindowConfig& config,
const CefBrowserSettings& settings) OVERRIDE;
void InitAsPopup(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
@@ -55,16 +52,20 @@ class RootWindowMac : public RootWindow, public BrowserWindow::Delegate {
float GetDeviceScaleFactor() const OVERRIDE;
CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
ClientWindowHandle GetWindowHandle() const OVERRIDE;
bool WithWindowlessRendering() const OVERRIDE;
bool WithExtension() const OVERRIDE;
// Called by RootWindowDelegate after the associated NSWindow has been
// destroyed.
void WindowDestroyed();
BrowserWindow* browser_window() const { return browser_window_.get(); }
RootWindow::Delegate* delegate() const { return delegate_; }
private:
void CreateBrowserWindow(const std::string& startup_url);
void CreateRootWindow(const CefBrowserSettings& settings);
void CreateRootWindow(const CefBrowserSettings& settings,
bool initially_hidden);
// BrowserWindow::Delegate methods.
void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
@@ -72,6 +73,7 @@ class RootWindowMac : public RootWindow, public BrowserWindow::Delegate {
void OnSetAddress(const std::string& url) OVERRIDE;
void OnSetTitle(const std::string& title) OVERRIDE;
void OnSetFullscreen(bool fullscreen) OVERRIDE;
void OnAutoResize(const CefSize& new_size) OVERRIDE;
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
@@ -82,9 +84,9 @@ class RootWindowMac : public RootWindow, public BrowserWindow::Delegate {
// After initialization all members are only accessed on the main thread.
// Members set during initialization.
RootWindow::Delegate* delegate_;
bool with_controls_;
bool with_osr_;
bool with_extension_;
bool is_popup_;
CefRect start_rect_;
scoped_ptr<BrowserWindow> browser_window_;

View File

@@ -118,6 +118,7 @@
client::BrowserWindow* browser_window = root_window_->browser_window();
if (browser_window)
browser_window->SetFocus(true);
root_window_->delegate()->OnRootWindowActivated(root_window_);
}
// Called when we are deactivated (when we lose focus).
@@ -236,8 +237,7 @@ NSRect GetScreenRectForWindow(NSWindow* window) {
} // namespace
RootWindowMac::RootWindowMac()
: delegate_(NULL),
with_controls_(false),
: with_controls_(false),
with_osr_(false),
is_popup_(false),
initialized_(false),
@@ -259,29 +259,27 @@ RootWindowMac::~RootWindowMac() {
}
void RootWindowMac::Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& bounds,
const CefBrowserSettings& settings,
const std::string& url) {
const RootWindowConfig& config,
const CefBrowserSettings& settings) {
DCHECK(delegate);
DCHECK(!initialized_);
delegate_ = delegate;
with_controls_ = with_controls;
with_osr_ = with_osr;
start_rect_ = bounds;
with_controls_ = config.with_controls;
with_osr_ = config.with_osr;
with_extension_ = config.with_extension;
start_rect_ = config.bounds;
CreateBrowserWindow(url);
CreateBrowserWindow(config.url);
initialized_ = true;
// Create the native root window on the main thread.
if (CURRENTLY_ON_MAIN_THREAD()) {
CreateRootWindow(settings);
CreateRootWindow(settings, config.initially_hidden);
} else {
MAIN_POST_CLOSURE(
base::Bind(&RootWindowMac::CreateRootWindow, this, settings));
MAIN_POST_CLOSURE(base::Bind(&RootWindowMac::CreateRootWindow, this,
settings, config.initially_hidden));
}
}
@@ -402,16 +400,18 @@ void RootWindowMac::Close(bool force) {
void RootWindowMac::SetDeviceScaleFactor(float device_scale_factor) {
REQUIRE_MAIN_THREAD();
if (browser_window_)
if (browser_window_ && with_osr_)
browser_window_->SetDeviceScaleFactor(device_scale_factor);
}
float RootWindowMac::GetDeviceScaleFactor() const {
REQUIRE_MAIN_THREAD();
if (browser_window_)
if (browser_window_ && with_osr_)
return browser_window_->GetDeviceScaleFactor();
return 1.0f;
NOTREACHED();
return 0.0f;
}
CefRefPtr<CefBrowser> RootWindowMac::GetBrowser() const {
@@ -427,6 +427,16 @@ ClientWindowHandle RootWindowMac::GetWindowHandle() const {
return [window_ contentView];
}
bool RootWindowMac::WithWindowlessRendering() const {
REQUIRE_MAIN_THREAD();
return with_osr_;
}
bool RootWindowMac::WithExtension() const {
REQUIRE_MAIN_THREAD();
return with_extension_;
}
void RootWindowMac::WindowDestroyed() {
window_ = nil;
window_destroyed_ = true;
@@ -443,7 +453,8 @@ void RootWindowMac::CreateBrowserWindow(const std::string& startup_url) {
}
}
void RootWindowMac::CreateRootWindow(const CefBrowserSettings& settings) {
void RootWindowMac::CreateRootWindow(const CefBrowserSettings& settings,
bool initially_hidden) {
REQUIRE_MAIN_THREAD();
DCHECK(!window_);
@@ -566,11 +577,13 @@ void RootWindowMac::CreateRootWindow(const CefBrowserSettings& settings) {
contentBounds.size.height);
}
// Show the window.
Show(ShowNormal);
if (!initially_hidden) {
// Show the window.
Show(ShowNormal);
// Size the window.
SetBounds(x, y, width, height);
// Size the window.
SetBounds(x, y, width, height);
}
}
void RootWindowMac::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
@@ -579,7 +592,9 @@ void RootWindowMac::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
// For popup browsers create the root window once the browser has been
// created.
if (is_popup_)
CreateRootWindow(CefBrowserSettings());
CreateRootWindow(CefBrowserSettings(), false);
delegate_->OnBrowserCreated(this, browser);
}
void RootWindowMac::OnBrowserWindowDestroyed() {
@@ -638,6 +653,29 @@ void RootWindowMac::OnSetFullscreen(bool fullscreen) {
}
}
void RootWindowMac::OnAutoResize(const CefSize& new_size) {
REQUIRE_MAIN_THREAD();
if (!window_)
return;
// Desired content rectangle.
NSRect content_rect;
content_rect.size.width = static_cast<int>(new_size.width);
content_rect.size.height =
static_cast<int>(new_size.height) + (with_controls_ ? URLBAR_HEIGHT : 0);
// Convert to a frame rectangle.
NSRect frame_rect = [window_ frameRectForContentRect:content_rect];
// Don't change the origin.
frame_rect.origin = window_.frame.origin;
[window_ setFrame:frame_rect display:YES];
// Make sure the window is visible.
Show(ShowNormal);
}
void RootWindowMac::OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) {

View File

@@ -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

View File

@@ -10,6 +10,7 @@
#include "include/base/cef_scoped_ptr.h"
#include "include/cef_command_line.h"
#include "tests/cefclient/browser/image_cache.h"
#include "tests/cefclient/browser/root_window.h"
#include "tests/cefclient/browser/temp_window.h"
@@ -23,15 +24,9 @@ class RootWindowManager : public RootWindow::Delegate {
// after all windows have closed.
explicit RootWindowManager(bool terminate_when_all_windows_closed);
// Create a new top-level native window that loads |url|.
// If |with_controls| is true the window will show controls.
// If |with_osr| is true the window will use off-screen rendering.
// If |bounds| is empty the default window size and location will be used.
// This method can be called from anywhere to create a new top-level window.
scoped_refptr<RootWindow> CreateRootWindow(bool with_controls,
bool with_osr,
const CefRect& bounds,
const std::string& url);
// Create a new top-level native window. This method can be called from
// anywhere.
scoped_refptr<RootWindow> CreateRootWindow(const RootWindowConfig& config);
// Create a new native popup window.
// If |with_controls| is true the window will show controls.
@@ -46,14 +41,46 @@ class RootWindowManager : public RootWindow::Delegate {
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings);
// Create a new top-level native window to host |extension|.
// If |with_controls| is true the window will show controls.
// If |with_osr| is true the window will use off-screen rendering.
// This method can be called from anywhere.
scoped_refptr<RootWindow> CreateRootWindowAsExtension(
CefRefPtr<CefExtension> extension,
const CefRect& source_bounds,
CefRefPtr<CefWindow> parent_window,
const base::Closure& close_callback,
bool with_controls,
bool with_osr);
// Returns true if a window hosting |extension| currently exists. Must be
// called on the main thread.
bool HasRootWindowAsExtension(CefRefPtr<CefExtension> extension);
// Returns the RootWindow associated with the specified browser ID. Must be
// called on the main thread.
scoped_refptr<RootWindow> GetWindowForBrowser(int browser_id);
scoped_refptr<RootWindow> GetWindowForBrowser(int browser_id) const;
// Returns the currently active/foreground RootWindow. May return NULL. Must
// be called on the main thread.
scoped_refptr<RootWindow> GetActiveRootWindow() const;
// Returns the currently active/foreground browser. May return NULL. Safe to
// call from any thread.
CefRefPtr<CefBrowser> GetActiveBrowser() const;
// Close all existing windows. If |force| is true onunload handlers will not
// be executed.
void CloseAllWindows(bool force);
// Manage the set of loaded extensions. RootWindows will be notified via the
// OnExtensionsChanged method.
void AddExtension(CefRefPtr<CefExtension> extension);
bool request_context_per_browser() const {
return request_context_per_browser_;
}
private:
// Allow deletion via scoped_ptr only.
friend struct base::DefaultDeleter<RootWindowManager>;
@@ -61,14 +88,23 @@ class RootWindowManager : public RootWindow::Delegate {
~RootWindowManager();
void OnRootWindowCreated(scoped_refptr<RootWindow> root_window);
void NotifyExtensionsChanged();
// RootWindow::Delegate methods.
CefRefPtr<CefRequestContext> GetRequestContext(
RootWindow* root_window) OVERRIDE;
CefRefPtr<CefImage> GetDefaultWindowIcon() OVERRIDE;
scoped_refptr<ImageCache> GetImageCache() OVERRIDE;
void OnTest(RootWindow* root_window, int test_id) OVERRIDE;
void OnExit(RootWindow* root_window) OVERRIDE;
void OnRootWindowDestroyed(RootWindow* root_window) OVERRIDE;
void OnRootWindowActivated(RootWindow* root_window) OVERRIDE;
void OnBrowserCreated(RootWindow* root_window,
CefRefPtr<CefBrowser> browser) OVERRIDE;
void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
const CefRect& source_bounds,
CefRefPtr<CefWindow> parent_window,
const base::Closure& close_callback,
bool with_osr) OVERRIDE;
const bool terminate_when_all_windows_closed_;
bool request_context_per_browser_;
@@ -78,11 +114,24 @@ class RootWindowManager : public RootWindow::Delegate {
typedef std::set<scoped_refptr<RootWindow>> RootWindowSet;
RootWindowSet root_windows_;
// The currently active/foreground RootWindow. Only accessed on the main
// thread.
scoped_refptr<RootWindow> active_root_window_;
// The currently active/foreground browser. Access is protected by
// |active_browser_lock_;
mutable base::Lock active_browser_lock_;
CefRefPtr<CefBrowser> active_browser_;
// Singleton window used as the temporary parent for popup browsers.
TempWindow temp_window_;
CefRefPtr<CefRequestContext> shared_request_context_;
CefRefPtr<CefImage> default_window_icon_;
// Loaded extensions. Only accessed on the main thread.
ExtensionSet extensions_;
scoped_refptr<ImageCache> image_cache_;
DISALLOW_COPY_AND_ASSIGN(RootWindowManager);
};

View File

@@ -12,10 +12,19 @@
namespace client {
namespace {
// Images that are loaded early and cached.
static const char* kDefaultImageCache[] = {"menu_icon", "window_icon"};
} // namespace
RootWindowViews::RootWindowViews()
: delegate_(NULL),
with_controls_(false),
: with_controls_(false),
with_extension_(false),
initially_hidden_(false),
is_popup_(false),
position_on_resize_(false),
initialized_(false),
window_destroyed_(false),
browser_destroyed_(false) {}
@@ -25,23 +34,31 @@ RootWindowViews::~RootWindowViews() {
}
void RootWindowViews::Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& bounds,
const CefBrowserSettings& settings,
const std::string& url) {
const RootWindowConfig& config,
const CefBrowserSettings& settings) {
DCHECK(delegate);
DCHECK(!with_osr); // Windowless rendering is not supported.
DCHECK(!config.with_osr); // Windowless rendering is not supported.
DCHECK(!initialized_);
delegate_ = delegate;
with_controls_ = with_controls;
initial_bounds_ = bounds;
CreateClientHandler(url);
with_controls_ = config.with_controls;
with_extension_ = config.with_extension;
initially_hidden_ = config.initially_hidden;
if (initially_hidden_ && !config.source_bounds.IsEmpty()) {
// The window will be sized and positioned in OnAutoResize().
initial_bounds_ = config.source_bounds;
position_on_resize_ = true;
} else {
initial_bounds_ = config.bounds;
}
parent_window_ = config.parent_window;
close_callback_ = config.close_callback;
CreateClientHandler(config.url);
initialized_ = true;
// Continue initialization on the main thread.
InitOnMainThread(settings, url);
InitOnMainThread(settings, config.url);
}
void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate,
@@ -167,20 +184,66 @@ ClientWindowHandle RootWindowViews::GetWindowHandle() const {
#endif
}
bool RootWindowViews::WithExtension() const {
REQUIRE_MAIN_THREAD();
return with_extension_;
}
bool RootWindowViews::WithControls() {
CEF_REQUIRE_UI_THREAD();
return with_controls_;
}
bool RootWindowViews::WithExtension() {
REQUIRE_MAIN_THREAD();
return with_extension_;
}
void RootWindowViews::OnExtensionsChanged(const ExtensionSet& extensions) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute this method on the UI thread.
CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnExtensionsChanged, this,
extensions));
return;
}
if (window_) {
window_->OnExtensionsChanged(extensions);
} else {
// Window may not exist yet for popups.
pending_extensions_ = extensions;
}
}
bool RootWindowViews::InitiallyHidden() {
CEF_REQUIRE_UI_THREAD();
return initially_hidden_;
}
CefRefPtr<CefWindow> RootWindowViews::GetParentWindow() {
CEF_REQUIRE_UI_THREAD();
return parent_window_;
}
CefRect RootWindowViews::GetWindowBounds() {
CEF_REQUIRE_UI_THREAD();
return initial_bounds_;
}
scoped_refptr<ImageCache> RootWindowViews::GetImageCache() {
CEF_REQUIRE_UI_THREAD();
return image_cache_;
}
void RootWindowViews::OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) {
CEF_REQUIRE_UI_THREAD();
DCHECK(!window_);
window_ = window;
if (!pending_extensions_.empty()) {
window_->OnExtensionsChanged(pending_extensions_);
pending_extensions_.clear();
}
}
void RootWindowViews::OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) {
@@ -192,6 +255,14 @@ void RootWindowViews::OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) {
base::Bind(&RootWindowViews::NotifyViewsWindowDestroyed, this));
}
void RootWindowViews::OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) {
CEF_REQUIRE_UI_THREAD();
// Continue on the main thread.
MAIN_POST_CLOSURE(
base::Bind(&RootWindowViews::NotifyViewsWindowActivated, this));
}
ViewsWindow::Delegate* RootWindowViews::GetDelegateForPopup(
CefRefPtr<CefClient> client) {
CEF_REQUIRE_UI_THREAD();
@@ -199,9 +270,30 @@ ViewsWindow::Delegate* RootWindowViews::GetDelegateForPopup(
ClientHandlerStd* handler = static_cast<ClientHandlerStd*>(client.get());
RootWindowViews* root_window =
static_cast<RootWindowViews*>(handler->delegate());
// Transfer some state to the child RootWindowViews.
root_window->image_cache_ = image_cache_;
return root_window;
}
void RootWindowViews::CreateExtensionWindow(
CefRefPtr<CefExtension> extension,
const CefRect& source_bounds,
CefRefPtr<CefWindow> parent_window,
const base::Closure& close_callback) {
if (!CURRENTLY_ON_MAIN_THREAD()) {
// Execute this method on the main thread.
MAIN_POST_CLOSURE(base::Bind(&RootWindowViews::CreateExtensionWindow, this,
extension, source_bounds, parent_window,
close_callback));
return;
}
delegate_->CreateExtensionWindow(extension, source_bounds, parent_window,
close_callback, false);
}
void RootWindowViews::OnTest(int test_id) {
if (!CURRENTLY_ON_MAIN_THREAD()) {
// Execute this method on the main thread.
@@ -226,6 +318,7 @@ void RootWindowViews::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
REQUIRE_MAIN_THREAD();
DCHECK(!browser_);
browser_ = browser;
delegate_->OnBrowserCreated(this, browser);
}
void RootWindowViews::OnBrowserClosing(CefRefPtr<CefBrowser> browser) {
@@ -293,6 +386,33 @@ void RootWindowViews::OnSetFullscreen(bool fullscreen) {
window_->SetFullscreen(fullscreen);
}
void RootWindowViews::OnAutoResize(const CefSize& new_size) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute this method on the UI thread.
CefPostTask(TID_UI,
base::Bind(&RootWindowViews::OnAutoResize, this, new_size));
return;
}
bool has_position = false;
CefPoint position;
if (position_on_resize_) {
// Position the window centered on and immediately below the source.
const int x_offset = (initial_bounds_.width - new_size.width) / 2;
position.Set(initial_bounds_.x + x_offset,
initial_bounds_.y + initial_bounds_.height);
has_position = true;
// Don't change the window position on future resizes.
position_on_resize_ = false;
}
if (window_) {
window_->SetBrowserSize(new_size, has_position, position);
window_->Show();
}
}
void RootWindowViews::OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) {
@@ -309,7 +429,8 @@ void RootWindowViews::OnSetLoadingState(bool isLoading,
if (isLoading) {
// Reset to the default window icon when loading begins.
window_->SetFavicon(delegate_->GetDefaultWindowIcon());
window_->SetFavicon(
delegate_->GetImageCache()->GetCachedImage("window_icon"));
}
}
}
@@ -360,22 +481,34 @@ void RootWindowViews::InitOnMainThread(const CefBrowserSettings& settings,
return;
}
CreateViewsWindow(settings, startup_url, delegate_->GetRequestContext(this));
image_cache_ = delegate_->GetImageCache();
// Populate the default image cache.
ImageCache::ImageInfoSet image_set;
for (size_t i = 0U; i < arraysize(kDefaultImageCache); ++i)
image_set.push_back(ImageCache::ImageInfo::Create2x(kDefaultImageCache[i]));
image_cache_->LoadImages(
image_set, base::Bind(&RootWindowViews::CreateViewsWindow, this, settings,
startup_url, delegate_->GetRequestContext(this)));
}
void RootWindowViews::CreateViewsWindow(
const CefBrowserSettings& settings,
const std::string& startup_url,
CefRefPtr<CefRequestContext> request_context) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute this method on the UI thread.
CefPostTask(TID_UI, base::Bind(&RootWindowViews::CreateViewsWindow, this,
settings, startup_url, request_context));
return;
}
CefRefPtr<CefRequestContext> request_context,
const ImageCache::ImageSet& images) {
CEF_REQUIRE_UI_THREAD();
DCHECK(!window_);
#ifndef NDEBUG
// Make sure the default images loaded successfully.
DCHECK_EQ(images.size(), arraysize(kDefaultImageCache));
for (size_t i = 0U; i < arraysize(kDefaultImageCache); ++i) {
DCHECK(images[i]) << "Default image " << i << " failed to load";
}
#endif
// Create the ViewsWindow. It will show itself after creation.
ViewsWindow::Create(this, client_handler_, startup_url, settings,
request_context);
@@ -387,10 +520,18 @@ void RootWindowViews::NotifyViewsWindowDestroyed() {
NotifyDestroyedIfDone();
}
void RootWindowViews::NotifyViewsWindowActivated() {
REQUIRE_MAIN_THREAD();
delegate_->OnRootWindowActivated(this);
}
void RootWindowViews::NotifyDestroyedIfDone() {
// Notify once both the window and the browser have been destroyed.
if (window_destroyed_ && browser_destroyed_)
if (window_destroyed_ && browser_destroyed_) {
delegate_->OnRootWindowDestroyed(this);
if (!close_callback_.is_null())
close_callback_.Run();
}
}
} // namespace client

View File

@@ -28,11 +28,8 @@ class RootWindowViews : public RootWindow,
// RootWindow methods:
void Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& rect,
const CefBrowserSettings& settings,
const std::string& url) OVERRIDE;
const RootWindowConfig& config,
const CefBrowserSettings& settings) OVERRIDE;
void InitAsPopup(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
@@ -48,16 +45,28 @@ class RootWindowViews : public RootWindow,
float GetDeviceScaleFactor() const OVERRIDE;
CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
ClientWindowHandle GetWindowHandle() const OVERRIDE;
bool WithWindowlessRendering() const OVERRIDE { return false; }
bool WithExtension() const OVERRIDE;
void OnExtensionsChanged(const ExtensionSet& extensions) OVERRIDE;
// ViewsWindow::Delegate methods:
bool WithControls() OVERRIDE;
bool WithExtension() OVERRIDE;
bool InitiallyHidden() OVERRIDE;
CefRefPtr<CefWindow> GetParentWindow() OVERRIDE;
CefRect GetWindowBounds() OVERRIDE;
scoped_refptr<ImageCache> GetImageCache() OVERRIDE;
void OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) OVERRIDE;
void OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) OVERRIDE;
void OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) OVERRIDE;
ViewsWindow::Delegate* GetDelegateForPopup(
CefRefPtr<CefClient> client) OVERRIDE;
virtual void OnTest(int test_id) OVERRIDE;
virtual void OnExit() OVERRIDE;
void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
const CefRect& source_bounds,
CefRefPtr<CefWindow> parent_window,
const base::Closure& close_callback) OVERRIDE;
void OnTest(int test_id) OVERRIDE;
void OnExit() OVERRIDE;
protected:
// ClientHandler::Delegate methods:
@@ -68,6 +77,7 @@ class RootWindowViews : public RootWindow,
void OnSetTitle(const std::string& title) OVERRIDE;
void OnSetFavicon(CefRefPtr<CefImage> image) OVERRIDE;
void OnSetFullscreen(bool fullscreen) OVERRIDE;
void OnAutoResize(const CefSize& new_size) OVERRIDE;
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
@@ -83,18 +93,24 @@ class RootWindowViews : public RootWindow,
const std::string& startup_url);
void CreateViewsWindow(const CefBrowserSettings& settings,
const std::string& startup_url,
CefRefPtr<CefRequestContext> request_context);
CefRefPtr<CefRequestContext> request_context,
const ImageCache::ImageSet& images);
void NotifyViewsWindowDestroyed();
void NotifyViewsWindowActivated();
void NotifyDestroyedIfDone();
// After initialization all members are only accessed on the main thread
// unless otherwise indicated.
// Members set during initialization.
RootWindow::Delegate* delegate_;
bool with_controls_;
bool with_extension_;
bool initially_hidden_;
CefRefPtr<CefWindow> parent_window_;
bool is_popup_;
CefRect initial_bounds_;
base::Closure close_callback_;
bool position_on_resize_;
CefRefPtr<ClientHandler> client_handler_;
bool initialized_;
@@ -105,6 +121,8 @@ class RootWindowViews : public RootWindow,
// Only accessed on the browser process UI thread.
CefRefPtr<ViewsWindow> window_;
ExtensionSet pending_extensions_;
scoped_refptr<ImageCache> image_cache_;
DISALLOW_COPY_AND_ASSIGN(RootWindowViews);
};

View File

@@ -51,7 +51,8 @@ int GetButtonWidth() {
static bool initialized = false;
if (!initialized) {
button_width = LogicalToDevice(BUTTON_WIDTH, GetDeviceScaleFactor());
button_width =
LogicalToDevice(BUTTON_WIDTH, client::GetDeviceScaleFactor());
initialized = true;
}
@@ -63,7 +64,8 @@ int GetURLBarHeight() {
static bool initialized = false;
if (!initialized) {
urlbar_height = LogicalToDevice(URLBAR_HEIGHT, GetDeviceScaleFactor());
urlbar_height =
LogicalToDevice(URLBAR_HEIGHT, client::GetDeviceScaleFactor());
initialized = true;
}
@@ -73,9 +75,9 @@ int GetURLBarHeight() {
} // namespace
RootWindowWin::RootWindowWin()
: delegate_(NULL),
with_controls_(false),
: with_controls_(false),
with_osr_(false),
with_extension_(false),
is_popup_(false),
start_rect_(),
initialized_(false),
@@ -114,33 +116,31 @@ RootWindowWin::~RootWindowWin() {
}
void RootWindowWin::Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& bounds,
const CefBrowserSettings& settings,
const std::string& url) {
const RootWindowConfig& config,
const CefBrowserSettings& settings) {
DCHECK(delegate);
DCHECK(!initialized_);
delegate_ = delegate;
with_controls_ = with_controls;
with_osr_ = with_osr;
with_controls_ = config.with_controls;
with_osr_ = config.with_osr;
with_extension_ = config.with_extension;
start_rect_.left = bounds.x;
start_rect_.top = bounds.y;
start_rect_.right = bounds.x + bounds.width;
start_rect_.bottom = bounds.y + bounds.height;
start_rect_.left = config.bounds.x;
start_rect_.top = config.bounds.y;
start_rect_.right = config.bounds.x + config.bounds.width;
start_rect_.bottom = config.bounds.y + config.bounds.height;
CreateBrowserWindow(url);
CreateBrowserWindow(config.url);
initialized_ = true;
// Create the native root window on the main thread.
if (CURRENTLY_ON_MAIN_THREAD()) {
CreateRootWindow(settings);
CreateRootWindow(settings, config.initially_hidden);
} else {
MAIN_POST_CLOSURE(
base::Bind(&RootWindowWin::CreateRootWindow, this, settings));
MAIN_POST_CLOSURE(base::Bind(&RootWindowWin::CreateRootWindow, this,
settings, config.initially_hidden));
}
}
@@ -233,16 +233,18 @@ void RootWindowWin::Close(bool force) {
void RootWindowWin::SetDeviceScaleFactor(float device_scale_factor) {
REQUIRE_MAIN_THREAD();
if (browser_window_)
if (browser_window_ && with_osr_)
browser_window_->SetDeviceScaleFactor(device_scale_factor);
}
float RootWindowWin::GetDeviceScaleFactor() const {
REQUIRE_MAIN_THREAD();
if (browser_window_)
if (browser_window_ && with_osr_)
return browser_window_->GetDeviceScaleFactor();
return client::GetDeviceScaleFactor();
NOTREACHED();
return 0.0f;
}
CefRefPtr<CefBrowser> RootWindowWin::GetBrowser() const {
@@ -258,6 +260,16 @@ ClientWindowHandle RootWindowWin::GetWindowHandle() const {
return hwnd_;
}
bool RootWindowWin::WithWindowlessRendering() const {
REQUIRE_MAIN_THREAD();
return with_osr_;
}
bool RootWindowWin::WithExtension() const {
REQUIRE_MAIN_THREAD();
return with_extension_;
}
void RootWindowWin::CreateBrowserWindow(const std::string& startup_url) {
if (with_osr_) {
OsrRenderer::Settings settings = {};
@@ -268,7 +280,8 @@ void RootWindowWin::CreateBrowserWindow(const std::string& startup_url) {
}
}
void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings) {
void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
bool initially_hidden) {
REQUIRE_MAIN_THREAD();
DCHECK(!hwnd_);
@@ -415,8 +428,10 @@ void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings) {
rect.right - rect.left, rect.bottom - rect.top);
}
// Show this window.
Show(ShowNormal);
if (!initially_hidden) {
// Show this window.
Show(ShowNormal);
}
}
// static
@@ -556,6 +571,11 @@ LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd,
self->OnPaint();
return 0;
case WM_ACTIVATE:
self->OnActivate(LOWORD(wParam) != WA_INACTIVE);
// Allow DefWindowProc to set keyboard focus.
break;
case WM_SETFOCUS:
self->OnFocus();
return 0;
@@ -631,6 +651,11 @@ void RootWindowWin::OnFocus() {
browser_window_->SetFocus(true);
}
void RootWindowWin::OnActivate(bool active) {
if (active)
delegate_->OnRootWindowActivated(this);
}
void RootWindowWin::OnSize(bool minimized) {
if (minimized) {
// Notify the browser window that it was hidden and do nothing further.
@@ -825,11 +850,13 @@ void RootWindowWin::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
if (is_popup_) {
// For popup browsers create the root window once the browser has been
// created.
CreateRootWindow(CefBrowserSettings());
CreateRootWindow(CefBrowserSettings(), false);
} else {
// Make sure the browser is sized correctly.
OnSize(false);
}
delegate_->OnBrowserCreated(this, browser);
}
void RootWindowWin::OnBrowserWindowDestroyed() {
@@ -876,6 +903,36 @@ void RootWindowWin::OnSetFullscreen(bool fullscreen) {
}
}
void RootWindowWin::OnAutoResize(const CefSize& new_size) {
REQUIRE_MAIN_THREAD();
if (!hwnd_)
return;
int new_width = new_size.width;
// Make the window wide enough to drag by the top menu bar.
if (new_width < 200)
new_width = 200;
RECT rect = {
0, 0, LogicalToDevice(new_width, client::GetDeviceScaleFactor()),
LogicalToDevice(new_size.height, client::GetDeviceScaleFactor())};
DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
DWORD ex_style = GetWindowLong(hwnd_, GWL_EXSTYLE);
bool has_menu = !(style & WS_CHILD) && (GetMenu(hwnd_) != NULL);
// The size value is for the client area. Calculate the whole window size
// based on the current style.
AdjustWindowRectEx(&rect, style, has_menu, ex_style);
// Size the window. The left/top values may be negative.
// Also show the window if it's not currently visible.
SetWindowPos(hwnd_, NULL, 0, 0, rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
void RootWindowWin::OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) {

View File

@@ -29,11 +29,8 @@ class RootWindowWin : public RootWindow, public BrowserWindow::Delegate {
// RootWindow methods.
void Init(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
const CefRect& rect,
const CefBrowserSettings& settings,
const std::string& url) OVERRIDE;
const RootWindowConfig& config,
const CefBrowserSettings& settings) OVERRIDE;
void InitAsPopup(RootWindow::Delegate* delegate,
bool with_controls,
bool with_osr,
@@ -49,10 +46,13 @@ class RootWindowWin : public RootWindow, public BrowserWindow::Delegate {
float GetDeviceScaleFactor() const OVERRIDE;
CefRefPtr<CefBrowser> GetBrowser() const OVERRIDE;
ClientWindowHandle GetWindowHandle() const OVERRIDE;
bool WithWindowlessRendering() const OVERRIDE;
bool WithExtension() const OVERRIDE;
private:
void CreateBrowserWindow(const std::string& startup_url);
void CreateRootWindow(const CefBrowserSettings& settings);
void CreateRootWindow(const CefBrowserSettings& settings,
bool initially_hidden);
// Register the root window class.
static void RegisterRootClass(HINSTANCE hInstance,
@@ -80,6 +80,7 @@ class RootWindowWin : public RootWindow, public BrowserWindow::Delegate {
// Event handlers.
void OnPaint();
void OnFocus();
void OnActivate(bool active);
void OnSize(bool minimized);
void OnMove();
bool OnEraseBkgnd();
@@ -96,6 +97,7 @@ class RootWindowWin : public RootWindow, public BrowserWindow::Delegate {
void OnSetAddress(const std::string& url) OVERRIDE;
void OnSetTitle(const std::string& title) OVERRIDE;
void OnSetFullscreen(bool fullscreen) OVERRIDE;
void OnAutoResize(const CefSize& new_size) OVERRIDE;
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
@@ -106,9 +108,9 @@ class RootWindowWin : public RootWindow, public BrowserWindow::Delegate {
// After initialization all members are only accessed on the main thread.
// Members set during initialization.
RootWindow::Delegate* delegate_;
bool with_controls_;
bool with_osr_;
bool with_extension_;
bool is_popup_;
RECT start_rect_;
scoped_ptr<BrowserWindow> browser_window_;

View File

@@ -119,11 +119,10 @@ void RunRequestTest(CefRefPtr<CefBrowser> browser) {
}
void RunNewWindowTest(CefRefPtr<CefBrowser> browser) {
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(
true, // Show controls.
browser->GetHost()->IsWindowRenderingDisabled(),
CefRect(), // Use default system size.
std::string()); // Use default URL.
RootWindowConfig config;
config.with_controls = true;
config.with_osr = browser->GetHost()->IsWindowRenderingDisabled();
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(config);
}
void RunPopupWindowTest(CefRefPtr<CefBrowser> browser) {
@@ -680,8 +679,9 @@ void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager) {
// Add provider for bundled resource files.
#if defined(OS_WIN)
// Read resources from the binary.
resource_manager->AddProvider(CreateBinaryResourceProvider(test_origin), 100,
std::string());
resource_manager->AddProvider(
CreateBinaryResourceProvider(test_origin, std::string()), 100,
std::string());
#elif defined(OS_POSIX)
// Read resources from a directory on disk.
std::string resource_dir;
@@ -693,6 +693,14 @@ void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager) {
}
void Alert(CefRefPtr<CefBrowser> browser, const std::string& message) {
if (browser->GetHost()->GetExtension()) {
// Alerts originating from extension hosts should instead be displayed in
// the active browser.
browser = MainContext::Get()->GetRootWindowManager()->GetActiveBrowser();
if (!browser)
return;
}
// Escape special characters in the message.
std::string msg = StringReplace(message, "\\", "\\\\");
msg = StringReplace(msg, "'", "\\'");

View File

@@ -147,8 +147,10 @@ void ViewsMenuBar::Reset() {
id_next_ = id_start_;
}
void ViewsMenuBar::OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point) {
void ViewsMenuBar::OnMenuButtonPressed(
CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point,
CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) {
CefRefPtr<CefMenuModel> menu_model = GetMenuModel(menu_button->GetID());
// Adjust menu position left by button width.

View File

@@ -73,8 +73,10 @@ class ViewsMenuBar : public CefMenuButtonDelegate, public CefMenuModelDelegate {
void OnButtonPressed(CefRefPtr<CefButton> button) OVERRIDE {}
// CefMenuButtonDelegate methods:
void OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point) OVERRIDE;
void OnMenuButtonPressed(
CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point,
CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) OVERRIDE;
// CefMenuModelDelegate methods:
void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,

View File

@@ -13,10 +13,11 @@
#include "include/wrapper/cef_helpers.h"
#include "tests/cefclient/browser/resource.h"
#include "tests/cefclient/browser/views_style.h"
#include "tests/shared/browser/resource_util.h"
#include "tests/shared/browser/extension_util.h"
#include "tests/shared/common/client_switches.h"
#if !defined(OS_WIN)
#define VK_ESCAPE 0x1B
#define VK_RETURN 0x0D
#define VK_MENU 0x12 // ALT key.
#endif
@@ -25,6 +26,8 @@ namespace client {
namespace {
const char kDefaultExtensionIcon[] = "window_icon";
// Control IDs for Views in the top-level Window.
enum ControlIds {
ID_WINDOW = 1,
@@ -39,6 +42,10 @@ enum ControlIds {
// Reserved range of top menu button IDs.
ID_TOP_MENU_FIRST,
ID_TOP_MENU_LAST = ID_TOP_MENU_FIRST + 10,
// Reserved range of extension button IDs.
ID_EXTENSION_BUTTON_FIRST,
ID_EXTENSION_BUTTON_LAST = ID_EXTENSION_BUTTON_FIRST + 10,
};
typedef std::vector<CefRefPtr<CefLabelButton>> LabelButtons;
@@ -122,6 +129,10 @@ void ViewsWindow::Show() {
CEF_REQUIRE_UI_THREAD();
if (window_)
window_->Show();
if (browser_view_) {
// Give keyboard focus to the BrowserView.
browser_view_->RequestFocus();
}
}
void ViewsWindow::Hide() {
@@ -148,6 +159,19 @@ void ViewsWindow::SetBounds(const CefRect& bounds) {
window_->SetBounds(bounds);
}
void ViewsWindow::SetBrowserSize(const CefSize& size,
bool has_position,
const CefPoint& position) {
CEF_REQUIRE_UI_THREAD();
if (browser_view_)
browser_view_->SetSize(size);
if (window_) {
window_->SizeToPreferredSize();
if (has_position)
window_->SetPosition(position);
}
}
void ViewsWindow::Close(bool force) {
CEF_REQUIRE_UI_THREAD();
if (!browser_view_)
@@ -251,21 +275,75 @@ void ViewsWindow::OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) {
views_style::ApplyTo(model);
}
bool ViewsWindow::OnPopupBrowserViewCreated(
void ViewsWindow::OnExtensionsChanged(const ExtensionSet& extensions) {
CEF_REQUIRE_UI_THREAD();
if (extensions.empty()) {
if (!extensions_.empty()) {
extensions_.clear();
UpdateExtensionControls();
}
return;
}
ImageCache::ImageInfoSet image_set;
ExtensionSet::const_iterator it = extensions.begin();
for (; it != extensions.end(); ++it) {
CefRefPtr<CefExtension> extension = *it;
bool internal = false;
const std::string& icon_path =
extension_util::GetExtensionIconPath(extension, &internal);
if (!icon_path.empty()) {
// Load the extension icon.
image_set.push_back(
ImageCache::ImageInfo::Create1x(icon_path, icon_path, internal));
} else {
// Get a NULL image and use the default icon.
image_set.push_back(ImageCache::ImageInfo::Empty());
}
}
delegate_->GetImageCache()->LoadImages(
image_set,
base::Bind(&ViewsWindow::OnExtensionIconsLoaded, this, extensions));
}
CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
bool is_devtools) {
CEF_REQUIRE_UI_THREAD();
// The popup browser client is created in CefLifeSpanHandler::OnBeforePopup()
// (e.g. via RootWindowViews::InitAsPopup()). The Delegate (RootWindowViews)
// knows the association between |client| and itself.
Delegate* popup_delegate = delegate_->GetDelegateForPopup(
popup_browser_view->GetBrowser()->GetHost()->GetClient());
Delegate* popup_delegate = delegate_->GetDelegateForPopup(client);
// Should not be the same RootWindowViews that owns |this|.
DCHECK(popup_delegate && popup_delegate != delegate_);
// Create a new ViewsWindow for the popup BrowserView.
return new ViewsWindow(popup_delegate, NULL);
}
bool ViewsWindow::OnPopupBrowserViewCreated(
CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
bool is_devtools) {
CEF_REQUIRE_UI_THREAD();
// Retrieve the ViewsWindow created in GetDelegateForPopupBrowserView.
CefRefPtr<ViewsWindow> popup_window =
new ViewsWindow(popup_delegate, popup_browser_view);
static_cast<ViewsWindow*>(static_cast<CefBrowserViewDelegate*>(
popup_browser_view->GetDelegate().get()));
// Should not be the same ViewsWindow as |this|.
DCHECK(popup_window && popup_window != this);
// Associate the ViewsWindow with the new popup browser.
popup_window->SetBrowserView(popup_browser_view);
// Create a new top-level Window for the popup. It will show itself after
// creation.
@@ -307,11 +385,32 @@ void ViewsWindow::OnButtonPressed(CefRefPtr<CefButton> button) {
}
}
void ViewsWindow::OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point) {
void ViewsWindow::OnMenuButtonPressed(
CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point,
CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) {
CEF_REQUIRE_UI_THREAD();
const int id = menu_button->GetID();
if (id >= ID_EXTENSION_BUTTON_FIRST && id <= ID_EXTENSION_BUTTON_LAST) {
const size_t extension_idx = id - ID_EXTENSION_BUTTON_FIRST;
if (extension_idx >= extensions_.size()) {
LOG(ERROR) << "Invalid extension index " << extension_idx;
return;
}
// Keep the button pressed until the extension window is closed.
extension_button_pressed_lock_ = button_pressed_lock;
// Create a window for the extension.
delegate_->CreateExtensionWindow(
extensions_[extension_idx].extension_, menu_button->GetBoundsInScreen(),
window_, base::Bind(&ViewsWindow::OnExtensionWindowClosed, this));
return;
}
DCHECK(with_controls_);
DCHECK_EQ(ID_MENU_BUTTON, menu_button->GetID());
DCHECK_EQ(ID_MENU_BUTTON, id);
menu_button->ShowMenu(button_menu_model_, screen_point,
CEF_MENU_ANCHOR_TOPRIGHT);
@@ -399,15 +498,16 @@ void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
// Add the BrowserView as the only child of the Window.
window_->AddChildView(browser_view_);
// Choose a reasonable minimum window size.
minimum_window_size_ = CefSize(100, 100);
if (!delegate_->WithExtension()) {
// Choose a reasonable minimum window size.
minimum_window_size_ = CefSize(100, 100);
}
}
// Show the Window.
window_->Show();
// Give keyboard focus to the BrowserView.
browser_view_->RequestFocus();
if (!delegate_->InitiallyHidden()) {
// Show the Window.
Show();
}
}
void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
@@ -422,6 +522,7 @@ void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
top_menu_bar_->Reset();
top_menu_bar_ = NULL;
}
extensions_panel_ = NULL;
window_ = NULL;
}
@@ -435,11 +536,32 @@ bool ViewsWindow::CanClose(CefRefPtr<CefWindow> window) {
return true;
}
CefRefPtr<CefWindow> ViewsWindow::GetParentWindow(CefRefPtr<CefWindow> window,
bool* is_menu,
bool* can_activate_menu) {
CEF_REQUIRE_UI_THREAD();
CefRefPtr<CefWindow> parent_window = delegate_->GetParentWindow();
if (parent_window) {
// Should be an extension window, in which case we want it to behave as a
// menu and allow activation.
DCHECK(delegate_->WithExtension());
*is_menu = true;
*can_activate_menu = true;
}
return parent_window;
}
bool ViewsWindow::IsFrameless(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
return frameless_;
}
bool ViewsWindow::CanResize(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
// Don't allow windows hosting extensions to resize.
return !delegate_->WithExtension();
}
bool ViewsWindow::OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
CEF_REQUIRE_UI_THREAD();
@@ -455,7 +577,17 @@ bool ViewsWindow::OnKeyEvent(CefRefPtr<CefWindow> window,
const CefKeyEvent& event) {
CEF_REQUIRE_UI_THREAD();
if (!window_ || !with_controls_)
if (!window_)
return false;
if (delegate_->WithExtension() && event.type == KEYEVENT_RAWKEYDOWN &&
event.windows_key_code == VK_ESCAPE) {
// Close the extension window on escape.
Close(false);
return true;
}
if (!with_controls_)
return false;
if (event.type == KEYEVENT_RAWKEYDOWN && event.windows_key_code == VK_MENU) {
@@ -509,6 +641,19 @@ void ViewsWindow::OnFocus(CefRefPtr<CefView> view) {
SetMenuFocusable(false);
}
}
if (view_id == ID_BROWSER_VIEW)
delegate_->OnViewsWindowActivated(this);
}
void ViewsWindow::OnBlur(CefRefPtr<CefView> view) {
CEF_REQUIRE_UI_THREAD();
const int view_id = view->GetID();
if (view_id == ID_BROWSER_VIEW && delegate_->WithExtension()) {
// Close windows hosting extensions when the browser loses focus.
Close(false);
}
}
void ViewsWindow::MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
@@ -529,7 +674,8 @@ ViewsWindow::ViewsWindow(Delegate* delegate,
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::GetGlobalCommandLine();
frameless_ = command_line->HasSwitch(switches::kHideFrame);
frameless_ = command_line->HasSwitch(switches::kHideFrame) ||
delegate_->WithExtension();
if (!command_line->HasSwitch(switches::kHideTopMenu)) {
top_menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST);
@@ -609,7 +755,9 @@ void ViewsWindow::AddControls() {
CefRefPtr<CefMenuButton> menu_button =
CefMenuButton::CreateMenuButton(this, CefString(), false, false);
menu_button->SetID(ID_MENU_BUTTON);
menu_button->SetImage(CEF_BUTTON_STATE_NORMAL, LoadImageIcon("menu_icon"));
menu_button->SetImage(
CEF_BUTTON_STATE_NORMAL,
delegate_->GetImageCache()->GetCachedImage("menu_icon"));
views_style::ApplyTo(menu_button.get());
menu_button->SetInkDropEnabled(true);
// Override the default minimum size.
@@ -628,6 +776,11 @@ void ViewsWindow::AddControls() {
for (size_t i = 0U; i < browse_buttons.size(); ++i)
top_panel->AddChildView(browse_buttons[i]);
top_panel->AddChildView(url_textfield);
UpdateExtensionControls();
DCHECK(extensions_panel_);
top_panel->AddChildView(extensions_panel_);
top_panel->AddChildView(menu_button);
views_style::ApplyTo(top_panel);
@@ -718,4 +871,84 @@ void ViewsWindow::ShowTopControls(bool show) {
}
}
void ViewsWindow::UpdateExtensionControls() {
CEF_REQUIRE_UI_THREAD();
if (!window_ || !with_controls_)
return;
if (!extensions_panel_) {
extensions_panel_ = CefPanel::CreatePanel(NULL);
// Use a horizontal box layout for |top_panel|.
CefBoxLayoutSettings top_panel_layout_settings;
top_panel_layout_settings.horizontal = true;
CefRefPtr<CefBoxLayout> top_panel_layout =
extensions_panel_->SetToBoxLayout(top_panel_layout_settings);
} else {
extensions_panel_->RemoveAllChildViews();
}
if (extensions_.size() >
ID_EXTENSION_BUTTON_LAST - ID_EXTENSION_BUTTON_FIRST) {
LOG(WARNING) << "Too many extensions loaded. Some will be ignored.";
}
ExtensionInfoSet::const_iterator it = extensions_.begin();
for (int id = ID_EXTENSION_BUTTON_FIRST;
it != extensions_.end() && id <= ID_EXTENSION_BUTTON_LAST; ++id, ++it) {
CefRefPtr<CefMenuButton> button =
CefMenuButton::CreateMenuButton(this, CefString(), false, false);
button->SetID(id);
button->SetImage(CEF_BUTTON_STATE_NORMAL, (*it).image_);
views_style::ApplyTo(button.get());
button->SetInkDropEnabled(true);
// Override the default minimum size.
button->SetMinimumSize(CefSize(0, 0));
extensions_panel_->AddChildView(button);
}
CefRefPtr<CefView> parent_view = extensions_panel_->GetParentView();
if (parent_view)
parent_view->InvalidateLayout();
}
void ViewsWindow::OnExtensionIconsLoaded(const ExtensionSet& extensions,
const ImageCache::ImageSet& images) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute this method on the UI thread.
CefPostTask(TID_UI, base::Bind(&ViewsWindow::OnExtensionIconsLoaded, this,
extensions, images));
return;
}
DCHECK_EQ(extensions.size(), images.size());
extensions_.clear();
ExtensionSet::const_iterator it1 = extensions.begin();
ImageCache::ImageSet::const_iterator it2 = images.begin();
for (; it1 != extensions.end() && it2 != images.end(); ++it1, ++it2) {
CefRefPtr<CefImage> icon = *it2;
if (!icon)
icon = delegate_->GetImageCache()->GetCachedImage(kDefaultExtensionIcon);
extensions_.push_back(ExtensionInfo(*it1, icon));
}
UpdateExtensionControls();
}
void ViewsWindow::OnExtensionWindowClosed() {
if (!CefCurrentlyOn(TID_UI)) {
// Execute this method on the UI thread.
CefPostTask(TID_UI,
base::Bind(&ViewsWindow::OnExtensionWindowClosed, this));
return;
}
// Restore the button state.
extension_button_pressed_lock_ = NULL;
}
} // namespace client

View File

@@ -6,8 +6,11 @@
#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_WINDOW_H_
#pragma once
#include <set>
#include <string>
#include <vector>
#include "include/base/cef_callback_forward.h"
#include "include/cef_menu_model_delegate.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_browser_view_delegate.h"
@@ -19,10 +22,13 @@
#include "include/views/cef_textfield_delegate.h"
#include "include/views/cef_window.h"
#include "include/views/cef_window_delegate.h"
#include "tests/cefclient/browser/image_cache.h"
#include "tests/cefclient/browser/views_menu_bar.h"
namespace client {
typedef std::set<CefRefPtr<CefExtension>> ExtensionSet;
// Implements a CefWindow that hosts a single CefBrowserView and optional
// Views-based controls. All methods must be called on the browser process UI
// thread.
@@ -39,9 +45,21 @@ class ViewsWindow : public CefBrowserViewDelegate,
// Return true if the window should show controls.
virtual bool WithControls() = 0;
// Return true if the window is hosting an extension.
virtual bool WithExtension() = 0;
// Return true if the window should be created initially hidden.
virtual bool InitiallyHidden() = 0;
// Returns the parent for this window.
virtual CefRefPtr<CefWindow> GetParentWindow() = 0;
// Return the initial window bounds.
virtual CefRect GetWindowBounds() = 0;
// Returns the ImageCache.
virtual scoped_refptr<ImageCache> GetImageCache() = 0;
// Called when the ViewsWindow is created.
virtual void OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) = 0;
@@ -49,9 +67,19 @@ class ViewsWindow : public CefBrowserViewDelegate,
// should be released in this callback.
virtual void OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) = 0;
// Called when the ViewsWindow is activated (becomes the foreground window).
virtual void OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) = 0;
// Return the Delegate for the popup window controlled by |client|.
virtual Delegate* GetDelegateForPopup(CefRefPtr<CefClient> client) = 0;
// Create a window for |extension|. |source_bounds| are the bounds of the
// UI element, like a button, that triggered the extension.
virtual void CreateExtensionWindow(CefRefPtr<CefExtension> extension,
const CefRect& source_bounds,
CefRefPtr<CefWindow> parent_window,
const base::Closure& close_callback) = 0;
// Called to execute a test. See resource.h for |test_id| values.
virtual void OnTest(int test_id) = 0;
@@ -76,6 +104,9 @@ class ViewsWindow : public CefBrowserViewDelegate,
void Minimize();
void Maximize();
void SetBounds(const CefRect& bounds);
void SetBrowserSize(const CefSize& size,
bool has_position,
const CefPoint& position);
void Close(bool force);
void SetAddress(const std::string& url);
void SetTitle(const std::string& title);
@@ -85,8 +116,14 @@ class ViewsWindow : public CefBrowserViewDelegate,
void SetDraggableRegions(const std::vector<CefDraggableRegion>& regions);
void TakeFocus(bool next);
void OnBeforeContextMenu(CefRefPtr<CefMenuModel> model);
void OnExtensionsChanged(const ExtensionSet& extensions);
// CefBrowserViewDelegate methods:
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
bool is_devtools) OVERRIDE;
bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
bool is_devtools) OVERRIDE;
@@ -95,8 +132,10 @@ class ViewsWindow : public CefBrowserViewDelegate,
void OnButtonPressed(CefRefPtr<CefButton> button) OVERRIDE;
// CefMenuButtonDelegate methods:
void OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point) OVERRIDE;
void OnMenuButtonPressed(
CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point,
CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) OVERRIDE;
// CefMenuModelDelegate methods:
void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
@@ -110,7 +149,11 @@ class ViewsWindow : public CefBrowserViewDelegate,
// CefWindowDelegate methods:
void OnWindowCreated(CefRefPtr<CefWindow> window) OVERRIDE;
void OnWindowDestroyed(CefRefPtr<CefWindow> window) OVERRIDE;
CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
bool* is_menu,
bool* can_activate_menu) OVERRIDE;
bool IsFrameless(CefRefPtr<CefWindow> window) OVERRIDE;
bool CanResize(CefRefPtr<CefWindow> window) OVERRIDE;
bool CanClose(CefRefPtr<CefWindow> window) OVERRIDE;
bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) OVERRIDE;
bool OnKeyEvent(CefRefPtr<CefWindow> window,
@@ -119,6 +162,7 @@ class ViewsWindow : public CefBrowserViewDelegate,
// CefViewDelegate methods:
CefSize GetMinimumSize(CefRefPtr<CefView> view) OVERRIDE;
void OnFocus(CefRefPtr<CefView> view) OVERRIDE;
void OnBlur(CefRefPtr<CefView> view) OVERRIDE;
// ViewsMenuBar::Delegate methods:
void MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
@@ -152,6 +196,13 @@ class ViewsWindow : public CefBrowserViewDelegate,
// Show/hide top controls on the Window.
void ShowTopControls(bool show);
// Update extension controls on the Window.
void UpdateExtensionControls();
void OnExtensionIconsLoaded(const ExtensionSet& extensions,
const ImageCache::ImageSet& images);
void OnExtensionWindowClosed();
Delegate* delegate_; // Not owned by this object.
CefRefPtr<CefBrowserView> browser_view_;
bool frameless_;
@@ -165,6 +216,20 @@ class ViewsWindow : public CefBrowserViewDelegate,
CefSize minimum_window_size_;
// Structure representing an extension.
struct ExtensionInfo {
ExtensionInfo(CefRefPtr<CefExtension> extension, CefRefPtr<CefImage> image)
: extension_(extension), image_(image) {}
CefRefPtr<CefExtension> extension_;
CefRefPtr<CefImage> image_;
};
typedef std::vector<ExtensionInfo> ExtensionInfoSet;
ExtensionInfoSet extensions_;
CefRefPtr<CefPanel> extensions_panel_;
CefRefPtr<CefMenuButtonPressedLock> extension_button_pressed_lock_;
IMPLEMENT_REFCOUNTING(ViewsWindow);
DISALLOW_COPY_AND_ASSIGN(ViewsWindow);
};

View File

@@ -119,12 +119,13 @@ int RunMain(int argc, char* argv[]) {
// Register scheme handlers.
test_runner::RegisterSchemeHandlers();
RootWindowConfig window_config;
window_config.with_controls =
!command_line->HasSwitch(switches::kHideControls);
window_config.with_osr = settings.windowless_rendering_enabled ? true : false;
// Create the first window.
context->GetRootWindowManager()->CreateRootWindow(
!command_line->HasSwitch(switches::kHideControls), // Show controls.
settings.windowless_rendering_enabled ? true : false,
CefRect(), // Use default system size.
std::string()); // Use default URL.
context->GetRootWindowManager()->CreateRootWindow(window_config);
// Run the message loop. This will block until Quit() is called.
int result = message_loop->Run();

View File

@@ -216,12 +216,13 @@ NSMenuItem* GetMenuItemWithAction(NSMenu* menu, SEL action_selector) {
}
}
client::RootWindowConfig window_config;
window_config.with_controls = with_controls_;
window_config.with_osr = with_osr_;
// Create the first window.
client::MainContext::Get()->GetRootWindowManager()->CreateRootWindow(
with_controls_, // Show controls.
with_osr_, // Use off-screen rendering.
CefRect(), // Use default system size.
std::string()); // Use default URL.
window_config);
}
- (void)tryToTerminateApplication:(NSApplication*)app {

View File

@@ -94,12 +94,13 @@ int RunMain(HINSTANCE hInstance, int nCmdShow) {
// Register scheme handlers.
test_runner::RegisterSchemeHandlers();
RootWindowConfig window_config;
window_config.with_controls =
!command_line->HasSwitch(switches::kHideControls);
window_config.with_osr = settings.windowless_rendering_enabled ? true : false;
// Create the first window.
context->GetRootWindowManager()->CreateRootWindow(
!command_line->HasSwitch(switches::kHideControls), // Show controls.
settings.windowless_rendering_enabled ? true : false,
CefRect(), // Use default system size.
std::string()); // Use default URL.
context->GetRootWindowManager()->CreateRootWindow(window_config);
// Run the message loop. This will block until Quit() is called by the
// RootWindowManager after all windows have been destroyed.

View File

@@ -0,0 +1,17 @@
# Color Extension
Demonstrates basic extension app loading and integration by using a popup to change the page color.
## Usage
Run `cefclient --load-extension=set_page_color`.
When using the Views framework (`--use-views`) an extension icon will be added to the control bar and clicking the icon will open the extension window. When not using the Views framework an extension window will be opened automatically on application start.
## Implementation
Based on the [set_page_color](https://developer.chrome.com/extensions/samples#search:browser%20action%20with%20a%20popup) example extension.
Calls:
* [tabs.executeScript](https://developer.chrome.com/extensions/tabs#method-executeScript)

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

View File

@@ -0,0 +1,14 @@
{
"name": "A browser action with a popup that changes the page color",
"description": "Change the current page color",
"version": "1.0",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"browser_action": {
"default_title": "Set this page's color.",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"manifest_version": 2
}

View File

@@ -0,0 +1,55 @@
<!doctype html>
<html>
<head>
<title>Set Page Color Popup</title>
<style>
body {
overflow: hidden;
margin: 0px;
padding: 0px;
background: white;
}
div:first-child {
margin-top: 0px;
}
div {
cursor: pointer;
text-align: center;
padding: 1px 3px;
font-family: sans-serif;
font-size: 0.8em;
width: 100px;
margin-top: 1px;
background: #cccccc;
}
div:hover {
background: #aaaaaa;
}
#red {
border: 1px solid red;
color: red;
}
#blue {
border: 1px solid blue;
color: blue;
}
#green {
border: 1px solid green;
color: green;
}
#yellow {
border: 1px solid yellow;
color: yellow;
}
</style>
<script src="popup.js"></script>
</head>
<body>
<div id="red">red</div>
<div id="blue">blue</div>
<div id="green">green</div>
<div id="yellow">yellow</div>
</body>
</html>

View File

@@ -0,0 +1,16 @@
// Copyright (c) 2012 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.
function click(e) {
chrome.tabs.executeScript(null,
{code:"document.body.style.backgroundColor='" + e.target.id + "'"});
}
document.addEventListener('DOMContentLoaded', function () {
var divs = document.querySelectorAll('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', click);
}
});

View File

@@ -52,6 +52,11 @@ IDS_WINDOW_ICON_1X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.1x.png
IDS_WINDOW_ICON_2X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.2x.png"
IDS_XMLHTTPREQUEST_HTML BINARY "..\\xmlhttprequest.html"
IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG BINARY "..\\extensions\\set_page_color\\icon.png"
IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON BINARY "..\\extensions\\set_page_color\\manifest.json"
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML BINARY "..\\extensions\\set_page_color\\popup.html"
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS BINARY "..\\extensions\\set_page_color\\popup.js"
/////////////////////////////////////////////////////////////////////////////
//
// Icon