Home | History | Annotate | Download | only in automation
      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