| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/automation/automation_provider.h" |
| |
| #include <set> |
| |
| #include "base/callback.h" |
| #include "base/debug/trace_event.h" |
| #include "base/file_path.h" |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/json/string_escape.h" |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/process_util.h" |
| #include "base/stl_util-inl.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_util.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task.h" |
| #include "base/threading/thread.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/autocomplete/autocomplete_edit.h" |
| #include "chrome/browser/autofill/autofill_manager.h" |
| #include "chrome/browser/automation/automation_autocomplete_edit_tracker.h" |
| #include "chrome/browser/automation/automation_browser_tracker.h" |
| #include "chrome/browser/automation/automation_extension_tracker.h" |
| #include "chrome/browser/automation/automation_provider_list.h" |
| #include "chrome/browser/automation/automation_provider_observers.h" |
| #include "chrome/browser/automation/automation_resource_message_filter.h" |
| #include "chrome/browser/automation/automation_tab_tracker.h" |
| #include "chrome/browser/automation/automation_window_tracker.h" |
| #include "chrome/browser/automation/ui_controls.h" |
| #include "chrome/browser/blocked_content_container.h" |
| #include "chrome/browser/bookmarks/bookmark_model.h" |
| #include "chrome/browser/bookmarks/bookmark_storage.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browsing_data_remover.h" |
| #include "chrome/browser/character_encoding.h" |
| #include "chrome/browser/content_settings/host_content_settings_map.h" |
| #include "chrome/browser/debugger/devtools_manager.h" |
| #include "chrome/browser/dom_operation_notification_details.h" |
| #include "chrome/browser/download/download_item.h" |
| #include "chrome/browser/download/download_shelf.h" |
| #include "chrome/browser/download/save_package.h" |
| #include "chrome/browser/extensions/crx_installer.h" |
| #include "chrome/browser/extensions/extension_browser_event_router.h" |
| #include "chrome/browser/extensions/extension_host.h" |
| #include "chrome/browser/extensions/extension_install_ui.h" |
| #include "chrome/browser/extensions/extension_message_service.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_tabs_module.h" |
| #include "chrome/browser/extensions/extension_toolbar_model.h" |
| #include "chrome/browser/extensions/user_script_master.h" |
| #include "chrome/browser/io_thread.h" |
| #include "chrome/browser/net/url_request_mock_util.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/printing/print_job.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ssl/ssl_blocking_page.h" |
| #include "chrome/browser/ssl/ssl_manager.h" |
| #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h" |
| #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/download/download_tab_helper.h" |
| #include "chrome/browser/ui/find_bar/find_bar.h" |
| #include "chrome/browser/ui/find_bar/find_bar_controller.h" |
| #include "chrome/browser/ui/find_bar/find_notification_details.h" |
| #include "chrome/browser/ui/find_bar/find_tab_helper.h" |
| #include "chrome/browser/ui/login/login_prompt.h" |
| #include "chrome/browser/ui/omnibox/location_bar.h" |
| #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| #include "chrome/common/automation_constants.h" |
| #include "chrome/common/automation_messages.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/chrome_version_info.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/test/automation/tab_proxy.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/tab_contents/navigation_entry.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/browser/tab_contents/tab_contents_view.h" |
| #include "content/common/json_value_serializer.h" |
| #include "net/proxy/proxy_config_service_fixed.h" |
| #include "net/proxy/proxy_service.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "webkit/glue/password_form.h" |
| |
| #if defined(OS_WIN) |
| #include "chrome/browser/external_tab_container_win.h" |
| #endif // defined(OS_WIN) |
| |
| using base::Time; |
| |
| AutomationProvider::AutomationProvider(Profile* profile) |
| : profile_(profile), |
| reply_message_(NULL), |
| reinitialize_on_channel_error_(false), |
| is_connected_(false), |
| initial_tab_loads_complete_(false), |
| network_library_initialized_(true) { |
| TRACE_EVENT_BEGIN("AutomationProvider::AutomationProvider", 0, ""); |
| |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| browser_tracker_.reset(new AutomationBrowserTracker(this)); |
| extension_tracker_.reset(new AutomationExtensionTracker(this)); |
| tab_tracker_.reset(new AutomationTabTracker(this)); |
| window_tracker_.reset(new AutomationWindowTracker(this)); |
| autocomplete_edit_tracker_.reset( |
| new AutomationAutocompleteEditTracker(this)); |
| new_tab_ui_load_observer_.reset(new NewTabUILoadObserver(this)); |
| metric_event_duration_observer_.reset(new MetricEventDurationObserver()); |
| extension_test_result_observer_.reset( |
| new ExtensionTestResultNotificationObserver(this)); |
| g_browser_process->AddRefModule(); |
| |
| TRACE_EVENT_END("AutomationProvider::AutomationProvider", 0, ""); |
| } |
| |
| AutomationProvider::~AutomationProvider() { |
| if (channel_.get()) |
| channel_->Close(); |
| |
| g_browser_process->ReleaseModule(); |
| } |
| |
| bool AutomationProvider::InitializeChannel(const std::string& channel_id) { |
| TRACE_EVENT_BEGIN("AutomationProvider::InitializeChannel", 0, ""); |
| |
| channel_id_ = channel_id; |
| std::string effective_channel_id = channel_id; |
| |
| // If the channel_id starts with kNamedInterfacePrefix, create a named IPC |
| // server and listen on it, else connect as client to an existing IPC server |
| bool use_named_interface = |
| channel_id.find(automation::kNamedInterfacePrefix) == 0; |
| if (use_named_interface) { |
| effective_channel_id = channel_id.substr( |
| strlen(automation::kNamedInterfacePrefix)); |
| if (effective_channel_id.length() <= 0) |
| return false; |
| |
| reinitialize_on_channel_error_ = true; |
| } |
| |
| if (!automation_resource_message_filter_.get()) { |
| automation_resource_message_filter_ = new AutomationResourceMessageFilter; |
| } |
| |
| channel_.reset(new IPC::SyncChannel( |
| effective_channel_id, |
| use_named_interface ? IPC::Channel::MODE_NAMED_SERVER |
| : IPC::Channel::MODE_CLIENT, |
| this, |
| g_browser_process->io_thread()->message_loop(), |
| true, g_browser_process->shutdown_event())); |
| channel_->AddFilter(automation_resource_message_filter_); |
| |
| #if defined(OS_CHROMEOS) |
| // Wait for the network manager to initialize. |
| // The observer will delete itself when done. |
| network_library_initialized_ = false; |
| NetworkManagerInitObserver* observer = new NetworkManagerInitObserver(this); |
| if (!observer->Init()) |
| delete observer; |
| #endif |
| |
| TRACE_EVENT_END("AutomationProvider::InitializeChannel", 0, ""); |
| |
| return true; |
| } |
| |
| std::string AutomationProvider::GetProtocolVersion() { |
| chrome::VersionInfo version_info; |
| return version_info.Version().c_str(); |
| } |
| |
| void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) { |
| if (expected_tabs == 0) |
| OnInitialTabLoadsComplete(); |
| else |
| initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this)); |
| } |
| |
| void AutomationProvider::OnInitialTabLoadsComplete() { |
| initial_tab_loads_complete_ = true; |
| if (is_connected_ && network_library_initialized_) |
| Send(new AutomationMsg_InitialLoadsComplete()); |
| } |
| |
| void AutomationProvider::OnNetworkLibraryInit() { |
| network_library_initialized_ = true; |
| if (is_connected_ && initial_tab_loads_complete_) |
| Send(new AutomationMsg_InitialLoadsComplete()); |
| } |
| |
| void AutomationProvider::AddLoginHandler(NavigationController* tab, |
| LoginHandler* handler) { |
| login_handler_map_[tab] = handler; |
| } |
| |
| void AutomationProvider::RemoveLoginHandler(NavigationController* tab) { |
| DCHECK(login_handler_map_[tab]); |
| login_handler_map_.erase(tab); |
| } |
| |
| int AutomationProvider::GetIndexForNavigationController( |
| const NavigationController* controller, const Browser* parent) const { |
| DCHECK(parent); |
| return parent->GetIndexOfController(controller); |
| } |
| |
| int AutomationProvider::AddExtension(const Extension* extension) { |
| DCHECK(extension); |
| return extension_tracker_->Add(extension); |
| } |
| |
| // TODO(phajdan.jr): move to TestingAutomationProvider. |
| DictionaryValue* AutomationProvider::GetDictionaryFromDownloadItem( |
| const DownloadItem* download) { |
| std::map<DownloadItem::DownloadState, std::string> state_to_string; |
| state_to_string[DownloadItem::IN_PROGRESS] = std::string("IN_PROGRESS"); |
| state_to_string[DownloadItem::CANCELLED] = std::string("CANCELLED"); |
| state_to_string[DownloadItem::REMOVING] = std::string("REMOVING"); |
| state_to_string[DownloadItem::INTERRUPTED] = std::string("INTERRUPTED"); |
| state_to_string[DownloadItem::COMPLETE] = std::string("COMPLETE"); |
| |
| std::map<DownloadItem::SafetyState, std::string> safety_state_to_string; |
| safety_state_to_string[DownloadItem::SAFE] = std::string("SAFE"); |
| safety_state_to_string[DownloadItem::DANGEROUS] = std::string("DANGEROUS"); |
| safety_state_to_string[DownloadItem::DANGEROUS_BUT_VALIDATED] = |
| std::string("DANGEROUS_BUT_VALIDATED"); |
| |
| DictionaryValue* dl_item_value = new DictionaryValue; |
| dl_item_value->SetInteger("id", static_cast<int>(download->id())); |
| dl_item_value->SetString("url", download->url().spec()); |
| dl_item_value->SetString("referrer_url", download->referrer_url().spec()); |
| dl_item_value->SetString("file_name", |
| download->GetFileNameToReportUser().value()); |
| dl_item_value->SetString("full_path", |
| download->GetTargetFilePath().value()); |
| dl_item_value->SetBoolean("is_paused", download->is_paused()); |
| dl_item_value->SetBoolean("open_when_complete", |
| download->open_when_complete()); |
| dl_item_value->SetBoolean("is_extension_install", |
| download->is_extension_install()); |
| dl_item_value->SetBoolean("is_temporary", download->is_temporary()); |
| dl_item_value->SetBoolean("is_otr", download->is_otr()); // incognito |
| dl_item_value->SetString("state", state_to_string[download->state()]); |
| dl_item_value->SetString("safety_state", |
| safety_state_to_string[download->safety_state()]); |
| dl_item_value->SetInteger("PercentComplete", download->PercentComplete()); |
| |
| return dl_item_value; |
| } |
| |
| const Extension* AutomationProvider::GetExtension(int extension_handle) { |
| return extension_tracker_->GetResource(extension_handle); |
| } |
| |
| const Extension* AutomationProvider::GetEnabledExtension(int extension_handle) { |
| const Extension* extension = |
| extension_tracker_->GetResource(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (extension && service && |
| service->GetExtensionById(extension->id(), false)) |
| return extension; |
| return NULL; |
| } |
| |
| const Extension* AutomationProvider::GetDisabledExtension( |
| int extension_handle) { |
| const Extension* extension = |
| extension_tracker_->GetResource(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (extension && service && |
| service->GetExtensionById(extension->id(), true) && |
| !service->GetExtensionById(extension->id(), false)) |
| return extension; |
| return NULL; |
| } |
| |
| void AutomationProvider::OnChannelConnected(int pid) { |
| is_connected_ = true; |
| LOG(INFO) << "Testing channel connected, sending hello message"; |
| |
| // Send a hello message with our current automation protocol version. |
| channel_->Send(new AutomationMsg_Hello(GetProtocolVersion())); |
| if (initial_tab_loads_complete_ && network_library_initialized_) |
| Send(new AutomationMsg_InitialLoadsComplete()); |
| } |
| |
| bool AutomationProvider::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| bool deserialize_success = true; |
| IPC_BEGIN_MESSAGE_MAP_EX(AutomationProvider, message, deserialize_success) |
| #if !defined(OS_MACOSX) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WindowDrag, |
| WindowSimulateDrag) |
| #endif // !defined(OS_MACOSX) |
| IPC_MESSAGE_HANDLER(AutomationMsg_HandleUnused, HandleUnused) |
| IPC_MESSAGE_HANDLER(AutomationMsg_SetProxyConfig, SetProxyConfig) |
| IPC_MESSAGE_HANDLER(AutomationMsg_PrintAsync, PrintAsync) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Find, HandleFindRequest) |
| IPC_MESSAGE_HANDLER(AutomationMsg_OverrideEncoding, OverrideEncoding) |
| IPC_MESSAGE_HANDLER(AutomationMsg_SelectAll, SelectAll) |
| IPC_MESSAGE_HANDLER(AutomationMsg_Cut, Cut) |
| IPC_MESSAGE_HANDLER(AutomationMsg_Copy, Copy) |
| IPC_MESSAGE_HANDLER(AutomationMsg_Paste, Paste) |
| IPC_MESSAGE_HANDLER(AutomationMsg_ReloadAsync, ReloadAsync) |
| IPC_MESSAGE_HANDLER(AutomationMsg_StopAsync, StopAsync) |
| IPC_MESSAGE_HANDLER(AutomationMsg_SetPageFontSize, OnSetPageFontSize) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_InstallExtension, |
| InstallExtension) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForExtensionTestResult, |
| WaitForExtensionTestResult) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY( |
| AutomationMsg_InstallExtensionAndGetHandle, |
| InstallExtensionAndGetHandle) |
| IPC_MESSAGE_HANDLER(AutomationMsg_UninstallExtension, |
| UninstallExtension) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_EnableExtension, |
| EnableExtension) |
| IPC_MESSAGE_HANDLER(AutomationMsg_DisableExtension, |
| DisableExtension) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY( |
| AutomationMsg_ExecuteExtensionActionInActiveTabAsync, |
| ExecuteExtensionActionInActiveTabAsync) |
| IPC_MESSAGE_HANDLER(AutomationMsg_MoveExtensionBrowserAction, |
| MoveExtensionBrowserAction) |
| IPC_MESSAGE_HANDLER(AutomationMsg_GetExtensionProperty, |
| GetExtensionProperty) |
| IPC_MESSAGE_HANDLER(AutomationMsg_SaveAsAsync, SaveAsAsync) |
| IPC_MESSAGE_HANDLER(AutomationMsg_RemoveBrowsingData, RemoveBrowsingData) |
| IPC_MESSAGE_HANDLER(AutomationMsg_JavaScriptStressTestControl, |
| JavaScriptStressTestControl) |
| #if defined(OS_WIN) |
| // These are for use with external tabs. |
| IPC_MESSAGE_HANDLER(AutomationMsg_CreateExternalTab, CreateExternalTab) |
| IPC_MESSAGE_HANDLER(AutomationMsg_ProcessUnhandledAccelerator, |
| ProcessUnhandledAccelerator) |
| IPC_MESSAGE_HANDLER(AutomationMsg_SetInitialFocus, SetInitialFocus) |
| IPC_MESSAGE_HANDLER(AutomationMsg_TabReposition, OnTabReposition) |
| IPC_MESSAGE_HANDLER(AutomationMsg_ForwardContextMenuCommandToChrome, |
| OnForwardContextMenuCommandToChrome) |
| IPC_MESSAGE_HANDLER(AutomationMsg_NavigateInExternalTab, |
| NavigateInExternalTab) |
| IPC_MESSAGE_HANDLER(AutomationMsg_NavigateExternalTabAtIndex, |
| NavigateExternalTabAtIndex) |
| IPC_MESSAGE_HANDLER(AutomationMsg_ConnectExternalTab, ConnectExternalTab) |
| IPC_MESSAGE_HANDLER(AutomationMsg_HandleMessageFromExternalHost, |
| OnMessageFromExternalHost) |
| IPC_MESSAGE_HANDLER(AutomationMsg_BrowserMove, OnBrowserMoved) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_RunUnloadHandlers, |
| OnRunUnloadHandlers) |
| IPC_MESSAGE_HANDLER(AutomationMsg_SetZoomLevel, OnSetZoomLevel) |
| #endif // defined(OS_WIN) |
| IPC_MESSAGE_UNHANDLED(handled = false; OnUnhandledMessage()) |
| IPC_END_MESSAGE_MAP_EX() |
| if (!deserialize_success) |
| OnMessageDeserializationFailure(); |
| return handled; |
| } |
| |
| void AutomationProvider::OnUnhandledMessage() { |
| // We should not hang here. Print a message to indicate what's going on, |
| // and disconnect the channel to notify the caller about the error |
| // in a way it can't ignore, and make any further attempts to send |
| // messages fail fast. |
| LOG(ERROR) << "AutomationProvider received a message it can't handle. " |
| << "Please make sure that you use switches::kTestingChannelID " |
| << "for test code (TestingAutomationProvider), and " |
| << "switches::kAutomationClientChannelID for everything else " |
| << "(like ChromeFrame). Closing the automation channel."; |
| channel_->Close(); |
| } |
| |
| void AutomationProvider::OnMessageDeserializationFailure() { |
| LOG(ERROR) << "Failed to deserialize IPC message. " |
| << "Closing the automation channel."; |
| channel_->Close(); |
| } |
| |
| // This task just adds another task to the event queue. This is useful if |
| // you want to ensure that any tasks added to the event queue after this one |
| // have already been processed by the time |task| is run. |
| class InvokeTaskLaterTask : public Task { |
| public: |
| explicit InvokeTaskLaterTask(Task* task) : task_(task) {} |
| virtual ~InvokeTaskLaterTask() {} |
| |
| virtual void Run() { |
| MessageLoop::current()->PostTask(FROM_HERE, task_); |
| } |
| |
| private: |
| Task* task_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InvokeTaskLaterTask); |
| }; |
| |
| void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) { |
| if (window_tracker_->ContainsHandle(handle)) { |
| window_tracker_->Remove(window_tracker_->GetResource(handle)); |
| } |
| } |
| |
| bool AutomationProvider::ReinitializeChannel() { |
| base::ThreadRestrictions::ScopedAllowIO allow_io; |
| |
| // Make sure any old channels are cleaned up before starting up a new one. |
| channel_.reset(); |
| return InitializeChannel(channel_id_); |
| } |
| |
| void AutomationProvider::OnChannelError() { |
| if (reinitialize_on_channel_error_) { |
| VLOG(1) << "AutomationProxy disconnected, resetting AutomationProvider."; |
| if (ReinitializeChannel()) |
| return; |
| VLOG(1) << "Error reinitializing AutomationProvider channel."; |
| } |
| VLOG(1) << "AutomationProxy went away, shutting down app."; |
| AutomationProviderList::GetInstance()->RemoveProvider(this); |
| } |
| |
| bool AutomationProvider::Send(IPC::Message* msg) { |
| DCHECK(channel_.get()); |
| return channel_->Send(msg); |
| } |
| |
| Browser* AutomationProvider::FindAndActivateTab( |
| NavigationController* controller) { |
| int tab_index; |
| Browser* browser = Browser::GetBrowserForController(controller, &tab_index); |
| if (browser) |
| browser->ActivateTabAt(tab_index, true); |
| |
| return browser; |
| } |
| |
| void AutomationProvider::HandleFindRequest( |
| int handle, |
| const AutomationMsg_Find_Params& params, |
| IPC::Message* reply_message) { |
| if (!tab_tracker_->ContainsHandle(handle)) { |
| AutomationMsg_Find::WriteReplyParams(reply_message, -1, -1); |
| Send(reply_message); |
| return; |
| } |
| |
| NavigationController* nav = tab_tracker_->GetResource(handle); |
| TabContents* tab_contents = nav->tab_contents(); |
| |
| SendFindRequest(tab_contents, |
| false, |
| params.search_string, |
| params.forward, |
| params.match_case, |
| params.find_next, |
| reply_message); |
| } |
| |
| void AutomationProvider::SendFindRequest( |
| TabContents* tab_contents, |
| bool with_json, |
| const string16& search_string, |
| bool forward, |
| bool match_case, |
| bool find_next, |
| IPC::Message* reply_message) { |
| int request_id = FindInPageNotificationObserver::kFindInPageRequestId; |
| FindInPageNotificationObserver* observer = |
| new FindInPageNotificationObserver(this, |
| tab_contents, |
| with_json, |
| reply_message); |
| if (!with_json) { |
| find_in_page_observer_.reset(observer); |
| } |
| TabContentsWrapper* wrapper = |
| TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); |
| if (wrapper) |
| wrapper->find_tab_helper()->set_current_find_request_id(request_id); |
| |
| tab_contents->render_view_host()->StartFinding( |
| FindInPageNotificationObserver::kFindInPageRequestId, |
| search_string, |
| forward, |
| match_case, |
| find_next); |
| } |
| |
| class SetProxyConfigTask : public Task { |
| public: |
| SetProxyConfigTask(net::URLRequestContextGetter* request_context_getter, |
| const std::string& new_proxy_config) |
| : request_context_getter_(request_context_getter), |
| proxy_config_(new_proxy_config) {} |
| virtual void Run() { |
| // First, deserialize the JSON string. If this fails, log and bail. |
| JSONStringValueSerializer deserializer(proxy_config_); |
| std::string error_msg; |
| scoped_ptr<Value> root(deserializer.Deserialize(NULL, &error_msg)); |
| if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) { |
| DLOG(WARNING) << "Received bad JSON string for ProxyConfig: " |
| << error_msg; |
| return; |
| } |
| |
| scoped_ptr<DictionaryValue> dict( |
| static_cast<DictionaryValue*>(root.release())); |
| // Now put together a proxy configuration from the deserialized string. |
| net::ProxyConfig pc; |
| PopulateProxyConfig(*dict.get(), &pc); |
| |
| net::ProxyService* proxy_service = |
| request_context_getter_->GetURLRequestContext()->proxy_service(); |
| DCHECK(proxy_service); |
| scoped_ptr<net::ProxyConfigService> proxy_config_service( |
| new net::ProxyConfigServiceFixed(pc)); |
| proxy_service->ResetConfigService(proxy_config_service.release()); |
| } |
| |
| void PopulateProxyConfig(const DictionaryValue& dict, net::ProxyConfig* pc) { |
| DCHECK(pc); |
| bool no_proxy = false; |
| if (dict.GetBoolean(automation::kJSONProxyNoProxy, &no_proxy)) { |
| // Make no changes to the ProxyConfig. |
| return; |
| } |
| bool auto_config; |
| if (dict.GetBoolean(automation::kJSONProxyAutoconfig, &auto_config)) { |
| pc->set_auto_detect(true); |
| } |
| std::string pac_url; |
| if (dict.GetString(automation::kJSONProxyPacUrl, &pac_url)) { |
| pc->set_pac_url(GURL(pac_url)); |
| } |
| std::string proxy_bypass_list; |
| if (dict.GetString(automation::kJSONProxyBypassList, &proxy_bypass_list)) { |
| pc->proxy_rules().bypass_rules.ParseFromString(proxy_bypass_list); |
| } |
| std::string proxy_server; |
| if (dict.GetString(automation::kJSONProxyServer, &proxy_server)) { |
| pc->proxy_rules().ParseFromString(proxy_server); |
| } |
| } |
| |
| private: |
| scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| std::string proxy_config_; |
| }; |
| |
| |
| void AutomationProvider::SetProxyConfig(const std::string& new_proxy_config) { |
| net::URLRequestContextGetter* context_getter = |
| Profile::GetDefaultRequestContext(); |
| if (!context_getter) { |
| FilePath user_data_dir; |
| PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| DCHECK(profile_manager); |
| Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); |
| DCHECK(profile); |
| context_getter = profile->GetRequestContext(); |
| } |
| DCHECK(context_getter); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| new SetProxyConfigTask(context_getter, new_proxy_config)); |
| } |
| |
| TabContents* AutomationProvider::GetTabContentsForHandle( |
| int handle, NavigationController** tab) { |
| if (tab_tracker_->ContainsHandle(handle)) { |
| NavigationController* nav_controller = tab_tracker_->GetResource(handle); |
| if (tab) |
| *tab = nav_controller; |
| return nav_controller->tab_contents(); |
| } |
| return NULL; |
| } |
| |
| // Gets the current used encoding name of the page in the specified tab. |
| void AutomationProvider::OverrideEncoding(int tab_handle, |
| const std::string& encoding_name, |
| bool* success) { |
| *success = false; |
| if (tab_tracker_->ContainsHandle(tab_handle)) { |
| NavigationController* nav = tab_tracker_->GetResource(tab_handle); |
| if (!nav) |
| return; |
| Browser* browser = FindAndActivateTab(nav); |
| |
| // If the browser has UI, simulate what a user would do. |
| // Activate the tab and then click the encoding menu. |
| if (browser && |
| browser->command_updater()->IsCommandEnabled(IDC_ENCODING_MENU)) { |
| int selected_encoding_id = |
| CharacterEncoding::GetCommandIdByCanonicalEncodingName(encoding_name); |
| if (selected_encoding_id) { |
| browser->OverrideEncoding(selected_encoding_id); |
| *success = true; |
| } |
| } else { |
| // There is no UI, Chrome probably runs as Chrome-Frame mode. |
| // Try to get TabContents and call its override_encoding method. |
| TabContents* contents = nav->tab_contents(); |
| if (!contents) |
| return; |
| const std::string selected_encoding = |
| CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding_name); |
| if (selected_encoding.empty()) |
| return; |
| contents->SetOverrideEncoding(selected_encoding); |
| } |
| } |
| } |
| |
| void AutomationProvider::SelectAll(int tab_handle) { |
| RenderViewHost* view = GetViewForTab(tab_handle); |
| if (!view) { |
| NOTREACHED(); |
| return; |
| } |
| |
| view->SelectAll(); |
| } |
| |
| void AutomationProvider::Cut(int tab_handle) { |
| RenderViewHost* view = GetViewForTab(tab_handle); |
| if (!view) { |
| NOTREACHED(); |
| return; |
| } |
| |
| view->Cut(); |
| } |
| |
| void AutomationProvider::Copy(int tab_handle) { |
| RenderViewHost* view = GetViewForTab(tab_handle); |
| if (!view) { |
| NOTREACHED(); |
| return; |
| } |
| |
| view->Copy(); |
| } |
| |
| void AutomationProvider::Paste(int tab_handle) { |
| RenderViewHost* view = GetViewForTab(tab_handle); |
| if (!view) { |
| NOTREACHED(); |
| return; |
| } |
| |
| view->Paste(); |
| } |
| |
| void AutomationProvider::ReloadAsync(int tab_handle) { |
| if (tab_tracker_->ContainsHandle(tab_handle)) { |
| NavigationController* tab = tab_tracker_->GetResource(tab_handle); |
| if (!tab) { |
| NOTREACHED(); |
| return; |
| } |
| |
| const bool check_for_repost = true; |
| tab->Reload(check_for_repost); |
| } |
| } |
| |
| void AutomationProvider::StopAsync(int tab_handle) { |
| RenderViewHost* view = GetViewForTab(tab_handle); |
| if (!view) { |
| // We tolerate StopAsync being called even before a view has been created. |
| // So just log a warning instead of a NOTREACHED(). |
| DLOG(WARNING) << "StopAsync: no view for handle " << tab_handle; |
| return; |
| } |
| |
| view->Stop(); |
| } |
| |
| void AutomationProvider::OnSetPageFontSize(int tab_handle, |
| int font_size) { |
| AutomationPageFontSize automation_font_size = |
| static_cast<AutomationPageFontSize>(font_size); |
| |
| if (automation_font_size < SMALLEST_FONT || |
| automation_font_size > LARGEST_FONT) { |
| DLOG(ERROR) << "Invalid font size specified : " |
| << font_size; |
| return; |
| } |
| |
| if (tab_tracker_->ContainsHandle(tab_handle)) { |
| NavigationController* tab = tab_tracker_->GetResource(tab_handle); |
| DCHECK(tab != NULL); |
| if (tab && tab->tab_contents()) { |
| DCHECK(tab->tab_contents()->profile() != NULL); |
| tab->tab_contents()->profile()->GetPrefs()->SetInteger( |
| prefs::kWebKitDefaultFontSize, font_size); |
| } |
| } |
| } |
| |
| void AutomationProvider::RemoveBrowsingData(int remove_mask) { |
| BrowsingDataRemover* remover; |
| remover = new BrowsingDataRemover(profile(), |
| BrowsingDataRemover::EVERYTHING, // All time periods. |
| base::Time()); |
| remover->Remove(remove_mask); |
| // BrowsingDataRemover deletes itself. |
| } |
| |
| void AutomationProvider::JavaScriptStressTestControl(int tab_handle, |
| int cmd, |
| int param) { |
| RenderViewHost* view = GetViewForTab(tab_handle); |
| if (!view) { |
| NOTREACHED(); |
| return; |
| } |
| |
| view->JavaScriptStressTestControl(cmd, param); |
| } |
| |
| RenderViewHost* AutomationProvider::GetViewForTab(int tab_handle) { |
| if (tab_tracker_->ContainsHandle(tab_handle)) { |
| NavigationController* tab = tab_tracker_->GetResource(tab_handle); |
| if (!tab) { |
| NOTREACHED(); |
| return NULL; |
| } |
| |
| TabContents* tab_contents = tab->tab_contents(); |
| if (!tab_contents) { |
| NOTREACHED(); |
| return NULL; |
| } |
| |
| RenderViewHost* view_host = tab_contents->render_view_host(); |
| return view_host; |
| } |
| |
| return NULL; |
| } |
| |
| void AutomationProvider::InstallExtension(const FilePath& crx_path, |
| IPC::Message* reply_message) { |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (service) { |
| // The observer will delete itself when done. |
| new ExtensionInstallNotificationObserver(this, |
| AutomationMsg_InstallExtension::ID, |
| reply_message); |
| |
| scoped_refptr<CrxInstaller> installer( |
| new CrxInstaller(service, NULL)); // silent install, no UI |
| installer->InstallCrx(crx_path); |
| } else { |
| AutomationMsg_InstallExtension::WriteReplyParams( |
| reply_message, AUTOMATION_MSG_EXTENSION_INSTALL_FAILED); |
| Send(reply_message); |
| } |
| } |
| |
| void AutomationProvider::WaitForExtensionTestResult( |
| IPC::Message* reply_message) { |
| DCHECK(!reply_message_); |
| reply_message_ = reply_message; |
| // Call MaybeSendResult, because the result might have come in before |
| // we were waiting on it. |
| extension_test_result_observer_->MaybeSendResult(); |
| } |
| |
| void AutomationProvider::InstallExtensionAndGetHandle( |
| const FilePath& crx_path, bool with_ui, IPC::Message* reply_message) { |
| ExtensionService* service = profile_->GetExtensionService(); |
| ExtensionProcessManager* manager = profile_->GetExtensionProcessManager(); |
| if (service && manager) { |
| // The observer will delete itself when done. |
| new ExtensionReadyNotificationObserver( |
| manager, |
| this, |
| AutomationMsg_InstallExtensionAndGetHandle::ID, |
| reply_message); |
| |
| ExtensionInstallUI* client = |
| (with_ui ? new ExtensionInstallUI(profile_) : NULL); |
| scoped_refptr<CrxInstaller> installer(new CrxInstaller(service, client)); |
| installer->InstallCrx(crx_path); |
| } else { |
| AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams( |
| reply_message, 0); |
| Send(reply_message); |
| } |
| } |
| |
| void AutomationProvider::UninstallExtension(int extension_handle, |
| bool* success) { |
| *success = false; |
| const Extension* extension = GetExtension(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (extension && service) { |
| ExtensionUnloadNotificationObserver observer; |
| service->UninstallExtension(extension->id(), false, NULL); |
| // The extension unload notification should have been sent synchronously |
| // with the uninstall. Just to be safe, check that it was received. |
| *success = observer.did_receive_unload_notification(); |
| } |
| } |
| |
| void AutomationProvider::EnableExtension(int extension_handle, |
| IPC::Message* reply_message) { |
| const Extension* extension = GetDisabledExtension(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| ExtensionProcessManager* manager = profile_->GetExtensionProcessManager(); |
| // Only enable if this extension is disabled. |
| if (extension && service && manager) { |
| // The observer will delete itself when done. |
| new ExtensionReadyNotificationObserver( |
| manager, |
| this, |
| AutomationMsg_EnableExtension::ID, |
| reply_message); |
| service->EnableExtension(extension->id()); |
| } else { |
| AutomationMsg_EnableExtension::WriteReplyParams(reply_message, false); |
| Send(reply_message); |
| } |
| } |
| |
| void AutomationProvider::DisableExtension(int extension_handle, |
| bool* success) { |
| *success = false; |
| const Extension* extension = GetEnabledExtension(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (extension && service) { |
| ExtensionUnloadNotificationObserver observer; |
| service->DisableExtension(extension->id()); |
| // The extension unload notification should have been sent synchronously |
| // with the disable. Just to be safe, check that it was received. |
| *success = observer.did_receive_unload_notification(); |
| } |
| } |
| |
| void AutomationProvider::ExecuteExtensionActionInActiveTabAsync( |
| int extension_handle, int browser_handle, |
| IPC::Message* reply_message) { |
| bool success = false; |
| const Extension* extension = GetEnabledExtension(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| ExtensionMessageService* message_service = |
| profile_->GetExtensionMessageService(); |
| Browser* browser = browser_tracker_->GetResource(browser_handle); |
| if (extension && service && message_service && browser) { |
| int tab_id = ExtensionTabUtil::GetTabId(browser->GetSelectedTabContents()); |
| if (extension->page_action()) { |
| service->browser_event_router()->PageActionExecuted( |
| browser->profile(), extension->id(), "action", tab_id, "", 1); |
| success = true; |
| } else if (extension->browser_action()) { |
| service->browser_event_router()->BrowserActionExecuted( |
| browser->profile(), extension->id(), browser); |
| success = true; |
| } |
| } |
| AutomationMsg_ExecuteExtensionActionInActiveTabAsync::WriteReplyParams( |
| reply_message, success); |
| Send(reply_message); |
| } |
| |
| void AutomationProvider::MoveExtensionBrowserAction( |
| int extension_handle, int index, bool* success) { |
| *success = false; |
| const Extension* extension = GetEnabledExtension(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (extension && service) { |
| ExtensionToolbarModel* toolbar = service->toolbar_model(); |
| if (toolbar) { |
| if (index >= 0 && index < static_cast<int>(toolbar->size())) { |
| toolbar->MoveBrowserAction(extension, index); |
| *success = true; |
| } else { |
| DLOG(WARNING) << "Attempted to move browser action to invalid index."; |
| } |
| } |
| } |
| } |
| |
| void AutomationProvider::GetExtensionProperty( |
| int extension_handle, |
| AutomationMsg_ExtensionProperty type, |
| bool* success, |
| std::string* value) { |
| *success = false; |
| const Extension* extension = GetExtension(extension_handle); |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (extension && service) { |
| ExtensionToolbarModel* toolbar = service->toolbar_model(); |
| int found_index = -1; |
| int index = 0; |
| switch (type) { |
| case AUTOMATION_MSG_EXTENSION_ID: |
| *value = extension->id(); |
| *success = true; |
| break; |
| case AUTOMATION_MSG_EXTENSION_NAME: |
| *value = extension->name(); |
| *success = true; |
| break; |
| case AUTOMATION_MSG_EXTENSION_VERSION: |
| *value = extension->VersionString(); |
| *success = true; |
| break; |
| case AUTOMATION_MSG_EXTENSION_BROWSER_ACTION_INDEX: |
| if (toolbar) { |
| for (ExtensionList::const_iterator iter = toolbar->begin(); |
| iter != toolbar->end(); iter++) { |
| // Skip this extension if we are in incognito mode |
| // and it is not incognito-enabled. |
| if (profile_->IsOffTheRecord() && |
| !service->IsIncognitoEnabled((*iter)->id())) |
| continue; |
| if (*iter == extension) { |
| found_index = index; |
| break; |
| } |
| index++; |
| } |
| *value = base::IntToString(found_index); |
| *success = true; |
| } |
| break; |
| default: |
| LOG(WARNING) << "Trying to get undefined extension property"; |
| break; |
| } |
| } |
| } |
| |
| void AutomationProvider::SaveAsAsync(int tab_handle) { |
| NavigationController* tab = NULL; |
| TabContents* tab_contents = GetTabContentsForHandle(tab_handle, &tab); |
| if (tab_contents) { |
| TabContentsWrapper* wrapper = |
| TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); |
| wrapper->download_tab_helper()->OnSavePage(); |
| } |
| } |