1// Copyright (c) 2011 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/memory/scoped_ptr.h"
10#include "base/time.h"
11#include "base/values.h"
12#include "chrome/browser/automation/automation_provider.h"
13#include "chrome/browser/automation/automation_provider_json.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_list.h"
17#include "chrome/browser/ui/browser.h"
18#include "content/browser/browser_thread.h"
19#include "content/browser/renderer_host/browser_render_process_host.h"
20#include "content/browser/renderer_host/render_view_host.h"
21#include "content/browser/tab_contents/tab_contents.h"
22#include "net/base/cookie_monster.h"
23#include "net/base/cookie_store.h"
24#include "net/url_request/url_request_context.h"
25#include "net/url_request/url_request_context_getter.h"
26
27namespace {
28
29void GetCookiesOnIOThread(
30    const GURL& url,
31    const scoped_refptr<net::URLRequestContextGetter>& context_getter,
32    base::WaitableEvent* event,
33    std::string* cookies) {
34  *cookies =
35      context_getter->GetURLRequestContext()->cookie_store()->GetCookies(url);
36  event->Signal();
37}
38
39void GetCanonicalCookiesOnIOThread(
40    const GURL& url,
41    const scoped_refptr<net::URLRequestContextGetter>& context_getter,
42    base::WaitableEvent* event,
43    net::CookieList* cookie_list) {
44  *cookie_list =
45      context_getter->GetURLRequestContext()->cookie_store()->
46      GetCookieMonster()->GetAllCookiesForURL(url);
47  event->Signal();
48}
49
50void SetCookieOnIOThread(
51    const GURL& url,
52    const std::string& value,
53    const scoped_refptr<net::URLRequestContextGetter>& context_getter,
54    base::WaitableEvent* event,
55    bool* success) {
56  *success =
57      context_getter->GetURLRequestContext()->cookie_store()->
58      SetCookie(url, value);
59  event->Signal();
60}
61
62void SetCookieWithDetailsOnIOThread(
63    const GURL& url,
64    const net::CookieMonster::CanonicalCookie& cookie,
65    const std::string& original_domain,
66    const scoped_refptr<net::URLRequestContextGetter>& context_getter,
67    base::WaitableEvent* event,
68    bool* success) {
69  net::CookieMonster* cookie_monster =
70      context_getter->GetURLRequestContext()->cookie_store()->
71      GetCookieMonster();
72  *success = cookie_monster->SetCookieWithDetails(
73      url, cookie.Name(), cookie.Value(), original_domain,
74      cookie.Path(), cookie.ExpiryDate(), cookie.IsSecure(),
75      cookie.IsHttpOnly());
76  event->Signal();
77}
78
79void DeleteCookieOnIOThread(
80    const GURL& url,
81    const std::string& name,
82    const scoped_refptr<net::URLRequestContextGetter>& context_getter,
83    base::WaitableEvent* event) {
84  context_getter->GetURLRequestContext()->cookie_store()->
85      DeleteCookie(url, name);
86  event->Signal();
87}
88
89}  // namespace
90
91namespace automation_util {
92
93Browser* GetBrowserAt(int index) {
94  if (index < 0 || index >= static_cast<int>(BrowserList::size()))
95    return NULL;
96  return *(BrowserList::begin() + index);
97}
98
99TabContents* GetTabContentsAt(int browser_index, int tab_index) {
100  if (tab_index < 0)
101    return NULL;
102  Browser* browser = GetBrowserAt(browser_index);
103  if (!browser || tab_index >= browser->tab_count())
104    return NULL;
105  return browser->GetTabContentsAt(tab_index);
106}
107
108void GetCookies(const GURL& url,
109                TabContents* contents,
110                int* value_size,
111                std::string* value) {
112  *value_size = -1;
113  if (url.is_valid() && contents) {
114    // Since we may be on the UI thread don't call GetURLRequestContext().
115    // Get the request context specific to the current TabContents and app.
116    const Extension* installed_app = static_cast<BrowserRenderProcessHost*>(
117        contents->render_view_host()->process())->installed_app();
118    scoped_refptr<net::URLRequestContextGetter> context_getter =
119        contents->profile()->GetRequestContextForPossibleApp(installed_app);
120
121    base::WaitableEvent event(true /* manual reset */,
122                              false /* not initially signaled */);
123    CHECK(BrowserThread::PostTask(
124              BrowserThread::IO, FROM_HERE,
125              NewRunnableFunction(&GetCookiesOnIOThread,
126                                  url, context_getter, &event, value)));
127    event.Wait();
128
129    *value_size = static_cast<int>(value->size());
130  }
131}
132
133void SetCookie(const GURL& url,
134               const std::string& value,
135               TabContents* contents,
136               int* response_value) {
137  *response_value = -1;
138
139  if (url.is_valid() && contents) {
140    // Since we may be on the UI thread don't call GetURLRequestContext().
141    // Get the request context specific to the current TabContents and app.
142    const Extension* installed_app = static_cast<BrowserRenderProcessHost*>(
143        contents->render_view_host()->process())->installed_app();
144    scoped_refptr<net::URLRequestContextGetter> context_getter =
145        contents->profile()->GetRequestContextForPossibleApp(installed_app);
146
147    base::WaitableEvent event(true /* manual reset */,
148                              false /* not initially signaled */);
149    bool success = false;
150    CHECK(BrowserThread::PostTask(
151              BrowserThread::IO, FROM_HERE,
152              NewRunnableFunction(&SetCookieOnIOThread,
153                                  url, value, context_getter, &event,
154                                  &success)));
155    event.Wait();
156    if (success)
157      *response_value = 1;
158  }
159}
160
161void DeleteCookie(const GURL& url,
162                  const std::string& cookie_name,
163                  TabContents* contents,
164                  bool* success) {
165  *success = false;
166  if (url.is_valid() && contents) {
167    // Since we may be on the UI thread don't call GetURLRequestContext().
168    // Get the request context specific to the current TabContents and app.
169    const Extension* installed_app = static_cast<BrowserRenderProcessHost*>(
170        contents->render_view_host()->process())->installed_app();
171    scoped_refptr<net::URLRequestContextGetter> context_getter =
172        contents->profile()->GetRequestContextForPossibleApp(installed_app);
173
174    base::WaitableEvent event(true /* manual reset */,
175                              false /* not initially signaled */);
176    CHECK(BrowserThread::PostTask(
177              BrowserThread::IO, FROM_HERE,
178              NewRunnableFunction(&DeleteCookieOnIOThread,
179                                  url, cookie_name, context_getter, &event)));
180    event.Wait();
181    *success = true;
182  }
183}
184
185void GetCookiesJSON(AutomationProvider* provider,
186                    DictionaryValue* args,
187                    IPC::Message* reply_message) {
188  AutomationJSONReply reply(provider, reply_message);
189  std::string url;
190  if (!args->GetString("url", &url)) {
191    reply.SendError("'url' missing or invalid");
192    return;
193  }
194
195  // Since we may be on the UI thread don't call GetURLRequestContext().
196  scoped_refptr<net::URLRequestContextGetter> context_getter =
197      provider->profile()->GetRequestContext();
198
199  net::CookieList cookie_list;
200  base::WaitableEvent event(true /* manual reset */,
201                            false /* not initially signaled */);
202  Task* task = NewRunnableFunction(
203      &GetCanonicalCookiesOnIOThread,
204      GURL(url), context_getter, &event, &cookie_list);
205  if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) {
206    reply.SendError("Couldn't post task to get the cookies");
207    return;
208  }
209  event.Wait();
210
211  ListValue* list = new ListValue();
212  for (size_t i = 0; i < cookie_list.size(); ++i) {
213    const net::CookieMonster::CanonicalCookie& cookie = cookie_list[i];
214    DictionaryValue* cookie_dict = new DictionaryValue();
215    cookie_dict->SetString("name", cookie.Name());
216    cookie_dict->SetString("value", cookie.Value());
217    cookie_dict->SetString("path", cookie.Path());
218    cookie_dict->SetString("domain", cookie.Domain());
219    cookie_dict->SetBoolean("secure", cookie.IsSecure());
220    cookie_dict->SetBoolean("http_only", cookie.IsHttpOnly());
221    if (cookie.DoesExpire())
222      cookie_dict->SetDouble("expiry", cookie.ExpiryDate().ToDoubleT());
223    list->Append(cookie_dict);
224  }
225  DictionaryValue dict;
226  dict.Set("cookies", list);
227  reply.SendSuccess(&dict);
228}
229
230void DeleteCookieJSON(AutomationProvider* provider,
231                      DictionaryValue* args,
232                      IPC::Message* reply_message) {
233  AutomationJSONReply reply(provider, reply_message);
234  std::string url, name;
235  if (!args->GetString("url", &url)) {
236    reply.SendError("'url' missing or invalid");
237    return;
238  }
239  if (!args->GetString("name", &name)) {
240    reply.SendError("'name' missing or invalid");
241    return;
242  }
243
244  // Since we may be on the UI thread don't call GetURLRequestContext().
245  scoped_refptr<net::URLRequestContextGetter> context_getter =
246      provider->profile()->GetRequestContext();
247
248  base::WaitableEvent event(true /* manual reset */,
249                            false /* not initially signaled */);
250  Task* task = NewRunnableFunction(
251      &DeleteCookieOnIOThread,
252      GURL(url), name, context_getter, &event);
253  if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) {
254    reply.SendError("Couldn't post task to delete the cookie");
255    return;
256  }
257  event.Wait();
258  reply.SendSuccess(NULL);
259}
260
261void SetCookieJSON(AutomationProvider* provider,
262                   DictionaryValue* args,
263                   IPC::Message* reply_message) {
264  AutomationJSONReply reply(provider, reply_message);
265  std::string url;
266  if (!args->GetString("url", &url)) {
267    reply.SendError("'url' missing or invalid");
268    return;
269  }
270  DictionaryValue* cookie_dict;
271  if (!args->GetDictionary("cookie", &cookie_dict)) {
272    reply.SendError("'cookie' missing or invalid");
273    return;
274  }
275  std::string name, value;
276  std::string domain;
277  std::string path = "/";
278  bool secure = false;
279  double expiry = 0;
280  bool http_only = false;
281  if (!cookie_dict->GetString("name", &name)) {
282    reply.SendError("'name' missing or invalid");
283    return;
284  }
285  if (!cookie_dict->GetString("value", &value)) {
286    reply.SendError("'value' missing or invalid");
287    return;
288  }
289  if (cookie_dict->HasKey("domain") &&
290      !cookie_dict->GetString("domain", &domain)) {
291    reply.SendError("optional 'domain' invalid");
292    return;
293  }
294  if (cookie_dict->HasKey("path") &&
295      !cookie_dict->GetString("path", &path)) {
296    reply.SendError("optional 'path' invalid");
297    return;
298  }
299  if (cookie_dict->HasKey("secure") &&
300      !cookie_dict->GetBoolean("secure", &secure)) {
301    reply.SendError("optional 'secure' invalid");
302    return;
303  }
304  if (cookie_dict->HasKey("expiry")) {
305    int expiry_int;
306    if (cookie_dict->GetInteger("expiry", &expiry_int)) {
307      expiry = expiry_int;
308    } else if (!cookie_dict->GetDouble("expiry", &expiry)) {
309      reply.SendError("optional 'expiry' invalid");
310      return;
311    }
312  }
313  if (cookie_dict->HasKey("http_only") &&
314      !cookie_dict->GetBoolean("http_only", &http_only)) {
315    reply.SendError("optional 'http_only' invalid");
316    return;
317  }
318
319  scoped_ptr<net::CookieMonster::CanonicalCookie> cookie(
320      net::CookieMonster::CanonicalCookie::Create(
321          GURL(url), name, value, domain, path, base::Time(),
322          base::Time::FromDoubleT(expiry), secure, http_only));
323  if (!cookie.get()) {
324    reply.SendError("given 'cookie' parameters are invalid");
325    return;
326  }
327
328  // Since we may be on the UI thread don't call GetURLRequestContext().
329  scoped_refptr<net::URLRequestContextGetter> context_getter =
330      provider->profile()->GetRequestContext();
331
332  base::WaitableEvent event(true /* manual reset */,
333                            false /* not initially signaled */);
334  bool success = false;
335  Task* task = NewRunnableFunction(
336      &SetCookieWithDetailsOnIOThread,
337      GURL(url), *cookie.get(), domain, context_getter, &event, &success);
338  if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) {
339    reply.SendError("Couldn't post task to set the cookie");
340    return;
341  }
342  event.Wait();
343
344  if (!success) {
345    reply.SendError("Could not set the cookie");
346    return;
347  }
348  reply.SendSuccess(NULL);
349}
350
351}  // namespace automation_util
352