diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
index 888db0c177e72a8a786f8133e0546b219a0c9736..0e3aa72a8b3283445c6b482181c13c6c0163057d 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -111,7 +111,9 @@ using base::Time;
 
 AutomationProvider::AutomationProvider(Profile* profile)
     : profile_(profile),
-      reply_message_(NULL) {
+      reply_message_(NULL),
+      is_connected_(false),
+      initial_loads_complete_(false) {
   TRACE_EVENT_BEGIN("AutomationProvider::AutomationProvider", 0, "");
 
   browser_tracker_.reset(new AutomationBrowserTracker(this));
@@ -147,23 +149,38 @@ AutomationProvider::~AutomationProvider() {
   g_browser_process->ReleaseModule();
 }
 
-void AutomationProvider::ConnectToChannel(const std::string& channel_id) {
-  TRACE_EVENT_BEGIN("AutomationProvider::ConnectToChannel", 0, "");
+bool AutomationProvider::InitializeChannel(const std::string& channel_id) {
+  TRACE_EVENT_BEGIN("AutomationProvider::InitializeChannel", 0, "");
+
+  std::string effective_channel_id = channel_id;
+
+  // If the channel_id starts with kNamedInterfacePrefix, create a named IPC
+  // server and listen on it, else connect as client to an existing IPC server
+  bool use_named_interface =
+      channel_id.find(automation::kNamedInterfacePrefix) == 0;
+  if (use_named_interface) {
+    effective_channel_id = channel_id.substr(
+        strlen(automation::kNamedInterfacePrefix));
+    if (effective_channel_id.length() <= 0)
+      return false;
+  }
 
   if (!automation_resource_message_filter_.get()) {
     automation_resource_message_filter_ = new AutomationResourceMessageFilter;
   }
 
-  channel_.reset(
-      new IPC::SyncChannel(channel_id, IPC::Channel::MODE_CLIENT, this,
-                           g_browser_process->io_thread()->message_loop(),
-                           true, g_browser_process->shutdown_event()));
+  channel_.reset(new IPC::SyncChannel(
+      effective_channel_id,
+      use_named_interface ? IPC::Channel::MODE_NAMED_SERVER
+                          : IPC::Channel::MODE_CLIENT,
+      this,
+      g_browser_process->io_thread()->message_loop(),
+      true, g_browser_process->shutdown_event()));
   channel_->AddFilter(automation_resource_message_filter_);
 
-  // Send a hello message with our current automation protocol version.
-  channel_->Send(new AutomationMsg_Hello(0, GetProtocolVersion().c_str()));
+  TRACE_EVENT_END("AutomationProvider::InitializeChannel", 0, "");
 
-  TRACE_EVENT_END("AutomationProvider::ConnectToChannel", 0, "");
+  return true;
 }
 
 std::string AutomationProvider::GetProtocolVersion() {
@@ -172,11 +189,16 @@ std::string AutomationProvider::GetProtocolVersion() {
 }
 
 void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) {
-  if (expected_tabs == 0) {
-    Send(new AutomationMsg_InitialLoadsComplete(0));
-  } else {
+  if (expected_tabs == 0)
+    OnInitialLoadsComplete();
+  else
     initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this));
-  }
+}
+
+void AutomationProvider::OnInitialLoadsComplete() {
+  initial_loads_complete_ = true;
+  if (is_connected_)
+    Send(new AutomationMsg_InitialLoadsComplete(0));
 }
 
 NotificationObserver* AutomationProvider::AddNavigationStatusListener(
@@ -325,6 +347,17 @@ const Extension* AutomationProvider::GetDisabledExtension(
   return NULL;
 }
 
+void AutomationProvider::OnChannelConnected(int pid) {
+  is_connected_ = true;
+  LOG(INFO) << "Testing channel connected, sending hello message";
+
+  // Send a hello message with our current automation protocol version.
+  chrome::VersionInfo version_info;
+  channel_->Send(new AutomationMsg_Hello(0, version_info.Version()));
+  if (initial_loads_complete_)
+    Send(new AutomationMsg_InitialLoadsComplete(0));
+}
+
 void AutomationProvider::OnMessageReceived(const IPC::Message& message) {
   IPC_BEGIN_MESSAGE_MAP(AutomationProvider, message)
 #if !defined(OS_MACOSX)
diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h
index 8abc0b2eb3c7ef972ed57f97805c98615629972c..797d8b08abb0179c66e3cbbdb7178b37ed440ffb 100644
--- a/chrome/browser/automation/automation_provider.h
+++ b/chrome/browser/automation/automation_provider.h
@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/compiler_specific.h"
 #include "base/observer_list.h"
 #include "base/scoped_ptr.h"
 #include "base/string16.h"
@@ -82,15 +83,23 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
 
   Profile* profile() const { return profile_; }
 
-  // Establishes a connection to an automation client, if present.
-  // An AutomationProxy should be established (probably in a different process)
-  // before calling this.
-  void ConnectToChannel(const std::string& channel_id);
+  // Initializes a channel for a connection to an AutomationProxy.
+  // If channel_id starts with kNamedInterfacePrefix, it will act
+  // as a server, create a named IPC socket with channel_id as its
+  // path, and will listen on the socket for incoming connections.
+  // If channel_id does not, it will act as a client and establish
+  // a connection on its primary IPC channel. See ipc/ipc_channel_posix.cc
+  // for more information about kPrimaryIPCChannel.
+  bool InitializeChannel(const std::string& channel_id) WARN_UNUSED_RESULT;
 
   // Sets the number of tabs that we expect; when this number of tabs has
   // loaded, an AutomationMsg_InitialLoadsComplete message is sent.
   void SetExpectedTabCount(size_t expected_tabs);
 
+  // Called when the inital set of tabs has finished loading.
+  // Call SetExpectedTabCount(0) to set this to true immediately.
+  void OnInitialLoadsComplete();
+
   // Add a listener for navigation status notification. Currently only
   // navigation completion is observed; when the |number_of_navigations|
   // complete, the completed_response object is sent; if the server requires
@@ -138,6 +147,7 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
 
   // IPC implementations
   virtual bool Send(IPC::Message* msg);
+  virtual void OnChannelConnected(int pid);
   virtual void OnMessageReceived(const IPC::Message& msg);
   virtual void OnChannelError();
 
@@ -401,6 +411,12 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
   scoped_ptr<AutomationExtensionTracker> extension_tracker_;
   PortContainerMap port_containers_;
 
+  // True iff connected to an AutomationProxy.
+  bool is_connected_;
+
+  // True iff browser finished loading initial set of tabs.
+  bool initial_loads_complete_;
+
   DISALLOW_COPY_AND_ASSIGN(AutomationProvider);
 };
 
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index 505836c3b2636b79451de4e84a3e772a5d4d57a4..dc0e9392833e1b0798d282c3d7a166429e04d629 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -146,7 +146,7 @@ DictionaryValue* InitialLoadObserver::GetTimingInformation() const {
 
 void InitialLoadObserver::ConditionMet() {
   registrar_.RemoveAll();
-  automation_->Send(new AutomationMsg_InitialLoadsComplete(0));
+  automation_->OnInitialLoadsComplete();
 }
 
 NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation)
diff --git a/chrome/browser/ui/browser_init.cc b/chrome/browser/ui/browser_init.cc
index 7747c8adaa357e4e47a8d26963ec8343eec714ae..8835996679d5a38046c0968b460fa92edfb3c7a4 100644
--- a/chrome/browser/ui/browser_init.cc
+++ b/chrome/browser/ui/browser_init.cc
@@ -1004,10 +1004,11 @@ bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line,
         expected_tab_count =
             std::max(1, static_cast<int>(command_line.args().size()));
       }
-      CreateAutomationProvider<TestingAutomationProvider>(
+      if (!CreateAutomationProvider<TestingAutomationProvider>(
           testing_channel_id,
           profile,
-          static_cast<size_t>(expected_tab_count));
+          static_cast<size_t>(expected_tab_count)))
+        return false;
     }
   }
 
@@ -1024,11 +1025,13 @@ bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line,
       silent_launch = true;
 
     if (command_line.HasSwitch(switches::kChromeFrame)) {
-      CreateAutomationProvider<ChromeFrameAutomationProvider>(
-          automation_channel_id, profile, expected_tabs);
+      if (!CreateAutomationProvider<ChromeFrameAutomationProvider>(
+          automation_channel_id, profile, expected_tabs))
+        return false;
     } else {
-      CreateAutomationProvider<AutomationProvider>(automation_channel_id,
-                                                   profile, expected_tabs);
+      if (!CreateAutomationProvider<AutomationProvider>(
+          automation_channel_id, profile, expected_tabs))
+        return false;
     }
   }
 
@@ -1087,16 +1090,20 @@ bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line,
 }
 
 template <class AutomationProviderClass>
-void BrowserInit::CreateAutomationProvider(const std::string& channel_id,
+bool BrowserInit::CreateAutomationProvider(const std::string& channel_id,
                                            Profile* profile,
                                            size_t expected_tabs) {
   scoped_refptr<AutomationProviderClass> automation =
       new AutomationProviderClass(profile);
-  automation->ConnectToChannel(channel_id);
+
+  if (!automation->InitializeChannel(channel_id))
+    return false;
   automation->SetExpectedTabCount(expected_tabs);
 
   AutomationProviderList* list =
       g_browser_process->InitAutomationProviderList();
   DCHECK(list);
   list->AddProvider(automation);
+
+  return true;
 }
diff --git a/chrome/browser/ui/browser_init.h b/chrome/browser/ui/browser_init.h
index 07e99e9828104bc5721f707e427b802839daabc7..746320c0acae509871ae24a846e673ed2b1bb8b8 100644
--- a/chrome/browser/ui/browser_init.h
+++ b/chrome/browser/ui/browser_init.h
@@ -53,7 +53,7 @@ class BrowserInit {
   }
 
   template <class AutomationProviderClass>
-  static void CreateAutomationProvider(const std::string& channel_id,
+  static bool CreateAutomationProvider(const std::string& channel_id,
                                        Profile* profile,
                                        size_t expected_tabs);
 
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 20c97052a66eb9fd4fe4eb49b12734391ba9d9a9..60a768c3eeec54fd8ef83001f56112ccc565d2ca 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -174,6 +174,8 @@
       'sources': [
         'test/automated_ui_tests/automated_ui_test_base.cc',
         'test/automated_ui_tests/automated_ui_test_base.h',
+        'test/automation/proxy_launcher.cc',
+        'test/automation/proxy_launcher.h',
         'test/testing_browser_process.h',
         'test/ui/javascript_test_util.cc',
         'test/ui/npapi_test_helper.cc',
@@ -524,6 +526,7 @@
         'test/ui/dromaeo_benchmark_uitest.cc',
         'test/ui/history_uitest.cc',
         'test/ui/layout_plugin_uitest.cc',
+        'test/ui/named_interface_uitest.cc',
         'test/ui/npapi_uitest.cc',
         'test/ui/omnibox_uitest.cc',
         'test/ui/pepper_uitest.cc',
@@ -602,6 +605,10 @@
               },
             },
           },
+          'sources!': [
+            # TODO(dtu): port to windows http://crosbug.com/8515
+            'test/ui/named_interface_uitest.cc',
+          ],
         }, { # else: OS != "win"
           'sources!': [
             # TODO(port): http://crbug.com/45770
@@ -3282,6 +3289,8 @@
              '-Wno-uninitialized',
           ],
           'sources': [
+            'test/automation/proxy_launcher.cc',
+            'test/automation/proxy_launcher.h',
             'test/pyautolib/pyautolib.cc',
             'test/pyautolib/pyautolib.h',
             'test/ui/ui_test.cc',
diff --git a/chrome/common/automation_constants.cc b/chrome/common/automation_constants.cc
index c2538714076aaefee05ccdbc6e084218d84c7071..9480254175ace3711c3a9666687518cc2f2fa132 100644
--- a/chrome/common/automation_constants.cc
+++ b/chrome/common/automation_constants.cc
@@ -5,6 +5,7 @@
 #include "chrome/common/automation_constants.h"
 
 namespace automation {
+
 // JSON value labels for proxy settings that are passed in via
 // AutomationMsg_SetProxyConfig.
 const char kJSONProxyAutoconfig[] = "proxy.autoconfig";
@@ -12,4 +13,10 @@ const char kJSONProxyNoProxy[] = "proxy.no_proxy";
 const char kJSONProxyPacUrl[] = "proxy.pac_url";
 const char kJSONProxyBypassList[] = "proxy.bypass_list";
 const char kJSONProxyServer[] = "proxy.server";
-}
+
+// Named testing interface is used when you want to connect an
+// AutomationProxy to an already-running browser instance.
+const char kNamedInterfacePrefix[] = "NamedTestingInterface:";
+
+}  // namespace automation
+
diff --git a/chrome/common/automation_constants.h b/chrome/common/automation_constants.h
index 13b120eef2af5a99dfa0d8017cfee9c9258ae14b..65797764543b8db89230154867c66adc27670684 100644
--- a/chrome/common/automation_constants.h
+++ b/chrome/common/automation_constants.h
@@ -7,6 +7,7 @@
 #pragma once
 
 namespace automation {
+
 // JSON value labels for proxy settings that are passed in via
 // AutomationMsg_SetProxyConfig. These are here since they are used by both
 // AutomationProvider and AutomationProxy.
@@ -16,9 +17,16 @@ extern const char kJSONProxyPacUrl[];
 extern const char kJSONProxyBypassList[];
 extern const char kJSONProxyServer[];
 
+// When passing the kTestingChannelID switch to the browser, prepend
+// this prefix to the channel id to enable the named testing interface.
+// Named testing interface is used when you want to connect an
+// AutomationProxy to an already-running browser instance.
+extern const char kNamedInterfacePrefix[];
+
 // Amount of time to wait before querying the browser.
 static const int kSleepTime = 250;
-}
+
+}  // namespace automation
 
 // Used by AutomationProxy, declared here so that other headers don't need
 // to include automation_proxy.h.
diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc
index 4da271ba80e984736f44bff14f84d78df5b74992..cd5d9afb2105d75f746334c238c5b30c077b9493 100644
--- a/chrome/test/automation/automation_proxy.cc
+++ b/chrome/test/automation/automation_proxy.cc
@@ -107,10 +107,8 @@ AutomationProxy::AutomationProxy(int command_execution_timeout_ms,
   // least it is legal... ;-)
   DCHECK_GE(command_execution_timeout_ms, 0);
   listener_thread_id_ = PlatformThread::CurrentId();
-  InitializeChannelID();
   InitializeHandleTracker();
   InitializeThread();
-  InitializeChannel();
 }
 
 AutomationProxy::~AutomationProxy() {
@@ -122,7 +120,7 @@ AutomationProxy::~AutomationProxy() {
   tracker_.reset();
 }
 
-void AutomationProxy::InitializeChannelID() {
+std::string AutomationProxy::GenerateChannelID() {
   // The channel counter keeps us out of trouble if we create and destroy
   // several AutomationProxies sequentially over the course of a test run.
   // (Creating the channel sometimes failed before when running a lot of
@@ -133,7 +131,7 @@ void AutomationProxy::InitializeChannelID() {
   std::ostringstream buf;
   buf << "ChromeTestingInterface:" << base::GetCurrentProcId() <<
          "." << ++channel_counter;
-  channel_id_ = buf.str();
+  return buf.str();
 }
 
 void AutomationProxy::InitializeThread() {
@@ -146,7 +144,8 @@ void AutomationProxy::InitializeThread() {
   thread_.swap(thread);
 }
 
-void AutomationProxy::InitializeChannel() {
+void AutomationProxy::InitializeChannel(const std::string& channel_id,
+                                        bool use_named_interface) {
   DCHECK(shutdown_event_.get() != NULL);
 
   // TODO(iyengar)
@@ -154,8 +153,9 @@ void AutomationProxy::InitializeChannel() {
   // provider, where we use the shutdown event provided by the chrome browser
   // process.
   channel_.reset(new IPC::SyncChannel(
-    channel_id_,
-    IPC::Channel::MODE_SERVER,
+    channel_id,
+    use_named_interface ? IPC::Channel::MODE_NAMED_CLIENT
+                        : IPC::Channel::MODE_SERVER,
     this,  // we are the listener
     thread_->message_loop(),
     true,
diff --git a/chrome/test/automation/automation_proxy.h b/chrome/test/automation/automation_proxy.h
index 853a6d3cfcf0e98239a19b9d7ef2fb0a34b4f88c..7902ef0bd369a61e9ef87107b8e74075345275f7 100644
--- a/chrome/test/automation/automation_proxy.h
+++ b/chrome/test/automation/automation_proxy.h
@@ -61,6 +61,17 @@ class AutomationProxy : public IPC::Channel::Listener,
   AutomationProxy(int command_execution_timeout_ms, bool disconnect_on_failure);
   virtual ~AutomationProxy();
 
+  // Creates a previously unused channel id.
+  static std::string GenerateChannelID();
+
+  // Initializes a channel for a connection to an AutomationProvider.
+  // If use_named_interface is false, it will act as a client
+  // and connect to the named IPC socket with channel_id as its path.
+  // If use_named_interface is true, it will act as a server and
+  // use an anonymous socketpair instead.
+  void InitializeChannel(const std::string& channel_id,
+                         bool use_named_interface);
+
   // IPC callback
   virtual void OnMessageReceived(const IPC::Message& msg);
   virtual void OnChannelError();
@@ -208,10 +219,6 @@ class AutomationProxy : public IPC::Channel::Listener,
                             const std::string& password) WARN_UNUSED_RESULT;
 #endif
 
-  // Returns the ID of the automation IPC channel, so that it can be
-  // passed to the app as a launch parameter.
-  const std::string& channel_id() const { return channel_id_; }
-
 #if defined(OS_POSIX)
   base::file_handle_mapping_vector fds_to_map() const;
 #endif
@@ -263,12 +270,9 @@ class AutomationProxy : public IPC::Channel::Listener,
 
  protected:
   template <class T> scoped_refptr<T> ProxyObjectFromHandle(int handle);
-  void InitializeChannelID();
   void InitializeThread();
-  void InitializeChannel();
   void InitializeHandleTracker();
 
-  std::string channel_id_;
   scoped_ptr<base::Thread> thread_;
   scoped_ptr<IPC::SyncChannel> channel_;
   scoped_ptr<AutomationHandleTracker> tracker_;
diff --git a/chrome/test/automation/automation_proxy_uitest.cc b/chrome/test/automation/automation_proxy_uitest.cc
index 76c22d50bbd552e659f916ba401fb81d0a11c307..d963f1dc910ed58805286cd69232a5044ce6105a 100644
--- a/chrome/test/automation/automation_proxy_uitest.cc
+++ b/chrome/test/automation/automation_proxy_uitest.cc
@@ -29,6 +29,7 @@
 #include "chrome/test/automation/autocomplete_edit_proxy.h"
 #include "chrome/test/automation/automation_proxy_uitest.h"
 #include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/proxy_launcher.h"
 #include "chrome/test/automation/tab_proxy.h"
 #include "chrome/test/automation/window_proxy.h"
 #include "chrome/test/ui_test_utils.h"
@@ -47,6 +48,34 @@ using testing::CreateFunctor;
 using testing::StrEq;
 using testing::_;
 
+
+// Replace the default automation proxy with our mock client.
+class ExternalTabUITestMockLauncher : public ProxyLauncher {
+ public:
+  explicit ExternalTabUITestMockLauncher(ExternalTabUITestMockClient **mock)
+      : mock_(mock) {
+    channel_id_ = AutomationProxy::GenerateChannelID();
+  }
+
+  AutomationProxy* CreateAutomationProxy(int execution_timeout) {
+    *mock_ = new ExternalTabUITestMockClient(execution_timeout);
+    (*mock_)->InitializeChannel(channel_id_, false);
+    return *mock_;
+  }
+
+  void InitializeConnection(UITestBase* ui_test_base) const {
+    ui_test_base->LaunchBrowserAndServer();
+  }
+
+  std::string PrefixedChannelID() const {
+    return channel_id_;
+  }
+
+ private:
+  ExternalTabUITestMockClient **mock_;
+  std::string channel_id_;      // Channel id of automation proxy.
+};
+
 class AutomationProxyTest : public UITest {
  protected:
   AutomationProxyTest() {
@@ -853,9 +882,9 @@ template <typename T> T** ReceivePointer(scoped_refptr<T>& p) {  // NOLINT
   return reinterpret_cast<T**>(&p);
 }
 
-AutomationProxy* ExternalTabUITest::CreateAutomationProxy(int exec_timeout) {
-  mock_ = new ExternalTabUITestMockClient(exec_timeout);
-  return mock_;
+// Replace the default automation proxy with our mock client.
+ProxyLauncher* ExternalTabUITest::CreateProxyLauncher() {
+  return new ExternalTabUITestMockLauncher(&mock_);
 }
 
 // Create with specifying a url
diff --git a/chrome/test/automation/automation_proxy_uitest.h b/chrome/test/automation/automation_proxy_uitest.h
index 1f2c9f35c6d0bcab6108e609f213e5464e820c8c..cda06c1895fc0de8b4a42b088ee6e8e95afd70d6 100644
--- a/chrome/test/automation/automation_proxy_uitest.h
+++ b/chrome/test/automation/automation_proxy_uitest.h
@@ -110,16 +110,17 @@ class ExternalTabUITestMockClient : public AutomationProxy {
 class ExternalTabUITest : public UITest {
  public:
   ExternalTabUITest() : UITest(MessageLoop::TYPE_UI) {}
-  // Override UITest's CreateAutomationProxy to provide the unit test
+  // Override UITest's CreateProxyLauncher to provide the unit test
   // with our special implementation of AutomationProxy.
-  // This function is called from within UITest::LaunchBrowserAndServer.
-  virtual AutomationProxy* CreateAutomationProxy(int execution_timeout);
+  // This function is called from within UITest::SetUp().
+  virtual ProxyLauncher* CreateProxyLauncher();
  protected:
   // Filtered Inet will override automation callbacks for network resources.
   virtual bool ShouldFilterInet() {
     return false;
   }
   ExternalTabUITestMockClient* mock_;
+  std::string channel_id_;      // Channel id of automation proxy.
 };
 
 #endif  // CHROME_TEST_AUTOMATION_AUTOMATION_PROXY_UITEST_H_
diff --git a/chrome/test/automation/proxy_launcher.cc b/chrome/test/automation/proxy_launcher.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2325958b618122a74ce6209f5541e6f69ae4a5c2
--- /dev/null
+++ b/chrome/test/automation/proxy_launcher.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2010 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/proxy_launcher.h"
+
+#include "chrome/common/automation_constants.h"
+#include "chrome/common/logging_chrome.h"
+#include "chrome/test/automation/automation_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+
+// Default path of named testing interface.
+static const char kInterfacePath[] = "/var/tmp/ChromeTestingInterface";
+
+// NamedProxyLauncher functions
+
+NamedProxyLauncher::NamedProxyLauncher(bool launch_browser,
+                                       bool disconnect_on_failure)
+    : launch_browser_(launch_browser),
+      disconnect_on_failure_(disconnect_on_failure) {
+  channel_id_ = kInterfacePath;
+}
+
+AutomationProxy* NamedProxyLauncher::CreateAutomationProxy(
+    int execution_timeout) {
+  AutomationProxy* proxy = new AutomationProxy(execution_timeout,
+                                               disconnect_on_failure_);
+  proxy->InitializeChannel(channel_id_, true);
+  return proxy;
+}
+
+void NamedProxyLauncher::InitializeConnection(UITestBase* ui_test_base) const {
+  if (launch_browser_) {
+    // Set up IPC testing interface as a client.
+    ui_test_base->LaunchBrowser();
+
+    // Wait for browser to be ready for connections.
+    struct stat file_info;
+    while (stat(kInterfacePath, &file_info))
+      PlatformThread::Sleep(automation::kSleepTime);
+  }
+
+  ui_test_base->ConnectToRunningBrowser();
+}
+
+std::string NamedProxyLauncher::PrefixedChannelID() const {
+  std::string channel_id;
+  channel_id.append(automation::kNamedInterfacePrefix).append(channel_id_);
+  return channel_id;
+}
+
+// AnonymousProxyLauncher functions
+
+AnonymousProxyLauncher::AnonymousProxyLauncher(bool disconnect_on_failure)
+    : disconnect_on_failure_(disconnect_on_failure) {
+  channel_id_ = AutomationProxy::GenerateChannelID();
+}
+
+AutomationProxy* AnonymousProxyLauncher::CreateAutomationProxy(
+    int execution_timeout) {
+  AutomationProxy* proxy = new AutomationProxy(execution_timeout,
+                                               disconnect_on_failure_);
+  proxy->InitializeChannel(channel_id_, false);
+  return proxy;
+}
+
+void AnonymousProxyLauncher::InitializeConnection(
+    UITestBase* ui_test_base) const {
+  ui_test_base->LaunchBrowserAndServer();
+}
+
+std::string AnonymousProxyLauncher::PrefixedChannelID() const {
+  return channel_id_;
+}
+
diff --git a/chrome/test/automation/proxy_launcher.h b/chrome/test/automation/proxy_launcher.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f4d04dd8d93f0ff7832eb03a992c20109519a50
--- /dev/null
+++ b/chrome/test/automation/proxy_launcher.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2010 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_PROXY_LAUNCHER_H_
+#define CHROME_TEST_AUTOMATION_PROXY_LAUNCHER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class AutomationProxy;
+class UITestBase;
+
+// Subclass from this class to use a different implementation of AutomationProxy
+// or to use different channel IDs inside a class that derives from UITest.
+class ProxyLauncher {
+ public:
+  ProxyLauncher() {}
+  virtual ~ProxyLauncher() {}
+
+  // Creates an automation proxy.
+  virtual AutomationProxy* CreateAutomationProxy(
+      int execution_timeout) = 0;
+
+  // Launches the browser if needed and establishes a connection
+  // connection with it using the specified UITestBase.
+  virtual void InitializeConnection(UITestBase* ui_test_base) const = 0;
+
+  // Returns the automation proxy's channel with any prefixes prepended,
+  // for passing as a command line parameter over to the browser.
+  virtual std::string PrefixedChannelID() const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProxyLauncher);
+};
+
+// Uses an automation proxy that communicates over a named socket.
+// This is used if you want to connect an AutomationProxy
+// to a browser process that is already running.
+// The channel id of the proxy is a constant specified by kInterfacePath.
+class NamedProxyLauncher : public ProxyLauncher {
+ public:
+  // If launch_browser is true, launches Chrome with named interface enabled.
+  // Otherwise, there should be an existing instance the proxy can connect to.
+  NamedProxyLauncher(bool launch_browser, bool disconnect_on_failure);
+
+  virtual AutomationProxy* CreateAutomationProxy(int execution_timeout);
+  virtual void InitializeConnection(UITestBase* ui_test_base) const;
+  virtual std::string PrefixedChannelID() const;
+
+ protected:
+  std::string channel_id_;      // Channel id of automation proxy.
+  bool launch_browser_;         // True if we should launch the browser too.
+  bool disconnect_on_failure_;  // True if we disconnect on IPC channel failure.
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NamedProxyLauncher);
+};
+
+// Uses an automation proxy that communicates over an anonymous socket.
+class AnonymousProxyLauncher : public ProxyLauncher {
+ public:
+  explicit AnonymousProxyLauncher(bool disconnect_on_failure);
+  virtual AutomationProxy* CreateAutomationProxy(int execution_timeout);
+  virtual void InitializeConnection(UITestBase* ui_test_base) const;
+  virtual std::string PrefixedChannelID() const;
+
+ protected:
+  std::string channel_id_;      // Channel id of automation proxy.
+  bool disconnect_on_failure_;  // True if we disconnect on IPC channel failure.
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AnonymousProxyLauncher);
+};
+
+#endif  // CHROME_TEST_AUTOMATION_PROXY_LAUNCHER_H_
+
diff --git a/chrome/test/ui/named_interface_uitest.cc b/chrome/test/ui/named_interface_uitest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..84341fc6af21e4bdc261f1716daf9a7ba5e8728d
--- /dev/null
+++ b/chrome/test/ui/named_interface_uitest.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2010 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/ui/ui_test.h"
+
+#include "chrome/common/url_constants.h"
+#include "chrome/test/automation/proxy_launcher.h"
+
+// The named testing interface enables the use of a named socket for controlling
+// the browser. This eliminates the dependency that the browser must be forked
+// from the controlling process.
+namespace {
+
+class NamedInterfaceTest : public UITest {
+ public:
+  NamedInterfaceTest() {
+    show_window_ = true;
+  }
+
+  virtual ProxyLauncher *CreateProxyLauncher() {
+    return new NamedProxyLauncher(true, true);
+  }
+};
+
+// This test is flaky on Linux bots.  http://crbug.com/66414
+#if defined(OS_LINUX)
+#define MAYBE_BasicNamedInterface FLAKY_BasicNamedInterface
+#else
+#define MAYBE_BasicNamedInterface BasicNamedInterface
+#endif
+
+// Basic sanity test for named testing interface which
+// launches a browser instance that uses a named socket, then
+// sends it some commands to open some tabs over that socket.
+TEST_F(NamedInterfaceTest, MAYBE_BasicNamedInterface) {
+  scoped_refptr<BrowserProxy> browser_proxy(
+      automation()->GetBrowserWindow(0));
+  ASSERT_TRUE(browser_proxy.get());
+
+  for (int i = 0; i < 10; ++i)
+    ASSERT_TRUE(browser_proxy->AppendTab(GURL(chrome::kAboutBlankURL)));
+}
+
+// TODO(dtu): crosbug.com/8514: Write a test that makes sure you can disconnect,
+//            then reconnect with a new connection and continue automation.
+
+}  // namespace
+
diff --git a/chrome/test/ui/ui_test.cc b/chrome/test/ui/ui_test.cc
index ebc285ad6dec1a89e6f5b5bf6351fff420df4f5e..f06fa89bf1f67f120faf131b1c6641896510761d 100644
--- a/chrome/test/ui/ui_test.cc
+++ b/chrome/test/ui/ui_test.cc
@@ -43,6 +43,7 @@
 #include "chrome/test/automation/automation_proxy.h"
 #include "chrome/test/automation/browser_proxy.h"
 #include "chrome/test/automation/javascript_execution_controller.h"
+#include "chrome/test/automation/proxy_launcher.h"
 #include "chrome/test/automation/tab_proxy.h"
 #include "chrome/test/automation/window_proxy.h"
 #include "chrome/test/chrome_process_util.h"
@@ -140,7 +141,9 @@ void UITestBase::SetUp() {
   JavaScriptExecutionController::set_timeout(
       TestTimeouts::action_max_timeout_ms());
   test_start_time_ = Time::NowFromSystemTime();
-  LaunchBrowserAndServer();
+
+  launcher_.reset(CreateProxyLauncher());
+  launcher_->InitializeConnection(this);
 }
 
 void UITestBase::TearDown() {
@@ -175,24 +178,39 @@ void UITestBase::TearDown() {
 
 // TODO(phajdan.jr): get rid of set_command_execution_timeout_ms.
 void UITestBase::set_command_execution_timeout_ms(int timeout) {
-  server_->set_command_execution_timeout_ms(timeout);
+  automation_proxy_->set_command_execution_timeout_ms(timeout);
   VLOG(1) << "Automation command execution timeout set to " << timeout << " ms";
 }
 
-AutomationProxy* UITestBase::CreateAutomationProxy(int execution_timeout) {
-  return new AutomationProxy(execution_timeout, false);
+ProxyLauncher* UITestBase::CreateProxyLauncher() {
+  return new AnonymousProxyLauncher(false);
+}
+
+void UITestBase::LaunchBrowser() {
+  LaunchBrowser(launch_arguments_, clear_profile_);
 }
 
 void UITestBase::LaunchBrowserAndServer() {
-  // Set up IPC testing interface server.
-  server_.reset(CreateAutomationProxy(
-                    TestTimeouts::command_execution_timeout_ms()));
+  // Set up IPC testing interface as a server.
+  automation_proxy_.reset(launcher_->CreateAutomationProxy(
+                              TestTimeouts::command_execution_timeout_ms()));
 
   LaunchBrowser(launch_arguments_, clear_profile_);
-  ASSERT_EQ(AUTOMATION_SUCCESS, server_->WaitForAppLaunch())
+  WaitForBrowserLaunch();
+}
+
+void UITestBase::ConnectToRunningBrowser() {
+  // Set up IPC testing interface as a client.
+  automation_proxy_.reset(launcher_->CreateAutomationProxy(
+                              TestTimeouts::command_execution_timeout_ms()));
+  WaitForBrowserLaunch();
+}
+
+void UITestBase::WaitForBrowserLaunch() {
+  ASSERT_EQ(AUTOMATION_SUCCESS, automation_proxy_->WaitForAppLaunch())
       << "Error while awaiting automation ping from browser process";
   if (wait_for_initial_loads_)
-    ASSERT_TRUE(server_->WaitForInitialLoads());
+    ASSERT_TRUE(automation_proxy_->WaitForInitialLoads());
   else
     PlatformThread::Sleep(sleep_timeout_ms());
 
@@ -210,7 +228,7 @@ void UITestBase::CloseBrowserAndServer() {
     AssertAppNotRunning(StringPrintf(
         L"Unable to quit all browser processes. Original PID %d", process_id_));
 
-  server_.reset();  // Shut down IPC testing interface.
+  automation_proxy_.reset();  // Shut down IPC testing interface.
 }
 
 void UITestBase::LaunchBrowser(const CommandLine& arguments,
@@ -567,7 +585,7 @@ FilePath UITestBase::GetDownloadDirectory() {
 }
 
 void UITestBase::CloseBrowserAsync(BrowserProxy* browser) const {
-  ASSERT_TRUE(server_->Send(
+  ASSERT_TRUE(automation_proxy_->Send(
       new AutomationMsg_CloseBrowserRequestAsync(0, browser->handle())));
 }
 
@@ -579,7 +597,7 @@ bool UITestBase::CloseBrowser(BrowserProxy* browser,
 
   bool result = true;
 
-  bool succeeded = server_->Send(new AutomationMsg_CloseBrowser(
+  bool succeeded = automation_proxy_->Send(new AutomationMsg_CloseBrowser(
       0, browser->handle(), &result, application_closed));
 
   if (!succeeded)
@@ -694,10 +712,9 @@ void UITestBase::PrepareTestCommandline(CommandLine* command_line) {
   if (dom_automation_enabled_)
     command_line->AppendSwitch(switches::kDomAutomationController);
 
-  if (include_testing_id_) {
+  if (include_testing_id_)
     command_line->AppendSwitchASCII(switches::kTestingChannelID,
-                                   server_->channel_id());
-  }
+                                    launcher_->PrefixedChannelID());
 
   if (!show_error_dialogs_ &&
       !CommandLine::ForCurrentProcess()->HasSwitch(
@@ -786,10 +803,11 @@ bool UITestBase::LaunchBrowserHelper(const CommandLine& arguments,
             << browser_wrapper;
   }
 
-  bool started = base::LaunchApp(command_line.argv(),
-                                 server_->fds_to_map(),
-                                 wait,
-                                 process);
+  base::file_handle_mapping_vector fds;
+  if (automation_proxy_.get())
+    fds = automation_proxy_->fds_to_map();
+
+  bool started = base::LaunchApp(command_line.argv(), fds, wait, process);
 #endif
 
   return started;
@@ -867,11 +885,11 @@ void UITest::TearDown() {
   PlatformTest::TearDown();
 }
 
-AutomationProxy* UITest::CreateAutomationProxy(int execution_timeout) {
+ProxyLauncher* UITest::CreateProxyLauncher() {
   // Make the AutomationProxy disconnect the channel on the first error,
   // so that we avoid spending a lot of time in timeouts. The browser is likely
   // hosed if we hit those errors.
-  return new AutomationProxy(execution_timeout, true);
+  return new AnonymousProxyLauncher(true);
 }
 
 static CommandLine* CreatePythonCommandLine() {
diff --git a/chrome/test/ui/ui_test.h b/chrome/test/ui/ui_test.h
index ced9f348f8990fa01ff268f7901286611f598787..3305cc5c3666d280431d6e981a118b0d6381d3dd 100644
--- a/chrome/test/ui/ui_test.h
+++ b/chrome/test/ui/ui_test.h
@@ -38,6 +38,7 @@ class BrowserProxy;
 class DictionaryValue;
 class FilePath;
 class GURL;
+class ProxyLauncher;
 class ScopedTempDir;
 class TabProxy;
 
@@ -68,14 +69,21 @@ class UITestBase {
  public:
   // ********* Utility functions *********
 
-  // Launches the browser and IPC testing server.
+  // Launches the browser only.
+  void LaunchBrowser();
+
+  // Launches the browser and IPC testing connection in server mode.
   void LaunchBrowserAndServer();
 
+  // Launches the IPC testing connection in client mode,
+  // which then attempts to connect to a browser.
+  void ConnectToRunningBrowser();
+
   // Only for pyauto.
   void set_command_execution_timeout_ms(int timeout);
 
-  // Overridable so that derived classes can provide their own AutomationProxy.
-  virtual AutomationProxy* CreateAutomationProxy(int execution_timeout);
+  // Overridable so that derived classes can provide their own ProxyLauncher.
+  virtual ProxyLauncher* CreateProxyLauncher();
 
   // Closes the browser and IPC testing server.
   void CloseBrowserAndServer();
@@ -102,7 +110,7 @@ class UITestBase {
   // Terminates the browser, simulates end of session.
   void TerminateBrowser();
 
-  // Tells the browser to navigato to the givne URL in the active tab
+  // Tells the browser to navigate to the given URL in the active tab
   // of the first app window.
   // Does not wait for the navigation to complete to return.
   void NavigateToURLAsync(const GURL& url);
@@ -361,8 +369,8 @@ class UITestBase {
 
  protected:
   AutomationProxy* automation() {
-    EXPECT_TRUE(server_.get());
-    return server_.get();
+    EXPECT_TRUE(automation_proxy_.get());
+    return automation_proxy_.get();
   }
 
   virtual bool ShouldFilterInet() {
@@ -412,6 +420,7 @@ class UITestBase {
                                         // id on the command line? Default is
                                         // true.
   bool enable_file_cookies_;            // Enable file cookies, default is true.
+  scoped_ptr<ProxyLauncher> launcher_;  // Launches browser and AutomationProxy.
   ProfileType profile_type_;            // Are we using a profile with a
                                         // complex theme?
   FilePath websocket_pid_file_;         // PID file for websocket server.
@@ -419,6 +428,8 @@ class UITestBase {
                                         // the browser. Used in ShutdownTest.
 
  private:
+  void WaitForBrowserLaunch();
+
   bool LaunchBrowserHelper(const CommandLine& arguments,
                            bool wait,
                            base::ProcessHandle* process);
@@ -450,7 +461,7 @@ class UITestBase {
   static std::string js_flags_;         // Flags passed to the JS engine.
   static std::string log_level_;        // Logging level.
 
-  scoped_ptr<AutomationProxy> server_;
+  scoped_ptr<AutomationProxy> automation_proxy_;
 
   std::string ui_test_name_;
 
@@ -468,7 +479,7 @@ class UITest : public UITestBase, public PlatformTest {
   virtual void SetUp();
   virtual void TearDown();
 
-  virtual AutomationProxy* CreateAutomationProxy(int execution_timeout);
+  virtual ProxyLauncher* CreateProxyLauncher();
 
   // Synchronously launches local http server normally used to run LayoutTests.
   void StartHttpServer(const FilePath& root_directory);
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp
index 75d028b9e4a307da2d5d9e9e249ca0c20f3ba3d9..0eb7ea4d36327cd9e5b846c55499e6eb2566221f 100644
--- a/chrome_frame/chrome_frame.gyp
+++ b/chrome_frame/chrome_frame.gyp
@@ -560,7 +560,10 @@
         'test/win_event_receiver.h',
         'chrome_tab.h',
         '../base/test/test_file_util_win.cc',
+        '../chrome/test/automation/proxy_launcher.cc',
+        '../chrome/test/automation/proxy_launcher.h',
         '../chrome/test/ui/ui_test.cc',
+        '../chrome/test/ui/ui_test.h',
         '../chrome/test/ui/ui_test_suite.cc',
         '../chrome/test/ui/ui_test_suite.h',
         '../chrome/test/chrome_process_util.cc',
diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc
index 8fb4d48709cbfd7f942ff638c668b3a7a7155182..e0d8ead839173eef44d166df0f554a18b4c6ec03 100644
--- a/chrome_frame/chrome_frame_automation.cc
+++ b/chrome_frame/chrome_frame_automation.cc
@@ -136,10 +136,13 @@ class ChromeFrameAutomationProxyImpl::CFMsgDispatcher
 };
 
 ChromeFrameAutomationProxyImpl::ChromeFrameAutomationProxyImpl(
-    AutomationProxyCacheEntry* entry, int launch_timeout)
+    AutomationProxyCacheEntry* entry,
+    std::string channel_id, int launch_timeout)
     : AutomationProxy(launch_timeout, false), proxy_entry_(entry) {
   TRACE_EVENT_BEGIN("chromeframe.automationproxy", this, "");
 
+  InitializeChannel(channel_id, false);
+
   sync_ = new CFMsgDispatcher();
   message_filter_ = new TabProxyNotificationMessageFilter(tracker_.get());
 
@@ -263,8 +266,10 @@ void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params,
   // destruction notification.
 
   // At same time we must destroy/stop the thread from another thread.
+  std::string channel_id = AutomationProxy::GenerateChannelID();
   ChromeFrameAutomationProxyImpl* proxy =
-      new ChromeFrameAutomationProxyImpl(this, params->launch_timeout());
+      new ChromeFrameAutomationProxyImpl(this, channel_id,
+                                         params->launch_timeout());
 
   // Ensure that the automation proxy actually respects our choice on whether
   // or not to check the version.
@@ -274,7 +279,7 @@ void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params,
   scoped_ptr<CommandLine> command_line(
       chrome_launcher::CreateLaunchCommandLine());
   command_line->AppendSwitchASCII(switches::kAutomationClientChannelID,
-                                  proxy->channel_id());
+                                  channel_id);
 
   // Run Chrome in Chrome Frame mode. In practice, this modifies the paths
   // and registry keys that Chrome looks in via the BrowserDistribution
diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h
index 895c9cab110c1e511c852234faac071bc5ab8939..7432ee268bd82b8cc0d6566f73760c0123005f6d 100644
--- a/chrome_frame/chrome_frame_automation.h
+++ b/chrome_frame/chrome_frame_automation.h
@@ -93,6 +93,7 @@ class ChromeFrameAutomationProxyImpl
  protected:
   friend class AutomationProxyCacheEntry;
   ChromeFrameAutomationProxyImpl(AutomationProxyCacheEntry* entry,
+                                 std::string channel_id,
                                  int launch_timeout);
 
   class CFMsgDispatcher;
diff --git a/chrome_frame/test/automation_client_mock.cc b/chrome_frame/test/automation_client_mock.cc
index 729ecbd0beab133a323a6fbad797094b942b4aa1..e4bdad045e59ffe533c6313150a4c012dd594c7a 100644
--- a/chrome_frame/test/automation_client_mock.cc
+++ b/chrome_frame/test/automation_client_mock.cc
@@ -320,7 +320,8 @@ class TestChromeFrameAutomationProxyImpl
  public:
   TestChromeFrameAutomationProxyImpl()
         // 1 is an unneeded timeout.
-      : ChromeFrameAutomationProxyImpl(NULL, 1) {
+      : ChromeFrameAutomationProxyImpl(
+          NULL, AutomationProxy::GenerateChannelID(), 1) {
   }
   MOCK_METHOD3(
       SendAsAsync,
@@ -476,4 +477,3 @@ TEST_F(CFACMockTest, NavigateTwiceAfterInitToSameUrl) {
   EXPECT_CALL(mock_proxy_, ReleaseTabProxy(testing::Eq(tab_handle_))).Times(1);
   client_->Uninitialize();
 }
-
diff --git a/chrome_frame/test/net/test_automation_provider.cc b/chrome_frame/test/net/test_automation_provider.cc
index 56251260d4984c4eded77cb77fd0f3e377f7a1c8..4b4d2a12992be9f83351cfabfafde26bab244523 100644
--- a/chrome_frame/test/net/test_automation_provider.cc
+++ b/chrome_frame/test/net/test_automation_provider.cc
@@ -119,7 +119,7 @@ TestAutomationProvider* TestAutomationProvider::NewAutomationProvider(
     Profile* p, const std::string& channel,
     TestAutomationProviderDelegate* delegate) {
   TestAutomationProvider* automation = new TestAutomationProvider(p, delegate);
-  automation->ConnectToChannel(channel);
+  automation->InitializeChannel(channel);
   automation->SetExpectedTabCount(1);
   return automation;
 }