feat(extensions): add chrome.i18n API (#22455)
* feat(extensions): implement chrome.i18n API * refactor(extensions): use forward declaration for ScriptExecutor * fix(extensions): add message filter to handle i18n GetMessageBundle * test(extensions): add chrome.i18n tests * fix(extensions): message filter lint error * fix: remove exclusive test * fix(extensions): format api feature arrays Co-Authored-By: Jeremy Apthorp <nornagon@nornagon.net> * fix(extensions): uncomment chrome.i18n usage in old extensions test Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
This commit is contained in:
parent
1e9fa204ee
commit
d6701ff435
18 changed files with 502 additions and 3 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -42,7 +42,9 @@
|
|||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/browser/script_executor.h"
|
||||
namespace extensions {
|
||||
class ScriptExecutor;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace blink {
|
||||
|
|
|
@ -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
|
||||
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
]
|
||||
|
|
24
shell/browser/extensions/api/i18n/i18n_api.cc
Normal file
24
shell/browser/extensions/api/i18n/i18n_api.cc
Normal file
|
@ -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
shell/browser/extensions/api/i18n/i18n_api.h
Normal file
20
shell/browser/extensions/api/i18n/i18n_api.h
Normal file
|
@ -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
shell/browser/extensions/electron_extension_message_filter.cc
Normal file
158
shell/browser/extensions/electron_extension_message_filter.cc
Normal file
|
@ -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
shell/browser/extensions/electron_extension_message_filter.h
Normal file
69
shell/browser/extensions/electron_extension_message_filter.h
Normal file
|
@ -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_
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue