From 607a1d9f49739b59d8c303a5890e89c6c3e24053 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Thu, 28 Sep 2017 15:40:26 +0200 Subject: [PATCH] Add chrome.tabs.create API support (issue #1947) --- include/capi/cef_extension_handler_capi.h | 29 ++- include/cef_extension_handler.h | 27 +++ .../browser/extensions/api/tabs/tabs_api.cc | 37 ++++ libcef/browser/extensions/api/tabs/tabs_api.h | 13 ++ .../extensions/browser_extensions_util.cc | 2 +- .../extensions/browser_extensions_util.h | 2 +- .../extensions/chrome_api_registration.cc | 2 + .../extensions/extension_function_details.cc | 186 +++++++++++++++++- .../extensions/extension_function_details.h | 37 +++- libcef_dll/cpptoc/extension_handler_cpptoc.cc | 89 ++++++++- libcef_dll/ctocpp/extension_handler_ctocpp.cc | 60 +++++- libcef_dll/ctocpp/extension_handler_ctocpp.h | 11 +- .../extensions/chrome_tabs_unittest.cc | 172 ++++++++++++++++ 13 files changed, 658 insertions(+), 9 deletions(-) diff --git a/include/capi/cef_extension_handler_capi.h b/include/capi/cef_extension_handler_capi.h index b0328fa64..4d2625316 100644 --- a/include/capi/cef_extension_handler_capi.h +++ b/include/capi/cef_extension_handler_capi.h @@ -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_ @@ -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 diff --git a/include/cef_extension_handler.h b/include/cef_extension_handler.h index ab4e47276..7b0b86a36 100644 --- a/include/cef_extension_handler.h +++ b/include/cef_extension_handler.h @@ -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 extension, + CefRefPtr browser, + CefRefPtr active_browser, + int index, + const CefString& url, + bool active, + CefWindowInfo& windowInfo, + CefRefPtr& 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 diff --git a/libcef/browser/extensions/api/tabs/tabs_api.cc b/libcef/browser/extensions/api/tabs/tabs_api.cc index 94bbdc3f2..ac9c2495a 100644 --- a/libcef/browser/extensions/api/tabs/tabs_api.cc +++ b/libcef/browser/extensions/api/tabs/tabs_api.cc @@ -52,12 +52,49 @@ void ZoomModeToZoomSettings(zoom::ZoomController::ZoomMode zoom_mode, } } +template +void AssignOptionalValue(const std::unique_ptr& source, + std::unique_ptr& 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 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 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) {} diff --git a/libcef/browser/extensions/api/tabs/tabs_api.h b/libcef/browser/extensions/api/tabs/tabs_api.h index 3a3574918..e4c6a2eab 100644 --- a/libcef/browser/extensions/api/tabs/tabs_api.h +++ b/libcef/browser/extensions/api/tabs/tabs_api.h @@ -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: diff --git a/libcef/browser/extensions/browser_extensions_util.cc b/libcef/browser/extensions/browser_extensions_util.cc index 708a79170..f4741b4e2 100644 --- a/libcef/browser/extensions/browser_extensions_util.cc +++ b/libcef/browser/extensions/browser_extensions_util.cc @@ -137,7 +137,7 @@ CefRefPtr 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 = diff --git a/libcef/browser/extensions/browser_extensions_util.h b/libcef/browser/extensions/browser_extensions_util.h index 6bb8a7879..c4432234a 100644 --- a/libcef/browser/extensions/browser_extensions_util.h +++ b/libcef/browser/extensions/browser_extensions_util.h @@ -53,7 +53,7 @@ CefRefPtr 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 GetBrowserForTabId( int tab_id, diff --git a/libcef/browser/extensions/chrome_api_registration.cc b/libcef/browser/extensions/chrome_api_registration.cc index bd202e0c3..3dc677191 100644 --- a/libcef/browser/extensions/chrome_api_registration.cc +++ b/libcef/browser/extensions/chrome_api_registration.cc @@ -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), @@ -82,6 +83,7 @@ void ChromeFunctionRegistry::RegisterAll(ExtensionFunctionRegistry* registry) { registry->RegisterFunction(); registry->RegisterFunction(); registry->RegisterFunction(); + registry->RegisterFunction(); registry->RegisterFunction(); registry->RegisterFunction(); registry->RegisterFunction(); diff --git a/libcef/browser/extensions/extension_function_details.cc b/libcef/browser/extensions/extension_function_details.cc index ed4c704ec..5efc4a712 100644 --- a/libcef/browser/extensions/extension_function_details.cc +++ b/libcef/browser/extensions/extension_function_details.cc @@ -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" @@ -208,13 +213,12 @@ CefRefPtr 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 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)) { @@ -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 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 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 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 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 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 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 CefExtensionFunctionDetails::CreateTabObject( + CefRefPtr 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(); + tab_object->id = base::MakeUnique(new_browser->GetIdentifier()); + tab_object->index = index; + tab_object->window_id = *tab_object->id; + tab_object->status = base::MakeUnique( + 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(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(contents_size.width()); + tab_object->height = base::MakeUnique(contents_size.height()); + tab_object->url = base::MakeUnique(contents->GetURL().spec()); + tab_object->title = + base::MakeUnique(base::UTF16ToUTF8(contents->GetTitle())); + + content::NavigationEntry* entry = contents->GetController().GetVisibleEntry(); + if (entry && entry->GetFavicon().valid) { + tab_object->fav_icon_url = + base::MakeUnique(entry->GetFavicon().url.spec()); + } + + if (opener_browser_id >= 0) + tab_object->opener_tab_id = base::MakeUnique(opener_browser_id); + + return tab_object; +} + +// static +std::unique_ptr +CefExtensionFunctionDetails::CreateMutedInfo(content::WebContents* contents) { + DCHECK(contents); + std::unique_ptr info(new api::tabs::MutedInfo); + info->muted = contents->IsAudioMuted(); + // TODO(cef): Maybe populate |info->reason|. + return info; +} + CefRefPtr CefExtensionFunctionDetails::GetCefExtension() const { if (!cef_extension_) { cef_extension_ = diff --git a/libcef/browser/extensions/extension_function_details.h b/libcef/browser/extensions/extension_function_details.h index 82cc27a66..7304b170c 100644 --- a/libcef/browser/extensions/extension_function_details.h +++ b/libcef/browser/extensions/extension_function_details.h @@ -11,6 +11,7 @@ #include "base/callback_forward.h" #include "base/macros.h" +#include "chrome/common/extensions/api/tabs.h" #include "ui/gfx/native_widget_types.h" class Profile; @@ -64,7 +65,7 @@ class CefExtensionFunctionDetails { bool CanAccessBrowser(CefRefPtr target) const; // Returns the browser matching |tab_id| or NULL if the browser cannot be - // found or does not have a WebContents. If |tab_id| is -1 the "current" + // found or does not have a WebContents. If |tab_id| is < 0 the "current" // browser will be returned. |error_message| can optionally be passed in and // will be set with an appropriate message on error. This method should only // be called one time per extension function and will check all necessary @@ -89,6 +90,40 @@ class CefExtensionFunctionDetails { base::OnceCallback)>; bool LoadFile(const std::string& file, LoadFileCallback callback) const; + struct OpenTabParams { + OpenTabParams(); + ~OpenTabParams(); + + std::unique_ptr window_id; + std::unique_ptr opener_tab_id; + std::unique_ptr url; + std::unique_ptr active; + std::unique_ptr pinned; + std::unique_ptr index; + }; + + // Opens a new tab given creation parameters |params|. Returns a Tab object + // if successful, or NULL and optionally sets |error_message| if an error + // occurs. + base::DictionaryValue* OpenTab(const OpenTabParams& params, + bool user_gesture, + std::string* error_message) const; + + // Creates a Tab object (see chrome/common/extensions/api/tabs.json) with + // information about the state of a browser tab. Depending on the + // permissions of the extension, the object may or may not include sensitive + // data such as the tab's URL. + std::unique_ptr CreateTabObject( + CefRefPtr new_browser, + int opener_browser_id, + bool active, + int index) const; + + // Creates a tab MutedInfo object (see chrome/common/extensions/api/tabs.json) + // with information about the mute state of a browser tab. + static std::unique_ptr CreateMutedInfo( + content::WebContents* contents); + // Returns a pointer to the associated UIThreadExtensionFunction UIThreadExtensionFunction* function() { return function_; } const UIThreadExtensionFunction* function() const { return function_; } diff --git a/libcef_dll/cpptoc/extension_handler_cpptoc.cc b/libcef_dll/cpptoc/extension_handler_cpptoc.cc index 5e910b4fb..ab1c72790 100644 --- a/libcef_dll/cpptoc/extension_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/extension_handler_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=a4c5733b13d0513946314faf2ac0745c86419df7$ +// $hash=05b6fa7134b9e58dcef8262b0b726e4afd6e3512$ // #include "libcef_dll/cpptoc/extension_handler_cpptoc.h" @@ -133,6 +133,92 @@ int CEF_CALLBACK extension_handler_on_before_background_browser( return _retval; } +int CEF_CALLBACK +extension_handler_on_before_browser(struct _cef_extension_handler_t* self, + cef_extension_t* extension, + cef_browser_t* browser, + cef_browser_t* active_browser, + int index, + const cef_string_t* url, + int active, + cef_window_info_t* windowInfo, + cef_client_t** client, + struct _cef_browser_settings_t* settings) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: extension; type: refptr_diff + DCHECK(extension); + if (!extension) + return 0; + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) + return 0; + // Verify param: active_browser; type: refptr_diff + DCHECK(active_browser); + if (!active_browser) + return 0; + // Verify param: url; type: string_byref_const + DCHECK(url); + if (!url) + return 0; + // Verify param: windowInfo; type: struct_byref + DCHECK(windowInfo); + if (!windowInfo) + return 0; + // Verify param: client; type: refptr_same_byref + DCHECK(client); + if (!client) + return 0; + // Verify param: settings; type: struct_byref + DCHECK(settings); + if (!settings) + return 0; + + // Translate param: windowInfo; type: struct_byref + CefWindowInfo windowInfoObj; + if (windowInfo) + windowInfoObj.AttachTo(*windowInfo); + // Translate param: client; type: refptr_same_byref + CefRefPtr clientPtr; + if (client && *client) + clientPtr = CefClientCppToC::Unwrap(*client); + CefClient* clientOrig = clientPtr.get(); + // Translate param: settings; type: struct_byref + CefBrowserSettings settingsObj; + if (settings) + settingsObj.AttachTo(*settings); + + // Execute + bool _retval = CefExtensionHandlerCppToC::Get(self)->OnBeforeBrowser( + CefExtensionCToCpp::Wrap(extension), CefBrowserCToCpp::Wrap(browser), + CefBrowserCToCpp::Wrap(active_browser), index, CefString(url), + active ? true : false, windowInfoObj, clientPtr, settingsObj); + + // Restore param: windowInfo; type: struct_byref + if (windowInfo) + windowInfoObj.DetachTo(*windowInfo); + // Restore param: client; type: refptr_same_byref + if (client) { + if (clientPtr.get()) { + if (clientPtr.get() != clientOrig) { + *client = CefClientCppToC::Wrap(clientPtr); + } + } else { + *client = NULL; + } + } + // Restore param: settings; type: struct_byref + if (settings) + settingsObj.DetachTo(*settings); + + // Return type: bool + return _retval; +} + cef_browser_t* CEF_CALLBACK extension_handler_get_active_browser(struct _cef_extension_handler_t* self, cef_extension_t* extension, @@ -243,6 +329,7 @@ CefExtensionHandlerCppToC::CefExtensionHandlerCppToC() { GetStruct()->on_extension_unloaded = extension_handler_on_extension_unloaded; GetStruct()->on_before_background_browser = extension_handler_on_before_background_browser; + GetStruct()->on_before_browser = extension_handler_on_before_browser; GetStruct()->get_active_browser = extension_handler_get_active_browser; GetStruct()->can_access_browser = extension_handler_can_access_browser; GetStruct()->get_extension_resource = diff --git a/libcef_dll/ctocpp/extension_handler_ctocpp.cc b/libcef_dll/ctocpp/extension_handler_ctocpp.cc index c3f14f876..e1f2669f7 100644 --- a/libcef_dll/ctocpp/extension_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/extension_handler_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=b8f2ada19624a72741d86b5d6ff5214a4b711dd6$ +// $hash=0166cbfe3039f90a07fd2d9267178ad1fdb0d4f7$ // #include "libcef_dll/ctocpp/extension_handler_ctocpp.h" @@ -109,6 +109,64 @@ bool CefExtensionHandlerCToCpp::OnBeforeBackgroundBrowser( return _retval ? true : false; } +bool CefExtensionHandlerCToCpp::OnBeforeBrowser( + CefRefPtr extension, + CefRefPtr browser, + CefRefPtr active_browser, + int index, + const CefString& url, + bool active, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings) { + cef_extension_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_before_browser)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: extension; type: refptr_diff + DCHECK(extension.get()); + if (!extension.get()) + return false; + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) + return false; + // Verify param: active_browser; type: refptr_diff + DCHECK(active_browser.get()); + if (!active_browser.get()) + return false; + // Verify param: url; type: string_byref_const + DCHECK(!url.empty()); + if (url.empty()) + return false; + + // Translate param: client; type: refptr_same_byref + cef_client_t* clientStruct = NULL; + if (client.get()) + clientStruct = CefClientCToCpp::Unwrap(client); + cef_client_t* clientOrig = clientStruct; + + // Execute + int _retval = _struct->on_before_browser( + _struct, CefExtensionCppToC::Wrap(extension), + CefBrowserCppToC::Wrap(browser), CefBrowserCppToC::Wrap(active_browser), + index, url.GetStruct(), active, &windowInfo, &clientStruct, &settings); + + // Restore param:client; type: refptr_same_byref + if (clientStruct) { + if (clientStruct != clientOrig) { + client = CefClientCToCpp::Wrap(clientStruct); + } + } else { + client = NULL; + } + + // Return type: bool + return _retval ? true : false; +} + CefRefPtr CefExtensionHandlerCToCpp::GetActiveBrowser( CefRefPtr extension, CefRefPtr browser, diff --git a/libcef_dll/ctocpp/extension_handler_ctocpp.h b/libcef_dll/ctocpp/extension_handler_ctocpp.h index c27df7f2d..a36c5fe75 100644 --- a/libcef_dll/ctocpp/extension_handler_ctocpp.h +++ b/libcef_dll/ctocpp/extension_handler_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=369179037f2f6abc4d30e25099b52acf2f71e1e2$ +// $hash=0d7d8e2694c5828bb5506fbb5737819b60094428$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_EXTENSION_HANDLER_CTOCPP_H_ @@ -43,6 +43,15 @@ class CefExtensionHandlerCToCpp const CefString& url, CefRefPtr& client, CefBrowserSettings& settings) override; + bool OnBeforeBrowser(CefRefPtr extension, + CefRefPtr browser, + CefRefPtr active_browser, + int index, + const CefString& url, + bool active, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings) override; CefRefPtr GetActiveBrowser(CefRefPtr extension, CefRefPtr browser, bool include_incognito) override; diff --git a/tests/ceftests/extensions/chrome_tabs_unittest.cc b/tests/ceftests/extensions/chrome_tabs_unittest.cc index b7a8e72b4..0705b4ffe 100644 --- a/tests/ceftests/extensions/chrome_tabs_unittest.cc +++ b/tests/ceftests/extensions/chrome_tabs_unittest.cc @@ -6,6 +6,8 @@ #include +#include "include/cef_parser.h" + #include "tests/ceftests/test_util.h" #include "tests/shared/browser/extension_util.h" @@ -291,6 +293,7 @@ class TabsTestHandler : public ExtensionTestHandler { void set_expected_api_call_count(int val) { expected_api_call_count_ = val; } bool got_success_message() const { return got_success_message_; } + void set_got_success_message() { got_success_message_.yes(); } private: void CreateBrowserForExtensionIfReady() { @@ -361,6 +364,175 @@ class TabsTestHandler : public ExtensionTestHandler { int got_can_access_browser_count_; }; +// +// chrome.tabs.create tests. +// + +const char kCreateBrowserURL[] = + "https://test-extensions.com/chrome-tabs-create"; +const char kTabCallbackMessage[] = "tab-callback"; +const int kCreateTabIndex = 2; + +// Class for chrome.tabs.create tests. +class CreateTestHandler : public TabsTestHandler { + public: + explicit CreateTestHandler(RequestContextType request_context_type) + : TabsTestHandler(request_context_type) {} + + bool OnBeforeBrowser(CefRefPtr extension, + CefRefPtr browser, + CefRefPtr active_browser, + int index, + const CefString& url, + bool foreground, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings) override { + EXPECT_TRUE(extension->IsSame(this->extension())); + EXPECT_TRUE(browser->IsSame(extension_browser())); + EXPECT_TRUE(active_browser->IsSame(main_browser())); + EXPECT_EQ(kCreateTabIndex, index); + EXPECT_STREQ(kCreateBrowserURL, url.ToString().c_str()); + EXPECT_TRUE(foreground); + EXPECT_TRUE(client); + + EXPECT_FALSE(got_on_before_browser_); + got_on_before_browser_.yes(); + + return false; + } + + void OnAddMainBrowserResources() override { + AddResource(kCreateBrowserURL, GetCreatedBrowserHTML(), "text/html"); + + TabsTestHandler::OnAddMainBrowserResources(); + } + + void OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) override { + if (extension_browser() && main_browser()) { + if (isLoading) { + // Keep a reference to the newly created browser. + EXPECT_FALSE(created_browser_); + created_browser_ = browser; + return; + } else { + const std::string& url = browser->GetMainFrame()->GetURL(); + if (url == kCreateBrowserURL) { + EXPECT_TRUE(browser->IsSame(created_browser_)); + return; + } + } + } + + TabsTestHandler::OnLoadingStateChange(browser, isLoading, canGoBack, + canGoForward); + } + + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + const std::string& url = request->GetURL(); + if (url == kCreateBrowserURL) { + EXPECT_TRUE(browser->IsSame(created_browser_)); + EXPECT_FALSE(got_create_browser_url_request_); + got_create_browser_url_request_.yes(); + } + + return TabsTestHandler::GetResourceHandler(browser, frame, request); + } + + protected: + std::string GetTabsApiJS() const override { + std::stringstream ss; + ss << kCreateTabIndex; + + return "chrome.tabs.create({url: \"" + std::string(kCreateBrowserURL) + + "\", index: " + ss.str() + + "}, function(tab) { window.testQuery({request:'" + + kTabCallbackMessage + ":' + JSON.stringify(tab)}); });"; + } + + bool OnMessage(CefRefPtr browser, + const std::string& message) override { + if (message.find(kTabCallbackMessage) != std::string::npos) { + EXPECT_TRUE(browser->IsSame(extension_browser())); + EXPECT_FALSE(got_tab_callback_message_); + got_tab_callback_message_.yes(); + + // Verify the contents of the Tab object. + const std::string& json_str = + message.substr(strlen(kTabCallbackMessage) + 1); + CefRefPtr obj = CefParseJSON(json_str, JSON_PARSER_RFC); + EXPECT_TRUE(obj); + EXPECT_EQ(VTYPE_DICTIONARY, obj->GetType()); + CefRefPtr dict = obj->GetDictionary(); + + int index = dict->GetInt("index"); + EXPECT_EQ(kCreateTabIndex, index); + + int id = dict->GetInt("id"); + int windowId = dict->GetInt("windowId"); + EXPECT_EQ(created_browser_->GetIdentifier(), id); + EXPECT_EQ(created_browser_->GetIdentifier(), windowId); + + const std::string& url = dict->GetString("url"); + EXPECT_STREQ(kCreateBrowserURL, url.c_str()); + + TriggerDestroyTestIfReady(); + return true; + } else if (message == kSuccessMessage) { + // Overriding default kSuccessMessage handling. + EXPECT_TRUE(browser->IsSame(created_browser_)); + EXPECT_FALSE(got_success_message()); + set_got_success_message(); + TriggerDestroyTestIfReady(); + return true; + } + + return TabsTestHandler::OnMessage(browser, message); + } + + void OnDestroyTest() override { + created_browser_ = NULL; + + EXPECT_TRUE(got_on_before_browser_); + EXPECT_TRUE(got_create_browser_url_request_); + EXPECT_TRUE(got_tab_callback_message_); + + TabsTestHandler::OnDestroyTest(); + } + + private: + std::string GetCreatedBrowserHTML() { + return "Created"; + } + + void TriggerDestroyTestIfReady() { + if (got_tab_callback_message_ && got_success_message()) + TriggerDestroyTest(); + } + + CefRefPtr created_browser_; + + TrackCallback got_on_before_browser_; + TrackCallback got_create_browser_url_request_; + TrackCallback got_tab_callback_message_; + + IMPLEMENT_REFCOUNTING(CreateTestHandler); + DISALLOW_COPY_AND_ASSIGN(CreateTestHandler); +}; + +} // namespace + +TABS_TEST_GROUP_ALL(Create, CreateTestHandler); + +namespace { + // // chrome.tabs.executeScript tests. //