app_shell: Make renderer run background page JavaScript

* Install URL protocol handler for chrome-extension:// and chrome-extension-resource://
* Allow extension urls to be handled.
* Register extensions with IO thread extension InfoMap.
* Bonus: app_shell no longer crashes on startup.

With this change an extension background page can print "Hello world" using console.log().

BUG=288226,332982
TEST=browser_tests. Also run app_shell --app=/path/to/extension with an extension with background.js. The JavaScript executes and console.log() works.

Review URL: https://codereview.chromium.org/136453005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245325 0039d316-1c4b-4281-b951-d872f2087c98
parent a208843d
......@@ -6,6 +6,8 @@ include_rules = [
# TODO(jamescook): Remove these. http://crbug.com/305404
# Chrome pieces for bring-up.
"+chrome/browser/extensions/extension_protocols.h",
"+chrome/browser/extensions/extension_resource_protocols.h",
"+chrome/common/chrome_paths.h",
"+chrome/common/extensions/extension_file_util.h",
"+chrome/common/extensions/features/base_feature_provider.h",
......
......@@ -46,6 +46,10 @@ class ShellBrowserMainParts : public content::BrowserMainParts,
return browser_context_.get();
}
extensions::ShellExtensionSystem* extension_system() {
return extension_system_;
}
// BrowserMainParts overrides.
virtual void PreEarlyInitialization() OVERRIDE;
virtual void PreMainMessageLoopStart() OVERRIDE;
......
......@@ -6,7 +6,25 @@
#include "apps/shell/shell_browser_context.h"
#include "apps/shell/shell_browser_main_parts.h"
#include "apps/shell/shell_extension_system.h"
#include "base/command_line.h"
#include "chrome/browser/extensions/extension_protocols.h"
#include "chrome/browser/extensions/extension_resource_protocols.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/shell/browser/shell_browser_context.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/info_map.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "url/gurl.h"
using content::BrowserThread;
using extensions::ExtensionRegistry;
namespace apps {
......@@ -27,9 +45,99 @@ net::URLRequestContextGetter*
ShellContentBrowserClient::CreateRequestContext(
content::BrowserContext* content_browser_context,
content::ProtocolHandlerMap* protocol_handlers) {
// TODO(jamescook): Should this be an off-the-record context?
// Handle chrome-extension: and chrome-extension-resource: requests.
extensions::InfoMap* extension_info_map =
browser_main_parts_->extension_system()->info_map();
(*protocol_handlers)[extensions::kExtensionScheme] =
linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
CreateExtensionProtocolHandler(false /*is_incognito*/,
extension_info_map));
(*protocol_handlers)[extensions::kExtensionResourceScheme] =
linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
CreateExtensionResourceProtocolHandler());
// Let content::ShellBrowserContext handle the rest of the setup.
return browser_main_parts_->browser_context()->CreateRequestContext(
protocol_handlers);
}
bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
if (!url.is_valid())
return false;
// Keep in sync with ProtocolHandlers added in CreateRequestContext() and in
// content::ShellURLRequestContextGetter::GetURLRequestContext().
static const char* const kProtocolList[] = {
chrome::kBlobScheme,
chrome::kChromeUIScheme,
chrome::kChromeDevToolsScheme,
chrome::kDataScheme,
content::kFileScheme,
content::kFileSystemScheme,
extensions::kExtensionScheme,
extensions::kExtensionResourceScheme,
};
for (size_t i = 0; i < arraysize(kProtocolList); ++i) {
if (url.scheme() == kProtocolList[i])
return true;
}
return false;
}
void ShellContentBrowserClient::SiteInstanceGotProcess(
content::SiteInstance* site_instance) {
// If this isn't an extension renderer there's nothing to do.
const extensions::Extension* extension = GetExtension(site_instance);
if (!extension)
return;
// TODO(jamescook): Add to extension service process_map().
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&extensions::InfoMap::RegisterExtensionProcess,
browser_main_parts_->extension_system()->info_map(),
extension->id(),
site_instance->GetProcess()->GetID(),
site_instance->GetId()));
}
void ShellContentBrowserClient::SiteInstanceDeleting(
content::SiteInstance* site_instance) {
// If this isn't an extension renderer there's nothing to do.
const extensions::Extension* extension = GetExtension(site_instance);
if (!extension)
return;
// TODO(jamescook): Remove from extension service process_map().
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&extensions::InfoMap::UnregisterExtensionProcess,
browser_main_parts_->extension_system()->info_map(),
extension->id(),
site_instance->GetProcess()->GetID(),
site_instance->GetId()));
}
void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
CommandLine* command_line, int child_process_id) {
std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
if (process_type == switches::kRendererProcess) {
// TODO(jamescook): Should we check here if the process is in the extension
// service process map, or can we assume all renderers are extension
// renderers?
command_line->AppendSwitch(switches::kExtensionProcess);
}
}
const extensions::Extension* ShellContentBrowserClient::GetExtension(
content::SiteInstance* site_instance) {
ExtensionRegistry* registry =
ExtensionRegistry::Get(site_instance->GetBrowserContext());
return registry->enabled_extensions().GetExtensionOrAppByURL(
site_instance->GetSiteURL());
}
} // namespace apps
......@@ -8,6 +8,12 @@
#include "base/compiler_specific.h"
#include "content/public/browser/content_browser_client.h"
class GURL;
namespace extensions {
class Extension;
}
namespace apps {
class ShellBrowserMainParts;
......@@ -24,8 +30,19 @@ class ShellContentBrowserClient : public content::ContentBrowserClient {
content::ProtocolHandlerMap* protocol_handlers) OVERRIDE;
// TODO(jamescook): Quota management?
// TODO(jamescook): Speech recognition?
virtual bool IsHandledURL(const GURL& url) OVERRIDE;
virtual void SiteInstanceGotProcess(content::SiteInstance* site_instance)
OVERRIDE;
virtual void SiteInstanceDeleting(content::SiteInstance* site_instance)
OVERRIDE;
virtual void AppendExtraCommandLineSwitches(CommandLine* command_line,
int child_process_id) OVERRIDE;
private:
// Returns the extension or app associated with |site_instance| or NULL.
const extensions::Extension* GetExtension(
content::SiteInstance* site_instance);
// Owned by content::BrowserMainLoop.
ShellBrowserMainParts* browser_main_parts_;
......
......@@ -12,15 +12,18 @@
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/info_map.h"
#include "extensions/browser/lazy_background_task_queue.h"
#include "extensions/browser/process_manager.h"
using content::BrowserContext;
using content::BrowserThread;
namespace extensions {
......@@ -44,10 +47,16 @@ bool ShellExtensionSystem::LoadAndLaunchApp(const base::FilePath& app_dir) {
return false;
}
// TODO(jamescook): We may want to do some of these things here:
// * Create a PermissionsUpdater.
// * Call PermissionsUpdater::GrantActivePermissions().
// * Call ExtensionService::SatisfyImports().
// * Call ExtensionPrefs::OnExtensionInstalled().
// * Send NOTIFICATION_EXTENSION_INSTALLED.
ExtensionRegistry::Get(browser_context_)->AddEnabled(extension);
// TODO(jamescook): If RegisterExtensionWithRequestContexts() did something,
// this would be the place to call it.
RegisterExtensionWithRequestContexts(extension);
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_LOADED,
......@@ -56,9 +65,6 @@ bool ShellExtensionSystem::LoadAndLaunchApp(const base::FilePath& app_dir) {
// Inform the rest of the extensions system to start.
ready_.Signal();
LOG(WARNING) << "-----------------------------------";
LOG(WARNING) << "app_shell is expected to crash now.";
LOG(WARNING) << "-----------------------------------";
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSIONS_READY,
content::Source<BrowserContext>(browser_context_),
......@@ -79,7 +85,6 @@ void ShellExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
}
ExtensionService* ShellExtensionSystem::extension_service() {
NOTREACHED();
return NULL;
}
......@@ -104,7 +109,9 @@ StateStore* ShellExtensionSystem::rules_store() {
}
InfoMap* ShellExtensionSystem::info_map() {
return NULL;
if (!info_map_.get())
info_map_ = new InfoMap;
return info_map_;
}
LazyBackgroundTaskQueue* ShellExtensionSystem::lazy_background_task_queue() {
......@@ -133,6 +140,11 @@ InstallVerifier* ShellExtensionSystem::install_verifier() {
void ShellExtensionSystem::RegisterExtensionWithRequestContexts(
const Extension* extension) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&InfoMap::AddExtension, info_map(),
make_scoped_refptr(extension), base::Time::Now(),
false, false));
}
void ShellExtensionSystem::UnregisterExtensionWithRequestContexts(
......
......@@ -20,6 +20,7 @@ class BrowserContext;
namespace extensions {
class EventRouter;
class InfoMap;
class LazyBackgroundTaskQueue;
class ProcessManager;
......@@ -63,6 +64,9 @@ class ShellExtensionSystem : public ExtensionSystem {
private:
content::BrowserContext* browser_context_; // Not owned.
// Data to be accessed on the IO thread. Must outlive process_manager_.
scoped_refptr<InfoMap> info_map_;
scoped_ptr<LazyBackgroundTaskQueue> lazy_background_task_queue_;
scoped_ptr<EventRouter> event_router_;
scoped_ptr<ProcessManager> process_manager_;
......
......@@ -30,6 +30,8 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
......@@ -191,13 +193,19 @@ void ExtensionHost::CreateRenderViewSoon() {
void ExtensionHost::CreateRenderViewNow() {
LoadInitialURL();
if (IsBackgroundPage()) {
if (!IsBackgroundPage()) {
DCHECK(IsRenderViewLive());
ExtensionSystem::GetForBrowserContext(browser_context_)->
extension_service()->DidCreateRenderViewForBackgroundPage(this);
ExtensionService* service = GetExtensionService();
if (service)
service->DidCreateRenderViewForBackgroundPage(this);
}
}
ExtensionService* ExtensionHost::GetExtensionService() {
return ExtensionSystem::GetForBrowserContext(browser_context_)
->extension_service();
}
const GURL& ExtensionHost::GetURL() const {
return host_contents()->GetURL();
}
......@@ -315,8 +323,13 @@ void ExtensionHost::DocumentAvailableInMainFrame() {
void ExtensionHost::OnDocumentAvailable() {
DCHECK(extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
ExtensionSystem::GetForBrowserContext(browser_context_)->
extension_service()->SetBackgroundPageReady(extension_);
ExtensionService* service = GetExtensionService();
if (service)
service->SetBackgroundPageReady(extension_);
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
content::Source<const Extension>(extension_),
content::NotificationService::NoDetails());
}
void ExtensionHost::CloseContents(WebContents* contents) {
......
......@@ -19,6 +19,7 @@
#include "extensions/common/stack_frame.h"
#include "extensions/common/view_type.h"
class ExtensionService;
class PrefsTabHelper;
namespace content {
......@@ -128,6 +129,9 @@ class ExtensionHost : public content::WebContentsDelegate,
// Actually create the RenderView for this host. See CreateRenderViewSoon.
void CreateRenderViewNow();
// Returns the ExtensionService for |browser_context_| or NULL if none exists.
ExtensionService* GetExtensionService();
// Message handlers.
void OnRequest(const ExtensionHostMsg_Request_Params& params);
void OnEventAck();
......
......@@ -2502,6 +2502,8 @@ void ExtensionService::Observe(int type,
break;
}
case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
// TODO(jamescook): Extract this block of code to src/extensions so it
// can be shared with app_shell.
content::RenderProcessHost* process =
content::Source<content::RenderProcessHost>(source).ptr();
Profile* host_profile =
......@@ -2623,10 +2625,6 @@ bool ExtensionService::IsBackgroundPageReady(const Extension* extension) const {
void ExtensionService::SetBackgroundPageReady(const Extension* extension) {
DCHECK(extensions::BackgroundInfo::HasBackgroundPage(extension));
extension_runtime_data_[extension->id()].background_page_ready = true;
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
content::Source<const Extension>(extension),
content::NotificationService::NoDetails());
}
bool ExtensionService::IsBeingUpgraded(const Extension* extension) const {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment