Home | History | Annotate | Download | only in runtime
      1 // Copyright (c) 2012 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 "chrome/browser/extensions/api/runtime/runtime_api.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/values.h"
     12 #include "chrome/browser/browser_process.h"
     13 #include "chrome/browser/chrome_notification_types.h"
     14 #include "chrome/browser/extensions/event_router.h"
     15 #include "chrome/browser/extensions/extension_host.h"
     16 #include "chrome/browser/extensions/extension_process_manager.h"
     17 #include "chrome/browser/extensions/extension_service.h"
     18 #include "chrome/browser/extensions/extension_system.h"
     19 #include "chrome/browser/extensions/lazy_background_task_queue.h"
     20 #include "chrome/browser/extensions/updater/extension_updater.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/profiles/profile_manager.h"
     23 #include "chrome/browser/ui/browser_finder.h"
     24 #include "chrome/browser/ui/browser_navigator.h"
     25 #include "chrome/browser/ui/browser_window.h"
     26 #include "chrome/common/extensions/background_info.h"
     27 #include "chrome/common/extensions/extension.h"
     28 #include "chrome/common/omaha_query_params/omaha_query_params.h"
     29 #include "content/public/browser/child_process_security_policy.h"
     30 #include "content/public/browser/notification_service.h"
     31 #include "content/public/browser/render_process_host.h"
     32 #include "content/public/browser/render_view_host.h"
     33 #include "extensions/common/error_utils.h"
     34 #include "url/gurl.h"
     35 #include "webkit/browser/fileapi/isolated_context.h"
     36 
     37 namespace GetPlatformInfo = extensions::api::runtime::GetPlatformInfo;
     38 
     39 namespace extensions {
     40 
     41 namespace {
     42 
     43 const char kOnStartupEvent[] = "runtime.onStartup";
     44 const char kOnInstalledEvent[] = "runtime.onInstalled";
     45 const char kOnUpdateAvailableEvent[] = "runtime.onUpdateAvailable";
     46 const char kOnBrowserUpdateAvailableEvent[] =
     47     "runtime.onBrowserUpdateAvailable";
     48 const char kOnRestartRequiredEvent[] = "runtime.onRestartRequired";
     49 const char kNoBackgroundPageError[] = "You do not have a background page.";
     50 const char kPageLoadError[] = "Background page failed to load.";
     51 const char kInstallReason[] = "reason";
     52 const char kInstallReasonChromeUpdate[] = "chrome_update";
     53 const char kInstallReasonUpdate[] = "update";
     54 const char kInstallReasonInstall[] = "install";
     55 const char kInstallPreviousVersion[] = "previousVersion";
     56 const char kInvalidUrlError[] = "Invalid URL.";
     57 const char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
     58 const char kUpdateFound[] = "update_available";
     59 const char kUpdateNotFound[] = "no_update";
     60 const char kUpdateThrottled[] = "throttled";
     61 
     62 // A preference key storing the url loaded when an extension is uninstalled.
     63 const char kUninstallUrl[] = "uninstall_url";
     64 
     65 // The name of the directory to be returned by getPackageDirectoryEntry. This
     66 // particular value does not matter to user code, but is chosen for consistency
     67 // with the equivalent Pepper API.
     68 const char kPackageDirectoryPath[] = "crxfs";
     69 
     70 static void DispatchOnStartupEventImpl(
     71     Profile* profile,
     72     const std::string& extension_id,
     73     bool first_call,
     74     ExtensionHost* host) {
     75   // A NULL host from the LazyBackgroundTaskQueue means the page failed to
     76   // load. Give up.
     77   if (!host && !first_call)
     78     return;
     79 
     80   // Don't send onStartup events to incognito profiles.
     81   if (profile->IsOffTheRecord())
     82     return;
     83 
     84   if (g_browser_process->IsShuttingDown() ||
     85       !g_browser_process->profile_manager()->IsValidProfile(profile))
     86     return;
     87   ExtensionSystem* system = ExtensionSystem::Get(profile);
     88   if (!system)
     89     return;
     90 
     91   // If this is a persistent background page, we want to wait for it to load
     92   // (it might not be ready, since this is startup). But only enqueue once.
     93   // If it fails to load the first time, don't bother trying again.
     94   const Extension* extension =
     95       system->extension_service()->extensions()->GetByID(extension_id);
     96   if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) &&
     97       first_call &&
     98       system->lazy_background_task_queue()->
     99           ShouldEnqueueTask(profile, extension)) {
    100     system->lazy_background_task_queue()->AddPendingTask(
    101         profile, extension_id,
    102         base::Bind(&DispatchOnStartupEventImpl,
    103                    profile, extension_id, false));
    104     return;
    105   }
    106 
    107   scoped_ptr<base::ListValue> event_args(new base::ListValue());
    108   scoped_ptr<Event> event(new Event(kOnStartupEvent, event_args.Pass()));
    109   system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
    110 }
    111 
    112 void SetUninstallUrl(ExtensionPrefs* prefs,
    113                      const std::string& extension_id,
    114                      const std::string& url_string) {
    115   prefs->UpdateExtensionPref(extension_id,
    116                              kUninstallUrl,
    117                              base::Value::CreateStringValue(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 // static
    130 void RuntimeEventRouter::DispatchOnStartupEvent(
    131     Profile* profile, const std::string& extension_id) {
    132   DispatchOnStartupEventImpl(profile, extension_id, true, NULL);
    133 }
    134 
    135 // static
    136 void RuntimeEventRouter::DispatchOnInstalledEvent(
    137     Profile* profile,
    138     const std::string& extension_id,
    139     const Version& old_version,
    140     bool chrome_updated) {
    141   ExtensionSystem* system = ExtensionSystem::Get(profile);
    142   if (!system)
    143     return;
    144 
    145   scoped_ptr<base::ListValue> event_args(new base::ListValue());
    146   base::DictionaryValue* info = new base::DictionaryValue();
    147   event_args->Append(info);
    148   if (old_version.IsValid()) {
    149     info->SetString(kInstallReason, kInstallReasonUpdate);
    150     info->SetString(kInstallPreviousVersion, old_version.GetString());
    151   } else if (chrome_updated) {
    152     info->SetString(kInstallReason, kInstallReasonChromeUpdate);
    153   } else {
    154     info->SetString(kInstallReason, kInstallReasonInstall);
    155   }
    156   DCHECK(system->event_router());
    157   scoped_ptr<Event> event(new Event(kOnInstalledEvent, event_args.Pass()));
    158   system->event_router()->DispatchEventWithLazyListener(extension_id,
    159                                                         event.Pass());
    160 }
    161 
    162 // static
    163 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
    164     Profile* profile,
    165     const std::string& extension_id,
    166     const base::DictionaryValue* manifest) {
    167   ExtensionSystem* system = ExtensionSystem::Get(profile);
    168   if (!system)
    169     return;
    170 
    171   scoped_ptr<base::ListValue> args(new base::ListValue);
    172   args->Append(manifest->DeepCopy());
    173   DCHECK(system->event_router());
    174   scoped_ptr<Event> event(new Event(kOnUpdateAvailableEvent, args.Pass()));
    175   system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
    176 }
    177 
    178 // static
    179 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
    180     Profile* profile) {
    181   ExtensionSystem* system = ExtensionSystem::Get(profile);
    182   if (!system)
    183     return;
    184 
    185   scoped_ptr<base::ListValue> args(new base::ListValue);
    186   DCHECK(system->event_router());
    187   scoped_ptr<Event> event(new Event(kOnBrowserUpdateAvailableEvent,
    188                                     args.Pass()));
    189   system->event_router()->BroadcastEvent(event.Pass());
    190 }
    191 
    192 // static
    193 void RuntimeEventRouter::DispatchOnRestartRequiredEvent(
    194     Profile* profile,
    195     const std::string& app_id,
    196     api::runtime::OnRestartRequired::Reason reason) {
    197   ExtensionSystem* system = ExtensionSystem::Get(profile);
    198   if (!system)
    199     return;
    200 
    201   scoped_ptr<Event> event(
    202       new Event(kOnRestartRequiredEvent,
    203                 api::runtime::OnRestartRequired::Create(reason)));
    204 
    205   DCHECK(system->event_router());
    206   system->event_router()->DispatchEventToExtension(app_id, event.Pass());
    207 }
    208 
    209 // static
    210 void RuntimeEventRouter::OnExtensionUninstalled(
    211     Profile* profile,
    212     const std::string& extension_id) {
    213 #if defined(ENABLE_EXTENSIONS)
    214   GURL uninstall_url(GetUninstallUrl(ExtensionPrefs::Get(profile),
    215                                      extension_id));
    216 
    217   if (uninstall_url.is_empty())
    218     return;
    219 
    220   Browser* browser = chrome::FindLastActiveWithProfile(profile,
    221       chrome::GetActiveDesktop());
    222   if (!browser)
    223     browser = new Browser(Browser::CreateParams(profile,
    224                                                 chrome::GetActiveDesktop()));
    225 
    226   chrome::NavigateParams params(browser, uninstall_url,
    227                                 content::PAGE_TRANSITION_CLIENT_REDIRECT);
    228   params.disposition = NEW_FOREGROUND_TAB;
    229   params.user_gesture = false;
    230   chrome::Navigate(&params);
    231 #endif  // defined(ENABLE_EXTENSIONS)
    232 }
    233 
    234 bool RuntimeGetBackgroundPageFunction::RunImpl() {
    235   ExtensionSystem* system = ExtensionSystem::Get(profile());
    236   ExtensionHost* host = system->process_manager()->
    237       GetBackgroundHostForExtension(extension_id());
    238   if (system->lazy_background_task_queue()->ShouldEnqueueTask(
    239           profile(), GetExtension())) {
    240     system->lazy_background_task_queue()->AddPendingTask(
    241        profile(), extension_id(),
    242        base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this));
    243   } else if (host) {
    244     OnPageLoaded(host);
    245   } else {
    246     error_ = kNoBackgroundPageError;
    247     return false;
    248   }
    249 
    250   return true;
    251 }
    252 
    253 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
    254   if (host) {
    255     SendResponse(true);
    256   } else {
    257     error_ = kPageLoadError;
    258     SendResponse(false);
    259   }
    260 }
    261 
    262 bool RuntimeSetUninstallUrlFunction::RunImpl() {
    263   std::string url_string;
    264   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
    265 
    266   GURL url(url_string);
    267   if (!url.is_valid()) {
    268     error_ = ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string);
    269     return false;
    270   }
    271 
    272   SetUninstallUrl(ExtensionPrefs::Get(profile()), extension_id(), url_string);
    273   return true;
    274 }
    275 
    276 bool RuntimeReloadFunction::RunImpl() {
    277   // We can't call ReloadExtension directly, since when this method finishes
    278   // it tries to decrease the reference count for the extension, which fails
    279   // if the extension has already been reloaded; so instead we post a task.
    280   base::MessageLoop::current()->PostTask(FROM_HERE,
    281       base::Bind(&ExtensionService::ReloadExtension,
    282                  profile()->GetExtensionService()->AsWeakPtr(),
    283                  extension_id()));
    284   return true;
    285 }
    286 
    287 RuntimeRequestUpdateCheckFunction::RuntimeRequestUpdateCheckFunction() {
    288   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
    289                  content::NotificationService::AllSources());
    290 }
    291 
    292 bool RuntimeRequestUpdateCheckFunction::RunImpl() {
    293   ExtensionSystem* system = ExtensionSystem::Get(profile());
    294   ExtensionService* service = system->extension_service();
    295   ExtensionUpdater* updater = service->updater();
    296   if (!updater) {
    297     error_ = kUpdatesDisabledError;
    298     return false;
    299   }
    300 
    301   did_reply_ = false;
    302   if (!updater->CheckExtensionSoon(extension_id(), base::Bind(
    303       &RuntimeRequestUpdateCheckFunction::CheckComplete, this))) {
    304     did_reply_ = true;
    305     SetResult(new base::StringValue(kUpdateThrottled));
    306     SendResponse(true);
    307   }
    308   return true;
    309 }
    310 
    311 void RuntimeRequestUpdateCheckFunction::CheckComplete() {
    312   if (did_reply_)
    313     return;
    314 
    315   did_reply_ = true;
    316 
    317   // Since no UPDATE_FOUND notification was seen, this generally would mean
    318   // that no update is found, but a previous update check might have already
    319   // queued up an update, so check for that here to make sure we return the
    320   // right value.
    321   ExtensionSystem* system = ExtensionSystem::Get(profile());
    322   ExtensionService* service = system->extension_service();
    323   const Extension* update = service->GetPendingExtensionUpdate(extension_id());
    324   if (update) {
    325     ReplyUpdateFound(update->VersionString());
    326   } else {
    327     SetResult(new base::StringValue(kUpdateNotFound));
    328   }
    329   SendResponse(true);
    330 }
    331 
    332 void RuntimeRequestUpdateCheckFunction::Observe(
    333     int type,
    334     const content::NotificationSource& source,
    335     const content::NotificationDetails& details) {
    336   if (did_reply_)
    337     return;
    338 
    339   DCHECK(type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND);
    340   typedef const std::pair<std::string, Version> UpdateDetails;
    341   const std::string& id = content::Details<UpdateDetails>(details)->first;
    342   const Version& version = content::Details<UpdateDetails>(details)->second;
    343   if (id == extension_id()) {
    344     ReplyUpdateFound(version.GetString());
    345   }
    346 }
    347 
    348 void RuntimeRequestUpdateCheckFunction::ReplyUpdateFound(
    349     const std::string& version) {
    350   did_reply_ = true;
    351   results_.reset(new base::ListValue);
    352   results_->AppendString(kUpdateFound);
    353   base::DictionaryValue* details = new base::DictionaryValue;
    354   results_->Append(details);
    355   details->SetString("version", version);
    356   SendResponse(true);
    357 }
    358 
    359 bool RuntimeGetPlatformInfoFunction::RunImpl() {
    360   GetPlatformInfo::Results::PlatformInfo info;
    361 
    362   const char* os = chrome::OmahaQueryParams::getOS();
    363   if (strcmp(os, "mac") == 0) {
    364     info.os = GetPlatformInfo::Results::PlatformInfo::OS_MAC_;
    365   } else if (strcmp(os, "win") == 0) {
    366     info.os = GetPlatformInfo::Results::PlatformInfo::OS_WIN_;
    367   } else if (strcmp(os, "android") == 0) {
    368     info.os = GetPlatformInfo::Results::PlatformInfo::OS_ANDROID_;
    369   } else if (strcmp(os, "cros") == 0) {
    370     info.os = GetPlatformInfo::Results::PlatformInfo::OS_CROS_;
    371   } else if (strcmp(os, "linux") == 0) {
    372     info.os = GetPlatformInfo::Results::PlatformInfo::OS_LINUX_;
    373   } else if (strcmp(os, "openbsd") == 0) {
    374     info.os = GetPlatformInfo::Results::PlatformInfo::OS_OPENBSD_;
    375   } else {
    376     NOTREACHED();
    377     return false;
    378   }
    379 
    380   const char* arch = chrome::OmahaQueryParams::getArch();
    381   if (strcmp(arch, "arm") == 0) {
    382     info.arch = GetPlatformInfo::Results::PlatformInfo::ARCH_ARM;
    383   } else if (strcmp(arch, "x86") == 0) {
    384     info.arch = GetPlatformInfo::Results::PlatformInfo::ARCH_X86_32;
    385   } else if (strcmp(arch, "x64") == 0) {
    386     info.arch = GetPlatformInfo::Results::PlatformInfo::ARCH_X86_64;
    387   } else {
    388     NOTREACHED();
    389     return false;
    390   }
    391 
    392   const char* nacl_arch = chrome::OmahaQueryParams::getNaclArch();
    393   if (strcmp(nacl_arch, "arm") == 0) {
    394     info.nacl_arch = GetPlatformInfo::Results::PlatformInfo::NACL_ARCH_ARM;
    395   } else if (strcmp(nacl_arch, "x86-32") == 0) {
    396     info.nacl_arch = GetPlatformInfo::Results::PlatformInfo::NACL_ARCH_X86_32;
    397   } else if (strcmp(nacl_arch, "x86-64") == 0) {
    398     info.nacl_arch = GetPlatformInfo::Results::PlatformInfo::NACL_ARCH_X86_64;
    399   } else {
    400     NOTREACHED();
    401     return false;
    402   }
    403 
    404   results_ = GetPlatformInfo::Results::Create(info);
    405   return true;
    406 }
    407 
    408 bool RuntimeGetPackageDirectoryEntryFunction::RunImpl() {
    409   fileapi::IsolatedContext* isolated_context =
    410       fileapi::IsolatedContext::GetInstance();
    411   DCHECK(isolated_context);
    412 
    413   std::string relative_path = kPackageDirectoryPath;
    414   base::FilePath path = extension_->path();
    415   std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
    416       fileapi::kFileSystemTypeNativeLocal, path, &relative_path);
    417 
    418   int renderer_id = render_view_host_->GetProcess()->GetID();
    419   content::ChildProcessSecurityPolicy* policy =
    420       content::ChildProcessSecurityPolicy::GetInstance();
    421   policy->GrantReadFileSystem(renderer_id, filesystem_id);
    422   base::DictionaryValue* dict = new base::DictionaryValue();
    423   SetResult(dict);
    424   dict->SetString("fileSystemId", filesystem_id);
    425   dict->SetString("baseName", relative_path);
    426   return true;
    427 }
    428 
    429 }   // namespace extensions
    430