Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [extensions] implement a couple of tabs APIs #21779

Merged
merged 10 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions chromium_src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
6 changes: 6 additions & 0 deletions filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -613,6 +615,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",
Expand All @@ -621,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 = [
Expand Down
2 changes: 2 additions & 0 deletions shell/browser/api/atom_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions shell/browser/api/atom_api_web_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
#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;
}
Expand Down Expand Up @@ -327,6 +331,12 @@ class WebContents : public gin_helper::TrackableObject<WebContents>,

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|.
WebContents(v8::Isolate* isolate, content::WebContents* web_contents);
Expand Down Expand Up @@ -537,6 +547,10 @@ class WebContents : public gin_helper::TrackableObject<WebContents>,
std::unique_ptr<WebViewGuestDelegate> guest_delegate_;
std::unique_ptr<FrameSubscriber> frame_subscriber_;

#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
std::unique_ptr<extensions::ScriptExecutor> script_executor_;
#endif

// The host webcontents that may contain this webcontents.
WebContents* embedder_ = nullptr;

Expand Down
133 changes: 133 additions & 0 deletions shell/browser/extensions/api/tabs/tabs_api.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 <memory>
#include <utility>

#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<InjectDetails> 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
49 changes: 49 additions & 0 deletions shell/browser/extensions/api/tabs/tabs_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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_
#define SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_

#include <string>

#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_TABS_API_H_
4 changes: 3 additions & 1 deletion shell/browser/extensions/atom_extensions_browser_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -53,7 +54,8 @@ AtomExtensionsBrowserClient::AtomExtensionsBrowserClient()

AddAPIProvider(
std::make_unique<extensions::CoreExtensionsBrowserAPIProvider>());
// AddAPIProvider(std::make_unique<AtomExtensionsBrowserAPIProvider>());
AddAPIProvider(
std::make_unique<extensions::ElectronExtensionsBrowserAPIProvider>());
}

AtomExtensionsBrowserClient::~AtomExtensionsBrowserClient() {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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/extensions/api/tabs/tabs_api.h"

namespace extensions {

ElectronExtensionsBrowserAPIProvider::ElectronExtensionsBrowserAPIProvider() =
default;
ElectronExtensionsBrowserAPIProvider::~ElectronExtensionsBrowserAPIProvider() =
default;

void ElectronExtensionsBrowserAPIProvider::RegisterExtensionFunctions(
ExtensionFunctionRegistry* registry) {
registry->RegisterFunction<TabsExecuteScriptFunction>();
/*
// Generated APIs from Electron.
api::ElectronGeneratedFunctionRegistry::RegisterAll(registry);
*/
}

} // namespace extensions
Original file line number Diff line number Diff line change
@@ -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_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"

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_ELECTRON_EXTENSIONS_BROWSER_API_PROVIDER_H_
19 changes: 18 additions & 1 deletion shell/browser/extensions/electron_messaging_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,13 +41,28 @@ ElectronMessagingDelegate::IsNativeMessagingHostAllowed(

std::unique_ptr<base::DictionaryValue>
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<base::DictionaryValue>();
tab->SetWithoutPathExpansion(
"id", std::make_unique<base::Value>(api_contents->ID()));
return tab;
}
}
return nullptr;
}

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<MessagePort> ElectronMessagingDelegate::CreateReceiverForTab(
Expand Down