From 0df46d789e9e00e99c300f2203512f0c5986cf5e Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Thu, 9 Jan 2020 11:26:06 -0800 Subject: [PATCH 01/10] feat: [extensions] tabs.executeScript --- filenames.gni | 2 + shell/browser/api/atom_api_web_contents.cc | 2 + shell/browser/api/atom_api_web_contents.h | 8 + .../atom_extensions_browser_client.cc | 4 +- ...lectron_extensions_browser_api_provider.cc | 217 ++++++++++++++++++ ...electron_extensions_browser_api_provider.h | 27 +++ .../extensions/electron_messaging_delegate.cc | 12 + .../atom_extensions_api_provider.cc | 33 ++- 8 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 shell/browser/extensions/electron_extensions_browser_api_provider.cc create mode 100644 shell/browser/extensions/electron_extensions_browser_api_provider.h diff --git a/filenames.gni b/filenames.gni index 14547b7ca6cda..77c5b3633cd7a 100644 --- a/filenames.gni +++ b/filenames.gni @@ -613,6 +613,8 @@ filenames = { "shell/browser/extensions/electron_process_manager_delegate.h", "shell/browser/extensions/electron_extensions_api_client.cc", "shell/browser/extensions/electron_extensions_api_client.h", + "shell/browser/extensions/electron_extensions_browser_api_provider.cc", + "shell/browser/extensions/electron_extensions_browser_api_provider.h", "shell/browser/extensions/electron_messaging_delegate.cc", "shell/browser/extensions/electron_messaging_delegate.h", "shell/common/extensions/atom_extensions_api_provider.cc", diff --git a/shell/browser/api/atom_api_web_contents.cc b/shell/browser/api/atom_api_web_contents.cc index 3241b38e9b082..a0f50066c1725 100644 --- a/shell/browser/api/atom_api_web_contents.cc +++ b/shell/browser/api/atom_api_web_contents.cc @@ -361,6 +361,7 @@ WebContents::WebContents(v8::Isolate* isolate, #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) extensions::AtomExtensionWebContentsObserver::CreateForWebContents( web_contents); + script_executor_.reset(new extensions::ScriptExecutor(web_contents)); #endif registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser, base::Unretained(this))); @@ -521,6 +522,7 @@ void WebContents::InitWithSessionAndOptions( #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) extensions::AtomExtensionWebContentsObserver::CreateForWebContents( web_contents()); + script_executor_.reset(new extensions::ScriptExecutor(web_contents())); #endif registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser, diff --git a/shell/browser/api/atom_api_web_contents.h b/shell/browser/api/atom_api_web_contents.h index 952cfe6c75e19..5872ef64d7ca3 100644 --- a/shell/browser/api/atom_api_web_contents.h +++ b/shell/browser/api/atom_api_web_contents.h @@ -36,6 +36,8 @@ #include "shell/browser/printing/print_preview_message_handler.h" #endif +#include "extensions/browser/script_executor.h" + namespace blink { struct WebDeviceEmulationParams; } @@ -327,6 +329,10 @@ class WebContents : public gin_helper::TrackableObject, WebContents* embedder() { return embedder_; } + extensions::ScriptExecutor* script_executor() { + return script_executor_.get(); + } + protected: // Does not manage lifetime of |web_contents|. WebContents(v8::Isolate* isolate, content::WebContents* web_contents); @@ -537,6 +543,8 @@ class WebContents : public gin_helper::TrackableObject, std::unique_ptr guest_delegate_; std::unique_ptr frame_subscriber_; + std::unique_ptr script_executor_; + // The host webcontents that may contain this webcontents. WebContents* embedder_ = nullptr; diff --git a/shell/browser/extensions/atom_extensions_browser_client.cc b/shell/browser/extensions/atom_extensions_browser_client.cc index 8b249bdceba6a..b1ef761c837b9 100644 --- a/shell/browser/extensions/atom_extensions_browser_client.cc +++ b/shell/browser/extensions/atom_extensions_browser_client.cc @@ -36,6 +36,7 @@ #include "shell/browser/extensions/atom_extension_web_contents_observer.h" #include "shell/browser/extensions/atom_navigation_ui_data.h" #include "shell/browser/extensions/electron_extensions_api_client.h" +#include "shell/browser/extensions/electron_extensions_browser_api_provider.h" #include "shell/browser/extensions/electron_process_manager_delegate.h" using content::BrowserContext; @@ -53,7 +54,8 @@ AtomExtensionsBrowserClient::AtomExtensionsBrowserClient() AddAPIProvider( std::make_unique()); - // AddAPIProvider(std::make_unique()); + AddAPIProvider( + std::make_unique()); } AtomExtensionsBrowserClient::~AtomExtensionsBrowserClient() {} diff --git a/shell/browser/extensions/electron_extensions_browser_api_provider.cc b/shell/browser/extensions/electron_extensions_browser_api_provider.cc new file mode 100644 index 0000000000000..d58d278634744 --- /dev/null +++ b/shell/browser/extensions/electron_extensions_browser_api_provider.cc @@ -0,0 +1,217 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/extensions/electron_extensions_browser_api_provider.h" + +#include "extensions/browser/extension_function_registry.h" + +#include "shell/browser/api/atom_api_web_contents.h" + +////////////////////////////////////////// + +#include "extensions/browser/api/execute_code_function.h" +#include "extensions/browser/extension_function.h" + +namespace extensions { + +using api::extension_types::InjectDetails; + +// Implement API call tabs.executeScript and tabs.insertCSS. +class ExecuteCodeInTabFunction : public ExecuteCodeFunction { + public: + ExecuteCodeInTabFunction(); + + protected: + ~ExecuteCodeInTabFunction() override; + + // Initializes |execute_tab_id_| and |details_|. + InitResult Init() override; + bool CanExecuteScriptOnPage(std::string* error) override; + ScriptExecutor* GetScriptExecutor(std::string* error) override; + bool IsWebView() const override; + const GURL& GetWebViewSrc() const override; + + private: + // Id of tab which executes code. + int execute_tab_id_; +}; + +class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { + protected: + bool ShouldInsertCSS() const override; + + private: + ~TabsExecuteScriptFunction() override {} + + DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT) +}; + +ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() : execute_tab_id_(-1) {} + +ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} + +ExecuteCodeFunction::InitResult ExecuteCodeInTabFunction::Init() { + if (init_result_) + return init_result_.value(); + + // |tab_id| is optional so it's ok if it's not there. + int tab_id = -1; + if (args_->GetInteger(0, &tab_id) && tab_id < 0) + return set_init_result(VALIDATION_FAILURE); + LOG(INFO) << "tab_id = " << tab_id; + + // |details| are not optional. + base::DictionaryValue* details_value = NULL; + if (!args_->GetDictionary(1, &details_value)) + return set_init_result(VALIDATION_FAILURE); + LOG(INFO) << "details_value = " << details_value; + std::unique_ptr details(new InjectDetails()); + if (!InjectDetails::Populate(*details_value, details.get())) + return set_init_result(VALIDATION_FAILURE); + + // If the tab ID wasn't given then it needs to be converted to the + // currently active tab's ID. + if (tab_id == -1) { + /* + Browser* browser = chrome_details_.GetCurrentBrowser(); + // Can happen during shutdown. + if (!browser) + return set_init_result_error(tabs_constants::kNoCurrentWindowError); + content::WebContents* web_contents = NULL; + // Can happen during shutdown. + if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) + return set_init_result_error(tabs_constants::kNoTabInBrowserWindowError); + */ + // XXX: ??? + return set_init_result(VALIDATION_FAILURE); + } + + execute_tab_id_ = tab_id; + details_ = std::move(details); + set_host_id(HostID(HostID::EXTENSIONS, extension()->id())); + return set_init_result(SUCCESS); +} + +bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { + /* + content::WebContents* contents = nullptr; + + // If |tab_id| is specified, look for the tab. Otherwise default to selected + // tab in the current window. + CHECK_GE(execute_tab_id_, 0); + if (!GetTabById(execute_tab_id_, browser_context(), + include_incognito_information(), nullptr, nullptr, &contents, + nullptr, error)) { + return false; + } + + CHECK(contents); + + int frame_id = details_->frame_id ? *details_->frame_id + : ExtensionApiFrameIdMap::kTopFrameId; + content::RenderFrameHost* rfh = + ExtensionApiFrameIdMap::GetRenderFrameHostById(contents, frame_id); + if (!rfh) { + *error = ErrorUtils::FormatErrorMessage( + tabs_constants::kFrameNotFoundError, base::NumberToString(frame_id), + base::NumberToString(execute_tab_id_)); + return false; + } + + // Content scripts declared in manifest.json can access frames at about:-URLs + // if the extension has permission to access the frame's origin, so also allow + // programmatic content scripts at about:-URLs for allowed origins. + GURL effective_document_url(rfh->GetLastCommittedURL()); + bool is_about_url = effective_document_url.SchemeIs(url::kAboutScheme); + if (is_about_url && details_->match_about_blank && + *details_->match_about_blank) { + effective_document_url = GURL(rfh->GetLastCommittedOrigin().Serialize()); + } + + if (!effective_document_url.is_valid()) { + // Unknown URL, e.g. because no load was committed yet. Allow for now, the + // renderer will check again and fail the injection if needed. + return true; + } + + // NOTE: This can give the wrong answer due to race conditions, but it is OK, + // we check again in the renderer. + if (!extension()->permissions_data()->CanAccessPage(effective_document_url, + execute_tab_id_, error)) { + if (is_about_url && + extension()->permissions_data()->active_permissions().HasAPIPermission( + APIPermission::kTab)) { + *error = ErrorUtils::FormatErrorMessage( + manifest_errors::kCannotAccessAboutUrl, + rfh->GetLastCommittedURL().spec(), + rfh->GetLastCommittedOrigin().Serialize()); + } + return false; + } + + */ + return true; +} + +ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor( + std::string* error) { + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), execute_tab_id_); + if (!contents) + return nullptr; + return contents->script_executor(); + /* + Browser* browser = nullptr; + content::WebContents* contents = nullptr; + + bool success = GetTabById(execute_tab_id_, browser_context(), + include_incognito_information(), &browser, nullptr, + &contents, nullptr, error) && + contents && browser; + + if (!success) + return nullptr; + + return TabHelper::FromWebContents(contents)->script_executor(); + */ +} + +bool ExecuteCodeInTabFunction::IsWebView() const { + return false; +} + +const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const { + return GURL::EmptyGURL(); +} + +bool TabsExecuteScriptFunction::ShouldInsertCSS() const { + return false; +} + +} // namespace extensions + +////////////////////////////////////////// + +namespace extensions { + +ElectronExtensionsBrowserAPIProvider::ElectronExtensionsBrowserAPIProvider() = + default; +ElectronExtensionsBrowserAPIProvider::~ElectronExtensionsBrowserAPIProvider() = + default; + +void ElectronExtensionsBrowserAPIProvider::RegisterExtensionFunctions( + ExtensionFunctionRegistry* registry) { + registry->RegisterFunction(); + /* + // Preferences. + registry->RegisterFunction(); + registry->RegisterFunction(); + registry->RegisterFunction(); + + // Generated APIs from Electron. + api::ElectronGeneratedFunctionRegistry::RegisterAll(registry); + */ +} + +} // namespace extensions diff --git a/shell/browser/extensions/electron_extensions_browser_api_provider.h b/shell/browser/extensions/electron_extensions_browser_api_provider.h new file mode 100644 index 0000000000000..0f9affbacdb60 --- /dev/null +++ b/shell/browser/extensions/electron_extensions_browser_api_provider.h @@ -0,0 +1,27 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_API_PROVIDER_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_API_PROVIDER_H_ + +#include "base/macros.h" +#include "extensions/browser/extensions_browser_api_provider.h" + +namespace extensions { + +class ElectronExtensionsBrowserAPIProvider + : public ExtensionsBrowserAPIProvider { + public: + ElectronExtensionsBrowserAPIProvider(); + ~ElectronExtensionsBrowserAPIProvider() override; + + void RegisterExtensionFunctions(ExtensionFunctionRegistry* registry) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsBrowserAPIProvider); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_API_PROVIDER_H_ diff --git a/shell/browser/extensions/electron_messaging_delegate.cc b/shell/browser/extensions/electron_messaging_delegate.cc index 77df80c4799e6..14bed93684dc2 100644 --- a/shell/browser/extensions/electron_messaging_delegate.cc +++ b/shell/browser/extensions/electron_messaging_delegate.cc @@ -23,6 +23,8 @@ #include "ui/gfx/native_widget_types.h" #include "url/gurl.h" +#include "shell/browser/api/atom_api_web_contents.h" + namespace extensions { ElectronMessagingDelegate::ElectronMessagingDelegate() = default; @@ -39,6 +41,16 @@ ElectronMessagingDelegate::IsNativeMessagingHostAllowed( std::unique_ptr ElectronMessagingDelegate::MaybeGetTabInfo(content::WebContents* web_contents) { + if (web_contents) { + auto* api_contents = electron::api::WebContents::FromWrappedClass( + v8::Isolate::GetCurrent(), web_contents); + if (api_contents) { + auto tab = std::make_unique(); + tab->SetWithoutPathExpansion( + "id", std::make_unique(api_contents->ID())); + return tab; + } + } return nullptr; } diff --git a/shell/common/extensions/atom_extensions_api_provider.cc b/shell/common/extensions/atom_extensions_api_provider.cc index d345b3c26c9b8..83d5f75632504 100644 --- a/shell/common/extensions/atom_extensions_api_provider.cc +++ b/shell/common/extensions/atom_extensions_api_provider.cc @@ -12,7 +12,9 @@ #include "base/strings/utf_string_conversions.h" #include "electron/buildflags/buildflags.h" #include "extensions/common/alias.h" +#include "extensions/common/features/feature_provider.h" #include "extensions/common/features/json_feature_provider_source.h" +#include "extensions/common/features/simple_feature.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_handler.h" #include "extensions/common/manifest_handlers/permissions_parser.h" @@ -78,14 +80,21 @@ AtomExtensionsAPIProvider::~AtomExtensionsAPIProvider() = default; void AtomExtensionsAPIProvider::AddAPIFeatures( extensions::FeatureProvider* provider) { // AddShellAPIFeatures(provider); + + { + extensions::SimpleFeature* feature = new extensions::SimpleFeature(); + feature->set_name("tabs"); + // feature->set_channel(extensions::version_info::Channel::STABLE); + feature->set_contexts({extensions::Feature::BLESSED_EXTENSION_CONTEXT}); + feature->set_extension_types({extensions::Manifest::TYPE_EXTENSION}); + provider->AddFeature("tabs", feature); + } } void AtomExtensionsAPIProvider::AddManifestFeatures( extensions::FeatureProvider* provider) { -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) // TODO(samuelmaddock): why is the extensions namespace generated? extensions::AddAtomManifestFeatures(provider); -#endif } void AtomExtensionsAPIProvider::AddPermissionFeatures( @@ -104,12 +113,32 @@ void AtomExtensionsAPIProvider::AddAPIJSONSources( } bool AtomExtensionsAPIProvider::IsAPISchemaGenerated(const std::string& name) { + if (name == "tabs") { + return true; + } // return shell::api::ShellGeneratedSchemas::IsGenerated(name); return false; } base::StringPiece AtomExtensionsAPIProvider::GetAPISchema( const std::string& name) { + if (name == "tabs") { + return "{\"namespace\": \"tabs\", \"functions\": [{\"name\": " + "\"executeScript\", \"type\": \"function\", \"parameters\": " + "[{\"type\": \"integer\", \"name\": \"tabId\", \"minimum\": 0, " + "\"optional\": true, \"description\": \"The ID of the tab in which " + "to run the script; defaults to the active tab of the current " + "window.\"}, { \"$ref\": \"extensionTypes.InjectDetails\", " + "\"name\": \"details\", \"description\": \"Details of the script to " + "run. Either the code or the file property must be set, but both " + "may not be set at the same time.\" }, { \"type\": \"function\", " + "\"name\": \"callback\", \"optional\": true, \"description\": " + "\"Called after all the JavaScript has been executed.\", " + "\"parameters\": [ { \"name\": \"result\", \"optional\": true, " + "\"type\": \"array\", \"items\": {\"type\": \"any\", \"minimum\": " + "0}, \"description\": \"The result of the script in every injected " + "frame.\" } ] } ]}]}"; + } // return shell::api::ShellGeneratedSchemas::Get(name); return ""; } From fe71887e291bd0a64ae1b1d3dc218325dd7f6fb7 Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Thu, 9 Jan 2020 14:19:12 -0800 Subject: [PATCH 02/10] implement CanExecuteScriptOnPage --- shell/browser/api/atom_api_web_contents.h | 6 ++ ...lectron_extensions_browser_api_provider.cc | 59 ++++++------------- 2 files changed, 23 insertions(+), 42 deletions(-) diff --git a/shell/browser/api/atom_api_web_contents.h b/shell/browser/api/atom_api_web_contents.h index 5872ef64d7ca3..5fd45233ad088 100644 --- a/shell/browser/api/atom_api_web_contents.h +++ b/shell/browser/api/atom_api_web_contents.h @@ -36,7 +36,9 @@ #include "shell/browser/printing/print_preview_message_handler.h" #endif +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #include "extensions/browser/script_executor.h" +#endif namespace blink { struct WebDeviceEmulationParams; @@ -329,9 +331,11 @@ class WebContents : public gin_helper::TrackableObject, WebContents* embedder() { return embedder_; } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) extensions::ScriptExecutor* script_executor() { return script_executor_.get(); } +#endif protected: // Does not manage lifetime of |web_contents|. @@ -543,7 +547,9 @@ class WebContents : public gin_helper::TrackableObject, std::unique_ptr guest_delegate_; std::unique_ptr frame_subscriber_; +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) std::unique_ptr script_executor_; +#endif // The host webcontents that may contain this webcontents. WebContents* embedder_ = nullptr; diff --git a/shell/browser/extensions/electron_extensions_browser_api_provider.cc b/shell/browser/extensions/electron_extensions_browser_api_provider.cc index d58d278634744..7db2ad6d783fb 100644 --- a/shell/browser/extensions/electron_extensions_browser_api_provider.cc +++ b/shell/browser/extensions/electron_extensions_browser_api_provider.cc @@ -6,15 +6,21 @@ #include "extensions/browser/extension_function_registry.h" -#include "shell/browser/api/atom_api_web_contents.h" - ////////////////////////////////////////// +// TODO: Move to separate file #include "extensions/browser/api/execute_code_function.h" +#include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/browser/extension_function.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/permissions_data.h" +#include "shell/browser/api/atom_api_web_contents.h" namespace extensions { +const char kFrameNotFoundError[] = "No frame with id * in tab *."; + using api::extension_types::InjectDetails; // Implement API call tabs.executeScript and tabs.insertCSS. @@ -59,31 +65,19 @@ ExecuteCodeFunction::InitResult ExecuteCodeInTabFunction::Init() { int tab_id = -1; if (args_->GetInteger(0, &tab_id) && tab_id < 0) return set_init_result(VALIDATION_FAILURE); - LOG(INFO) << "tab_id = " << tab_id; // |details| are not optional. base::DictionaryValue* details_value = NULL; if (!args_->GetDictionary(1, &details_value)) return set_init_result(VALIDATION_FAILURE); - LOG(INFO) << "details_value = " << details_value; std::unique_ptr details(new InjectDetails()); if (!InjectDetails::Populate(*details_value, details.get())) return set_init_result(VALIDATION_FAILURE); - // If the tab ID wasn't given then it needs to be converted to the - // currently active tab's ID. if (tab_id == -1) { - /* - Browser* browser = chrome_details_.GetCurrentBrowser(); - // Can happen during shutdown. - if (!browser) - return set_init_result_error(tabs_constants::kNoCurrentWindowError); - content::WebContents* web_contents = NULL; - // Can happen during shutdown. - if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) - return set_init_result_error(tabs_constants::kNoTabInBrowserWindowError); - */ - // XXX: ??? + // There's no useful concept of a "default tab" in Electron. + // TODO(nornagon): we could potentially kick this to an event to allow the + // app to decide what "default tab" means for them? return set_init_result(VALIDATION_FAILURE); } @@ -94,27 +88,23 @@ ExecuteCodeFunction::InitResult ExecuteCodeInTabFunction::Init() { } bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { - /* - content::WebContents* contents = nullptr; - // If |tab_id| is specified, look for the tab. Otherwise default to selected // tab in the current window. CHECK_GE(execute_tab_id_, 0); - if (!GetTabById(execute_tab_id_, browser_context(), - include_incognito_information(), nullptr, nullptr, &contents, - nullptr, error)) { + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), execute_tab_id_); + if (!contents) { return false; } - CHECK(contents); - int frame_id = details_->frame_id ? *details_->frame_id : ExtensionApiFrameIdMap::kTopFrameId; content::RenderFrameHost* rfh = - ExtensionApiFrameIdMap::GetRenderFrameHostById(contents, frame_id); + ExtensionApiFrameIdMap::GetRenderFrameHostById(contents->web_contents(), + frame_id); if (!rfh) { *error = ErrorUtils::FormatErrorMessage( - tabs_constants::kFrameNotFoundError, base::NumberToString(frame_id), + kFrameNotFoundError, base::NumberToString(frame_id), base::NumberToString(execute_tab_id_)); return false; } @@ -150,7 +140,6 @@ bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { return false; } - */ return true; } @@ -161,20 +150,6 @@ ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor( if (!contents) return nullptr; return contents->script_executor(); - /* - Browser* browser = nullptr; - content::WebContents* contents = nullptr; - - bool success = GetTabById(execute_tab_id_, browser_context(), - include_incognito_information(), &browser, nullptr, - &contents, nullptr, error) && - contents && browser; - - if (!success) - return nullptr; - - return TabHelper::FromWebContents(contents)->script_executor(); - */ } bool ExecuteCodeInTabFunction::IsWebView() const { From fcd638456f504458745483b3ef1c2c461040b4a1 Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Tue, 14 Jan 2020 14:47:50 -0800 Subject: [PATCH 03/10] add test --- spec-main/extensions-spec.ts | 17 +++++++++++++++++ .../extensions/chrome-api/background.js | 2 ++ .../extensions/chrome-api/manifest.json | 3 +++ 3 files changed, 22 insertions(+) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 588dfcfa4557c..34b3fcf09a58f 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -121,6 +121,23 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex }) }) + describe('chrome.tabs', () => { + it('executeScript', async () => { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) + ;(customSession as any).loadExtension(path.join(fixtures, 'extensions', 'chrome-api')) + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) + await w.loadURL(url) + + const message = { method: 'executeScript', args: ['1 + 2'] } + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + + const [,, responseString] = await emittedOnce(w.webContents, 'console-message') + const response = JSON.parse(responseString) + + expect(response).to.equal(3) + }) + }) + describe('background pages', () => { it('loads a lazy background page when sending a message', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) diff --git a/spec-main/fixtures/extensions/chrome-api/background.js b/spec-main/fixtures/extensions/chrome-api/background.js index c86ff51ddb9dd..06d2a9d777960 100644 --- a/spec-main/fixtures/extensions/chrome-api/background.js +++ b/spec-main/fixtures/extensions/chrome-api/background.js @@ -17,4 +17,6 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { break } } + // Respond asynchronously + return true }) diff --git a/spec-main/fixtures/extensions/chrome-api/manifest.json b/spec-main/fixtures/extensions/chrome-api/manifest.json index fd4d88b910968..10e82e9ffd379 100644 --- a/spec-main/fixtures/extensions/chrome-api/manifest.json +++ b/spec-main/fixtures/extensions/chrome-api/manifest.json @@ -12,5 +12,8 @@ "scripts": ["background.js"], "persistent": false }, + "permissions": [ + "" + ], "manifest_version": 2 } From cc0ffe41cc4f6cb5e82ec6eab525de2785c739ed Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Tue, 14 Jan 2020 15:08:13 -0800 Subject: [PATCH 04/10] move executeScript impl to its own file --- filenames.gni | 2 + shell/browser/extensions/api/tabs/tabs_api.cc | 126 ++++++++++++++ shell/browser/extensions/api/tabs/tabs_api.h | 49 ++++++ ...lectron_extensions_browser_api_provider.cc | 163 +----------------- 4 files changed, 178 insertions(+), 162 deletions(-) create mode 100644 shell/browser/extensions/api/tabs/tabs_api.cc create mode 100644 shell/browser/extensions/api/tabs/tabs_api.h diff --git a/filenames.gni b/filenames.gni index 77c5b3633cd7a..a182ae0e84800 100644 --- a/filenames.gni +++ b/filenames.gni @@ -591,6 +591,8 @@ filenames = { lib_sources_extensions = [ "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc", "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h", + "shell/browser/extensions/api/tabs/tabs_api.cc", + "shell/browser/extensions/api/tabs/tabs_api.h", "shell/browser/extensions/atom_extensions_browser_client.cc", "shell/browser/extensions/atom_extensions_browser_client.h", "shell/browser/extensions/atom_browser_context_keyed_service_factories.cc", diff --git a/shell/browser/extensions/api/tabs/tabs_api.cc b/shell/browser/extensions/api/tabs/tabs_api.cc new file mode 100644 index 0000000000000..8c8aacfa05a9c --- /dev/null +++ b/shell/browser/extensions/api/tabs/tabs_api.cc @@ -0,0 +1,126 @@ +#include "shell/browser/extensions/api/tabs/tabs_api.h" + +#include "extensions/browser/extension_api_frame_id_map.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/permissions_data.h" +#include "shell/browser/api/atom_api_web_contents.h" + +namespace extensions { + +const char kFrameNotFoundError[] = "No frame with id * in tab *."; + +using api::extension_types::InjectDetails; + +ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() : execute_tab_id_(-1) {} + +ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} + +ExecuteCodeFunction::InitResult ExecuteCodeInTabFunction::Init() { + if (init_result_) + return init_result_.value(); + + // |tab_id| is optional so it's ok if it's not there. + int tab_id = -1; + if (args_->GetInteger(0, &tab_id) && tab_id < 0) + return set_init_result(VALIDATION_FAILURE); + + // |details| are not optional. + base::DictionaryValue* details_value = NULL; + if (!args_->GetDictionary(1, &details_value)) + return set_init_result(VALIDATION_FAILURE); + std::unique_ptr details(new InjectDetails()); + if (!InjectDetails::Populate(*details_value, details.get())) + return set_init_result(VALIDATION_FAILURE); + + if (tab_id == -1) { + // There's no useful concept of a "default tab" in Electron. + // TODO(nornagon): we could potentially kick this to an event to allow the + // app to decide what "default tab" means for them? + return set_init_result(VALIDATION_FAILURE); + } + + execute_tab_id_ = tab_id; + details_ = std::move(details); + set_host_id(HostID(HostID::EXTENSIONS, extension()->id())); + return set_init_result(SUCCESS); +} + +bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { + // If |tab_id| is specified, look for the tab. Otherwise default to selected + // tab in the current window. + CHECK_GE(execute_tab_id_, 0); + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), execute_tab_id_); + if (!contents) { + return false; + } + + int frame_id = details_->frame_id ? *details_->frame_id + : ExtensionApiFrameIdMap::kTopFrameId; + content::RenderFrameHost* rfh = + ExtensionApiFrameIdMap::GetRenderFrameHostById(contents->web_contents(), + frame_id); + if (!rfh) { + *error = ErrorUtils::FormatErrorMessage( + kFrameNotFoundError, base::NumberToString(frame_id), + base::NumberToString(execute_tab_id_)); + return false; + } + + // Content scripts declared in manifest.json can access frames at about:-URLs + // if the extension has permission to access the frame's origin, so also allow + // programmatic content scripts at about:-URLs for allowed origins. + GURL effective_document_url(rfh->GetLastCommittedURL()); + bool is_about_url = effective_document_url.SchemeIs(url::kAboutScheme); + if (is_about_url && details_->match_about_blank && + *details_->match_about_blank) { + effective_document_url = GURL(rfh->GetLastCommittedOrigin().Serialize()); + } + + if (!effective_document_url.is_valid()) { + // Unknown URL, e.g. because no load was committed yet. Allow for now, the + // renderer will check again and fail the injection if needed. + return true; + } + + // NOTE: This can give the wrong answer due to race conditions, but it is OK, + // we check again in the renderer. + if (!extension()->permissions_data()->CanAccessPage(effective_document_url, + execute_tab_id_, error)) { + if (is_about_url && + extension()->permissions_data()->active_permissions().HasAPIPermission( + APIPermission::kTab)) { + *error = ErrorUtils::FormatErrorMessage( + manifest_errors::kCannotAccessAboutUrl, + rfh->GetLastCommittedURL().spec(), + rfh->GetLastCommittedOrigin().Serialize()); + } + return false; + } + + return true; +} + +ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor( + std::string* error) { + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), execute_tab_id_); + if (!contents) + return nullptr; + return contents->script_executor(); +} + +bool ExecuteCodeInTabFunction::IsWebView() const { + return false; +} + +const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const { + return GURL::EmptyGURL(); +} + +bool TabsExecuteScriptFunction::ShouldInsertCSS() const { + return false; +} + +} // namespace extensions diff --git a/shell/browser/extensions/api/tabs/tabs_api.h b/shell/browser/extensions/api/tabs/tabs_api.h new file mode 100644 index 0000000000000..b1b139975aa78 --- /dev/null +++ b/shell/browser/extensions/api/tabs/tabs_api.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_ +#define SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_ + +#include + +#include "extensions/browser/api/execute_code_function.h" +#include "extensions/browser/extension_function.h" +#include "extensions/common/extension_resource.h" +#include "url/gurl.h" + +namespace extensions { + +// Implement API call tabs.executeScript and tabs.insertCSS. +class ExecuteCodeInTabFunction : public ExecuteCodeFunction { + public: + ExecuteCodeInTabFunction(); + + protected: + ~ExecuteCodeInTabFunction() override; + + // Initializes |execute_tab_id_| and |details_|. + InitResult Init() override; + bool CanExecuteScriptOnPage(std::string* error) override; + ScriptExecutor* GetScriptExecutor(std::string* error) override; + bool IsWebView() const override; + const GURL& GetWebViewSrc() const override; + + private: + // Id of tab which executes code. + int execute_tab_id_; +}; + +class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { + protected: + bool ShouldInsertCSS() const override; + + private: + ~TabsExecuteScriptFunction() override {} + + DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT) +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_API_TABS_API_H_ diff --git a/shell/browser/extensions/electron_extensions_browser_api_provider.cc b/shell/browser/extensions/electron_extensions_browser_api_provider.cc index 7db2ad6d783fb..97509496a2a2b 100644 --- a/shell/browser/extensions/electron_extensions_browser_api_provider.cc +++ b/shell/browser/extensions/electron_extensions_browser_api_provider.cc @@ -5,168 +5,7 @@ #include "shell/browser/extensions/electron_extensions_browser_api_provider.h" #include "extensions/browser/extension_function_registry.h" - -////////////////////////////////////////// -// TODO: Move to separate file - -#include "extensions/browser/api/execute_code_function.h" -#include "extensions/browser/extension_api_frame_id_map.h" -#include "extensions/browser/extension_function.h" -#include "extensions/common/error_utils.h" -#include "extensions/common/manifest_constants.h" -#include "extensions/common/permissions/permissions_data.h" -#include "shell/browser/api/atom_api_web_contents.h" - -namespace extensions { - -const char kFrameNotFoundError[] = "No frame with id * in tab *."; - -using api::extension_types::InjectDetails; - -// Implement API call tabs.executeScript and tabs.insertCSS. -class ExecuteCodeInTabFunction : public ExecuteCodeFunction { - public: - ExecuteCodeInTabFunction(); - - protected: - ~ExecuteCodeInTabFunction() override; - - // Initializes |execute_tab_id_| and |details_|. - InitResult Init() override; - bool CanExecuteScriptOnPage(std::string* error) override; - ScriptExecutor* GetScriptExecutor(std::string* error) override; - bool IsWebView() const override; - const GURL& GetWebViewSrc() const override; - - private: - // Id of tab which executes code. - int execute_tab_id_; -}; - -class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { - protected: - bool ShouldInsertCSS() const override; - - private: - ~TabsExecuteScriptFunction() override {} - - DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT) -}; - -ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() : execute_tab_id_(-1) {} - -ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} - -ExecuteCodeFunction::InitResult ExecuteCodeInTabFunction::Init() { - if (init_result_) - return init_result_.value(); - - // |tab_id| is optional so it's ok if it's not there. - int tab_id = -1; - if (args_->GetInteger(0, &tab_id) && tab_id < 0) - return set_init_result(VALIDATION_FAILURE); - - // |details| are not optional. - base::DictionaryValue* details_value = NULL; - if (!args_->GetDictionary(1, &details_value)) - return set_init_result(VALIDATION_FAILURE); - std::unique_ptr details(new InjectDetails()); - if (!InjectDetails::Populate(*details_value, details.get())) - return set_init_result(VALIDATION_FAILURE); - - if (tab_id == -1) { - // There's no useful concept of a "default tab" in Electron. - // TODO(nornagon): we could potentially kick this to an event to allow the - // app to decide what "default tab" means for them? - return set_init_result(VALIDATION_FAILURE); - } - - execute_tab_id_ = tab_id; - details_ = std::move(details); - set_host_id(HostID(HostID::EXTENSIONS, extension()->id())); - return set_init_result(SUCCESS); -} - -bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { - // If |tab_id| is specified, look for the tab. Otherwise default to selected - // tab in the current window. - CHECK_GE(execute_tab_id_, 0); - auto* contents = electron::api::WebContents::FromWeakMapID( - v8::Isolate::GetCurrent(), execute_tab_id_); - if (!contents) { - return false; - } - - int frame_id = details_->frame_id ? *details_->frame_id - : ExtensionApiFrameIdMap::kTopFrameId; - content::RenderFrameHost* rfh = - ExtensionApiFrameIdMap::GetRenderFrameHostById(contents->web_contents(), - frame_id); - if (!rfh) { - *error = ErrorUtils::FormatErrorMessage( - kFrameNotFoundError, base::NumberToString(frame_id), - base::NumberToString(execute_tab_id_)); - return false; - } - - // Content scripts declared in manifest.json can access frames at about:-URLs - // if the extension has permission to access the frame's origin, so also allow - // programmatic content scripts at about:-URLs for allowed origins. - GURL effective_document_url(rfh->GetLastCommittedURL()); - bool is_about_url = effective_document_url.SchemeIs(url::kAboutScheme); - if (is_about_url && details_->match_about_blank && - *details_->match_about_blank) { - effective_document_url = GURL(rfh->GetLastCommittedOrigin().Serialize()); - } - - if (!effective_document_url.is_valid()) { - // Unknown URL, e.g. because no load was committed yet. Allow for now, the - // renderer will check again and fail the injection if needed. - return true; - } - - // NOTE: This can give the wrong answer due to race conditions, but it is OK, - // we check again in the renderer. - if (!extension()->permissions_data()->CanAccessPage(effective_document_url, - execute_tab_id_, error)) { - if (is_about_url && - extension()->permissions_data()->active_permissions().HasAPIPermission( - APIPermission::kTab)) { - *error = ErrorUtils::FormatErrorMessage( - manifest_errors::kCannotAccessAboutUrl, - rfh->GetLastCommittedURL().spec(), - rfh->GetLastCommittedOrigin().Serialize()); - } - return false; - } - - return true; -} - -ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor( - std::string* error) { - auto* contents = electron::api::WebContents::FromWeakMapID( - v8::Isolate::GetCurrent(), execute_tab_id_); - if (!contents) - return nullptr; - return contents->script_executor(); -} - -bool ExecuteCodeInTabFunction::IsWebView() const { - return false; -} - -const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const { - return GURL::EmptyGURL(); -} - -bool TabsExecuteScriptFunction::ShouldInsertCSS() const { - return false; -} - -} // namespace extensions - -////////////////////////////////////////// +#include "shell/browser/extensions/api/tabs/tabs_api.h" namespace extensions { From 4a1a5c99cfce379385c11e1ef084d98f01a703e7 Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Tue, 14 Jan 2020 16:06:24 -0800 Subject: [PATCH 05/10] don't encode json in a string constant --- BUILD.gn | 3 +- ...lectron_extensions_browser_api_provider.cc | 5 -- shell/common/extensions/api/BUILD.gn | 51 ++++++++++++++++++- .../common/extensions/api/_api_features.json | 7 +++ shell/common/extensions/api/tabs.json | 43 ++++++++++++++++ .../atom_extensions_api_provider.cc | 41 +++------------ 6 files changed, 108 insertions(+), 42 deletions(-) create mode 100644 shell/common/extensions/api/_api_features.json create mode 100644 shell/common/extensions/api/tabs.json diff --git a/BUILD.gn b/BUILD.gn index 3eb77d4479225..61cb54170a495 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -664,7 +664,8 @@ source_set("electron_lib") { ] } - public_deps += [ "shell/common/extensions/api:extensions_features" ] + deps += [ "shell/common/extensions/api:extensions_features" ] + deps += [ "shell/common/extensions/api" ] deps += [ "//components/pref_registry", "//components/user_prefs", diff --git a/shell/browser/extensions/electron_extensions_browser_api_provider.cc b/shell/browser/extensions/electron_extensions_browser_api_provider.cc index 97509496a2a2b..f5c2f14fdf9d0 100644 --- a/shell/browser/extensions/electron_extensions_browser_api_provider.cc +++ b/shell/browser/extensions/electron_extensions_browser_api_provider.cc @@ -18,11 +18,6 @@ void ElectronExtensionsBrowserAPIProvider::RegisterExtensionFunctions( ExtensionFunctionRegistry* registry) { registry->RegisterFunction(); /* - // Preferences. - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - // Generated APIs from Electron. api::ElectronGeneratedFunctionRegistry::RegisterAll(registry); */ diff --git a/shell/common/extensions/api/BUILD.gn b/shell/common/extensions/api/BUILD.gn index 847a1521d16f4..0b54e4c59a5f1 100644 --- a/shell/common/extensions/api/BUILD.gn +++ b/shell/common/extensions/api/BUILD.gn @@ -11,8 +11,16 @@ assert(enable_extensions) ################################################################################ # Public Targets +group("api") { + public_deps = [ + ":generated_api_json_strings", + ":generated_api_types", + ] +} + group("extensions_features") { public_deps = [ + ":api_features", ":manifest_features", # TODO(devlin): It would be nicer to have this dependency hoisted up to @@ -25,11 +33,52 @@ group("extensions_features") { ################################################################################ # Private Targets +generated_json_strings("generated_api_json_strings") { + sources = [ + "tabs.json", + ] + + configs = [ "//build/config:precompiled_headers" ] + bundle_name = "Electron" + schema_include_rules = "extensions/common/api:extensions::api::%(namespace)s" + + root_namespace = "extensions::api::%(namespace)s" + deps = [ + "//extensions/common/api", + ] + + visibility = [ ":api" ] +} + +generated_types("generated_api_types") { + sources = [ + "tabs.json", + ] + configs = [ "//build/config:precompiled_headers" ] + schema_include_rules = "extensions/common/api:extensions::api::%(namespace)s" + + root_namespace = "extensions::api::%(namespace)s" + deps = [ + "//extensions/common/api", + ] + + visibility = [ ":api" ] +} + json_features("manifest_features") { feature_type = "ManifestFeature" - method_name = "AddAtomManifestFeatures" + method_name = "AddElectronManifestFeatures" sources = [ "_manifest_features.json", ] visibility = [ ":extensions_features" ] } + +json_features("api_features") { + feature_type = "APIFeature" + method_name = "AddElectronAPIFeatures" + sources = [ + "_api_features.json", + ] + visibility = [ ":extensions_features" ] +} diff --git a/shell/common/extensions/api/_api_features.json b/shell/common/extensions/api/_api_features.json new file mode 100644 index 0000000000000..e8a5c37f09d5a --- /dev/null +++ b/shell/common/extensions/api/_api_features.json @@ -0,0 +1,7 @@ +{ + "tabs": { + "channel": "stable", + "extension_types": ["extension"], + "contexts": ["blessed_extension"] + } +} diff --git a/shell/common/extensions/api/tabs.json b/shell/common/extensions/api/tabs.json new file mode 100644 index 0000000000000..1a5208277f408 --- /dev/null +++ b/shell/common/extensions/api/tabs.json @@ -0,0 +1,43 @@ +[ + { + "namespace": "tabs", + "functions": [ + { + "name": "executeScript", + "type": "function", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "optional": true, + "description": "The ID of the tab in which to run the script; defaults to the active tab of the current window." + }, + { + "$ref": "extensionTypes.InjectDetails", + "name": "details", + "description": "Details of the script to run. Either the code or the file property must be set, but both may not be set at the same time." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "description": "Called after all the JavaScript has been executed.", + "parameters": [ + { + "name": "result", + "optional": true, + "type": "array", + "items": { + "type": "any", + "minimum": 0 + }, + "description": "The result of the script in every injected frame." + } + ] + } + ] + } + ] + } +] diff --git a/shell/common/extensions/atom_extensions_api_provider.cc b/shell/common/extensions/atom_extensions_api_provider.cc index 83d5f75632504..5012c49b45262 100644 --- a/shell/common/extensions/atom_extensions_api_provider.cc +++ b/shell/common/extensions/atom_extensions_api_provider.cc @@ -11,6 +11,7 @@ #include "base/containers/span.h" #include "base/strings/utf_string_conversions.h" #include "electron/buildflags/buildflags.h" +#include "electron/shell/common/extensions/api/generated_schemas.h" #include "extensions/common/alias.h" #include "extensions/common/features/feature_provider.h" #include "extensions/common/features/json_feature_provider_source.h" @@ -20,6 +21,7 @@ #include "extensions/common/manifest_handlers/permissions_parser.h" #include "extensions/common/manifest_url_handlers.h" #include "extensions/common/permissions/permissions_info.h" +#include "shell/common/extensions/api/api_features.h" #include "shell/common/extensions/api/manifest_features.h" namespace extensions { @@ -79,22 +81,13 @@ AtomExtensionsAPIProvider::~AtomExtensionsAPIProvider() = default; void AtomExtensionsAPIProvider::AddAPIFeatures( extensions::FeatureProvider* provider) { - // AddShellAPIFeatures(provider); - - { - extensions::SimpleFeature* feature = new extensions::SimpleFeature(); - feature->set_name("tabs"); - // feature->set_channel(extensions::version_info::Channel::STABLE); - feature->set_contexts({extensions::Feature::BLESSED_EXTENSION_CONTEXT}); - feature->set_extension_types({extensions::Manifest::TYPE_EXTENSION}); - provider->AddFeature("tabs", feature); - } + extensions::AddElectronAPIFeatures(provider); } void AtomExtensionsAPIProvider::AddManifestFeatures( extensions::FeatureProvider* provider) { // TODO(samuelmaddock): why is the extensions namespace generated? - extensions::AddAtomManifestFeatures(provider); + extensions::AddElectronManifestFeatures(provider); } void AtomExtensionsAPIProvider::AddPermissionFeatures( @@ -113,34 +106,12 @@ void AtomExtensionsAPIProvider::AddAPIJSONSources( } bool AtomExtensionsAPIProvider::IsAPISchemaGenerated(const std::string& name) { - if (name == "tabs") { - return true; - } - // return shell::api::ShellGeneratedSchemas::IsGenerated(name); - return false; + return extensions::api::ElectronGeneratedSchemas::IsGenerated(name); } base::StringPiece AtomExtensionsAPIProvider::GetAPISchema( const std::string& name) { - if (name == "tabs") { - return "{\"namespace\": \"tabs\", \"functions\": [{\"name\": " - "\"executeScript\", \"type\": \"function\", \"parameters\": " - "[{\"type\": \"integer\", \"name\": \"tabId\", \"minimum\": 0, " - "\"optional\": true, \"description\": \"The ID of the tab in which " - "to run the script; defaults to the active tab of the current " - "window.\"}, { \"$ref\": \"extensionTypes.InjectDetails\", " - "\"name\": \"details\", \"description\": \"Details of the script to " - "run. Either the code or the file property must be set, but both " - "may not be set at the same time.\" }, { \"type\": \"function\", " - "\"name\": \"callback\", \"optional\": true, \"description\": " - "\"Called after all the JavaScript has been executed.\", " - "\"parameters\": [ { \"name\": \"result\", \"optional\": true, " - "\"type\": \"array\", \"items\": {\"type\": \"any\", \"minimum\": " - "0}, \"description\": \"The result of the script in every injected " - "frame.\" } ] } ]}]}"; - } - // return shell::api::ShellGeneratedSchemas::Get(name); - return ""; + return extensions::api::ElectronGeneratedSchemas::Get(name); } void AtomExtensionsAPIProvider::RegisterPermissions( From d150a0cc98cb50c557f43c1bb82d675f0cce426d Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Tue, 14 Jan 2020 16:33:27 -0800 Subject: [PATCH 06/10] remove some TODOs --- shell/common/extensions/atom_extensions_api_provider.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/shell/common/extensions/atom_extensions_api_provider.cc b/shell/common/extensions/atom_extensions_api_provider.cc index 5012c49b45262..042b5bf04b41b 100644 --- a/shell/common/extensions/atom_extensions_api_provider.cc +++ b/shell/common/extensions/atom_extensions_api_provider.cc @@ -77,8 +77,6 @@ namespace electron { AtomExtensionsAPIProvider::AtomExtensionsAPIProvider() = default; AtomExtensionsAPIProvider::~AtomExtensionsAPIProvider() = default; -// TODO(samuelmaddock): generate API features? - void AtomExtensionsAPIProvider::AddAPIFeatures( extensions::FeatureProvider* provider) { extensions::AddElectronAPIFeatures(provider); @@ -86,7 +84,6 @@ void AtomExtensionsAPIProvider::AddAPIFeatures( void AtomExtensionsAPIProvider::AddManifestFeatures( extensions::FeatureProvider* provider) { - // TODO(samuelmaddock): why is the extensions namespace generated? extensions::AddElectronManifestFeatures(provider); } From edcd97084efe1a366b0148c43a9a676d8d7ed4c9 Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Tue, 14 Jan 2020 17:07:14 -0800 Subject: [PATCH 07/10] lint --- shell/browser/extensions/api/tabs/tabs_api.cc | 7 +++++++ shell/browser/extensions/api/tabs/tabs_api.h | 6 +++--- .../extensions/electron_extensions_browser_api_provider.h | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/shell/browser/extensions/api/tabs/tabs_api.cc b/shell/browser/extensions/api/tabs/tabs_api.cc index 8c8aacfa05a9c..b35422f2b7aa5 100644 --- a/shell/browser/extensions/api/tabs/tabs_api.cc +++ b/shell/browser/extensions/api/tabs/tabs_api.cc @@ -1,5 +1,12 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + #include "shell/browser/extensions/api/tabs/tabs_api.h" +#include +#include + #include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/common/error_utils.h" #include "extensions/common/manifest_constants.h" diff --git a/shell/browser/extensions/api/tabs/tabs_api.h b/shell/browser/extensions/api/tabs/tabs_api.h index b1b139975aa78..1848907e17ff1 100644 --- a/shell/browser/extensions/api/tabs/tabs_api.h +++ b/shell/browser/extensions/api/tabs/tabs_api.h @@ -1,5 +1,5 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #ifndef SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_ @@ -46,4 +46,4 @@ class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_API_TABS_API_H_ +#endif // SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_ diff --git a/shell/browser/extensions/electron_extensions_browser_api_provider.h b/shell/browser/extensions/electron_extensions_browser_api_provider.h index 0f9affbacdb60..072011ffa16ca 100644 --- a/shell/browser/extensions/electron_extensions_browser_api_provider.h +++ b/shell/browser/extensions/electron_extensions_browser_api_provider.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_API_PROVIDER_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_API_PROVIDER_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_BROWSER_API_PROVIDER_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_BROWSER_API_PROVIDER_H_ #include "base/macros.h" #include "extensions/browser/extensions_browser_api_provider.h" @@ -24,4 +24,4 @@ class ElectronExtensionsBrowserAPIProvider } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_API_PROVIDER_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_BROWSER_API_PROVIDER_H_ From 0bf468c3b35ee82cb3f6239bd2ea6593c11624c7 Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Tue, 14 Jan 2020 18:54:23 -0800 Subject: [PATCH 08/10] tabs.sendMessage --- chromium_src/BUILD.gn | 7 + filenames.gni | 2 + .../extensions/electron_messaging_delegate.cc | 7 +- shell/common/extensions/api/tabs.json | 43 ++++ .../atom_extensions_renderer_client.cc | 4 +- ...electron_extensions_dispatcher_delegate.cc | 214 ++++++++++++++++++ .../electron_extensions_dispatcher_delegate.h | 36 +++ 7 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc create mode 100644 shell/renderer/extensions/electron_extensions_dispatcher_delegate.h diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn index 51767228f4ed8..ae76b4a3ac678 100644 --- a/chromium_src/BUILD.gn +++ b/chromium_src/BUILD.gn @@ -229,6 +229,13 @@ static_library("chrome") { "//components/vector_icons:vector_icons", ] } + + if (enable_electron_extensions) { + sources += [ + "//chrome/renderer/extensions/tabs_hooks_delegate.cc", + "//chrome/renderer/extensions/tabs_hooks_delegate.h", + ] + } } source_set("plugins") { diff --git a/filenames.gni b/filenames.gni index a182ae0e84800..b7a30005423bc 100644 --- a/filenames.gni +++ b/filenames.gni @@ -625,6 +625,8 @@ filenames = { "shell/common/extensions/atom_extensions_client.h", "shell/renderer/extensions/atom_extensions_renderer_client.cc", "shell/renderer/extensions/atom_extensions_renderer_client.h", + "shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc", + "shell/renderer/extensions/electron_extensions_dispatcher_delegate.h", ] app_sources = [ diff --git a/shell/browser/extensions/electron_messaging_delegate.cc b/shell/browser/extensions/electron_messaging_delegate.cc index 14bed93684dc2..ff0d864deed67 100644 --- a/shell/browser/extensions/electron_messaging_delegate.cc +++ b/shell/browser/extensions/electron_messaging_delegate.cc @@ -57,7 +57,12 @@ ElectronMessagingDelegate::MaybeGetTabInfo(content::WebContents* web_contents) { content::WebContents* ElectronMessagingDelegate::GetWebContentsByTabId( content::BrowserContext* browser_context, int tab_id) { - return nullptr; + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), tab_id); + if (!contents) { + return nullptr; + } + return contents->web_contents(); } std::unique_ptr ElectronMessagingDelegate::CreateReceiverForTab( diff --git a/shell/common/extensions/api/tabs.json b/shell/common/extensions/api/tabs.json index 1a5208277f408..15865bf83625b 100644 --- a/shell/common/extensions/api/tabs.json +++ b/shell/common/extensions/api/tabs.json @@ -37,6 +37,49 @@ ] } ] + }, + { + "name": "sendMessage", + "nocompile": true, + "type": "function", + "description": "Sends a single message to the content script(s) in the specified tab, with an optional callback to run when a response is sent back. The $(ref:runtime.onMessage) event is fired in each content script running in the specified tab for the current extension.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0 + }, + { + "type": "any", + "name": "message", + "description": "The message to send. This message should be a JSON-ifiable object." + }, + { + "type": "object", + "name": "options", + "properties": { + "frameId": { + "type": "integer", + "optional": true, + "minimum": 0, + "description": "Send a message to a specific frame identified by frameId instead of all frames in the tab." + } + }, + "optional": true + }, + { + "type": "function", + "name": "responseCallback", + "optional": true, + "parameters": [ + { + "name": "response", + "type": "any", + "description": "The JSON response object sent by the handler of the message. If an error occurs while connecting to the specified tab, the callback is called with no arguments and $(ref:runtime.lastError) is set to the error message." + } + ] + } + ] } ] } diff --git a/shell/renderer/extensions/atom_extensions_renderer_client.cc b/shell/renderer/extensions/atom_extensions_renderer_client.cc index 337268f9a85a2..0fc87bdce4873 100644 --- a/shell/renderer/extensions/atom_extensions_renderer_client.cc +++ b/shell/renderer/extensions/atom_extensions_renderer_client.cc @@ -6,13 +6,13 @@ #include "content/public/renderer/render_thread.h" #include "extensions/renderer/dispatcher.h" -#include "extensions/renderer/dispatcher_delegate.h" +#include "shell/renderer/extensions/electron_extensions_dispatcher_delegate.h" namespace electron { AtomExtensionsRendererClient::AtomExtensionsRendererClient() : dispatcher_(std::make_unique( - std::make_unique())) { + std::make_unique())) { dispatcher_->OnRenderThreadStarted(content::RenderThread::Get()); } diff --git a/shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc new file mode 100644 index 0000000000000..7909fd6c82adc --- /dev/null +++ b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc @@ -0,0 +1,214 @@ +// Copyright 2014 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 "shell/renderer/extensions/electron_extensions_dispatcher_delegate.h" + +#include "chrome/renderer/extensions/tabs_hooks_delegate.h" +#include "extensions/renderer/bindings/api_bindings_system.h" +#include "extensions/renderer/lazy_background_page_native_handler.h" +#include "extensions/renderer/module_system.h" +#include "extensions/renderer/native_extension_bindings_system.h" +#include "extensions/renderer/native_handler.h" + +#include + +using extensions::NativeHandler; + +ElectronExtensionsDispatcherDelegate::ElectronExtensionsDispatcherDelegate() {} + +ElectronExtensionsDispatcherDelegate::~ElectronExtensionsDispatcherDelegate() {} + +void ElectronExtensionsDispatcherDelegate::RegisterNativeHandlers( + extensions::Dispatcher* dispatcher, + extensions::ModuleSystem* module_system, + extensions::NativeExtensionBindingsSystem* bindings_system, + extensions::ScriptContext* context) { + // The following are native handlers that are defined in //extensions, but + // are only used for APIs defined in Chrome. + // TODO(devlin): We should clean this up. If an API is defined in Chrome, + // there's no reason to have its native handlers residing and being compiled + // in //extensions. + module_system->RegisterNativeHandler( + "lazy_background_page", + std::unique_ptr( + new extensions::LazyBackgroundPageNativeHandler(context))); +} + +void ElectronExtensionsDispatcherDelegate::PopulateSourceMap( + extensions::ResourceBundleSourceMap* source_map) { +#if 0 + // Custom bindings. + source_map->RegisterSource("action", IDR_ACTION_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("browserAction", + IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("declarativeContent", + IDR_DECLARATIVE_CONTENT_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("desktopCapture", + IDR_DESKTOP_CAPTURE_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("developerPrivate", + IDR_DEVELOPER_PRIVATE_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("downloads", IDR_DOWNLOADS_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("gcm", IDR_GCM_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("identity", IDR_IDENTITY_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("imageWriterPrivate", + IDR_IMAGE_WRITER_PRIVATE_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("input.ime", IDR_INPUT_IME_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("mediaGalleries", + IDR_MEDIA_GALLERIES_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("notifications", + IDR_NOTIFICATIONS_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("omnibox", IDR_OMNIBOX_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("pageAction", IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("pageCapture", + IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("syncFileSystem", + IDR_SYNC_FILE_SYSTEM_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("systemIndicator", + IDR_SYSTEM_INDICATOR_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("tabCapture", IDR_TAB_CAPTURE_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("tts", IDR_TTS_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("ttsEngine", IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS); + +#if defined(OS_CHROMEOS) + source_map->RegisterSource("certificateProvider", + IDR_CERTIFICATE_PROVIDER_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("enterprise.platformKeys", + IDR_ENTERPRISE_PLATFORM_KEYS_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("enterprise.platformKeys.internalAPI", + IDR_ENTERPRISE_PLATFORM_KEYS_INTERNAL_API_JS); + source_map->RegisterSource("enterprise.platformKeys.KeyPair", + IDR_ENTERPRISE_PLATFORM_KEYS_KEY_PAIR_JS); + source_map->RegisterSource("enterprise.platformKeys.SubtleCrypto", + IDR_ENTERPRISE_PLATFORM_KEYS_SUBTLE_CRYPTO_JS); + source_map->RegisterSource("enterprise.platformKeys.Token", + IDR_ENTERPRISE_PLATFORM_KEYS_TOKEN_JS); + source_map->RegisterSource("fileBrowserHandler", + IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("fileManagerPrivate", + IDR_FILE_MANAGER_PRIVATE_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("fileSystemProvider", + IDR_FILE_SYSTEM_PROVIDER_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("platformKeys", + IDR_PLATFORM_KEYS_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("platformKeys.getPublicKey", + IDR_PLATFORM_KEYS_GET_PUBLIC_KEY_JS); + source_map->RegisterSource("platformKeys.internalAPI", + IDR_PLATFORM_KEYS_INTERNAL_API_JS); + source_map->RegisterSource("platformKeys.Key", IDR_PLATFORM_KEYS_KEY_JS); + source_map->RegisterSource("platformKeys.SubtleCrypto", + IDR_PLATFORM_KEYS_SUBTLE_CRYPTO_JS); + source_map->RegisterSource("platformKeys.utils", IDR_PLATFORM_KEYS_UTILS_JS); + source_map->RegisterSource("terminalPrivate", + IDR_TERMINAL_PRIVATE_CUSTOM_BINDINGS_JS); + + // IME service on Chrome OS. + source_map->RegisterSource("chromeos.ime.mojom.input_engine.mojom", + IDR_IME_SERVICE_MOJOM_JS); + source_map->RegisterSource("chromeos.ime.service", + IDR_IME_SERVICE_BINDINGS_JS); +#endif // defined(OS_CHROMEOS) + + source_map->RegisterSource("cast.streaming.rtpStream", + IDR_CAST_STREAMING_RTP_STREAM_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("cast.streaming.session", + IDR_CAST_STREAMING_SESSION_CUSTOM_BINDINGS_JS); + source_map->RegisterSource( + "cast.streaming.udpTransport", + IDR_CAST_STREAMING_UDP_TRANSPORT_CUSTOM_BINDINGS_JS); + source_map->RegisterSource( + "cast.streaming.receiverSession", + IDR_CAST_STREAMING_RECEIVER_SESSION_CUSTOM_BINDINGS_JS); + source_map->RegisterSource( + "webrtcDesktopCapturePrivate", + IDR_WEBRTC_DESKTOP_CAPTURE_PRIVATE_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("webrtcLoggingPrivate", + IDR_WEBRTC_LOGGING_PRIVATE_CUSTOM_BINDINGS_JS); + + // Platform app sources that are not API-specific.. + source_map->RegisterSource("chromeWebViewInternal", + IDR_CHROME_WEB_VIEW_INTERNAL_CUSTOM_BINDINGS_JS); + source_map->RegisterSource("chromeWebView", IDR_CHROME_WEB_VIEW_JS); + + // Media router. + source_map->RegisterSource( + "chrome/common/media_router/mojom/media_controller.mojom", + IDR_MEDIA_CONTROLLER_MOJOM_JS); + source_map->RegisterSource( + "chrome/common/media_router/mojom/media_router.mojom", + IDR_MEDIA_ROUTER_MOJOM_JS); + source_map->RegisterSource( + "chrome/common/media_router/mojom/media_status.mojom", + IDR_MEDIA_STATUS_MOJOM_JS); + source_map->RegisterSource("media_router_bindings", + IDR_MEDIA_ROUTER_BINDINGS_JS); + source_map->RegisterSource("mojo/public/mojom/base/time.mojom", + IDR_MOJO_TIME_MOJOM_JS); + source_map->RegisterSource("mojo/public/mojom/base/unguessable_token.mojom", + IDR_MOJO_UNGUESSABLE_TOKEN_MOJOM_JS); + source_map->RegisterSource("net/interfaces/ip_address.mojom", + IDR_MOJO_IP_ADDRESS_MOJOM_JS); + source_map->RegisterSource("net/interfaces/ip_endpoint.mojom", + IDR_MOJO_IP_ENDPOINT_MOJOM_JS); + source_map->RegisterSource("url/mojom/origin.mojom", IDR_ORIGIN_MOJOM_JS); + source_map->RegisterSource("url/mojom/url.mojom", IDR_MOJO_URL_MOJOM_JS); + source_map->RegisterSource("media/mojo/mojom/remoting_common.mojom", + IDR_REMOTING_COMMON_JS); + source_map->RegisterSource( + "media/mojo/mojom/mirror_service_remoting.mojom", + IDR_MEDIA_REMOTING_JS); + source_map->RegisterSource( + "components/mirroring/mojom/mirroring_service_host.mojom", + IDR_MIRRORING_SERVICE_HOST_MOJOM_JS); + source_map->RegisterSource( + "components/mirroring/mojom/cast_message_channel.mojom", + IDR_MIRRORING_CAST_MESSAGE_CHANNEL_MOJOM_JS); + source_map->RegisterSource( + "components/mirroring/mojom/session_observer.mojom", + IDR_MIRRORING_SESSION_OBSERVER_MOJOM_JS); + source_map->RegisterSource( + "components/mirroring/mojom/session_parameters.mojom", + IDR_MIRRORING_SESSION_PARAMETERS_JS); +#endif +} + +void ElectronExtensionsDispatcherDelegate::RequireWebViewModules( + extensions::ScriptContext* context) { +#if 0 + DCHECK(context->GetAvailability("webViewInternal").is_available()); + context->module_system()->Require("chromeWebView"); +#endif +} + +void ElectronExtensionsDispatcherDelegate::OnActiveExtensionsUpdated( + const std::set& extension_ids) { +#if 0 + // In single-process mode, the browser process reports the active extensions. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + ::switches::kSingleProcess)) + return; + crash_keys::SetActiveExtensions(extension_ids); +#endif +} + +void ElectronExtensionsDispatcherDelegate::InitializeBindingsSystem( + extensions::Dispatcher* dispatcher, + extensions::NativeExtensionBindingsSystem* bindings_system) { + extensions::APIBindingsSystem* bindings = bindings_system->api_system(); +#if 0 + bindings->GetHooksForAPI("app")->SetDelegate( + std::make_unique( + dispatcher, bindings->request_handler())); + bindings->GetHooksForAPI("extension") + ->SetDelegate(std::make_unique( + bindings_system->messaging_service())); +#endif + bindings->GetHooksForAPI("tabs")->SetDelegate( + std::make_unique( + bindings_system->messaging_service())); +#if 0 + bindings->GetHooksForAPI("accessibilityPrivate") + ->SetDelegate( + std::make_unique()); +#endif +} diff --git a/shell/renderer/extensions/electron_extensions_dispatcher_delegate.h b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.h new file mode 100644 index 0000000000000..e2a39672c9cae --- /dev/null +++ b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.h @@ -0,0 +1,36 @@ +// Copyright 2014 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 CHROME_RENDERER_EXTENSIONS_CHROME_EXTENSIONS_DISPATCHER_DELEGATE_H_ +#define CHROME_RENDERER_EXTENSIONS_CHROME_EXTENSIONS_DISPATCHER_DELEGATE_H_ + +#include "base/macros.h" +#include "extensions/renderer/dispatcher_delegate.h" + +class ElectronExtensionsDispatcherDelegate + : public extensions::DispatcherDelegate { + public: + ElectronExtensionsDispatcherDelegate(); + ~ElectronExtensionsDispatcherDelegate() override; + + private: + // extensions::DispatcherDelegate implementation. + void RegisterNativeHandlers( + extensions::Dispatcher* dispatcher, + extensions::ModuleSystem* module_system, + extensions::NativeExtensionBindingsSystem* bindings_system, + extensions::ScriptContext* context) override; + void PopulateSourceMap( + extensions::ResourceBundleSourceMap* source_map) override; + void RequireWebViewModules(extensions::ScriptContext* context) override; + void OnActiveExtensionsUpdated( + const std::set& extensions_ids) override; + void InitializeBindingsSystem( + extensions::Dispatcher* dispatcher, + extensions::NativeExtensionBindingsSystem* bindings_system) override; + + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsDispatcherDelegate); +}; + +#endif // CHROME_RENDERER_EXTENSIONS_CHROME_EXTENSIONS_DISPATCHER_DELEGATE_H_ From 1d0d15e05d747a84902a3c3383ec5995214ae213 Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Wed, 15 Jan 2020 09:38:59 -0800 Subject: [PATCH 09/10] lint --- ...electron_extensions_dispatcher_delegate.cc | 177 +----------------- .../electron_extensions_dispatcher_delegate.h | 9 +- 2 files changed, 13 insertions(+), 173 deletions(-) diff --git a/shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc index 7909fd6c82adc..cc19d2a3966b8 100644 --- a/shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc +++ b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc @@ -4,6 +4,10 @@ #include "shell/renderer/extensions/electron_extensions_dispatcher_delegate.h" +#include +#include +#include + #include "chrome/renderer/extensions/tabs_hooks_delegate.h" #include "extensions/renderer/bindings/api_bindings_system.h" #include "extensions/renderer/lazy_background_page_native_handler.h" @@ -11,8 +15,6 @@ #include "extensions/renderer/native_extension_bindings_system.h" #include "extensions/renderer/native_handler.h" -#include - using extensions::NativeHandler; ElectronExtensionsDispatcherDelegate::ElectronExtensionsDispatcherDelegate() {} @@ -24,11 +26,6 @@ void ElectronExtensionsDispatcherDelegate::RegisterNativeHandlers( extensions::ModuleSystem* module_system, extensions::NativeExtensionBindingsSystem* bindings_system, extensions::ScriptContext* context) { - // The following are native handlers that are defined in //extensions, but - // are only used for APIs defined in Chrome. - // TODO(devlin): We should clean this up. If an API is defined in Chrome, - // there's no reason to have its native handlers residing and being compiled - // in //extensions. module_system->RegisterNativeHandler( "lazy_background_page", std::unique_ptr( @@ -36,179 +33,19 @@ void ElectronExtensionsDispatcherDelegate::RegisterNativeHandlers( } void ElectronExtensionsDispatcherDelegate::PopulateSourceMap( - extensions::ResourceBundleSourceMap* source_map) { -#if 0 - // Custom bindings. - source_map->RegisterSource("action", IDR_ACTION_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("browserAction", - IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("declarativeContent", - IDR_DECLARATIVE_CONTENT_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("desktopCapture", - IDR_DESKTOP_CAPTURE_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("developerPrivate", - IDR_DEVELOPER_PRIVATE_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("downloads", IDR_DOWNLOADS_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("gcm", IDR_GCM_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("identity", IDR_IDENTITY_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("imageWriterPrivate", - IDR_IMAGE_WRITER_PRIVATE_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("input.ime", IDR_INPUT_IME_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("mediaGalleries", - IDR_MEDIA_GALLERIES_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("notifications", - IDR_NOTIFICATIONS_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("omnibox", IDR_OMNIBOX_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("pageAction", IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("pageCapture", - IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("syncFileSystem", - IDR_SYNC_FILE_SYSTEM_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("systemIndicator", - IDR_SYSTEM_INDICATOR_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("tabCapture", IDR_TAB_CAPTURE_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("tts", IDR_TTS_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("ttsEngine", IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS); - -#if defined(OS_CHROMEOS) - source_map->RegisterSource("certificateProvider", - IDR_CERTIFICATE_PROVIDER_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("enterprise.platformKeys", - IDR_ENTERPRISE_PLATFORM_KEYS_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("enterprise.platformKeys.internalAPI", - IDR_ENTERPRISE_PLATFORM_KEYS_INTERNAL_API_JS); - source_map->RegisterSource("enterprise.platformKeys.KeyPair", - IDR_ENTERPRISE_PLATFORM_KEYS_KEY_PAIR_JS); - source_map->RegisterSource("enterprise.platformKeys.SubtleCrypto", - IDR_ENTERPRISE_PLATFORM_KEYS_SUBTLE_CRYPTO_JS); - source_map->RegisterSource("enterprise.platformKeys.Token", - IDR_ENTERPRISE_PLATFORM_KEYS_TOKEN_JS); - source_map->RegisterSource("fileBrowserHandler", - IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("fileManagerPrivate", - IDR_FILE_MANAGER_PRIVATE_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("fileSystemProvider", - IDR_FILE_SYSTEM_PROVIDER_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("platformKeys", - IDR_PLATFORM_KEYS_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("platformKeys.getPublicKey", - IDR_PLATFORM_KEYS_GET_PUBLIC_KEY_JS); - source_map->RegisterSource("platformKeys.internalAPI", - IDR_PLATFORM_KEYS_INTERNAL_API_JS); - source_map->RegisterSource("platformKeys.Key", IDR_PLATFORM_KEYS_KEY_JS); - source_map->RegisterSource("platformKeys.SubtleCrypto", - IDR_PLATFORM_KEYS_SUBTLE_CRYPTO_JS); - source_map->RegisterSource("platformKeys.utils", IDR_PLATFORM_KEYS_UTILS_JS); - source_map->RegisterSource("terminalPrivate", - IDR_TERMINAL_PRIVATE_CUSTOM_BINDINGS_JS); - - // IME service on Chrome OS. - source_map->RegisterSource("chromeos.ime.mojom.input_engine.mojom", - IDR_IME_SERVICE_MOJOM_JS); - source_map->RegisterSource("chromeos.ime.service", - IDR_IME_SERVICE_BINDINGS_JS); -#endif // defined(OS_CHROMEOS) - - source_map->RegisterSource("cast.streaming.rtpStream", - IDR_CAST_STREAMING_RTP_STREAM_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("cast.streaming.session", - IDR_CAST_STREAMING_SESSION_CUSTOM_BINDINGS_JS); - source_map->RegisterSource( - "cast.streaming.udpTransport", - IDR_CAST_STREAMING_UDP_TRANSPORT_CUSTOM_BINDINGS_JS); - source_map->RegisterSource( - "cast.streaming.receiverSession", - IDR_CAST_STREAMING_RECEIVER_SESSION_CUSTOM_BINDINGS_JS); - source_map->RegisterSource( - "webrtcDesktopCapturePrivate", - IDR_WEBRTC_DESKTOP_CAPTURE_PRIVATE_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("webrtcLoggingPrivate", - IDR_WEBRTC_LOGGING_PRIVATE_CUSTOM_BINDINGS_JS); - - // Platform app sources that are not API-specific.. - source_map->RegisterSource("chromeWebViewInternal", - IDR_CHROME_WEB_VIEW_INTERNAL_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("chromeWebView", IDR_CHROME_WEB_VIEW_JS); - - // Media router. - source_map->RegisterSource( - "chrome/common/media_router/mojom/media_controller.mojom", - IDR_MEDIA_CONTROLLER_MOJOM_JS); - source_map->RegisterSource( - "chrome/common/media_router/mojom/media_router.mojom", - IDR_MEDIA_ROUTER_MOJOM_JS); - source_map->RegisterSource( - "chrome/common/media_router/mojom/media_status.mojom", - IDR_MEDIA_STATUS_MOJOM_JS); - source_map->RegisterSource("media_router_bindings", - IDR_MEDIA_ROUTER_BINDINGS_JS); - source_map->RegisterSource("mojo/public/mojom/base/time.mojom", - IDR_MOJO_TIME_MOJOM_JS); - source_map->RegisterSource("mojo/public/mojom/base/unguessable_token.mojom", - IDR_MOJO_UNGUESSABLE_TOKEN_MOJOM_JS); - source_map->RegisterSource("net/interfaces/ip_address.mojom", - IDR_MOJO_IP_ADDRESS_MOJOM_JS); - source_map->RegisterSource("net/interfaces/ip_endpoint.mojom", - IDR_MOJO_IP_ENDPOINT_MOJOM_JS); - source_map->RegisterSource("url/mojom/origin.mojom", IDR_ORIGIN_MOJOM_JS); - source_map->RegisterSource("url/mojom/url.mojom", IDR_MOJO_URL_MOJOM_JS); - source_map->RegisterSource("media/mojo/mojom/remoting_common.mojom", - IDR_REMOTING_COMMON_JS); - source_map->RegisterSource( - "media/mojo/mojom/mirror_service_remoting.mojom", - IDR_MEDIA_REMOTING_JS); - source_map->RegisterSource( - "components/mirroring/mojom/mirroring_service_host.mojom", - IDR_MIRRORING_SERVICE_HOST_MOJOM_JS); - source_map->RegisterSource( - "components/mirroring/mojom/cast_message_channel.mojom", - IDR_MIRRORING_CAST_MESSAGE_CHANNEL_MOJOM_JS); - source_map->RegisterSource( - "components/mirroring/mojom/session_observer.mojom", - IDR_MIRRORING_SESSION_OBSERVER_MOJOM_JS); - source_map->RegisterSource( - "components/mirroring/mojom/session_parameters.mojom", - IDR_MIRRORING_SESSION_PARAMETERS_JS); -#endif -} + extensions::ResourceBundleSourceMap* source_map) {} void ElectronExtensionsDispatcherDelegate::RequireWebViewModules( - extensions::ScriptContext* context) { -#if 0 - DCHECK(context->GetAvailability("webViewInternal").is_available()); - context->module_system()->Require("chromeWebView"); -#endif -} + extensions::ScriptContext* context) {} void ElectronExtensionsDispatcherDelegate::OnActiveExtensionsUpdated( - const std::set& extension_ids) { -#if 0 - // In single-process mode, the browser process reports the active extensions. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - ::switches::kSingleProcess)) - return; - crash_keys::SetActiveExtensions(extension_ids); -#endif -} + const std::set& extension_ids) {} void ElectronExtensionsDispatcherDelegate::InitializeBindingsSystem( extensions::Dispatcher* dispatcher, extensions::NativeExtensionBindingsSystem* bindings_system) { extensions::APIBindingsSystem* bindings = bindings_system->api_system(); -#if 0 - bindings->GetHooksForAPI("app")->SetDelegate( - std::make_unique( - dispatcher, bindings->request_handler())); - bindings->GetHooksForAPI("extension") - ->SetDelegate(std::make_unique( - bindings_system->messaging_service())); -#endif bindings->GetHooksForAPI("tabs")->SetDelegate( std::make_unique( bindings_system->messaging_service())); -#if 0 - bindings->GetHooksForAPI("accessibilityPrivate") - ->SetDelegate( - std::make_unique()); -#endif } diff --git a/shell/renderer/extensions/electron_extensions_dispatcher_delegate.h b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.h index e2a39672c9cae..82537bbf46a58 100644 --- a/shell/renderer/extensions/electron_extensions_dispatcher_delegate.h +++ b/shell/renderer/extensions/electron_extensions_dispatcher_delegate.h @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_RENDERER_EXTENSIONS_CHROME_EXTENSIONS_DISPATCHER_DELEGATE_H_ -#define CHROME_RENDERER_EXTENSIONS_CHROME_EXTENSIONS_DISPATCHER_DELEGATE_H_ +#ifndef SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_DISPATCHER_DELEGATE_H_ +#define SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_DISPATCHER_DELEGATE_H_ + +#include +#include #include "base/macros.h" #include "extensions/renderer/dispatcher_delegate.h" @@ -33,4 +36,4 @@ class ElectronExtensionsDispatcherDelegate DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsDispatcherDelegate); }; -#endif // CHROME_RENDERER_EXTENSIONS_CHROME_EXTENSIONS_DISPATCHER_DELEGATE_H_ +#endif // SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_DISPATCHER_DELEGATE_H_ From 54932b2556fb81728e4a5fb5de71f40e16bf73d5 Mon Sep 17 00:00:00 2001 From: Jeremy Apthorp Date: Wed, 15 Jan 2020 09:43:08 -0800 Subject: [PATCH 10/10] add test for sendMessage --- spec-main/extensions-spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 34b3fcf09a58f..ae831f998a90d 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -136,6 +136,22 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex expect(response).to.equal(3) }) + + it('sendMessage receives the response', async function () { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) + ;(customSession as any).loadExtension(path.join(fixtures, 'extensions', 'chrome-api')) + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }) + await w.loadURL(url) + + const message = { method: 'sendMessage', args: ['Hello World!'] } + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + + const [,, responseString] = await emittedOnce(w.webContents, 'console-message') + const response = JSON.parse(responseString) + + expect(response.message).to.equal('Hello World!') + expect(response.tabId).to.equal(w.webContents.id) + }) }) describe('background pages', () => {