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