| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/automation/automation_util.h" |
| |
| #include <string> |
| |
| #include "ash/shell.h" |
| #include "ash/shell_delegate.h" |
| #include "base/bind.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/automation/automation_provider.h" |
| #include "chrome/browser/automation/automation_provider_json.h" |
| #include "chrome/browser/extensions/extension_process_manager.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_system.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sessions/session_id.h" |
| #include "chrome/browser/sessions/session_tab_helper.h" |
| #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_iterator.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/host_desktop.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/browser/view_type_utils.h" |
| #include "net/cookies/canonical_cookie.h" |
| #include "net/cookies/cookie_constants.h" |
| #include "net/cookies/cookie_monster.h" |
| #include "net/cookies/cookie_store.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/login/existing_user_controller.h" |
| #include "chrome/browser/chromeos/login/login_display.h" |
| #include "chrome/browser/chromeos/login/login_display_host_impl.h" |
| #include "chrome/browser/chromeos/login/webui_login_display.h" |
| #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" |
| #endif |
| |
| using content::BrowserThread; |
| using content::RenderViewHost; |
| using content::WebContents; |
| |
| #if defined(OS_CHROMEOS) |
| using chromeos::ExistingUserController; |
| using chromeos::User; |
| using chromeos::UserManager; |
| #endif |
| |
| namespace { |
| |
| void GetCookiesCallback(base::WaitableEvent* event, |
| std::string* cookies, |
| const std::string& cookie_line) { |
| *cookies = cookie_line; |
| event->Signal(); |
| } |
| |
| void GetCookiesOnIOThread( |
| const GURL& url, |
| const scoped_refptr<net::URLRequestContextGetter>& context_getter, |
| base::WaitableEvent* event, |
| std::string* cookies) { |
| context_getter->GetURLRequestContext()->cookie_store()-> |
| GetCookiesWithOptionsAsync(url, net::CookieOptions(), |
| base::Bind(&GetCookiesCallback, event, cookies)); |
| } |
| |
| void GetCanonicalCookiesCallback( |
| base::WaitableEvent* event, |
| net::CookieList* cookie_list, |
| const net::CookieList& cookies) { |
| *cookie_list = cookies; |
| event->Signal(); |
| } |
| |
| void GetCanonicalCookiesOnIOThread( |
| const GURL& url, |
| const scoped_refptr<net::URLRequestContextGetter>& context_getter, |
| base::WaitableEvent* event, |
| net::CookieList* cookie_list) { |
| context_getter->GetURLRequestContext()->cookie_store()-> |
| GetCookieMonster()->GetAllCookiesForURLAsync( |
| url, |
| base::Bind(&GetCanonicalCookiesCallback, event, cookie_list)); |
| } |
| |
| void SetCookieCallback(base::WaitableEvent* event, |
| bool* success, |
| bool result) { |
| *success = result; |
| event->Signal(); |
| } |
| |
| void SetCookieOnIOThread( |
| const GURL& url, |
| const std::string& value, |
| const scoped_refptr<net::URLRequestContextGetter>& context_getter, |
| base::WaitableEvent* event, |
| bool* success) { |
| context_getter->GetURLRequestContext()->cookie_store()-> |
| SetCookieWithOptionsAsync( |
| url, value, net::CookieOptions(), |
| base::Bind(&SetCookieCallback, event, success)); |
| } |
| |
| void SetCookieWithDetailsOnIOThread( |
| const GURL& url, |
| const net::CanonicalCookie& cookie, |
| const std::string& original_domain, |
| const scoped_refptr<net::URLRequestContextGetter>& context_getter, |
| base::WaitableEvent* event, |
| bool* success) { |
| net::CookieMonster* cookie_monster = |
| context_getter->GetURLRequestContext()->cookie_store()-> |
| GetCookieMonster(); |
| cookie_monster->SetCookieWithDetailsAsync( |
| url, cookie.Name(), cookie.Value(), original_domain, |
| cookie.Path(), cookie.ExpiryDate(), cookie.IsSecure(), |
| cookie.IsHttpOnly(), cookie.Priority(), |
| base::Bind(&SetCookieCallback, event, success)); |
| } |
| |
| void DeleteCookieCallback(base::WaitableEvent* event) { |
| event->Signal(); |
| } |
| |
| void DeleteCookieOnIOThread( |
| const GURL& url, |
| const std::string& name, |
| const scoped_refptr<net::URLRequestContextGetter>& context_getter, |
| base::WaitableEvent* event) { |
| context_getter->GetURLRequestContext()->cookie_store()-> |
| DeleteCookieAsync(url, name, |
| base::Bind(&DeleteCookieCallback, event)); |
| } |
| |
| } // namespace |
| |
| namespace automation_util { |
| |
| Browser* GetBrowserAt(int index) { |
| // The automation layer doesn't support non-native desktops. |
| BrowserList* native_list = |
| BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE); |
| if (index < 0 || index >= static_cast<int>(native_list->size())) |
| return NULL; |
| return native_list->get(index); |
| } |
| |
| WebContents* GetWebContentsAt(int browser_index, int tab_index) { |
| if (tab_index < 0) |
| return NULL; |
| Browser* browser = GetBrowserAt(browser_index); |
| if (!browser || tab_index >= browser->tab_strip_model()->count()) |
| return NULL; |
| return browser->tab_strip_model()->GetWebContentsAt(tab_index); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| Profile* GetCurrentProfileOnChromeOS(std::string* error_message) { |
| const UserManager* user_manager = UserManager::Get(); |
| if (!user_manager) { |
| *error_message = "No user manager."; |
| return NULL; |
| } |
| if (!user_manager->IsUserLoggedIn()) { |
| ExistingUserController* controller = |
| ExistingUserController::current_controller(); |
| if (!controller) { |
| *error_message = "Cannot get controller though user is not logged in."; |
| return NULL; |
| } |
| chromeos::LoginDisplayHostImpl* webui_host = |
| static_cast<chromeos::LoginDisplayHostImpl*>( |
| controller->login_display_host()); |
| content::WebUI* web_ui = webui_host->GetOobeUI()->web_ui(); |
| if (!web_ui) { |
| *error_message = "Unable to get webui from login display host."; |
| return NULL; |
| } |
| return Profile::FromWebUI(web_ui); |
| } else { |
| ash::Shell* shell = ash::Shell::GetInstance(); |
| if (!shell || !shell->delegate()) { |
| *error_message = "Unable to get shell delegate."; |
| return NULL; |
| } |
| return Profile::FromBrowserContext( |
| shell->delegate()->GetCurrentBrowserContext()); |
| } |
| return NULL; |
| } |
| #endif // defined(OS_CHROMEOS) |
| |
| Browser* GetBrowserForTab(WebContents* tab) { |
| for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| Browser* browser = *it; |
| for (int tab_index = 0; |
| tab_index < browser->tab_strip_model()->count(); |
| ++tab_index) { |
| if (browser->tab_strip_model()->GetWebContentsAt(tab_index) == tab) |
| return browser; |
| } |
| } |
| return NULL; |
| } |
| |
| net::URLRequestContextGetter* GetRequestContext(WebContents* contents) { |
| // Since we may be on the UI thread don't call GetURLRequestContext(). |
| // Get the request context specific to the current WebContents and app. |
| return contents->GetBrowserContext()->GetRequestContextForRenderProcess( |
| contents->GetRenderProcessHost()->GetID()); |
| } |
| |
| void GetCookies(const GURL& url, |
| WebContents* contents, |
| int* value_size, |
| std::string* value) { |
| *value_size = -1; |
| if (url.is_valid() && contents) { |
| scoped_refptr<net::URLRequestContextGetter> context_getter = |
| GetRequestContext(contents); |
| base::WaitableEvent event(true /* manual reset */, |
| false /* not initially signaled */); |
| CHECK(BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&GetCookiesOnIOThread, url, context_getter, &event, value))); |
| event.Wait(); |
| |
| *value_size = static_cast<int>(value->size()); |
| } |
| } |
| |
| void SetCookie(const GURL& url, |
| const std::string& value, |
| WebContents* contents, |
| int* response_value) { |
| *response_value = -1; |
| |
| if (url.is_valid() && contents) { |
| scoped_refptr<net::URLRequestContextGetter> context_getter = |
| GetRequestContext(contents); |
| base::WaitableEvent event(true /* manual reset */, |
| false /* not initially signaled */); |
| bool success = false; |
| CHECK(BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&SetCookieOnIOThread, url, value, context_getter, &event, |
| &success))); |
| event.Wait(); |
| if (success) |
| *response_value = 1; |
| } |
| } |
| |
| void DeleteCookie(const GURL& url, |
| const std::string& cookie_name, |
| WebContents* contents, |
| bool* success) { |
| *success = false; |
| if (url.is_valid() && contents) { |
| scoped_refptr<net::URLRequestContextGetter> context_getter = |
| GetRequestContext(contents); |
| base::WaitableEvent event(true /* manual reset */, |
| false /* not initially signaled */); |
| CHECK(BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&DeleteCookieOnIOThread, url, cookie_name, context_getter, |
| &event))); |
| event.Wait(); |
| *success = true; |
| } |
| } |
| |
| void GetCookiesJSON(AutomationProvider* provider, |
| DictionaryValue* args, |
| IPC::Message* reply_message) { |
| AutomationJSONReply reply(provider, reply_message); |
| std::string url; |
| if (!args->GetString("url", &url)) { |
| reply.SendError("'url' missing or invalid"); |
| return; |
| } |
| |
| // Since we may be on the UI thread don't call GetURLRequestContext(). |
| scoped_refptr<net::URLRequestContextGetter> context_getter = |
| provider->profile()->GetRequestContext(); |
| |
| net::CookieList cookie_list; |
| base::WaitableEvent event(true /* manual reset */, |
| false /* not initially signaled */); |
| base::Closure task = base::Bind(&GetCanonicalCookiesOnIOThread, GURL(url), |
| context_getter, &event, &cookie_list); |
| if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { |
| reply.SendError("Couldn't post task to get the cookies"); |
| return; |
| } |
| event.Wait(); |
| |
| ListValue* list = new ListValue(); |
| for (size_t i = 0; i < cookie_list.size(); ++i) { |
| const net::CanonicalCookie& cookie = cookie_list[i]; |
| DictionaryValue* cookie_dict = new DictionaryValue(); |
| cookie_dict->SetString("name", cookie.Name()); |
| cookie_dict->SetString("value", cookie.Value()); |
| cookie_dict->SetString("path", cookie.Path()); |
| cookie_dict->SetString("domain", cookie.Domain()); |
| cookie_dict->SetBoolean("secure", cookie.IsSecure()); |
| cookie_dict->SetBoolean("http_only", cookie.IsHttpOnly()); |
| if (cookie.IsPersistent()) |
| cookie_dict->SetDouble("expiry", cookie.ExpiryDate().ToDoubleT()); |
| if (cookie.Priority() != net::COOKIE_PRIORITY_DEFAULT) { |
| cookie_dict->SetString("priority", |
| net::CookiePriorityToString(cookie.Priority())); |
| } |
| list->Append(cookie_dict); |
| } |
| DictionaryValue dict; |
| dict.Set("cookies", list); |
| reply.SendSuccess(&dict); |
| } |
| |
| void DeleteCookieJSON(AutomationProvider* provider, |
| DictionaryValue* args, |
| IPC::Message* reply_message) { |
| AutomationJSONReply reply(provider, reply_message); |
| std::string url, name; |
| if (!args->GetString("url", &url)) { |
| reply.SendError("'url' missing or invalid"); |
| return; |
| } |
| if (!args->GetString("name", &name)) { |
| reply.SendError("'name' missing or invalid"); |
| return; |
| } |
| |
| // Since we may be on the UI thread don't call GetURLRequestContext(). |
| scoped_refptr<net::URLRequestContextGetter> context_getter = |
| provider->profile()->GetRequestContext(); |
| |
| base::WaitableEvent event(true /* manual reset */, |
| false /* not initially signaled */); |
| base::Closure task = base::Bind(&DeleteCookieOnIOThread, GURL(url), name, |
| context_getter, &event); |
| if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { |
| reply.SendError("Couldn't post task to delete the cookie"); |
| return; |
| } |
| event.Wait(); |
| reply.SendSuccess(NULL); |
| } |
| |
| void SetCookieJSON(AutomationProvider* provider, |
| DictionaryValue* args, |
| IPC::Message* reply_message) { |
| AutomationJSONReply reply(provider, reply_message); |
| std::string url; |
| if (!args->GetString("url", &url)) { |
| reply.SendError("'url' missing or invalid"); |
| return; |
| } |
| DictionaryValue* cookie_dict; |
| if (!args->GetDictionary("cookie", &cookie_dict)) { |
| reply.SendError("'cookie' missing or invalid"); |
| return; |
| } |
| std::string name, value; |
| std::string domain; |
| std::string path = "/"; |
| bool secure = false; |
| double expiry = 0; |
| bool http_only = false; |
| net::CookiePriority priority = net::COOKIE_PRIORITY_DEFAULT; |
| |
| if (!cookie_dict->GetString("name", &name)) { |
| reply.SendError("'name' missing or invalid"); |
| return; |
| } |
| if (!cookie_dict->GetString("value", &value)) { |
| reply.SendError("'value' missing or invalid"); |
| return; |
| } |
| if (cookie_dict->HasKey("domain") && |
| !cookie_dict->GetString("domain", &domain)) { |
| reply.SendError("optional 'domain' invalid"); |
| return; |
| } |
| if (cookie_dict->HasKey("path") && |
| !cookie_dict->GetString("path", &path)) { |
| reply.SendError("optional 'path' invalid"); |
| return; |
| } |
| if (cookie_dict->HasKey("secure") && |
| !cookie_dict->GetBoolean("secure", &secure)) { |
| reply.SendError("optional 'secure' invalid"); |
| return; |
| } |
| if (cookie_dict->HasKey("expiry")) { |
| if (!cookie_dict->GetDouble("expiry", &expiry)) { |
| reply.SendError("optional 'expiry' invalid"); |
| return; |
| } |
| } |
| if (cookie_dict->HasKey("http_only") && |
| !cookie_dict->GetBoolean("http_only", &http_only)) { |
| reply.SendError("optional 'http_only' invalid"); |
| return; |
| } |
| if (cookie_dict->HasKey("priority")) { |
| std::string priority_string; |
| if (!cookie_dict->GetString("priority", &priority_string)) { |
| reply.SendError("optional 'priority' invalid"); |
| return; |
| } |
| priority = net::StringToCookiePriority(priority_string); |
| } |
| |
| scoped_ptr<net::CanonicalCookie> cookie( |
| net::CanonicalCookie::Create( |
| GURL(url), name, value, domain, path, base::Time(), |
| base::Time::FromDoubleT(expiry), secure, http_only, priority)); |
| if (!cookie.get()) { |
| reply.SendError("given 'cookie' parameters are invalid"); |
| return; |
| } |
| |
| // Since we may be on the UI thread don't call GetURLRequestContext(). |
| scoped_refptr<net::URLRequestContextGetter> context_getter = |
| provider->profile()->GetRequestContext(); |
| |
| base::WaitableEvent event(true /* manual reset */, |
| false /* not initially signaled */); |
| bool success = false; |
| base::Closure task = base::Bind( |
| &SetCookieWithDetailsOnIOThread, GURL(url), *cookie.get(), domain, |
| context_getter, &event, &success); |
| if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) { |
| reply.SendError("Couldn't post task to set the cookie"); |
| return; |
| } |
| event.Wait(); |
| |
| if (!success) { |
| reply.SendError("Could not set the cookie"); |
| return; |
| } |
| reply.SendSuccess(NULL); |
| } |
| |
| bool SendErrorIfModalDialogActive(AutomationProvider* provider, |
| IPC::Message* message) { |
| bool active = AppModalDialogQueue::GetInstance()->HasActiveDialog(); |
| if (active) |
| AutomationJSONReply(provider, message).SendError("Blocked by modal dialog"); |
| return active; |
| } |
| |
| } // namespace automation_util |