Skip to content

Commit

Permalink
Add chrome.tabs.create API support (issue chromiumembedded#1947)
Browse files Browse the repository at this point in the history
  • Loading branch information
magreenblatt committed Sep 28, 2017
1 parent 264fa66 commit 531f5a3
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 9 deletions.
29 changes: 28 additions & 1 deletion include/capi/cef_extension_handler_capi.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=b49f4c91db8eccdfe9ded503d8bb32ee0e433f60$
// $hash=b6311a69fc01fa19d3c49230f412a5963633ce27$
//

#ifndef CEF_INCLUDE_CAPI_CEF_EXTENSION_HANDLER_CAPI_H_
Expand Down Expand Up @@ -129,6 +129,33 @@ typedef struct _cef_extension_handler_t {
struct _cef_client_t** client,
struct _cef_browser_settings_t* settings);

///
// Called when an extension API (e.g. chrome.tabs.create) requests creation of
// a new browser. |extension| and |browser| are the source of the API call.
// |active_browser| may optionally be specified via the windowId property or
// returned via the get_active_browser() callback and provides the default
// |client| and |settings| values for the new browser. |index| is the position
// value optionally specified via the index property. |url| is the URL that
// will be loaded in the browser. |active| is true (1) if the new browser
// should be active when opened. To allow creation of the browser optionally
// modify |windowInfo|, |client| and |settings| and return false (0). To
// cancel creation of the browser return true (1). Successful creation will be
// indicated by a call to cef_life_span_handler_t::OnAfterCreated. Any
// modifications to |windowInfo| will be ignored if |active_browser| is
// wrapped in a cef_browser_view_t.
///
int(CEF_CALLBACK* on_before_browser)(
struct _cef_extension_handler_t* self,
struct _cef_extension_t* extension,
struct _cef_browser_t* browser,
struct _cef_browser_t* active_browser,
int index,
const cef_string_t* url,
int active,
struct _cef_window_info_t* windowInfo,
struct _cef_client_t** client,
struct _cef_browser_settings_t* settings);

///
// Called when no tabId is specified to an extension API call that accepts a
// tabId parameter (e.g. chrome.tabs.*). |extension| and |browser| are the
Expand Down
27 changes: 27 additions & 0 deletions include/cef_extension_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,33 @@ class CefExtensionHandler : public virtual CefBaseRefCounted {
return false;
}

///
// Called when an extension API (e.g. chrome.tabs.create) requests creation of
// a new browser. |extension| and |browser| are the source of the API call.
// |active_browser| may optionally be specified via the windowId property or
// returned via the GetActiveBrowser() callback and provides the default
// |client| and |settings| values for the new browser. |index| is the position
// value optionally specified via the index property. |url| is the URL that
// will be loaded in the browser. |active| is true if the new browser should
// be active when opened. To allow creation of the browser optionally modify
// |windowInfo|, |client| and |settings| and return false. To cancel creation
// of the browser return true. Successful creation will be indicated by a call
// to CefLifeSpanHandler::OnAfterCreated. Any modifications to |windowInfo|
// will be ignored if |active_browser| is wrapped in a CefBrowserView.
///
/*--cef()--*/
virtual bool OnBeforeBrowser(CefRefPtr<CefExtension> extension,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefBrowser> active_browser,
int index,
const CefString& url,
bool active,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings) {
return false;
}

///
// Called when no tabId is specified to an extension API call that accepts a
// tabId parameter (e.g. chrome.tabs.*). |extension| and |browser| are the
Expand Down
37 changes: 37 additions & 0 deletions libcef/browser/extensions/api/tabs/tabs_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,49 @@ void ZoomModeToZoomSettings(zoom::ZoomController::ZoomMode zoom_mode,
}
}

template <typename T>
void AssignOptionalValue(const std::unique_ptr<T>& source,
std::unique_ptr<T>& destination) {
if (source.get()) {
destination.reset(new T(*source));
}
}

} // namespace

ExtensionFunction::ResponseAction TabsGetFunction::Run() {
return RespondNow(Error(kNotImplementedError));
}

TabsCreateFunction::TabsCreateFunction() : cef_details_(this) {}

ExtensionFunction::ResponseAction TabsCreateFunction::Run() {
std::unique_ptr<tabs::Create::Params> params(
tabs::Create::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());

CefExtensionFunctionDetails::OpenTabParams options;
AssignOptionalValue(params->create_properties.window_id, options.window_id);
AssignOptionalValue(params->create_properties.opener_tab_id,
options.opener_tab_id);
AssignOptionalValue(params->create_properties.selected, options.active);
// The 'active' property has replaced the 'selected' property.
AssignOptionalValue(params->create_properties.active, options.active);
AssignOptionalValue(params->create_properties.pinned, options.pinned);
AssignOptionalValue(params->create_properties.index, options.index);
AssignOptionalValue(params->create_properties.url, options.url);

std::string error;
std::unique_ptr<base::DictionaryValue> result(
cef_details_.OpenTab(options, user_gesture(), &error));
if (!result)
return RespondNow(Error(error));

// Return data about the newly created tab.
return RespondNow(has_callback() ? OneArgument(std::move(result))
: NoArguments());
}

ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
: cef_details_(this), execute_tab_id_(-1) {}

Expand Down
13 changes: 13 additions & 0 deletions libcef/browser/extensions/api/tabs/tabs_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ class TabsGetFunction : public UIThreadExtensionFunction {
DECLARE_EXTENSION_FUNCTION("tabs.get", TABS_GET)
};

class TabsCreateFunction : public UIThreadExtensionFunction {
public:
TabsCreateFunction();
~TabsCreateFunction() override {}

ResponseAction Run() override;

DECLARE_EXTENSION_FUNCTION("tabs.create", TABS_CREATE)

private:
const CefExtensionFunctionDetails cef_details_;
};

// Implement API call tabs.executeScript and tabs.insertCSS.
class ExecuteCodeInTabFunction : public ExecuteCodeFunction {
public:
Expand Down
2 changes: 1 addition & 1 deletion libcef/browser/extensions/browser_extensions_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ CefRefPtr<CefBrowserHostImpl> GetBrowserForTabId(
content::BrowserContext* browser_context) {
CEF_REQUIRE_UIT();
DCHECK(browser_context);
if (tab_id == -1 || !browser_context)
if (tab_id < 0 || !browser_context)
return nullptr;

CefBrowserContextImpl* browser_context_impl =
Expand Down
2 changes: 1 addition & 1 deletion libcef/browser/extensions/browser_extensions_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ CefRefPtr<CefBrowserHostImpl> GetOwnerBrowserForHost(
bool* is_guest_view);

// Returns the browser matching |tab_id| and |browser_context|. Returns false if
// |tab_id| is -1 or a matching browser cannot be found within
// |tab_id| is < 0 or a matching browser cannot be found within
// |browser_context|. Similar in concept to ExtensionTabUtil::GetTabById.
CefRefPtr<CefBrowserHostImpl> GetBrowserForTabId(
int tab_id,
Expand Down
2 changes: 2 additions & 0 deletions libcef/browser/extensions/chrome_api_registration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const char* const kSupportedAPIs[] = {
EXTENSION_FUNCTION_NAME(StorageStorageAreaGetBytesInUseFunction),
"tabs",
EXTENSION_FUNCTION_NAME(cefimpl::TabsGetFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsCreateFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsExecuteScriptFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsInsertCSSFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsSetZoomFunction),
Expand Down Expand Up @@ -82,6 +83,7 @@ void ChromeFunctionRegistry::RegisterAll(ExtensionFunctionRegistry* registry) {
registry->RegisterFunction<cefimpl::TabsExecuteScriptFunction>();
registry->RegisterFunction<cefimpl::TabsInsertCSSFunction>();
registry->RegisterFunction<cefimpl::TabsGetFunction>();
registry->RegisterFunction<cefimpl::TabsCreateFunction>();
registry->RegisterFunction<cefimpl::TabsSetZoomFunction>();
registry->RegisterFunction<cefimpl::TabsGetZoomFunction>();
registry->RegisterFunction<cefimpl::TabsSetZoomSettingsFunction>();
Expand Down
186 changes: 184 additions & 2 deletions libcef/browser/extensions/extension_function_details.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
#include "libcef/browser/browser_context_impl.h"
#include "libcef/browser/extensions/browser_extensions_util.h"
#include "libcef/browser/extensions/extension_system.h"
#include "libcef/browser/navigate_params.h"
#include "libcef/browser/thread_util.h"

#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_entry.h"
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_function_dispatcher.h"
#include "extensions/common/error_utils.h"
Expand Down Expand Up @@ -208,13 +213,12 @@ CefRefPtr<CefBrowserHostImpl>
CefExtensionFunctionDetails::GetBrowserForTabIdFirstTime(
int tab_id,
std::string* error_message) const {
DCHECK_GE(tab_id, -1);
DCHECK(!get_browser_called_first_time_);
get_browser_called_first_time_ = true;

CefRefPtr<CefBrowserHostImpl> browser;

if (tab_id != -1) {
if (tab_id >= 0) {
// May be an invalid tabId or in the wrong BrowserContext.
browser = GetBrowserForTabId(tab_id, function_->browser_context());
if (!browser || !browser->web_contents() || !CanAccessBrowser(browser)) {
Expand Down Expand Up @@ -277,6 +281,184 @@ bool CefExtensionFunctionDetails::LoadFile(const std::string& file,
return false;
}

CefExtensionFunctionDetails::OpenTabParams::OpenTabParams() {}

CefExtensionFunctionDetails::OpenTabParams::~OpenTabParams() {}

base::DictionaryValue* CefExtensionFunctionDetails::OpenTab(
const OpenTabParams& params,
bool user_gesture,
std::string* error_message) const {
CefRefPtr<CefBrowserHostImpl> sender_browser = GetSenderBrowser();
if (!sender_browser)
return nullptr;

// windowId defaults to "current" window.
int window_id = extension_misc::kCurrentWindowId;
if (params.window_id.get())
window_id = *params.window_id;

// CEF doesn't have the concept of windows containing tab strips so we'll
// select an "active browser" for BrowserContext sharing instead.
CefRefPtr<CefBrowserHostImpl> active_browser =
GetBrowserForTabIdFirstTime(window_id, error_message);
if (!active_browser)
return nullptr;

// If an opener browser was specified then we expect it to exist.
int opener_browser_id = -1;
if (params.opener_tab_id.get() && *params.opener_tab_id >= 0) {
if (GetBrowserForTabIdAgain(*params.opener_tab_id, error_message)) {
opener_browser_id = *params.opener_tab_id;
} else {
return nullptr;
}
}

GURL url;
if (params.url.get()) {
std::string url_string = *params.url;
url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
function()->extension());
if (!url.is_valid()) {
if (error_message) {
*error_message =
ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string);
}
return nullptr;
}
}

// Don't let extensions crash the browser or renderers.
if (ExtensionTabUtil::IsKillURL(url)) {
if (error_message)
*error_message = keys::kNoCrashBrowserError;
return nullptr;
}

// Default to foreground for the new tab. The presence of 'active' property
// will override this default.
bool active = true;
if (params.active.get())
active = *params.active;

// CEF doesn't use the index value but we let the client see/modify it.
int index = 0;
if (params.index.get())
index = *params.index;

CefBrowserContextImpl* browser_context_impl =
CefBrowserContextImpl::GetForContext(active_browser->GetBrowserContext());

// A CEF representation should always exist.
CefRefPtr<CefExtension> cef_extension =
browser_context_impl->extension_system()->GetExtension(
function()->extension()->id());
DCHECK(cef_extension);
if (!cef_extension)
return nullptr;

// Always use the same request context that the extension was registered with.
// May represent an *Impl or *Proxy BrowserContext.
// GetLoaderContext() will return NULL for internal extensions.
CefRefPtr<CefRequestContext> request_context =
cef_extension->GetLoaderContext();
if (!request_context)
return nullptr;

CefBrowserHostImpl::CreateParams create_params;
create_params.url = url;
create_params.request_context = request_context;
create_params.window_info.reset(new CefWindowInfo);

#if defined(OS_WIN)
create_params.window_info->SetAsPopup(NULL, CefString());
#endif

// Start with the active browser's settings.
create_params.client = active_browser->GetClient();
create_params.settings = active_browser->settings();

CefRefPtr<CefExtensionHandler> handler = cef_extension->GetHandler();
if (handler.get() &&
handler->OnBeforeBrowser(cef_extension, sender_browser.get(),
active_browser.get(), index, url.spec(), active,
*create_params.window_info, create_params.client,
create_params.settings)) {
// Cancel the browser creation.
return nullptr;
}

if (active_browser->IsViewsHosted()) {
// The new browser will also be Views hosted.
create_params.window_info.reset();
}

// Browser creation may fail under certain rare circumstances.
CefRefPtr<CefBrowserHostImpl> new_browser =
CefBrowserHostImpl::Create(create_params);
if (!new_browser)
return nullptr;

// Return data about the newly created tab.
auto result = CreateTabObject(new_browser, opener_browser_id, active, index);
ExtensionTabUtil::ScrubTabForExtension(
function()->extension(), new_browser->web_contents(), result.get());
return result->ToValue().release();
}

std::unique_ptr<api::tabs::Tab> CefExtensionFunctionDetails::CreateTabObject(
CefRefPtr<CefBrowserHostImpl> new_browser,
int opener_browser_id,
bool active,
int index) const {
content::WebContents* contents = new_browser->web_contents();

bool is_loading = contents->IsLoading();
auto tab_object = base::MakeUnique<api::tabs::Tab>();
tab_object->id = base::MakeUnique<int>(new_browser->GetIdentifier());
tab_object->index = index;
tab_object->window_id = *tab_object->id;
tab_object->status = base::MakeUnique<std::string>(
is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete);
tab_object->active = active;
tab_object->selected = true;
tab_object->highlighted = true;
tab_object->pinned = false;
tab_object->audible = base::MakeUnique<bool>(contents->WasRecentlyAudible());
tab_object->discarded = false;
tab_object->auto_discardable = false;
tab_object->muted_info = CreateMutedInfo(contents);
tab_object->incognito = false;
gfx::Size contents_size = contents->GetContainerBounds().size();
tab_object->width = base::MakeUnique<int>(contents_size.width());
tab_object->height = base::MakeUnique<int>(contents_size.height());
tab_object->url = base::MakeUnique<std::string>(contents->GetURL().spec());
tab_object->title =
base::MakeUnique<std::string>(base::UTF16ToUTF8(contents->GetTitle()));

content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
if (entry && entry->GetFavicon().valid) {
tab_object->fav_icon_url =
base::MakeUnique<std::string>(entry->GetFavicon().url.spec());
}

if (opener_browser_id >= 0)
tab_object->opener_tab_id = base::MakeUnique<int>(opener_browser_id);

return tab_object;
}

// static
std::unique_ptr<api::tabs::MutedInfo>
CefExtensionFunctionDetails::CreateMutedInfo(content::WebContents* contents) {
DCHECK(contents);
std::unique_ptr<api::tabs::MutedInfo> info(new api::tabs::MutedInfo);
info->muted = contents->IsAudioMuted();
// TODO(cef): Maybe populate |info->reason|.
return info;
}

CefRefPtr<CefExtension> CefExtensionFunctionDetails::GetCefExtension() const {
if (!cef_extension_) {
cef_extension_ =
Expand Down
Loading

0 comments on commit 531f5a3

Please sign in to comment.