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.
5 #include "chrome/browser/automation/automation_util.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"
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"
52 using content::BrowserThread
;
53 using content::RenderViewHost
;
54 using content::WebContents
;
56 #if defined(OS_CHROMEOS)
57 using chromeos::ExistingUserController
;
59 using chromeos::UserManager
;
64 void GetCookiesCallback(base::WaitableEvent
* event
,
66 const std::string
& cookie_line
) {
67 *cookies
= cookie_line
;
71 void GetCookiesOnIOThread(
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
));
81 void GetCanonicalCookiesCallback(
82 base::WaitableEvent
* event
,
83 net::CookieList
* cookie_list
,
84 const net::CookieList
& cookies
) {
85 *cookie_list
= cookies
;
89 void GetCanonicalCookiesOnIOThread(
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(
97 base::Bind(&GetCanonicalCookiesCallback
, event
, cookie_list
));
100 void SetCookieCallback(base::WaitableEvent
* event
,
107 void SetCookieOnIOThread(
109 const std::string
& value
,
110 const scoped_refptr
<net::URLRequestContextGetter
>& context_getter
,
111 base::WaitableEvent
* event
,
113 context_getter
->GetURLRequestContext()->cookie_store()->
114 SetCookieWithOptionsAsync(
115 url
, value
, net::CookieOptions(),
116 base::Bind(&SetCookieCallback
, event
, success
));
119 void SetCookieWithDetailsOnIOThread(
121 const net::CanonicalCookie
& cookie
,
122 const std::string
& original_domain
,
123 const scoped_refptr
<net::URLRequestContextGetter
>& context_getter
,
124 base::WaitableEvent
* event
,
126 net::CookieMonster
* cookie_monster
=
127 context_getter
->GetURLRequestContext()->cookie_store()->
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
));
136 void DeleteCookieCallback(base::WaitableEvent
* event
) {
140 void DeleteCookieOnIOThread(
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
));
152 namespace automation_util
{
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()))
160 return native_list
->get(index
);
163 WebContents
* GetWebContentsAt(int browser_index
, int tab_index
) {
166 Browser
* browser
= GetBrowserAt(browser_index
);
167 if (!browser
|| tab_index
>= browser
->tab_strip_model()->count())
169 return browser
->tab_strip_model()->GetWebContentsAt(tab_index
);
172 #if defined(OS_CHROMEOS)
173 Profile
* GetCurrentProfileOnChromeOS(std::string
* error_message
) {
174 const UserManager
* user_manager
= UserManager::Get();
176 *error_message
= "No user manager.";
179 if (!user_manager
->IsUserLoggedIn()) {
180 ExistingUserController
* controller
=
181 ExistingUserController::current_controller();
183 *error_message
= "Cannot get controller though user is not logged in.";
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();
191 *error_message
= "Unable to get webui from login display host.";
194 return Profile::FromWebUI(web_ui
);
196 return ProfileManager::GetActiveUserProfile();
200 #endif // defined(OS_CHROMEOS)
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();
208 if (browser
->tab_strip_model()->GetWebContentsAt(tab_index
) == tab
)
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());
222 void GetCookies(const GURL
& url
,
223 WebContents
* contents
,
225 std::string
* value
) {
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
)));
237 *value_size
= static_cast<int>(value
->size());
241 void SetCookie(const GURL
& url
,
242 const std::string
& value
,
243 WebContents
* contents
,
244 int* response_value
) {
245 *response_value
= -1;
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
,
263 void DeleteCookie(const GURL
& url
,
264 const std::string
& cookie_name
,
265 WebContents
* contents
,
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
,
282 void GetCookiesJSON(AutomationProvider
* provider
,
283 base::DictionaryValue
* args
,
284 IPC::Message
* reply_message
) {
285 AutomationJSONReply
reply(provider
, reply_message
);
287 if (!args
->GetString("url", &url
)) {
288 reply
.SendError("'url' missing or invalid");
292 // Since we may be on the UI thread don't call GetURLRequestContext().
293 scoped_refptr
<net::URLRequestContextGetter
> context_getter
=
294 provider
->profile()->GetRequestContext();
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");
307 base::ListValue
* list
= new base::ListValue();
308 for (size_t i
= 0; i
< cookie_list
.size(); ++i
) {
309 const net::CanonicalCookie
& cookie
= cookie_list
[i
];
310 base::DictionaryValue
* cookie_dict
= new base::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()));
323 list
->Append(cookie_dict
);
325 base::DictionaryValue dict
;
326 dict
.Set("cookies", list
);
327 reply
.SendSuccess(&dict
);
330 void DeleteCookieJSON(AutomationProvider
* provider
,
331 base::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");
339 if (!args
->GetString("name", &name
)) {
340 reply
.SendError("'name' missing or invalid");
344 // Since we may be on the UI thread don't call GetURLRequestContext().
345 scoped_refptr
<net::URLRequestContextGetter
> context_getter
=
346 provider
->profile()->GetRequestContext();
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");
357 reply
.SendSuccess(NULL
);
360 void SetCookieJSON(AutomationProvider
* provider
,
361 base::DictionaryValue
* args
,
362 IPC::Message
* reply_message
) {
363 AutomationJSONReply
reply(provider
, reply_message
);
365 if (!args
->GetString("url", &url
)) {
366 reply
.SendError("'url' missing or invalid");
369 base::DictionaryValue
* cookie_dict
;
370 if (!args
->GetDictionary("cookie", &cookie_dict
)) {
371 reply
.SendError("'cookie' missing or invalid");
374 std::string name
, value
;
376 std::string path
= "/";
379 bool http_only
= false;
380 net::CookiePriority priority
= net::COOKIE_PRIORITY_DEFAULT
;
382 if (!cookie_dict
->GetString("name", &name
)) {
383 reply
.SendError("'name' missing or invalid");
386 if (!cookie_dict
->GetString("value", &value
)) {
387 reply
.SendError("'value' missing or invalid");
390 if (cookie_dict
->HasKey("domain") &&
391 !cookie_dict
->GetString("domain", &domain
)) {
392 reply
.SendError("optional 'domain' invalid");
395 if (cookie_dict
->HasKey("path") &&
396 !cookie_dict
->GetString("path", &path
)) {
397 reply
.SendError("optional 'path' invalid");
400 if (cookie_dict
->HasKey("secure") &&
401 !cookie_dict
->GetBoolean("secure", &secure
)) {
402 reply
.SendError("optional 'secure' invalid");
405 if (cookie_dict
->HasKey("expiry")) {
406 if (!cookie_dict
->GetDouble("expiry", &expiry
)) {
407 reply
.SendError("optional 'expiry' invalid");
411 if (cookie_dict
->HasKey("http_only") &&
412 !cookie_dict
->GetBoolean("http_only", &http_only
)) {
413 reply
.SendError("optional 'http_only' invalid");
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");
422 priority
= net::StringToCookiePriority(priority_string
);
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
));
430 reply
.SendError("given 'cookie' parameters are invalid");
434 // Since we may be on the UI thread don't call GetURLRequestContext().
435 scoped_refptr
<net::URLRequestContextGetter
> context_getter
=
436 provider
->profile()->GetRequestContext();
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");
451 reply
.SendError("Could not set the cookie");
454 reply
.SendSuccess(NULL
);
457 bool SendErrorIfModalDialogActive(AutomationProvider
* provider
,
458 IPC::Message
* message
) {
459 bool active
= AppModalDialogQueue::GetInstance()->HasActiveDialog();
461 AutomationJSONReply(provider
, message
).SendError("Blocked by modal dialog");
465 } // namespace automation_util