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"
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
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
{
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
95 class BrowserIdleTimer
: public base::IdleTimer
{
98 : base::IdleTimer(TimeDelta::FromSeconds(kBrowserReleaseMemoryInterval
),
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
)
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 ////////////////////////////////////////////////////////////////////////////////
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
) {
149 *index_result
= index
;
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);
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);
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
,
193 BrowserType::Type type
,
194 const std::wstring
& app_name
)
197 initial_show_command_(show_command
),
198 is_attempting_to_close_browser_(false),
200 chrome_updater_factory_(this),
201 method_factory_(this),
202 hung_window_detector_(&hung_plugin_action_
),
204 tabstrip_model_(this, profile
),
205 toolbar_model_(this),
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
))
220 initial_show_command_
= SW_SHOWMAXIMIZED
;
221 window_
= BrowserWindow::CreateBrowserWindow(this, create_bounds
,
224 // See note where SIZE_TO_CONTENTS is defined in browser.h for an explanation
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
))
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();
251 session_service
->SetWindowType(session_id_
, type_
);
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
)
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
288 profile_
->ResetTabRestoreService();
291 SessionService
* session_service
= profile_
->GetSessionService();
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.
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.
332 window_
->Show(initial_show_command_
, resize_to_fit
);
333 if ((initial_show_command_
== SW_SHOWNORMAL
) ||
334 (initial_show_command_
== SW_SHOWMAXIMIZED
))
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() {
357 GURL
Browser::GetHomePage() {
358 if (profile_
->GetPrefs()->GetBoolean(prefs::kHomePageIsNewTabPage
)) {
359 return NewTabUIURL();
361 GURL home_page
= GURL(URLFixerUpper::FixupURL(
362 profile_
->GetPrefs()->GetString(prefs::kHomePage
),
364 if (!home_page
.is_valid())
365 return NewTabUIURL();
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
));
382 window_
->SetWindowTitle(
383 l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT
,
384 current_tab
->GetTitle()));
387 ////////////////////////////////////////////////////////////////////////////////
390 void Browser::WindowActivationChanged(bool 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() {
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
++) {
442 for (int tab
= 0; tab
< tab_count(); tab
++) {
443 if (GetTabContentsAt(tab
)->controller() ==
444 scheduled_updates_
[i
].source
->controller()) {
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
;
474 updated
->second
|= flags
;
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
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
,
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
)) {
545 const WebContents
* const web_contents
= current_tab
->AsWebContents();
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
);
565 Browser
* b
= GetOrCreateTabbedBrowser();
568 // If we have just created a new browser window, make sure we select the
570 if (b
->tab_count() == 0 && disposition
== NEW_BACKGROUND_TAB
)
571 disposition
= NEW_FOREGROUND_TAB
;
573 b
->OpenURL(url
, disposition
, transition
);
575 b
->MoveToFront(true);
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
);
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
);
630 } else if (disposition
!= SUPPRESS_OPEN
) {
632 AddTabWithURL(url
, transition
, disposition
!= NEW_BACKGROUND_TAB
,
636 if (disposition
!= NEW_BACKGROUND_TAB
&& source_tab_was_frontmost
) {
637 // Give the focus to the newly navigated tab, if the source tab was
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,
651 // Only update the UI when something visible has changed.
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
,
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();
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);
713 b
->MoveToFront(true);
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
);
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
)
746 Browser
* browser
= new Browser(contents_bounds
, SIZE_TO_CONTENTS
, profile_
,
748 browser
->AddNewContents(
749 source
, new_contents
, NEW_FOREGROUND_TAB
, contents_bounds
, true);
751 browser
->window_
->ContinueDetachConstrainedWindowDrag(
752 mouse_pt
, frame_component
);
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);
764 browser
->window()->ContinueDetachConstrainedWindowDrag(mouse_pt
,
769 void Browser::ActivateContents(TabContents
* contents
) {
770 tabstrip_model_
.SelectTabContentsAt(
771 tabstrip_model_
.GetIndexOfTabContents(contents
), false);
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
);
803 int index
= tabstrip_model_
.GetIndexOfTabContents(source
);
804 if (index
== TabStripModel::kNoTab
) {
805 NOTREACHED() << "CloseContents called for tab not in our strip";
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";
817 ::SetWindowPos(GetTopLevelHWND(), NULL
, pos
.x(), pos
.y(), pos
.width(),
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
,
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(),
833 html_view
->InitDialog();
834 html_view
->window()->Show();
837 void Browser::Observe(NotificationType type
,
838 const NotificationSource
& source
,
839 const NotificationDetails
& details
) {
841 case NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED
: {
842 DCHECK(!g_browser_process
->IsUsingNewFrames());
843 TabContents
* current_tab
= GetSelectedTabContents();
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();
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()));
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);
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();
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);
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();
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())
1001 SessionService
* session_service
= profile()->GetSessionService();
1002 if (!session_service
)
1006 wp
.length
= sizeof(wp
);
1008 HWND hwnd
= reinterpret_cast<HWND
>(window_
->GetPlatformID());
1009 if (!::GetWindowPlacement(hwnd
, &wp
))
1012 session_service
->SetWindowBounds(session_id_
,
1013 gfx::Rect(wp
.rcNormalPosition
),
1014 (wp
.showCmd
& SW_MAXIMIZE
) == SW_MAXIMIZE
);
1017 void Browser::SaveWindowPlacement() {
1019 wp
.length
= sizeof(wp
);
1021 HWND hwnd
= reinterpret_cast<HWND
>(window_
->GetPlatformID());
1022 if (!::GetWindowPlacement(hwnd
, &wp
))
1025 PrefService
* prefs
= g_browser_process
->local_state();
1027 std::wstring
name(prefs::kBrowserWindowPlacement
);
1028 if (!app_name_
.empty()) {
1030 name
.append(app_name_
);
1033 DictionaryValue
* win_pref
= prefs
->GetMutableDictionary(name
.c_str());
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();
1045 location_bar
->location_entry()->SetFocus();
1048 void Browser::SyncHistoryWithTabs(int index
) {
1049 if (!profile()->HasSessionService())
1051 SessionService
* session_service
= profile()->GetSessionService();
1052 if (session_service
) {
1053 for (int i
= index
; i
< tab_count(); ++i
) {
1054 TabContents
* contents
= GetTabContentsAt(i
);
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()) {
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())
1090 ProcessPendingTabs();
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
1104 // Process beforeunload tabs first. When that queue is empty, process
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
1112 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting
1113 // somewhere around here so that we have accurate measurements of shutdown
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();
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
,
1145 bool* proceed_to_fire_unload
) {
1146 if (!is_attempting_to_close_browser_
) {
1147 *proceed_to_fire_unload
= proceed
;
1152 CancelWindowClose();
1153 *proceed_to_fire_unload
= false;
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;
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();
1192 void Browser::OnWindowClosing() {
1193 if (!ShouldCloseWindow())
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());
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.";
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,
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.
1228 contents
->WasHidden();
1232 TabContents
* Browser::AddWebApplicationTab(Profile
* profile
,
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
,
1242 if (contents
->AsWebContents())
1243 contents
->AsWebContents()->SetWebApp(web_app
);
1246 contents
->controller()->LoadURLLazily(
1247 web_app
->url(), PageTransition::LINK
, web_app
->name(), NULL
);
1249 tabstrip_model_
.AddTabContents(contents
, -1, PageTransition::LINK
, !lazy
);
1253 TabContents
* Browser::AddTabWithNavigationController(
1254 NavigationController
* ctrl
, PageTransition::Type type
) {
1255 TabContents
* tc
= ctrl
->active_contents();
1256 tabstrip_model_
.AddTabContents(tc
, -1, type
, true);
1260 NavigationController
* Browser::AddRestoredTab(
1261 const std::vector
<TabNavigation
>& navigations
,
1262 int selected_navigation
,
1264 NavigationController
* restored_controller
=
1265 BuildRestoredNavigationController(navigations
, selected_navigation
);
1267 tabstrip_model_
.InsertTabContentsAt(
1268 tabstrip_model_
.count(),
1269 restored_controller
->active_contents(),
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.
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
,
1308 // Append the TabContents before showing it so the window doesn't flash
1310 new_window
->tabstrip_model()->AppendTabContents(detached_contents
, true);
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 {
1321 if (BrowserList::GetBrowserCountForType(profile_
,
1322 BrowserType::TABBED_BROWSER
) > 1 ||
1324 result
|= TAB_TEAROFF_ACTION
;
1325 if (tab_count() > 1)
1326 result
|= TAB_MOVE_ACTION
;
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
);
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
);
1351 void Browser::ShowApplicationMenu(const gfx::Point p
) {
1355 HWND hwnd
= reinterpret_cast<HWND
>(window_
->GetPlatformID());
1359 RunSimpleFrameMenu(t
, hwnd
);
1362 void Browser::ValidateLoadingAnimations() {
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
1371 MessageLoop::current()->PostTask(FROM_HERE
,
1372 method_factory_
.NewRunnableMethod(&Browser::CloseFrame
));
1375 ////////////////////////////////////////////////////////////////////////////////
1376 // Browser, TabStripModelObserver implementation:
1378 void Browser::TabInsertedAt(TabContents
* contents
,
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
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();
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
,
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();
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.
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
);
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())
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
,
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
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
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 {
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_
);
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
);
1580 browser
= new Browser(gfx::Rect(), SW_SHOWNORMAL
, profile_
,
1581 BrowserType::TABBED_BROWSER
, std::wstring());
1586 void Browser::RemoveScheduledUpdatesFor(TabContents
* contents
) {
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
);
1601 void Browser::ShowNativeUI(const GURL
& url
) {
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);
1613 TabContents
* contents
= CreateTabContentsForURL(url
, profile_
,
1614 PageTransition::LINK
, false,
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
;
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());
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_
);
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
);
1654 browser
->MoveToFront(true);
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
);
1666 void Browser::ConvertToTabbedBrowser() {
1667 if (GetType() != BrowserType::BROWSER
) {
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);
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
,
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());
1718 void Browser::ConvertContentsToApplication(TabContents
* contents
) {
1719 if (!contents
->AsWebContents() || !contents
->AsWebContents()->web_app()) {
1724 int index
= tabstrip_model_
.GetIndexOfTabContents(contents
);
1728 WebApp
* app
= contents
->AsWebContents()->web_app();
1729 const std::wstring
& app_name
=
1730 app
->name().empty() ? ComputeApplicationNameFromURL(app
->url()) :
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);
1743 std::wstring
Browser::ComputeApplicationNameFromURL(const GURL
& url
) {
1745 t
.append(url
.host());
1747 t
.append(url
.path());
1748 return UTF8ToWide(t
);
1752 void Browser::OpenWebApplication(Profile
* profile
,
1755 const std::wstring
& app_name
=
1756 app
->name().empty() ? ComputeApplicationNameFromURL(app
->url()) :
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);
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
;
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())
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();
1786 prefs
->RegisterDictionaryPref(window_pref
.c_str());
1789 NavigationController
* Browser::GetSelectedNavigationController() const {
1790 TabContents
* tc
= GetSelectedTabContents();
1792 return tc
->controller();
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
)
1806 // First save to local state, this is for remembering on subsequent starts.
1807 PrefService
* prefs
= g_browser_process
->local_state();
1809 std::wstring
name(prefs::kBrowserWindowPlacement
);
1810 if (!app_name_
.empty()) {
1812 name
.append(app_name_
);
1815 DictionaryValue
* win_pref
= prefs
->GetMutableDictionary(name
.c_str());
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
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();
1849 title
= contents
->GetTitle();
1850 FormatTitleForDisplay(&title
);
1853 title
= l10n_util::GetString(IDS_TAB_UNTITLED_TITLE
);
1855 return l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT
, title
);
1859 void Browser::FormatTitleForDisplay(std::wstring
* title
) {
1860 size_t current_index
= 0;
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
;