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 "ash/shell.h"
     10 #include "ash/shell_delegate.h"
     11 #include "base/bind.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "base/synchronization/waitable_event.h"
     16 #include "base/time/time.h"
     17 #include "base/values.h"
     18 #include "chrome/browser/automation/automation_provider.h"
     19 #include "chrome/browser/automation/automation_provider_json.h"
     20 #include "chrome/browser/extensions/extension_process_manager.h"
     21 #include "chrome/browser/extensions/extension_service.h"
     22 #include "chrome/browser/extensions/extension_system.h"
     23 #include "chrome/browser/profiles/profile.h"
     24 #include "chrome/browser/sessions/session_id.h"
     25 #include "chrome/browser/sessions/session_tab_helper.h"
     26 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
     27 #include "chrome/browser/ui/browser.h"
     28 #include "chrome/browser/ui/browser_iterator.h"
     29 #include "chrome/browser/ui/browser_list.h"
     30 #include "chrome/browser/ui/host_desktop.h"
     31 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     32 #include "chrome/common/automation_id.h"
     33 #include "chrome/common/extensions/extension.h"
     34 #include "content/public/browser/browser_thread.h"
     35 #include "content/public/browser/render_process_host.h"
     36 #include "content/public/browser/render_view_host.h"
     37 #include "content/public/browser/web_contents.h"
     38 #include "extensions/browser/view_type_utils.h"
     39 #include "net/cookies/canonical_cookie.h"
     40 #include "net/cookies/cookie_constants.h"
     41 #include "net/cookies/cookie_monster.h"
     42 #include "net/cookies/cookie_store.h"
     43 #include "net/url_request/url_request_context.h"
     44 #include "net/url_request/url_request_context_getter.h"
     45 
     46 #if defined(OS_CHROMEOS)
     47 #include "chrome/browser/chromeos/login/existing_user_controller.h"
     48 #include "chrome/browser/chromeos/login/login_display.h"
     49 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
     50 #include "chrome/browser/chromeos/login/webui_login_display.h"
     51 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
     52 #endif
     53 
     54 #if defined(ENABLE_FULL_PRINTING)
     55 #include "chrome/browser/printing/print_preview_dialog_controller.h"
     56 #endif
     57 
     58 using content::BrowserThread;
     59 using content::RenderViewHost;
     60 using content::WebContents;
     61 
     62 #if defined(OS_CHROMEOS)
     63 using chromeos::ExistingUserController;
     64 using chromeos::User;
     65 using chromeos::UserManager;
     66 #endif
     67 
     68 namespace {
     69 
     70 void GetCookiesCallback(base::WaitableEvent* event,
     71                         std::string* cookies,
     72                         const std::string& cookie_line) {
     73   *cookies = cookie_line;
     74   event->Signal();
     75 }
     76 
     77 void GetCookiesOnIOThread(
     78     const GURL& url,
     79     const scoped_refptr<net::URLRequestContextGetter>& context_getter,
     80     base::WaitableEvent* event,
     81     std::string* cookies) {
     82   context_getter->GetURLRequestContext()->cookie_store()->
     83       GetCookiesWithOptionsAsync(url, net::CookieOptions(),
     84                       base::Bind(&GetCookiesCallback, event, cookies));
     85 }
     86 
     87 void GetCanonicalCookiesCallback(
     88     base::WaitableEvent* event,
     89     net::CookieList* cookie_list,
     90     const net::CookieList& cookies) {
     91   *cookie_list = cookies;
     92   event->Signal();
     93 }
     94 
     95 void GetCanonicalCookiesOnIOThread(
     96     const GURL& url,
     97     const scoped_refptr<net::URLRequestContextGetter>& context_getter,
     98     base::WaitableEvent* event,
     99     net::CookieList* cookie_list) {
    100   context_getter->GetURLRequestContext()->cookie_store()->
    101       GetCookieMonster()->GetAllCookiesForURLAsync(
    102           url,
    103           base::Bind(&GetCanonicalCookiesCallback, event, cookie_list));
    104 }
    105 
    106 void SetCookieCallback(base::WaitableEvent* event,
    107                        bool* success,
    108                        bool result) {
    109   *success = result;
    110   event->Signal();
    111 }
    112 
    113 void SetCookieOnIOThread(
    114     const GURL& url,
    115     const std::string& value,
    116     const scoped_refptr<net::URLRequestContextGetter>& context_getter,
    117     base::WaitableEvent* event,
    118     bool* success) {
    119   context_getter->GetURLRequestContext()->cookie_store()->
    120       SetCookieWithOptionsAsync(
    121           url, value, net::CookieOptions(),
    122           base::Bind(&SetCookieCallback, event, success));
    123 }
    124 
    125 void SetCookieWithDetailsOnIOThread(
    126     const GURL& url,
    127     const net::CanonicalCookie& cookie,
    128     const std::string& original_domain,
    129     const scoped_refptr<net::URLRequestContextGetter>& context_getter,
    130     base::WaitableEvent* event,
    131     bool* success) {
    132   net::CookieMonster* cookie_monster =
    133       context_getter->GetURLRequestContext()->cookie_store()->
    134       GetCookieMonster();
    135   cookie_monster->SetCookieWithDetailsAsync(
    136       url, cookie.Name(), cookie.Value(), original_domain,
    137       cookie.Path(), cookie.ExpiryDate(), cookie.IsSecure(),
    138       cookie.IsHttpOnly(), cookie.Priority(),
    139       base::Bind(&SetCookieCallback, event, success));
    140 }
    141 
    142 void DeleteCookieCallback(base::WaitableEvent* event) {
    143   event->Signal();
    144 }
    145 
    146 void DeleteCookieOnIOThread(
    147     const GURL& url,
    148     const std::string& name,
    149     const scoped_refptr<net::URLRequestContextGetter>& context_getter,
    150     base::WaitableEvent* event) {
    151   context_getter->GetURLRequestContext()->cookie_store()->
    152       DeleteCookieAsync(url, name,
    153                         base::Bind(&DeleteCookieCallback, event));
    154 }
    155 
    156 }  // namespace
    157 
    158 namespace automation_util {
    159 
    160 Browser* GetBrowserAt(int index) {
    161   // The automation layer doesn't support non-native desktops.
    162   BrowserList* native_list =
    163       BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE);
    164   if (index < 0 || index >= static_cast<int>(native_list->size()))
    165     return NULL;
    166   return native_list->get(index);
    167 }
    168 
    169 WebContents* GetWebContentsAt(int browser_index, int tab_index) {
    170   if (tab_index < 0)
    171     return NULL;
    172   Browser* browser = GetBrowserAt(browser_index);
    173   if (!browser || tab_index >= browser->tab_strip_model()->count())
    174     return NULL;
    175   return browser->tab_strip_model()->GetWebContentsAt(tab_index);
    176 }
    177 
    178 #if defined(OS_CHROMEOS)
    179 Profile* GetCurrentProfileOnChromeOS(std::string* error_message) {
    180   const UserManager* user_manager = UserManager::Get();
    181   if (!user_manager) {
    182     *error_message = "No user manager.";
    183     return NULL;
    184   }
    185   if (!user_manager->IsUserLoggedIn()) {
    186     ExistingUserController* controller =
    187         ExistingUserController::current_controller();
    188     if (!controller) {
    189       *error_message = "Cannot get controller though user is not logged in.";
    190       return NULL;
    191     }
    192     chromeos::LoginDisplayHostImpl* webui_host =
    193         static_cast<chromeos::LoginDisplayHostImpl*>(
    194             controller->login_display_host());
    195     content::WebUI* web_ui = webui_host->GetOobeUI()->web_ui();
    196     if (!web_ui) {
    197       *error_message = "Unable to get webui from login display host.";
    198       return NULL;
    199     }
    200     return Profile::FromWebUI(web_ui);
    201   } else {
    202     ash::Shell* shell = ash::Shell::GetInstance();
    203     if (!shell || !shell->delegate()) {
    204       *error_message = "Unable to get shell delegate.";
    205       return NULL;
    206     }
    207     return Profile::FromBrowserContext(
    208         shell->delegate()->GetCurrentBrowserContext());
    209   }
    210   return NULL;
    211 }
    212 #endif  // defined(OS_CHROMEOS)
    213 
    214 Browser* GetBrowserForTab(WebContents* tab) {
    215   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    216     Browser* browser = *it;
    217     for (int tab_index = 0;
    218          tab_index < browser->tab_strip_model()->count();
    219          ++tab_index) {
    220       if (browser->tab_strip_model()->GetWebContentsAt(tab_index) == tab)
    221         return browser;
    222     }
    223   }
    224   return NULL;
    225 }
    226 
    227 net::URLRequestContextGetter* GetRequestContext(WebContents* contents) {
    228   // Since we may be on the UI thread don't call GetURLRequestContext().
    229   // Get the request context specific to the current WebContents and app.
    230   return contents->GetBrowserContext()->GetRequestContextForRenderProcess(
    231       contents->GetRenderProcessHost()->GetID());
    232 }
    233 
    234 void GetCookies(const GURL& url,
    235                 WebContents* contents,
    236                 int* value_size,
    237                 std::string* value) {
    238   *value_size = -1;
    239   if (url.is_valid() && contents) {
    240     scoped_refptr<net::URLRequestContextGetter> context_getter =
    241         GetRequestContext(contents);
    242     base::WaitableEvent event(true /* manual reset */,
    243                               false /* not initially signaled */);
    244     CHECK(BrowserThread::PostTask(
    245         BrowserThread::IO, FROM_HERE,
    246         base::Bind(&GetCookiesOnIOThread, url, context_getter, &event, value)));
    247     event.Wait();
    248 
    249     *value_size = static_cast<int>(value->size());
    250   }
    251 }
    252 
    253 void SetCookie(const GURL& url,
    254                const std::string& value,
    255                WebContents* contents,
    256                int* response_value) {
    257   *response_value = -1;
    258 
    259   if (url.is_valid() && contents) {
    260     scoped_refptr<net::URLRequestContextGetter> context_getter =
    261         GetRequestContext(contents);
    262     base::WaitableEvent event(true /* manual reset */,
    263                               false /* not initially signaled */);
    264     bool success = false;
    265     CHECK(BrowserThread::PostTask(
    266         BrowserThread::IO, FROM_HERE,
    267         base::Bind(&SetCookieOnIOThread, url, value, context_getter, &event,
    268                    &success)));
    269     event.Wait();
    270     if (success)
    271       *response_value = 1;
    272   }
    273 }
    274 
    275 void DeleteCookie(const GURL& url,
    276                   const std::string& cookie_name,
    277                   WebContents* contents,
    278                   bool* success) {
    279   *success = false;
    280   if (url.is_valid() && contents) {
    281     scoped_refptr<net::URLRequestContextGetter> context_getter =
    282         GetRequestContext(contents);
    283     base::WaitableEvent event(true /* manual reset */,
    284                               false /* not initially signaled */);
    285     CHECK(BrowserThread::PostTask(
    286         BrowserThread::IO, FROM_HERE,
    287         base::Bind(&DeleteCookieOnIOThread, url, cookie_name, context_getter,
    288                    &event)));
    289     event.Wait();
    290     *success = true;
    291   }
    292 }
    293 
    294 void GetCookiesJSON(AutomationProvider* provider,
    295                     DictionaryValue* args,
    296                     IPC::Message* reply_message) {
    297   AutomationJSONReply reply(provider, reply_message);
    298   std::string url;
    299   if (!args->GetString("url", &url)) {
    300     reply.SendError("'url' missing or invalid");
    301     return;
    302   }
    303 
    304   // Since we may be on the UI thread don't call GetURLRequestContext().
    305   scoped_refptr<net::URLRequestContextGetter> context_getter =
    306       provider->profile()->GetRequestContext();
    307 
    308   net::CookieList cookie_list;
    309   base::WaitableEvent event(true /* manual reset */,
    310                             false /* not initially signaled */);
    311   base::Closure task = base::Bind(&GetCanonicalCookiesOnIOThread, GURL(url),
    312                                   context_getter, &event, &cookie_list);
    313   if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) {
    314     reply.SendError("Couldn't post task to get the cookies");
    315     return;
    316   }
    317   event.Wait();
    318 
    319   ListValue* list = new ListValue();
    320   for (size_t i = 0; i < cookie_list.size(); ++i) {
    321     const net::CanonicalCookie& cookie = cookie_list[i];
    322     DictionaryValue* cookie_dict = new DictionaryValue();
    323     cookie_dict->SetString("name", cookie.Name());
    324     cookie_dict->SetString("value", cookie.Value());
    325     cookie_dict->SetString("path", cookie.Path());
    326     cookie_dict->SetString("domain", cookie.Domain());
    327     cookie_dict->SetBoolean("secure", cookie.IsSecure());
    328     cookie_dict->SetBoolean("http_only", cookie.IsHttpOnly());
    329     if (cookie.IsPersistent())
    330       cookie_dict->SetDouble("expiry", cookie.ExpiryDate().ToDoubleT());
    331     if (cookie.Priority() != net::COOKIE_PRIORITY_DEFAULT) {
    332       cookie_dict->SetString("priority",
    333                              net::CookiePriorityToString(cookie.Priority()));
    334     }
    335     list->Append(cookie_dict);
    336   }
    337   DictionaryValue dict;
    338   dict.Set("cookies", list);
    339   reply.SendSuccess(&dict);
    340 }
    341 
    342 void DeleteCookieJSON(AutomationProvider* provider,
    343                       DictionaryValue* args,
    344                       IPC::Message* reply_message) {
    345   AutomationJSONReply reply(provider, reply_message);
    346   std::string url, name;
    347   if (!args->GetString("url", &url)) {
    348     reply.SendError("'url' missing or invalid");
    349     return;
    350   }
    351   if (!args->GetString("name", &name)) {
    352     reply.SendError("'name' missing or invalid");
    353     return;
    354   }
    355 
    356   // Since we may be on the UI thread don't call GetURLRequestContext().
    357   scoped_refptr<net::URLRequestContextGetter> context_getter =
    358       provider->profile()->GetRequestContext();
    359 
    360   base::WaitableEvent event(true /* manual reset */,
    361                             false /* not initially signaled */);
    362   base::Closure task = base::Bind(&DeleteCookieOnIOThread, GURL(url), name,
    363                                   context_getter, &event);
    364   if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) {
    365     reply.SendError("Couldn't post task to delete the cookie");
    366     return;
    367   }
    368   event.Wait();
    369   reply.SendSuccess(NULL);
    370 }
    371 
    372 void SetCookieJSON(AutomationProvider* provider,
    373                    DictionaryValue* args,
    374                    IPC::Message* reply_message) {
    375   AutomationJSONReply reply(provider, reply_message);
    376   std::string url;
    377   if (!args->GetString("url", &url)) {
    378     reply.SendError("'url' missing or invalid");
    379     return;
    380   }
    381   DictionaryValue* cookie_dict;
    382   if (!args->GetDictionary("cookie", &cookie_dict)) {
    383     reply.SendError("'cookie' missing or invalid");
    384     return;
    385   }
    386   std::string name, value;
    387   std::string domain;
    388   std::string path = "/";
    389   bool secure = false;
    390   double expiry = 0;
    391   bool http_only = false;
    392   net::CookiePriority priority = net::COOKIE_PRIORITY_DEFAULT;
    393 
    394   if (!cookie_dict->GetString("name", &name)) {
    395     reply.SendError("'name' missing or invalid");
    396     return;
    397   }
    398   if (!cookie_dict->GetString("value", &value)) {
    399     reply.SendError("'value' missing or invalid");
    400     return;
    401   }
    402   if (cookie_dict->HasKey("domain") &&
    403       !cookie_dict->GetString("domain", &domain)) {
    404     reply.SendError("optional 'domain' invalid");
    405     return;
    406   }
    407   if (cookie_dict->HasKey("path") &&
    408       !cookie_dict->GetString("path", &path)) {
    409     reply.SendError("optional 'path' invalid");
    410     return;
    411   }
    412   if (cookie_dict->HasKey("secure") &&
    413       !cookie_dict->GetBoolean("secure", &secure)) {
    414     reply.SendError("optional 'secure' invalid");
    415     return;
    416   }
    417   if (cookie_dict->HasKey("expiry")) {
    418     if (!cookie_dict->GetDouble("expiry", &expiry)) {
    419       reply.SendError("optional 'expiry' invalid");
    420       return;
    421     }
    422   }
    423   if (cookie_dict->HasKey("http_only") &&
    424       !cookie_dict->GetBoolean("http_only", &http_only)) {
    425     reply.SendError("optional 'http_only' invalid");
    426     return;
    427   }
    428   if (cookie_dict->HasKey("priority")) {
    429     std::string priority_string;
    430     if (!cookie_dict->GetString("priority", &priority_string)) {
    431       reply.SendError("optional 'priority' invalid");
    432       return;
    433     }
    434     priority = net::StringToCookiePriority(priority_string);
    435   }
    436 
    437   scoped_ptr<net::CanonicalCookie> cookie(
    438       net::CanonicalCookie::Create(
    439           GURL(url), name, value, domain, path, base::Time(),
    440           base::Time::FromDoubleT(expiry), secure, http_only, priority));
    441   if (!cookie.get()) {
    442     reply.SendError("given 'cookie' parameters are invalid");
    443     return;
    444   }
    445 
    446   // Since we may be on the UI thread don't call GetURLRequestContext().
    447   scoped_refptr<net::URLRequestContextGetter> context_getter =
    448       provider->profile()->GetRequestContext();
    449 
    450   base::WaitableEvent event(true /* manual reset */,
    451                             false /* not initially signaled */);
    452   bool success = false;
    453   base::Closure task = base::Bind(
    454       &SetCookieWithDetailsOnIOThread, GURL(url), *cookie.get(), domain,
    455       context_getter, &event, &success);
    456   if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) {
    457     reply.SendError("Couldn't post task to set the cookie");
    458     return;
    459   }
    460   event.Wait();
    461 
    462   if (!success) {
    463     reply.SendError("Could not set the cookie");
    464     return;
    465   }
    466   reply.SendSuccess(NULL);
    467 }
    468 
    469 bool SendErrorIfModalDialogActive(AutomationProvider* provider,
    470                                   IPC::Message* message) {
    471   bool active = AppModalDialogQueue::GetInstance()->HasActiveDialog();
    472   if (active) {
    473     AutomationJSONReply(provider, message).SendErrorCode(
    474         automation::kBlockedByModalDialog);
    475   }
    476   return active;
    477 }
    478 
    479 AutomationId GetIdForTab(const WebContents* tab) {
    480   const SessionTabHelper* session_tab_helper =
    481       SessionTabHelper::FromWebContents(tab);
    482   return AutomationId(AutomationId::kTypeTab,
    483                       base::IntToString(session_tab_helper->session_id().id()));
    484 }
    485 
    486 AutomationId GetIdForExtensionView(
    487     const content::RenderViewHost* render_view_host) {
    488   AutomationId::Type type;
    489   WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
    490   switch (extensions::GetViewType(web_contents)) {
    491     case extensions::VIEW_TYPE_EXTENSION_POPUP:
    492       type = AutomationId::kTypeExtensionPopup;
    493       break;
    494     case extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE:
    495       type = AutomationId::kTypeExtensionBgPage;
    496       break;
    497     case extensions::VIEW_TYPE_EXTENSION_INFOBAR:
    498       type = AutomationId::kTypeExtensionInfobar;
    499       break;
    500     case extensions::VIEW_TYPE_APP_SHELL:
    501       type = AutomationId::kTypeAppShell;
    502       break;
    503     default:
    504       type = AutomationId::kTypeInvalid;
    505       break;
    506   }
    507   // Since these extension views do not permit navigation, using the
    508   // renderer process and view ID should suffice.
    509   std::string id = base::StringPrintf("%d|%d",
    510       render_view_host->GetRoutingID(),
    511       render_view_host->GetProcess()->GetID());
    512   return AutomationId(type, id);
    513 }
    514 
    515 AutomationId GetIdForExtension(const extensions::Extension* extension) {
    516   return AutomationId(AutomationId::kTypeExtension, extension->id());
    517 }
    518 
    519 bool GetTabForId(const AutomationId& id, WebContents** tab) {
    520   if (id.type() != AutomationId::kTypeTab)
    521     return false;
    522 
    523 #if defined(ENABLE_FULL_PRINTING)
    524   printing::PrintPreviewDialogController* preview_controller =
    525       printing::PrintPreviewDialogController::GetInstance();
    526 #endif
    527   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    528     Browser* browser = *it;
    529     for (int tab_index = 0;
    530          tab_index < browser->tab_strip_model()->count();
    531          ++tab_index) {
    532       WebContents* web_contents =
    533           browser->tab_strip_model()->GetWebContentsAt(tab_index);
    534       SessionTabHelper* session_tab_helper =
    535           SessionTabHelper::FromWebContents(web_contents);
    536       if (base::IntToString(
    537               session_tab_helper->session_id().id()) == id.id()) {
    538         *tab = web_contents;
    539         return true;
    540       }
    541 
    542 #if defined(ENABLE_FULL_PRINTING)
    543       if (preview_controller) {
    544         WebContents* print_preview_contents =
    545             preview_controller->GetPrintPreviewForContents(web_contents);
    546         if (print_preview_contents) {
    547           SessionTabHelper* preview_session_tab_helper =
    548               SessionTabHelper::FromWebContents(print_preview_contents);
    549           std::string preview_id = base::IntToString(
    550               preview_session_tab_helper->session_id().id());
    551           if (preview_id == id.id()) {
    552             *tab = print_preview_contents;
    553             return true;
    554           }
    555         }
    556       }
    557 #endif
    558     }
    559   }
    560   return false;
    561 }
    562 
    563 namespace {
    564 
    565 bool GetExtensionRenderViewForId(
    566     const AutomationId& id,
    567     Profile* profile,
    568     RenderViewHost** rvh) {
    569   ExtensionProcessManager* extension_mgr =
    570       extensions::ExtensionSystem::Get(profile)->process_manager();
    571   const ExtensionProcessManager::ViewSet view_set =
    572       extension_mgr->GetAllViews();
    573   for (ExtensionProcessManager::ViewSet::const_iterator iter = view_set.begin();
    574        iter != view_set.end(); ++iter) {
    575     content::RenderViewHost* host = *iter;
    576     AutomationId this_id = GetIdForExtensionView(host);
    577     if (id == this_id) {
    578       *rvh = host;
    579       return true;
    580     }
    581   }
    582   return false;
    583 }
    584 
    585 }  // namespace
    586 
    587 bool GetRenderViewForId(
    588     const AutomationId& id,
    589     Profile* profile,
    590     RenderViewHost** rvh) {
    591   switch (id.type()) {
    592     case AutomationId::kTypeTab: {
    593       WebContents* tab;
    594       if (!GetTabForId(id, &tab))
    595         return false;
    596       *rvh = tab->GetRenderViewHost();
    597       break;
    598     }
    599     case AutomationId::kTypeExtensionPopup:
    600     case AutomationId::kTypeExtensionBgPage:
    601     case AutomationId::kTypeExtensionInfobar:
    602     case AutomationId::kTypeAppShell:
    603       if (!GetExtensionRenderViewForId(id, profile, rvh))
    604         return false;
    605       break;
    606     default:
    607       return false;
    608   }
    609   return true;
    610 }
    611 
    612 bool GetExtensionForId(
    613     const AutomationId& id,
    614     Profile* profile,
    615     const extensions::Extension** extension) {
    616   if (id.type() != AutomationId::kTypeExtension)
    617     return false;
    618   ExtensionService* service = extensions::ExtensionSystem::Get(profile)->
    619       extension_service();
    620   const extensions::Extension* installed_extension =
    621       service->GetInstalledExtension(id.id());
    622   if (installed_extension)
    623     *extension = installed_extension;
    624   return !!installed_extension;
    625 }
    626 
    627 bool DoesObjectWithIdExist(const AutomationId& id, Profile* profile) {
    628   switch (id.type()) {
    629     case AutomationId::kTypeTab: {
    630       WebContents* tab;
    631       return GetTabForId(id, &tab);
    632     }
    633     case AutomationId::kTypeExtensionPopup:
    634     case AutomationId::kTypeExtensionBgPage:
    635     case AutomationId::kTypeExtensionInfobar:
    636     case AutomationId::kTypeAppShell: {
    637       RenderViewHost* rvh;
    638       return GetExtensionRenderViewForId(id, profile, &rvh);
    639     }
    640     case AutomationId::kTypeExtension: {
    641       const extensions::Extension* extension;
    642       return GetExtensionForId(id, profile, &extension);
    643     }
    644     default:
    645       break;
    646   }
    647   return false;
    648 }
    649 
    650 }  // namespace automation_util
    651