1// Copyright (c) 2011 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 "base/callback.h"
10#include "base/debug/trace_event.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_number_conversions.h"
20#include "base/string_util.h"
21#include "base/synchronization/waitable_event.h"
22#include "base/task.h"
23#include "base/threading/thread.h"
24#include "base/utf_string_conversions.h"
25#include "base/values.h"
26#include "chrome/app/chrome_command_ids.h"
27#include "chrome/browser/autocomplete/autocomplete_edit.h"
28#include "chrome/browser/autofill/autofill_manager.h"
29#include "chrome/browser/automation/automation_autocomplete_edit_tracker.h"
30#include "chrome/browser/automation/automation_browser_tracker.h"
31#include "chrome/browser/automation/automation_extension_tracker.h"
32#include "chrome/browser/automation/automation_provider_list.h"
33#include "chrome/browser/automation/automation_provider_observers.h"
34#include "chrome/browser/automation/automation_resource_message_filter.h"
35#include "chrome/browser/automation/automation_tab_tracker.h"
36#include "chrome/browser/automation/automation_window_tracker.h"
37#include "chrome/browser/automation/ui_controls.h"
38#include "chrome/browser/blocked_content_container.h"
39#include "chrome/browser/bookmarks/bookmark_model.h"
40#include "chrome/browser/bookmarks/bookmark_storage.h"
41#include "chrome/browser/browser_process.h"
42#include "chrome/browser/browsing_data_remover.h"
43#include "chrome/browser/character_encoding.h"
44#include "chrome/browser/content_settings/host_content_settings_map.h"
45#include "chrome/browser/debugger/devtools_manager.h"
46#include "chrome/browser/dom_operation_notification_details.h"
47#include "chrome/browser/download/download_item.h"
48#include "chrome/browser/download/download_shelf.h"
49#include "chrome/browser/download/save_package.h"
50#include "chrome/browser/extensions/crx_installer.h"
51#include "chrome/browser/extensions/extension_browser_event_router.h"
52#include "chrome/browser/extensions/extension_host.h"
53#include "chrome/browser/extensions/extension_install_ui.h"
54#include "chrome/browser/extensions/extension_message_service.h"
55#include "chrome/browser/extensions/extension_service.h"
56#include "chrome/browser/extensions/extension_tabs_module.h"
57#include "chrome/browser/extensions/extension_toolbar_model.h"
58#include "chrome/browser/extensions/user_script_master.h"
59#include "chrome/browser/io_thread.h"
60#include "chrome/browser/net/url_request_mock_util.h"
61#include "chrome/browser/platform_util.h"
62#include "chrome/browser/prefs/pref_service.h"
63#include "chrome/browser/printing/print_job.h"
64#include "chrome/browser/profiles/profile_manager.h"
65#include "chrome/browser/ssl/ssl_blocking_page.h"
66#include "chrome/browser/ssl/ssl_manager.h"
67#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
68#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
69#include "chrome/browser/ui/browser_list.h"
70#include "chrome/browser/ui/browser_window.h"
71#include "chrome/browser/ui/download/download_tab_helper.h"
72#include "chrome/browser/ui/find_bar/find_bar.h"
73#include "chrome/browser/ui/find_bar/find_bar_controller.h"
74#include "chrome/browser/ui/find_bar/find_notification_details.h"
75#include "chrome/browser/ui/find_bar/find_tab_helper.h"
76#include "chrome/browser/ui/login/login_prompt.h"
77#include "chrome/browser/ui/omnibox/location_bar.h"
78#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
79#include "chrome/common/automation_constants.h"
80#include "chrome/common/automation_messages.h"
81#include "chrome/common/chrome_constants.h"
82#include "chrome/common/chrome_paths.h"
83#include "chrome/common/chrome_switches.h"
84#include "chrome/common/chrome_version_info.h"
85#include "chrome/common/extensions/extension.h"
86#include "chrome/common/pref_names.h"
87#include "chrome/common/url_constants.h"
88#include "chrome/test/automation/tab_proxy.h"
89#include "content/browser/browser_thread.h"
90#include "content/browser/renderer_host/render_process_host.h"
91#include "content/browser/renderer_host/render_view_host.h"
92#include "content/browser/tab_contents/navigation_entry.h"
93#include "content/browser/tab_contents/tab_contents.h"
94#include "content/browser/tab_contents/tab_contents_view.h"
95#include "content/common/json_value_serializer.h"
96#include "net/proxy/proxy_config_service_fixed.h"
97#include "net/proxy/proxy_service.h"
98#include "net/url_request/url_request_context.h"
99#include "net/url_request/url_request_context_getter.h"
100#include "webkit/glue/password_form.h"
101
102#if defined(OS_WIN)
103#include "chrome/browser/external_tab_container_win.h"
104#endif  // defined(OS_WIN)
105
106using base::Time;
107
108AutomationProvider::AutomationProvider(Profile* profile)
109    : profile_(profile),
110      reply_message_(NULL),
111      reinitialize_on_channel_error_(false),
112      is_connected_(false),
113      initial_tab_loads_complete_(false),
114      network_library_initialized_(true) {
115  TRACE_EVENT_BEGIN("AutomationProvider::AutomationProvider", 0, "");
116
117  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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  metric_event_duration_observer_.reset(new MetricEventDurationObserver());
127  extension_test_result_observer_.reset(
128      new ExtensionTestResultNotificationObserver(this));
129  g_browser_process->AddRefModule();
130
131  TRACE_EVENT_END("AutomationProvider::AutomationProvider", 0, "");
132}
133
134AutomationProvider::~AutomationProvider() {
135  if (channel_.get())
136    channel_->Close();
137
138  g_browser_process->ReleaseModule();
139}
140
141bool AutomationProvider::InitializeChannel(const std::string& channel_id) {
142  TRACE_EVENT_BEGIN("AutomationProvider::InitializeChannel", 0, "");
143
144  channel_id_ = channel_id;
145  std::string effective_channel_id = channel_id;
146
147  // If the channel_id starts with kNamedInterfacePrefix, create a named IPC
148  // server and listen on it, else connect as client to an existing IPC server
149  bool use_named_interface =
150      channel_id.find(automation::kNamedInterfacePrefix) == 0;
151  if (use_named_interface) {
152    effective_channel_id = channel_id.substr(
153        strlen(automation::kNamedInterfacePrefix));
154    if (effective_channel_id.length() <= 0)
155      return false;
156
157    reinitialize_on_channel_error_ = true;
158  }
159
160  if (!automation_resource_message_filter_.get()) {
161    automation_resource_message_filter_ = new AutomationResourceMessageFilter;
162  }
163
164  channel_.reset(new IPC::SyncChannel(
165      effective_channel_id,
166      use_named_interface ? IPC::Channel::MODE_NAMED_SERVER
167                          : IPC::Channel::MODE_CLIENT,
168      this,
169      g_browser_process->io_thread()->message_loop(),
170      true, g_browser_process->shutdown_event()));
171  channel_->AddFilter(automation_resource_message_filter_);
172
173#if defined(OS_CHROMEOS)
174  // Wait for the network manager to initialize.
175  // The observer will delete itself when done.
176  network_library_initialized_ = false;
177  NetworkManagerInitObserver* observer = new NetworkManagerInitObserver(this);
178  if (!observer->Init())
179    delete observer;
180#endif
181
182  TRACE_EVENT_END("AutomationProvider::InitializeChannel", 0, "");
183
184  return true;
185}
186
187std::string AutomationProvider::GetProtocolVersion() {
188  chrome::VersionInfo version_info;
189  return version_info.Version().c_str();
190}
191
192void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) {
193  if (expected_tabs == 0)
194    OnInitialTabLoadsComplete();
195  else
196    initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this));
197}
198
199void AutomationProvider::OnInitialTabLoadsComplete() {
200  initial_tab_loads_complete_ = true;
201  if (is_connected_ && network_library_initialized_)
202    Send(new AutomationMsg_InitialLoadsComplete());
203}
204
205void AutomationProvider::OnNetworkLibraryInit() {
206  network_library_initialized_ = true;
207  if (is_connected_ && initial_tab_loads_complete_)
208    Send(new AutomationMsg_InitialLoadsComplete());
209}
210
211void AutomationProvider::AddLoginHandler(NavigationController* tab,
212                                         LoginHandler* handler) {
213  login_handler_map_[tab] = handler;
214}
215
216void AutomationProvider::RemoveLoginHandler(NavigationController* tab) {
217  DCHECK(login_handler_map_[tab]);
218  login_handler_map_.erase(tab);
219}
220
221int AutomationProvider::GetIndexForNavigationController(
222    const NavigationController* controller, const Browser* parent) const {
223  DCHECK(parent);
224  return parent->GetIndexOfController(controller);
225}
226
227int AutomationProvider::AddExtension(const Extension* extension) {
228  DCHECK(extension);
229  return extension_tracker_->Add(extension);
230}
231
232// TODO(phajdan.jr): move to TestingAutomationProvider.
233DictionaryValue* AutomationProvider::GetDictionaryFromDownloadItem(
234    const DownloadItem* download) {
235  std::map<DownloadItem::DownloadState, std::string> state_to_string;
236  state_to_string[DownloadItem::IN_PROGRESS] = std::string("IN_PROGRESS");
237  state_to_string[DownloadItem::CANCELLED] = std::string("CANCELLED");
238  state_to_string[DownloadItem::REMOVING] = std::string("REMOVING");
239  state_to_string[DownloadItem::INTERRUPTED] = std::string("INTERRUPTED");
240  state_to_string[DownloadItem::COMPLETE] = std::string("COMPLETE");
241
242  std::map<DownloadItem::SafetyState, std::string> safety_state_to_string;
243  safety_state_to_string[DownloadItem::SAFE] = std::string("SAFE");
244  safety_state_to_string[DownloadItem::DANGEROUS] = std::string("DANGEROUS");
245  safety_state_to_string[DownloadItem::DANGEROUS_BUT_VALIDATED] =
246      std::string("DANGEROUS_BUT_VALIDATED");
247
248  DictionaryValue* dl_item_value = new DictionaryValue;
249  dl_item_value->SetInteger("id", static_cast<int>(download->id()));
250  dl_item_value->SetString("url", download->url().spec());
251  dl_item_value->SetString("referrer_url", download->referrer_url().spec());
252  dl_item_value->SetString("file_name",
253                           download->GetFileNameToReportUser().value());
254  dl_item_value->SetString("full_path",
255                           download->GetTargetFilePath().value());
256  dl_item_value->SetBoolean("is_paused", download->is_paused());
257  dl_item_value->SetBoolean("open_when_complete",
258                            download->open_when_complete());
259  dl_item_value->SetBoolean("is_extension_install",
260                            download->is_extension_install());
261  dl_item_value->SetBoolean("is_temporary", download->is_temporary());
262  dl_item_value->SetBoolean("is_otr", download->is_otr());  // incognito
263  dl_item_value->SetString("state", state_to_string[download->state()]);
264  dl_item_value->SetString("safety_state",
265                           safety_state_to_string[download->safety_state()]);
266  dl_item_value->SetInteger("PercentComplete", download->PercentComplete());
267
268  return dl_item_value;
269}
270
271const Extension* AutomationProvider::GetExtension(int extension_handle) {
272  return extension_tracker_->GetResource(extension_handle);
273}
274
275const Extension* AutomationProvider::GetEnabledExtension(int extension_handle) {
276  const Extension* extension =
277      extension_tracker_->GetResource(extension_handle);
278  ExtensionService* service = profile_->GetExtensionService();
279  if (extension && service &&
280      service->GetExtensionById(extension->id(), false))
281    return extension;
282  return NULL;
283}
284
285const Extension* AutomationProvider::GetDisabledExtension(
286    int extension_handle) {
287  const Extension* extension =
288      extension_tracker_->GetResource(extension_handle);
289  ExtensionService* service = profile_->GetExtensionService();
290  if (extension && service &&
291      service->GetExtensionById(extension->id(), true) &&
292      !service->GetExtensionById(extension->id(), false))
293    return extension;
294  return NULL;
295}
296
297void AutomationProvider::OnChannelConnected(int pid) {
298  is_connected_ = true;
299  LOG(INFO) << "Testing channel connected, sending hello message";
300
301  // Send a hello message with our current automation protocol version.
302  channel_->Send(new AutomationMsg_Hello(GetProtocolVersion()));
303  if (initial_tab_loads_complete_ && network_library_initialized_)
304    Send(new AutomationMsg_InitialLoadsComplete());
305}
306
307bool AutomationProvider::OnMessageReceived(const IPC::Message& message) {
308  bool handled = true;
309  bool deserialize_success = true;
310  IPC_BEGIN_MESSAGE_MAP_EX(AutomationProvider, message, deserialize_success)
311#if !defined(OS_MACOSX)
312    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WindowDrag,
313                                    WindowSimulateDrag)
314#endif  // !defined(OS_MACOSX)
315    IPC_MESSAGE_HANDLER(AutomationMsg_HandleUnused, HandleUnused)
316    IPC_MESSAGE_HANDLER(AutomationMsg_SetProxyConfig, SetProxyConfig)
317    IPC_MESSAGE_HANDLER(AutomationMsg_PrintAsync, PrintAsync)
318    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Find, HandleFindRequest)
319    IPC_MESSAGE_HANDLER(AutomationMsg_OverrideEncoding, OverrideEncoding)
320    IPC_MESSAGE_HANDLER(AutomationMsg_SelectAll, SelectAll)
321    IPC_MESSAGE_HANDLER(AutomationMsg_Cut, Cut)
322    IPC_MESSAGE_HANDLER(AutomationMsg_Copy, Copy)
323    IPC_MESSAGE_HANDLER(AutomationMsg_Paste, Paste)
324    IPC_MESSAGE_HANDLER(AutomationMsg_ReloadAsync, ReloadAsync)
325    IPC_MESSAGE_HANDLER(AutomationMsg_StopAsync, StopAsync)
326    IPC_MESSAGE_HANDLER(AutomationMsg_SetPageFontSize, OnSetPageFontSize)
327    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_InstallExtension,
328                                    InstallExtension)
329    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForExtensionTestResult,
330                                    WaitForExtensionTestResult)
331    IPC_MESSAGE_HANDLER_DELAY_REPLY(
332        AutomationMsg_InstallExtensionAndGetHandle,
333        InstallExtensionAndGetHandle)
334    IPC_MESSAGE_HANDLER(AutomationMsg_UninstallExtension,
335                        UninstallExtension)
336    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_EnableExtension,
337                                    EnableExtension)
338    IPC_MESSAGE_HANDLER(AutomationMsg_DisableExtension,
339                        DisableExtension)
340    IPC_MESSAGE_HANDLER_DELAY_REPLY(
341        AutomationMsg_ExecuteExtensionActionInActiveTabAsync,
342        ExecuteExtensionActionInActiveTabAsync)
343    IPC_MESSAGE_HANDLER(AutomationMsg_MoveExtensionBrowserAction,
344                        MoveExtensionBrowserAction)
345    IPC_MESSAGE_HANDLER(AutomationMsg_GetExtensionProperty,
346                        GetExtensionProperty)
347    IPC_MESSAGE_HANDLER(AutomationMsg_SaveAsAsync, SaveAsAsync)
348    IPC_MESSAGE_HANDLER(AutomationMsg_RemoveBrowsingData, RemoveBrowsingData)
349    IPC_MESSAGE_HANDLER(AutomationMsg_JavaScriptStressTestControl,
350                        JavaScriptStressTestControl)
351#if defined(OS_WIN)
352    // These are for use with external tabs.
353    IPC_MESSAGE_HANDLER(AutomationMsg_CreateExternalTab, CreateExternalTab)
354    IPC_MESSAGE_HANDLER(AutomationMsg_ProcessUnhandledAccelerator,
355                        ProcessUnhandledAccelerator)
356    IPC_MESSAGE_HANDLER(AutomationMsg_SetInitialFocus, SetInitialFocus)
357    IPC_MESSAGE_HANDLER(AutomationMsg_TabReposition, OnTabReposition)
358    IPC_MESSAGE_HANDLER(AutomationMsg_ForwardContextMenuCommandToChrome,
359                        OnForwardContextMenuCommandToChrome)
360    IPC_MESSAGE_HANDLER(AutomationMsg_NavigateInExternalTab,
361                        NavigateInExternalTab)
362    IPC_MESSAGE_HANDLER(AutomationMsg_NavigateExternalTabAtIndex,
363                        NavigateExternalTabAtIndex)
364    IPC_MESSAGE_HANDLER(AutomationMsg_ConnectExternalTab, ConnectExternalTab)
365    IPC_MESSAGE_HANDLER(AutomationMsg_HandleMessageFromExternalHost,
366                        OnMessageFromExternalHost)
367    IPC_MESSAGE_HANDLER(AutomationMsg_BrowserMove, OnBrowserMoved)
368    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_RunUnloadHandlers,
369                                    OnRunUnloadHandlers)
370    IPC_MESSAGE_HANDLER(AutomationMsg_SetZoomLevel, OnSetZoomLevel)
371#endif  // defined(OS_WIN)
372    IPC_MESSAGE_UNHANDLED(handled = false; OnUnhandledMessage())
373  IPC_END_MESSAGE_MAP_EX()
374  if (!deserialize_success)
375    OnMessageDeserializationFailure();
376  return handled;
377}
378
379void AutomationProvider::OnUnhandledMessage() {
380  // We should not hang here. Print a message to indicate what's going on,
381  // and disconnect the channel to notify the caller about the error
382  // in a way it can't ignore, and make any further attempts to send
383  // messages fail fast.
384  LOG(ERROR) << "AutomationProvider received a message it can't handle. "
385             << "Please make sure that you use switches::kTestingChannelID "
386             << "for test code (TestingAutomationProvider), and "
387             << "switches::kAutomationClientChannelID for everything else "
388             << "(like ChromeFrame). Closing the automation channel.";
389  channel_->Close();
390}
391
392void AutomationProvider::OnMessageDeserializationFailure() {
393  LOG(ERROR) << "Failed to deserialize IPC message. "
394             << "Closing the automation channel.";
395  channel_->Close();
396}
397
398// This task just adds another task to the event queue.  This is useful if
399// you want to ensure that any tasks added to the event queue after this one
400// have already been processed by the time |task| is run.
401class InvokeTaskLaterTask : public Task {
402 public:
403  explicit InvokeTaskLaterTask(Task* task) : task_(task) {}
404  virtual ~InvokeTaskLaterTask() {}
405
406  virtual void Run() {
407    MessageLoop::current()->PostTask(FROM_HERE, task_);
408  }
409
410 private:
411  Task* task_;
412
413  DISALLOW_COPY_AND_ASSIGN(InvokeTaskLaterTask);
414};
415
416void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) {
417  if (window_tracker_->ContainsHandle(handle)) {
418    window_tracker_->Remove(window_tracker_->GetResource(handle));
419  }
420}
421
422bool AutomationProvider::ReinitializeChannel() {
423  base::ThreadRestrictions::ScopedAllowIO allow_io;
424
425  // Make sure any old channels are cleaned up before starting up a new one.
426  channel_.reset();
427  return InitializeChannel(channel_id_);
428}
429
430void AutomationProvider::OnChannelError() {
431  if (reinitialize_on_channel_error_) {
432    VLOG(1) << "AutomationProxy disconnected, resetting AutomationProvider.";
433    if (ReinitializeChannel())
434      return;
435    VLOG(1) << "Error reinitializing AutomationProvider channel.";
436  }
437  VLOG(1) << "AutomationProxy went away, shutting down app.";
438  AutomationProviderList::GetInstance()->RemoveProvider(this);
439}
440
441bool AutomationProvider::Send(IPC::Message* msg) {
442  DCHECK(channel_.get());
443  return channel_->Send(msg);
444}
445
446Browser* AutomationProvider::FindAndActivateTab(
447    NavigationController* controller) {
448  int tab_index;
449  Browser* browser = Browser::GetBrowserForController(controller, &tab_index);
450  if (browser)
451    browser->ActivateTabAt(tab_index, true);
452
453  return browser;
454}
455
456void AutomationProvider::HandleFindRequest(
457    int handle,
458    const AutomationMsg_Find_Params& params,
459    IPC::Message* reply_message) {
460  if (!tab_tracker_->ContainsHandle(handle)) {
461    AutomationMsg_Find::WriteReplyParams(reply_message, -1, -1);
462    Send(reply_message);
463    return;
464  }
465
466  NavigationController* nav = tab_tracker_->GetResource(handle);
467  TabContents* tab_contents = nav->tab_contents();
468
469  SendFindRequest(tab_contents,
470                  false,
471                  params.search_string,
472                  params.forward,
473                  params.match_case,
474                  params.find_next,
475                  reply_message);
476}
477
478void AutomationProvider::SendFindRequest(
479    TabContents* tab_contents,
480    bool with_json,
481    const string16& search_string,
482    bool forward,
483    bool match_case,
484    bool find_next,
485    IPC::Message* reply_message) {
486  int request_id = FindInPageNotificationObserver::kFindInPageRequestId;
487  FindInPageNotificationObserver* observer =
488      new FindInPageNotificationObserver(this,
489                                         tab_contents,
490                                         with_json,
491                                         reply_message);
492  if (!with_json) {
493    find_in_page_observer_.reset(observer);
494  }
495  TabContentsWrapper* wrapper =
496      TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
497  if (wrapper)
498    wrapper->find_tab_helper()->set_current_find_request_id(request_id);
499
500  tab_contents->render_view_host()->StartFinding(
501      FindInPageNotificationObserver::kFindInPageRequestId,
502      search_string,
503      forward,
504      match_case,
505      find_next);
506}
507
508class SetProxyConfigTask : public Task {
509 public:
510  SetProxyConfigTask(net::URLRequestContextGetter* request_context_getter,
511                     const std::string& new_proxy_config)
512      : request_context_getter_(request_context_getter),
513        proxy_config_(new_proxy_config) {}
514  virtual void Run() {
515    // First, deserialize the JSON string. If this fails, log and bail.
516    JSONStringValueSerializer deserializer(proxy_config_);
517    std::string error_msg;
518    scoped_ptr<Value> root(deserializer.Deserialize(NULL, &error_msg));
519    if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) {
520      DLOG(WARNING) << "Received bad JSON string for ProxyConfig: "
521                    << error_msg;
522      return;
523    }
524
525    scoped_ptr<DictionaryValue> dict(
526        static_cast<DictionaryValue*>(root.release()));
527    // Now put together a proxy configuration from the deserialized string.
528    net::ProxyConfig pc;
529    PopulateProxyConfig(*dict.get(), &pc);
530
531    net::ProxyService* proxy_service =
532        request_context_getter_->GetURLRequestContext()->proxy_service();
533    DCHECK(proxy_service);
534    scoped_ptr<net::ProxyConfigService> proxy_config_service(
535        new net::ProxyConfigServiceFixed(pc));
536    proxy_service->ResetConfigService(proxy_config_service.release());
537  }
538
539  void PopulateProxyConfig(const DictionaryValue& dict, net::ProxyConfig* pc) {
540    DCHECK(pc);
541    bool no_proxy = false;
542    if (dict.GetBoolean(automation::kJSONProxyNoProxy, &no_proxy)) {
543      // Make no changes to the ProxyConfig.
544      return;
545    }
546    bool auto_config;
547    if (dict.GetBoolean(automation::kJSONProxyAutoconfig, &auto_config)) {
548      pc->set_auto_detect(true);
549    }
550    std::string pac_url;
551    if (dict.GetString(automation::kJSONProxyPacUrl, &pac_url)) {
552      pc->set_pac_url(GURL(pac_url));
553    }
554    std::string proxy_bypass_list;
555    if (dict.GetString(automation::kJSONProxyBypassList, &proxy_bypass_list)) {
556      pc->proxy_rules().bypass_rules.ParseFromString(proxy_bypass_list);
557    }
558    std::string proxy_server;
559    if (dict.GetString(automation::kJSONProxyServer, &proxy_server)) {
560      pc->proxy_rules().ParseFromString(proxy_server);
561    }
562  }
563
564 private:
565  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
566  std::string proxy_config_;
567};
568
569
570void AutomationProvider::SetProxyConfig(const std::string& new_proxy_config) {
571  net::URLRequestContextGetter* context_getter =
572      Profile::GetDefaultRequestContext();
573  if (!context_getter) {
574    FilePath user_data_dir;
575    PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
576    ProfileManager* profile_manager = g_browser_process->profile_manager();
577    DCHECK(profile_manager);
578    Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
579    DCHECK(profile);
580    context_getter = profile->GetRequestContext();
581  }
582  DCHECK(context_getter);
583
584  BrowserThread::PostTask(
585      BrowserThread::IO, FROM_HERE,
586      new SetProxyConfigTask(context_getter, new_proxy_config));
587}
588
589TabContents* AutomationProvider::GetTabContentsForHandle(
590    int handle, NavigationController** tab) {
591  if (tab_tracker_->ContainsHandle(handle)) {
592    NavigationController* nav_controller = tab_tracker_->GetResource(handle);
593    if (tab)
594      *tab = nav_controller;
595    return nav_controller->tab_contents();
596  }
597  return NULL;
598}
599
600// Gets the current used encoding name of the page in the specified tab.
601void AutomationProvider::OverrideEncoding(int tab_handle,
602                                          const std::string& encoding_name,
603                                          bool* success) {
604  *success = false;
605  if (tab_tracker_->ContainsHandle(tab_handle)) {
606    NavigationController* nav = tab_tracker_->GetResource(tab_handle);
607    if (!nav)
608      return;
609    Browser* browser = FindAndActivateTab(nav);
610
611    // If the browser has UI, simulate what a user would do.
612    // Activate the tab and then click the encoding menu.
613    if (browser &&
614        browser->command_updater()->IsCommandEnabled(IDC_ENCODING_MENU)) {
615      int selected_encoding_id =
616          CharacterEncoding::GetCommandIdByCanonicalEncodingName(encoding_name);
617      if (selected_encoding_id) {
618        browser->OverrideEncoding(selected_encoding_id);
619        *success = true;
620      }
621    } else {
622      // There is no UI, Chrome probably runs as Chrome-Frame mode.
623      // Try to get TabContents and call its override_encoding method.
624      TabContents* contents = nav->tab_contents();
625      if (!contents)
626        return;
627      const std::string selected_encoding =
628          CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding_name);
629      if (selected_encoding.empty())
630        return;
631      contents->SetOverrideEncoding(selected_encoding);
632    }
633  }
634}
635
636void AutomationProvider::SelectAll(int tab_handle) {
637  RenderViewHost* view = GetViewForTab(tab_handle);
638  if (!view) {
639    NOTREACHED();
640    return;
641  }
642
643  view->SelectAll();
644}
645
646void AutomationProvider::Cut(int tab_handle) {
647  RenderViewHost* view = GetViewForTab(tab_handle);
648  if (!view) {
649    NOTREACHED();
650    return;
651  }
652
653  view->Cut();
654}
655
656void AutomationProvider::Copy(int tab_handle) {
657  RenderViewHost* view = GetViewForTab(tab_handle);
658  if (!view) {
659    NOTREACHED();
660    return;
661  }
662
663  view->Copy();
664}
665
666void AutomationProvider::Paste(int tab_handle) {
667  RenderViewHost* view = GetViewForTab(tab_handle);
668  if (!view) {
669    NOTREACHED();
670    return;
671  }
672
673  view->Paste();
674}
675
676void AutomationProvider::ReloadAsync(int tab_handle) {
677  if (tab_tracker_->ContainsHandle(tab_handle)) {
678    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
679    if (!tab) {
680      NOTREACHED();
681      return;
682    }
683
684    const bool check_for_repost = true;
685    tab->Reload(check_for_repost);
686  }
687}
688
689void AutomationProvider::StopAsync(int tab_handle) {
690  RenderViewHost* view = GetViewForTab(tab_handle);
691  if (!view) {
692    // We tolerate StopAsync being called even before a view has been created.
693    // So just log a warning instead of a NOTREACHED().
694    DLOG(WARNING) << "StopAsync: no view for handle " << tab_handle;
695    return;
696  }
697
698  view->Stop();
699}
700
701void AutomationProvider::OnSetPageFontSize(int tab_handle,
702                                           int font_size) {
703  AutomationPageFontSize automation_font_size =
704      static_cast<AutomationPageFontSize>(font_size);
705
706  if (automation_font_size < SMALLEST_FONT ||
707      automation_font_size > LARGEST_FONT) {
708      DLOG(ERROR) << "Invalid font size specified : "
709                  << font_size;
710      return;
711  }
712
713  if (tab_tracker_->ContainsHandle(tab_handle)) {
714    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
715    DCHECK(tab != NULL);
716    if (tab && tab->tab_contents()) {
717      DCHECK(tab->tab_contents()->profile() != NULL);
718      tab->tab_contents()->profile()->GetPrefs()->SetInteger(
719          prefs::kWebKitDefaultFontSize, font_size);
720    }
721  }
722}
723
724void AutomationProvider::RemoveBrowsingData(int remove_mask) {
725  BrowsingDataRemover* remover;
726  remover = new BrowsingDataRemover(profile(),
727      BrowsingDataRemover::EVERYTHING,  // All time periods.
728      base::Time());
729  remover->Remove(remove_mask);
730  // BrowsingDataRemover deletes itself.
731}
732
733void AutomationProvider::JavaScriptStressTestControl(int tab_handle,
734                                                     int cmd,
735                                                     int param) {
736  RenderViewHost* view = GetViewForTab(tab_handle);
737  if (!view) {
738    NOTREACHED();
739    return;
740  }
741
742  view->JavaScriptStressTestControl(cmd, param);
743}
744
745RenderViewHost* AutomationProvider::GetViewForTab(int tab_handle) {
746  if (tab_tracker_->ContainsHandle(tab_handle)) {
747    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
748    if (!tab) {
749      NOTREACHED();
750      return NULL;
751    }
752
753    TabContents* tab_contents = tab->tab_contents();
754    if (!tab_contents) {
755      NOTREACHED();
756      return NULL;
757    }
758
759    RenderViewHost* view_host = tab_contents->render_view_host();
760    return view_host;
761  }
762
763  return NULL;
764}
765
766void AutomationProvider::InstallExtension(const FilePath& crx_path,
767                                          IPC::Message* reply_message) {
768  ExtensionService* service = profile_->GetExtensionService();
769  if (service) {
770    // The observer will delete itself when done.
771    new ExtensionInstallNotificationObserver(this,
772                                             AutomationMsg_InstallExtension::ID,
773                                             reply_message);
774
775    scoped_refptr<CrxInstaller> installer(
776        new CrxInstaller(service, NULL));  // silent install, no UI
777    installer->InstallCrx(crx_path);
778  } else {
779    AutomationMsg_InstallExtension::WriteReplyParams(
780        reply_message, AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
781    Send(reply_message);
782  }
783}
784
785void AutomationProvider::WaitForExtensionTestResult(
786    IPC::Message* reply_message) {
787  DCHECK(!reply_message_);
788  reply_message_ = reply_message;
789  // Call MaybeSendResult, because the result might have come in before
790  // we were waiting on it.
791  extension_test_result_observer_->MaybeSendResult();
792}
793
794void AutomationProvider::InstallExtensionAndGetHandle(
795    const FilePath& crx_path, bool with_ui, IPC::Message* reply_message) {
796  ExtensionService* service = profile_->GetExtensionService();
797  ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
798  if (service && manager) {
799    // The observer will delete itself when done.
800    new ExtensionReadyNotificationObserver(
801        manager,
802        this,
803        AutomationMsg_InstallExtensionAndGetHandle::ID,
804        reply_message);
805
806    ExtensionInstallUI* client =
807        (with_ui ? new ExtensionInstallUI(profile_) : NULL);
808    scoped_refptr<CrxInstaller> installer(new CrxInstaller(service, client));
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  const Extension* extension = GetExtension(extension_handle);
821  ExtensionService* service = profile_->GetExtensionService();
822  if (extension && service) {
823    ExtensionUnloadNotificationObserver observer;
824    service->UninstallExtension(extension->id(), false, NULL);
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  const Extension* extension = GetDisabledExtension(extension_handle);
834  ExtensionService* service = profile_->GetExtensionService();
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  const Extension* extension = GetEnabledExtension(extension_handle);
855  ExtensionService* service = profile_->GetExtensionService();
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  const Extension* extension = GetEnabledExtension(extension_handle);
870  ExtensionService* service = profile_->GetExtensionService();
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      service->browser_event_router()->PageActionExecuted(
878          browser->profile(), extension->id(), "action", tab_id, "", 1);
879      success = true;
880    } else if (extension->browser_action()) {
881      service->browser_event_router()->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  const Extension* extension = GetEnabledExtension(extension_handle);
895  ExtensionService* service = profile_->GetExtensionService();
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  const Extension* extension = GetExtension(extension_handle);
916  ExtensionService* service = profile_->GetExtensionService();
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)->id()))
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    TabContentsWrapper* wrapper =
965        TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
966    wrapper->download_tab_helper()->OnSavePage();
967  }
968}
969