Add a NavigateToURL automation method which uses the JSON interface. Use it in ChromeDriver.
BUG=none
TEST=none

Review URL: http://codereview.chromium.org/6588115

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76820 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index 435cef4..7afc0d0 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -222,12 +222,14 @@
     AutomationProvider* automation,
     IPC::Message* reply_message,
     int number_of_navigations,
-    bool include_current_navigation)
+    bool include_current_navigation,
+    bool use_json_interface)
     : automation_(automation->AsWeakPtr()),
       reply_message_(reply_message),
       controller_(controller),
       navigations_remaining_(number_of_navigations),
-      navigation_started_(false) {
+      navigation_started_(false),
+      use_json_interface_(use_json_interface) {
   DCHECK_LT(0, navigations_remaining_);
   Source<NavigationController> source(controller_);
   registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, source);
@@ -295,9 +297,16 @@
 void NavigationNotificationObserver::ConditionMet(
     AutomationMsg_NavigationResponseValues navigation_result) {
   if (automation_) {
-    IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
-        reply_message_.get(), navigation_result);
-    automation_->Send(reply_message_.release());
+    if (use_json_interface_) {
+      DictionaryValue dict;
+      dict.SetInteger("result", navigation_result);
+      AutomationJSONReply(automation_, reply_message_.release())
+          .SendSuccess(&dict);
+    } else {
+      IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
+          reply_message_.get(), navigation_result);
+      automation_->Send(reply_message_.release());
+    }
   }
 
   delete this;
@@ -347,7 +356,7 @@
 
   new NavigationNotificationObserver(controller, automation_,
                                      reply_message_.release(),
-                                     1, false);
+                                     1, false, false);
 }
 
 TabClosedNotificationObserver::TabClosedNotificationObserver(
@@ -852,7 +861,7 @@
     case IDC_RELOAD: {
       new NavigationNotificationObserver(
           &browser->GetSelectedTabContents()->controller(),
-          automation, reply_message, 1, false);
+          automation, reply_message, 1, false, false);
       break;
     }
     default: {
@@ -2045,7 +2054,7 @@
                                                          true);
     new NavigationNotificationObserver(controller, automation_,
                                        reply_message_.release(),
-                                       1, false);
+                                       1, false, false);
   }
   delete this;
 }
diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h
index 14c1abfb..2005862 100644
--- a/chrome/browser/automation/automation_provider_observers.h
+++ b/chrome/browser/automation/automation_provider_observers.h
@@ -133,7 +133,8 @@
                                  AutomationProvider* automation,
                                  IPC::Message* reply_message,
                                  int number_of_navigations,
-                                 bool include_current_navigation);
+                                 bool include_current_navigation,
+                                 bool use_json_interface);
   virtual ~NavigationNotificationObserver();
 
   virtual void Observe(NotificationType type,
@@ -149,6 +150,7 @@
   NavigationController* controller_;
   int navigations_remaining_;
   bool navigation_started_;
+  bool use_json_interface_;
 
   DISALLOW_COPY_AND_ASSIGN(NavigationNotificationObserver);
 };
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index d5a0d91..b0dcbdf 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -151,6 +151,25 @@
   DISALLOW_COPY_AND_ASSIGN(AutomationInterstitialPage);
 };
 
+Browser* GetBrowserAt(int index) {
+  if (index < 0)
+    return NULL;
+  BrowserList::const_iterator iter = BrowserList::begin();
+  for (; (iter != BrowserList::end()) && (index > 0); ++iter, --index) {}
+  if (iter == BrowserList::end())
+    return NULL;
+  return *iter;
+}
+
+TabContents* GetTabContentsAt(int browser_index, int tab_index) {
+  if (tab_index < 0)
+    return NULL;
+  Browser* browser = GetBrowserAt(browser_index);
+  if (!browser || tab_index >= browser->tab_count())
+    return NULL;
+  return browser->GetTabContentsAt(tab_index);
+}
+
 }  // namespace
 
 TestingAutomationProvider::TestingAutomationProvider(Profile* profile)
@@ -593,7 +612,7 @@
 
     if (browser) {
       new NavigationNotificationObserver(tab, this, reply_message,
-                                         number_of_navigations, false);
+                                         number_of_navigations, false, false);
 
       // TODO(darin): avoid conversion to GURL.
       browser->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
@@ -641,7 +660,8 @@
     NavigationController* tab = tab_tracker_->GetResource(handle);
     Browser* browser = FindAndActivateTab(tab);
     if (browser && browser->command_updater()->IsCommandEnabled(IDC_RELOAD)) {
-      new NavigationNotificationObserver(tab, this, reply_message, 1, false);
+      new NavigationNotificationObserver(
+          tab, this, reply_message, 1, false, false);
       browser->Reload(CURRENT_TAB);
       return;
     }
@@ -665,7 +685,8 @@
       // not strictly correct, because a navigation can require both proxy and
       // server auth, but it should be OK for now.
       LoginHandler* handler = iter->second;
-      new NavigationNotificationObserver(tab, this, reply_message, 1, false);
+      new NavigationNotificationObserver(
+          tab, this, reply_message, 1, false, false);
       handler->SetAuth(WideToUTF16Hack(username), WideToUTF16Hack(password));
       return;
     }
@@ -685,7 +706,8 @@
     if (iter != login_handler_map_.end()) {
       // If auth is needed again after this, something is screwy.
       LoginHandler* handler = iter->second;
-      new NavigationNotificationObserver(tab, this, reply_message, 1, false);
+      new NavigationNotificationObserver(
+          tab, this, reply_message, 1, false, false);
       handler->CancelAuth();
       return;
     }
@@ -753,13 +775,9 @@
 
 void TestingAutomationProvider::GetBrowserWindow(int index, int* handle) {
   *handle = 0;
-  if (index >= 0) {
-    BrowserList::const_iterator iter = BrowserList::begin();
-    for (; (iter != BrowserList::end()) && (index > 0); ++iter, --index) {}
-    if (iter != BrowserList::end()) {
-      *handle = browser_tracker_->Add(*iter);
-    }
-  }
+  Browser* browser = GetBrowserAt(index);
+  if (browser)
+    *handle = browser_tracker_->Add(browser);
 }
 
 void TestingAutomationProvider::FindNormalBrowserWindow(int* handle) {
@@ -1348,7 +1366,7 @@
     TabContents* tab_contents = controller->tab_contents();
 
     new NavigationNotificationObserver(controller, this, reply_message, 1,
-                                       false);
+                                       false, false);
 
     AutomationInterstitialPage* interstitial =
         new AutomationInterstitialPage(tab_contents,
@@ -1448,7 +1466,7 @@
       if (ssl_blocking_page) {
         if (proceed) {
           new NavigationNotificationObserver(tab, this, reply_message, 1,
-                                             false);
+                                             false, false);
           ssl_blocking_page->Proceed();
           return;
         }
@@ -1815,7 +1833,7 @@
       if (info_bar_index < nav_controller->tab_contents()->infobar_count()) {
         if (wait_for_navigation) {
           new NavigationNotificationObserver(nav_controller, this,
-                                             reply_message, 1, false);
+                                             reply_message, 1, false, false);
         }
         InfoBarDelegate* delegate =
             nav_controller->tab_contents()->GetInfoBarDelegateAt(
@@ -1858,7 +1876,8 @@
     return;
   }
 
-  new NavigationNotificationObserver(controller, this, reply_message, 1, true);
+  new NavigationNotificationObserver(
+      controller, this, reply_message, 1, true, false);
 }
 
 void TestingAutomationProvider::SetIntPreference(int handle,
@@ -1983,7 +2002,7 @@
     Browser* browser = FindAndActivateTab(tab);
     if (browser && browser->command_updater()->IsCommandEnabled(IDC_BACK)) {
       new NavigationNotificationObserver(tab, this, reply_message,
-                                         number_of_navigations, false);
+                                         number_of_navigations, false, false);
       browser->GoBack(CURRENT_TAB);
       return;
     }
@@ -2001,7 +2020,7 @@
     Browser* browser = FindAndActivateTab(tab);
     if (browser && browser->command_updater()->IsCommandEnabled(IDC_FORWARD)) {
       new NavigationNotificationObserver(tab, this, reply_message,
-                                         number_of_navigations, false);
+                                         number_of_navigations, false, false);
       browser->GoForward(CURRENT_TAB);
       return;
     }
@@ -2095,6 +2114,10 @@
   std::map<std::string, JsonHandler> handler_map;
   handler_map["WaitForAllTabsToStopLoading"] =
       &TestingAutomationProvider::WaitForAllTabsToStopLoading;
+  handler_map["GetIndicesFromTab"] =
+      &TestingAutomationProvider::GetIndicesFromTab;
+  handler_map["NavigateToURL"] =
+      &TestingAutomationProvider::NavigateToURL;
 #if defined(OS_CHROMEOS)
   handler_map["LoginAsGuest"] = &TestingAutomationProvider::LoginAsGuest;
   handler_map["Login"] = &TestingAutomationProvider::Login;
@@ -4775,6 +4798,73 @@
   new AllTabsStoppedLoadingObserver(this, reply_message);
 }
 
+void TestingAutomationProvider::GetIndicesFromTab(
+    DictionaryValue* args,
+    IPC::Message* reply_message) {
+  AutomationJSONReply reply(this, reply_message);
+  int tab_handle = 0;
+  if (!args->GetInteger("tab_handle", &tab_handle) ||
+      !tab_tracker_->ContainsHandle(tab_handle)) {
+    reply.SendError("'tab_handle' missing or invalid");
+    return;
+  }
+  NavigationController* controller = tab_tracker_->GetResource(tab_handle);
+  BrowserList::const_iterator iter = BrowserList::begin();
+  int browser_index = 0;
+  for (; iter != BrowserList::end(); ++iter, ++browser_index) {
+    Browser* browser = *iter;
+    for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
+      if (browser->GetTabContentsAt(tab_index) == controller->tab_contents()) {
+        DictionaryValue dict;
+        dict.SetInteger("windex", browser_index);
+        dict.SetInteger("tab_index", tab_index);
+        reply.SendSuccess(&dict);
+        return;
+      }
+    }
+  }
+  reply.SendError("Could not find tab among current browser windows");
+}
+
+void TestingAutomationProvider::NavigateToURL(
+    DictionaryValue* args,
+    IPC::Message* reply_message) {
+  int browser_index = 0, tab_index = 0, navigation_count = 0;
+  std::string url;
+  if (!args->GetInteger("windex", &browser_index)) {
+    AutomationJSONReply(this, reply_message)
+        .SendError("'windex' missing or invalid");
+    return;
+  }
+  if (!args->GetInteger("tab_index", &tab_index)) {
+    AutomationJSONReply(this, reply_message)
+        .SendError("'tab_index' missing or invalid");
+    return;
+  }
+  if (!args->GetString("url", &url)) {
+    AutomationJSONReply(this, reply_message)
+        .SendError("'url' missing or invalid");
+    return;
+  }
+  if (!args->GetInteger("navigation_count", &navigation_count)) {
+    AutomationJSONReply(this, reply_message)
+        .SendError("'navigation_count' missing or invalid");
+    return;
+  }
+  Browser* browser = GetBrowserAt(browser_index);
+  TabContents* tab_contents = GetTabContentsAt(browser_index, tab_index);
+  if (!browser || !tab_contents) {
+    AutomationJSONReply(this, reply_message)
+        .SendError("Cannot locate tab or browser to navigate");
+    return;
+  }
+  new NavigationNotificationObserver(
+      &tab_contents->controller(), this, reply_message,
+      navigation_count, false, true);
+  browser->OpenURLFromTab(
+      tab_contents, GURL(url), GURL(), CURRENT_TAB, PageTransition::TYPED);
+}
+
 void TestingAutomationProvider::WaitForTabCountToBecome(
     int browser_handle,
     int target_tab_count,
diff --git a/chrome/browser/automation/testing_automation_provider.h b/chrome/browser/automation/testing_automation_provider.h
index 7d7c95c8..5828447 100644
--- a/chrome/browser/automation/testing_automation_provider.h
+++ b/chrome/browser/automation/testing_automation_provider.h
@@ -798,6 +798,22 @@
   void WaitForAllTabsToStopLoading(DictionaryValue* args,
                                    IPC::Message* reply_message);
 
+  // Gets the browser and tab index of the given tab. Uses the JSON interface.
+  // Example:
+  //   input: { "tab_handle": 3 }
+  //   output: { "browser_index": 1, "tab_index": 5 }
+  void GetIndicesFromTab(DictionaryValue* args, IPC::Message* reply_message);
+
+  // Navigates to the given URL. Uses the JSON interface.
+  // Example:
+  //   input: { "browser_index": 1,
+  //            "tab_index": 3,
+  //            "url": "http://www.google.com",
+  //            "navigation_count": 1  // number of navigations to wait for
+  //          }
+  //   output: { "result": AUTOMATION_MSG_NAVIGATION_SUCCESS }
+  void NavigateToURL(DictionaryValue* args, IPC::Message* reply_message);
+
 #if defined(OS_CHROMEOS)
   void LoginAsGuest(DictionaryValue* args, IPC::Message* reply_message);
 
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index ed01763..49b2219 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1788,6 +1788,8 @@
              'test/automation/autocomplete_edit_proxy.h',
              'test/automation/automation_handle_tracker.cc',
              'test/automation/automation_handle_tracker.h',
+             'test/automation/automation_json_requests.cc',
+             'test/automation/automation_json_requests.h',
              'test/automation/automation_proxy.cc',
              'test/automation/automation_proxy.h',
              'test/automation/browser_proxy.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 8e4330a8..83b46df 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -112,6 +112,8 @@
         'test/automation/autocomplete_edit_proxy.h',
         'test/automation/automation_handle_tracker.cc',
         'test/automation/automation_handle_tracker.h',
+        'test/automation/automation_json_requests.cc',
+        'test/automation/automation_json_requests.h',
         'test/automation/automation_proxy.cc',
         'test/automation/automation_proxy.h',
         'test/automation/browser_proxy.cc',
diff --git a/chrome/test/automation/automation_json_requests.cc b/chrome/test/automation/automation_json_requests.cc
new file mode 100644
index 0000000..c523b47
--- /dev/null
+++ b/chrome/test/automation/automation_json_requests.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 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/test/automation/automation_json_requests.h"
+
+#include "base/scoped_ptr.h"
+#include "base/values.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "chrome/common/automation_messages.h"
+#include "chrome/test/automation/automation_proxy.h"
+
+namespace {
+
+bool SendAutomationJSONRequest(AutomationMessageSender* sender,
+                               const DictionaryValue& request_dict,
+                               DictionaryValue* reply_dict) {
+  std::string request, reply;
+  base::JSONWriter::Write(&request_dict, false, &request);
+  bool success = false;
+  if (!SendAutomationJSONRequest(sender, request, &reply, &success))
+    return false;
+  if (!success) {
+    std::string command;
+    request_dict.GetString("command", &command);
+    LOG(ERROR) << "JSON request failed: " << command;
+    return false;
+  }
+  scoped_ptr<Value> value(base::JSONReader::Read(reply, true));
+  if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
+    return false;
+  reply_dict->MergeDictionary(static_cast<DictionaryValue*>(value.get()));
+  return true;
+}
+
+}  // namespace
+
+bool SendAutomationJSONRequest(AutomationMessageSender* sender,
+                               const std::string& request,
+                               std::string* reply,
+                               bool* success) {
+  return sender->Send(new AutomationMsg_SendJSONRequest(
+      -1, request, reply, success));
+}
+
+bool SendGetIndicesFromTabJSONRequest(
+    AutomationMessageSender* sender,
+    int handle,
+    int* browser_index,
+    int* tab_index) {
+  DictionaryValue request_dict;
+  request_dict.SetString("command", "GetIndicesFromTab");
+  request_dict.SetInteger("tab_handle", handle);
+  DictionaryValue reply_dict;
+  if (!SendAutomationJSONRequest(sender, request_dict, &reply_dict))
+    return false;
+  if (!reply_dict.GetInteger("windex", browser_index))
+    return false;
+  if (!reply_dict.GetInteger("tab_index", tab_index))
+    return false;
+  return true;
+}
+
+bool SendNavigateToURLJSONRequest(
+    AutomationMessageSender* sender,
+    int browser_index,
+    int tab_index,
+    const GURL& url,
+    int navigation_count,
+    AutomationMsg_NavigationResponseValues* nav_response) {
+  DictionaryValue dict;
+  dict.SetString("command", "NavigateToURL");
+  dict.SetInteger("windex", browser_index);
+  dict.SetInteger("tab_index", tab_index);
+  dict.SetString("url", url.possibly_invalid_spec());
+  dict.SetInteger("navigation_count", navigation_count);
+  DictionaryValue reply_dict;
+  if (!SendAutomationJSONRequest(sender, dict, &reply_dict))
+    return false;
+  int response = 0;
+  if (!reply_dict.GetInteger("result", &response))
+    return false;
+  *nav_response = static_cast<AutomationMsg_NavigationResponseValues>(response);
+  return true;
+}
diff --git a/chrome/test/automation/automation_json_requests.h b/chrome/test/automation/automation_json_requests.h
new file mode 100644
index 0000000..667c6af
--- /dev/null
+++ b/chrome/test/automation/automation_json_requests.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_TEST_AUTOMATION_AUTOMATION_JSON_REQUESTS_H_
+#define CHROME_TEST_AUTOMATION_AUTOMATION_JSON_REQUESTS_H_
+#pragma once
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "chrome/common/automation_constants.h"
+
+class AutomationMessageSender;
+class GURL;
+
+// Sends a JSON request to the chrome automation provider. Returns true
+// if the JSON request was successfully sent and the reply was received.
+// If true, |success| will be set to whether the JSON request was
+// completed successfully by the automation provider.
+bool SendAutomationJSONRequest(AutomationMessageSender* sender,
+                               const std::string& request,
+                               std::string* reply,
+                               bool* success) WARN_UNUSED_RESULT;
+
+// Requests the current browser and tab indices for the given |TabProxy|
+// handle. Returns true on success.
+bool SendGetIndicesFromTabJSONRequest(
+    AutomationMessageSender* sender,
+    int handle,
+    int* browser_index,
+    int* tab_index) WARN_UNUSED_RESULT;
+
+// Requests to navigate to the given url and wait for the given number of
+// navigations to complete. Returns true on success.
+bool SendNavigateToURLJSONRequest(
+    AutomationMessageSender* sender,
+    int browser_index,
+    int tab_index,
+    const GURL& url,
+    int navigation_count,
+    AutomationMsg_NavigationResponseValues* nav_response) WARN_UNUSED_RESULT;
+
+#endif  // CHROME_TEST_AUTOMATION_AUTOMATION_JSON_REQUESTS_H_
diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc
index 6aa64b3..96915caa 100644
--- a/chrome/test/automation/automation_proxy.cc
+++ b/chrome/test/automation/automation_proxy.cc
@@ -17,6 +17,7 @@
 #include "chrome/common/automation_constants.h"
 #include "chrome/common/automation_messages.h"
 #include "chrome/common/chrome_version_info.h"
+#include "chrome/test/automation/automation_json_requests.h"
 #include "chrome/test/automation/browser_proxy.h"
 #include "chrome/test/automation/extension_proxy.h"
 #include "chrome/test/automation/tab_proxy.h"
@@ -555,8 +556,7 @@
 bool AutomationProxy::SendJSONRequest(const std::string& request,
                                       std::string* response) {
   bool result = false;
-  return Send(new AutomationMsg_SendJSONRequest(
-      -1, request, response, &result));
+  if (!SendAutomationJSONRequest(this, request, response, &result))
+    return false;
   return result;
 }
-
diff --git a/chrome/test/webdriver/automation.cc b/chrome/test/webdriver/automation.cc
index f40d8e9a..6f56f93 100644
--- a/chrome/test/webdriver/automation.cc
+++ b/chrome/test/webdriver/automation.cc
@@ -18,9 +18,11 @@
 #include "base/path_service.h"
 #include "base/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/common/automation_constants.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/test/automation/automation_json_requests.h"
 #include "chrome/test/automation/automation_proxy.h"
 #include "chrome/test/automation/browser_proxy.h"
 #include "chrome/test/automation/proxy_launcher.h"
@@ -262,12 +264,19 @@
 void Automation::NavigateToURL(int tab_id,
                                const std::string& url,
                                bool* success) {
-  TabProxy* tab = GetTabById(tab_id);
-  if (!tab) {
+  int browser_index = 0, tab_index = 0;
+  if (!GetIndicesForTab(tab_id, &browser_index, &tab_index)) {
     *success = false;
     return;
   }
-  *success = tab->NavigateToURL(GURL(url));
+
+  AutomationMsg_NavigationResponseValues navigate_response;
+  if (!SendNavigateToURLJSONRequest(automation(), browser_index, tab_index,
+                                    GURL(url), 1, &navigate_response)) {
+    *success = false;
+    return;
+  }
+  *success = navigate_response != AUTOMATION_MSG_NAVIGATION_ERROR;
 }
 
 void Automation::GoForward(int tab_id, bool* success) {
@@ -389,14 +398,14 @@
                            bool* success) {
   *success = false;
   int browser_count = 0;
-  if (!launcher_->automation()->GetBrowserWindowCount(&browser_count)) {
+  if (!automation()->GetBrowserWindowCount(&browser_count)) {
     LOG(ERROR) << "Failed to get browser window count";
     return;
   }
   TabIdMap tab_id_map;
   for (int browser_index = 0; browser_index < browser_count; ++browser_index) {
     scoped_refptr<BrowserProxy> browser =
-        launcher_->automation()->GetBrowserWindow(browser_index);
+        automation()->GetBrowserWindow(browser_index);
     if (!browser.get())
       continue;
     int tab_count = 0;
@@ -431,7 +440,7 @@
 }
 
 void Automation::GetVersion(std::string* version) {
-  *version = launcher_->automation()->server_version();
+  *version = automation()->server_version();
 }
 
 void Automation::WaitForAllTabsToStopLoading(bool* success) {
@@ -439,7 +448,7 @@
   dict.SetString("command", "WaitForAllTabsToStopLoading");
   std::string request, reply;
   base::JSONWriter::Write(&dict, false, &request);
-  *success = launcher_->automation()->SendJSONRequest(request, &reply);
+  *success = automation()->SendJSONRequest(request, &reply);
 }
 
 TabProxy* Automation::GetTabById(int tab_id) {
@@ -450,6 +459,10 @@
   return NULL;
 }
 
+AutomationProxy* Automation::automation() const {
+  return launcher_->automation();
+}
+
 bool Automation::SendJSONRequest(int tab_id,
                                  const DictionaryValue& dict,
                                  std::string* reply) {
@@ -482,4 +495,14 @@
   return browser->SendJSONRequest(request, reply);
 }
 
+bool Automation::GetIndicesForTab(
+    int tab_id, int* browser_index, int* tab_index) {
+  if (!SendGetIndicesFromTabJSONRequest(automation(), tab_id,
+                                        browser_index, tab_index)) {
+    LOG(ERROR) << "Could not get browser and tab indices for WebDriver tab id";
+    return false;
+  }
+  return true;
+}
+
 }  // namespace webdriver
diff --git a/chrome/test/webdriver/automation.h b/chrome/test/webdriver/automation.h
index 0be141a..b773f4c 100644
--- a/chrome/test/webdriver/automation.h
+++ b/chrome/test/webdriver/automation.h
@@ -15,6 +15,7 @@
 #include "chrome/common/automation_constants.h"
 #include "ui/base/keycodes/keyboard_codes.h"
 
+class AutomationProxy;
 class DictionaryValue;
 class FilePath;
 class GURL;
@@ -114,7 +115,9 @@
 
  private:
   typedef std::map<int, scoped_refptr<TabProxy> > TabIdMap;
+
   TabProxy* GetTabById(int tab_id);
+  AutomationProxy* automation() const;
 
   scoped_ptr<ProxyLauncher> launcher_;
   // Map from tab ID to |TabProxy|. The tab ID is simply the |AutomationHandle|
@@ -124,6 +127,8 @@
   bool SendJSONRequest(
       int tab_id, const DictionaryValue& dict, std::string* reply);
 
+  bool GetIndicesForTab(int tab_id, int* browser_index, int* tab_index);
+
   DISALLOW_COPY_AND_ASSIGN(Automation);
 };