| // 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/browser_process_impl.h" |
| |
| #include <map> |
| |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/path_service.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "chrome/browser/automation/automation_provider_list.h" |
| #include "chrome/browser/browser_main.h" |
| #include "chrome/browser/browser_process_sub_thread.h" |
| #include "chrome/browser/browser_trial.h" |
| #include "chrome/browser/debugger/browser_list_tabcontents_provider.h" |
| #include "chrome/browser/debugger/devtools_http_protocol_handler.h" |
| #include "chrome/browser/debugger/devtools_manager.h" |
| #include "chrome/browser/debugger/devtools_protocol_handler.h" |
| #include "chrome/browser/download/download_file_manager.h" |
| #include "chrome/browser/download/save_file_manager.h" |
| #include "chrome/browser/extensions/extension_event_router_forwarder.h" |
| #include "chrome/browser/extensions/extension_tab_id_map.h" |
| #include "chrome/browser/extensions/user_script_listener.h" |
| #include "chrome/browser/first_run/upgrade_util.h" |
| #include "chrome/browser/google/google_url_tracker.h" |
| #include "chrome/browser/gpu_process_host_ui_shim.h" |
| #include "chrome/browser/icon_manager.h" |
| #include "chrome/browser/intranet_redirect_detector.h" |
| #include "chrome/browser/io_thread.h" |
| #include "chrome/browser/metrics/metrics_service.h" |
| #include "chrome/browser/metrics/thread_watcher.h" |
| #include "chrome/browser/net/chrome_net_log.h" |
| #include "chrome/browser/net/predictor_api.h" |
| #include "chrome/browser/net/sdch_dictionary_fetcher.h" |
| #include "chrome/browser/notifications/notification_ui_manager.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/plugin_data_remover.h" |
| #include "chrome/browser/plugin_updater.h" |
| #include "chrome/browser/policy/browser_policy_connector.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/printing/print_job_manager.h" |
| #include "chrome/browser/printing/print_preview_tab_controller.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/safe_browsing/client_side_detection_service.h" |
| #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
| #include "chrome/browser/shell_integration.h" |
| #include "chrome/browser/sidebar/sidebar_manager.h" |
| #include "chrome/browser/tab_closeable_state_watcher.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension_l10n_util.h" |
| #include "chrome/common/extensions/extension_resource.h" |
| #include "chrome/common/json_pref_store.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/switch_utils.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/installer/util/google_update_constants.h" |
| #include "content/browser/browser_child_process_host.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/child_process_security_policy.h" |
| #include "content/browser/plugin_service.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "content/browser/renderer_host/resource_dispatcher_host.h" |
| #include "content/common/notification_service.h" |
| #include "ipc/ipc_logging.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "ui/base/clipboard/clipboard.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "webkit/database/database_tracker.h" |
| |
| #if defined(OS_WIN) |
| #include "views/focus/view_storage.h" |
| #endif |
| |
| #if defined(IPC_MESSAGE_LOG_ENABLED) |
| #include "content/common/child_process_messages.h" |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/proxy_config_service_impl.h" |
| #endif // defined(OS_CHROMEOS) |
| |
| #if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) |
| // How often to check if the persistent instance of Chrome needs to restart |
| // to install an update. |
| static const int kUpdateCheckIntervalHours = 6; |
| #endif |
| |
| #if defined(USE_X11) |
| // How long to wait for the File thread to complete during EndSession, on |
| // Linux. We have a timeout here because we're unable to run the UI messageloop |
| // and there's some deadlock risk. Our only option is to exit anyway. |
| static const int kEndSessionTimeoutSeconds = 10; |
| #endif |
| |
| BrowserProcessImpl::BrowserProcessImpl(const CommandLine& command_line) |
| : created_resource_dispatcher_host_(false), |
| created_metrics_service_(false), |
| created_io_thread_(false), |
| created_file_thread_(false), |
| created_db_thread_(false), |
| created_process_launcher_thread_(false), |
| created_cache_thread_(false), |
| created_gpu_thread_(false), |
| created_watchdog_thread_(false), |
| created_profile_manager_(false), |
| created_local_state_(false), |
| created_icon_manager_(false), |
| created_devtools_manager_(false), |
| created_sidebar_manager_(false), |
| created_browser_policy_connector_(false), |
| created_notification_ui_manager_(false), |
| created_safe_browsing_detection_service_(false), |
| module_ref_count_(0), |
| did_start_(false), |
| checked_for_new_frames_(false), |
| using_new_frames_(false) { |
| g_browser_process = this; |
| clipboard_.reset(new ui::Clipboard); |
| main_notification_service_.reset(new NotificationService); |
| |
| notification_registrar_.Add(this, |
| NotificationType::APP_TERMINATING, |
| NotificationService::AllSources()); |
| |
| // Must be created after the NotificationService. |
| print_job_manager_.reset(new printing::PrintJobManager); |
| |
| shutdown_event_.reset(new base::WaitableEvent(true, false)); |
| |
| net_log_.reset(new ChromeNetLog); |
| |
| extension_event_router_forwarder_ = new ExtensionEventRouterForwarder; |
| |
| ExtensionTabIdMap::GetInstance()->Init(); |
| } |
| |
| BrowserProcessImpl::~BrowserProcessImpl() { |
| FilePath profile_path; |
| bool clear_local_state_on_exit; |
| |
| // Store the profile path for clearing local state data on exit. |
| clear_local_state_on_exit = ShouldClearLocalState(&profile_path); |
| |
| // Delete the AutomationProviderList before NotificationService, |
| // since it may try to unregister notifications |
| // Both NotificationService and AutomationProvider are singleton instances in |
| // the BrowserProcess. Since AutomationProvider may have some active |
| // notification observers, it is essential that it gets destroyed before the |
| // NotificationService. NotificationService won't be destroyed until after |
| // this destructor is run. |
| automation_provider_list_.reset(); |
| |
| // We need to shutdown the SdchDictionaryFetcher as it regularly holds |
| // a pointer to a URLFetcher, and that URLFetcher (upon destruction) will do |
| // a PostDelayedTask onto the IO thread. This shutdown call will both discard |
| // any pending URLFetchers, and avoid creating any more. |
| SdchDictionaryFetcher::Shutdown(); |
| |
| // We need to destroy the MetricsService, GoogleURLTracker, |
| // IntranetRedirectDetector, and SafeBrowsing ClientSideDetectionService |
| // before the io_thread_ gets destroyed, since their destructors can call the |
| // URLFetcher destructor, which does a PostDelayedTask operation on the IO |
| // thread. (The IO thread will handle that URLFetcher operation before going |
| // away.) |
| metrics_service_.reset(); |
| google_url_tracker_.reset(); |
| intranet_redirect_detector_.reset(); |
| safe_browsing_detection_service_.reset(); |
| |
| // Need to clear the desktop notification balloons before the io_thread_ and |
| // before the profiles, since if there are any still showing we will access |
| // those things during teardown. |
| notification_ui_manager_.reset(); |
| |
| // Need to clear profiles (download managers) before the io_thread_. |
| profile_manager_.reset(); |
| |
| // Debugger must be cleaned up before IO thread and NotificationService. |
| if (devtools_http_handler_.get()) { |
| devtools_http_handler_->Stop(); |
| devtools_http_handler_ = NULL; |
| } |
| if (devtools_legacy_handler_.get()) { |
| devtools_legacy_handler_->Stop(); |
| devtools_legacy_handler_ = NULL; |
| } |
| |
| if (resource_dispatcher_host_.get()) { |
| // Need to tell Safe Browsing Service that the IO thread is going away |
| // since it cached a pointer to it. |
| if (resource_dispatcher_host()->safe_browsing_service()) |
| resource_dispatcher_host()->safe_browsing_service()->ShutDown(); |
| |
| // Cancel pending requests and prevent new requests. |
| resource_dispatcher_host()->Shutdown(); |
| } |
| |
| ExtensionTabIdMap::GetInstance()->Shutdown(); |
| |
| // The policy providers managed by |browser_policy_connector_| need to shut |
| // down while the IO and FILE threads are still alive. |
| browser_policy_connector_.reset(); |
| |
| #if defined(USE_X11) |
| // The IO thread must outlive the BACKGROUND_X11 thread. |
| background_x11_thread_.reset(); |
| #endif |
| |
| // Wait for removing plugin data to finish before shutting down the IO thread. |
| WaitForPluginDataRemoverToFinish(); |
| |
| // Destroying the GpuProcessHostUIShims on the UI thread posts a task to |
| // delete related objects on the GPU thread. This must be done before |
| // stopping the GPU thread. The GPU thread will close IPC channels to renderer |
| // processes so this has to happen before stopping the IO thread. |
| GpuProcessHostUIShim::DestroyAll(); |
| gpu_thread_.reset(); |
| |
| // Need to stop io_thread_ before resource_dispatcher_host_, since |
| // io_thread_ may still deref ResourceDispatcherHost and handle resource |
| // request before going away. |
| io_thread_.reset(); |
| |
| // The IO thread was the only user of this thread. |
| cache_thread_.reset(); |
| |
| // Stop the process launcher thread after the IO thread, in case the IO thread |
| // posted a task to terminate a process on the process launcher thread. |
| process_launcher_thread_.reset(); |
| |
| // Clean up state that lives on the file_thread_ before it goes away. |
| if (resource_dispatcher_host_.get()) { |
| resource_dispatcher_host()->download_file_manager()->Shutdown(); |
| resource_dispatcher_host()->save_file_manager()->Shutdown(); |
| } |
| |
| // Need to stop the file_thread_ here to force it to process messages in its |
| // message loop from the previous call to shutdown the DownloadFileManager, |
| // SaveFileManager and SessionService. |
| file_thread_.reset(); |
| |
| // With the file_thread_ flushed, we can release any icon resources. |
| icon_manager_.reset(); |
| |
| // Need to destroy ResourceDispatcherHost before PluginService and |
| // SafeBrowsingService, since it caches a pointer to it. This also |
| // causes the webkit thread to terminate. |
| resource_dispatcher_host_.reset(); |
| |
| // Wait for the pending print jobs to finish. |
| print_job_manager_->OnQuit(); |
| print_job_manager_.reset(); |
| |
| // Destroy TabCloseableStateWatcher before NotificationService since the |
| // former registers for notifications. |
| tab_closeable_state_watcher_.reset(); |
| |
| // Now OK to destroy NotificationService. |
| main_notification_service_.reset(); |
| |
| // Prior to clearing local state, we want to complete tasks pending |
| // on the db thread too. |
| db_thread_.reset(); |
| |
| // Stop the watchdog thread after stopping other threads. |
| watchdog_thread_.reset(); |
| |
| // At this point, no render process exist and the file, io, db, and |
| // webkit threads in this process have all terminated, so it's safe |
| // to access local state data such as cookies, database, or local storage. |
| if (clear_local_state_on_exit) |
| ClearLocalState(profile_path); |
| |
| g_browser_process = NULL; |
| } |
| |
| #if defined(OS_WIN) |
| // Send a QuitTask to the given MessageLoop. |
| static void PostQuit(MessageLoop* message_loop) { |
| message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| } |
| #elif defined(USE_X11) |
| static void Signal(base::WaitableEvent* event) { |
| event->Signal(); |
| } |
| #endif |
| |
| unsigned int BrowserProcessImpl::AddRefModule() { |
| DCHECK(CalledOnValidThread()); |
| did_start_ = true; |
| module_ref_count_++; |
| return module_ref_count_; |
| } |
| |
| unsigned int BrowserProcessImpl::ReleaseModule() { |
| DCHECK(CalledOnValidThread()); |
| DCHECK_NE(0u, module_ref_count_); |
| module_ref_count_--; |
| if (0 == module_ref_count_) { |
| // Allow UI and IO threads to do blocking IO on shutdown, since we do a lot |
| // of it on shutdown for valid reasons. |
| base::ThreadRestrictions::SetIOAllowed(true); |
| io_thread()->message_loop()->PostTask( |
| FROM_HERE, |
| NewRunnableFunction(&base::ThreadRestrictions::SetIOAllowed, true)); |
| MessageLoop::current()->PostTask( |
| FROM_HERE, NewRunnableFunction(DidEndMainMessageLoop)); |
| MessageLoop::current()->Quit(); |
| } |
| return module_ref_count_; |
| } |
| |
| void BrowserProcessImpl::EndSession() { |
| #if defined(OS_WIN) || defined(USE_X11) |
| // Notify we are going away. |
| shutdown_event_->Signal(); |
| #endif |
| |
| // Mark all the profiles as clean. |
| ProfileManager* pm = profile_manager(); |
| std::vector<Profile*> profiles(pm->GetLoadedProfiles()); |
| for (size_t i = 0; i < profiles.size(); ++i) |
| profiles[i]->MarkAsCleanShutdown(); |
| |
| // Tell the metrics service it was cleanly shutdown. |
| MetricsService* metrics = g_browser_process->metrics_service(); |
| if (metrics && local_state()) { |
| metrics->RecordStartOfSessionEnd(); |
| |
| // MetricsService lazily writes to prefs, force it to write now. |
| local_state()->SavePersistentPrefs(); |
| } |
| |
| // We must write that the profile and metrics service shutdown cleanly, |
| // otherwise on startup we'll think we crashed. So we block until done and |
| // then proceed with normal shutdown. |
| #if defined(USE_X11) |
| // Can't run a local loop on linux. Instead create a waitable event. |
| base::WaitableEvent done_writing(false, false); |
| BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| NewRunnableFunction(Signal, &done_writing)); |
| done_writing.TimedWait( |
| base::TimeDelta::FromSeconds(kEndSessionTimeoutSeconds)); |
| #elif defined(OS_WIN) |
| BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| NewRunnableFunction(PostQuit, MessageLoop::current())); |
| MessageLoop::current()->Run(); |
| #else |
| NOTIMPLEMENTED(); |
| #endif |
| } |
| |
| ResourceDispatcherHost* BrowserProcessImpl::resource_dispatcher_host() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_resource_dispatcher_host_) |
| CreateResourceDispatcherHost(); |
| return resource_dispatcher_host_.get(); |
| } |
| |
| MetricsService* BrowserProcessImpl::metrics_service() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_metrics_service_) |
| CreateMetricsService(); |
| return metrics_service_.get(); |
| } |
| |
| IOThread* BrowserProcessImpl::io_thread() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_io_thread_) |
| CreateIOThread(); |
| return io_thread_.get(); |
| } |
| |
| base::Thread* BrowserProcessImpl::file_thread() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_file_thread_) |
| CreateFileThread(); |
| return file_thread_.get(); |
| } |
| |
| base::Thread* BrowserProcessImpl::db_thread() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_db_thread_) |
| CreateDBThread(); |
| return db_thread_.get(); |
| } |
| |
| base::Thread* BrowserProcessImpl::process_launcher_thread() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_process_launcher_thread_) |
| CreateProcessLauncherThread(); |
| return process_launcher_thread_.get(); |
| } |
| |
| base::Thread* BrowserProcessImpl::cache_thread() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_cache_thread_) |
| CreateCacheThread(); |
| return cache_thread_.get(); |
| } |
| |
| base::Thread* BrowserProcessImpl::gpu_thread() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_gpu_thread_) |
| CreateGpuThread(); |
| return gpu_thread_.get(); |
| } |
| |
| #if defined(USE_X11) |
| base::Thread* BrowserProcessImpl::background_x11_thread() { |
| DCHECK(CalledOnValidThread()); |
| // The BACKGROUND_X11 thread is created when the IO thread is created. |
| if (!created_io_thread_) |
| CreateIOThread(); |
| return background_x11_thread_.get(); |
| } |
| #endif |
| |
| WatchDogThread* BrowserProcessImpl::watchdog_thread() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_watchdog_thread_) |
| CreateWatchdogThread(); |
| DCHECK(watchdog_thread_.get() != NULL); |
| return watchdog_thread_.get(); |
| } |
| |
| ProfileManager* BrowserProcessImpl::profile_manager() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_profile_manager_) |
| CreateProfileManager(); |
| return profile_manager_.get(); |
| } |
| |
| PrefService* BrowserProcessImpl::local_state() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_local_state_) |
| CreateLocalState(); |
| return local_state_.get(); |
| } |
| |
| DevToolsManager* BrowserProcessImpl::devtools_manager() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_devtools_manager_) |
| CreateDevToolsManager(); |
| return devtools_manager_.get(); |
| } |
| |
| SidebarManager* BrowserProcessImpl::sidebar_manager() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_sidebar_manager_) |
| CreateSidebarManager(); |
| return sidebar_manager_.get(); |
| } |
| |
| ui::Clipboard* BrowserProcessImpl::clipboard() { |
| DCHECK(CalledOnValidThread()); |
| return clipboard_.get(); |
| } |
| |
| net::URLRequestContextGetter* BrowserProcessImpl::system_request_context() { |
| DCHECK(CalledOnValidThread()); |
| return io_thread()->system_url_request_context_getter(); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| chromeos::ProxyConfigServiceImpl* |
| BrowserProcessImpl::chromeos_proxy_config_service_impl() { |
| DCHECK(CalledOnValidThread()); |
| if (!chromeos_proxy_config_service_impl_) { |
| chromeos_proxy_config_service_impl_ = |
| new chromeos::ProxyConfigServiceImpl(); |
| } |
| return chromeos_proxy_config_service_impl_; |
| } |
| #endif // defined(OS_CHROMEOS) |
| |
| ExtensionEventRouterForwarder* |
| BrowserProcessImpl::extension_event_router_forwarder() { |
| return extension_event_router_forwarder_.get(); |
| } |
| |
| NotificationUIManager* BrowserProcessImpl::notification_ui_manager() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_notification_ui_manager_) |
| CreateNotificationUIManager(); |
| return notification_ui_manager_.get(); |
| } |
| |
| policy::BrowserPolicyConnector* BrowserProcessImpl::browser_policy_connector() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_browser_policy_connector_) { |
| DCHECK(browser_policy_connector_.get() == NULL); |
| created_browser_policy_connector_ = true; |
| browser_policy_connector_.reset(new policy::BrowserPolicyConnector()); |
| } |
| return browser_policy_connector_.get(); |
| } |
| |
| IconManager* BrowserProcessImpl::icon_manager() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_icon_manager_) |
| CreateIconManager(); |
| return icon_manager_.get(); |
| } |
| |
| ThumbnailGenerator* BrowserProcessImpl::GetThumbnailGenerator() { |
| return &thumbnail_generator_; |
| } |
| |
| AutomationProviderList* BrowserProcessImpl::InitAutomationProviderList() { |
| DCHECK(CalledOnValidThread()); |
| if (automation_provider_list_.get() == NULL) { |
| automation_provider_list_.reset(AutomationProviderList::GetInstance()); |
| } |
| return automation_provider_list_.get(); |
| } |
| |
| void BrowserProcessImpl::InitDevToolsHttpProtocolHandler( |
| const std::string& ip, |
| int port, |
| const std::string& frontend_url) { |
| DCHECK(CalledOnValidThread()); |
| devtools_http_handler_ = |
| DevToolsHttpProtocolHandler::Start(ip, |
| port, |
| frontend_url, |
| new BrowserListTabContentsProvider()); |
| } |
| |
| void BrowserProcessImpl::InitDevToolsLegacyProtocolHandler(int port) { |
| DCHECK(CalledOnValidThread()); |
| devtools_legacy_handler_ = DevToolsProtocolHandler::Start(port); |
| } |
| |
| bool BrowserProcessImpl::IsShuttingDown() { |
| DCHECK(CalledOnValidThread()); |
| return did_start_ && 0 == module_ref_count_; |
| } |
| |
| printing::PrintJobManager* BrowserProcessImpl::print_job_manager() { |
| // TODO(abarth): DCHECK(CalledOnValidThread()); |
| // http://code.google.com/p/chromium/issues/detail?id=6828 |
| // print_job_manager_ is initialized in the constructor and destroyed in the |
| // destructor, so it should always be valid. |
| DCHECK(print_job_manager_.get()); |
| return print_job_manager_.get(); |
| } |
| |
| printing::PrintPreviewTabController* |
| BrowserProcessImpl::print_preview_tab_controller() { |
| DCHECK(CalledOnValidThread()); |
| if (!print_preview_tab_controller_.get()) |
| CreatePrintPreviewTabController(); |
| return print_preview_tab_controller_.get(); |
| } |
| |
| GoogleURLTracker* BrowserProcessImpl::google_url_tracker() { |
| DCHECK(CalledOnValidThread()); |
| if (!google_url_tracker_.get()) |
| CreateGoogleURLTracker(); |
| return google_url_tracker_.get(); |
| } |
| |
| IntranetRedirectDetector* BrowserProcessImpl::intranet_redirect_detector() { |
| DCHECK(CalledOnValidThread()); |
| if (!intranet_redirect_detector_.get()) |
| CreateIntranetRedirectDetector(); |
| return intranet_redirect_detector_.get(); |
| } |
| |
| const std::string& BrowserProcessImpl::GetApplicationLocale() { |
| DCHECK(!locale_.empty()); |
| return locale_; |
| } |
| |
| void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) { |
| locale_ = locale; |
| extension_l10n_util::SetProcessLocale(locale); |
| } |
| |
| DownloadStatusUpdater* BrowserProcessImpl::download_status_updater() { |
| return &download_status_updater_; |
| } |
| |
| base::WaitableEvent* BrowserProcessImpl::shutdown_event() { |
| return shutdown_event_.get(); |
| } |
| |
| TabCloseableStateWatcher* BrowserProcessImpl::tab_closeable_state_watcher() { |
| DCHECK(CalledOnValidThread()); |
| if (!tab_closeable_state_watcher_.get()) |
| CreateTabCloseableStateWatcher(); |
| return tab_closeable_state_watcher_.get(); |
| } |
| |
| safe_browsing::ClientSideDetectionService* |
| BrowserProcessImpl::safe_browsing_detection_service() { |
| DCHECK(CalledOnValidThread()); |
| if (!created_safe_browsing_detection_service_) { |
| CreateSafeBrowsingDetectionService(); |
| } |
| return safe_browsing_detection_service_.get(); |
| } |
| |
| bool BrowserProcessImpl::plugin_finder_disabled() const { |
| return *plugin_finder_disabled_pref_; |
| } |
| |
| void BrowserProcessImpl::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| if (type == NotificationType::APP_TERMINATING) { |
| Profile* profile = ProfileManager::GetDefaultProfile(); |
| if (profile) { |
| PrefService* prefs = profile->GetPrefs(); |
| if (prefs->GetBoolean(prefs::kClearSiteDataOnExit) && |
| local_state()->GetBoolean(prefs::kClearPluginLSODataEnabled)) { |
| plugin_data_remover_ = new PluginDataRemover(); |
| if (!plugin_data_remover_mime_type().empty()) |
| plugin_data_remover_->set_mime_type(plugin_data_remover_mime_type()); |
| plugin_data_remover_->StartRemoving(base::Time()); |
| } |
| } |
| } else if (type == NotificationType::PREF_CHANGED) { |
| std::string* pref = Details<std::string>(details).ptr(); |
| if (*pref == prefs::kDefaultBrowserSettingEnabled) { |
| if (local_state_->GetBoolean(prefs::kDefaultBrowserSettingEnabled)) |
| ShellIntegration::SetAsDefaultBrowser(); |
| } else if (*pref == prefs::kDisabledSchemes) { |
| ApplyDisabledSchemesPolicy(); |
| } |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void BrowserProcessImpl::WaitForPluginDataRemoverToFinish() { |
| if (plugin_data_remover_.get()) |
| plugin_data_remover_->Wait(); |
| } |
| |
| #if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) |
| void BrowserProcessImpl::StartAutoupdateTimer() { |
| autoupdate_timer_.Start( |
| base::TimeDelta::FromHours(kUpdateCheckIntervalHours), |
| this, |
| &BrowserProcessImpl::OnAutoupdateTimer); |
| } |
| #endif |
| |
| ChromeNetLog* BrowserProcessImpl::net_log() { |
| return net_log_.get(); |
| } |
| |
| void BrowserProcessImpl::ClearLocalState(const FilePath& profile_path) { |
| webkit_database::DatabaseTracker::ClearLocalState(profile_path); |
| } |
| |
| bool BrowserProcessImpl::ShouldClearLocalState(FilePath* profile_path) { |
| FilePath user_data_dir; |
| Profile* profile; |
| |
| // Check for the existence of a profile manager. When quitting early, |
| // e.g. because another chrome instance is running, or when invoked with |
| // options such as --uninstall or --try-chrome-again=0, the profile manager |
| // does not exist yet. |
| if (!profile_manager_.get()) |
| return false; |
| |
| PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); |
| profile = profile_manager_->GetDefaultProfile(user_data_dir); |
| if (!profile) |
| return false; |
| *profile_path = profile->GetPath(); |
| return profile->GetPrefs()->GetBoolean(prefs::kClearSiteDataOnExit); |
| } |
| |
| void BrowserProcessImpl::CreateResourceDispatcherHost() { |
| DCHECK(!created_resource_dispatcher_host_ && |
| resource_dispatcher_host_.get() == NULL); |
| created_resource_dispatcher_host_ = true; |
| |
| // UserScriptListener will delete itself. |
| ResourceQueue::DelegateSet resource_queue_delegates; |
| resource_queue_delegates.insert(new UserScriptListener()); |
| |
| resource_dispatcher_host_.reset( |
| new ResourceDispatcherHost(resource_queue_delegates)); |
| resource_dispatcher_host_->Initialize(); |
| } |
| |
| void BrowserProcessImpl::CreateMetricsService() { |
| DCHECK(!created_metrics_service_ && metrics_service_.get() == NULL); |
| created_metrics_service_ = true; |
| |
| metrics_service_.reset(new MetricsService); |
| } |
| |
| void BrowserProcessImpl::CreateIOThread() { |
| DCHECK(!created_io_thread_ && io_thread_.get() == NULL); |
| created_io_thread_ = true; |
| |
| // Prior to starting the io thread, we create the plugin service as |
| // it is predominantly used from the io thread, but must be created |
| // on the main thread. The service ctor is inexpensive and does not |
| // invoke the io_thread() accessor. |
| PluginService::GetInstance(); |
| |
| #if defined(USE_X11) |
| // The lifetime of the BACKGROUND_X11 thread is a subset of the IO thread so |
| // we start it now. |
| scoped_ptr<base::Thread> background_x11_thread( |
| new BrowserProcessSubThread(BrowserThread::BACKGROUND_X11)); |
| if (!background_x11_thread->Start()) |
| return; |
| background_x11_thread_.swap(background_x11_thread); |
| #endif |
| |
| scoped_ptr<IOThread> thread(new IOThread( |
| local_state(), net_log_.get(), extension_event_router_forwarder_.get())); |
| base::Thread::Options options; |
| options.message_loop_type = MessageLoop::TYPE_IO; |
| if (!thread->StartWithOptions(options)) |
| return; |
| io_thread_.swap(thread); |
| } |
| |
| void BrowserProcessImpl::CreateFileThread() { |
| DCHECK(!created_file_thread_ && file_thread_.get() == NULL); |
| created_file_thread_ = true; |
| |
| scoped_ptr<base::Thread> thread( |
| new BrowserProcessSubThread(BrowserThread::FILE)); |
| base::Thread::Options options; |
| #if defined(OS_WIN) |
| // On Windows, the FILE thread needs to be have a UI message loop which pumps |
| // messages in such a way that Google Update can communicate back to us. |
| options.message_loop_type = MessageLoop::TYPE_UI; |
| #else |
| options.message_loop_type = MessageLoop::TYPE_IO; |
| #endif |
| if (!thread->StartWithOptions(options)) |
| return; |
| file_thread_.swap(thread); |
| } |
| |
| void BrowserProcessImpl::CreateDBThread() { |
| DCHECK(!created_db_thread_ && db_thread_.get() == NULL); |
| created_db_thread_ = true; |
| |
| scoped_ptr<base::Thread> thread( |
| new BrowserProcessSubThread(BrowserThread::DB)); |
| if (!thread->Start()) |
| return; |
| db_thread_.swap(thread); |
| } |
| |
| void BrowserProcessImpl::CreateProcessLauncherThread() { |
| DCHECK(!created_process_launcher_thread_ && !process_launcher_thread_.get()); |
| created_process_launcher_thread_ = true; |
| |
| scoped_ptr<base::Thread> thread( |
| new BrowserProcessSubThread(BrowserThread::PROCESS_LAUNCHER)); |
| if (!thread->Start()) |
| return; |
| process_launcher_thread_.swap(thread); |
| } |
| |
| void BrowserProcessImpl::CreateCacheThread() { |
| DCHECK(!created_cache_thread_ && !cache_thread_.get()); |
| created_cache_thread_ = true; |
| |
| scoped_ptr<base::Thread> thread( |
| new BrowserThread(BrowserThread::CACHE)); |
| base::Thread::Options options; |
| options.message_loop_type = MessageLoop::TYPE_IO; |
| if (!thread->StartWithOptions(options)) |
| return; |
| cache_thread_.swap(thread); |
| } |
| |
| void BrowserProcessImpl::CreateGpuThread() { |
| DCHECK(!created_gpu_thread_ && !gpu_thread_.get()); |
| created_gpu_thread_ = true; |
| |
| scoped_ptr<base::Thread> thread(new BrowserThread(BrowserThread::GPU)); |
| |
| base::Thread::Options options; |
| #if defined(OS_WIN) |
| // On Windows the GPU thread needs to pump the compositor child window's |
| // message loop. TODO(apatrick): make this an IO thread if / when we get rid |
| // of this child window. Unfortunately it might always be necessary for |
| // Windows XP because we cannot share the backing store textures between |
| // processes. |
| options.message_loop_type = MessageLoop::TYPE_UI; |
| #else |
| options.message_loop_type = MessageLoop::TYPE_IO; |
| #endif |
| |
| if (!thread->StartWithOptions(options)) |
| return; |
| gpu_thread_.swap(thread); |
| } |
| |
| void BrowserProcessImpl::CreateWatchdogThread() { |
| DCHECK(!created_watchdog_thread_ && watchdog_thread_.get() == NULL); |
| created_watchdog_thread_ = true; |
| |
| scoped_ptr<WatchDogThread> thread(new WatchDogThread()); |
| if (!thread->Start()) |
| return; |
| watchdog_thread_.swap(thread); |
| } |
| |
| void BrowserProcessImpl::CreateProfileManager() { |
| DCHECK(!created_profile_manager_ && profile_manager_.get() == NULL); |
| created_profile_manager_ = true; |
| |
| profile_manager_.reset(new ProfileManager()); |
| } |
| |
| void BrowserProcessImpl::CreateLocalState() { |
| DCHECK(!created_local_state_ && local_state_.get() == NULL); |
| created_local_state_ = true; |
| |
| FilePath local_state_path; |
| PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path); |
| local_state_.reset( |
| PrefService::CreatePrefService(local_state_path, NULL, NULL)); |
| |
| pref_change_registrar_.Init(local_state_.get()); |
| |
| // Make sure the the plugin updater gets notifications of changes |
| // in the plugin policy lists. |
| local_state_->RegisterListPref(prefs::kPluginsDisabledPlugins); |
| pref_change_registrar_.Add(prefs::kPluginsDisabledPlugins, |
| PluginUpdater::GetInstance()); |
| local_state_->RegisterListPref(prefs::kPluginsDisabledPluginsExceptions); |
| pref_change_registrar_.Add(prefs::kPluginsDisabledPluginsExceptions, |
| PluginUpdater::GetInstance()); |
| local_state_->RegisterListPref(prefs::kPluginsEnabledPlugins); |
| pref_change_registrar_.Add(prefs::kPluginsEnabledPlugins, |
| PluginUpdater::GetInstance()); |
| |
| // Initialize and set up notifications for the printing enabled |
| // preference. |
| local_state_->RegisterBooleanPref(prefs::kPrintingEnabled, true); |
| bool printing_enabled = |
| local_state_->GetBoolean(prefs::kPrintingEnabled); |
| print_job_manager_->set_printing_enabled(printing_enabled); |
| pref_change_registrar_.Add(prefs::kPrintingEnabled, |
| print_job_manager_.get()); |
| |
| // Initialize the notification for the default browser setting policy. |
| local_state_->RegisterBooleanPref(prefs::kDefaultBrowserSettingEnabled, |
| false); |
| if (local_state_->IsManagedPreference(prefs::kDefaultBrowserSettingEnabled)) { |
| if (local_state_->GetBoolean(prefs::kDefaultBrowserSettingEnabled)) |
| ShellIntegration::SetAsDefaultBrowser(); |
| } |
| pref_change_registrar_.Add(prefs::kDefaultBrowserSettingEnabled, this); |
| |
| // Initialize the preference for the plugin finder policy. |
| // This preference is only needed on the IO thread so make it available there. |
| local_state_->RegisterBooleanPref(prefs::kDisablePluginFinder, false); |
| plugin_finder_disabled_pref_.Init(prefs::kDisablePluginFinder, |
| local_state_.get(), NULL); |
| plugin_finder_disabled_pref_.MoveToThread(BrowserThread::IO); |
| |
| // Initialize the preference for the disabled schemes policy, and |
| // load the initial policy on startup. |
| local_state_->RegisterListPref(prefs::kDisabledSchemes); |
| disabled_schemes_pref_.Init(prefs::kDisabledSchemes, local_state_.get(), |
| this); |
| ApplyDisabledSchemesPolicy(); |
| } |
| |
| void BrowserProcessImpl::CreateIconManager() { |
| DCHECK(!created_icon_manager_ && icon_manager_.get() == NULL); |
| created_icon_manager_ = true; |
| icon_manager_.reset(new IconManager); |
| } |
| |
| void BrowserProcessImpl::CreateDevToolsManager() { |
| DCHECK(devtools_manager_.get() == NULL); |
| created_devtools_manager_ = true; |
| devtools_manager_ = new DevToolsManager(); |
| } |
| |
| void BrowserProcessImpl::CreateSidebarManager() { |
| DCHECK(sidebar_manager_.get() == NULL); |
| created_sidebar_manager_ = true; |
| sidebar_manager_ = new SidebarManager(); |
| } |
| |
| void BrowserProcessImpl::CreateGoogleURLTracker() { |
| DCHECK(google_url_tracker_.get() == NULL); |
| scoped_ptr<GoogleURLTracker> google_url_tracker(new GoogleURLTracker); |
| google_url_tracker_.swap(google_url_tracker); |
| } |
| |
| void BrowserProcessImpl::CreateIntranetRedirectDetector() { |
| DCHECK(intranet_redirect_detector_.get() == NULL); |
| scoped_ptr<IntranetRedirectDetector> intranet_redirect_detector( |
| new IntranetRedirectDetector); |
| intranet_redirect_detector_.swap(intranet_redirect_detector); |
| } |
| |
| void BrowserProcessImpl::CreateNotificationUIManager() { |
| DCHECK(notification_ui_manager_.get() == NULL); |
| notification_ui_manager_.reset(NotificationUIManager::Create(local_state())); |
| |
| created_notification_ui_manager_ = true; |
| } |
| |
| void BrowserProcessImpl::CreateTabCloseableStateWatcher() { |
| DCHECK(tab_closeable_state_watcher_.get() == NULL); |
| tab_closeable_state_watcher_.reset(TabCloseableStateWatcher::Create()); |
| } |
| |
| void BrowserProcessImpl::CreatePrintPreviewTabController() { |
| DCHECK(print_preview_tab_controller_.get() == NULL); |
| print_preview_tab_controller_ = new printing::PrintPreviewTabController(); |
| } |
| |
| void BrowserProcessImpl::CreateSafeBrowsingDetectionService() { |
| DCHECK(safe_browsing_detection_service_.get() == NULL); |
| // Set this flag to true so that we don't retry indefinitely to |
| // create the service class if there was an error. |
| created_safe_browsing_detection_service_ = true; |
| |
| FilePath model_file_path; |
| Profile* profile = profile_manager() ? |
| profile_manager()->GetDefaultProfile() : NULL; |
| if (IsSafeBrowsingDetectionServiceEnabled() && |
| PathService::Get(chrome::DIR_USER_DATA, &model_file_path) && |
| profile && profile->GetRequestContext()) { |
| safe_browsing_detection_service_.reset( |
| safe_browsing::ClientSideDetectionService::Create( |
| model_file_path.Append(chrome::kSafeBrowsingPhishingModelFilename), |
| profile->GetRequestContext())); |
| } |
| } |
| |
| bool BrowserProcessImpl::IsSafeBrowsingDetectionServiceEnabled() { |
| // The safe browsing client-side detection is enabled only if the switch is |
| // enabled and when safe browsing related stats is allowed to be collected. |
| // For now we only enable client-side detection on the canary, dev and beta |
| // channel. |
| #ifdef OS_CHROMEOS |
| return false; |
| #else |
| std::string channel = platform_util::GetVersionStringModifier(); |
| return !CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableClientSidePhishingDetection) && |
| resource_dispatcher_host()->safe_browsing_service() && |
| resource_dispatcher_host()->safe_browsing_service()->CanReportStats() && |
| // TODO(noelutz): use platform_util::GetChannel() once it has been |
| // pushed to the release branch. |
| (channel == "beta" || channel == "dev" || channel == "canary" || |
| channel == "beta-m" || channel == "dev-m" || channel == "canary-m"); |
| |
| #endif |
| } |
| |
| void BrowserProcessImpl::ApplyDisabledSchemesPolicy() { |
| std::set<std::string> schemes; |
| for (ListValue::const_iterator iter = (*disabled_schemes_pref_)->begin(); |
| iter != (*disabled_schemes_pref_)->end(); ++iter) { |
| std::string scheme; |
| if ((*iter)->GetAsString(&scheme)) |
| schemes.insert(scheme); |
| } |
| ChildProcessSecurityPolicy::GetInstance()->RegisterDisabledSchemes(schemes); |
| } |
| |
| // The BrowserProcess object must outlive the file thread so we use traits |
| // which don't do any management. |
| DISABLE_RUNNABLE_METHOD_REFCOUNT(BrowserProcessImpl); |
| |
| #if defined(IPC_MESSAGE_LOG_ENABLED) |
| |
| void BrowserProcessImpl::SetIPCLoggingEnabled(bool enable) { |
| // First enable myself. |
| if (enable) |
| IPC::Logging::GetInstance()->Enable(); |
| else |
| IPC::Logging::GetInstance()->Disable(); |
| |
| // Now tell subprocesses. Messages to ChildProcess-derived |
| // processes must be done on the IO thread. |
| io_thread()->message_loop()->PostTask |
| (FROM_HERE, |
| NewRunnableMethod( |
| this, |
| &BrowserProcessImpl::SetIPCLoggingEnabledForChildProcesses, |
| enable)); |
| |
| // Finally, tell the renderers which don't derive from ChildProcess. |
| // Messages to the renderers must be done on the UI (main) thread. |
| for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator()); |
| !i.IsAtEnd(); i.Advance()) |
| i.GetCurrentValue()->Send(new ChildProcessMsg_SetIPCLoggingEnabled(enable)); |
| } |
| |
| // Helper for SetIPCLoggingEnabled. |
| void BrowserProcessImpl::SetIPCLoggingEnabledForChildProcesses(bool enabled) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| BrowserChildProcessHost::Iterator i; // default constr references a singleton |
| while (!i.Done()) { |
| i->Send(new ChildProcessMsg_SetIPCLoggingEnabled(enabled)); |
| ++i; |
| } |
| } |
| |
| #endif // IPC_MESSAGE_LOG_ENABLED |
| |
| // Mac is currently not supported. |
| #if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) |
| |
| bool BrowserProcessImpl::CanAutorestartForUpdate() const { |
| // Check if browser is in the background and if it needs to be restarted to |
| // apply a pending update. |
| return BrowserList::size() == 0 && BrowserList::WillKeepAlive() && |
| upgrade_util::IsUpdatePendingRestart(); |
| } |
| |
| // Switches to add when auto-restarting Chrome. |
| const char* const kSwitchesToAddOnAutorestart[] = { |
| switches::kNoStartupWindow |
| }; |
| |
| void BrowserProcessImpl::RestartPersistentInstance() { |
| CommandLine* old_cl = CommandLine::ForCurrentProcess(); |
| scoped_ptr<CommandLine> new_cl(new CommandLine(old_cl->GetProgram())); |
| |
| std::map<std::string, CommandLine::StringType> switches = |
| old_cl->GetSwitches(); |
| |
| switches::RemoveSwitchesForAutostart(&switches); |
| |
| // Append the rest of the switches (along with their values, if any) |
| // to the new command line |
| for (std::map<std::string, CommandLine::StringType>::const_iterator i = |
| switches.begin(); i != switches.end(); ++i) { |
| CommandLine::StringType switch_value = i->second; |
| if (switch_value.length() > 0) { |
| new_cl->AppendSwitchNative(i->first, i->second); |
| } else { |
| new_cl->AppendSwitch(i->first); |
| } |
| } |
| |
| // Ensure that our desired switches are set on the new process. |
| for (size_t i = 0; i < arraysize(kSwitchesToAddOnAutorestart); ++i) { |
| if (!new_cl->HasSwitch(kSwitchesToAddOnAutorestart[i])) |
| new_cl->AppendSwitch(kSwitchesToAddOnAutorestart[i]); |
| } |
| |
| DLOG(WARNING) << "Shutting down current instance of the browser."; |
| BrowserList::CloseAllBrowsersAndExit(); |
| |
| // Transfer ownership to Upgrade. |
| upgrade_util::SetNewCommandLine(new_cl.release()); |
| } |
| |
| void BrowserProcessImpl::OnAutoupdateTimer() { |
| if (CanAutorestartForUpdate()) { |
| DLOG(WARNING) << "Detected update. Restarting browser."; |
| RestartPersistentInstance(); |
| } |
| } |
| |
| #endif // (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) |