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 "base/bind.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/synchronization/waitable_event.h" 14 #include "base/time/time.h" 15 #include "base/values.h" 16 #include "chrome/browser/automation/automation_provider.h" 17 #include "chrome/browser/automation/automation_provider_json.h" 18 #include "chrome/browser/extensions/extension_service.h" 19 #include "chrome/browser/extensions/extension_system.h" 20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/sessions/session_id.h" 22 #include "chrome/browser/sessions/session_tab_helper.h" 23 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" 24 #include "chrome/browser/ui/browser.h" 25 #include "chrome/browser/ui/browser_iterator.h" 26 #include "chrome/browser/ui/browser_list.h" 27 #include "chrome/browser/ui/host_desktop.h" 28 #include "chrome/browser/ui/tabs/tab_strip_model.h" 29 #include "content/public/browser/browser_thread.h" 30 #include "content/public/browser/render_process_host.h" 31 #include "content/public/browser/render_view_host.h" 32 #include "content/public/browser/web_contents.h" 33 #include "extensions/browser/process_manager.h" 34 #include "extensions/browser/view_type_utils.h" 35 #include "extensions/common/extension.h" 36 #include "net/cookies/canonical_cookie.h" 37 #include "net/cookies/cookie_constants.h" 38 #include "net/cookies/cookie_monster.h" 39 #include "net/cookies/cookie_store.h" 40 #include "net/url_request/url_request_context.h" 41 #include "net/url_request/url_request_context_getter.h" 42 43 #if defined(OS_CHROMEOS) 44 #include "chrome/browser/chromeos/login/existing_user_controller.h" 45 #include "chrome/browser/chromeos/login/login_display.h" 46 #include "chrome/browser/chromeos/login/login_display_host_impl.h" 47 #include "chrome/browser/chromeos/login/webui_login_display.h" 48 #include "chrome/browser/profiles/profile_manager.h" 49 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" 50 #endif 51 52 using content::BrowserThread; 53 using content::RenderViewHost; 54 using content::WebContents; 55 56 #if defined(OS_CHROMEOS) 57 using chromeos::ExistingUserController; 58 using chromeos::User; 59 using chromeos::UserManager; 60 #endif 61 62 namespace { 63 64 void GetCookiesCallback(base::WaitableEvent* event, 65 std::string* cookies, 66 const std::string& cookie_line) { 67 *cookies = cookie_line; 68 event->Signal(); 69 } 70 71 void GetCookiesOnIOThread( 72 const GURL& url, 73 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 74 base::WaitableEvent* event, 75 std::string* cookies) { 76 context_getter->GetURLRequestContext()->cookie_store()-> 77 GetCookiesWithOptionsAsync(url, net::CookieOptions(), 78 base::Bind(&GetCookiesCallback, event, cookies)); 79 } 80 81 void GetCanonicalCookiesCallback( 82 base::WaitableEvent* event, 83 net::CookieList* cookie_list, 84 const net::CookieList& cookies) { 85 *cookie_list = cookies; 86 event->Signal(); 87 } 88 89 void GetCanonicalCookiesOnIOThread( 90 const GURL& url, 91 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 92 base::WaitableEvent* event, 93 net::CookieList* cookie_list) { 94 context_getter->GetURLRequestContext()->cookie_store()-> 95 GetCookieMonster()->GetAllCookiesForURLAsync( 96 url, 97 base::Bind(&GetCanonicalCookiesCallback, event, cookie_list)); 98 } 99 100 void SetCookieCallback(base::WaitableEvent* event, 101 bool* success, 102 bool result) { 103 *success = result; 104 event->Signal(); 105 } 106 107 void SetCookieOnIOThread( 108 const GURL& url, 109 const std::string& value, 110 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 111 base::WaitableEvent* event, 112 bool* success) { 113 context_getter->GetURLRequestContext()->cookie_store()-> 114 SetCookieWithOptionsAsync( 115 url, value, net::CookieOptions(), 116 base::Bind(&SetCookieCallback, event, success)); 117 } 118 119 void SetCookieWithDetailsOnIOThread( 120 const GURL& url, 121 const net::CanonicalCookie& cookie, 122 const std::string& original_domain, 123 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 124 base::WaitableEvent* event, 125 bool* success) { 126 net::CookieMonster* cookie_monster = 127 context_getter->GetURLRequestContext()->cookie_store()-> 128 GetCookieMonster(); 129 cookie_monster->SetCookieWithDetailsAsync( 130 url, cookie.Name(), cookie.Value(), original_domain, 131 cookie.Path(), cookie.ExpiryDate(), cookie.IsSecure(), 132 cookie.IsHttpOnly(), cookie.Priority(), 133 base::Bind(&SetCookieCallback, event, success)); 134 } 135 136 void DeleteCookieCallback(base::WaitableEvent* event) { 137 event->Signal(); 138 } 139 140 void DeleteCookieOnIOThread( 141 const GURL& url, 142 const std::string& name, 143 const scoped_refptr<net::URLRequestContextGetter>& context_getter, 144 base::WaitableEvent* event) { 145 context_getter->GetURLRequestContext()->cookie_store()-> 146 DeleteCookieAsync(url, name, 147 base::Bind(&DeleteCookieCallback, event)); 148 } 149 150 } // namespace 151 152 namespace automation_util { 153 154 Browser* GetBrowserAt(int index) { 155 // The automation layer doesn't support non-native desktops. 156 BrowserList* native_list = 157 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE); 158 if (index < 0 || index >= static_cast<int>(native_list->size())) 159 return NULL; 160 return native_list->get(index); 161 } 162 163 WebContents* GetWebContentsAt(int browser_index, int tab_index) { 164 if (tab_index < 0) 165 return NULL; 166 Browser* browser = GetBrowserAt(browser_index); 167 if (!browser || tab_index >= browser->tab_strip_model()->count()) 168 return NULL; 169 return browser->tab_strip_model()->GetWebContentsAt(tab_index); 170 } 171 172 #if defined(OS_CHROMEOS) 173 Profile* GetCurrentProfileOnChromeOS(std::string* error_message) { 174 const UserManager* user_manager = UserManager::Get(); 175 if (!user_manager) { 176 *error_message = "No user manager."; 177 return NULL; 178 } 179 if (!user_manager->IsUserLoggedIn()) { 180 ExistingUserController* controller = 181 ExistingUserController::current_controller(); 182 if (!controller) { 183 *error_message = "Cannot get controller though user is not logged in."; 184 return NULL; 185 } 186 chromeos::LoginDisplayHostImpl* webui_host = 187 static_cast<chromeos::LoginDisplayHostImpl*>( 188 controller->login_display_host()); 189 content::WebUI* web_ui = webui_host->GetOobeUI()->web_ui(); 190 if (!web_ui) { 191 *error_message = "Unable to get webui from login display host."; 192 return NULL; 193 } 194 return Profile::FromWebUI(web_ui); 195 } else { 196 return ProfileManager::GetActiveUserProfileOrOffTheRecord(); 197 } 198 return NULL; 199 } 200 #endif // defined(OS_CHROMEOS) 201 202 Browser* GetBrowserForTab(WebContents* tab) { 203 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 204 Browser* browser = *it; 205 for (int tab_index = 0; 206 tab_index < browser->tab_strip_model()->count(); 207 ++tab_index) { 208 if (browser->tab_strip_model()->GetWebContentsAt(tab_index) == tab) 209 return browser; 210 } 211 } 212 return NULL; 213 } 214 215 net::URLRequestContextGetter* GetRequestContext(WebContents* contents) { 216 // Since we may be on the UI thread don't call GetURLRequestContext(). 217 // Get the request context specific to the current WebContents and app. 218 return contents->GetBrowserContext()->GetRequestContextForRenderProcess( 219 contents->GetRenderProcessHost()->GetID()); 220 } 221 222 void GetCookies(const GURL& url, 223 WebContents* contents, 224 int* value_size, 225 std::string* value) { 226 *value_size = -1; 227 if (url.is_valid() && contents) { 228 scoped_refptr<net::URLRequestContextGetter> context_getter = 229 GetRequestContext(contents); 230 base::WaitableEvent event(true /* manual reset */, 231 false /* not initially signaled */); 232 CHECK(BrowserThread::PostTask( 233 BrowserThread::IO, FROM_HERE, 234 base::Bind(&GetCookiesOnIOThread, url, context_getter, &event, value))); 235 event.Wait(); 236 237 *value_size = static_cast<int>(value->size()); 238 } 239 } 240 241 void SetCookie(const GURL& url, 242 const std::string& value, 243 WebContents* contents, 244 int* response_value) { 245 *response_value = -1; 246 247 if (url.is_valid() && contents) { 248 scoped_refptr<net::URLRequestContextGetter> context_getter = 249 GetRequestContext(contents); 250 base::WaitableEvent event(true /* manual reset */, 251 false /* not initially signaled */); 252 bool success = false; 253 CHECK(BrowserThread::PostTask( 254 BrowserThread::IO, FROM_HERE, 255 base::Bind(&SetCookieOnIOThread, url, value, context_getter, &event, 256 &success))); 257 event.Wait(); 258 if (success) 259 *response_value = 1; 260 } 261 } 262 263 void DeleteCookie(const GURL& url, 264 const std::string& cookie_name, 265 WebContents* contents, 266 bool* success) { 267 *success = false; 268 if (url.is_valid() && contents) { 269 scoped_refptr<net::URLRequestContextGetter> context_getter = 270 GetRequestContext(contents); 271 base::WaitableEvent event(true /* manual reset */, 272 false /* not initially signaled */); 273 CHECK(BrowserThread::PostTask( 274 BrowserThread::IO, FROM_HERE, 275 base::Bind(&DeleteCookieOnIOThread, url, cookie_name, context_getter, 276 &event))); 277 event.Wait(); 278 *success = true; 279 } 280 } 281 282 void GetCookiesJSON(AutomationProvider* provider, 283 DictionaryValue* args, 284 IPC::Message* reply_message) { 285 AutomationJSONReply reply(provider, reply_message); 286 std::string url; 287 if (!args->GetString("url", &url)) { 288 reply.SendError("'url' missing or invalid"); 289 return; 290 } 291 292 // Since we may be on the UI thread don't call GetURLRequestContext(). 293 scoped_refptr<net::URLRequestContextGetter> context_getter = 294 provider->profile()->GetRequestContext(); 295 296 net::CookieList cookie_list; 297 base::WaitableEvent event(true /* manual reset */, 298 false /* not initially signaled */); 299 base::Closure task = base::Bind(&GetCanonicalCookiesOnIOThread, GURL(url), 300 context_getter, &event, &cookie_list); 301 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { 302 reply.SendError("Couldn't post task to get the cookies"); 303 return; 304 } 305 event.Wait(); 306 307 ListValue* list = new ListValue(); 308 for (size_t i = 0; i < cookie_list.size(); ++i) { 309 const net::CanonicalCookie& cookie = cookie_list[i]; 310 DictionaryValue* cookie_dict = new DictionaryValue(); 311 cookie_dict->SetString("name", cookie.Name()); 312 cookie_dict->SetString("value", cookie.Value()); 313 cookie_dict->SetString("path", cookie.Path()); 314 cookie_dict->SetString("domain", cookie.Domain()); 315 cookie_dict->SetBoolean("secure", cookie.IsSecure()); 316 cookie_dict->SetBoolean("http_only", cookie.IsHttpOnly()); 317 if (cookie.IsPersistent()) 318 cookie_dict->SetDouble("expiry", cookie.ExpiryDate().ToDoubleT()); 319 if (cookie.Priority() != net::COOKIE_PRIORITY_DEFAULT) { 320 cookie_dict->SetString("priority", 321 net::CookiePriorityToString(cookie.Priority())); 322 } 323 list->Append(cookie_dict); 324 } 325 DictionaryValue dict; 326 dict.Set("cookies", list); 327 reply.SendSuccess(&dict); 328 } 329 330 void DeleteCookieJSON(AutomationProvider* provider, 331 DictionaryValue* args, 332 IPC::Message* reply_message) { 333 AutomationJSONReply reply(provider, reply_message); 334 std::string url, name; 335 if (!args->GetString("url", &url)) { 336 reply.SendError("'url' missing or invalid"); 337 return; 338 } 339 if (!args->GetString("name", &name)) { 340 reply.SendError("'name' missing or invalid"); 341 return; 342 } 343 344 // Since we may be on the UI thread don't call GetURLRequestContext(). 345 scoped_refptr<net::URLRequestContextGetter> context_getter = 346 provider->profile()->GetRequestContext(); 347 348 base::WaitableEvent event(true /* manual reset */, 349 false /* not initially signaled */); 350 base::Closure task = base::Bind(&DeleteCookieOnIOThread, GURL(url), name, 351 context_getter, &event); 352 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { 353 reply.SendError("Couldn't post task to delete the cookie"); 354 return; 355 } 356 event.Wait(); 357 reply.SendSuccess(NULL); 358 } 359 360 void SetCookieJSON(AutomationProvider* provider, 361 DictionaryValue* args, 362 IPC::Message* reply_message) { 363 AutomationJSONReply reply(provider, reply_message); 364 std::string url; 365 if (!args->GetString("url", &url)) { 366 reply.SendError("'url' missing or invalid"); 367 return; 368 } 369 DictionaryValue* cookie_dict; 370 if (!args->GetDictionary("cookie", &cookie_dict)) { 371 reply.SendError("'cookie' missing or invalid"); 372 return; 373 } 374 std::string name, value; 375 std::string domain; 376 std::string path = "/"; 377 bool secure = false; 378 double expiry = 0; 379 bool http_only = false; 380 net::CookiePriority priority = net::COOKIE_PRIORITY_DEFAULT; 381 382 if (!cookie_dict->GetString("name", &name)) { 383 reply.SendError("'name' missing or invalid"); 384 return; 385 } 386 if (!cookie_dict->GetString("value", &value)) { 387 reply.SendError("'value' missing or invalid"); 388 return; 389 } 390 if (cookie_dict->HasKey("domain") && 391 !cookie_dict->GetString("domain", &domain)) { 392 reply.SendError("optional 'domain' invalid"); 393 return; 394 } 395 if (cookie_dict->HasKey("path") && 396 !cookie_dict->GetString("path", &path)) { 397 reply.SendError("optional 'path' invalid"); 398 return; 399 } 400 if (cookie_dict->HasKey("secure") && 401 !cookie_dict->GetBoolean("secure", &secure)) { 402 reply.SendError("optional 'secure' invalid"); 403 return; 404 } 405 if (cookie_dict->HasKey("expiry")) { 406 if (!cookie_dict->GetDouble("expiry", &expiry)) { 407 reply.SendError("optional 'expiry' invalid"); 408 return; 409 } 410 } 411 if (cookie_dict->HasKey("http_only") && 412 !cookie_dict->GetBoolean("http_only", &http_only)) { 413 reply.SendError("optional 'http_only' invalid"); 414 return; 415 } 416 if (cookie_dict->HasKey("priority")) { 417 std::string priority_string; 418 if (!cookie_dict->GetString("priority", &priority_string)) { 419 reply.SendError("optional 'priority' invalid"); 420 return; 421 } 422 priority = net::StringToCookiePriority(priority_string); 423 } 424 425 scoped_ptr<net::CanonicalCookie> cookie( 426 net::CanonicalCookie::Create( 427 GURL(url), name, value, domain, path, base::Time(), 428 base::Time::FromDoubleT(expiry), secure, http_only, priority)); 429 if (!cookie.get()) { 430 reply.SendError("given 'cookie' parameters are invalid"); 431 return; 432 } 433 434 // Since we may be on the UI thread don't call GetURLRequestContext(). 435 scoped_refptr<net::URLRequestContextGetter> context_getter = 436 provider->profile()->GetRequestContext(); 437 438 base::WaitableEvent event(true /* manual reset */, 439 false /* not initially signaled */); 440 bool success = false; 441 base::Closure task = base::Bind( 442 &SetCookieWithDetailsOnIOThread, GURL(url), *cookie.get(), domain, 443 context_getter, &event, &success); 444 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { 445 reply.SendError("Couldn't post task to set the cookie"); 446 return; 447 } 448 event.Wait(); 449 450 if (!success) { 451 reply.SendError("Could not set the cookie"); 452 return; 453 } 454 reply.SendSuccess(NULL); 455 } 456 457 bool SendErrorIfModalDialogActive(AutomationProvider* provider, 458 IPC::Message* message) { 459 bool active = AppModalDialogQueue::GetInstance()->HasActiveDialog(); 460 if (active) 461 AutomationJSONReply(provider, message).SendError("Blocked by modal dialog"); 462 return active; 463 } 464 465 } // namespace automation_util 466