automation_provider.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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_provider.h"
6
7#include <set>
8
9#include "app/message_box_flags.h"
10#include "base/callback.h"
11#include "base/file_path.h"
12#include "base/json/json_reader.h"
13#include "base/json/json_writer.h"
14#include "base/json/string_escape.h"
15#include "base/message_loop.h"
16#include "base/path_service.h"
17#include "base/process_util.h"
18#include "base/stl_util-inl.h"
19#include "base/string_util.h"
20#include "base/task.h"
21#include "base/thread.h"
22#include "base/trace_event.h"
23#include "base/string_number_conversions.h"
24#include "base/utf_string_conversions.h"
25#include "base/values.h"
26#include "base/waitable_event.h"
27#include "chrome/app/chrome_dll_resource.h"
28#include "chrome/browser/app_modal_dialog.h"
29#include "chrome/browser/app_modal_dialog_queue.h"
30#include "chrome/browser/autofill/autofill_manager.h"
31#include "chrome/browser/automation/automation_autocomplete_edit_tracker.h"
32#include "chrome/browser/automation/automation_browser_tracker.h"
33#include "chrome/browser/automation/automation_extension_tracker.h"
34#include "chrome/browser/automation/automation_provider_list.h"
35#include "chrome/browser/automation/automation_provider_observers.h"
36#include "chrome/browser/automation/automation_resource_message_filter.h"
37#include "chrome/browser/automation/automation_tab_tracker.h"
38#include "chrome/browser/automation/automation_window_tracker.h"
39#include "chrome/browser/automation/extension_port_container.h"
40#include "chrome/browser/autocomplete/autocomplete_edit.h"
41#include "chrome/browser/blocked_popup_container.h"
42#include "chrome/browser/bookmarks/bookmark_model.h"
43#include "chrome/browser/bookmarks/bookmark_storage.h"
44#include "chrome/browser/browser_list.h"
45#include "chrome/browser/browser_process.h"
46#include "chrome/browser/browser_window.h"
47#include "chrome/browser/browsing_data_remover.h"
48#include "chrome/browser/character_encoding.h"
49#include "chrome/browser/chrome_thread.h"
50#include "chrome/browser/dom_operation_notification_details.h"
51#include "chrome/browser/debugger/devtools_manager.h"
52#include "chrome/browser/download/download_item.h"
53#include "chrome/browser/download/download_shelf.h"
54#include "chrome/browser/download/save_package.h"
55#include "chrome/browser/extensions/crx_installer.h"
56#include "chrome/browser/extensions/extension_browser_event_router.h"
57#include "chrome/browser/extensions/extension_host.h"
58#include "chrome/browser/extensions/extension_install_ui.h"
59#include "chrome/browser/extensions/extension_message_service.h"
60#include "chrome/browser/extensions/extension_tabs_module.h"
61#include "chrome/browser/extensions/extension_toolbar_model.h"
62#include "chrome/browser/extensions/extensions_service.h"
63#include "chrome/browser/extensions/user_script_master.h"
64#include "chrome/browser/find_bar.h"
65#include "chrome/browser/find_bar_controller.h"
66#include "chrome/browser/find_notification_details.h"
67#include "chrome/browser/host_content_settings_map.h"
68#include "chrome/browser/importer/importer.h"
69#include "chrome/browser/importer/importer_data_types.h"
70#include "chrome/browser/io_thread.h"
71#include "chrome/browser/location_bar.h"
72#include "chrome/browser/login_prompt.h"
73#include "chrome/browser/net/url_request_mock_util.h"
74#include "chrome/browser/platform_util.h"
75#include "chrome/browser/prefs/pref_service.h"
76#include "chrome/browser/printing/print_job.h"
77#include "chrome/browser/profile_manager.h"
78#include "chrome/browser/renderer_host/render_process_host.h"
79#include "chrome/browser/renderer_host/render_view_host.h"
80#include "chrome/browser/ssl/ssl_manager.h"
81#include "chrome/browser/ssl/ssl_blocking_page.h"
82#include "chrome/browser/tab_contents/infobar_delegate.h"
83#include "chrome/browser/tab_contents/navigation_entry.h"
84#include "chrome/browser/tab_contents/tab_contents.h"
85#include "chrome/browser/tab_contents/tab_contents_view.h"
86#include "chrome/browser/translate/translate_infobar_delegate.h"
87#include "chrome/common/automation_constants.h"
88#include "chrome/common/chrome_constants.h"
89#include "chrome/common/chrome_paths.h"
90#include "chrome/common/chrome_switches.h"
91#include "chrome/common/chrome_version_info.h"
92#include "chrome/common/extensions/extension.h"
93#include "chrome/common/json_value_serializer.h"
94#include "chrome/common/net/url_request_context_getter.h"
95#include "chrome/common/notification_service.h"
96#include "chrome/common/pref_names.h"
97#include "chrome/common/url_constants.h"
98#include "chrome/test/automation/automation_messages.h"
99#include "chrome/test/automation/tab_proxy.h"
100#include "net/proxy/proxy_service.h"
101#include "net/proxy/proxy_config_service_fixed.h"
102#include "net/url_request/url_request_context.h"
103#include "chrome/browser/automation/ui_controls.h"
104#include "views/event.h"
105#include "webkit/glue/password_form.h"
106#include "webkit/glue/plugins/plugin_list.h"
107
108#if defined(OS_WIN)
109#include "chrome/browser/external_tab_container_win.h"
110#endif  // defined(OS_WIN)
111
112using base::Time;
113
114AutomationProvider::AutomationProvider(Profile* profile)
115    : profile_(profile),
116      reply_message_(NULL) {
117  TRACE_EVENT_BEGIN("AutomationProvider::AutomationProvider", 0, "");
118
119  browser_tracker_.reset(new AutomationBrowserTracker(this));
120  extension_tracker_.reset(new AutomationExtensionTracker(this));
121  tab_tracker_.reset(new AutomationTabTracker(this));
122  window_tracker_.reset(new AutomationWindowTracker(this));
123  autocomplete_edit_tracker_.reset(
124      new AutomationAutocompleteEditTracker(this));
125  new_tab_ui_load_observer_.reset(new NewTabUILoadObserver(this));
126  dom_operation_observer_.reset(new DomOperationNotificationObserver(this));
127  metric_event_duration_observer_.reset(new MetricEventDurationObserver());
128  extension_test_result_observer_.reset(
129      new ExtensionTestResultNotificationObserver(this));
130  g_browser_process->AddRefModule();
131
132  TRACE_EVENT_END("AutomationProvider::AutomationProvider", 0, "");
133}
134
135AutomationProvider::~AutomationProvider() {
136  STLDeleteContainerPairSecondPointers(port_containers_.begin(),
137                                       port_containers_.end());
138  port_containers_.clear();
139
140  // Make sure that any outstanding NotificationObservers also get destroyed.
141  ObserverList<NotificationObserver>::Iterator it(notification_observer_list_);
142  NotificationObserver* observer;
143  while ((observer = it.GetNext()) != NULL)
144    delete observer;
145
146  if (channel_.get()) {
147    channel_->Close();
148  }
149  g_browser_process->ReleaseModule();
150}
151
152void AutomationProvider::ConnectToChannel(const std::string& channel_id) {
153  TRACE_EVENT_BEGIN("AutomationProvider::ConnectToChannel", 0, "");
154
155  automation_resource_message_filter_ = new AutomationResourceMessageFilter;
156  channel_.reset(
157      new IPC::SyncChannel(channel_id, IPC::Channel::MODE_CLIENT, this,
158                           automation_resource_message_filter_,
159                           g_browser_process->io_thread()->message_loop(),
160                           true, g_browser_process->shutdown_event()));
161  chrome::VersionInfo version_info;
162
163  // Send a hello message with our current automation protocol version.
164  channel_->Send(new AutomationMsg_Hello(0, version_info.Version().c_str()));
165
166  TRACE_EVENT_END("AutomationProvider::ConnectToChannel", 0, "");
167}
168
169void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) {
170  if (expected_tabs == 0) {
171    Send(new AutomationMsg_InitialLoadsComplete(0));
172  } else {
173    initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this));
174  }
175}
176
177NotificationObserver* AutomationProvider::AddNavigationStatusListener(
178    NavigationController* tab, IPC::Message* reply_message,
179    int number_of_navigations, bool include_current_navigation) {
180  NotificationObserver* observer =
181      new NavigationNotificationObserver(tab, this, reply_message,
182                                         number_of_navigations,
183                                         include_current_navigation);
184
185  notification_observer_list_.AddObserver(observer);
186  return observer;
187}
188
189void AutomationProvider::RemoveNavigationStatusListener(
190    NotificationObserver* obs) {
191  notification_observer_list_.RemoveObserver(obs);
192}
193
194NotificationObserver* AutomationProvider::AddTabStripObserver(
195    Browser* parent,
196    IPC::Message* reply_message) {
197  NotificationObserver* observer =
198      new TabAppendedNotificationObserver(parent, this, reply_message);
199  notification_observer_list_.AddObserver(observer);
200
201  return observer;
202}
203
204void AutomationProvider::RemoveTabStripObserver(NotificationObserver* obs) {
205  notification_observer_list_.RemoveObserver(obs);
206}
207
208void AutomationProvider::AddLoginHandler(NavigationController* tab,
209                                         LoginHandler* handler) {
210  login_handler_map_[tab] = handler;
211}
212
213void AutomationProvider::RemoveLoginHandler(NavigationController* tab) {
214  DCHECK(login_handler_map_[tab]);
215  login_handler_map_.erase(tab);
216}
217
218void AutomationProvider::AddPortContainer(ExtensionPortContainer* port) {
219  int port_id = port->port_id();
220  DCHECK_NE(-1, port_id);
221  DCHECK(port_containers_.find(port_id) == port_containers_.end());
222
223  port_containers_[port_id] = port;
224}
225
226void AutomationProvider::RemovePortContainer(ExtensionPortContainer* port) {
227  int port_id = port->port_id();
228  DCHECK_NE(-1, port_id);
229
230  PortContainerMap::iterator it = port_containers_.find(port_id);
231  DCHECK(it != port_containers_.end());
232
233  if (it != port_containers_.end()) {
234    delete it->second;
235    port_containers_.erase(it);
236  }
237}
238
239ExtensionPortContainer* AutomationProvider::GetPortContainer(
240    int port_id) const {
241  PortContainerMap::const_iterator it = port_containers_.find(port_id);
242  if (it == port_containers_.end())
243    return NULL;
244
245  return it->second;
246}
247
248int AutomationProvider::GetIndexForNavigationController(
249    const NavigationController* controller, const Browser* parent) const {
250  DCHECK(parent);
251  return parent->GetIndexOfController(controller);
252}
253
254int AutomationProvider::AddExtension(Extension* extension) {
255  DCHECK(extension);
256  return extension_tracker_->Add(extension);
257}
258
259// TODO(phajdan.jr): move to TestingAutomationProvider.
260DictionaryValue* AutomationProvider::GetDictionaryFromDownloadItem(
261    const DownloadItem* download) {
262  std::map<DownloadItem::DownloadState, std::string> state_to_string;
263  state_to_string[DownloadItem::IN_PROGRESS] = std::string("IN_PROGRESS");
264  state_to_string[DownloadItem::CANCELLED] = std::string("CANCELLED");
265  state_to_string[DownloadItem::REMOVING] = std::string("REMOVING");
266  state_to_string[DownloadItem::COMPLETE] = std::string("COMPLETE");
267
268  std::map<DownloadItem::SafetyState, std::string> safety_state_to_string;
269  safety_state_to_string[DownloadItem::SAFE] = std::string("SAFE");
270  safety_state_to_string[DownloadItem::DANGEROUS] = std::string("DANGEROUS");
271  safety_state_to_string[DownloadItem::DANGEROUS_BUT_VALIDATED] =
272      std::string("DANGEROUS_BUT_VALIDATED");
273
274  DictionaryValue* dl_item_value = new DictionaryValue;
275  dl_item_value->SetInteger("id", static_cast<int>(download->id()));
276  dl_item_value->SetString("url", download->url().spec());
277  dl_item_value->SetString("referrer_url", download->referrer_url().spec());
278  dl_item_value->SetString("file_name", download->GetFileName().value());
279  dl_item_value->SetString("full_path", download->full_path().value());
280  dl_item_value->SetBoolean("is_paused", download->is_paused());
281  dl_item_value->SetBoolean("open_when_complete",
282                            download->open_when_complete());
283  dl_item_value->SetBoolean("is_extension_install",
284                            download->is_extension_install());
285  dl_item_value->SetBoolean("is_temporary", download->is_temporary());
286  dl_item_value->SetBoolean("is_otr", download->is_otr());  // off-the-record
287  dl_item_value->SetString("state", state_to_string[download->state()]);
288  dl_item_value->SetString("safety_state",
289                           safety_state_to_string[download->safety_state()]);
290  dl_item_value->SetInteger("PercentComplete", download->PercentComplete());
291
292  return dl_item_value;
293}
294
295Extension* AutomationProvider::GetExtension(int extension_handle) {
296  return extension_tracker_->GetResource(extension_handle);
297}
298
299Extension* AutomationProvider::GetEnabledExtension(int extension_handle) {
300  Extension* extension = extension_tracker_->GetResource(extension_handle);
301  ExtensionsService* service = profile_->GetExtensionsService();
302  if (extension && service &&
303      service->GetExtensionById(extension->id(), false))
304    return extension;
305  return NULL;
306}
307
308Extension* AutomationProvider::GetDisabledExtension(int extension_handle) {
309  Extension* extension = extension_tracker_->GetResource(extension_handle);
310  ExtensionsService* service = profile_->GetExtensionsService();
311  if (extension && service &&
312      service->GetExtensionById(extension->id(), true) &&
313      !service->GetExtensionById(extension->id(), false))
314    return extension;
315  return NULL;
316}
317
318void AutomationProvider::OnMessageReceived(const IPC::Message& message) {
319  IPC_BEGIN_MESSAGE_MAP(AutomationProvider, message)
320#if !defined(OS_MACOSX)
321    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WindowDrag,
322                                    WindowSimulateDrag)
323#endif  // !defined(OS_MACOSX)
324#if defined(OS_WIN)
325    IPC_MESSAGE_HANDLER(AutomationMsg_TabHWND, GetTabHWND)
326#endif  // defined(OS_WIN)
327    IPC_MESSAGE_HANDLER(AutomationMsg_HandleUnused, HandleUnused)
328    IPC_MESSAGE_HANDLER(AutomationMsg_SetProxyConfig, SetProxyConfig);
329    IPC_MESSAGE_HANDLER(AutomationMsg_PrintAsync, PrintAsync)
330    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Find, HandleFindRequest)
331    IPC_MESSAGE_HANDLER(AutomationMsg_OverrideEncoding, OverrideEncoding)
332    IPC_MESSAGE_HANDLER(AutomationMsg_SelectAll, SelectAll)
333    IPC_MESSAGE_HANDLER(AutomationMsg_Cut, Cut)
334    IPC_MESSAGE_HANDLER(AutomationMsg_Copy, Copy)
335    IPC_MESSAGE_HANDLER(AutomationMsg_Paste, Paste)
336    IPC_MESSAGE_HANDLER(AutomationMsg_ReloadAsync, ReloadAsync)
337    IPC_MESSAGE_HANDLER(AutomationMsg_StopAsync, StopAsync)
338    IPC_MESSAGE_HANDLER(AutomationMsg_SetPageFontSize, OnSetPageFontSize)
339    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_InstallExtension,
340                                    InstallExtension)
341    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_LoadExpandedExtension,
342                                    LoadExpandedExtension)
343    IPC_MESSAGE_HANDLER(AutomationMsg_GetEnabledExtensions,
344                        GetEnabledExtensions)
345    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForExtensionTestResult,
346                                    WaitForExtensionTestResult)
347    IPC_MESSAGE_HANDLER_DELAY_REPLY(
348        AutomationMsg_InstallExtensionAndGetHandle,
349        InstallExtensionAndGetHandle)
350    IPC_MESSAGE_HANDLER(AutomationMsg_UninstallExtension,
351                        UninstallExtension)
352    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_EnableExtension,
353                                    EnableExtension)
354    IPC_MESSAGE_HANDLER(AutomationMsg_DisableExtension,
355                        DisableExtension)
356    IPC_MESSAGE_HANDLER_DELAY_REPLY(
357        AutomationMsg_ExecuteExtensionActionInActiveTabAsync,
358        ExecuteExtensionActionInActiveTabAsync)
359    IPC_MESSAGE_HANDLER(AutomationMsg_MoveExtensionBrowserAction,
360                        MoveExtensionBrowserAction)
361    IPC_MESSAGE_HANDLER(AutomationMsg_GetExtensionProperty,
362                        GetExtensionProperty)
363    IPC_MESSAGE_HANDLER(AutomationMsg_SaveAsAsync, SaveAsAsync)
364    IPC_MESSAGE_HANDLER(AutomationMsg_RemoveBrowsingData, RemoveBrowsingData)
365#if defined(OS_WIN)
366    // These are for use with external tabs.
367    IPC_MESSAGE_HANDLER(AutomationMsg_CreateExternalTab, CreateExternalTab)
368    IPC_MESSAGE_HANDLER(AutomationMsg_ProcessUnhandledAccelerator,
369                        ProcessUnhandledAccelerator)
370    IPC_MESSAGE_HANDLER(AutomationMsg_SetInitialFocus, SetInitialFocus)
371    IPC_MESSAGE_HANDLER(AutomationMsg_TabReposition, OnTabReposition)
372    IPC_MESSAGE_HANDLER(AutomationMsg_ForwardContextMenuCommandToChrome,
373                        OnForwardContextMenuCommandToChrome)
374    IPC_MESSAGE_HANDLER(AutomationMsg_NavigateInExternalTab,
375                        NavigateInExternalTab)
376    IPC_MESSAGE_HANDLER(AutomationMsg_NavigateExternalTabAtIndex,
377                        NavigateExternalTabAtIndex)
378    IPC_MESSAGE_HANDLER(AutomationMsg_ConnectExternalTab, ConnectExternalTab)
379    IPC_MESSAGE_HANDLER(AutomationMsg_SetEnableExtensionAutomation,
380                        SetEnableExtensionAutomation)
381    IPC_MESSAGE_HANDLER(AutomationMsg_HandleMessageFromExternalHost,
382                        OnMessageFromExternalHost)
383    IPC_MESSAGE_HANDLER(AutomationMsg_BrowserMove, OnBrowserMoved)
384    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_RunUnloadHandlers,
385                                    OnRunUnloadHandlers)
386    IPC_MESSAGE_HANDLER(AutomationMsg_SetZoomLevel, OnSetZoomLevel)
387#endif  // defined(OS_WIN)
388#if defined(OS_CHROMEOS)
389    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_LoginWithUserAndPass,
390                                    LoginWithUserAndPass)
391#endif  // defined(OS_CHROMEOS)
392    IPC_MESSAGE_UNHANDLED(OnUnhandledMessage())
393  IPC_END_MESSAGE_MAP()
394}
395
396void AutomationProvider::OnUnhandledMessage() {
397  // We should not hang here. Print a message to indicate what's going on,
398  // and disconnect the channel to notify the caller about the error
399  // in a way it can't ignore, and make any further attempts to send
400  // messages fail fast.
401  LOG(ERROR) << "AutomationProvider received a message it can't handle. "
402             << "Please make sure that you use switches::kTestingChannelID "
403             << "for test code (TestingAutomationProvider), and "
404             << "switches::kAutomationClientChannelID for everything else "
405             << "(like ChromeFrame). Closing the automation channel.";
406  channel_->Close();
407}
408
409// This task just adds another task to the event queue.  This is useful if
410// you want to ensure that any tasks added to the event queue after this one
411// have already been processed by the time |task| is run.
412class InvokeTaskLaterTask : public Task {
413 public:
414  explicit InvokeTaskLaterTask(Task* task) : task_(task) {}
415  virtual ~InvokeTaskLaterTask() {}
416
417  virtual void Run() {
418    MessageLoop::current()->PostTask(FROM_HERE, task_);
419  }
420
421 private:
422  Task* task_;
423
424  DISALLOW_COPY_AND_ASSIGN(InvokeTaskLaterTask);
425};
426
427void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) {
428  if (window_tracker_->ContainsHandle(handle)) {
429    window_tracker_->Remove(window_tracker_->GetResource(handle));
430  }
431}
432
433void AutomationProvider::OnChannelError() {
434  LOG(INFO) << "AutomationProxy went away, shutting down app.";
435  AutomationProviderList::GetInstance()->RemoveProvider(this);
436}
437
438bool AutomationProvider::Send(IPC::Message* msg) {
439  DCHECK(channel_.get());
440  return channel_->Send(msg);
441}
442
443Browser* AutomationProvider::FindAndActivateTab(
444    NavigationController* controller) {
445  int tab_index;
446  Browser* browser = Browser::GetBrowserForController(controller, &tab_index);
447  if (browser)
448    browser->SelectTabContentsAt(tab_index, true);
449
450  return browser;
451}
452
453void AutomationProvider::HandleFindRequest(
454    int handle,
455    const AutomationMsg_Find_Params& params,
456    IPC::Message* reply_message) {
457  if (!tab_tracker_->ContainsHandle(handle)) {
458    AutomationMsg_FindInPage::WriteReplyParams(reply_message, -1, -1);
459    Send(reply_message);
460    return;
461  }
462
463  NavigationController* nav = tab_tracker_->GetResource(handle);
464  TabContents* tab_contents = nav->tab_contents();
465
466  find_in_page_observer_.reset(new
467      FindInPageNotificationObserver(this, tab_contents, reply_message));
468
469  tab_contents->set_current_find_request_id(
470      FindInPageNotificationObserver::kFindInPageRequestId);
471  tab_contents->render_view_host()->StartFinding(
472      FindInPageNotificationObserver::kFindInPageRequestId,
473      params.search_string, params.forward, params.match_case,
474      params.find_next);
475}
476
477class SetProxyConfigTask : public Task {
478 public:
479  SetProxyConfigTask(URLRequestContextGetter* request_context_getter,
480                     const std::string& new_proxy_config)
481      : request_context_getter_(request_context_getter),
482        proxy_config_(new_proxy_config) {}
483  virtual void Run() {
484    // First, deserialize the JSON string. If this fails, log and bail.
485    JSONStringValueSerializer deserializer(proxy_config_);
486    std::string error_msg;
487    scoped_ptr<Value> root(deserializer.Deserialize(NULL, &error_msg));
488    if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) {
489      DLOG(WARNING) << "Received bad JSON string for ProxyConfig: "
490                    << error_msg;
491      return;
492    }
493
494    scoped_ptr<DictionaryValue> dict(
495        static_cast<DictionaryValue*>(root.release()));
496    // Now put together a proxy configuration from the deserialized string.
497    net::ProxyConfig pc;
498    PopulateProxyConfig(*dict.get(), &pc);
499
500    net::ProxyService* proxy_service =
501        request_context_getter_->GetURLRequestContext()->proxy_service();
502    DCHECK(proxy_service);
503    scoped_ptr<net::ProxyConfigService> proxy_config_service(
504        new net::ProxyConfigServiceFixed(pc));
505    proxy_service->ResetConfigService(proxy_config_service.release());
506  }
507
508  void PopulateProxyConfig(const DictionaryValue& dict, net::ProxyConfig* pc) {
509    DCHECK(pc);
510    bool no_proxy = false;
511    if (dict.GetBoolean(automation::kJSONProxyNoProxy, &no_proxy)) {
512      // Make no changes to the ProxyConfig.
513      return;
514    }
515    bool auto_config;
516    if (dict.GetBoolean(automation::kJSONProxyAutoconfig, &auto_config)) {
517      pc->set_auto_detect(true);
518    }
519    std::string pac_url;
520    if (dict.GetString(automation::kJSONProxyPacUrl, &pac_url)) {
521      pc->set_pac_url(GURL(pac_url));
522    }
523    std::string proxy_bypass_list;
524    if (dict.GetString(automation::kJSONProxyBypassList, &proxy_bypass_list)) {
525      pc->proxy_rules().bypass_rules.ParseFromString(proxy_bypass_list);
526    }
527    std::string proxy_server;
528    if (dict.GetString(automation::kJSONProxyServer, &proxy_server)) {
529      pc->proxy_rules().ParseFromString(proxy_server);
530    }
531  }
532
533 private:
534  scoped_refptr<URLRequestContextGetter> request_context_getter_;
535  std::string proxy_config_;
536};
537
538
539void AutomationProvider::SetProxyConfig(const std::string& new_proxy_config) {
540  URLRequestContextGetter* context_getter = Profile::GetDefaultRequestContext();
541  if (!context_getter) {
542    FilePath user_data_dir;
543    PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
544    ProfileManager* profile_manager = g_browser_process->profile_manager();
545    DCHECK(profile_manager);
546    Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
547    DCHECK(profile);
548    context_getter = profile->GetRequestContext();
549  }
550  DCHECK(context_getter);
551
552  ChromeThread::PostTask(
553      ChromeThread::IO, FROM_HERE,
554      new SetProxyConfigTask(context_getter, new_proxy_config));
555}
556
557TabContents* AutomationProvider::GetTabContentsForHandle(
558    int handle, NavigationController** tab) {
559  if (tab_tracker_->ContainsHandle(handle)) {
560    NavigationController* nav_controller = tab_tracker_->GetResource(handle);
561    if (tab)
562      *tab = nav_controller;
563    return nav_controller->tab_contents();
564  }
565  return NULL;
566}
567
568// Gets the current used encoding name of the page in the specified tab.
569void AutomationProvider::OverrideEncoding(int tab_handle,
570                                          const std::string& encoding_name,
571                                          bool* success) {
572  *success = false;
573  if (tab_tracker_->ContainsHandle(tab_handle)) {
574    NavigationController* nav = tab_tracker_->GetResource(tab_handle);
575    if (!nav)
576      return;
577    Browser* browser = FindAndActivateTab(nav);
578
579    // If the browser has UI, simulate what a user would do.
580    // Activate the tab and then click the encoding menu.
581    if (browser &&
582        browser->command_updater()->IsCommandEnabled(IDC_ENCODING_MENU)) {
583      int selected_encoding_id =
584          CharacterEncoding::GetCommandIdByCanonicalEncodingName(encoding_name);
585      if (selected_encoding_id) {
586        browser->OverrideEncoding(selected_encoding_id);
587        *success = true;
588      }
589    } else {
590      // There is no UI, Chrome probably runs as Chrome-Frame mode.
591      // Try to get TabContents and call its override_encoding method.
592      TabContents* contents = nav->tab_contents();
593      if (!contents)
594        return;
595      const std::string selected_encoding =
596          CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding_name);
597      if (selected_encoding.empty())
598        return;
599      contents->SetOverrideEncoding(selected_encoding);
600    }
601  }
602}
603
604void AutomationProvider::SelectAll(int tab_handle) {
605  RenderViewHost* view = GetViewForTab(tab_handle);
606  if (!view) {
607    NOTREACHED();
608    return;
609  }
610
611  view->SelectAll();
612}
613
614void AutomationProvider::Cut(int tab_handle) {
615  RenderViewHost* view = GetViewForTab(tab_handle);
616  if (!view) {
617    NOTREACHED();
618    return;
619  }
620
621  view->Cut();
622}
623
624void AutomationProvider::Copy(int tab_handle) {
625  RenderViewHost* view = GetViewForTab(tab_handle);
626  if (!view) {
627    NOTREACHED();
628    return;
629  }
630
631  view->Copy();
632}
633
634void AutomationProvider::Paste(int tab_handle) {
635  RenderViewHost* view = GetViewForTab(tab_handle);
636  if (!view) {
637    NOTREACHED();
638    return;
639  }
640
641  view->Paste();
642}
643
644void AutomationProvider::ReloadAsync(int tab_handle) {
645  if (tab_tracker_->ContainsHandle(tab_handle)) {
646    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
647    if (!tab) {
648      NOTREACHED();
649      return;
650    }
651
652    const bool check_for_repost = true;
653    tab->Reload(check_for_repost);
654  }
655}
656
657void AutomationProvider::StopAsync(int tab_handle) {
658  RenderViewHost* view = GetViewForTab(tab_handle);
659  if (!view) {
660    // We tolerate StopAsync being called even before a view has been created.
661    // So just log a warning instead of a NOTREACHED().
662    DLOG(WARNING) << "StopAsync: no view for handle " << tab_handle;
663    return;
664  }
665
666  view->Stop();
667}
668
669void AutomationProvider::OnSetPageFontSize(int tab_handle,
670                                           int font_size) {
671  AutomationPageFontSize automation_font_size =
672      static_cast<AutomationPageFontSize>(font_size);
673
674  if (automation_font_size < SMALLEST_FONT ||
675      automation_font_size > LARGEST_FONT) {
676      DLOG(ERROR) << "Invalid font size specified : "
677                  << font_size;
678      return;
679  }
680
681  if (tab_tracker_->ContainsHandle(tab_handle)) {
682    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
683    DCHECK(tab != NULL);
684    if (tab && tab->tab_contents()) {
685      DCHECK(tab->tab_contents()->profile() != NULL);
686      tab->tab_contents()->profile()->GetPrefs()->SetInteger(
687          prefs::kWebKitDefaultFontSize, font_size);
688    }
689  }
690}
691
692void AutomationProvider::RemoveBrowsingData(int remove_mask) {
693  BrowsingDataRemover* remover;
694  remover = new BrowsingDataRemover(profile(),
695      BrowsingDataRemover::EVERYTHING,  // All time periods.
696      base::Time());
697  remover->Remove(remove_mask);
698  // BrowsingDataRemover deletes itself.
699}
700
701RenderViewHost* AutomationProvider::GetViewForTab(int tab_handle) {
702  if (tab_tracker_->ContainsHandle(tab_handle)) {
703    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
704    if (!tab) {
705      NOTREACHED();
706      return NULL;
707    }
708
709    TabContents* tab_contents = tab->tab_contents();
710    if (!tab_contents) {
711      NOTREACHED();
712      return NULL;
713    }
714
715    RenderViewHost* view_host = tab_contents->render_view_host();
716    return view_host;
717  }
718
719  return NULL;
720}
721
722void AutomationProvider::InstallExtension(const FilePath& crx_path,
723                                          IPC::Message* reply_message) {
724  ExtensionsService* service = profile_->GetExtensionsService();
725  if (service) {
726    // The observer will delete itself when done.
727    new ExtensionInstallNotificationObserver(this,
728                                             AutomationMsg_InstallExtension::ID,
729                                             reply_message);
730
731    const FilePath& install_dir = service->install_directory();
732    scoped_refptr<CrxInstaller> installer(
733        new CrxInstaller(install_dir,
734                         service,
735                         NULL));  // silent install, no UI
736    installer->set_allow_privilege_increase(true);
737    installer->InstallCrx(crx_path);
738  } else {
739    AutomationMsg_InstallExtension::WriteReplyParams(
740        reply_message, AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
741    Send(reply_message);
742  }
743}
744
745void AutomationProvider::LoadExpandedExtension(
746    const FilePath& extension_dir,
747    IPC::Message* reply_message) {
748  if (profile_->GetExtensionsService()) {
749    // The observer will delete itself when done.
750    new ExtensionInstallNotificationObserver(
751        this,
752        AutomationMsg_LoadExpandedExtension::ID,
753        reply_message);
754
755    profile_->GetExtensionsService()->LoadExtension(extension_dir);
756  } else {
757    AutomationMsg_LoadExpandedExtension::WriteReplyParams(
758        reply_message, AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
759    Send(reply_message);
760  }
761}
762
763void AutomationProvider::GetEnabledExtensions(
764    std::vector<FilePath>* result) {
765  ExtensionsService* service = profile_->GetExtensionsService();
766  DCHECK(service);
767  if (service->extensions_enabled()) {
768    const ExtensionList* extensions = service->extensions();
769    DCHECK(extensions);
770    for (size_t i = 0; i < extensions->size(); ++i) {
771      Extension* extension = (*extensions)[i];
772      DCHECK(extension);
773      if (extension->location() == Extension::INTERNAL ||
774          extension->location() == Extension::LOAD) {
775        result->push_back(extension->path());
776      }
777    }
778  }
779}
780
781void AutomationProvider::WaitForExtensionTestResult(
782    IPC::Message* reply_message) {
783  DCHECK(reply_message_ == NULL);
784  reply_message_ = reply_message;
785  // Call MaybeSendResult, because the result might have come in before
786  // we were waiting on it.
787  extension_test_result_observer_->MaybeSendResult();
788}
789
790void AutomationProvider::InstallExtensionAndGetHandle(
791    const FilePath& crx_path, bool with_ui, IPC::Message* reply_message) {
792  ExtensionsService* service = profile_->GetExtensionsService();
793  ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
794  if (service && manager) {
795    // The observer will delete itself when done.
796    new ExtensionReadyNotificationObserver(
797        manager,
798        this,
799        AutomationMsg_InstallExtensionAndGetHandle::ID,
800        reply_message);
801
802    ExtensionInstallUI* client =
803        (with_ui ? new ExtensionInstallUI(profile_) : NULL);
804    scoped_refptr<CrxInstaller> installer(
805        new CrxInstaller(service->install_directory(),
806                         service,
807                         client));
808    installer->set_allow_privilege_increase(true);
809    installer->InstallCrx(crx_path);
810  } else {
811    AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams(
812        reply_message, 0);
813    Send(reply_message);
814  }
815}
816
817void AutomationProvider::UninstallExtension(int extension_handle,
818                                            bool* success) {
819  *success = false;
820  Extension* extension = GetExtension(extension_handle);
821  ExtensionsService* service = profile_->GetExtensionsService();
822  if (extension && service) {
823    ExtensionUnloadNotificationObserver observer;
824    service->UninstallExtension(extension->id(), false);
825    // The extension unload notification should have been sent synchronously
826    // with the uninstall. Just to be safe, check that it was received.
827    *success = observer.did_receive_unload_notification();
828  }
829}
830
831void AutomationProvider::EnableExtension(int extension_handle,
832                                         IPC::Message* reply_message) {
833  Extension* extension = GetDisabledExtension(extension_handle);
834  ExtensionsService* service = profile_->GetExtensionsService();
835  ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
836  // Only enable if this extension is disabled.
837  if (extension && service && manager) {
838    // The observer will delete itself when done.
839    new ExtensionReadyNotificationObserver(
840        manager,
841        this,
842        AutomationMsg_EnableExtension::ID,
843        reply_message);
844    service->EnableExtension(extension->id());
845  } else {
846    AutomationMsg_EnableExtension::WriteReplyParams(reply_message, false);
847    Send(reply_message);
848  }
849}
850
851void AutomationProvider::DisableExtension(int extension_handle,
852                                          bool* success) {
853  *success = false;
854  Extension* extension = GetEnabledExtension(extension_handle);
855  ExtensionsService* service = profile_->GetExtensionsService();
856  if (extension && service) {
857    ExtensionUnloadNotificationObserver observer;
858    service->DisableExtension(extension->id());
859    // The extension unload notification should have been sent synchronously
860    // with the disable. Just to be safe, check that it was received.
861    *success = observer.did_receive_unload_notification();
862  }
863}
864
865void AutomationProvider::ExecuteExtensionActionInActiveTabAsync(
866    int extension_handle, int browser_handle,
867    IPC::Message* reply_message) {
868  bool success = false;
869  Extension* extension = GetEnabledExtension(extension_handle);
870  ExtensionsService* service = profile_->GetExtensionsService();
871  ExtensionMessageService* message_service =
872      profile_->GetExtensionMessageService();
873  Browser* browser = browser_tracker_->GetResource(browser_handle);
874  if (extension && service && message_service && browser) {
875    int tab_id = ExtensionTabUtil::GetTabId(browser->GetSelectedTabContents());
876    if (extension->page_action()) {
877      ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted(
878          browser->profile(), extension->id(), "action", tab_id, "", 1);
879      success = true;
880    } else if (extension->browser_action()) {
881      ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
882          browser->profile(), extension->id(), browser);
883      success = true;
884    }
885  }
886  AutomationMsg_ExecuteExtensionActionInActiveTabAsync::WriteReplyParams(
887      reply_message, success);
888  Send(reply_message);
889}
890
891void AutomationProvider::MoveExtensionBrowserAction(
892    int extension_handle, int index, bool* success) {
893  *success = false;
894  Extension* extension = GetEnabledExtension(extension_handle);
895  ExtensionsService* service = profile_->GetExtensionsService();
896  if (extension && service) {
897    ExtensionToolbarModel* toolbar = service->toolbar_model();
898    if (toolbar) {
899      if (index >= 0 && index < static_cast<int>(toolbar->size())) {
900        toolbar->MoveBrowserAction(extension, index);
901        *success = true;
902      } else {
903        DLOG(WARNING) << "Attempted to move browser action to invalid index.";
904      }
905    }
906  }
907}
908
909void AutomationProvider::GetExtensionProperty(
910    int extension_handle,
911    AutomationMsg_ExtensionProperty type,
912    bool* success,
913    std::string* value) {
914  *success = false;
915  Extension* extension = GetExtension(extension_handle);
916  ExtensionsService* service = profile_->GetExtensionsService();
917  if (extension && service) {
918    ExtensionToolbarModel* toolbar = service->toolbar_model();
919    int found_index = -1;
920    int index = 0;
921    switch (type) {
922      case AUTOMATION_MSG_EXTENSION_ID:
923        *value = extension->id();
924        *success = true;
925        break;
926      case AUTOMATION_MSG_EXTENSION_NAME:
927        *value = extension->name();
928        *success = true;
929        break;
930      case AUTOMATION_MSG_EXTENSION_VERSION:
931        *value = extension->VersionString();
932        *success = true;
933        break;
934      case AUTOMATION_MSG_EXTENSION_BROWSER_ACTION_INDEX:
935        if (toolbar) {
936          for (ExtensionList::const_iterator iter = toolbar->begin();
937               iter != toolbar->end(); iter++) {
938            // Skip this extension if we are in incognito mode
939            // and it is not incognito-enabled.
940            if (profile_->IsOffTheRecord() &&
941                !service->IsIncognitoEnabled(*iter))
942              continue;
943            if (*iter == extension) {
944              found_index = index;
945              break;
946            }
947            index++;
948          }
949          *value = base::IntToString(found_index);
950          *success = true;
951        }
952        break;
953      default:
954        LOG(WARNING) << "Trying to get undefined extension property";
955        break;
956    }
957  }
958}
959
960void AutomationProvider::SaveAsAsync(int tab_handle) {
961  NavigationController* tab = NULL;
962  TabContents* tab_contents = GetTabContentsForHandle(tab_handle, &tab);
963  if (tab_contents)
964    tab_contents->OnSavePage();
965}
966