1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_list.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h" 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h" 9731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/metrics/histogram.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "build/build_config.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_process.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_shutdown.h" 1321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile_manager.h" 14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_window.h" 15dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_process_host.h" 16dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/navigation_controller.h" 17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_registrar.h" 18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h" 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/result_codes.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_MACOSX) 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chrome_browser_application_mac.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 25201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#if defined(OS_CHROMEOS) 26201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/chromeos/boot_times_loader.h" 2721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/chromeos/cros/cros_library.h" 2821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/chromeos/cros/login_library.h" 2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/chromeos/cros/update_library.h" 303f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/browser/chromeos/wm_ipc.h" 31201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#endif 32201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// This object is instantiated when the first Browser object is added to the 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// list and delete when the last one is removed. It watches for loads and 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// creates histograms of some global object counts. 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass BrowserActivityObserver : public NotificationObserver { 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public: 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BrowserActivityObserver() { 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::AllSources()); 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ~BrowserActivityObserver() {} 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private: 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NotificationObserver implementation. 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch virtual void Observe(NotificationType type, 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationSource& source, 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationDetails& details) { 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(type == NotificationType::NAV_ENTRY_COMMITTED); 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NavigationController::LoadCommittedDetails& load = 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *Details<NavigationController::LoadCommittedDetails>(details).ptr(); 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!load.is_main_frame || load.is_auto || load.is_in_page) 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // Don't log for subframes or other trivial types. 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LogRenderProcessHostCount(); 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LogBrowserTabCount(); 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Counts the number of active RenderProcessHosts and logs them. 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch void LogRenderProcessHostCount() const { 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int hosts_count = 0; 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator()); 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !i.IsAtEnd(); i.Advance()) 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++hosts_count; 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UMA_HISTOGRAM_CUSTOM_COUNTS("MPArch.RPHCountPerLoad", hosts_count, 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1, 50, 50); 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Counts the number of tabs in each browser window and logs them. This is 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // different than the number of TabContents objects since TabContents objects 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // can be used for popups and in dialog boxes. We're just counting toplevel 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // tabs here. 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch void LogBrowserTabCount() const { 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int tab_count = 0; 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); 784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch browser_iterator != BrowserList::end(); browser_iterator++) { 794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch // Record how many tabs each window has open. 804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerWindow", 814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch (*browser_iterator)->tab_count(), 1, 200, 50); 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_count += (*browser_iterator)->tab_count(); 834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch } 844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch // Record how many tabs total are open (across all windows). 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tab_count, 1, 200, 50); 864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Browser* browser = BrowserList::GetLastActive(); 884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if (browser) { 894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch // Record how many tabs the active window has open. 904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountActiveWindow", 914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch browser->tab_count(), 1, 200, 50); 924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch } 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationRegistrar registrar_; 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DISALLOW_COPY_AND_ASSIGN(BrowserActivityObserver); 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowserActivityObserver* activity_observer = NULL; 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Type used to indicate only the type should be matched. 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMatchNothing = 0; 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// See BrowserMatches for details. 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMatchOriginalProfile = 1 << 0; 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// See BrowserMatches for details. 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMatchCanSupportWindowFeature = 1 << 1; 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Returns true if the specified |browser| matches the specified arguments. 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// |match_types| is a bitmask dictating what parameters to match: 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// . If it contains kMatchOriginalProfile then the original profile of the 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// browser must match |profile->GetOriginalProfile()|. This is used to match 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// incognito windows. 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// . If it contains kMatchCanSupportWindowFeature 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// |CanSupportWindowFeature(window_feature)| must return true. 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BrowserMatches(Browser* browser, 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Profile* profile, 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::Type type, 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::WindowFeature window_feature, 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch uint32 match_types) { 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (match_types & kMatchCanSupportWindowFeature && 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !browser->CanSupportWindowFeature(window_feature)) { 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (match_types & kMatchOriginalProfile) { 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (browser->profile()->GetOriginalProfile() != 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile->GetOriginalProfile()) 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (browser->profile() != profile) { 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (type != Browser::TYPE_ANY && browser->type() != type) 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Returns the first browser in the specified iterator that returns true from 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// |BrowserMatches|, or null if no browsers match the arguments. See 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// |BrowserMatches| for details on the arguments. 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochtemplate <class T> 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowser* FindBrowserMatching(const T& begin, 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const T& end, 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Profile* profile, 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::Type type, 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::WindowFeature window_feature, 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch uint32 match_types) { 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (T i = begin; i != end; ++i) { 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (BrowserMatches(*i, profile, type, window_feature, match_types)) 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return *i; 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowserList::BrowserVector BrowserList::browsers_; 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochObserverList<BrowserList::Observer> BrowserList::observers_; 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::AddBrowser(Browser* browser) { 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(browser); 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browsers_.push_back(browser); 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->AddRefModule(); 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!activity_observer) 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch activity_observer = new BrowserActivityObserver; 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::current()->Notify( 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationType::BROWSER_OPENED, 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<Browser>(browser), 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::NoDetails()); 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send out notifications after add has occurred. Do some basic checking to 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // try to catch evil observers that change the list from under us. 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t original_count = observers_.size(); 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FOR_EACH_OBSERVER(Observer, observers_, OnBrowserAdded(browser)); 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK_EQ(original_count, observers_.size()) 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << "observer list modified during notification"; 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static 18872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid BrowserList::MarkAsCleanShutdown() { 18972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for (const_iterator i = begin(); i != end(); ++i) { 19072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen (*i)->profile()->MarkAsCleanShutdown(); 19172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#if defined(OS_CHROMEOS) 19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static 19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid BrowserList::NotifyWindowManagerAboutSignout() { 19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen static bool notified = false; 19872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!notified) { 19972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Let the window manager know that we're going away before we start closing 20072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // windows so it can display a graceful transition to a black screen. 20172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen chromeos::WmIpc::instance()->NotifyAboutSignout(); 20272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen notified = true; 20372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 20472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 20572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 20672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static 20772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool BrowserList::signout_ = false; 20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#endif 21021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 21121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// static 21272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid BrowserList::NotifyAndTerminate(bool fast_path) { 21321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#if defined(OS_CHROMEOS) 21472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!signout_) return; 21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NotifyWindowManagerAboutSignout(); 21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#endif 21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 21872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (fast_path) { 21972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NotificationService::current()->Notify(NotificationType::APP_TERMINATING, 22072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NotificationService::AllSources(), 22172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NotificationService::NoDetails()); 22272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 22372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 22472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#if defined(OS_CHROMEOS) 22572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen chromeos::CrosLibrary* cros_library = chromeos::CrosLibrary::Get(); 22672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (cros_library->EnsureLoaded()) { 22772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // If update has been installed, reboot, otherwise, sign out. 22872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (cros_library->GetUpdateLibrary()->status().status == 22972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT) { 23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen cros_library->GetUpdateLibrary()->RebootAfterUpdate(); 23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 23272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen cros_library->GetLoginLibrary()->StopSession(""); 23372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 23421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return; 23521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } 23621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // If running the Chrome OS build, but we're not on the device, fall through 23721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#endif 23821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen AllBrowsersClosedAndAppExiting(); 23921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen} 24021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::RemoveBrowser(Browser* browser) { 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RemoveBrowserFrom(browser, &last_active_browsers_); 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Closing all windows does not indicate quitting the application on the Mac, 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // however, many UI tests rely on this behavior so leave it be for now and 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // simply ignore the behavior on the Mac outside of unit tests. 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(andybons): Fix the UI tests to Do The Right Thing. 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool closing_last_browser = (browsers_.size() == 1); 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::current()->Notify( 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationType::BROWSER_CLOSED, 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<Browser>(browser), Details<bool>(&closing_last_browser)); 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 254731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick RemoveBrowserFrom(browser, &browsers_); 255731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 256731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Do some basic checking to try to catch evil observers 257731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // that change the list from under us. 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t original_count = observers_.size(); 259731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick FOR_EACH_OBSERVER(Observer, observers_, OnBrowserRemoved(browser)); 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK_EQ(original_count, observers_.size()) 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << "observer list modified during notification"; 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the last Browser object was destroyed, make sure we try to close any 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // remaining dependent windows too. 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (browsers_.empty()) { 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete activity_observer; 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch activity_observer = NULL; 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->ReleaseModule(); 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If we're exiting, send out the APP_TERMINATING notification to allow other 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // modules to shut themselves down. 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (browsers_.empty() && 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (browser_shutdown::IsTryingToQuit() || 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->IsShuttingDown())) { 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Last browser has just closed, and this is a user-initiated quit or there 2783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // is no module keeping the app alive, so send out our notification. No need 2793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // to call ProfileManager::ShutdownSessionServices() as part of the 2803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // shutdown, because Browser::WindowClosing() already makes sure that the 2813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // SessionService is created and notified. 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::current()->Notify(NotificationType::APP_TERMINATING, 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::AllSources(), 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::NoDetails()); 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AllBrowsersClosedAndAppExiting(); 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::AddObserver(BrowserList::Observer* observer) { 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch observers_.AddObserver(observer); 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::RemoveObserver(BrowserList::Observer* observer) { 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch observers_.RemoveObserver(observer); 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 29921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#if defined(OS_CHROMEOS) 30021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// static 30121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool BrowserList::NeedBeforeUnloadFired() { 30221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen bool need_before_unload_fired = false; 30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for (const_iterator i = begin(); i != end(); ++i) { 30421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen need_before_unload_fired = need_before_unload_fired || 30521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen (*i)->TabsNeedBeforeUnloadFired(); 30621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } 30721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return need_before_unload_fired; 30821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen} 30921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 31021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// static 31121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool BrowserList::PendingDownloads() { 31272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for (const_iterator i = begin(); i != end(); ++i) { 31321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen bool normal_downloads_are_present = false; 31421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen bool incognito_downloads_are_present = false; 31521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen (*i)->CheckDownloadsInProgress(&normal_downloads_are_present, 31621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen &incognito_downloads_are_present); 31721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (normal_downloads_are_present || incognito_downloads_are_present) 31821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return true; 31921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } 32021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return false; 32121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen} 32221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#endif 32321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 3253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid BrowserList::CloseAllBrowsers() { 3263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick bool session_ending = 3273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION; 3283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick bool use_post = !session_ending; 3293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick bool force_exit = false; 3303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#if defined(USE_X11) 3313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (session_ending) 3323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick force_exit = true; 3333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#endif 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tell everyone that we are shutting down. 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser_shutdown::SetTryingToQuit(true); 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Before we close the browsers shutdown all session services. That way an 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // exit can restore all browsers open before exiting. 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ProfileManager::ShutdownSessionServices(); 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If there are no browsers, send the APP_TERMINATING action here. Otherwise, 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // it will be sent by RemoveBrowser() when the last browser has closed. 3433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (force_exit || browsers_.empty()) { 34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NotifyAndTerminate(true); 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 347201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#if defined(OS_CHROMEOS) 348201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( 349201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch "StartedClosingWindows", false); 350201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#endif 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (BrowserList::const_iterator i = BrowserList::begin(); 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i != BrowserList::end();) { 35321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen Browser* browser = *i; 35421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen browser->window()->Close(); 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (use_post) { 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i; 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This path is hit during logoff/power-down. In this case we won't get 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // a final message and so we force the browser to be deleted. 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Close doesn't immediately destroy the browser 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (Browser::TabStripEmpty() uses invoke later) but when we're ending the 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // session we need to make sure the browser is destroyed now. So, invoke 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // DestroyBrowser to make sure the browser is deleted and cleanup can 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // happen. 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser->window()->DestroyBrowser(); 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i = BrowserList::begin(); 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (i != BrowserList::end() && browser == *i) { 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Destroying the browser should have removed it from the browser list. 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We should never get here. 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED(); 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 37821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid BrowserList::Exit() { 37921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#if defined(OS_CHROMEOS) 38072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signout_ = true; 38121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Fast shutdown for ChromeOS when there's no unload processing to be done. 38221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (chromeos::CrosLibrary::Get()->EnsureLoaded() 38321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen && !NeedBeforeUnloadFired() 38421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen && !PendingDownloads()) { 38572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NotifyAndTerminate(true); 38621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return; 38721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } 38821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#endif 38921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen CloseAllBrowsersAndExit(); 39021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen} 39121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 39221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// static 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::CloseAllBrowsersAndExit() { 39472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen MarkAsCleanShutdown(); // Don't notify users of crashes beyond this point. 395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::current()->Notify( 396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationType::APP_EXITING, 397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::AllSources(), 398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::NoDetails()); 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if !defined(OS_MACOSX) 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // On most platforms, closing all windows causes the application to exit. 4023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick CloseAllBrowsers(); 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#else 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // On the Mac, the application continues to run once all windows are closed. 4053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Terminate will result in a CloseAllBrowsers() call, and once (and if) 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // that is done, will cause the application to exit cleanly. 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch chrome_browser_application_mac::Terminate(); 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 4124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid BrowserList::SessionEnding() { 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // EndSession is invoked once per frame. Only do something the first time. 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static bool already_ended = false; 41572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // We may get called in the middle of shutdown, e.g. http://crbug.com/70852 41672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // In this case, do nothing. 41772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (already_ended || !NotificationService::current()) 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch already_ended = true; 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); 422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::current()->Notify( 424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationType::APP_EXITING, 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::AllSources(), 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::NoDetails()); 427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Write important data first. 429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->EndSession(); 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 4313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick BrowserList::CloseAllBrowsers(); 432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send out notification. This is used during testing so that the test harness 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // can properly shutdown before we exit. 435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::current()->Notify( 436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationType::SESSION_END, 437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::AllSources(), 438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::NoDetails()); 439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // And shutdown. 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser_shutdown::Shutdown(); 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN) 444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // At this point the message loop is still running yet we've shut everything 445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // down. If any messages are processed we'll likely crash. Exit now. 446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExitProcess(ResultCodes::NORMAL_EXIT); 4473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#elif defined(OS_LINUX) 4483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick _exit(ResultCodes::NORMAL_EXIT); 449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#else 450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTIMPLEMENTED(); 451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BrowserList::HasBrowserWithProfile(Profile* profile) { 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FindBrowserMatching(BrowserList::begin(), 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BrowserList::end(), 458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile, Browser::TYPE_ANY, 459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::FEATURE_NONE, 460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kMatchNothing) != NULL; 461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BrowserList::keep_alive_count_ = 0; 465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::StartKeepAlive() { 468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Increment the browser process refcount as long as we're keeping the 469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // application alive. 470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!WillKeepAlive()) 471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->AddRefModule(); 472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch keep_alive_count_++; 473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::EndKeepAlive() { 4774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch DCHECK_GT(keep_alive_count_, 0); 478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch keep_alive_count_--; 479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Allow the app to shutdown again. 480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!WillKeepAlive()) { 481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->ReleaseModule(); 482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If there are no browsers open and we aren't already shutting down, 483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // initiate a shutdown. Also skips shutdown if this is a unit test 484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (MessageLoop::current() == null). 485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (browsers_.empty() && !browser_shutdown::IsTryingToQuit() && 486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MessageLoop::current()) 4873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick CloseAllBrowsers(); 488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BrowserList::WillKeepAlive() { 493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return keep_alive_count_ > 0; 494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 497c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowserList::BrowserVector BrowserList::last_active_browsers_; 498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::SetLastActive(Browser* browser) { 501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RemoveBrowserFrom(browser, &last_active_browsers_); 502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch last_active_browsers_.push_back(browser); 503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FOR_EACH_OBSERVER(Observer, observers_, OnBrowserSetLastActive(browser)); 505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 508c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowser* BrowserList::GetLastActive() { 509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!last_active_browsers_.empty()) 510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return *(last_active_browsers_.rbegin()); 511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 516c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowser* BrowserList::GetLastActiveWithProfile(Profile* p) { 517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We are only interested in last active browsers, so we don't fall back to 518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // all browsers like FindBrowserWith* do. 519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FindBrowserMatching( 520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BrowserList::begin_last_active(), BrowserList::end_last_active(), p, 521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::TYPE_ANY, Browser::FEATURE_NONE, kMatchNothing); 522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 525c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowser* BrowserList::FindBrowserWithType(Profile* p, Browser::Type t, 526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool match_incognito) { 527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch uint32 match_types = match_incognito ? kMatchOriginalProfile : kMatchNothing; 528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser* browser = FindBrowserMatching( 529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BrowserList::begin_last_active(), BrowserList::end_last_active(), 530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p, t, Browser::FEATURE_NONE, match_types); 531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Fall back to a forward scan of all Browsers if no active one was found. 532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return browser ? browser : 533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FindBrowserMatching(BrowserList::begin(), BrowserList::end(), p, t, 534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::FEATURE_NONE, match_types); 535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 538c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowser* BrowserList::FindBrowserWithFeature(Profile* p, 539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::WindowFeature feature) { 540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser* browser = FindBrowserMatching( 541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BrowserList::begin_last_active(), BrowserList::end_last_active(), 542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p, Browser::TYPE_ANY, feature, kMatchCanSupportWindowFeature); 543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Fall back to a forward scan of all Browsers if no active one was found. 544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return browser ? browser : 545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FindBrowserMatching(BrowserList::begin(), BrowserList::end(), p, 546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Browser::TYPE_ANY, feature, 547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kMatchCanSupportWindowFeature); 548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 551c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowser* BrowserList::FindBrowserWithProfile(Profile* p) { 552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FindBrowserWithType(p, Browser::TYPE_ANY, false); 553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 556c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBrowser* BrowserList::FindBrowserWithID(SessionID::id_type desired_id) { 557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (BrowserList::const_iterator i = BrowserList::begin(); 558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i != BrowserList::end(); ++i) { 559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((*i)->session_id().id() == desired_id) 560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return *i; 561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochsize_t BrowserList::GetBrowserCountForType(Profile* p, Browser::Type type) { 567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t result = 0; 568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (BrowserList::const_iterator i = BrowserList::begin(); 569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i != BrowserList::end(); ++i) { 570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (BrowserMatches(*i, p, type, Browser::FEATURE_NONE, kMatchNothing)) 571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++result; 572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochsize_t BrowserList::GetBrowserCount(Profile* p) { 578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t result = 0; 579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (BrowserList::const_iterator i = BrowserList::begin(); 580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i != BrowserList::end(); ++i) { 581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (BrowserMatches(*i, p, Browser::TYPE_ANY, Browser::FEATURE_NONE, 582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kMatchNothing)) { 583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch result++; 584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BrowserList::IsOffTheRecordSessionActive() { 591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (BrowserList::const_iterator i = BrowserList::begin(); 592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch i != BrowserList::end(); ++i) { 593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((*i)->profile()->IsOffTheRecord()) 594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BrowserList::RemoveBrowserFrom(Browser* browser, 601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BrowserVector* browser_list) { 602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const iterator remove_browser = 603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::find(browser_list->begin(), browser_list->end(), browser); 604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (remove_browser != browser_list->end()) 605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser_list->erase(remove_browser); 606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 608c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabContentsIterator::TabContentsIterator() 609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : browser_iterator_(BrowserList::begin()), 610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch web_view_index_(-1), 611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch cur_(NULL) { 612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Advance(); 613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabContentsIterator::Advance() { 616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Unless we're at the beginning (index = -1) or end (iterator = end()), 617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // then the current TabContents should be valid. 618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(web_view_index_ || browser_iterator_ == BrowserList::end() || cur_) 619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << "Trying to advance past the end"; 620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Update cur_ to the next TabContents in the list. 622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (browser_iterator_ != BrowserList::end()) { 623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch web_view_index_++; 624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (web_view_index_ >= (*browser_iterator_)->tab_count()) { 626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // advance browsers 627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++browser_iterator_; 628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch web_view_index_ = 0; 629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (browser_iterator_ == BrowserList::end()) { 630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch cur_ = NULL; 631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 635ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen TabContentsWrapper* next_tab = 636ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen (*browser_iterator_)->GetTabContentsWrapperAt(web_view_index_); 637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (next_tab) { 638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch cur_ = next_tab; 639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 643