1 // Copyright (c) 2013 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/webview/webview_api.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h" 9 #include "chrome/browser/extensions/api/context_menus/context_menus_api.h" 10 #include "chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h" 11 #include "chrome/browser/extensions/tab_helper.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/common/extensions/api/webview.h" 14 #include "content/public/browser/render_process_host.h" 15 #include "content/public/browser/render_view_host.h" 16 #include "content/public/browser/storage_partition.h" 17 #include "content/public/browser/web_contents.h" 18 #include "content/public/common/stop_find_action.h" 19 #include "extensions/common/error_utils.h" 20 #include "third_party/WebKit/public/web/WebFindOptions.h" 21 22 using content::WebContents; 23 using extensions::api::tabs::InjectDetails; 24 using extensions::api::webview::SetPermission::Params; 25 namespace helpers = extensions::context_menus_api_helpers; 26 namespace webview = extensions::api::webview; 27 28 namespace extensions { 29 30 namespace { 31 int MaskForKey(const char* key) { 32 if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0) 33 return content::StoragePartition::REMOVE_DATA_MASK_APPCACHE; 34 if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0) 35 return content::StoragePartition::REMOVE_DATA_MASK_COOKIES; 36 if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0) 37 return content::StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS; 38 if (strcmp(key, extension_browsing_data_api_constants::kIndexedDBKey) == 0) 39 return content::StoragePartition::REMOVE_DATA_MASK_INDEXEDDB; 40 if (strcmp(key, extension_browsing_data_api_constants::kLocalStorageKey) == 0) 41 return content::StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE; 42 if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0) 43 return content::StoragePartition::REMOVE_DATA_MASK_WEBSQL; 44 return 0; 45 } 46 47 } // namespace 48 49 bool WebviewExtensionFunction::RunAsync() { 50 int instance_id = 0; 51 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &instance_id)); 52 WebViewGuest* guest = WebViewGuest::From( 53 render_view_host()->GetProcess()->GetID(), instance_id); 54 if (!guest) 55 return false; 56 57 return RunAsyncSafe(guest); 58 } 59 60 // TODO(lazyboy): Add checks similar to 61 // WebviewExtensionFunction::RunAsyncSafe(WebViewGuest*). 62 bool WebviewContextMenusCreateFunction::RunAsync() { 63 scoped_ptr<webview::ContextMenusCreate::Params> params( 64 webview::ContextMenusCreate::Params::Create(*args_)); 65 EXTENSION_FUNCTION_VALIDATE(params.get()); 66 67 MenuItem::Id id( 68 Profile::FromBrowserContext(browser_context())->IsOffTheRecord(), 69 MenuItem::ExtensionKey(extension_id(), params->instance_id)); 70 71 if (params->create_properties.id.get()) { 72 id.string_uid = *params->create_properties.id; 73 } else { 74 // The Generated Id is added by webview_custom_bindings.js. 75 base::DictionaryValue* properties = NULL; 76 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &properties)); 77 EXTENSION_FUNCTION_VALIDATE( 78 properties->GetInteger(helpers::kGeneratedIdKey, &id.uid)); 79 } 80 81 bool success = extensions::context_menus_api_helpers::CreateMenuItem( 82 params->create_properties, 83 Profile::FromBrowserContext(browser_context()), 84 GetExtension(), 85 id, 86 &error_); 87 88 SendResponse(success); 89 return success; 90 } 91 92 bool WebviewNavigateFunction::RunAsyncSafe(WebViewGuest* guest) { 93 scoped_ptr<webview::Navigate::Params> params( 94 webview::Navigate::Params::Create(*args_)); 95 EXTENSION_FUNCTION_VALIDATE(params.get()); 96 std::string src = params->src; 97 guest->NavigateGuest(src); 98 return true; 99 } 100 101 bool WebviewContextMenusUpdateFunction::RunAsync() { 102 scoped_ptr<webview::ContextMenusUpdate::Params> params( 103 webview::ContextMenusUpdate::Params::Create(*args_)); 104 EXTENSION_FUNCTION_VALIDATE(params.get()); 105 106 Profile* profile = Profile::FromBrowserContext(browser_context()); 107 MenuItem::Id item_id( 108 profile->IsOffTheRecord(), 109 MenuItem::ExtensionKey(extension_id(), params->instance_id)); 110 111 if (params->id.as_string) 112 item_id.string_uid = *params->id.as_string; 113 else if (params->id.as_integer) 114 item_id.uid = *params->id.as_integer; 115 else 116 NOTREACHED(); 117 118 bool success = extensions::context_menus_api_helpers::UpdateMenuItem( 119 params->update_properties, profile, GetExtension(), item_id, &error_); 120 SendResponse(success); 121 return success; 122 } 123 124 bool WebviewContextMenusRemoveFunction::RunAsync() { 125 scoped_ptr<webview::ContextMenusRemove::Params> params( 126 webview::ContextMenusRemove::Params::Create(*args_)); 127 EXTENSION_FUNCTION_VALIDATE(params.get()); 128 129 MenuManager* menu_manager = 130 MenuManager::Get(Profile::FromBrowserContext(browser_context())); 131 132 MenuItem::Id id( 133 Profile::FromBrowserContext(browser_context())->IsOffTheRecord(), 134 MenuItem::ExtensionKey(extension_id(), params->instance_id)); 135 136 if (params->menu_item_id.as_string) { 137 id.string_uid = *params->menu_item_id.as_string; 138 } else if (params->menu_item_id.as_integer) { 139 id.uid = *params->menu_item_id.as_integer; 140 } else { 141 NOTREACHED(); 142 } 143 144 bool success = true; 145 MenuItem* item = menu_manager->GetItemById(id); 146 // Ensure one <webview> can't remove another's menu items. 147 if (!item || item->id().extension_key != id.extension_key) { 148 error_ = ErrorUtils::FormatErrorMessage( 149 context_menus_api_helpers::kCannotFindItemError, 150 context_menus_api_helpers::GetIDString(id)); 151 success = false; 152 } else if (!menu_manager->RemoveContextMenuItem(id)) { 153 success = false; 154 } 155 156 SendResponse(success); 157 return success; 158 } 159 160 bool WebviewContextMenusRemoveAllFunction::RunAsync() { 161 scoped_ptr<webview::ContextMenusRemoveAll::Params> params( 162 webview::ContextMenusRemoveAll::Params::Create(*args_)); 163 EXTENSION_FUNCTION_VALIDATE(params.get()); 164 165 MenuManager* menu_manager = 166 MenuManager::Get(Profile::FromBrowserContext(browser_context())); 167 168 int webview_instance_id = params->instance_id; 169 menu_manager->RemoveAllContextItems( 170 MenuItem::ExtensionKey(GetExtension()->id(), webview_instance_id)); 171 SendResponse(true); 172 return true; 173 } 174 175 WebviewClearDataFunction::WebviewClearDataFunction() 176 : remove_mask_(0), bad_message_(false) {} 177 178 WebviewClearDataFunction::~WebviewClearDataFunction() {} 179 180 // Parses the |dataToRemove| argument to generate the remove mask. Sets 181 // |bad_message_| (like EXTENSION_FUNCTION_VALIDATE would if this were a bool 182 // method) if 'dataToRemove' is not present. 183 uint32 WebviewClearDataFunction::GetRemovalMask() { 184 base::DictionaryValue* data_to_remove; 185 if (!args_->GetDictionary(2, &data_to_remove)) { 186 bad_message_ = true; 187 return 0; 188 } 189 190 uint32 remove_mask = 0; 191 for (base::DictionaryValue::Iterator i(*data_to_remove); 192 !i.IsAtEnd(); 193 i.Advance()) { 194 bool selected = false; 195 if (!i.value().GetAsBoolean(&selected)) { 196 bad_message_ = true; 197 return 0; 198 } 199 if (selected) 200 remove_mask |= MaskForKey(i.key().c_str()); 201 } 202 203 return remove_mask; 204 } 205 206 // TODO(lazyboy): Parameters in this extension function are similar (or a 207 // sub-set) to BrowsingDataRemoverFunction. How can we share this code? 208 bool WebviewClearDataFunction::RunAsyncSafe(WebViewGuest* guest) { 209 // Grab the initial |options| parameter, and parse out the arguments. 210 base::DictionaryValue* options; 211 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options)); 212 DCHECK(options); 213 214 // If |ms_since_epoch| isn't set, default it to 0. 215 double ms_since_epoch; 216 if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey, 217 &ms_since_epoch)) { 218 ms_since_epoch = 0; 219 } 220 221 // base::Time takes a double that represents seconds since epoch. JavaScript 222 // gives developers milliseconds, so do a quick conversion before populating 223 // the object. Also, Time::FromDoubleT converts double time 0 to empty Time 224 // object. So we need to do special handling here. 225 remove_since_ = (ms_since_epoch == 0) ? 226 base::Time::UnixEpoch() : 227 base::Time::FromDoubleT(ms_since_epoch / 1000.0); 228 229 remove_mask_ = GetRemovalMask(); 230 if (bad_message_) 231 return false; 232 233 AddRef(); // Balanced below or in WebviewClearDataFunction::Done(). 234 235 bool scheduled = false; 236 if (remove_mask_) { 237 scheduled = guest->ClearData( 238 remove_since_, 239 remove_mask_, 240 base::Bind(&WebviewClearDataFunction::ClearDataDone, 241 this)); 242 } 243 if (!remove_mask_ || !scheduled) { 244 SendResponse(false); 245 Release(); // Balanced above. 246 return false; 247 } 248 249 // Will finish asynchronously. 250 return true; 251 } 252 253 void WebviewClearDataFunction::ClearDataDone() { 254 Release(); // Balanced in RunAsync(). 255 SendResponse(true); 256 } 257 258 WebviewExecuteCodeFunction::WebviewExecuteCodeFunction() 259 : guest_instance_id_(0), guest_src_(GURL::EmptyGURL()) {} 260 261 WebviewExecuteCodeFunction::~WebviewExecuteCodeFunction() { 262 } 263 264 bool WebviewExecuteCodeFunction::Init() { 265 if (details_.get()) 266 return true; 267 268 if (!args_->GetInteger(0, &guest_instance_id_)) 269 return false; 270 271 if (!guest_instance_id_) 272 return false; 273 274 std::string src; 275 if (!args_->GetString(1, &src)) 276 return false; 277 278 guest_src_ = GURL(src); 279 if (!guest_src_.is_valid()) 280 return false; 281 282 base::DictionaryValue* details_value = NULL; 283 if (!args_->GetDictionary(2, &details_value)) 284 return false; 285 scoped_ptr<InjectDetails> details(new InjectDetails()); 286 if (!InjectDetails::Populate(*details_value, details.get())) 287 return false; 288 289 details_ = details.Pass(); 290 return true; 291 } 292 293 bool WebviewExecuteCodeFunction::ShouldInsertCSS() const { 294 return false; 295 } 296 297 bool WebviewExecuteCodeFunction::CanExecuteScriptOnPage() { 298 return true; 299 } 300 301 extensions::ScriptExecutor* WebviewExecuteCodeFunction::GetScriptExecutor() { 302 WebViewGuest* guest = WebViewGuest::From( 303 render_view_host()->GetProcess()->GetID(), guest_instance_id_); 304 if (!guest) 305 return NULL; 306 307 return guest->script_executor(); 308 } 309 310 bool WebviewExecuteCodeFunction::IsWebView() const { 311 return true; 312 } 313 314 const GURL& WebviewExecuteCodeFunction::GetWebViewSrc() const { 315 return guest_src_; 316 } 317 318 WebviewExecuteScriptFunction::WebviewExecuteScriptFunction() { 319 } 320 321 void WebviewExecuteScriptFunction::OnExecuteCodeFinished( 322 const std::string& error, 323 int32 on_page_id, 324 const GURL& on_url, 325 const base::ListValue& result) { 326 if (error.empty()) 327 SetResult(result.DeepCopy()); 328 WebviewExecuteCodeFunction::OnExecuteCodeFinished(error, on_page_id, on_url, 329 result); 330 } 331 332 WebviewInsertCSSFunction::WebviewInsertCSSFunction() { 333 } 334 335 bool WebviewInsertCSSFunction::ShouldInsertCSS() const { 336 return true; 337 } 338 339 WebviewCaptureVisibleRegionFunction::WebviewCaptureVisibleRegionFunction() { 340 } 341 342 WebviewCaptureVisibleRegionFunction::~WebviewCaptureVisibleRegionFunction() { 343 } 344 345 bool WebviewCaptureVisibleRegionFunction::IsScreenshotEnabled() { 346 return true; 347 } 348 349 WebContents* WebviewCaptureVisibleRegionFunction::GetWebContentsForID( 350 int instance_id) { 351 WebViewGuest* guest = WebViewGuest::From( 352 render_view_host()->GetProcess()->GetID(), instance_id); 353 return guest ? guest->guest_web_contents() : NULL; 354 } 355 356 void WebviewCaptureVisibleRegionFunction::OnCaptureFailure( 357 FailureReason reason) { 358 SendResponse(false); 359 } 360 361 WebviewSetNameFunction::WebviewSetNameFunction() { 362 } 363 364 WebviewSetNameFunction::~WebviewSetNameFunction() { 365 } 366 367 WebviewSetZoomFunction::WebviewSetZoomFunction() { 368 } 369 370 WebviewSetZoomFunction::~WebviewSetZoomFunction() { 371 } 372 373 bool WebviewSetNameFunction::RunAsyncSafe(WebViewGuest* guest) { 374 scoped_ptr<webview::SetName::Params> params( 375 webview::SetName::Params::Create(*args_)); 376 EXTENSION_FUNCTION_VALIDATE(params.get()); 377 guest->SetName(params->frame_name); 378 SendResponse(true); 379 return true; 380 } 381 382 bool WebviewSetZoomFunction::RunAsyncSafe(WebViewGuest* guest) { 383 scoped_ptr<webview::SetZoom::Params> params( 384 webview::SetZoom::Params::Create(*args_)); 385 EXTENSION_FUNCTION_VALIDATE(params.get()); 386 guest->SetZoom(params->zoom_factor); 387 388 SendResponse(true); 389 return true; 390 } 391 392 WebviewGetZoomFunction::WebviewGetZoomFunction() { 393 } 394 395 WebviewGetZoomFunction::~WebviewGetZoomFunction() { 396 } 397 398 bool WebviewGetZoomFunction::RunAsyncSafe(WebViewGuest* guest) { 399 scoped_ptr<webview::GetZoom::Params> params( 400 webview::GetZoom::Params::Create(*args_)); 401 EXTENSION_FUNCTION_VALIDATE(params.get()); 402 403 double zoom_factor = guest->GetZoom(); 404 SetResult(base::Value::CreateDoubleValue(zoom_factor)); 405 SendResponse(true); 406 return true; 407 } 408 409 WebviewFindFunction::WebviewFindFunction() { 410 } 411 412 WebviewFindFunction::~WebviewFindFunction() { 413 } 414 415 bool WebviewFindFunction::RunAsyncSafe(WebViewGuest* guest) { 416 scoped_ptr<webview::Find::Params> params( 417 webview::Find::Params::Create(*args_)); 418 EXTENSION_FUNCTION_VALIDATE(params.get()); 419 420 // Convert the std::string search_text to string16. 421 base::string16 search_text; 422 base::UTF8ToUTF16(params->search_text.c_str(), 423 params->search_text.length(), 424 &search_text); 425 426 // Set the find options to their default values. 427 blink::WebFindOptions options; 428 if (params->options) { 429 options.forward = 430 params->options->backward ? !*params->options->backward : true; 431 options.matchCase = 432 params->options->match_case ? *params->options->match_case : false; 433 } 434 435 guest->Find(search_text, options, this); 436 return true; 437 } 438 439 WebviewStopFindingFunction::WebviewStopFindingFunction() { 440 } 441 442 WebviewStopFindingFunction::~WebviewStopFindingFunction() { 443 } 444 445 bool WebviewStopFindingFunction::RunAsyncSafe(WebViewGuest* guest) { 446 scoped_ptr<webview::StopFinding::Params> params( 447 webview::StopFinding::Params::Create(*args_)); 448 EXTENSION_FUNCTION_VALIDATE(params.get()); 449 450 // Set the StopFindAction. 451 content::StopFindAction action; 452 switch (params->action) { 453 case webview::StopFinding::Params::ACTION_CLEAR: 454 action = content::STOP_FIND_ACTION_CLEAR_SELECTION; 455 break; 456 case webview::StopFinding::Params::ACTION_KEEP: 457 action = content::STOP_FIND_ACTION_KEEP_SELECTION; 458 break; 459 case webview::StopFinding::Params::ACTION_ACTIVATE: 460 action = content::STOP_FIND_ACTION_ACTIVATE_SELECTION; 461 break; 462 default: 463 action = content::STOP_FIND_ACTION_KEEP_SELECTION; 464 } 465 466 guest->StopFinding(action); 467 return true; 468 } 469 470 WebviewGoFunction::WebviewGoFunction() { 471 } 472 473 WebviewGoFunction::~WebviewGoFunction() { 474 } 475 476 bool WebviewGoFunction::RunAsyncSafe(WebViewGuest* guest) { 477 scoped_ptr<webview::Go::Params> params(webview::Go::Params::Create(*args_)); 478 EXTENSION_FUNCTION_VALIDATE(params.get()); 479 480 guest->Go(params->relative_index); 481 return true; 482 } 483 484 WebviewReloadFunction::WebviewReloadFunction() { 485 } 486 487 WebviewReloadFunction::~WebviewReloadFunction() { 488 } 489 490 bool WebviewReloadFunction::RunAsyncSafe(WebViewGuest* guest) { 491 guest->Reload(); 492 return true; 493 } 494 495 WebviewSetPermissionFunction::WebviewSetPermissionFunction() { 496 } 497 498 WebviewSetPermissionFunction::~WebviewSetPermissionFunction() { 499 } 500 501 bool WebviewSetPermissionFunction::RunAsyncSafe(WebViewGuest* guest) { 502 scoped_ptr<webview::SetPermission::Params> params( 503 webview::SetPermission::Params::Create(*args_)); 504 EXTENSION_FUNCTION_VALIDATE(params.get()); 505 506 WebViewGuest::PermissionResponseAction action = WebViewGuest::DEFAULT; 507 switch (params->action) { 508 case Params::ACTION_ALLOW: 509 action = WebViewGuest::ALLOW; 510 break; 511 case Params::ACTION_DENY: 512 action = WebViewGuest::DENY; 513 break; 514 case Params::ACTION_DEFAULT: 515 break; 516 default: 517 NOTREACHED(); 518 } 519 520 std::string user_input; 521 if (params->user_input) 522 user_input = *params->user_input; 523 524 WebViewGuest::SetPermissionResult result = 525 guest->SetPermission(params->request_id, action, user_input); 526 527 EXTENSION_FUNCTION_VALIDATE(result != WebViewGuest::SET_PERMISSION_INVALID); 528 529 SetResult(base::Value::CreateBooleanValue( 530 result == WebViewGuest::SET_PERMISSION_ALLOWED)); 531 SendResponse(true); 532 return true; 533 } 534 535 WebviewShowContextMenuFunction::WebviewShowContextMenuFunction() { 536 } 537 538 WebviewShowContextMenuFunction::~WebviewShowContextMenuFunction() { 539 } 540 541 bool WebviewShowContextMenuFunction::RunAsyncSafe(WebViewGuest* guest) { 542 scoped_ptr<webview::ShowContextMenu::Params> params( 543 webview::ShowContextMenu::Params::Create(*args_)); 544 EXTENSION_FUNCTION_VALIDATE(params.get()); 545 546 // TODO(lazyboy): Actually implement filtering menu items, we pass NULL for 547 // now. 548 guest->ShowContextMenu(params->request_id, NULL); 549 550 SendResponse(true); 551 return true; 552 } 553 554 WebviewOverrideUserAgentFunction::WebviewOverrideUserAgentFunction() { 555 } 556 557 WebviewOverrideUserAgentFunction::~WebviewOverrideUserAgentFunction() { 558 } 559 560 bool WebviewOverrideUserAgentFunction::RunAsyncSafe(WebViewGuest* guest) { 561 scoped_ptr<extensions::api::webview::OverrideUserAgent::Params> params( 562 extensions::api::webview::OverrideUserAgent::Params::Create(*args_)); 563 EXTENSION_FUNCTION_VALIDATE(params.get()); 564 565 guest->SetUserAgentOverride(params->user_agent_override); 566 return true; 567 } 568 569 WebviewStopFunction::WebviewStopFunction() { 570 } 571 572 WebviewStopFunction::~WebviewStopFunction() { 573 } 574 575 bool WebviewStopFunction::RunAsyncSafe(WebViewGuest* guest) { 576 guest->Stop(); 577 return true; 578 } 579 580 WebviewTerminateFunction::WebviewTerminateFunction() { 581 } 582 583 WebviewTerminateFunction::~WebviewTerminateFunction() { 584 } 585 586 bool WebviewTerminateFunction::RunAsyncSafe(WebViewGuest* guest) { 587 guest->Terminate(); 588 return true; 589 } 590 591 } // namespace extensions 592