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/automation/automation_util.h" 6 7 #include <string> 8 9 #include "ash/shell.h" 10 #include "ash/shell_delegate.h" 11 #include "base/bind.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/synchronization/waitable_event.h" 16 #include "base/time/time.h" 17 #include "base/values.h" 18 #include "chrome/browser/automation/automation_provider.h" 19 #include "chrome/browser/automation/automation_provider_json.h" 20 #include "chrome/browser/extensions/extension_process_manager.h" 21 #include "chrome/browser/extensions/extension_service.h" 22 #include "chrome/browser/extensions/extension_system.h" 23 #include "chrome/browser/profiles/profile.h" 24 #include "chrome/browser/sessions/session_id.h" 25 #include "chrome/browser/sessions/session_tab_helper.h" 26 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" 27 #include "chrome/browser/ui/browser.h" 28 #include "chrome/browser/ui/browser_iterator.h" 29 #include "chrome/browser/ui/browser_list.h" 30 #include "chrome/browser/ui/host_desktop.h" 31 #include "chrome/browser/ui/tabs/tab_strip_model.h" 32 #include "chrome/common/automation_id.h" 33 #include "chrome/common/extensions/extension.h" 34 #include "content/public/browser/browser_thread.h" 35 #include "content/public/browser/render_process_host.h" 36 #include "content/public/browser/render_view_host.h" 37 #include "content/public/browser/web_contents.h" 38 #include "extensions/browser/view_type_utils.h" 39 #include "net/cookies/canonical_cookie.h" 40 #include "net/cookies/cookie_constants.h" 41 #include "net/cookies/cookie_monster.h" 42 #include "net/cookies/cookie_store.h" 43 #include "net/url_request/url_request_context.h" 44 #include "net/url_request/url_request_context_getter.h" 45 46 #if defined(OS_CHROMEOS) 47 #include "chrome/browser/chromeos/login/existing_user_controller.h" 48 #include "chrome/browser/chromeos/login/login_display.h" 49 #include "chrome/browser/chromeos/login/login_display_host_impl.h" 50 #include "chrome/browser/chromeos/login/webui_login_display.h" 51 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" 52 #endif 53 54 #if defined(ENABLE_FULL_PRINTING) 55 #include "chrome/browser/printing/print_preview_dialog_controller.h" 56 #endif 57 58 using content::BrowserThread; 59 using content::RenderViewHost; 60 using content::WebContents; 61 62 #if defined(OS_CHROMEOS) 63 using chromeos::ExistingUserController; 64 using chromeos::User; 65 using chromeos::UserManager; 66 #endif 67 68 namespace { 69 70 void GetCookiesCallback(base::WaitableEvent* event, 71 std::string* cookies, 72 const std::string& cookie_line) { 73 *cookies = cookie_line; 74 event->Signal(); 75 } 76 77 void GetCookiesOnIOThread( 78 const GURL& url, 79 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 80 base::WaitableEvent* event, 81 std::string* cookies) { 82 context_getter->GetURLRequestContext()->cookie_store()-> 83 GetCookiesWithOptionsAsync(url, net::CookieOptions(), 84 base::Bind(&GetCookiesCallback, event, cookies)); 85 } 86 87 void GetCanonicalCookiesCallback( 88 base::WaitableEvent* event, 89 net::CookieList* cookie_list, 90 const net::CookieList& cookies) { 91 *cookie_list = cookies; 92 event->Signal(); 93 } 94 95 void GetCanonicalCookiesOnIOThread( 96 const GURL& url, 97 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 98 base::WaitableEvent* event, 99 net::CookieList* cookie_list) { 100 context_getter->GetURLRequestContext()->cookie_store()-> 101 GetCookieMonster()->GetAllCookiesForURLAsync( 102 url, 103 base::Bind(&GetCanonicalCookiesCallback, event, cookie_list)); 104 } 105 106 void SetCookieCallback(base::WaitableEvent* event, 107 bool* success, 108 bool result) { 109 *success = result; 110 event->Signal(); 111 } 112 113 void SetCookieOnIOThread( 114 const GURL& url, 115 const std::string& value, 116 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 117 base::WaitableEvent* event, 118 bool* success) { 119 context_getter->GetURLRequestContext()->cookie_store()-> 120 SetCookieWithOptionsAsync( 121 url, value, net::CookieOptions(), 122 base::Bind(&SetCookieCallback, event, success)); 123 } 124 125 void SetCookieWithDetailsOnIOThread( 126 const GURL& url, 127 const net::CanonicalCookie& cookie, 128 const std::string& original_domain, 129 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 130 base::WaitableEvent* event, 131 bool* success) { 132 net::CookieMonster* cookie_monster = 133 context_getter->GetURLRequestContext()->cookie_store()-> 134 GetCookieMonster(); 135 cookie_monster->SetCookieWithDetailsAsync( 136 url, cookie.Name(), cookie.Value(), original_domain, 137 cookie.Path(), cookie.ExpiryDate(), cookie.IsSecure(), 138 cookie.IsHttpOnly(), cookie.Priority(), 139 base::Bind(&SetCookieCallback, event, success)); 140 } 141 142 void DeleteCookieCallback(base::WaitableEvent* event) { 143 event->Signal(); 144 } 145 146 void DeleteCookieOnIOThread( 147 const GURL& url, 148 const std::string& name, 149 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 150 base::WaitableEvent* event) { 151 context_getter->GetURLRequestContext()->cookie_store()-> 152 DeleteCookieAsync(url, name, 153 base::Bind(&DeleteCookieCallback, event)); 154 } 155 156 } // namespace 157 158 namespace automation_util { 159 160 Browser* GetBrowserAt(int index) { 161 // The automation layer doesn't support non-native desktops. 162 BrowserList* native_list = 163 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE); 164 if (index < 0 || index >= static_cast<int>(native_list->size())) 165 return NULL; 166 return native_list->get(index); 167 } 168 169 WebContents* GetWebContentsAt(int browser_index, int tab_index) { 170 if (tab_index < 0) 171 return NULL; 172 Browser* browser = GetBrowserAt(browser_index); 173 if (!browser || tab_index >= browser->tab_strip_model()->count()) 174 return NULL; 175 return browser->tab_strip_model()->GetWebContentsAt(tab_index); 176 } 177 178 #if defined(OS_CHROMEOS) 179 Profile* GetCurrentProfileOnChromeOS(std::string* error_message) { 180 const UserManager* user_manager = UserManager::Get(); 181 if (!user_manager) { 182 *error_message = "No user manager."; 183 return NULL; 184 } 185 if (!user_manager->IsUserLoggedIn()) { 186 ExistingUserController* controller = 187 ExistingUserController::current_controller(); 188 if (!controller) { 189 *error_message = "Cannot get controller though user is not logged in."; 190 return NULL; 191 } 192 chromeos::LoginDisplayHostImpl* webui_host = 193 static_cast<chromeos::LoginDisplayHostImpl*>( 194 controller->login_display_host()); 195 content::WebUI* web_ui = webui_host->GetOobeUI()->web_ui(); 196 if (!web_ui) { 197 *error_message = "Unable to get webui from login display host."; 198 return NULL; 199 } 200 return Profile::FromWebUI(web_ui); 201 } else { 202 ash::Shell* shell = ash::Shell::GetInstance(); 203 if (!shell || !shell->delegate()) { 204 *error_message = "Unable to get shell delegate."; 205 return NULL; 206 } 207 return Profile::FromBrowserContext( 208 shell->delegate()->GetCurrentBrowserContext()); 209 } 210 return NULL; 211 } 212 #endif // defined(OS_CHROMEOS) 213 214 Browser* GetBrowserForTab(WebContents* tab) { 215 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 216 Browser* browser = *it; 217 for (int tab_index = 0; 218 tab_index < browser->tab_strip_model()->count(); 219 ++tab_index) { 220 if (browser->tab_strip_model()->GetWebContentsAt(tab_index) == tab) 221 return browser; 222 } 223 } 224 return NULL; 225 } 226 227 net::URLRequestContextGetter* GetRequestContext(WebContents* contents) { 228 // Since we may be on the UI thread don't call GetURLRequestContext(). 229 // Get the request context specific to the current WebContents and app. 230 return contents->GetBrowserContext()->GetRequestContextForRenderProcess( 231 contents->GetRenderProcessHost()->GetID()); 232 } 233 234 void GetCookies(const GURL& url, 235 WebContents* contents, 236 int* value_size, 237 std::string* value) { 238 *value_size = -1; 239 if (url.is_valid() && contents) { 240 scoped_refptr<net::URLRequestContextGetter> context_getter = 241 GetRequestContext(contents); 242 base::WaitableEvent event(true /* manual reset */, 243 false /* not initially signaled */); 244 CHECK(BrowserThread::PostTask( 245 BrowserThread::IO, FROM_HERE, 246 base::Bind(&GetCookiesOnIOThread, url, context_getter, &event, value))); 247 event.Wait(); 248 249 *value_size = static_cast<int>(value->size()); 250 } 251 } 252 253 void SetCookie(const GURL& url, 254 const std::string& value, 255 WebContents* contents, 256 int* response_value) { 257 *response_value = -1; 258 259 if (url.is_valid() && contents) { 260 scoped_refptr<net::URLRequestContextGetter> context_getter = 261 GetRequestContext(contents); 262 base::WaitableEvent event(true /* manual reset */, 263 false /* not initially signaled */); 264 bool success = false; 265 CHECK(BrowserThread::PostTask( 266 BrowserThread::IO, FROM_HERE, 267 base::Bind(&SetCookieOnIOThread, url, value, context_getter, &event, 268 &success))); 269 event.Wait(); 270 if (success) 271 *response_value = 1; 272 } 273 } 274 275 void DeleteCookie(const GURL& url, 276 const std::string& cookie_name, 277 WebContents* contents, 278 bool* success) { 279 *success = false; 280 if (url.is_valid() && contents) { 281 scoped_refptr<net::URLRequestContextGetter> context_getter = 282 GetRequestContext(contents); 283 base::WaitableEvent event(true /* manual reset */, 284 false /* not initially signaled */); 285 CHECK(BrowserThread::PostTask( 286 BrowserThread::IO, FROM_HERE, 287 base::Bind(&DeleteCookieOnIOThread, url, cookie_name, context_getter, 288 &event))); 289 event.Wait(); 290 *success = true; 291 } 292 } 293 294 void GetCookiesJSON(AutomationProvider* provider, 295 DictionaryValue* args, 296 IPC::Message* reply_message) { 297 AutomationJSONReply reply(provider, reply_message); 298 std::string url; 299 if (!args->GetString("url", &url)) { 300 reply.SendError("'url' missing or invalid"); 301 return; 302 } 303 304 // Since we may be on the UI thread don't call GetURLRequestContext(). 305 scoped_refptr<net::URLRequestContextGetter> context_getter = 306 provider->profile()->GetRequestContext(); 307 308 net::CookieList cookie_list; 309 base::WaitableEvent event(true /* manual reset */, 310 false /* not initially signaled */); 311 base::Closure task = base::Bind(&GetCanonicalCookiesOnIOThread, GURL(url), 312 context_getter, &event, &cookie_list); 313 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { 314 reply.SendError("Couldn't post task to get the cookies"); 315 return; 316 } 317 event.Wait(); 318 319 ListValue* list = new ListValue(); 320 for (size_t i = 0; i < cookie_list.size(); ++i) { 321 const net::CanonicalCookie& cookie = cookie_list[i]; 322 DictionaryValue* cookie_dict = new DictionaryValue(); 323 cookie_dict->SetString("name", cookie.Name()); 324 cookie_dict->SetString("value", cookie.Value()); 325 cookie_dict->SetString("path", cookie.Path()); 326 cookie_dict->SetString("domain", cookie.Domain()); 327 cookie_dict->SetBoolean("secure", cookie.IsSecure()); 328 cookie_dict->SetBoolean("http_only", cookie.IsHttpOnly()); 329 if (cookie.IsPersistent()) 330 cookie_dict->SetDouble("expiry", cookie.ExpiryDate().ToDoubleT()); 331 if (cookie.Priority() != net::COOKIE_PRIORITY_DEFAULT) { 332 cookie_dict->SetString("priority", 333 net::CookiePriorityToString(cookie.Priority())); 334 } 335 list->Append(cookie_dict); 336 } 337 DictionaryValue dict; 338 dict.Set("cookies", list); 339 reply.SendSuccess(&dict); 340 } 341 342 void DeleteCookieJSON(AutomationProvider* provider, 343 DictionaryValue* args, 344 IPC::Message* reply_message) { 345 AutomationJSONReply reply(provider, reply_message); 346 std::string url, name; 347 if (!args->GetString("url", &url)) { 348 reply.SendError("'url' missing or invalid"); 349 return; 350 } 351 if (!args->GetString("name", &name)) { 352 reply.SendError("'name' missing or invalid"); 353 return; 354 } 355 356 // Since we may be on the UI thread don't call GetURLRequestContext(). 357 scoped_refptr<net::URLRequestContextGetter> context_getter = 358 provider->profile()->GetRequestContext(); 359 360 base::WaitableEvent event(true /* manual reset */, 361 false /* not initially signaled */); 362 base::Closure task = base::Bind(&DeleteCookieOnIOThread, GURL(url), name, 363 context_getter, &event); 364 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { 365 reply.SendError("Couldn't post task to delete the cookie"); 366 return; 367 } 368 event.Wait(); 369 reply.SendSuccess(NULL); 370 } 371 372 void SetCookieJSON(AutomationProvider* provider, 373 DictionaryValue* args, 374 IPC::Message* reply_message) { 375 AutomationJSONReply reply(provider, reply_message); 376 std::string url; 377 if (!args->GetString("url", &url)) { 378 reply.SendError("'url' missing or invalid"); 379 return; 380 } 381 DictionaryValue* cookie_dict; 382 if (!args->GetDictionary("cookie", &cookie_dict)) { 383 reply.SendError("'cookie' missing or invalid"); 384 return; 385 } 386 std::string name, value; 387 std::string domain; 388 std::string path = "/"; 389 bool secure = false; 390 double expiry = 0; 391 bool http_only = false; 392 net::CookiePriority priority = net::COOKIE_PRIORITY_DEFAULT; 393 394 if (!cookie_dict->GetString("name", &name)) { 395 reply.SendError("'name' missing or invalid"); 396 return; 397 } 398 if (!cookie_dict->GetString("value", &value)) { 399 reply.SendError("'value' missing or invalid"); 400 return; 401 } 402 if (cookie_dict->HasKey("domain") && 403 !cookie_dict->GetString("domain", &domain)) { 404 reply.SendError("optional 'domain' invalid"); 405 return; 406 } 407 if (cookie_dict->HasKey("path") && 408 !cookie_dict->GetString("path", &path)) { 409 reply.SendError("optional 'path' invalid"); 410 return; 411 } 412 if (cookie_dict->HasKey("secure") && 413 !cookie_dict->GetBoolean("secure", &secure)) { 414 reply.SendError("optional 'secure' invalid"); 415 return; 416 } 417 if (cookie_dict->HasKey("expiry")) { 418 if (!cookie_dict->GetDouble("expiry", &expiry)) { 419 reply.SendError("optional 'expiry' invalid"); 420 return; 421 } 422 } 423 if (cookie_dict->HasKey("http_only") && 424 !cookie_dict->GetBoolean("http_only", &http_only)) { 425 reply.SendError("optional 'http_only' invalid"); 426 return; 427 } 428 if (cookie_dict->HasKey("priority")) { 429 std::string priority_string; 430 if (!cookie_dict->GetString("priority", &priority_string)) { 431 reply.SendError("optional 'priority' invalid"); 432 return; 433 } 434 priority = net::StringToCookiePriority(priority_string); 435 } 436 437 scoped_ptr<net::CanonicalCookie> cookie( 438 net::CanonicalCookie::Create( 439 GURL(url), name, value, domain, path, base::Time(), 440 base::Time::FromDoubleT(expiry), secure, http_only, priority)); 441 if (!cookie.get()) { 442 reply.SendError("given 'cookie' parameters are invalid"); 443 return; 444 } 445 446 // Since we may be on the UI thread don't call GetURLRequestContext(). 447 scoped_refptr<net::URLRequestContextGetter> context_getter = 448 provider->profile()->GetRequestContext(); 449 450 base::WaitableEvent event(true /* manual reset */, 451 false /* not initially signaled */); 452 bool success = false; 453 base::Closure task = base::Bind( 454 &SetCookieWithDetailsOnIOThread, GURL(url), *cookie.get(), domain, 455 context_getter, &event, &success); 456 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { 457 reply.SendError("Couldn't post task to set the cookie"); 458 return; 459 } 460 event.Wait(); 461 462 if (!success) { 463 reply.SendError("Could not set the cookie"); 464 return; 465 } 466 reply.SendSuccess(NULL); 467 } 468 469 bool SendErrorIfModalDialogActive(AutomationProvider* provider, 470 IPC::Message* message) { 471 bool active = AppModalDialogQueue::GetInstance()->HasActiveDialog(); 472 if (active) { 473 AutomationJSONReply(provider, message).SendErrorCode( 474 automation::kBlockedByModalDialog); 475 } 476 return active; 477 } 478 479 AutomationId GetIdForTab(const WebContents* tab) { 480 const SessionTabHelper* session_tab_helper = 481 SessionTabHelper::FromWebContents(tab); 482 return AutomationId(AutomationId::kTypeTab, 483 base::IntToString(session_tab_helper->session_id().id())); 484 } 485 486 AutomationId GetIdForExtensionView( 487 const content::RenderViewHost* render_view_host) { 488 AutomationId::Type type; 489 WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host); 490 switch (extensions::GetViewType(web_contents)) { 491 case extensions::VIEW_TYPE_EXTENSION_POPUP: 492 type = AutomationId::kTypeExtensionPopup; 493 break; 494 case extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE: 495 type = AutomationId::kTypeExtensionBgPage; 496 break; 497 case extensions::VIEW_TYPE_EXTENSION_INFOBAR: 498 type = AutomationId::kTypeExtensionInfobar; 499 break; 500 case extensions::VIEW_TYPE_APP_SHELL: 501 type = AutomationId::kTypeAppShell; 502 break; 503 default: 504 type = AutomationId::kTypeInvalid; 505 break; 506 } 507 // Since these extension views do not permit navigation, using the 508 // renderer process and view ID should suffice. 509 std::string id = base::StringPrintf("%d|%d", 510 render_view_host->GetRoutingID(), 511 render_view_host->GetProcess()->GetID()); 512 return AutomationId(type, id); 513 } 514 515 AutomationId GetIdForExtension(const extensions::Extension* extension) { 516 return AutomationId(AutomationId::kTypeExtension, extension->id()); 517 } 518 519 bool GetTabForId(const AutomationId& id, WebContents** tab) { 520 if (id.type() != AutomationId::kTypeTab) 521 return false; 522 523 #if defined(ENABLE_FULL_PRINTING) 524 printing::PrintPreviewDialogController* preview_controller = 525 printing::PrintPreviewDialogController::GetInstance(); 526 #endif 527 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 528 Browser* browser = *it; 529 for (int tab_index = 0; 530 tab_index < browser->tab_strip_model()->count(); 531 ++tab_index) { 532 WebContents* web_contents = 533 browser->tab_strip_model()->GetWebContentsAt(tab_index); 534 SessionTabHelper* session_tab_helper = 535 SessionTabHelper::FromWebContents(web_contents); 536 if (base::IntToString( 537 session_tab_helper->session_id().id()) == id.id()) { 538 *tab = web_contents; 539 return true; 540 } 541 542 #if defined(ENABLE_FULL_PRINTING) 543 if (preview_controller) { 544 WebContents* print_preview_contents = 545 preview_controller->GetPrintPreviewForContents(web_contents); 546 if (print_preview_contents) { 547 SessionTabHelper* preview_session_tab_helper = 548 SessionTabHelper::FromWebContents(print_preview_contents); 549 std::string preview_id = base::IntToString( 550 preview_session_tab_helper->session_id().id()); 551 if (preview_id == id.id()) { 552 *tab = print_preview_contents; 553 return true; 554 } 555 } 556 } 557 #endif 558 } 559 } 560 return false; 561 } 562 563 namespace { 564 565 bool GetExtensionRenderViewForId( 566 const AutomationId& id, 567 Profile* profile, 568 RenderViewHost** rvh) { 569 ExtensionProcessManager* extension_mgr = 570 extensions::ExtensionSystem::Get(profile)->process_manager(); 571 const ExtensionProcessManager::ViewSet view_set = 572 extension_mgr->GetAllViews(); 573 for (ExtensionProcessManager::ViewSet::const_iterator iter = view_set.begin(); 574 iter != view_set.end(); ++iter) { 575 content::RenderViewHost* host = *iter; 576 AutomationId this_id = GetIdForExtensionView(host); 577 if (id == this_id) { 578 *rvh = host; 579 return true; 580 } 581 } 582 return false; 583 } 584 585 } // namespace 586 587 bool GetRenderViewForId( 588 const AutomationId& id, 589 Profile* profile, 590 RenderViewHost** rvh) { 591 switch (id.type()) { 592 case AutomationId::kTypeTab: { 593 WebContents* tab; 594 if (!GetTabForId(id, &tab)) 595 return false; 596 *rvh = tab->GetRenderViewHost(); 597 break; 598 } 599 case AutomationId::kTypeExtensionPopup: 600 case AutomationId::kTypeExtensionBgPage: 601 case AutomationId::kTypeExtensionInfobar: 602 case AutomationId::kTypeAppShell: 603 if (!GetExtensionRenderViewForId(id, profile, rvh)) 604 return false; 605 break; 606 default: 607 return false; 608 } 609 return true; 610 } 611 612 bool GetExtensionForId( 613 const AutomationId& id, 614 Profile* profile, 615 const extensions::Extension** extension) { 616 if (id.type() != AutomationId::kTypeExtension) 617 return false; 618 ExtensionService* service = extensions::ExtensionSystem::Get(profile)-> 619 extension_service(); 620 const extensions::Extension* installed_extension = 621 service->GetInstalledExtension(id.id()); 622 if (installed_extension) 623 *extension = installed_extension; 624 return !!installed_extension; 625 } 626 627 bool DoesObjectWithIdExist(const AutomationId& id, Profile* profile) { 628 switch (id.type()) { 629 case AutomationId::kTypeTab: { 630 WebContents* tab; 631 return GetTabForId(id, &tab); 632 } 633 case AutomationId::kTypeExtensionPopup: 634 case AutomationId::kTypeExtensionBgPage: 635 case AutomationId::kTypeExtensionInfobar: 636 case AutomationId::kTypeAppShell: { 637 RenderViewHost* rvh; 638 return GetExtensionRenderViewForId(id, profile, &rvh); 639 } 640 case AutomationId::kTypeExtension: { 641 const extensions::Extension* extension; 642 return GetExtensionForId(id, profile, &extension); 643 } 644 default: 645 break; 646 } 647 return false; 648 } 649 650 } // namespace automation_util 651