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: add chrome.i18n API #22570

Merged
merged 1 commit into from Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions filenames.gni
Expand Up @@ -601,6 +601,8 @@ filenames = {
]

lib_sources_extensions = [
"shell/browser/extensions/api/i18n/i18n_api.cc",
"shell/browser/extensions/api/i18n/i18n_api.h",
"shell/browser/extensions/api/resources_private/resources_private_api.cc",
"shell/browser/extensions/api/resources_private/resources_private_api.h",
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
Expand All @@ -619,6 +621,8 @@ filenames = {
"shell/browser/extensions/electron_extension_host_delegate.h",
"shell/browser/extensions/electron_extension_loader.cc",
"shell/browser/extensions/electron_extension_loader.h",
"shell/browser/extensions/electron_extension_message_filter.cc",
"shell/browser/extensions/electron_extension_message_filter.h",
"shell/browser/extensions/electron_extension_system.cc",
"shell/browser/extensions/electron_extension_system.h",
"shell/browser/extensions/electron_extension_system_factory.cc",
Expand Down
1 change: 1 addition & 0 deletions shell/browser/api/electron_api_web_contents.cc
Expand Up @@ -115,6 +115,7 @@
#endif

#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/browser/script_executor.h"
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
#endif

Expand Down
4 changes: 3 additions & 1 deletion shell/browser/api/electron_api_web_contents.h
Expand Up @@ -43,7 +43,9 @@
#endif

#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/browser/script_executor.h"
namespace extensions {
class ScriptExecutor;
}
#endif

namespace blink {
Expand Down
3 changes: 3 additions & 0 deletions shell/browser/electron_browser_client.cc
Expand Up @@ -146,6 +146,7 @@
#include "extensions/browser/process_map.h"
#include "extensions/common/api/mime_handler.mojom.h"
#include "extensions/common/extension.h"
#include "shell/browser/extensions/electron_extension_message_filter.h"
#include "shell/browser/extensions/electron_extension_system.h"
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
#endif
Expand Down Expand Up @@ -462,6 +463,8 @@ void ElectronBrowserClient::RenderProcessWillLaunch(
new extensions::ExtensionMessageFilter(process_id, browser_context));
host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(
process_id, browser_context));
host->AddFilter(
new ElectronExtensionMessageFilter(process_id, browser_context));
#endif

ProcessPreferences prefs;
Expand Down
1 change: 1 addition & 0 deletions shell/browser/extensions/api/BUILD.gn
Expand Up @@ -11,6 +11,7 @@ assert(enable_extensions,
function_registration("api_registration") {
sources = [
"//electron/shell/common/extensions/api/extension.json",
"//electron/shell/common/extensions/api/i18n.json",
"//electron/shell/common/extensions/api/resources_private.idl",
"//electron/shell/common/extensions/api/tabs.json",
]
Expand Down
24 changes: 24 additions & 0 deletions shell/browser/extensions/api/i18n/i18n_api.cc
@@ -0,0 +1,24 @@
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/browser/extensions/api/i18n/i18n_api.h"

#include <string>
#include <vector>

#include "chrome/browser/browser_process.h"
#include "shell/common/extensions/api/i18n.h"

namespace GetAcceptLanguages = extensions::api::i18n::GetAcceptLanguages;

namespace extensions {

ExtensionFunction::ResponseAction I18nGetAcceptLanguagesFunction::Run() {
auto locale = g_browser_process->GetApplicationLocale();
std::vector<std::string> accept_languages = {locale};
return RespondNow(
ArgumentList(GetAcceptLanguages::Results::Create(accept_languages)));
}

} // namespace extensions
20 changes: 20 additions & 0 deletions shell/browser/extensions/api/i18n/i18n_api.h
@@ -0,0 +1,20 @@
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
#define SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_

#include "extensions/browser/extension_function.h"

namespace extensions {

class I18nGetAcceptLanguagesFunction : public ExtensionFunction {
~I18nGetAcceptLanguagesFunction() override {}
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("i18n.getAcceptLanguages", I18N_GETACCEPTLANGUAGES)
};

} // namespace extensions

#endif // SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
158 changes: 158 additions & 0 deletions shell/browser/extensions/electron_extension_message_filter.cc
@@ -0,0 +1,158 @@
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/browser/extensions/electron_extension_message_filter.h"

#include <stdint.h>
#include <memory>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest_handlers/default_locale_handler.h"
#include "extensions/common/manifest_handlers/shared_module_info.h"
#include "extensions/common/message_bundle.h"

using content::BrowserThread;

namespace electron {

const uint32_t kExtensionFilteredMessageClasses[] = {
ExtensionMsgStart,
};

ElectronExtensionMessageFilter::ElectronExtensionMessageFilter(
int render_process_id,
content::BrowserContext* browser_context)
: BrowserMessageFilter(kExtensionFilteredMessageClasses,
base::size(kExtensionFilteredMessageClasses)),
render_process_id_(render_process_id),
browser_context_(browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}

ElectronExtensionMessageFilter::~ElectronExtensionMessageFilter() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}

bool ElectronExtensionMessageFilter::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ElectronExtensionMessageFilter, message)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ExtensionHostMsg_GetMessageBundle,
OnGetExtMessageBundle)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()

return handled;
}

void ElectronExtensionMessageFilter::OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) {
switch (message.type()) {
case ExtensionHostMsg_GetMessageBundle::ID:
*thread = BrowserThread::UI;
break;
default:
break;
}
}

void ElectronExtensionMessageFilter::OnDestruct() const {
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
delete this;
} else {
base::DeleteSoon(FROM_HERE, {BrowserThread::UI}, this);
}
}

void ElectronExtensionMessageFilter::OnGetExtMessageBundle(
const std::string& extension_id,
IPC::Message* reply_msg) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);

const extensions::ExtensionSet& extension_set =
extensions::ExtensionRegistry::Get(browser_context_)
->enabled_extensions();
const extensions::Extension* extension = extension_set.GetByID(extension_id);

if (!extension) { // The extension has gone.
ExtensionHostMsg_GetMessageBundle::WriteReplyParams(
reply_msg, extensions::MessageBundle::SubstitutionMap());
Send(reply_msg);
return;
}

const std::string& default_locale =
extensions::LocaleInfo::GetDefaultLocale(extension);
if (default_locale.empty()) {
// A little optimization: send the answer here to avoid an extra thread hop.
std::unique_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map(
extensions::file_util::LoadNonLocalizedMessageBundleSubstitutionMap(
extension_id));
ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg,
*dictionary_map);
Send(reply_msg);
return;
}

std::vector<base::FilePath> paths_to_load;
paths_to_load.push_back(extension->path());

auto imports = extensions::SharedModuleInfo::GetImports(extension);
// Iterate through the imports in reverse. This will allow later imported
// modules to override earlier imported modules, as the list order is
// maintained from the definition in manifest.json of the imports.
for (auto it = imports.rbegin(); it != imports.rend(); ++it) {
const extensions::Extension* imported_extension =
extension_set.GetByID(it->extension_id);
if (!imported_extension) {
NOTREACHED() << "Missing shared module " << it->extension_id;
continue;
}
paths_to_load.push_back(imported_extension->path());
}

// This blocks tab loading. Priority is inherited from the calling context.
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock()},
base::BindOnce(
&ElectronExtensionMessageFilter::OnGetExtMessageBundleAsync, this,
paths_to_load, extension_id, default_locale,
extension_l10n_util::GetGzippedMessagesPermissionForExtension(
extension),
reply_msg));
}

void ElectronExtensionMessageFilter::OnGetExtMessageBundleAsync(
const std::vector<base::FilePath>& extension_paths,
const std::string& main_extension_id,
const std::string& default_locale,
extension_l10n_util::GzippedMessagesPermission gzip_permission,
IPC::Message* reply_msg) {
std::unique_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map(
extensions::file_util::LoadMessageBundleSubstitutionMapFromPaths(
extension_paths, main_extension_id, default_locale, gzip_permission));

ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg,
*dictionary_map);
Send(reply_msg);
}

} // namespace electron
69 changes: 69 additions & 0 deletions shell/browser/extensions/electron_extension_message_filter.h
@@ -0,0 +1,69 @@
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_MESSAGE_FILTER_H_
#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_MESSAGE_FILTER_H_

#include <string>
#include <vector>

#include "base/macros.h"
#include "base/scoped_observer.h"
#include "base/sequenced_task_runner_helpers.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/extension_l10n_util.h"

namespace content {
class BrowserContext;
}

namespace extensions {
struct Message;
}

namespace electron {

// This class filters out incoming Electron-specific IPC messages from the
// extension process on the IPC thread.
class ElectronExtensionMessageFilter : public content::BrowserMessageFilter {
public:
ElectronExtensionMessageFilter(int render_process_id,
content::BrowserContext* browser_context);

// content::BrowserMessageFilter methods:
bool OnMessageReceived(const IPC::Message& message) override;
void OverrideThreadForMessage(const IPC::Message& message,
content::BrowserThread::ID* thread) override;
void OnDestruct() const override;

private:
friend class content::BrowserThread;
friend class base::DeleteHelper<ElectronExtensionMessageFilter>;

~ElectronExtensionMessageFilter() override;

void OnGetExtMessageBundle(const std::string& extension_id,
IPC::Message* reply_msg);
void OnGetExtMessageBundleAsync(
const std::vector<base::FilePath>& extension_paths,
const std::string& main_extension_id,
const std::string& default_locale,
extension_l10n_util::GzippedMessagesPermission gzip_permission,
IPC::Message* reply_msg);

const int render_process_id_;

// The BrowserContext associated with our renderer process. This should only
// be accessed on the UI thread! Furthermore since this class is refcounted it
// may outlive |browser_context_|, so make sure to NULL check if in doubt;
// async calls and the like.
content::BrowserContext* browser_context_;

DISALLOW_COPY_AND_ASSIGN(ElectronExtensionMessageFilter);
};

} // namespace electron

#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_MESSAGE_FILTER_H_
Expand Up @@ -6,6 +6,7 @@

#include "extensions/browser/extension_function_registry.h"
#include "shell/browser/extensions/api/generated_api_registration.h"
#include "shell/browser/extensions/api/i18n/i18n_api.h"
#include "shell/browser/extensions/api/tabs/tabs_api.h"

namespace extensions {
Expand Down
2 changes: 2 additions & 0 deletions shell/common/extensions/api/BUILD.gn
Expand Up @@ -37,6 +37,7 @@ group("extensions_features") {
generated_json_strings("generated_api_json_strings") {
sources = [
"extension.json",
"i18n.json",
"resources_private.idl",
"tabs.json",
]
Expand All @@ -53,6 +54,7 @@ generated_json_strings("generated_api_json_strings") {

generated_types("generated_api_types") {
sources = [
"i18n.json",
"resources_private.idl",
"tabs.json",
]
Expand Down
6 changes: 6 additions & 0 deletions shell/common/extensions/api/_api_features.json
Expand Up @@ -16,6 +16,12 @@
"extension.getURL": {
"contexts": ["blessed_extension", "unblessed_extension", "content_script"]
},
"i18n": {
"channel": "stable",
"extension_types": ["extension"],
"contexts": ["blessed_extension", "unblessed_extension", "content_script"],
"disallow_for_service_workers": true
},
"mimeHandlerViewGuestInternal": {
"internal": true,
"contexts": "all",
Expand Down