Fixing build: GetViewContainer changed name from under me. :)
[chromium-blink-merge.git] / chrome / browser / browser.cc
blob3f3ebe3b7bc85f4aefd89822d4890871fd6487ae
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/browser.h"
7 #include <windows.h>
8 #include <shellapi.h>
10 #include "base/command_line.h"
11 #include "base/idle_timer.h"
12 #include "base/logging.h"
13 #include "base/string_util.h"
14 #include "chrome/app/chrome_dll_resource.h"
15 #include "chrome/browser/browser_list.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/browser_shutdown.h"
18 #include "chrome/browser/browser_url_handler.h"
19 #include "chrome/browser/browser_window.h"
20 #include "chrome/browser/cert_store.h"
21 #include "chrome/browser/debugger/debugger_window.h"
22 #include "chrome/browser/dom_ui/new_tab_ui.h"
23 #include "chrome/browser/download/save_package.h"
24 #include "chrome/browser/frame_util.h"
25 #include "chrome/browser/navigation_controller.h"
26 #include "chrome/browser/navigation_entry.h"
27 #include "chrome/browser/plugin_process_host.h"
28 #include "chrome/browser/plugin_service.h"
29 #include "chrome/browser/profile.h"
30 #include "chrome/browser/ssl_error_info.h"
31 #include "chrome/browser/site_instance.h"
32 #include "chrome/browser/url_fixer_upper.h"
33 #include "chrome/browser/user_metrics.h"
34 #include "chrome/browser/view_ids.h"
35 #include "chrome/browser/views/download_shelf_view.h"
36 #include "chrome/browser/views/go_button.h"
37 #include "chrome/browser/views/bookmark_bar_view.h"
38 #include "chrome/browser/views/html_dialog_view.h"
39 #include "chrome/browser/views/location_bar_view.h"
40 #include "chrome/browser/views/status_bubble.h"
41 #include "chrome/browser/views/tabs/tab_strip.h"
42 #include "chrome/browser/views/toolbar_star_toggle.h"
43 #include "chrome/browser/web_contents_view.h"
44 #include "chrome/browser/window_sizer.h"
45 #include "chrome/common/chrome_constants.h"
46 #include "chrome/common/chrome_switches.h"
47 #include "chrome/common/l10n_util.h"
48 #include "chrome/common/pref_names.h"
49 #include "chrome/common/pref_service.h"
50 #include "chrome/common/win_util.h"
51 #include "net/base/cookie_monster.h"
52 #include "net/base/cookie_policy.h"
53 #include "net/base/net_util.h"
54 #include "net/base/registry_controlled_domain.h"
56 #include "chromium_strings.h"
57 #include "generated_resources.h"
59 static BrowserList g_browserlist;
61 // How long we wait before updating the browser chrome while loading a page.
62 static const int kUIUpdateCoalescingTimeMS = 200;
64 // Idle time before helping prune memory consumption.
65 static const int kBrowserReleaseMemoryInterval = 30; // In seconds.
67 // How much horizontal and vertical offset there is between newly opened
68 // windows.
69 static const int kWindowTilePixels = 20;
71 // How frequently we check for hung plugin windows.
72 static const int kDefaultHungPluginDetectFrequency = 2000;
74 // How long do we wait before we consider a window hung (in ms).
75 static const int kDefaultPluginMessageResponseTimeout = 30000;
77 ////////////////////////////////////////////////////////////////////////////////
79 // A task to reduce the working set of the plugins.
80 class ReducePluginsWorkingSetTask : public Task {
81 public:
82 virtual void Run() {
83 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
84 PluginProcessHost* plugin = const_cast<PluginProcessHost*>(*iter);
85 DCHECK(plugin->process());
86 Process process(plugin->process());
87 process.ReduceWorkingSet();
92 // A browser task to run when the user is not using the browser.
93 // In our case, we're trying to be nice to the operating system and release
94 // memory not in use.
95 class BrowserIdleTimer : public base::IdleTimer {
96 public:
97 BrowserIdleTimer()
98 : base::IdleTimer(TimeDelta::FromSeconds(kBrowserReleaseMemoryInterval),
99 false) {
102 virtual void OnIdle() {
103 // We're idle. Release browser and renderer unused pages.
105 // Handle the Browser.
106 Process process(GetCurrentProcess());
107 process.ReduceWorkingSet();
109 // Handle the Renderer(s).
110 RenderProcessHost::iterator renderer_iter;
111 for (renderer_iter = RenderProcessHost::begin(); renderer_iter !=
112 RenderProcessHost::end(); renderer_iter++) {
113 Process process(renderer_iter->second->process());
114 process.ReduceWorkingSet();
117 // Handle the Plugin(s). We need to iterate through the plugin processes on
118 // the IO thread because that thread manages the plugin process collection.
119 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
120 new ReducePluginsWorkingSetTask());
124 ////////////////////////////////////////////////////////////////////////////////
126 struct Browser::UIUpdate {
127 UIUpdate(const TabContents* src, unsigned flags)
128 : source(src),
129 changed_flags(flags) {
132 // The source of the update.
133 const TabContents* source;
135 // What changed in the UI.
136 unsigned changed_flags;
139 ////////////////////////////////////////////////////////////////////////////////
141 // static
142 Browser* Browser::GetBrowserForController(
143 const NavigationController* controller, int* index_result) {
144 BrowserList::const_iterator it;
145 for (it = BrowserList::begin(); it != BrowserList::end(); ++it) {
146 int index = (*it)->tabstrip_model_.GetIndexOfController(controller);
147 if (index != TabStripModel::kNoTab) {
148 if (index_result)
149 *index_result = index;
150 return *it;
154 return NULL;
157 // static
158 void Browser::OpenNewBrowserWindow(Profile* profile, int show_command) {
159 Browser* browser = new Browser(gfx::Rect(), show_command, profile,
160 BrowserType::TABBED_BROWSER, L"");
161 browser->AddBlankTab(true);
162 browser->Show();
165 // static
166 void Browser::RegisterPrefs(PrefService* prefs) {
167 prefs->RegisterIntegerPref(prefs::kPluginMessageResponseTimeout,
168 kDefaultPluginMessageResponseTimeout);
169 prefs->RegisterIntegerPref(prefs::kHungPluginDetectFrequency,
170 kDefaultHungPluginDetectFrequency);
171 prefs->RegisterDictionaryPref(prefs::kBrowserWindowPlacement);
172 prefs->RegisterIntegerPref(prefs::kOptionsWindowLastTabIndex, 0);
175 // static
176 void Browser::RegisterUserPrefs(PrefService* prefs) {
177 prefs->RegisterStringPref(prefs::kHomePage, L"chrome-internal:");
178 prefs->RegisterBooleanPref(prefs::kHomePageIsNewTabPage, true);
179 prefs->RegisterIntegerPref(prefs::kCookieBehavior,
180 net::CookiePolicy::ALLOW_ALL_COOKIES);
181 prefs->RegisterBooleanPref(prefs::kShowHomeButton, false);
182 prefs->RegisterStringPref(prefs::kRecentlySelectedEncoding, L"");
183 prefs->RegisterBooleanPref(prefs::kDeleteBrowsingHistory, true);
184 prefs->RegisterBooleanPref(prefs::kDeleteDownloadHistory, true);
185 prefs->RegisterBooleanPref(prefs::kDeleteCache, true);
186 prefs->RegisterBooleanPref(prefs::kDeleteCookies, true);
187 prefs->RegisterBooleanPref(prefs::kDeletePasswords, false);
190 Browser::Browser(const gfx::Rect& initial_bounds,
191 int show_command,
192 Profile* profile,
193 BrowserType::Type type,
194 const std::wstring& app_name)
195 : profile_(profile),
196 window_(NULL),
197 initial_show_command_(show_command),
198 is_attempting_to_close_browser_(false),
199 controller_(this),
200 chrome_updater_factory_(this),
201 method_factory_(this),
202 hung_window_detector_(&hung_plugin_action_),
203 ticker_(0),
204 tabstrip_model_(this, profile),
205 toolbar_model_(this),
206 type_(type),
207 app_name_(app_name),
208 idle_task_(new BrowserIdleTimer()) {
209 tabstrip_model_.AddObserver(this);
211 CommandLine parsed_command_line;
213 gfx::Rect create_bounds;
214 bool maximized = false;
215 WindowSizer::GetBrowserWindowBounds(app_name_, initial_bounds,
216 &create_bounds, &maximized);
217 if (parsed_command_line.HasSwitch(switches::kStartMaximized))
218 maximized = true;
219 if (maximized)
220 initial_show_command_ = SW_SHOWMAXIMIZED;
221 window_ = BrowserWindow::CreateBrowserWindow(this, create_bounds,
222 show_command);
224 // See note where SIZE_TO_CONTENTS is defined in browser.h for an explanation
225 // of this hack.
226 if (show_command == SIZE_TO_CONTENTS) {
227 // This codepath is deprecated with the new frames.
228 DCHECK(!g_browser_process->IsUsingNewFrames());
229 // SizeToContents causes a Layout so make sure the tab strip and toolbar
230 // are already initialized.
231 window_->SizeToContents(initial_bounds);
232 initial_show_command_ = SW_SHOWNORMAL;
235 // Start a hung plugin window detector for this browser object (as long as
236 // hang detection is not disabled).
237 if (!parsed_command_line.HasSwitch(switches::kDisableHangMonitor))
238 InitHangMonitor();
240 if (!g_browser_process->IsUsingNewFrames()) {
241 NotificationService::current()->
242 AddObserver(this, NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
243 NotificationService::AllSources());
245 NotificationService::current()->AddObserver(
246 this, NOTIFY_SSL_STATE_CHANGED, NotificationService::AllSources());
248 if (profile->HasSessionService()) {
249 SessionService* session_service = profile->GetSessionService();
250 if (session_service)
251 session_service->SetWindowType(session_id_, type_);
254 InitCommandState();
255 BrowserList::AddBrowser(this);
257 encoding_auto_detect_.Init(prefs::kWebKitUsesUniversalDetector,
258 profile_->GetPrefs(), NULL);
260 // Trim browser memory on idle for low & medium memory models.
261 if (g_browser_process->memory_model() < BrowserProcess::HIGH_MEMORY_MODEL)
262 idle_task_->Start();
264 // Show the First Run information bubble if we've been told to.
265 PrefService* local_state = g_browser_process->local_state();
266 if (local_state->IsPrefRegistered(prefs::kShouldShowFirstRunBubble) &&
267 local_state->GetBoolean(prefs::kShouldShowFirstRunBubble)) {
268 // Reset the preference so we don't show the bubble for subsequent windows.
269 local_state->ClearPref(prefs::kShouldShowFirstRunBubble);
270 GetLocationBarView()->ShowFirstRunBubble();
274 Browser::~Browser() {
275 // The tab strip should be empty at this point.
276 DCHECK(tabstrip_model_.empty());
277 tabstrip_model_.RemoveObserver(this);
279 BrowserList::RemoveBrowser(this);
281 if (!BrowserList::HasBrowserWithProfile(profile_)) {
282 // We're the last browser window with this profile. We need to nuke the
283 // TabRestoreService, which will start the shutdown of the
284 // NavigationControllers and allow for proper shutdown. If we don't do this
285 // chrome won't shutdown cleanly, and may end up crashing when some
286 // thread tries to use the IO thread (or another thread) that is no longer
287 // valid.
288 profile_->ResetTabRestoreService();
291 SessionService* session_service = profile_->GetSessionService();
292 if (session_service)
293 session_service->WindowClosed(session_id_);
295 if (!g_browser_process->IsUsingNewFrames()) {
296 NotificationService::current()->
297 RemoveObserver(this, NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
298 NotificationService::AllSources());
300 NotificationService::current()->RemoveObserver(
301 this, NOTIFY_SSL_STATE_CHANGED, NotificationService::AllSources());
303 // Stop hung plugin monitoring.
304 ticker_.Stop();
305 ticker_.UnregisterTickHandler(&hung_window_detector_);
307 if (profile_->IsOffTheRecord() &&
308 !BrowserList::IsOffTheRecordSessionActive()) {
309 // We reuse the OTR cookie store across OTR windows. If the last OTR
310 // window is closed, then we want to wipe the cookie store clean, so when
311 // an OTR window is open again, it starts with an empty cookie store. This
312 // also frees up the memory that the OTR cookies were using. OTR never
313 // loads or writes persistent cookies (there is no backing store), so we
314 // can just delete all of the cookies in the store.
315 profile_->GetRequestContext()->cookie_store()->DeleteAll(false);
318 // There may be pending file dialogs, we need to tell them that we've gone
319 // away so they don't try and call back to us.
320 if (select_file_dialog_.get())
321 select_file_dialog_->ListenerDestroyed();
324 void Browser::ShowAndFit(bool resize_to_fit) {
325 // Only allow one call after the browser is created.
326 if (initial_show_command_ < 0) {
327 // The frame is already visible, we're being invoked again either by the
328 // user clicking a link in another app or from a desktop shortcut.
329 window_->Activate();
330 return;
332 window_->Show(initial_show_command_, resize_to_fit);
333 if ((initial_show_command_ == SW_SHOWNORMAL) ||
334 (initial_show_command_ == SW_SHOWMAXIMIZED))
335 window_->Activate();
336 initial_show_command_ = -1;
338 // Setting the focus doesn't work when the window is invisible, so any focus
339 // initialization that happened before this will be lost.
341 // We really "should" restore the focus whenever the window becomes unhidden,
342 // but I think initializing is the only time where this can happen where there
343 // is some focus change we need to pick up, and this is easier than plumbing
344 // through an unhide message all the way from the frame.
346 // If we do find there are cases where we need to restore the focus on show,
347 // that should be added and this should be removed.
348 TabContents* selected_tab_contents = GetSelectedTabContents();
349 if (selected_tab_contents)
350 selected_tab_contents->RestoreFocus();
353 void Browser::CloseFrame() {
354 window_->Close();
357 GURL Browser::GetHomePage() {
358 if (profile_->GetPrefs()->GetBoolean(prefs::kHomePageIsNewTabPage)) {
359 return NewTabUIURL();
360 } else {
361 GURL home_page = GURL(URLFixerUpper::FixupURL(
362 profile_->GetPrefs()->GetString(prefs::kHomePage),
363 std::wstring()));
364 if (!home_page.is_valid())
365 return NewTabUIURL();
367 return home_page;
371 ////////////////////////////////////////////////////////////////////////////////
373 void Browser::SyncWindowTitle() {
374 DCHECK(!g_browser_process->IsUsingNewFrames());
376 TabContents* current_tab = GetSelectedTabContents();
377 if (!current_tab || current_tab->GetTitle().empty()) {
378 window_->SetWindowTitle(l10n_util::GetString(IDS_PRODUCT_NAME));
379 return;
382 window_->SetWindowTitle(
383 l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT,
384 current_tab->GetTitle()));
387 ////////////////////////////////////////////////////////////////////////////////
388 // Event Handlers
390 void Browser::WindowActivationChanged(bool is_active) {
391 if (is_active)
392 BrowserList::SetLastActive(this);
395 ////////////////////////////////////////////////////////////////////////////////
396 // Toolbar creation, management
398 LocationBarView* Browser::GetLocationBarView() const {
399 return window_->GetLocationBarView();
402 ////////////////////////////////////////////////////////////////////////////////
403 // Chrome update coalescing
405 void Browser::UpdateToolBar(bool should_restore_state) {
406 window_->UpdateToolbar(GetSelectedTabContents(), should_restore_state);
409 void Browser::ScheduleUIUpdate(const TabContents* source,
410 unsigned changed_flags) {
411 // Synchronously update the URL.
412 if (changed_flags & TabContents::INVALIDATE_URL &&
413 source == GetSelectedTabContents()) {
414 // Only update the URL for the current tab. Note that we do not update
415 // the navigation commands since those would have already been updated
416 // synchronously by NavigationStateChanged.
417 UpdateToolBar(false);
419 if (changed_flags == TabContents::INVALIDATE_URL)
420 return; // Just had an update URL and nothing else.
423 // Save the dirty bits.
424 scheduled_updates_.push_back(UIUpdate(source, changed_flags));
426 if (chrome_updater_factory_.empty()) {
427 // No task currently scheduled, start another.
428 MessageLoop::current()->PostDelayedTask(FROM_HERE,
429 chrome_updater_factory_.NewRunnableMethod(
430 &Browser::ProcessPendingUIUpdates),
431 kUIUpdateCoalescingTimeMS);
435 void Browser::ProcessPendingUIUpdates() {
436 #ifndef NDEBUG
437 // Validate that all tabs we have pending updates for exist. This is scary
438 // because the pending list must be kept in sync with any detached or
439 // deleted tabs. This code does not dereference any TabContents pointers.
440 for (size_t i = 0; i < scheduled_updates_.size(); i++) {
441 bool found = false;
442 for (int tab = 0; tab < tab_count(); tab++) {
443 if (GetTabContentsAt(tab)->controller() ==
444 scheduled_updates_[i].source->controller()) {
445 found = true;
446 break;
449 DCHECK(found);
451 #endif
453 chrome_updater_factory_.RevokeAll();
455 // We could have many updates for the same thing in the queue. This map tracks
456 // the bits of the stuff we've already updated for each TabContents so we
457 // don't update again.
458 typedef std::map<const TabContents*, unsigned> UpdateTracker;
459 UpdateTracker updated_stuff;
461 for (size_t i = 0; i < scheduled_updates_.size(); i++) {
462 // Do not dereference |contents|, it may be out-of-date!
463 const TabContents* contents = scheduled_updates_[i].source;
464 unsigned flags = scheduled_updates_[i].changed_flags;
466 // Remove any bits we have already updated, and save the new bits.
467 UpdateTracker::iterator updated = updated_stuff.find(contents);
468 if (updated != updated_stuff.end()) {
469 // Turn off bits already set.
470 flags &= ~updated->second;
471 if (!flags)
472 continue;
474 updated->second |= flags;
475 } else {
476 updated_stuff[contents] = flags;
479 // Updates to the title or favicon require a tab repaint. However, the
480 // inverse is not true since updates to the title also update the window
481 // title.
482 bool invalidate_tab = false;
483 if (flags & TabContents::INVALIDATE_TITLE ||
484 flags & TabContents::INVALIDATE_FAVICON) {
485 invalidate_tab = true;
487 // Anything that repaints the tab means the favicon is updated.
488 updated_stuff[contents] |= TabContents::INVALIDATE_FAVICON;
491 // Updating the URL happens synchronously in ScheduleUIUpdate.
493 if (flags & TabContents::INVALIDATE_TITLE &&
494 !g_browser_process->IsUsingNewFrames()) {
495 SyncWindowTitle(); // We'll update the tab due to invalide_tab below.
498 if (flags & TabContents::INVALIDATE_LOAD)
499 GetStatusBubble()->SetStatus(GetSelectedTabContents()->GetStatusText());
501 if (invalidate_tab) { // INVALIDATE_TITLE or INVALIDATE_FAVICON.
502 tabstrip_model_.UpdateTabContentsStateAt(
503 tabstrip_model_.GetIndexOfController(contents->controller()));
504 window_->UpdateTitleBar();
506 if (contents == GetSelectedTabContents()) {
507 TabContents* current_tab = GetSelectedTabContents();
508 controller_.UpdateCommandEnabled(IDC_CREATE_SHORTCUT,
509 current_tab->type() == TAB_CONTENTS_WEB &&
510 !current_tab->GetFavIcon().isNull());
514 // We don't need to process INVALIDATE_STATE, since that's not visible.
517 scheduled_updates_.clear();
520 ////////////////////////////////////////////////////////////////////////////////
521 // TabContentsDelegate
523 void Browser::OpenURLFromTab(TabContents* source,
524 const GURL& url,
525 WindowOpenDisposition disposition,
526 PageTransition::Type transition) {
527 // No code for these yet
528 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
530 TabContents* current_tab = source ? source : GetSelectedTabContents();
531 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
532 TabContents* new_contents = NULL;
534 // If the URL is part of the same web site, then load it in the same
535 // SiteInstance (and thus the same process). This is an optimization to
536 // reduce process overhead; it is not necessary for compatibility. (That is,
537 // the new tab will not have script connections to the previous tab, so it
538 // does not need to be part of the same SiteInstance or BrowsingInstance.)
539 // Default to loading in a new SiteInstance and BrowsingInstance.
540 // TODO(creis): should this apply to applications?
541 SiteInstance* instance = NULL;
542 // Don't use this logic when "--process-per-tab" is specified.
543 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
544 if (current_tab) {
545 const WebContents* const web_contents = current_tab->AsWebContents();
546 if (web_contents) {
547 const GURL& current_url = web_contents->GetURL();
548 if (SiteInstance::IsSameWebSite(current_url, url))
549 instance = web_contents->GetSiteInstance();
554 // If this is an application we can only have one tab so a new tab always
555 // goes into a tabbed browser window.
556 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
557 // If the disposition is OFF_THE_RECORD we don't want to create a new
558 // browser that will itself create another OTR browser. This will result in
559 // a browser leak (and crash below because no tab is created or selected).
560 if (disposition == OFF_THE_RECORD) {
561 OpenURLOffTheRecord(profile_, url);
562 return;
565 Browser* b = GetOrCreateTabbedBrowser();
566 DCHECK(b);
568 // If we have just created a new browser window, make sure we select the
569 // tab.
570 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
571 disposition = NEW_FOREGROUND_TAB;
573 b->OpenURL(url, disposition, transition);
574 b->Show();
575 b->MoveToFront(true);
576 return;
579 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
580 disposition = NEW_FOREGROUND_TAB;
582 if (disposition == NEW_WINDOW) {
583 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
584 BrowserType::TABBED_BROWSER, L"");
585 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
586 new_browser->Show();
587 } else if ((disposition == CURRENT_TAB) && current_tab) {
588 if (transition == PageTransition::TYPED ||
589 transition == PageTransition::AUTO_BOOKMARK ||
590 transition == PageTransition::GENERATED ||
591 transition == PageTransition::START_PAGE) {
592 // Don't forget the openers if this tab is a New Tab page opened at the
593 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
594 // navigation of one of these transition types before resetting the
595 // opener relationships (this allows for the use case of opening a new
596 // tab to do a quick look-up of something while viewing a tab earlier in
597 // the strip). We can make this heuristic more permissive if need be.
598 // TODO(beng): (http://b/1306495) write unit tests for this once this
599 // object is unit-testable.
600 int current_tab_index =
601 tabstrip_model_.GetIndexOfTabContents(current_tab);
602 bool forget_openers =
603 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
604 current_tab_index == (tab_count() - 1) &&
605 current_tab->controller()->GetEntryCount() == 1);
606 if (forget_openers) {
607 // If the user navigates the current tab to another page in any way
608 // other than by clicking a link, we want to pro-actively forget all
609 // TabStrip opener relationships since we assume they're beginning a
610 // different task by reusing the current tab.
611 tabstrip_model_.ForgetAllOpeners();
612 // In this specific case we also want to reset the group relationship,
613 // since it is now technically invalid.
614 tabstrip_model_.ForgetGroup(current_tab);
617 current_tab->controller()->LoadURL(url, transition);
618 // The TabContents might have changed as part of the navigation (ex: new tab
619 // page can become WebContents).
620 new_contents = current_tab->controller()->active_contents();
621 GetStatusBubble()->Hide();
623 // Synchronously update the location bar. This allows us to immediately
624 // have the URL bar update when the user types something, rather than
625 // going through the normal system of ScheduleUIUpdate which has a delay.
626 UpdateToolBar(false);
627 } else if (disposition == OFF_THE_RECORD) {
628 OpenURLOffTheRecord(profile_, url);
629 return;
630 } else if (disposition != SUPPRESS_OPEN) {
631 new_contents =
632 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
633 instance);
636 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
637 // Give the focus to the newly navigated tab, if the source tab was
638 // front-most.
639 new_contents->Focus();
643 void Browser::NavigationStateChanged(const TabContents* source,
644 unsigned changed_flags) {
645 if (!GetSelectedTabContents()) {
646 // Nothing is selected. This can happen when being restored from history,
647 // bail.
648 return;
651 // Only update the UI when something visible has changed.
652 if (changed_flags)
653 ScheduleUIUpdate(source, changed_flags);
655 // We don't schedule updates to the navigation commands since they will only
656 // change once per navigation, so we don't have to worry about flickering.
657 if (changed_flags & TabContents::INVALIDATE_URL)
658 UpdateNavigationCommands();
661 void Browser::ReplaceContents(TabContents* source, TabContents* new_contents) {
662 source->set_delegate(NULL);
663 new_contents->set_delegate(this);
665 RemoveScheduledUpdatesFor(source);
667 int index = tabstrip_model_.GetIndexOfTabContents(source);
668 tabstrip_model_.ReplaceTabContentsAt(index, new_contents);
670 if (is_attempting_to_close_browser_) {
671 // Need to do this asynchronously as it will close the tab, which is
672 // currently on the call stack above us.
673 MessageLoop::current()->PostTask(FROM_HERE,
674 method_factory_.NewRunnableMethod(&Browser::ClearUnloadState,
675 Source<TabContents>(source).ptr()));
677 // Need to remove ourselves as an observer for disconnection on the replaced
678 // TabContents, since we only care to fire onbeforeunload handlers on active
679 // Tabs. Make sure an observer is added for the replacement TabContents.
680 NotificationService::current()->
681 RemoveObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED,
682 Source<TabContents>(source));
683 NotificationService::current()->
684 AddObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED,
685 Source<TabContents>(new_contents));
689 void Browser::AddNewContents(TabContents* source,
690 TabContents* new_contents,
691 WindowOpenDisposition disposition,
692 const gfx::Rect& initial_pos,
693 bool user_gesture) {
694 DCHECK(disposition != SAVE_TO_DISK); // No code for this yet
696 // If this is an application we can only have one tab so we need to process
697 // this in tabbed browser window.
698 if (tabstrip_model_.count() > 0 &&
699 disposition != NEW_WINDOW && disposition != NEW_POPUP &&
700 type_ != BrowserType::TABBED_BROWSER) {
701 Browser* b = GetOrCreateTabbedBrowser();
702 DCHECK(b);
703 PageTransition::Type transition = PageTransition::LINK;
704 // If we were called from an "installed webapp" we want to emulate the code
705 // that is run from browser_init.cc for links from external applications.
706 // This means we need to open the tab with the START PAGE transition.
707 // AddNewContents doesn't support this but the TabStripModel's
708 // AddTabContents method does.
709 if (type_ == BrowserType::APPLICATION)
710 transition = PageTransition::START_PAGE;
711 b->tabstrip_model()->AddTabContents(new_contents, -1, transition, true);
712 b->Show();
713 b->MoveToFront(true);
714 return;
717 if (disposition == NEW_POPUP) {
718 BuildPopupWindow(source, new_contents, initial_pos);
719 } else if (disposition == NEW_WINDOW) {
720 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
721 BrowserType::TABBED_BROWSER, L"");
722 new_browser->AddNewContents(source, new_contents, NEW_FOREGROUND_TAB,
723 initial_pos, user_gesture);
724 new_browser->Show();
725 } else if (disposition == CURRENT_TAB) {
726 ReplaceContents(source, new_contents);
727 } else if (disposition != SUPPRESS_OPEN) {
728 tabstrip_model_.AddTabContents(new_contents, -1, PageTransition::LINK,
729 disposition == NEW_FOREGROUND_TAB);
733 void Browser::StartDraggingDetachedContents(TabContents* source,
734 TabContents* new_contents,
735 const gfx::Rect& contents_bounds,
736 const gfx::Point& mouse_pt,
737 int frame_component) {
738 if (!g_browser_process->IsUsingNewFrames()) {
739 BrowserType::Type new_type = BrowserType::BROWSER;
741 // If this is a minimal chrome browser, propagate to detached contents to
742 // avoid having URL fields in popups.
743 if (type_ == BrowserType::APPLICATION)
744 new_type = type_;
746 Browser* browser = new Browser(contents_bounds, SIZE_TO_CONTENTS, profile_,
747 new_type, L"");
748 browser->AddNewContents(
749 source, new_contents, NEW_FOREGROUND_TAB, contents_bounds, true);
750 browser->Show();
751 browser->window_->ContinueDetachConstrainedWindowDrag(
752 mouse_pt, frame_component);
753 } else {
754 // If we're inside an application frame, preserve that type (i.e. don't
755 // show a location bar on the new window), otherwise open a tab-less
756 // browser window with a location bar.
757 BrowserType::Type new_type =
758 type_ == BrowserType::APPLICATION ? type_ : BrowserType::BROWSER;
759 Browser* browser = new Browser(contents_bounds, SW_SHOWNORMAL, profile_,
760 BrowserType::BROWSER, std::wstring());
761 browser->AddNewContents(source, new_contents,
762 NEW_FOREGROUND_TAB, gfx::Rect(), true);
763 browser->Show();
764 browser->window()->ContinueDetachConstrainedWindowDrag(mouse_pt,
765 frame_component);
769 void Browser::ActivateContents(TabContents* contents) {
770 tabstrip_model_.SelectTabContentsAt(
771 tabstrip_model_.GetIndexOfTabContents(contents), false);
772 window_->Activate();
775 HWND Browser::GetTopLevelHWND() const {
776 return window_ ? reinterpret_cast<HWND>(window_->GetPlatformID()) : NULL;
779 void Browser::LoadingStateChanged(TabContents* source) {
780 tabstrip_model_.UpdateTabContentsLoadingAnimations();
782 window_->UpdateTitleBar();
784 // Let the go button know that it should change appearance if possible.
785 if (source == GetSelectedTabContents()) {
786 GetGoButton()->ScheduleChangeMode(
787 source->is_loading() ? GoButton::MODE_STOP : GoButton::MODE_GO);
789 GetStatusBubble()->SetStatus(GetSelectedTabContents()->GetStatusText());
793 void Browser::CloseContents(TabContents* source) {
794 if (is_attempting_to_close_browser_) {
795 // If we're trying to close the browser, just clear the state related to
796 // waiting for unload to fire. Don't actually try to close the tab as it
797 // will go down the slow shutdown path instead of the fast path of killing
798 // all the renderer processes.
799 ClearUnloadState(source);
800 return;
803 int index = tabstrip_model_.GetIndexOfTabContents(source);
804 if (index == TabStripModel::kNoTab) {
805 NOTREACHED() << "CloseContents called for tab not in our strip";
806 return;
808 tabstrip_model_.CloseTabContentsAt(index);
811 void Browser::MoveContents(TabContents* source, const gfx::Rect& pos) {
812 if (GetType() != BrowserType::BROWSER) {
813 NOTREACHED() << "moving invalid browser type";
814 return;
817 ::SetWindowPos(GetTopLevelHWND(), NULL, pos.x(), pos.y(), pos.width(),
818 pos.height(), 0);
819 win_util::AdjustWindowToFit(GetTopLevelHWND());
822 bool Browser::IsPopup(TabContents* source) {
823 // A non-tabbed BROWSER is an unconstrained popup.
824 return (GetType() == BrowserType::BROWSER);
827 void Browser::ShowHtmlDialog(HtmlDialogContentsDelegate* delegate,
828 HWND parent_hwnd) {
829 parent_hwnd = parent_hwnd ? parent_hwnd : GetTopLevelHWND();
830 HtmlDialogView* html_view = new HtmlDialogView(this, profile_, delegate);
831 ChromeViews::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(),
832 html_view);
833 html_view->InitDialog();
834 html_view->window()->Show();
837 void Browser::Observe(NotificationType type,
838 const NotificationSource& source,
839 const NotificationDetails& details) {
840 switch (type) {
841 case NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED: {
842 DCHECK(!g_browser_process->IsUsingNewFrames());
843 TabContents* current_tab = GetSelectedTabContents();
844 if (current_tab) {
845 Profile* event_profile = Source<Profile>(source).ptr();
846 if (event_profile->IsSameProfile(current_tab->profile())) {
847 // This forces the browser to query for the BookmarkBar again.
848 window_->ShelfVisibilityChanged();
851 break;
854 case NOTIFY_WEB_CONTENTS_DISCONNECTED:
855 if (is_attempting_to_close_browser_) {
856 // Need to do this asynchronously as it will close the tab, which is
857 // currently on the call stack above us.
858 MessageLoop::current()->PostTask(FROM_HERE,
859 method_factory_.NewRunnableMethod(&Browser::ClearUnloadState,
860 Source<TabContents>(source).ptr()));
862 break;
864 case NOTIFY_SSL_STATE_CHANGED:
865 // When the current tab's SSL state changes, we need to update the URL
866 // bar to reflect the new state. Note that it's possible for the selected
867 // tab contents to be NULL. This is because we listen for all sources
868 // (NavigationControllers) for convenience, so the notification could
869 // actually be for a different window while we're doing asynchronous
870 // closing of this one.
871 if (GetSelectedTabContents() &&
872 GetSelectedTabContents()->controller() ==
873 Source<NavigationController>(source).ptr())
874 UpdateToolBar(false);
875 break;
877 default:
878 NOTREACHED() << "Got a notification we didn't register for.";
882 void Browser::UpdateNavigationCommands() {
883 const TabContents* const current_tab = GetSelectedTabContents();
884 NavigationController* nc = current_tab->controller();
885 controller_.UpdateCommandEnabled(IDC_BACK, nc->CanGoBack());
886 controller_.UpdateCommandEnabled(IDC_FORWARD, nc->CanGoForward());
888 const WebContents* const web_contents = current_tab->AsWebContents();
890 if (web_contents) {
891 controller_.UpdateCommandEnabled(IDC_STAR, true);
892 SetStarredButtonToggled(web_contents->is_starred());
894 // View-source should not be enabled if already in view-source mode.
895 controller_.UpdateCommandEnabled(IDC_VIEWSOURCE,
896 current_tab->type() != TAB_CONTENTS_VIEW_SOURCE &&
897 current_tab->controller()->GetActiveEntry());
899 controller_.UpdateCommandEnabled(IDC_ZOOM, true);
900 bool enable_encoding =
901 SavePackage::IsSavableContents(web_contents->contents_mime_type()) &&
902 SavePackage::IsSavableURL(current_tab->GetURL());
903 controller_.UpdateCommandEnabled(IDC_ENCODING, enable_encoding);
905 controller_.UpdateCommandEnabled(IDC_SAVEPAGE,
906 SavePackage::IsSavableURL(current_tab->GetURL()));
907 controller_.UpdateCommandEnabled(IDC_SHOW_JS_CONSOLE, true);
908 } else {
909 controller_.UpdateCommandEnabled(IDC_VIEWSOURCE, false);
910 controller_.UpdateCommandEnabled(IDC_SHOW_JS_CONSOLE, false);
912 // Both disable the starring button and ensure it doesn't show a star.
913 controller_.UpdateCommandEnabled(IDC_STAR, false);
914 SetStarredButtonToggled(false);
915 controller_.UpdateCommandEnabled(IDC_ZOOM, false);
916 controller_.UpdateCommandEnabled(IDC_ENCODING, false);
918 controller_.UpdateCommandEnabled(IDC_SAVEPAGE, false);
921 controller_.UpdateCommandEnabled(IDC_CREATE_SHORTCUT,
922 current_tab->type() == TAB_CONTENTS_WEB &&
923 !current_tab->GetFavIcon().isNull());
924 controller_.UpdateCommandEnabled(IDC_FIND, web_contents != NULL);
925 controller_.UpdateCommandEnabled(IDC_PRINT, web_contents != NULL);
926 controller_.UpdateCommandEnabled(IDC_DUPLICATE,
927 CanDuplicateContentsAt(selected_index()));
930 // Notification that the starredness of a tab changed.
931 void Browser::URLStarredChanged(TabContents* source, bool starred) {
932 if (source == GetSelectedTabContents())
933 SetStarredButtonToggled(starred);
936 StatusBubble* Browser::GetStatusBubble() {
937 return window_->GetStatusBubble();
940 // Called whenever the window is moved so that we can update the position
941 // of any WS_POPUP HWNDs.
942 void Browser::WindowMoved() {
943 DCHECK(!g_browser_process->IsUsingNewFrames());
944 GetStatusBubble()->Reposition();
946 // Close the omnibox popup, if any.
947 LocationBarView* location_bar = GetLocationBarView();
948 if (location_bar)
949 location_bar->location_entry()->ClosePopup();
952 void Browser::ContentsMouseEvent(TabContents* source, UINT message) {
953 if (source == GetSelectedTabContents()) {
954 if (message == WM_MOUSEMOVE) {
955 GetStatusBubble()->MouseMoved();
956 } else if (message == WM_MOUSELEAVE) {
957 GetStatusBubble()->SetURL(GURL(), std::wstring());
962 void Browser::UpdateTargetURL(TabContents* source, const GURL& url) {
963 if (source == GetSelectedTabContents()) {
964 PrefService* prefs = profile_->GetPrefs();
965 GetStatusBubble()->SetURL(url, prefs->GetString(prefs::kAcceptLanguages));
969 void Browser::SetStarredButtonToggled(bool starred) {
970 window_->GetStarButton()->SetToggled(starred);
973 GoButton* Browser::GetGoButton() {
974 return window_->GetGoButton();
977 void Browser::ContentsZoomChange(bool zoom_in) {
978 controller_.ExecuteCommand(zoom_in ? IDC_ZOOM_PLUS : IDC_ZOOM_MINUS);
981 bool Browser::IsApplication() const {
982 return type_ == BrowserType::APPLICATION;
985 void Browser::ContentsStateChanged(TabContents* source) {
986 int index = tabstrip_model_.GetIndexOfTabContents(source);
987 if (index != TabStripModel::kNoTab)
988 tabstrip_model_.UpdateTabContentsStateAt(index);
991 bool Browser::ShouldDisplayURLField() {
992 return !IsApplication();
995 void Browser::SaveWindowPlacementToDatabase() {
996 // We don't want to be the ones who cause lazy initialization of the session
997 // service. This function gets called during initial window showing, and we
998 // don't want to bring in the session service this early.
999 if (!profile()->HasSessionService())
1000 return;
1001 SessionService* session_service = profile()->GetSessionService();
1002 if (!session_service)
1003 return;
1005 WINDOWPLACEMENT wp;
1006 wp.length = sizeof(wp);
1008 HWND hwnd = reinterpret_cast<HWND>(window_->GetPlatformID());
1009 if (!::GetWindowPlacement(hwnd, &wp))
1010 return;
1012 session_service->SetWindowBounds(session_id_,
1013 gfx::Rect(wp.rcNormalPosition),
1014 (wp.showCmd & SW_MAXIMIZE) == SW_MAXIMIZE);
1017 void Browser::SaveWindowPlacement() {
1018 WINDOWPLACEMENT wp;
1019 wp.length = sizeof(wp);
1021 HWND hwnd = reinterpret_cast<HWND>(window_->GetPlatformID());
1022 if (!::GetWindowPlacement(hwnd, &wp))
1023 return;
1025 PrefService* prefs = g_browser_process->local_state();
1026 DCHECK(prefs);
1027 std::wstring name(prefs::kBrowserWindowPlacement);
1028 if (!app_name_.empty()) {
1029 name.append(L"_");
1030 name.append(app_name_);
1033 DictionaryValue* win_pref = prefs->GetMutableDictionary(name.c_str());
1034 DCHECK(win_pref);
1035 win_pref->SetInteger(L"top", wp.rcNormalPosition.top);
1036 win_pref->SetInteger(L"left", wp.rcNormalPosition.left);
1037 win_pref->SetInteger(L"bottom", wp.rcNormalPosition.bottom);
1038 win_pref->SetInteger(L"right", wp.rcNormalPosition.right);
1039 win_pref->SetBoolean(L"maximized", wp.showCmd == SW_SHOWMAXIMIZED);
1042 void Browser::FocusLocationBar() {
1043 LocationBarView* location_bar = GetLocationBarView();
1044 if (location_bar)
1045 location_bar->location_entry()->SetFocus();
1048 void Browser::SyncHistoryWithTabs(int index) {
1049 if (!profile()->HasSessionService())
1050 return;
1051 SessionService* session_service = profile()->GetSessionService();
1052 if (session_service) {
1053 for (int i = index; i < tab_count(); ++i) {
1054 TabContents* contents = GetTabContentsAt(i);
1055 if (contents) {
1056 session_service->SetTabIndexInWindow(
1057 session_id(), contents->controller()->session_id(), i);
1063 void Browser::ToolbarSizeChanged(TabContents* source, bool is_animating) {
1064 if (source == GetSelectedTabContents() || source == NULL) {
1065 // This will refresh the shelf if needed.
1066 window_->SelectedTabToolbarSizeChanged(is_animating);
1070 void Browser::MoveToFront(bool should_activate) {
1071 window_->Activate();
1074 bool Browser::ShouldCloseWindow() {
1075 if (HasCompletedUnloadProcessing()) {
1076 return true;
1078 is_attempting_to_close_browser_ = true;
1080 for (int i = 0; i < tab_count(); ++i) {
1081 if (tabstrip_model_.TabHasUnloadListener(i)) {
1082 TabContents* tab = GetTabContentsAt(i);
1083 tabs_needing_before_unload_fired_.push_back(tab);
1087 if (tabs_needing_before_unload_fired_.empty())
1088 return true;
1090 ProcessPendingTabs();
1091 return false;
1094 void Browser::ProcessPendingTabs() {
1095 DCHECK(is_attempting_to_close_browser_);
1097 if (HasCompletedUnloadProcessing()) {
1098 // We've finished all the unload events and can proceed to close the
1099 // browser.
1100 OnWindowClosing();
1101 return;
1104 // Process beforeunload tabs first. When that queue is empty, process
1105 // unload tabs.
1106 if (!tabs_needing_before_unload_fired_.empty()) {
1107 TabContents* tab = tabs_needing_before_unload_fired_.back();
1108 tab->AsWebContents()->render_view_host()->FirePageBeforeUnload();
1109 } else if (!tabs_needing_unload_fired_.empty()) {
1110 // We've finished firing all beforeunload events and can proceed with unload
1111 // events.
1112 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting
1113 // somewhere around here so that we have accurate measurements of shutdown
1114 // time.
1115 // TODO(ojan): We can probably fire all the unload events in parallel and
1116 // get a perf benefit from that in the cases where the tab hangs in it's
1117 // unload handler or takes a long time to page in.
1118 TabContents* tab = tabs_needing_unload_fired_.back();
1119 tab->AsWebContents()->render_view_host()->FirePageUnload();
1120 } else {
1121 NOTREACHED();
1125 bool Browser::HasCompletedUnloadProcessing() {
1126 return is_attempting_to_close_browser_ &&
1127 tabs_needing_before_unload_fired_.empty() &&
1128 tabs_needing_unload_fired_.empty();
1131 void Browser::CancelWindowClose() {
1132 DCHECK(is_attempting_to_close_browser_);
1133 // Only cancelling beforeunload should be able to cancel the window's close.
1134 // So there had better be a tab that we think needs beforeunload fired.
1135 DCHECK(!tabs_needing_before_unload_fired_.empty());
1137 tabs_needing_before_unload_fired_.clear();
1138 tabs_needing_unload_fired_.clear();
1140 is_attempting_to_close_browser_ = false;
1143 void Browser::BeforeUnloadFired(TabContents* tab,
1144 bool proceed,
1145 bool* proceed_to_fire_unload) {
1146 if (!is_attempting_to_close_browser_) {
1147 *proceed_to_fire_unload = proceed;
1148 return;
1151 if (!proceed) {
1152 CancelWindowClose();
1153 *proceed_to_fire_unload = false;
1154 return;
1157 if (RemoveFromVector(&tabs_needing_before_unload_fired_, tab)) {
1158 // Now that beforeunload has fired, put the tab on the queue to fire unload.
1159 tabs_needing_unload_fired_.push_back(tab);
1160 ProcessPendingTabs();
1161 // We want to handle firing the unload event ourselves since we want to
1162 // fire all the beforeunload events before attempting to fire the unload
1163 // events should the user cancel closing the browser.
1164 *proceed_to_fire_unload = false;
1165 return;
1168 *proceed_to_fire_unload = true;
1171 void Browser::ClearUnloadState(TabContents* tab) {
1172 DCHECK(is_attempting_to_close_browser_);
1173 RemoveFromVector(&tabs_needing_before_unload_fired_, tab);
1174 RemoveFromVector(&tabs_needing_unload_fired_, tab);
1175 ProcessPendingTabs();
1178 bool Browser::RemoveFromVector(UnloadListenerVector* vector, TabContents* tab) {
1179 DCHECK(is_attempting_to_close_browser_);
1181 for (UnloadListenerVector::iterator it = vector->begin();
1182 it != vector->end();
1183 ++it) {
1184 if (*it == tab) {
1185 vector->erase(it);
1186 return true;
1189 return false;
1192 void Browser::OnWindowClosing() {
1193 if (!ShouldCloseWindow())
1194 return;
1196 if (BrowserList::size() == 1)
1197 browser_shutdown::OnShutdownStarting(browser_shutdown::WINDOW_CLOSE);
1199 // Don't use HasSessionService here, we want to force creation of the
1200 // session service so that user can restore what was open.
1201 SessionService* session_service = profile()->GetSessionService();
1202 if (session_service)
1203 session_service->WindowClosing(session_id());
1205 CloseAllTabs();
1208 // Tab Creation Functions
1210 TabContents* Browser::AddTabWithURL(
1211 const GURL& url, PageTransition::Type transition, bool foreground,
1212 SiteInstance* instance) {
1213 if (type_ == BrowserType::APPLICATION && tabstrip_model_.count() == 1) {
1214 NOTREACHED() << "Cannot add a tab in a mono tab application.";
1215 return NULL;
1218 GURL url_to_load = url;
1219 if (url_to_load.is_empty())
1220 url_to_load = GetHomePage();
1221 TabContents* contents =
1222 CreateTabContentsForURL(url_to_load, profile_, transition, false,
1223 instance);
1224 tabstrip_model_.AddTabContents(contents, -1, transition, foreground);
1225 // By default, content believes it is not hidden. When adding contents
1226 // in the background, tell it that it's hidden.
1227 if (!foreground)
1228 contents->WasHidden();
1229 return contents;
1232 TabContents* Browser::AddWebApplicationTab(Profile* profile,
1233 WebApp* web_app,
1234 bool lazy) {
1235 DCHECK(web_app);
1237 // TODO(acw): Do we need an "application launched" transition type?
1238 // TODO(creis): Should we reuse the current instance (ie. process) here?
1239 TabContents* contents =
1240 CreateTabContentsForURL(web_app->url(), profile, PageTransition::LINK,
1241 lazy, NULL);
1242 if (contents->AsWebContents())
1243 contents->AsWebContents()->SetWebApp(web_app);
1245 if (lazy) {
1246 contents->controller()->LoadURLLazily(
1247 web_app->url(), PageTransition::LINK, web_app->name(), NULL);
1249 tabstrip_model_.AddTabContents(contents, -1, PageTransition::LINK, !lazy);
1250 return contents;
1253 TabContents* Browser::AddTabWithNavigationController(
1254 NavigationController* ctrl, PageTransition::Type type) {
1255 TabContents* tc = ctrl->active_contents();
1256 tabstrip_model_.AddTabContents(tc, -1, type, true);
1257 return tc;
1260 NavigationController* Browser::AddRestoredTab(
1261 const std::vector<TabNavigation>& navigations,
1262 int selected_navigation,
1263 bool select) {
1264 NavigationController* restored_controller =
1265 BuildRestoredNavigationController(navigations, selected_navigation);
1267 tabstrip_model_.InsertTabContentsAt(
1268 tabstrip_model_.count(),
1269 restored_controller->active_contents(),
1270 select, false);
1271 if (profile_->HasSessionService()) {
1272 SessionService* session_service = profile_->GetSessionService();
1273 if (session_service)
1274 session_service->TabRestored(restored_controller);
1276 return restored_controller;
1279 void Browser::ReplaceRestoredTab(
1280 const std::vector<TabNavigation>& navigations,
1281 int selected_navigation) {
1282 NavigationController* restored_controller =
1283 BuildRestoredNavigationController(navigations, selected_navigation);
1285 tabstrip_model_.ReplaceNavigationControllerAt(
1286 tabstrip_model_.selected_index(),
1287 restored_controller);
1290 ////////////////////////////////////////////////////////////////////////////////
1291 // Browser, TabStripModelDelegate implementation:
1293 void Browser::CreateNewStripWithContents(TabContents* detached_contents,
1294 const gfx::Point& drop_point) {
1295 DCHECK(type_ == BrowserType::TABBED_BROWSER);
1297 // Create an empty new browser window the same size as the old one.
1298 CRect browser_rect;
1299 GetWindowRect(reinterpret_cast<HWND>(window_->GetPlatformID()), &browser_rect);
1300 gfx::Rect rect(0, 0);
1301 if (drop_point.x() != 0 || drop_point.y() != 0) {
1302 rect.SetRect(drop_point.x(), drop_point.y(), browser_rect.Width(),
1303 browser_rect.Height());
1305 Browser* new_window =
1306 new Browser(rect, SW_SHOWNORMAL, profile_, BrowserType::TABBED_BROWSER,
1307 std::wstring());
1308 // Append the TabContents before showing it so the window doesn't flash
1309 // black.
1310 new_window->tabstrip_model()->AppendTabContents(detached_contents, true);
1311 new_window->Show();
1313 // When we detach a tab we need to make sure any associated Find window moves
1314 // along with it to its new home (basically we just make new_window the parent
1315 // of the Find window).
1316 new_window->AdoptFindWindow(detached_contents);
1319 int Browser::GetDragActions() const {
1320 int result = 0;
1321 if (BrowserList::GetBrowserCountForType(profile_,
1322 BrowserType::TABBED_BROWSER) > 1 ||
1323 tab_count() > 1)
1324 result |= TAB_TEAROFF_ACTION;
1325 if (tab_count() > 1)
1326 result |= TAB_MOVE_ACTION;
1327 return result;
1330 TabContents* Browser::CreateTabContentsForURL(
1331 const GURL& url, Profile* profile, PageTransition::Type transition,
1332 bool defer_load, SiteInstance* instance) const {
1333 // Create an appropriate tab contents.
1334 GURL real_url = url;
1335 TabContentsType type = TabContents::TypeForURL(&real_url);
1336 DCHECK(type != TAB_CONTENTS_UNKNOWN_TYPE);
1338 TabContents* contents =
1339 TabContents::CreateWithType(type, GetTopLevelHWND(), profile, instance);
1340 contents->SetupController(profile);
1342 if (!defer_load) {
1343 // Load the initial URL before adding the new tab contents to the tab strip
1344 // so that the tab contents has navigation state.
1345 contents->controller()->LoadURL(url, transition);
1348 return contents;
1351 void Browser::ShowApplicationMenu(const gfx::Point p) {
1352 if (!window_)
1353 return;
1355 HWND hwnd = reinterpret_cast<HWND>(window_->GetPlatformID());
1356 CPoint t;
1357 t.x = p.x();
1358 t.y = p.y();
1359 RunSimpleFrameMenu(t, hwnd);
1362 void Browser::ValidateLoadingAnimations() {
1363 if (window_)
1364 window_->ValidateThrobber();
1367 void Browser::CloseFrameAfterDragSession() {
1368 // This is scheduled to run after we return to the message loop because
1369 // otherwise the frame will think the drag session is still active and ignore
1370 // the request.
1371 MessageLoop::current()->PostTask(FROM_HERE,
1372 method_factory_.NewRunnableMethod(&Browser::CloseFrame));
1375 ////////////////////////////////////////////////////////////////////////////////
1376 // Browser, TabStripModelObserver implementation:
1378 void Browser::TabInsertedAt(TabContents* contents,
1379 int index,
1380 bool foreground) {
1381 contents->set_delegate(this);
1382 contents->controller()->SetWindowID(session_id());
1384 SyncHistoryWithTabs(tabstrip_model_.GetIndexOfTabContents(contents));
1386 // When a tab is dropped into a tab strip we need to make sure that the
1387 // associated Find window is moved along with it. We therefore change the
1388 // parent of the Find window (if the parent is already correctly set this
1389 // does nothing).
1390 AdoptFindWindow(contents);
1392 // If the tab crashes in the beforeunload or unload handler, it won't be
1393 // able to ack. But we know we can close it.
1394 NotificationService::current()->
1395 AddObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED,
1396 Source<TabContents>(contents));
1399 void Browser::TabClosingAt(TabContents* contents, int index) {
1400 NavigationController* controller = contents->controller();
1401 DCHECK(controller);
1402 NotificationService::current()->
1403 Notify(NOTIFY_TAB_CLOSING,
1404 Source<NavigationController>(controller),
1405 NotificationService::NoDetails());
1407 // Sever the TabContents' connection back to us.
1408 contents->set_delegate(NULL);
1410 if (!g_browser_process->IsUsingNewFrames() &&
1411 contents == GetSelectedTabContents()) {
1412 // We need to reset the current tab contents to NULL before it gets
1413 // freed. This is because the focus manager performs some operation
1414 // on the selected tab contents when it is removed.
1415 window_->ShowTabContents(NULL);
1419 void Browser::TabDetachedAt(TabContents* contents, int index) {
1420 if (!g_browser_process->IsUsingNewFrames()) {
1421 // TODO(beng): (http://b/1085418) figure out if we really need to do this
1422 // here - surely the subsequent selection of another tab would
1423 // result in this action taking place?
1424 if (contents == GetSelectedTabContents())
1425 RemoveShelvesForTabContents(contents);
1428 contents->set_delegate(NULL);
1429 if (!tabstrip_model_.closing_all())
1430 SyncHistoryWithTabs(0);
1432 RemoveScheduledUpdatesFor(contents);
1434 NotificationService::current()->
1435 RemoveObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED,
1436 Source<TabContents>(contents));
1439 void Browser::TabSelectedAt(TabContents* old_contents,
1440 TabContents* new_contents,
1441 int index,
1442 bool user_gesture) {
1443 DCHECK(old_contents != new_contents);
1445 // If we have any update pending, do it now.
1446 if (!chrome_updater_factory_.empty() && old_contents)
1447 ProcessPendingUIUpdates();
1449 LocationBarView* location_bar = GetLocationBarView();
1450 if (old_contents) {
1451 if (!g_browser_process->IsUsingNewFrames()) {
1452 // Have the contents remember where focus was.
1453 old_contents->StoreFocus();
1456 // Save what the user's currently typing, so it can be restored when we
1457 // switch back to this tab.
1458 if (location_bar)
1459 location_bar->location_entry()->SaveStateToTab(old_contents);
1462 if (!g_browser_process->IsUsingNewFrames()) {
1463 // Tell the frame what happened so that the TabContents gets resized, etc.
1464 window_->ShowTabContents(new_contents);
1466 // Inform the tab that it is now selected.
1467 new_contents->DidBecomeSelected();
1468 if (BrowserList::GetLastActive() == this)
1469 new_contents->RestoreFocus();
1472 // Propagate the profile to the location bar.
1473 if (!g_browser_process->IsUsingNewFrames())
1474 window_->ProfileChanged(new_contents->profile());
1475 UpdateToolBar(true);
1477 // Force the go/stop button to change.
1478 if (new_contents->AsWebContents()) {
1479 GetGoButton()->ChangeMode(
1480 new_contents->is_loading() ? GoButton::MODE_STOP : GoButton::MODE_GO);
1481 } else {
1482 GetGoButton()->ChangeMode(GoButton::MODE_GO);
1485 // Update other parts of the toolbar.
1486 UpdateNavigationCommands();
1488 // Reset the status bubble.
1489 GetStatusBubble()->Hide();
1491 // Show the loading state (if any).
1492 GetStatusBubble()->SetStatus(GetSelectedTabContents()->GetStatusText());
1494 if (!g_browser_process->IsUsingNewFrames())
1495 SyncWindowTitle();
1497 // Update sessions. Don't force creation of sessions. If sessions doesn't
1498 // exist, the change will be picked up by sessions when created.
1499 if (profile_->HasSessionService()) {
1500 SessionService* session_service = profile_->GetSessionService();
1501 if (session_service && !tabstrip_model_.closing_all()) {
1502 session_service->SetSelectedTabInWindow(session_id(),
1503 tabstrip_model_.selected_index());
1508 void Browser::TabMoved(TabContents* contents,
1509 int from_index,
1510 int to_index) {
1511 DCHECK(from_index >= 0 && to_index >= 0);
1512 // Notify the history service.
1513 SyncHistoryWithTabs(std::min(from_index, to_index));
1516 void Browser::TabStripEmpty() {
1517 if (!g_browser_process->IsUsingNewFrames()) {
1518 // We need to reset the frame contents just in case this wasn't done while
1519 // detaching the tab. This happens when dragging out the last tab.
1520 window_->ShowTabContents(NULL);
1523 // Close the frame after we return to the message loop (not immediately,
1524 // otherwise it will destroy this object before the stack has a chance to
1525 // cleanly unwind.)
1526 // Note: This will be called several times if TabStripEmpty is called several
1527 // times. This is because it does not close the window if tabs are
1528 // still present.
1529 // NOTE: If you change to be immediate (no invokeLater) then you'll need to
1530 // update BrowserList::CloseAllBrowsers.
1531 MessageLoop::current()->PostTask(FROM_HERE,
1532 method_factory_.NewRunnableMethod(&Browser::CloseFrame));
1535 void Browser::RemoveShelvesForTabContents(TabContents* contents) {
1536 DCHECK(!g_browser_process->IsUsingNewFrames());
1538 ChromeViews::View* shelf = contents->GetDownloadShelfView();
1539 if (shelf && shelf->GetParent() != NULL)
1540 shelf->GetParent()->RemoveChildView(shelf);
1542 if (contents->AsWebContents()) {
1543 ChromeViews::View* info_bar =
1544 contents->AsWebContents()->view()->GetInfoBarView();
1545 if (info_bar && info_bar->GetParent() != NULL)
1546 info_bar->GetParent()->RemoveChildView(info_bar);
1550 BrowserType::Type Browser::GetType() const {
1551 return type_;
1554 void Browser::InitHangMonitor() {
1555 PrefService* pref_service = g_browser_process->local_state();
1556 DCHECK(pref_service != NULL);
1557 int plugin_message_response_timeout =
1558 pref_service->GetInteger(prefs::kPluginMessageResponseTimeout);
1559 int hung_plugin_detect_freq =
1560 pref_service->GetInteger(prefs::kHungPluginDetectFrequency);
1561 if ((hung_plugin_detect_freq > 0) &&
1562 hung_window_detector_.Initialize(GetTopLevelHWND(),
1563 plugin_message_response_timeout)) {
1564 ticker_.set_tick_interval(hung_plugin_detect_freq);
1565 ticker_.RegisterTickHandler(&hung_window_detector_);
1566 ticker_.Start();
1568 pref_service->SetInteger(prefs::kPluginMessageResponseTimeout,
1569 plugin_message_response_timeout);
1570 pref_service->SetInteger(prefs::kHungPluginDetectFrequency,
1571 hung_plugin_detect_freq);
1576 Browser* Browser::GetOrCreateTabbedBrowser() {
1577 Browser* browser = BrowserList::FindBrowserWithType(
1578 profile_, BrowserType::TABBED_BROWSER);
1579 if (!browser) {
1580 browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
1581 BrowserType::TABBED_BROWSER, std::wstring());
1583 return browser;
1586 void Browser::RemoveScheduledUpdatesFor(TabContents* contents) {
1587 if (!contents)
1588 return;
1590 // Remove any pending UI updates for the detached tab.
1591 UpdateVector::iterator cur_update = scheduled_updates_.begin();
1592 while (cur_update != scheduled_updates_.end()) {
1593 if (cur_update->source == contents) {
1594 cur_update = scheduled_updates_.erase(cur_update);
1595 } else {
1596 ++cur_update;
1601 void Browser::ShowNativeUI(const GURL& url) {
1602 int i, c;
1603 TabContents* tc;
1604 for (i = 0, c = tabstrip_model_.count(); i < c; ++i) {
1605 tc = tabstrip_model_.GetTabContentsAt(i);
1606 if (tc->type() == TAB_CONTENTS_NATIVE_UI &&
1607 tc->GetURL() == url) {
1608 tabstrip_model_.SelectTabContentsAt(i, false);
1609 return;
1613 TabContents* contents = CreateTabContentsForURL(url, profile_,
1614 PageTransition::LINK, false,
1615 NULL);
1616 AddNewContents(NULL, contents, NEW_FOREGROUND_TAB, gfx::Rect(), true);
1619 NavigationController* Browser::BuildRestoredNavigationController(
1620 const std::vector<TabNavigation>& navigations,
1621 int selected_navigation) {
1622 if (!navigations.empty()) {
1623 DCHECK(selected_navigation >= 0 &&
1624 selected_navigation < static_cast<int>(navigations.size()));
1625 // We should have a valid URL, if we don't fall back to the default.
1626 GURL url = navigations[selected_navigation].url;
1627 if (url.is_empty())
1628 url = GetHomePage();
1630 // Create a NavigationController. This constructor creates the appropriate
1631 // set of TabContents.
1632 return new NavigationController(
1633 profile_, navigations, selected_navigation, GetTopLevelHWND());
1634 } else {
1635 // No navigations. Create a tab with about:blank.
1636 TabContents* contents =
1637 CreateTabContentsForURL(GURL("about:blank"), profile_,
1638 PageTransition::START_PAGE, false, NULL);
1639 return new NavigationController(contents, profile_);
1643 // static
1644 void Browser::OpenURLOffTheRecord(Profile* profile, const GURL& url) {
1645 Profile* off_the_record_profile = profile->GetOffTheRecordProfile();
1646 Browser* browser = BrowserList::FindBrowserWithType(
1647 off_the_record_profile, BrowserType::TABBED_BROWSER);
1648 if (browser == NULL) {
1649 browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, off_the_record_profile,
1650 BrowserType::TABBED_BROWSER, L"");
1652 browser->AddTabWithURL(url, PageTransition::LINK, true, NULL);
1653 browser->Show();
1654 browser->MoveToFront(true);
1657 // static
1658 std::wstring Browser::ComputePopupTitle(const GURL& url,
1659 const std::wstring& title) {
1660 DCHECK(!g_browser_process->IsUsingNewFrames());
1661 std::wstring result(title);
1662 FormatTitleForDisplay(&result);
1663 return result;
1666 void Browser::ConvertToTabbedBrowser() {
1667 if (GetType() != BrowserType::BROWSER) {
1668 NOTREACHED();
1669 return;
1672 int tab_strip_index = tabstrip_model_.selected_index();
1673 TabContents* contents = tabstrip_model_.DetachTabContentsAt(tab_strip_index);
1674 Browser* browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
1675 BrowserType::TABBED_BROWSER, L"");
1676 browser->AddNewContents(
1677 NULL, contents, NEW_FOREGROUND_TAB, gfx::Rect(), true);
1678 browser->Show();
1681 void Browser::BuildPopupWindow(TabContents* source,
1682 TabContents* new_contents,
1683 const gfx::Rect& initial_pos) {
1684 BrowserType::Type type =
1685 type_ == BrowserType::APPLICATION ? type_ : BrowserType::BROWSER;
1686 Browser* browser = new Browser(initial_pos, SW_SHOWNORMAL, profile_, type,
1687 std::wstring());
1688 browser->AddNewContents(source, new_contents,
1689 NEW_FOREGROUND_TAB, gfx::Rect(), true);
1691 if (!g_browser_process->IsUsingNewFrames()) {
1692 // TODO(beng): (1031854) Move most of this to the frames!!
1693 // For newly opened popup windows, the incoming width/height
1694 // numbers are for the content area, but x/y are for the actual
1695 // window position. Thus we can't just call MoveContents().
1696 gfx::Rect window_rect =
1697 browser->window()->GetBoundsForContentBounds(initial_pos);
1698 window_rect.set_origin(initial_pos.origin());
1700 // When we are given x/y coordinates of 0 on a created popup window,
1701 // assume none were given by the window.open() command.
1702 if (window_rect.x() == 0 && window_rect.y() == 0) {
1703 gfx::Point origin = window()->GetNormalBounds().origin();
1704 origin.set_x(origin.x() + kWindowTilePixels);
1705 origin.set_y(origin.y() + kWindowTilePixels);
1706 window_rect.set_origin(origin);
1709 ::SetWindowPos(browser->GetTopLevelHWND(), NULL,
1710 window_rect.x(), window_rect.y(),
1711 window_rect.width(), window_rect.height(), 0);
1712 win_util::AdjustWindowToFit(browser->GetTopLevelHWND());
1715 browser->Show();
1718 void Browser::ConvertContentsToApplication(TabContents* contents) {
1719 if (!contents->AsWebContents() || !contents->AsWebContents()->web_app()) {
1720 NOTREACHED();
1721 return;
1724 int index = tabstrip_model_.GetIndexOfTabContents(contents);
1725 if (index < 0)
1726 return;
1728 WebApp* app = contents->AsWebContents()->web_app();
1729 const std::wstring& app_name =
1730 app->name().empty() ? ComputeApplicationNameFromURL(app->url()) :
1731 app->name();
1732 RegisterAppPrefs(app_name);
1734 tabstrip_model_.DetachTabContentsAt(index);
1735 Browser* browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
1736 BrowserType::APPLICATION, app_name);
1737 browser->AddNewContents(
1738 NULL, contents, NEW_FOREGROUND_TAB, gfx::Rect(), true);
1739 browser->Show();
1742 // static
1743 std::wstring Browser::ComputeApplicationNameFromURL(const GURL& url) {
1744 std::string t;
1745 t.append(url.host());
1746 t.append("_");
1747 t.append(url.path());
1748 return UTF8ToWide(t);
1751 // static
1752 void Browser::OpenWebApplication(Profile* profile,
1753 WebApp* app,
1754 int show_command) {
1755 const std::wstring& app_name =
1756 app->name().empty() ? ComputeApplicationNameFromURL(app->url()) :
1757 app->name();
1759 RegisterAppPrefs(app_name);
1760 Browser* browser = new Browser(gfx::Rect(), show_command, profile,
1761 BrowserType::APPLICATION, app_name);
1762 browser->AddWebApplicationTab(profile, app, false);
1763 browser->Show();
1766 // static
1767 void Browser::RegisterAppPrefs(const std::wstring& app_name) {
1768 // A set of apps that we've already started.
1769 static std::set<std::wstring>* g_app_names = NULL;
1771 if (!g_app_names)
1772 g_app_names = new std::set<std::wstring>;
1774 // Only register once for each app name.
1775 if (g_app_names->find(app_name) != g_app_names->end())
1776 return;
1777 g_app_names->insert(app_name);
1779 // We need to register the window position pref.
1780 std::wstring window_pref(prefs::kBrowserWindowPlacement);
1781 window_pref.append(L"_");
1782 window_pref.append(app_name);
1783 PrefService* prefs = g_browser_process->local_state();
1784 DCHECK(prefs);
1786 prefs->RegisterDictionaryPref(window_pref.c_str());
1789 NavigationController* Browser::GetSelectedNavigationController() const {
1790 TabContents* tc = GetSelectedTabContents();
1791 if (tc)
1792 return tc->controller();
1793 else
1794 return NULL;
1797 ///////////////////////////////////////////////////////////////////////////////
1798 // NEW FRAME TODO(beng): clean up this file
1799 // DO NOT PLACE METHODS NOT RELATED TO NEW FRAMES BELOW THIS LINE.
1801 void Browser::SaveWindowPosition(const gfx::Rect& bounds, bool maximized) {
1802 // We don't save window position for popups.
1803 if (GetType() == BrowserType::BROWSER)
1804 return;
1806 // First save to local state, this is for remembering on subsequent starts.
1807 PrefService* prefs = g_browser_process->local_state();
1808 DCHECK(prefs);
1809 std::wstring name(prefs::kBrowserWindowPlacement);
1810 if (!app_name_.empty()) {
1811 name.append(L"_");
1812 name.append(app_name_);
1815 DictionaryValue* win_pref = prefs->GetMutableDictionary(name.c_str());
1816 DCHECK(win_pref);
1817 win_pref->SetInteger(L"top", bounds.y());
1818 win_pref->SetInteger(L"left", bounds.x());
1819 win_pref->SetInteger(L"bottom", bounds.bottom());
1820 win_pref->SetInteger(L"right", bounds.right());
1821 win_pref->SetBoolean(L"maximized", maximized);
1823 // Then save to the session storage service, used when reloading a past
1824 // session. Note that we don't want to be the ones who cause lazy
1825 // initialization of the session service. This function gets called during
1826 // initial window showing, and we don't want to bring in the session service
1827 // this early.
1828 if (profile()->HasSessionService()) {
1829 SessionService* session_service = profile()->GetSessionService();
1830 if (session_service)
1831 session_service->SetWindowBounds(session_id_, bounds, maximized);
1835 void Browser::RestoreWindowPosition(gfx::Rect* bounds, bool* maximized) {
1836 DCHECK(bounds && maximized);
1837 WindowSizer::GetBrowserWindowBounds(app_name_, *bounds, bounds, maximized);
1840 SkBitmap Browser::GetCurrentPageIcon() const {
1841 TabContents* contents = tabstrip_model_.GetSelectedTabContents();
1842 return contents ? contents->GetFavIcon() : SkBitmap();
1845 std::wstring Browser::GetCurrentPageTitle() const {
1846 TabContents* contents = tabstrip_model_.GetSelectedTabContents();
1847 std::wstring title;
1848 if (contents) {
1849 title = contents->GetTitle();
1850 FormatTitleForDisplay(&title);
1852 if (title.empty())
1853 title = l10n_util::GetString(IDS_TAB_UNTITLED_TITLE);
1855 return l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT, title);
1858 // static
1859 void Browser::FormatTitleForDisplay(std::wstring* title) {
1860 size_t current_index = 0;
1861 size_t match_index;
1862 while ((match_index = title->find(L'\n', current_index)) !=
1863 std::wstring::npos) {
1864 title->replace(match_index, 1, L"");
1865 current_index = match_index;