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(¶ms); 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