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

View File

@@ -108,7 +108,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})
set_target_properties(${CEF_TARGET} PROPERTIES
RESOURCE "${CEFSIMPLE_RESOURCES_SRCS}"
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist
)
@@ -130,12 +129,12 @@ 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.
set(PREFIXES "mac/") # Remove these prefixes from input file paths.
COPY_MACOSX_RESOURCES("${CEFSIMPLE_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 "mac/")
COPY_MACOSX_RESOURCES("${CEFSIMPLE_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
endif()

View File

@@ -128,7 +128,6 @@ if(OS_MACOSX)
add_dependencies(${CEF_TARGET} libcef_dll_wrapper cef_gtest "${CEF_HELPER_TARGET}")
target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper cef_gtest ${CEF_STANDARD_LIBS})
set_target_properties(${CEF_TARGET} PROPERTIES
RESOURCE "${UNITTESTS_RESOURCES_SRCS}"
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/Info.plist
)
@@ -150,16 +149,15 @@ 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/"
"../shared/resources/"
)
COPY_MACOSX_RESOURCES("${UNITTESTS_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/"
"../shared/resources/"
)
COPY_MACOSX_RESOURCES("${UNITTESTS_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
endif()

View File

@@ -6,6 +6,7 @@
#include "include/base/cef_bind.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/ceftests/test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
@@ -143,3 +144,101 @@ TEST(DisplayTest, Title) {
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
const char kAutoResizeUrl[] = "http://tests-display/auto-resize.html";
class AutoResizeTestHandler : public RoutingTestHandler {
public:
AutoResizeTestHandler() {}
void RunTest() override {
// Add the resources that we will navigate to/from.
AddResource(kAutoResizeUrl,
"<html><head><style>"
"body {overflow:hidden;margin:0px;padding:0px;}"
"</style></head><body><div id=a>Content</div></body></html>",
"text/html");
// Create the browser.
CreateBrowser(kAutoResizeUrl);
// Time out the test after a reasonable period of time.
SetTestTimeout();
}
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
RoutingTestHandler::OnAfterCreated(browser);
browser->GetHost()->SetAutoResizeEnabled(true, CefSize(10, 10),
CefSize(500, 500));
}
bool OnAutoResize(CefRefPtr<CefBrowser> browser,
const CefSize& new_size) override {
if (!got_auto_resize1_) {
got_auto_resize1_.yes();
EXPECT_EQ(50, new_size.width);
EXPECT_EQ(18, new_size.height);
// Trigger a resize.
browser->GetMainFrame()->ExecuteJavaScript(
"document.getElementById('a').innerText='New Content';",
kAutoResizeUrl, 0);
} else if (!got_auto_resize2_) {
got_auto_resize2_.yes();
EXPECT_EQ(50, new_size.width);
EXPECT_EQ(36, new_size.height);
// Disable resize notifications.
browser->GetHost()->SetAutoResizeEnabled(false, CefSize(), CefSize());
// There should be no more resize notifications. End the test after a
// short delay.
browser->GetMainFrame()->ExecuteJavaScript(
"document.getElementById('a').innerText='New Content Again';"
"var interval = setInterval(function() {"
"window.testQuery({request:'done'});clearInterval(interval);}, 50);",
kAutoResizeUrl, 0);
} else {
EXPECT_TRUE(false); // Not reached.
}
return true;
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
EXPECT_STREQ("done", request.ToString().c_str());
EXPECT_FALSE(got_done_message_);
got_done_message_.yes();
DestroyTest();
return true;
}
void DestroyTest() override {
EXPECT_TRUE(got_auto_resize1_);
EXPECT_TRUE(got_auto_resize2_);
EXPECT_TRUE(got_done_message_);
TestHandler::DestroyTest();
}
private:
TrackCallback got_auto_resize1_;
TrackCallback got_auto_resize2_;
TrackCallback got_done_message_;
IMPLEMENT_REFCOUNTING(AutoResizeTestHandler);
};
} // namespace
// Test OnAutoResize notification.
TEST(DisplayTest, AutoResize) {
CefRefPtr<AutoResizeTestHandler> handler = new AutoResizeTestHandler();
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}

View File

@@ -5,9 +5,9 @@
#include "include/cef_scheme.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/file_util.h"
#include "tests/ceftests/test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/file_util.h"
namespace {
@@ -155,7 +155,8 @@ class DownloadTestHandler : public TestHandler {
// Create a new temporary directory.
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
test_path_ = file_util::JoinPath(temp_dir_.GetPath(), kTestFileName);
test_path_ =
client::file_util::JoinPath(temp_dir_.GetPath(), kTestFileName);
if (test_mode_ == NAVIGATED) {
// Add the resource that we'll navigate to.
@@ -318,7 +319,7 @@ class DownloadTestHandler : public TestHandler {
if (test_mode_ != PENDING) {
// Verify the file contents.
std::string contents;
EXPECT_TRUE(file_util::ReadFileToString(test_path_, &contents));
EXPECT_TRUE(client::file_util::ReadFileToString(test_path_, &contents));
EXPECT_STREQ(kTestContent, contents.c_str());
}

View File

@@ -0,0 +1,291 @@
// 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/ceftests/extensions/extension_test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/shared/browser/client_app_browser.h"
#include "tests/shared/browser/extension_util.h"
using client::ClientAppBrowser;
namespace {
const char kExtensionPath[] = "background-extension";
const char kBackgroundScript[] = "background.js";
// HTML file created internally to load the background script.
const char kGeneratedBackgroundPage[] = "_generated_background_page.html";
// Test load/unload of an extension with a background script.
class BackgroundLoadUnloadTestHandler : public ExtensionTestHandler {
public:
explicit BackgroundLoadUnloadTestHandler(
RequestContextType request_context_type)
: ExtensionTestHandler(request_context_type) {
// Only creating the extension browser.
set_create_main_browser(false);
}
// CefExtensionHandler methods:
void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_TRUE(extension);
EXPECT_TRUE(extension->IsLoaded());
EXPECT_TRUE(extension->GetLoaderContext());
EXPECT_TRUE(
loader_request_context()->IsSame(extension->GetLoaderContext()));
VerifyExtension(extension);
EXPECT_FALSE(got_loaded_);
got_loaded_.yes();
EXPECT_FALSE(extension_);
extension_ = extension;
background_page_url_ = GetExtensionURL(extension, kGeneratedBackgroundPage);
// Add extension resources.
script_url_ = GetExtensionURL(extension, kBackgroundScript);
AddResource(script_url_, GetMessageJS("extension_onload"),
"text/javascript");
}
void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_TRUE(extension);
EXPECT_FALSE(extension->IsLoaded());
EXPECT_FALSE(extension->GetLoaderContext());
EXPECT_FALSE(got_unloaded_);
got_unloaded_.yes();
EXPECT_TRUE(extension_);
EXPECT_TRUE(extension_->IsSame(extension));
// The extension should no longer be registered with the context.
if (loader_request_context())
VerifyExtensionInContext(extension, loader_request_context(), false,
true);
if (request_context() && !request_context_same_loader())
VerifyExtensionInContext(extension, request_context(), false, false);
extension_ = NULL;
// Execute asynchronously so call stacks have a chance to unwind.
// Will close the browser windows.
CefPostTask(
TID_UI,
base::Bind(&BackgroundLoadUnloadTestHandler::DestroyTest, this));
}
bool OnBeforeBackgroundBrowser(CefRefPtr<CefExtension> extension,
const CefString& url,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_TRUE(extension);
EXPECT_TRUE(extension->IsLoaded());
EXPECT_TRUE(extension->GetLoaderContext());
EXPECT_TRUE(
loader_request_context()->IsSame(extension->GetLoaderContext()));
VerifyExtension(extension);
const std::string& background_page_url =
GetExtensionURL(extension, kGeneratedBackgroundPage);
EXPECT_STREQ(background_page_url.c_str(), url.ToString().c_str());
EXPECT_FALSE(client);
client = this;
// Allow the browser creation.
return false;
}
// CefLoadHandler methods:
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override {
EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
EXPECT_TRUE(extension);
EXPECT_TRUE(extension_->IsSame(extension));
if (isLoading) {
EXPECT_FALSE(extension_browser_);
extension_browser_ = browser;
} else {
EXPECT_TRUE(browser->IsSame(extension_browser_));
const std::string& url = browser->GetMainFrame()->GetURL();
EXPECT_STREQ(background_page_url_.c_str(), url.c_str());
EXPECT_FALSE(got_load_done_);
got_load_done_.yes();
TriggerDestroyTestIfDone();
}
}
// CefRequestHandler methods:
CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
EXPECT_TRUE(browser->IsSame(extension_browser_));
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
EXPECT_TRUE(extension);
EXPECT_TRUE(extension_->IsSame(extension));
const std::string& url = request->GetURL();
if (url == background_page_url_) {
EXPECT_FALSE(got_background_page_url_request_);
got_background_page_url_request_.yes();
} else if (url == script_url_) {
EXPECT_FALSE(got_script_url_request_);
got_script_url_request_.yes();
} else {
EXPECT_TRUE(false); // Not reached.
}
// Handle the resource request.
return RoutingTestHandler::GetResourceHandler(browser, frame, request);
}
CefRefPtr<CefExtension> extension() const { return extension_; }
// Verify |extension| contents.
void VerifyExtension(CefRefPtr<CefExtension> extension) const {
EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
client::extension_util::GetInternalExtensionResourcePath(
extension->GetPath())
.c_str());
CefRefPtr<CefDictionaryValue> expected_manifest = CreateManifest();
TestDictionaryEqual(expected_manifest, extension->GetManifest());
VerifyExtensionInContext(extension, loader_request_context(), true, true);
if (!request_context_same_loader())
VerifyExtensionInContext(extension, request_context(), true, false);
}
std::string GetExtensionURL(CefRefPtr<CefExtension> extension,
const std::string& resource_path) const {
const std::string& identifier = extension->GetIdentifier();
const std::string& origin =
client::extension_util::GetExtensionOrigin(identifier);
EXPECT_FALSE(origin.empty());
return origin + resource_path;
}
protected:
void OnLoadExtensions() override {
LoadExtension(kExtensionPath, CreateManifest());
}
bool OnMessage(CefRefPtr<CefBrowser> browser,
const std::string& message) override {
EXPECT_STREQ("extension_onload", message.c_str());
EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
EXPECT_TRUE(extension);
EXPECT_TRUE(extension_->IsSame(extension));
EXPECT_TRUE(browser->IsSame(extension_browser_));
EXPECT_TRUE(browser->GetHost()->IsBackgroundHost());
EXPECT_FALSE(got_body_onload_);
got_body_onload_.yes();
TriggerDestroyTestIfDone();
return true;
}
void OnDestroyTest() override {
extension_browser_ = NULL;
EXPECT_TRUE(got_loaded_);
EXPECT_TRUE(got_background_page_url_request_);
EXPECT_TRUE(got_script_url_request_);
EXPECT_TRUE(got_body_onload_);
EXPECT_TRUE(got_load_done_);
EXPECT_TRUE(got_unloaded_);
}
// Create a manifest with background script.
CefRefPtr<CefDictionaryValue> CreateManifest() const {
CefRefPtr<CefDictionaryValue> manifest =
CreateDefaultManifest(ApiPermissionsList());
CefRefPtr<CefDictionaryValue> background = CefDictionaryValue::Create();
CefRefPtr<CefListValue> scripts = CefListValue::Create();
scripts->SetString(0, kBackgroundScript);
background->SetList("scripts", scripts);
manifest->SetDictionary("background", background);
return manifest;
}
void TriggerDestroyTestIfDone() {
if (got_body_onload_ && got_load_done_) {
TriggerDestroyTest();
}
}
virtual void TriggerDestroyTest() {
// Execute asynchronously so call stacks have a chance to unwind.
CefPostTask(TID_UI,
base::Bind(&BackgroundLoadUnloadTestHandler::UnloadExtension,
this, extension_));
}
CefRefPtr<CefExtension> extension_;
std::string script_url_;
std::string background_page_url_;
CefRefPtr<CefBrowser> extension_browser_;
TrackCallback got_loaded_;
TrackCallback got_background_page_url_request_;
TrackCallback got_script_url_request_;
TrackCallback got_body_onload_;
TrackCallback got_load_done_;
TrackCallback got_unloaded_;
IMPLEMENT_REFCOUNTING(BackgroundLoadUnloadTestHandler);
DISALLOW_COPY_AND_ASSIGN(BackgroundLoadUnloadTestHandler);
};
} // namespace
EXTENSION_TEST_GROUP_ALL(BackgroundLoadUnload, BackgroundLoadUnloadTestHandler);
namespace {
// Same as above but without the unload. Only do this with a custom context to
// avoid poluting the global context.
class BackgroundLoadNoUnloadTestHandler
: public BackgroundLoadUnloadTestHandler {
public:
explicit BackgroundLoadNoUnloadTestHandler(
RequestContextType request_context_type)
: BackgroundLoadUnloadTestHandler(request_context_type) {}
protected:
void TriggerDestroyTest() override {
// Release everything that references the request context. This should
// trigger unload of the extension.
CloseBrowser(extension_browser_, false);
extension_browser_ = NULL;
ReleaseRequestContexts();
}
};
} // namespace
EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(BackgroundLoadNoUnload,
BackgroundLoadNoUnloadTestHandler);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
// 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/ceftests/extensions/extension_test_handler.h"
#include "tests/ceftests/test_suite.h"
#include "tests/ceftests/test_util.h"
ExtensionTestHandler::ExtensionTestHandler(
RequestContextType request_context_type)
: request_context_type_(request_context_type), create_main_browser_(true) {
// Verify supported flag combinations.
if (request_context_on_disk()) {
EXPECT_TRUE(request_context_is_custom());
}
if (request_context_load_with_handler()) {
EXPECT_FALSE(request_context_load_without_handler());
}
if (request_context_load_without_handler()) {
EXPECT_TRUE(request_context_with_handler());
EXPECT_FALSE(request_context_load_with_handler());
}
}
ExtensionTestHandler::~ExtensionTestHandler() {
if (!request_context_temp_dir_.IsEmpty()) {
// Delete temporary directories on shutdown.
CefTestSuite::GetInstance()->RegisterTempDirectory(
request_context_temp_dir_.Take());
}
}
void ExtensionTestHandler::RunTest() {
if (create_main_browser_)
OnAddMainBrowserResources();
CefRefPtr<CefRequestContextHandler> rc_handler;
if (request_context_with_handler()) {
class Handler : public CefRequestContextHandler {
public:
explicit Handler(ExtensionTestHandler* test_handler)
: test_handler_(test_handler) {}
void OnRequestContextInitialized(
CefRefPtr<CefRequestContext> request_context) override {
if (test_handler_->create_main_browser()) {
// Load extensions after the RequestContext has been initialized by
// creation of the main browser.
test_handler_->OnLoadExtensions();
}
}
private:
ExtensionTestHandler* test_handler_;
IMPLEMENT_REFCOUNTING(Handler);
};
rc_handler = new Handler(this);
}
if (request_context_is_custom()) {
CefRequestContextSettings settings;
if (request_context_on_disk()) {
// Create a new temporary directory.
EXPECT_TRUE(request_context_temp_dir_.CreateUniqueTempDir());
CefString(&settings.cache_path) = request_context_temp_dir_.GetPath();
}
request_context_ = CefRequestContext::CreateContext(settings, rc_handler);
} else {
request_context_ = CefRequestContext::CreateContext(
CefRequestContext::GetGlobalContext(), rc_handler);
}
if (request_context_load_with_handler()) {
class Handler : public CefRequestContextHandler {
public:
Handler() {}
private:
IMPLEMENT_REFCOUNTING(Handler);
};
loader_request_context_ =
CefRequestContext::CreateContext(request_context_, new Handler());
} else if (request_context_load_without_handler()) {
loader_request_context_ =
CefRequestContext::CreateContext(request_context_, NULL);
} else {
loader_request_context_ = request_context_;
}
if (create_main_browser_) {
OnCreateMainBrowser();
} else {
// Creation of the extension browser will trigger initialization of the
// RequestContext, so just load the extensions now.
OnLoadExtensions();
}
// Time out the test after a reasonable period of time.
SetTestTimeout();
}
void ExtensionTestHandler::DestroyTest() {
OnDestroyTest();
ReleaseRequestContexts();
RoutingTestHandler::DestroyTest();
}
void ExtensionTestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
RoutingTestHandler::OnAfterCreated(browser);
if (create_main_browser() && !request_context_with_handler() &&
GetBrowserId() == browser->GetIdentifier()) {
// When the RequestContext doesn't have a handler we won't get a
// notification for RequestContext initialization. Instead use main browser
// creation to indicate that the RequestContext has been initialized.
OnLoadExtensions();
}
}
void ExtensionTestHandler::OnExtensionLoadFailed(cef_errorcode_t result) {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_TRUE(false); // Not reached.
}
// CefMessageRouterBrowserSide::Handler methods:
bool ExtensionTestHandler::OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) {
if (OnMessage(browser, request))
return true;
EXPECT_FALSE(true) << "Unexpected message: " << request.ToString();
return false;
}
// static
CefRefPtr<CefDictionaryValue> ExtensionTestHandler::CreateDefaultManifest(
const std::vector<std::string>& api_permissions) {
CefRefPtr<CefDictionaryValue> manifest = CefDictionaryValue::Create();
manifest->SetString("name", "An extension");
manifest->SetString("description", "An extension description");
manifest->SetString("version", "1.0");
manifest->SetInt("manifest_version", 2);
CefRefPtr<CefListValue> permissions = CefListValue::Create();
permissions->SetSize(api_permissions.size() + 2);
size_t idx = 0;
for (; idx < api_permissions.size(); ++idx)
permissions->SetString(idx, api_permissions[idx]);
// Allow access to all http/https origins.
permissions->SetString(idx++, "http://*/*");
permissions->SetString(idx++, "https://*/*");
manifest->SetList("permissions", permissions);
return manifest;
}
// static
std::string ExtensionTestHandler::GetMessageJS(const std::string& message) {
EXPECT_TRUE(!message.empty());
return "window.testQuery({request:'" + message + "'});";
}
// static
void ExtensionTestHandler::VerifyExtensionInContext(
CefRefPtr<CefExtension> extension,
CefRefPtr<CefRequestContext> context,
bool has_access,
bool is_loader) {
const CefString& extension_id = extension->GetIdentifier();
EXPECT_FALSE(extension_id.empty());
if (has_access) {
if (is_loader) {
EXPECT_TRUE(context->DidLoadExtension(extension_id));
} else {
EXPECT_FALSE(context->DidLoadExtension(extension_id));
}
EXPECT_TRUE(context->HasExtension(extension_id));
} else {
EXPECT_FALSE(context->DidLoadExtension(extension_id));
EXPECT_FALSE(context->HasExtension(extension_id));
}
CefRefPtr<CefExtension> extension2 = context->GetExtension(extension_id);
if (has_access) {
EXPECT_TRUE(extension2);
EXPECT_TRUE(extension->IsSame(extension2));
TestDictionaryEqual(extension->GetManifest(), extension2->GetManifest());
} else {
EXPECT_FALSE(extension2);
}
std::vector<CefString> extension_ids;
EXPECT_TRUE(context->GetExtensions(extension_ids));
// Should be our test extension and possibly the builtin PDF extension if it
// has finished loading (our extension may load first if the call to
// LoadExtension initializes the request context).
bool has_extension = false;
for (size_t i = 0; i < extension_ids.size(); ++i) {
if (extension_ids[i] == extension_id) {
has_extension = true;
break;
}
}
if (has_access) {
EXPECT_TRUE(has_extension);
} else {
EXPECT_FALSE(has_extension);
}
}
void ExtensionTestHandler::LoadExtension(
const std::string& extension_path,
CefRefPtr<CefDictionaryValue> manifest) {
EXPECT_TRUE(!extension_path.empty());
loader_request_context_->LoadExtension(extension_path, manifest, this);
}
void ExtensionTestHandler::UnloadExtension(CefRefPtr<CefExtension> extension) {
EXPECT_TRUE(extension);
extension->Unload();
EXPECT_FALSE(extension->IsLoaded());
EXPECT_FALSE(extension->GetLoaderContext());
}
void ExtensionTestHandler::ReleaseRequestContexts() {
request_context_ = NULL;
loader_request_context_ = NULL;
}

View File

@@ -0,0 +1,238 @@
// 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_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_
#define CEF_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_
#pragma once
#include <vector>
#include "include/cef_extension_handler.h"
#include "include/cef_values.h"
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
class ExtensionTestHandler : public RoutingTestHandler,
public CefExtensionHandler {
public:
// All tests must be able to run with all RequestContext combinations. See the
// EXTENSION_TEST_GROUP_* macros below.
enum RequestContextType {
// If set create a custom context. Otherwise, use the global context.
RC_TYPE_FLAG_CUSTOM = 1 << 0,
// If set store data on disk. Otherwise, store data in memory.
// Requires RC_TYPE_FLAG_CUSTOM.
RC_TYPE_FLAG_ON_DISK = 1 << 1,
// If set use a handler. Otherwise, don't.
RC_TYPE_FLAG_WITH_HANDLER = 1 << 2,
// If set load extensions with a different context that shares the same
// storage but specifies a different handler.
// Excludes RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER.
RC_TYPE_FLAG_LOAD_WITH_HANDLER = 1 << 3,
// If set load extensions with a different context that shares the same
// storage but doesn't specify a handler.
// Requires RC_TYPE_FLAG_WITH_HANDLER.
// Excludes RC_TYPE_FLAG_LOAD_WITH_HANDLER.
RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER = 1 << 4,
};
explicit ExtensionTestHandler(RequestContextType request_context_type);
virtual ~ExtensionTestHandler();
// TestHandler methods:
void RunTest() override;
void DestroyTest() override;
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
// CefExtensionHandler methods:
void OnExtensionLoadFailed(cef_errorcode_t result) override;
// CefMessageRouterBrowserSide::Handler methods:
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override;
CefRefPtr<CefRequestContext> request_context() const {
return request_context_;
}
CefRefPtr<CefRequestContext> loader_request_context() const {
return loader_request_context_;
}
bool request_context_is_custom() const {
return !!(request_context_type_ & RC_TYPE_FLAG_CUSTOM);
}
bool request_context_on_disk() const {
return !!(request_context_type_ & RC_TYPE_FLAG_ON_DISK);
}
bool request_context_with_handler() const {
return !!(request_context_type_ & RC_TYPE_FLAG_WITH_HANDLER);
}
bool request_context_load_with_handler() const {
return !!(request_context_type_ & RC_TYPE_FLAG_LOAD_WITH_HANDLER);
}
bool request_context_load_without_handler() const {
return !!(request_context_type_ & RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER);
}
bool request_context_same_loader() const {
return !(request_context_load_with_handler() ||
request_context_load_without_handler());
}
protected:
// Returns the default extension manifest.
typedef std::vector<std::string> ApiPermissionsList;
static CefRefPtr<CefDictionaryValue> CreateDefaultManifest(
const ApiPermissionsList& api_permissions);
// Returns the JS code that, when executed, will deliver |message| to the
// OnMessage callback.
static std::string GetMessageJS(const std::string& message);
// Run checks on the state of |extension| in |context|. If |has_access| is
// true then |context| is expected to have access to |extension|. If
// |is_loader| is true then |context| is expected to have loaded |extension|.
static void VerifyExtensionInContext(CefRefPtr<CefExtension> extension,
CefRefPtr<CefRequestContext> context,
bool has_access,
bool is_loader);
// Helper for loading/unloading an extension.
void LoadExtension(const std::string& extension_path,
CefRefPtr<CefDictionaryValue> manifest);
void UnloadExtension(CefRefPtr<CefExtension> extension);
// Release request contexts. This is normally called from DestroyTest().
void ReleaseRequestContexts();
void set_create_main_browser(bool val) { create_main_browser_ = val; }
bool create_main_browser() const { return create_main_browser_; }
// Called when its time to add resources for the main browser if
// |create_main_browser_| is true.
virtual void OnAddMainBrowserResources() {}
// Called when its time to create the main browser if
// |create_main_browser_| is true.
virtual void OnCreateMainBrowser() {}
// Called when its time to load extensions.
virtual void OnLoadExtensions() = 0;
// Called when |browser| receives |message|. Return true if the message is
// handled. The JS code that sends messages is created by GetMessageJS().
virtual bool OnMessage(CefRefPtr<CefBrowser> browser,
const std::string& message) = 0;
// Called to perform verification on test destruction.
virtual void OnDestroyTest() = 0;
private:
const RequestContextType request_context_type_;
CefScopedTempDir request_context_temp_dir_;
// Context used when creating browsers.
CefRefPtr<CefRequestContext> request_context_;
// Context used when loading extensions.
CefRefPtr<CefRequestContext> loader_request_context_;
// If true expect creation of a main browser. Default is true.
bool create_main_browser_;
DISALLOW_COPY_AND_ASSIGN(ExtensionTestHandler);
};
// Helper for implementing an extension test.
#define EXTENSION_TEST(name, test_class, rc_type) \
TEST(ExtensionTest, name) { \
CefRefPtr<test_class> handler = new test_class( \
static_cast<ExtensionTestHandler::RequestContextType>(rc_type)); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
}
// Helper for implementing extension tests that include all RequestContext
// combinations. When two or more extension tests significantly overlap in
// tested functionality the first test should use the ALL macro and the others
// should use the MINIMAL macro.
#define EXTENSION_TEST_GROUP_ALL(name, test_class) \
EXTENSION_TEST(name##RCGlobal, test_class, 0); \
EXTENSION_TEST(name##RCGlobalLoadWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \
EXTENSION_TEST(name##RCGlobalWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) \
EXTENSION_TEST(name##RCGlobalWithHandlerLoadWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \
EXTENSION_TEST(name##RCGlobalWithHandlerLoadWithoutHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER) \
EXTENSION_TEST(name##RCCustomInMemory, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM) \
EXTENSION_TEST(name##RCCustomInMemoryLoadWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \
EXTENSION_TEST(name##RCCustomInMemoryWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) \
EXTENSION_TEST(name##RCCustomInMemoryWithHandlerLoadWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \
EXTENSION_TEST(name##RCCustomInMemoryWithHandlerLoadWithoutHandler, \
test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER) \
EXTENSION_TEST(name##RCCustomOnDisk, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK) \
EXTENSION_TEST(name##RCCustomOnDiskLoadWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \
EXTENSION_TEST(name##RCCustomOnDiskWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER) \
EXTENSION_TEST(name##RCCustomOnDiskWithHandlerLoadWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITH_HANDLER) \
EXTENSION_TEST(name##RCCustomOnDiskWithHandlerLoadWithoutHandler, \
test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_ON_DISK | \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER | \
ExtensionTestHandler::RC_TYPE_FLAG_LOAD_WITHOUT_HANDLER)
#define EXTENSION_TEST_GROUP_MINIMAL_GLOBAL(name, test_class) \
EXTENSION_TEST(name##RCGlobal, test_class, 0); \
EXTENSION_TEST(name##RCGlobalWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER)
#define EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(name, test_class) \
EXTENSION_TEST(name##RCCustomInMemory, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM) \
EXTENSION_TEST(name##RCCustomInMemoryWithHandler, test_class, \
ExtensionTestHandler::RC_TYPE_FLAG_CUSTOM | \
ExtensionTestHandler::RC_TYPE_FLAG_WITH_HANDLER)
// Helper for implementing extension tests that include a minimal set of
// RequestContext combinations. This mostly just verifies that the test runs
// and doesn't leak state information in the context.
#define EXTENSION_TEST_GROUP_MINIMAL(name, test_class) \
EXTENSION_TEST_GROUP_MINIMAL_GLOBAL(name, test_class) \
EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(name, test_class)
#endif // CEF_TESTS_UNITTESTS_EXTENSIONS_EXTENSION_TEST_HANDLER_H_

View File

@@ -0,0 +1,241 @@
// 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/ceftests/extensions/extension_test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/shared/browser/extension_util.h"
namespace {
const char kExtensionPath[] = "view-extension";
// Test extension load/unload.
class ViewLoadUnloadTestHandler : public ExtensionTestHandler {
public:
explicit ViewLoadUnloadTestHandler(RequestContextType request_context_type)
: ExtensionTestHandler(request_context_type) {
// Only creating the extension browser.
set_create_main_browser(false);
}
// CefExtensionHandler methods:
void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_TRUE(extension);
EXPECT_TRUE(extension->IsLoaded());
EXPECT_TRUE(extension->GetLoaderContext());
EXPECT_TRUE(
loader_request_context()->IsSame(extension->GetLoaderContext()));
VerifyExtension(extension);
EXPECT_FALSE(got_loaded_);
got_loaded_.yes();
EXPECT_FALSE(extension_);
extension_ = extension;
CreateBrowserForExtension();
}
void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_TRUE(extension);
EXPECT_FALSE(extension->IsLoaded());
EXPECT_FALSE(extension->GetLoaderContext());
EXPECT_FALSE(got_unloaded_);
got_unloaded_.yes();
EXPECT_TRUE(extension_);
EXPECT_TRUE(extension_->IsSame(extension));
// The extension should no longer be registered with the context.
if (loader_request_context())
VerifyExtensionInContext(extension, loader_request_context(), false,
true);
if (request_context() && !request_context_same_loader())
VerifyExtensionInContext(extension, request_context(), false, false);
extension_ = NULL;
// Execute asynchronously so call stacks have a chance to unwind.
// Will close the browser windows.
CefPostTask(TID_UI,
base::Bind(&ViewLoadUnloadTestHandler::DestroyTest, this));
}
// CefLoadHandler methods:
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override {
EXPECT_FALSE(browser->GetHost()->IsBackgroundHost());
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
EXPECT_TRUE(extension);
EXPECT_TRUE(extension_->IsSame(extension));
if (isLoading) {
EXPECT_FALSE(extension_browser_);
extension_browser_ = browser;
} else {
EXPECT_TRUE(browser->IsSame(extension_browser_));
const std::string& url = browser->GetMainFrame()->GetURL();
EXPECT_STREQ(extension_url_.c_str(), url.c_str());
EXPECT_FALSE(got_load_done_);
got_load_done_.yes();
TriggerDestroyTestIfDone();
}
}
// CefRequestHandler methods:
CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
EXPECT_FALSE(browser->GetHost()->IsBackgroundHost());
EXPECT_TRUE(browser->IsSame(extension_browser_));
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
EXPECT_TRUE(extension);
EXPECT_TRUE(extension_->IsSame(extension));
const std::string& url = request->GetURL();
EXPECT_STREQ(extension_url_.c_str(), url.c_str());
EXPECT_FALSE(got_url_request_);
got_url_request_.yes();
// Handle the resource request.
return RoutingTestHandler::GetResourceHandler(browser, frame, request);
}
protected:
void OnLoadExtensions() override {
LoadExtension(kExtensionPath, CreateManifest());
}
bool OnMessage(CefRefPtr<CefBrowser> browser,
const std::string& message) override {
EXPECT_FALSE(browser->GetHost()->IsBackgroundHost());
EXPECT_STREQ("extension_onload", message.c_str());
CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension();
EXPECT_TRUE(extension);
EXPECT_TRUE(extension_->IsSame(extension));
EXPECT_TRUE(browser->IsSame(extension_browser_));
EXPECT_FALSE(got_body_onload_);
got_body_onload_.yes();
TriggerDestroyTestIfDone();
return true;
}
void OnDestroyTest() override {
extension_browser_ = NULL;
EXPECT_TRUE(got_loaded_);
EXPECT_TRUE(got_url_request_);
EXPECT_TRUE(got_body_onload_);
EXPECT_TRUE(got_load_done_);
EXPECT_TRUE(got_unloaded_);
}
// Create the default manifest.
CefRefPtr<CefDictionaryValue> CreateManifest() const {
return CreateDefaultManifest(ApiPermissionsList());
}
// Verify |extension| contents.
void VerifyExtension(CefRefPtr<CefExtension> extension) const {
EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
client::extension_util::GetInternalExtensionResourcePath(
extension->GetPath())
.c_str());
CefRefPtr<CefDictionaryValue> expected_manifest = CreateManifest();
TestDictionaryEqual(expected_manifest, extension->GetManifest());
VerifyExtensionInContext(extension, loader_request_context(), true, true);
if (!request_context_same_loader())
VerifyExtensionInContext(extension, request_context(), true, false);
}
void CreateBrowserForExtension() {
const std::string& identifier = extension_->GetIdentifier();
EXPECT_FALSE(identifier.empty());
const std::string& origin =
client::extension_util::GetExtensionOrigin(identifier);
EXPECT_FALSE(origin.empty());
// Add extension resources.
extension_url_ = origin + "extension.html";
AddResource(extension_url_,
"<html><body onLoad=" + GetMessageJS("extension_onload") +
">Extension</body></html>",
"text/html");
// Create a browser to host the extension.
CreateBrowser(extension_url_, request_context());
}
void TriggerDestroyTestIfDone() {
if (got_body_onload_ && got_load_done_) {
TriggerDestroyTest();
}
}
virtual void TriggerDestroyTest() {
// Execute asynchronously so call stacks have a chance to unwind.
CefPostTask(TID_UI, base::Bind(&ViewLoadUnloadTestHandler::UnloadExtension,
this, extension_));
}
CefRefPtr<CefExtension> extension_;
std::string extension_url_;
CefRefPtr<CefBrowser> extension_browser_;
TrackCallback got_loaded_;
TrackCallback got_url_request_;
TrackCallback got_body_onload_;
TrackCallback got_load_done_;
TrackCallback got_unloaded_;
IMPLEMENT_REFCOUNTING(ViewLoadUnloadTestHandler);
DISALLOW_COPY_AND_ASSIGN(ViewLoadUnloadTestHandler);
};
} // namespace
EXTENSION_TEST_GROUP_ALL(ViewLoadUnload, ViewLoadUnloadTestHandler);
namespace {
// Same as above but without the unload. Only do this with a custom context to
// avoid poluting the global context.
class ViewLoadNoUnloadTestHandler : public ViewLoadUnloadTestHandler {
public:
explicit ViewLoadNoUnloadTestHandler(RequestContextType request_context_type)
: ViewLoadUnloadTestHandler(request_context_type) {}
protected:
void TriggerDestroyTest() override {
// Release everything that references the request context. This should
// trigger unload of the extension.
CloseBrowser(extension_browser_, false);
extension_browser_ = NULL;
ReleaseRequestContexts();
}
};
} // namespace
EXTENSION_TEST_GROUP_MINIMAL_CUSTOM(ViewLoadNoUnload,
ViewLoadNoUnloadTestHandler);

View File

@@ -5,31 +5,33 @@
#include <string>
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/file_util.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/file_util.h"
TEST(FileUtil, JoinPath) {
// Should return whichever path component is non-empty.
EXPECT_STREQ("", file_util::JoinPath("", "").c_str());
EXPECT_STREQ("path1", file_util::JoinPath("path1", "").c_str());
EXPECT_STREQ("path2", file_util::JoinPath("", "path2").c_str());
EXPECT_STREQ("", client::file_util::JoinPath("", "").c_str());
EXPECT_STREQ("path1", client::file_util::JoinPath("path1", "").c_str());
EXPECT_STREQ("path2", client::file_util::JoinPath("", "path2").c_str());
const std::string& expected =
std::string("path1") + file_util::kPathSep + std::string("path2");
std::string("path1") + client::file_util::kPathSep + std::string("path2");
// Should always be 1 kPathSep character between paths.
EXPECT_STREQ(expected.c_str(), file_util::JoinPath("path1", "path2").c_str());
EXPECT_STREQ(
expected.c_str(),
file_util::JoinPath(std::string("path1") + file_util::kPathSep, "path2")
.c_str());
EXPECT_STREQ(
expected.c_str(),
file_util::JoinPath("path1", file_util::kPathSep + std::string("path2"))
.c_str());
EXPECT_STREQ(expected.c_str(),
file_util::JoinPath(std::string("path1") + file_util::kPathSep,
file_util::kPathSep + std::string("path2"))
client::file_util::JoinPath("path1", "path2").c_str());
EXPECT_STREQ(expected.c_str(),
client::file_util::JoinPath(
std::string("path1") + client::file_util::kPathSep, "path2")
.c_str());
EXPECT_STREQ(expected.c_str(),
client::file_util::JoinPath(
"path1", client::file_util::kPathSep + std::string("path2"))
.c_str());
EXPECT_STREQ(expected.c_str(),
client::file_util::JoinPath(
std::string("path1") + client::file_util::kPathSep,
client::file_util::kPathSep + std::string("path2"))
.c_str());
}
@@ -38,13 +40,21 @@ TEST(FileUtil, WriteAndReadFile) {
EXPECT_TRUE(dir.CreateUniqueTempDir());
const std::string& data = "Test contents to read/write";
const std::string& path = file_util::JoinPath(dir.GetPath(), "test.txt");
const std::string& path =
client::file_util::JoinPath(dir.GetPath(), "test.txt");
EXPECT_EQ(static_cast<int>(data.size()),
file_util::WriteFile(path.c_str(), data.data(),
static_cast<int>(data.size())));
client::file_util::WriteFile(path.c_str(), data.data(),
static_cast<int>(data.size())));
std::string read;
EXPECT_TRUE(file_util::ReadFileToString(path.c_str(), &read));
EXPECT_TRUE(client::file_util::ReadFileToString(path.c_str(), &read));
EXPECT_STREQ(data.c_str(), read.c_str());
}
TEST(FileUtil, GetFileExtension) {
EXPECT_TRUE(client::file_util::GetFileExtension(std::string()).empty());
EXPECT_TRUE(client::file_util::GetFileExtension("/path/to/foo").empty());
EXPECT_STREQ("ext",
client::file_util::GetFileExtension("/path/to/foo.ext").c_str());
}

View File

@@ -969,14 +969,14 @@ class OSRTestHandler : public RoutingTestHandler,
return false;
}
void OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) {
void OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) override {
if (test_type_ == OSR_TEST_TAKE_FOCUS) {
EXPECT_TRUE(true);
DestroySucceededTestSoon();
}
}
void OnGotFocus(CefRefPtr<CefBrowser> browser) {
void OnGotFocus(CefRefPtr<CefBrowser> browser) override {
if (test_type_ == OSR_TEST_GOT_FOCUS) {
EXPECT_TRUE(true);
DestroySucceededTestSoon();

View File

@@ -10,9 +10,9 @@
#include "include/wrapper/cef_resource_manager.h"
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include "tests/ceftests/file_util.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/file_util.h"
namespace {
@@ -32,7 +32,8 @@ std::string CreateContents(const std::string& message) {
void WriteFile(const std::string& path, const std::string& contents) {
int contents_size = static_cast<int>(contents.size());
int write_ct = file_util::WriteFile(path, contents.data(), contents_size);
int write_ct =
client::file_util::WriteFile(path, contents.data(), contents_size);
EXPECT_EQ(contents_size, write_ct);
}
@@ -682,15 +683,15 @@ TEST(ResourceManagerTest, DirectoryProvider) {
// Write the files to disk.
const std::string& temp_dir = scoped_dir.GetPath();
WriteFile(file_util::JoinPath(temp_dir, kFile1),
WriteFile(client::file_util::JoinPath(temp_dir, kFile1),
CreateContents(success1_message));
WriteFile(file_util::JoinPath(temp_dir, kFile2),
WriteFile(client::file_util::JoinPath(temp_dir, kFile2),
CreateContents(success2_message));
// Also include a subdirectory.
const std::string& sub_dir = file_util::JoinPath(temp_dir, "sub");
const std::string& sub_dir = client::file_util::JoinPath(temp_dir, "sub");
EXPECT_TRUE(CefCreateDirectory(sub_dir));
WriteFile(file_util::JoinPath(sub_dir, kFile3),
WriteFile(client::file_util::JoinPath(sub_dir, kFile3),
CreateContents(success3_message));
state.manager_->AddDirectoryProvider(kUrlBase, temp_dir, 0, std::string());
@@ -742,21 +743,21 @@ TEST(ResourceManagerTest, ArchiveProvider) {
const std::string& temp_dir = scoped_dir.GetPath();
// Write the files to disk.
const std::string& file_dir = file_util::JoinPath(temp_dir, "files");
const std::string& file_dir = client::file_util::JoinPath(temp_dir, "files");
EXPECT_TRUE(CefCreateDirectory(file_dir));
WriteFile(file_util::JoinPath(file_dir, kFile1),
WriteFile(client::file_util::JoinPath(file_dir, kFile1),
CreateContents(success1_message));
WriteFile(file_util::JoinPath(file_dir, kFile2),
WriteFile(client::file_util::JoinPath(file_dir, kFile2),
CreateContents(success2_message));
// Also include a subdirectory.
const std::string& sub_dir = file_util::JoinPath(file_dir, "sub");
const std::string& sub_dir = client::file_util::JoinPath(file_dir, "sub");
EXPECT_TRUE(CefCreateDirectory(sub_dir));
WriteFile(file_util::JoinPath(sub_dir, kFile3),
WriteFile(client::file_util::JoinPath(sub_dir, kFile3),
CreateContents(success3_message));
const std::string& archive_path =
file_util::JoinPath(temp_dir, "archive.zip");
client::file_util::JoinPath(temp_dir, "archive.zip");
// Create the archive file.
EXPECT_TRUE(CefZipDirectory(file_dir, archive_path, false));

View File

@@ -201,6 +201,8 @@ int main(int argc, char* argv[]) {
// Shut down CEF.
CefShutdown();
test_suite.DeleteTempDirectories();
// Destroy the MessageLoop.
message_loop.reset(nullptr);

View File

@@ -161,7 +161,8 @@ class TestSchemeHandler : public TestHandler {
#if defined(OS_LINUX)
// CustomStandardXHR* tests are flaky on Linux, sometimes returning
// ERR_ABORTED. Make the tests less flaky by also accepting that value.
if (!(test_results_->expected_error_code == 0 && errorCode == ERR_ABORTED)) {
if (!(test_results_->expected_error_code == 0 &&
errorCode == ERR_ABORTED)) {
EXPECT_EQ(test_results_->expected_error_code, errorCode);
}
#else

View File

@@ -4,6 +4,7 @@
#include "tests/ceftests/test_suite.h"
#include "include/cef_file_util.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/common/client_switches.h"
@@ -162,6 +163,19 @@ bool CefTestSuite::GetCachePath(std::string& path) const {
return false;
}
void CefTestSuite::RegisterTempDirectory(const CefString& directory) {
base::AutoLock lock_scope(temp_directories_lock_);
temp_directories_.push_back(directory);
}
void CefTestSuite::DeleteTempDirectories() {
base::AutoLock lock_scope(temp_directories_lock_);
for (size_t i = 0U; i < temp_directories_.size(); ++i) {
CefDeleteFile(temp_directories_[i], true);
}
temp_directories_.clear();
}
void CefTestSuite::PreInitialize() {
#if defined(OS_WIN)
testing::GTEST_FLAG(catch_exceptions) = false;

View File

@@ -7,6 +7,7 @@
#pragma once
#include <string>
#include <vector>
#include "include/cef_command_line.h"
#include "include/wrapper/cef_helpers.h"
@@ -26,6 +27,12 @@ class CefTestSuite {
void GetSettings(CefSettings& settings) const;
bool GetCachePath(std::string& path) const;
// Register a temp directory that should be deleted on shutdown.
void RegisterTempDirectory(const CefString& directory);
// Called after shutdown to delete any registered temp directories.
void DeleteTempDirectories();
CefRefPtr<CefCommandLine> command_line() const { return command_line_; }
// The return value from Run().
@@ -40,6 +47,9 @@ class CefTestSuite {
CefScopedArgArray argv_;
CefRefPtr<CefCommandLine> command_line_;
std::vector<CefString> temp_directories_;
base::Lock temp_directories_lock_;
int retval_;
};

View File

@@ -8,9 +8,9 @@
#include "include/cef_trace.h"
#include "include/cef_waitable_event.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/file_util.h"
#include "tests/ceftests/test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/file_util.h"
// Use the CEF version of the TRACE_* macros instead of the Chromium version.
#undef USING_CHROMIUM_INCLUDES
@@ -81,7 +81,7 @@ class TracingTestHandler : public CefEndTracingCallback,
void ReadTracingFile(const std::string& file_path) {
EXPECT_FILE_THREAD();
EXPECT_TRUE(file_util::ReadFileToString(file_path, &trace_data_));
EXPECT_TRUE(client::file_util::ReadFileToString(file_path, &trace_data_));
EXPECT_TRUE(CefDeleteFile(file_path, false));
completion_event_->Signal();

View File

@@ -12,11 +12,11 @@
#include "include/cef_waitable_event.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/file_util.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_suite.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/file_util.h"
#include "tests/shared/renderer/client_app_renderer.h"
using client::ClientAppRenderer;
@@ -777,9 +777,10 @@ class RequestTestRunner : public base::RefCountedThreadSafe<RequestTestRunner> {
EXPECT_TRUE(post_file_tmpdir_.CreateUniqueTempDir());
const std::string& path =
file_util::JoinPath(post_file_tmpdir_.GetPath(), "example.txt");
client::file_util::JoinPath(post_file_tmpdir_.GetPath(), "example.txt");
const char content[] = "HELLO FRIEND!";
int write_ct = file_util::WriteFile(path, content, sizeof(content) - 1);
int write_ct =
client::file_util::WriteFile(path, content, sizeof(content) - 1);
EXPECT_EQ(static_cast<int>(sizeof(content) - 1), write_ct);
SetUploadFile(settings_.request, path);

View File

@@ -10,6 +10,7 @@
#include "include/views/cef_menu_button_delegate.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/image_util.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/thread_helper.h"
#include "tests/ceftests/views/test_window_delegate.h"
#include "tests/gtest/include/gtest/gtest.h"
@@ -98,8 +99,10 @@ class EmptyMenuButtonDelegate : public CefMenuButtonDelegate {
public:
EmptyMenuButtonDelegate() {}
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 {
EXPECT_TRUE(false); // Not reached.
}
@@ -377,8 +380,10 @@ class TestMenuButtonDelegate : public CefMenuButtonDelegate,
public:
TestMenuButtonDelegate() {}
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 {
window_ = menu_button->GetWindow();
CefRefPtr<CefMenuModel> model = CefMenuModel::CreateMenuModel(this);
@@ -752,3 +757,123 @@ BUTTON_TEST_ASYNC(
BUTTON_TEST_ASYNC(MenuButtonClickFramelessNoTextNoMarkerNoImageFramelessWindow);
BUTTON_TEST_ASYNC(
MenuButtonClickFramelessNoTextWithMarkerNoImageFramelessWindow);
namespace {
class TestMenuButtonCustomPopupDelegate : public CefMenuButtonDelegate,
public CefWindowDelegate {
public:
explicit TestMenuButtonCustomPopupDelegate(bool can_activate)
: can_activate_(can_activate) {}
void OnMenuButtonPressed(
CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point,
CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock) override {
parent_window_ = menu_button->GetWindow();
button_pressed_lock_ = button_pressed_lock;
popup_window_ = CefWindow::CreateTopLevelWindow(this);
popup_window_->SetBounds(CefRect(screen_point.x, screen_point.y, 100, 100));
CefRefPtr<CefLabelButton> button =
CefLabelButton::CreateLabelButton(this, "Button", true);
button->SetFocusable(can_activate_);
popup_window_->AddChildView(button);
popup_window_->Show();
// Wait a bit before trying to click the popup button.
CefPostDelayedTask(TID_UI, base::Bind(ClickMenuItem, menu_button),
kClickDelayMS);
}
void OnButtonPressed(CefRefPtr<CefButton> button) override {
EXPECT_TRUE(button->GetWindow()->IsSame(popup_window_));
popup_window_->Close();
popup_window_ = nullptr;
button_pressed_lock_ = nullptr;
}
CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
bool* is_menu,
bool* can_activate_menu) override {
EXPECT_TRUE(parent_window_);
*is_menu = true;
*can_activate_menu = can_activate_;
return parent_window_;
}
bool IsFrameless(CefRefPtr<CefWindow> window) override {
return true;
}
void OnFocus(CefRefPtr<CefView> view) override {
if (popup_window_ && view->GetWindow()->IsSame(popup_window_)) {
EXPECT_TRUE(can_activate_);
got_focus_.yes();
}
}
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
if (can_activate_)
EXPECT_TRUE(got_focus_);
else
EXPECT_FALSE(got_focus_);
// Complete the test by closing the parent window.
parent_window_->Close();
parent_window_ = nullptr;
}
private:
const bool can_activate_;
CefRefPtr<CefWindow> parent_window_;
CefRefPtr<CefWindow> popup_window_;
CefRefPtr<CefMenuButtonPressedLock> button_pressed_lock_;
TrackCallback got_focus_;
IMPLEMENT_REFCOUNTING(TestMenuButtonCustomPopupDelegate);
DISALLOW_COPY_AND_ASSIGN(TestMenuButtonCustomPopupDelegate);
};
void RunMenuButtonCustomPopupClick(bool can_activate,
CefRefPtr<CefWindow> window) {
CefRefPtr<CefMenuButton> button = CefMenuButton::CreateMenuButton(
new TestMenuButtonCustomPopupDelegate(can_activate), "Custom", true,
false);
button->SetID(kButtonID);
window->AddChildView(button);
window->Layout();
window->Show();
// Wait a bit before trying to click the button.
CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID),
kClickDelayMS);
}
void MenuButtonCustomPopupClick(CefRefPtr<CefWaitableEvent> event,
bool can_activate) {
TestWindowDelegate::Config config;
config.on_window_created =
base::Bind(RunMenuButtonCustomPopupClick, can_activate);
config.close_window = false;
TestWindowDelegate::RunTest(event, config);
}
void MenuButtonCustomPopupActivateImpl(CefRefPtr<CefWaitableEvent> event) {
MenuButtonCustomPopupClick(event, true);
}
void MenuButtonCustomPopupNoActivateImpl(CefRefPtr<CefWaitableEvent> event) {
MenuButtonCustomPopupClick(event, false);
}
} // namespace
BUTTON_TEST_ASYNC(MenuButtonCustomPopupActivate);
BUTTON_TEST_ASYNC(MenuButtonCustomPopupNoActivate);

View File

@@ -166,6 +166,7 @@ WEBUI_TEST(appcache_internals);
WEBUI_TEST(accessibility);
WEBUI_TEST(blob_internals);
WEBUI_TEST(credits);
WEBUI_TEST(extensions_support);
WEBUI_TEST(gpu);
WEBUI_TEST(histograms);
WEBUI_TEST(indexeddb_internals);

View File

@@ -0,0 +1,239 @@
// 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/shared/browser/extension_util.h"
#include "include/base/cef_bind.h"
#include "include/cef_parser.h"
#include "include/cef_path_util.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/shared/browser/file_util.h"
#include "tests/shared/browser/resource_util.h"
namespace client {
namespace extension_util {
namespace {
std::string GetResourcesPath() {
CefString resources_dir;
if (CefGetPath(PK_DIR_RESOURCES, resources_dir) && !resources_dir.empty()) {
return resources_dir.ToString() + file_util::kPathSep;
}
return std::string();
}
// Internal extension paths may be prefixed with PK_DIR_RESOURCES and always
// use forward slash as path separator.
std::string GetInternalPath(const std::string& extension_path) {
const std::string& resources_path = GetResourcesPath();
std::string internal_path;
if (!resources_path.empty() && extension_path.find(resources_path) == 0U) {
internal_path = extension_path.substr(resources_path.size());
} else {
internal_path = extension_path;
}
#if defined(OS_WIN)
// Normalize path separators.
std::replace(internal_path.begin(), internal_path.end(), '\\', '/');
#endif
return internal_path;
}
typedef base::Callback<void(CefRefPtr<CefDictionaryValue> /*manifest*/)>
ManifestCallback;
void RunManifestCallback(const ManifestCallback& callback,
CefRefPtr<CefDictionaryValue> manifest) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the browser UI thread.
CefPostTask(TID_UI, base::Bind(RunManifestCallback, callback, manifest));
return;
}
callback.Run(manifest);
}
// Asynchronously reads the manifest and executes |callback| on the UI thread.
void GetInternalManifest(const std::string& extension_path,
const ManifestCallback& callback) {
if (!CefCurrentlyOn(TID_FILE)) {
// Execute on the browser FILE thread.
CefPostTask(TID_FILE,
base::Bind(GetInternalManifest, extension_path, callback));
return;
}
const std::string& manifest_path = GetInternalExtensionResourcePath(
file_util::JoinPath(extension_path, "manifest.json"));
std::string manifest_contents;
if (!LoadBinaryResource(manifest_path.c_str(), manifest_contents) ||
manifest_contents.empty()) {
LOG(ERROR) << "Failed to load manifest from " << manifest_path;
RunManifestCallback(callback, NULL);
return;
}
cef_json_parser_error_t error_code;
CefString error_msg;
CefRefPtr<CefValue> value = CefParseJSONAndReturnError(
manifest_contents, JSON_PARSER_RFC, error_code, error_msg);
if (!value || value->GetType() != VTYPE_DICTIONARY) {
if (error_msg.empty())
error_msg = "Incorrectly formatted dictionary contents.";
LOG(ERROR) << "Failed to parse manifest from " << manifest_path << "; "
<< error_msg.ToString();
RunManifestCallback(callback, NULL);
return;
}
RunManifestCallback(callback, value->GetDictionary());
}
void LoadExtensionWithManifest(CefRefPtr<CefRequestContext> request_context,
const std::string& extension_path,
CefRefPtr<CefExtensionHandler> handler,
CefRefPtr<CefDictionaryValue> manifest) {
CEF_REQUIRE_UI_THREAD();
// Load the extension internally. Resource requests will be handled via
// AddInternalExtensionToResourceManager.
request_context->LoadExtension(extension_path, manifest, handler);
}
} // namespace
bool IsInternalExtension(const std::string& extension_path) {
// List of internally handled extensions.
static const char* extensions[] = {"set_page_color"};
const std::string& internal_path = GetInternalPath(extension_path);
for (size_t i = 0; i < arraysize(extensions); ++i) {
// Exact match or first directory component.
const std::string& extension = extensions[i];
if (internal_path == extension ||
internal_path.find(extension + '/') == 0) {
return true;
}
}
return false;
}
std::string GetInternalExtensionResourcePath(
const std::string& extension_path) {
return "extensions/" + GetInternalPath(extension_path);
}
std::string GetExtensionResourcePath(const std::string& extension_path,
bool* internal) {
const bool is_internal = IsInternalExtension(extension_path);
if (internal)
*internal = is_internal;
if (is_internal)
return GetInternalExtensionResourcePath(extension_path);
return extension_path;
}
bool GetExtensionResourceContents(const std::string& extension_path,
std::string& contents) {
CEF_REQUIRE_FILE_THREAD();
if (IsInternalExtension(extension_path)) {
const std::string& contents_path =
GetInternalExtensionResourcePath(extension_path);
return LoadBinaryResource(contents_path.c_str(), contents);
}
return file_util::ReadFileToString(extension_path, &contents);
}
void LoadExtension(CefRefPtr<CefRequestContext> request_context,
const std::string& extension_path,
CefRefPtr<CefExtensionHandler> handler) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the browser UI thread.
CefPostTask(TID_UI, base::Bind(LoadExtension, request_context,
extension_path, handler));
return;
}
if (IsInternalExtension(extension_path)) {
// Read the extension manifest and load asynchronously.
GetInternalManifest(extension_path,
base::Bind(LoadExtensionWithManifest, request_context,
extension_path, handler));
} else {
// Load the extension from disk.
request_context->LoadExtension(extension_path, NULL, handler);
}
}
void AddInternalExtensionToResourceManager(
CefRefPtr<CefExtension> extension,
CefRefPtr<CefResourceManager> resource_manager) {
DCHECK(IsInternalExtension(extension->GetPath()));
if (!CefCurrentlyOn(TID_IO)) {
// Execute on the browser IO thread.
CefPostTask(TID_IO, base::Bind(AddInternalExtensionToResourceManager,
extension, resource_manager));
return;
}
const std::string& origin = GetExtensionOrigin(extension->GetIdentifier());
const std::string& resource_path =
GetInternalExtensionResourcePath(extension->GetPath());
// Add provider for bundled resource files.
#if defined(OS_WIN)
// Read resources from the binary.
resource_manager->AddProvider(
CreateBinaryResourceProvider(origin, resource_path), 50, std::string());
#elif defined(OS_POSIX)
// Read resources from a directory on disk.
std::string resource_dir;
if (GetResourceDir(resource_dir)) {
resource_dir += "/" + resource_path;
resource_manager->AddDirectoryProvider(origin, resource_dir, 50,
std::string());
}
#endif
}
std::string GetExtensionOrigin(const std::string& extension_id) {
return "chrome-extension://" + extension_id + "/";
}
std::string GetExtensionURL(CefRefPtr<CefExtension> extension) {
CefRefPtr<CefDictionaryValue> browser_action =
extension->GetManifest()->GetDictionary("browser_action");
if (browser_action) {
const std::string& default_popup =
browser_action->GetString("default_popup");
if (!default_popup.empty())
return GetExtensionOrigin(extension->GetIdentifier()) + default_popup;
}
return std::string();
}
std::string GetExtensionIconPath(CefRefPtr<CefExtension> extension,
bool* internal) {
CefRefPtr<CefDictionaryValue> browser_action =
extension->GetManifest()->GetDictionary("browser_action");
if (browser_action) {
const std::string& default_icon = browser_action->GetString("default_icon");
if (!default_icon.empty()) {
return GetExtensionResourcePath(
file_util::JoinPath(extension->GetPath(), default_icon), internal);
}
}
return std::string();
}
} // namespace extension_util
} // namespace client

View File

@@ -0,0 +1,80 @@
// 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_EXTENSION_UTIL_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_
#pragma once
#include <string>
#include "include/cef_extension.h"
#include "include/cef_extension_handler.h"
#include "include/wrapper/cef_resource_manager.h"
namespace client {
namespace extension_util {
// Returns true if |extension_path| can be handled internally via
// LoadBinaryResource. This checks a hard-coded list of allowed extension path
// components.
bool IsInternalExtension(const std::string& extension_path);
// Returns the path relative to the resource directory after removing the
// PK_DIR_RESOURCES prefix. This will be the relative path expected by
// LoadBinaryResource (uses '/' as path separator on all platforms). Only call
// this method for internal extensions, either when IsInternalExtension returns
// true or when the extension is handled internally through some means other
// than LoadBinaryResource. Use GetExtensionResourcePath instead if you are
// unsure whether the extension is internal or external.
std::string GetInternalExtensionResourcePath(const std::string& extension_path);
// Returns the resource path for |extension_path|. For external extensions this
// will be the full file path on disk. For internal extensions this will be the
// relative path expected by LoadBinaryResource (uses '/' as path separator on
// all platforms). Internal extensions must be on the hard-coded list enforced
// by IsInternalExtension. If |internal| is non-NULL it will be set to true if
// the extension is handled internally.
std::string GetExtensionResourcePath(const std::string& extension_path,
bool* internal);
// Read the contents of |extension_path| into |contents|. For external
// extensions this will read the file from disk. For internal extensions this
// will call LoadBinaryResource. Internal extensions must be on the hard-coded
// list enforced by IsInternalExtension. Returns true on success. Must be
// called on the FILE thread.
bool GetExtensionResourceContents(const std::string& extension_path,
std::string& contents);
// Load |extension_path| in |request_context|. May be an internal or external
// extension. Internal extensions must be on the hard-coded list enforced by
// IsInternalExtension.
void LoadExtension(CefRefPtr<CefRequestContext> request_context,
const std::string& extension_path,
CefRefPtr<CefExtensionHandler> handler);
// Register an internal handler for extension resources. Internal extensions
// must be on the hard-coded list enforced by IsInternalExtension.
void AddInternalExtensionToResourceManager(
CefRefPtr<CefExtension> extension,
CefRefPtr<CefResourceManager> resource_manager);
// Returns the URL origin for |extension_id|.
std::string GetExtensionOrigin(const std::string& extension_id);
// Parse browser_action manifest values as defined at
// https://developer.chrome.com/extensions/browserAction
// Look for a browser_action.default_popup manifest value.
std::string GetExtensionURL(CefRefPtr<CefExtension> extension);
// Look for a browser_action.default_icon manifest value and return the resource
// path. If |internal| is non-NULL it will be set to true if the extension is
// handled internally.
std::string GetExtensionIconPath(CefRefPtr<CefExtension> extension,
bool* internal);
} // namespace extension_util
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_EXTENSION_UTIL_H_

View File

@@ -2,7 +2,7 @@
// 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.
#include "tests/ceftests/file_util.h"
#include "tests/shared/browser/file_util.h"
#include "include/base/cef_build.h"
#include "include/base/cef_scoped_ptr.h"
@@ -12,6 +12,7 @@
#include <cstdio>
#include <memory>
namespace client {
namespace file_util {
namespace {
@@ -109,4 +110,12 @@ std::string JoinPath(const std::string& path1, const std::string& path2) {
return result;
}
std::string GetFileExtension(const std::string& path) {
size_t sep = path.find_last_of(".");
if (sep != std::string::npos)
return path.substr(sep + 1);
return std::string();
}
} // namespace file_util
} // namespace client

View File

@@ -2,13 +2,14 @@
// 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.
#ifndef CEF_TESTS_UNITTESTS_FILE_UTIL_H_
#define CEF_TESTS_UNITTESTS_FILE_UTIL_H_
#ifndef CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_
#define CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_
#pragma once
#include <limits>
#include <string>
namespace client {
namespace file_util {
// Platform-specific path separator.
@@ -35,6 +36,10 @@ int WriteFile(const std::string& path, const char* data, int size);
// separator.
std::string JoinPath(const std::string& path1, const std::string& path2);
} // namespace file_util
// Extracts the file extension from |path|.
std::string GetFileExtension(const std::string& path);
#endif // CEF_TESTS_UNITTESTS_FILE_UTIL_H_
} // namespace file_util
} // namespace client
#endif // CEF_TESTS_SHARED_BROWSER_FILE_UTIL_H_

View File

@@ -1,32 +0,0 @@
// Copyright (c) 2013 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/shared/browser/resource_util.h"
#include <algorithm>
#include <string>
#include "include/base/cef_logging.h"
namespace client {
CefRefPtr<CefImage> LoadImageIcon(const char* icon_name) {
CefRefPtr<CefImage> icon = CefImage::CreateImage();
std::string image, resource;
resource = std::string(icon_name) + ".1x.png";
if (LoadBinaryResource(resource.c_str(), image))
icon->AddPNG(1.0f, image.c_str(), image.size());
resource = std::string(icon_name) + ".2x.png";
if (LoadBinaryResource(resource.c_str(), image))
icon->AddPNG(2.0f, image.c_str(), image.size());
// Icons must be 16 in DIP.
DCHECK_EQ(16U, std::max(icon->GetWidth(), icon->GetHeight()));
return icon;
}
} // namespace client

View File

@@ -30,15 +30,10 @@ CefRefPtr<CefStreamReader> GetBinaryResourceReader(const char* resource_name);
#if defined(OS_WIN)
// Create a new provider for loading binary resources.
CefResourceManager::Provider* CreateBinaryResourceProvider(
const std::string& url_path);
const std::string& url_path,
const std::string& resource_path_prefix);
#endif
// Load an image icon at different scale factors. The image representations are
// expected to be 16 DIP in size. For example, if |icon_name| is "image" then
// the expected file names are "image.1x.png" for the 1x scale image (16 pixels)
// and "image.2x.png" for the 2x scale image (32 pixels).
CefRefPtr<CefImage> LoadImageIcon(const char* icon_name);
} // namespace client
#endif // CEF_TESTS_SHARED_BROWSER_RESOURCE_UTIL_H_

View File

@@ -33,9 +33,14 @@ bool LoadBinaryResource(int binaryId, DWORD& dwSize, LPBYTE& pBytes) {
// Provider of binary resources.
class BinaryResourceProvider : public CefResourceManager::Provider {
public:
explicit BinaryResourceProvider(const std::string& url_path)
: url_path_(url_path) {
BinaryResourceProvider(const std::string& url_path,
const std::string& resource_path_prefix)
: url_path_(url_path), resource_path_prefix_(resource_path_prefix) {
DCHECK(!url_path.empty());
if (!resource_path_prefix_.empty() &&
resource_path_prefix_[resource_path_prefix_.length() - 1] != '/') {
resource_path_prefix_ += "/";
}
}
bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
@@ -49,8 +54,11 @@ class BinaryResourceProvider : public CefResourceManager::Provider {
CefRefPtr<CefResourceHandler> handler;
const std::string& relative_path = url.substr(url_path_.length());
std::string relative_path = url.substr(url_path_.length());
if (!relative_path.empty()) {
if (!resource_path_prefix_.empty())
relative_path = resource_path_prefix_ + relative_path;
CefRefPtr<CefStreamReader> stream =
GetBinaryResourceReader(relative_path.data());
if (stream.get()) {
@@ -65,6 +73,7 @@ class BinaryResourceProvider : public CefResourceManager::Provider {
private:
std::string url_path_;
std::string resource_path_prefix_;
DISALLOW_COPY_AND_ASSIGN(BinaryResourceProvider);
};
@@ -109,8 +118,9 @@ CefRefPtr<CefStreamReader> GetBinaryResourceReader(const char* resource_name) {
}
CefResourceManager::Provider* CreateBinaryResourceProvider(
const std::string& url_path) {
return new BinaryResourceProvider(url_path);
const std::string& url_path,
const std::string& resource_path_prefix) {
return new BinaryResourceProvider(url_path, resource_path_prefix);
}
} // namespace client

View File

@@ -38,6 +38,7 @@ const char kHideTopMenu[] = "hide-top-menu";
const char kWidevineCdmPath[] = "widevine-cdm-path";
const char kSslClientCertificate[] = "ssl-client-certificate";
const char kCRLSetsPath[] = "crl-sets-path";
const char kLoadExtension[] = "load-extension";
} // namespace switches
} // namespace client

View File

@@ -32,6 +32,7 @@ extern const char kHideTopMenu[];
extern const char kWidevineCdmPath[];
extern const char kSslClientCertificate[];
extern const char kCRLSetsPath[];
extern const char kLoadExtension[];
} // namespace switches
} // namespace client