Home | History | Annotate | Download | only in runtime
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "extensions/browser/api/runtime/runtime_api.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/lazy_instance.h"
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/values.h"
     14 #include "base/version.h"
     15 #include "content/public/browser/browser_context.h"
     16 #include "content/public/browser/child_process_security_policy.h"
     17 #include "content/public/browser/notification_service.h"
     18 #include "content/public/browser/render_process_host.h"
     19 #include "content/public/browser/render_view_host.h"
     20 #include "extensions/browser/api/runtime/runtime_api_delegate.h"
     21 #include "extensions/browser/event_router.h"
     22 #include "extensions/browser/extension_host.h"
     23 #include "extensions/browser/extension_prefs.h"
     24 #include "extensions/browser/extension_registry.h"
     25 #include "extensions/browser/extension_system.h"
     26 #include "extensions/browser/extension_util.h"
     27 #include "extensions/browser/extensions_browser_client.h"
     28 #include "extensions/browser/lazy_background_task_queue.h"
     29 #include "extensions/browser/notification_types.h"
     30 #include "extensions/browser/process_manager.h"
     31 #include "extensions/common/api/runtime.h"
     32 #include "extensions/common/error_utils.h"
     33 #include "extensions/common/extension.h"
     34 #include "extensions/common/manifest_handlers/background_info.h"
     35 #include "extensions/common/manifest_handlers/shared_module_info.h"
     36 #include "storage/browser/fileapi/isolated_context.h"
     37 #include "url/gurl.h"
     38 
     39 using content::BrowserContext;
     40 
     41 namespace extensions {
     42 
     43 namespace runtime = core_api::runtime;
     44 
     45 namespace {
     46 
     47 const char kNoBackgroundPageError[] = "You do not have a background page.";
     48 const char kPageLoadError[] = "Background page failed to load.";
     49 const char kInstallId[] = "id";
     50 const char kInstallReason[] = "reason";
     51 const char kInstallReasonChromeUpdate[] = "chrome_update";
     52 const char kInstallReasonUpdate[] = "update";
     53 const char kInstallReasonInstall[] = "install";
     54 const char kInstallReasonSharedModuleUpdate[] = "shared_module_update";
     55 const char kInstallPreviousVersion[] = "previousVersion";
     56 const char kInvalidUrlError[] = "Invalid URL.";
     57 const char kPlatformInfoUnavailable[] = "Platform information unavailable.";
     58 
     59 const char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
     60 
     61 // A preference key storing the url loaded when an extension is uninstalled.
     62 const char kUninstallUrl[] = "uninstall_url";
     63 
     64 // The name of the directory to be returned by getPackageDirectoryEntry. This
     65 // particular value does not matter to user code, but is chosen for consistency
     66 // with the equivalent Pepper API.
     67 const char kPackageDirectoryPath[] = "crxfs";
     68 
     69 void DispatchOnStartupEventImpl(BrowserContext* browser_context,
     70                                 const std::string& extension_id,
     71                                 bool first_call,
     72                                 ExtensionHost* host) {
     73   // A NULL host from the LazyBackgroundTaskQueue means the page failed to
     74   // load. Give up.
     75   if (!host && !first_call)
     76     return;
     77 
     78   // Don't send onStartup events to incognito browser contexts.
     79   if (browser_context->IsOffTheRecord())
     80     return;
     81 
     82   if (ExtensionsBrowserClient::Get()->IsShuttingDown() ||
     83       !ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
     84     return;
     85   ExtensionSystem* system = ExtensionSystem::Get(browser_context);
     86   if (!system)
     87     return;
     88 
     89   // If this is a persistent background page, we want to wait for it to load
     90   // (it might not be ready, since this is startup). But only enqueue once.
     91   // If it fails to load the first time, don't bother trying again.
     92   const Extension* extension =
     93       ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID(
     94           extension_id);
     95   if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) &&
     96       first_call &&
     97       system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context,
     98                                                               extension)) {
     99     system->lazy_background_task_queue()->AddPendingTask(
    100         browser_context,
    101         extension_id,
    102         base::Bind(
    103             &DispatchOnStartupEventImpl, browser_context, extension_id, false));
    104     return;
    105   }
    106 
    107   scoped_ptr<base::ListValue> event_args(new base::ListValue());
    108   scoped_ptr<Event> event(
    109       new Event(runtime::OnStartup::kEventName, event_args.Pass()));
    110   system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
    111 }
    112 
    113 void SetUninstallURL(ExtensionPrefs* prefs,
    114                      const std::string& extension_id,
    115                      const std::string& url_string) {
    116   prefs->UpdateExtensionPref(
    117       extension_id, kUninstallUrl, new base::StringValue(url_string));
    118 }
    119 
    120 std::string GetUninstallURL(ExtensionPrefs* prefs,
    121                             const std::string& extension_id) {
    122   std::string url_string;
    123   prefs->ReadPrefAsString(extension_id, kUninstallUrl, &url_string);
    124   return url_string;
    125 }
    126 
    127 }  // namespace
    128 
    129 ///////////////////////////////////////////////////////////////////////////////
    130 
    131 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
    132     g_factory = LAZY_INSTANCE_INITIALIZER;
    133 
    134 // static
    135 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
    136   return g_factory.Pointer();
    137 }
    138 
    139 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
    140     : browser_context_(context),
    141       dispatch_chrome_updated_event_(false),
    142       extension_registry_observer_(this) {
    143   registrar_.Add(this,
    144                  extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
    145                  content::Source<BrowserContext>(context));
    146   extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
    147 
    148   delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
    149       browser_context_);
    150 
    151   // Check if registered events are up-to-date. We can only do this once
    152   // per browser context, since it updates internal state when called.
    153   dispatch_chrome_updated_event_ =
    154       ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_);
    155 }
    156 
    157 RuntimeAPI::~RuntimeAPI() {
    158   delegate_->RemoveUpdateObserver(this);
    159 }
    160 
    161 void RuntimeAPI::Observe(int type,
    162                          const content::NotificationSource& source,
    163                          const content::NotificationDetails& details) {
    164   DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type);
    165   // We're done restarting Chrome after an update.
    166   dispatch_chrome_updated_event_ = false;
    167 
    168   delegate_->AddUpdateObserver(this);
    169 
    170   // RuntimeAPI is redirected in incognito, so |browser_context_| is never
    171   // incognito. We don't observe incognito ProcessManagers but that is OK
    172   // because we don't send onStartup events to incognito browser contexts.
    173   DCHECK(!browser_context_->IsOffTheRecord());
    174   // Some tests use partially constructed Profiles without a process manager.
    175   ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_);
    176   if (extension_system->process_manager())
    177     extension_system->process_manager()->AddObserver(this);
    178 }
    179 
    180 void RuntimeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
    181                                    const Extension* extension) {
    182   if (!dispatch_chrome_updated_event_)
    183     return;
    184 
    185   // Dispatch the onInstalled event with reason "chrome_update".
    186   base::MessageLoop::current()->PostTask(
    187       FROM_HERE,
    188       base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
    189                  browser_context_,
    190                  extension->id(),
    191                  Version(),
    192                  true));
    193 }
    194 
    195 void RuntimeAPI::OnExtensionWillBeInstalled(
    196     content::BrowserContext* browser_context,
    197     const Extension* extension,
    198     bool is_update,
    199     bool from_ephemeral,
    200     const std::string& old_name) {
    201   // Ephemeral apps are not considered to be installed and do not receive
    202   // the onInstalled() event.
    203   if (util::IsEphemeralApp(extension->id(), browser_context_))
    204     return;
    205 
    206   Version old_version = delegate_->GetPreviousExtensionVersion(extension);
    207 
    208   // Dispatch the onInstalled event.
    209   base::MessageLoop::current()->PostTask(
    210       FROM_HERE,
    211       base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
    212                  browser_context_,
    213                  extension->id(),
    214                  old_version,
    215                  false));
    216 }
    217 
    218 void RuntimeAPI::OnExtensionUninstalled(
    219     content::BrowserContext* browser_context,
    220     const Extension* extension,
    221     UninstallReason reason) {
    222   // Ephemeral apps are not considered to be installed, so the uninstall URL
    223   // is not invoked when they are removed.
    224   if (util::IsEphemeralApp(extension->id(), browser_context_))
    225     return;
    226 
    227   RuntimeEventRouter::OnExtensionUninstalled(
    228       browser_context_, extension->id(), reason);
    229 }
    230 
    231 void RuntimeAPI::Shutdown() {
    232   // ExtensionSystem deletes its ProcessManager during the Shutdown() phase, so
    233   // the observer must be removed here and not in the RuntimeAPI destructor.
    234   ProcessManager* process_manager =
    235       ExtensionSystem::Get(browser_context_)->process_manager();
    236   // Some tests use partially constructed Profiles without a process manager.
    237   if (process_manager)
    238     process_manager->RemoveObserver(this);
    239 }
    240 
    241 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) {
    242   RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
    243       browser_context_, extension->id(), extension->manifest()->value());
    244 }
    245 
    246 void RuntimeAPI::OnChromeUpdateAvailable() {
    247   RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_);
    248 }
    249 
    250 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) {
    251   RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id());
    252 }
    253 
    254 void RuntimeAPI::ReloadExtension(const std::string& extension_id) {
    255   delegate_->ReloadExtension(extension_id);
    256 }
    257 
    258 bool RuntimeAPI::CheckForUpdates(
    259     const std::string& extension_id,
    260     const RuntimeAPIDelegate::UpdateCheckCallback& callback) {
    261   return delegate_->CheckForUpdates(extension_id, callback);
    262 }
    263 
    264 void RuntimeAPI::OpenURL(const GURL& update_url) {
    265   delegate_->OpenURL(update_url);
    266 }
    267 
    268 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
    269   return delegate_->GetPlatformInfo(info);
    270 }
    271 
    272 bool RuntimeAPI::RestartDevice(std::string* error_message) {
    273   return delegate_->RestartDevice(error_message);
    274 }
    275 
    276 ///////////////////////////////////////////////////////////////////////////////
    277 
    278 // static
    279 void RuntimeEventRouter::DispatchOnStartupEvent(
    280     content::BrowserContext* context,
    281     const std::string& extension_id) {
    282   DispatchOnStartupEventImpl(context, extension_id, true, NULL);
    283 }
    284 
    285 // static
    286 void RuntimeEventRouter::DispatchOnInstalledEvent(
    287     content::BrowserContext* context,
    288     const std::string& extension_id,
    289     const Version& old_version,
    290     bool chrome_updated) {
    291   if (!ExtensionsBrowserClient::Get()->IsValidContext(context))
    292     return;
    293   ExtensionSystem* system = ExtensionSystem::Get(context);
    294   if (!system)
    295     return;
    296 
    297   scoped_ptr<base::ListValue> event_args(new base::ListValue());
    298   base::DictionaryValue* info = new base::DictionaryValue();
    299   event_args->Append(info);
    300   if (old_version.IsValid()) {
    301     info->SetString(kInstallReason, kInstallReasonUpdate);
    302     info->SetString(kInstallPreviousVersion, old_version.GetString());
    303   } else if (chrome_updated) {
    304     info->SetString(kInstallReason, kInstallReasonChromeUpdate);
    305   } else {
    306     info->SetString(kInstallReason, kInstallReasonInstall);
    307   }
    308   DCHECK(system->event_router());
    309   scoped_ptr<Event> event(
    310       new Event(runtime::OnInstalled::kEventName, event_args.Pass()));
    311   system->event_router()->DispatchEventWithLazyListener(extension_id,
    312                                                         event.Pass());
    313 
    314   if (old_version.IsValid()) {
    315     const Extension* extension =
    316         ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
    317             extension_id);
    318     if (extension && SharedModuleInfo::IsSharedModule(extension)) {
    319       scoped_ptr<ExtensionSet> dependents =
    320           system->GetDependentExtensions(extension);
    321       for (ExtensionSet::const_iterator i = dependents->begin();
    322            i != dependents->end();
    323            i++) {
    324         scoped_ptr<base::ListValue> sm_event_args(new base::ListValue());
    325         base::DictionaryValue* sm_info = new base::DictionaryValue();
    326         sm_event_args->Append(sm_info);
    327         sm_info->SetString(kInstallReason, kInstallReasonSharedModuleUpdate);
    328         sm_info->SetString(kInstallPreviousVersion, old_version.GetString());
    329         sm_info->SetString(kInstallId, extension_id);
    330         scoped_ptr<Event> sm_event(
    331             new Event(runtime::OnInstalled::kEventName, sm_event_args.Pass()));
    332         system->event_router()->DispatchEventWithLazyListener((*i)->id(),
    333                                                               sm_event.Pass());
    334       }
    335     }
    336   }
    337 }
    338 
    339 // static
    340 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
    341     content::BrowserContext* context,
    342     const std::string& extension_id,
    343     const base::DictionaryValue* manifest) {
    344   ExtensionSystem* system = ExtensionSystem::Get(context);
    345   if (!system)
    346     return;
    347 
    348   scoped_ptr<base::ListValue> args(new base::ListValue);
    349   args->Append(manifest->DeepCopy());
    350   DCHECK(system->event_router());
    351   scoped_ptr<Event> event(
    352       new Event(runtime::OnUpdateAvailable::kEventName, args.Pass()));
    353   system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
    354 }
    355 
    356 // static
    357 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
    358     content::BrowserContext* context) {
    359   ExtensionSystem* system = ExtensionSystem::Get(context);
    360   if (!system)
    361     return;
    362 
    363   scoped_ptr<base::ListValue> args(new base::ListValue);
    364   DCHECK(system->event_router());
    365   scoped_ptr<Event> event(
    366       new Event(runtime::OnBrowserUpdateAvailable::kEventName, args.Pass()));
    367   system->event_router()->BroadcastEvent(event.Pass());
    368 }
    369 
    370 // static
    371 void RuntimeEventRouter::DispatchOnRestartRequiredEvent(
    372     content::BrowserContext* context,
    373     const std::string& app_id,
    374     core_api::runtime::OnRestartRequired::Reason reason) {
    375   ExtensionSystem* system = ExtensionSystem::Get(context);
    376   if (!system)
    377     return;
    378 
    379   scoped_ptr<Event> event(
    380       new Event(runtime::OnRestartRequired::kEventName,
    381                 core_api::runtime::OnRestartRequired::Create(reason)));
    382 
    383   DCHECK(system->event_router());
    384   system->event_router()->DispatchEventToExtension(app_id, event.Pass());
    385 }
    386 
    387 // static
    388 void RuntimeEventRouter::OnExtensionUninstalled(
    389     content::BrowserContext* context,
    390     const std::string& extension_id,
    391     UninstallReason reason) {
    392   if (!(reason == UNINSTALL_REASON_USER_INITIATED ||
    393         reason == UNINSTALL_REASON_MANAGEMENT_API)) {
    394     return;
    395   }
    396 
    397   GURL uninstall_url(
    398       GetUninstallURL(ExtensionPrefs::Get(context), extension_id));
    399 
    400   if (uninstall_url.is_empty())
    401     return;
    402 
    403   RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url);
    404 }
    405 
    406 ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() {
    407   ExtensionSystem* system = ExtensionSystem::Get(browser_context());
    408   ExtensionHost* host =
    409       system->process_manager()->GetBackgroundHostForExtension(extension_id());
    410   if (system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context(),
    411                                                               extension())) {
    412     system->lazy_background_task_queue()->AddPendingTask(
    413         browser_context(),
    414         extension_id(),
    415         base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this));
    416   } else if (host) {
    417     OnPageLoaded(host);
    418   } else {
    419     return RespondNow(Error(kNoBackgroundPageError));
    420   }
    421 
    422   return RespondLater();
    423 }
    424 
    425 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
    426   if (host) {
    427     Respond(NoArguments());
    428   } else {
    429     Respond(Error(kPageLoadError));
    430   }
    431 }
    432 
    433 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() {
    434   std::string url_string;
    435   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
    436 
    437   GURL url(url_string);
    438   if (!url.is_valid()) {
    439     return RespondNow(
    440         Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string)));
    441   }
    442   SetUninstallURL(
    443       ExtensionPrefs::Get(browser_context()), extension_id(), url_string);
    444   return RespondNow(NoArguments());
    445 }
    446 
    447 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() {
    448   RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension(
    449       extension_id());
    450   return RespondNow(NoArguments());
    451 }
    452 
    453 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() {
    454   if (!RuntimeAPI::GetFactoryInstance()
    455            ->Get(browser_context())
    456            ->CheckForUpdates(
    457                extension_id(),
    458                base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete,
    459                           this))) {
    460     return RespondNow(Error(kUpdatesDisabledError));
    461   }
    462   return RespondLater();
    463 }
    464 
    465 void RuntimeRequestUpdateCheckFunction::CheckComplete(
    466     const RuntimeAPIDelegate::UpdateCheckResult& result) {
    467   if (result.success) {
    468     base::DictionaryValue* details = new base::DictionaryValue;
    469     details->SetString("version", result.version);
    470     Respond(TwoArguments(new base::StringValue(result.response), details));
    471   } else {
    472     // HMM(kalman): Why does !success not imply Error()?
    473     Respond(OneArgument(new base::StringValue(result.response)));
    474   }
    475 }
    476 
    477 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() {
    478   std::string message;
    479   bool result =
    480       RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
    481           &message);
    482   if (!result) {
    483     return RespondNow(Error(message));
    484   }
    485   return RespondNow(NoArguments());
    486 }
    487 
    488 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
    489   runtime::PlatformInfo info;
    490   if (!RuntimeAPI::GetFactoryInstance()
    491            ->Get(browser_context())
    492            ->GetPlatformInfo(&info)) {
    493     return RespondNow(Error(kPlatformInfoUnavailable));
    494   }
    495   return RespondNow(
    496       ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
    497 }
    498 
    499 ExtensionFunction::ResponseAction
    500 RuntimeGetPackageDirectoryEntryFunction::Run() {
    501   storage::IsolatedContext* isolated_context =
    502       storage::IsolatedContext::GetInstance();
    503   DCHECK(isolated_context);
    504 
    505   std::string relative_path = kPackageDirectoryPath;
    506   base::FilePath path = extension_->path();
    507   std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
    508       storage::kFileSystemTypeNativeLocal, std::string(), path, &relative_path);
    509 
    510   int renderer_id = render_view_host_->GetProcess()->GetID();
    511   content::ChildProcessSecurityPolicy* policy =
    512       content::ChildProcessSecurityPolicy::GetInstance();
    513   policy->GrantReadFileSystem(renderer_id, filesystem_id);
    514   base::DictionaryValue* dict = new base::DictionaryValue();
    515   dict->SetString("fileSystemId", filesystem_id);
    516   dict->SetString("baseName", relative_path);
    517   return RespondNow(OneArgument(dict));
    518 }
    519 
    520 }  // namespace extensions
    521