blob: 0215ec7665f647b9dfd7beb33e362d7a62f21021 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// 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.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/tab_contents.h"
6
7#include "chrome/browser/cert_store.h"
8#include "chrome/browser/navigation_entry.h"
9#include "chrome/browser/views/download_shelf_view.h"
10#include "chrome/browser/views/download_started_animation.h"
[email protected]d6598c052008-11-05 19:03:2511#include "chrome/browser/views/blocked_popup_container.h"
initial.commit09911bf2008-07-26 23:55:2912#include "chrome/browser/web_contents.h"
13#include "chrome/browser/tab_contents_delegate.h"
[email protected]a4feef82008-10-02 15:11:2214#include "chrome/common/l10n_util.h"
initial.commit09911bf2008-07-26 23:55:2915#include "chrome/common/pref_names.h"
[email protected]1eb89e82008-08-15 12:27:0316#include "chrome/common/pref_service.h"
[email protected]4d0bd102008-10-16 00:26:3017#include "chrome/views/container.h"
initial.commit09911bf2008-07-26 23:55:2918#include "chrome/views/native_scroll_bar.h"
[email protected]1eb89e82008-08-15 12:27:0319#include "chrome/views/root_view.h"
initial.commit09911bf2008-07-26 23:55:2920#include "chrome/views/view.h"
21#include "chrome/views/view_storage.h"
22
23#include "generated_resources.h"
24
[email protected]d5f942ba2008-09-26 19:30:3425namespace {
26
27BOOL CALLBACK InvalidateWindow(HWND hwnd, LPARAM lparam) {
[email protected]4d0bd102008-10-16 00:26:3028 // Note: erase is required to properly paint some widgets borders. This can
29 // be seen with textfields.
[email protected]d5f942ba2008-09-26 19:30:3430 InvalidateRect(hwnd, NULL, TRUE);
31 return TRUE;
32}
33
34} // namespace
35
initial.commit09911bf2008-07-26 23:55:2936TabContents::TabContents(TabContentsType type)
[email protected]d5f942ba2008-09-26 19:30:3437 : type_(type),
initial.commit09911bf2008-07-26 23:55:2938 delegate_(NULL),
39 controller_(NULL),
[email protected]d5f942ba2008-09-26 19:30:3440 is_loading_(false),
41 is_active_(true),
initial.commit09911bf2008-07-26 23:55:2942 is_crashed_(false),
[email protected]d5f942ba2008-09-26 19:30:3443 waiting_for_response_(false),
44 saved_location_bar_state_(NULL),
45 shelf_visible_(false),
46 max_page_id_(-1),
[email protected]d6598c052008-11-05 19:03:2547 blocked_popups_(NULL),
[email protected]d5f942ba2008-09-26 19:30:3448 capturing_contents_(false) {
initial.commit09911bf2008-07-26 23:55:2949 last_focused_view_storage_id_ =
[email protected]c2dacc92008-10-16 23:51:3850 views::ViewStorage::GetSharedInstance()->CreateStorageID();
initial.commit09911bf2008-07-26 23:55:2951}
52
53TabContents::~TabContents() {
54 // Makes sure to remove any stored view we may still have in the ViewStorage.
55 //
56 // It is possible the view went away before us, so we only do this if the
57 // view is registered.
[email protected]c2dacc92008-10-16 23:51:3858 views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance();
initial.commit09911bf2008-07-26 23:55:2959 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
60 view_storage->RemoveView(last_focused_view_storage_id_);
61}
62
[email protected]d5f942ba2008-09-26 19:30:3463// static
64void TabContents::RegisterUserPrefs(PrefService* prefs) {
65 prefs->RegisterBooleanPref(prefs::kBlockPopups, false);
initial.commit09911bf2008-07-26 23:55:2966}
67
initial.commit09911bf2008-07-26 23:55:2968
69void TabContents::CloseContents() {
70 // Destroy our NavigationController, which will Destroy all tabs it owns.
71 controller_->Destroy();
72 // Note that the controller may have deleted us at this point,
73 // so don't touch any member variables here.
74}
75
76void TabContents::Destroy() {
77 // First cleanly close all child windows.
78 // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
79 // some of these to close. CloseWindows is async, so it might get called
80 // twice before it runs.
81 int size = static_cast<int>(child_windows_.size());
82 for (int i = size - 1; i >= 0; --i) {
83 ConstrainedWindow* window = child_windows_[i];
84 if (window)
85 window->CloseConstrainedWindow();
86 }
87
88 // Notify any observer that have a reference on this tab contents.
89 NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_DESTROYED,
90 Source<TabContents>(this),
91 NotificationService::NoDetails());
92
93 // If we still have a window handle, destroy it. GetContainerHWND can return
94 // NULL if this contents was part of a window that closed.
95 if (GetContainerHWND())
96 ::DestroyWindow(GetContainerHWND());
97
98 // Notify our NavigationController. Make sure we are deleted first, so
99 // that the controller is the last to die.
100 NavigationController* controller = controller_;
101 TabContentsType type = this->type();
102
103 delete this;
104
105 controller->TabContentsWasDestroyed(type);
106}
107
[email protected]d5f942ba2008-09-26 19:30:34108void TabContents::SetupController(Profile* profile) {
109 DCHECK(!controller_);
110 controller_ = new NavigationController(this, profile);
111}
112
113bool TabContents::SupportsURL(GURL* url) {
114 GURL u(*url);
115 if (TabContents::TypeForURL(&u) == type()) {
116 *url = u;
117 return true;
118 }
119 return false;
120}
121
122const GURL& TabContents::GetURL() const {
123 // We may not have a navigation entry yet
124 NavigationEntry* entry = controller_->GetActiveEntry();
125 return entry ? entry->display_url() : GURL::EmptyGURL();
126}
127
128const std::wstring& TabContents::GetTitle() const {
[email protected]cbab76d2008-10-13 22:42:47129 // We use the title for the last committed entry rather than a pending
130 // navigation entry. For example, when the user types in a URL, we want to
131 // keep the old page's title until the new load has committed and we get a new
132 // title.
133 // The exception is with transient pages, for which we really want to use
134 // their title, as they are not committed.
135 NavigationEntry* entry = controller_->GetTransientEntry();
[email protected]3d627bbc2008-10-23 20:49:07136 if (entry)
137 return entry->GetTitleForDisplay();
[email protected]cbab76d2008-10-13 22:42:47138
139 entry = controller_->GetLastCommittedEntry();
[email protected]d5f942ba2008-09-26 19:30:34140 if (entry)
[email protected]3d627bbc2008-10-23 20:49:07141 return entry->GetTitleForDisplay();
[email protected]d5f942ba2008-09-26 19:30:34142 else if (controller_->LoadingURLLazily())
143 return controller_->GetLazyTitle();
144 return EmptyWString();
145}
146
147int32 TabContents::GetMaxPageID() {
148 if (GetSiteInstance())
149 return GetSiteInstance()->max_page_id();
150 else
151 return max_page_id_;
152}
153
154void TabContents::UpdateMaxPageID(int32 page_id) {
155 // Ensure both the SiteInstance and RenderProcessHost update their max page
156 // IDs in sync. Only WebContents will also have site instances, except during
157 // testing.
158 if (GetSiteInstance())
159 GetSiteInstance()->UpdateMaxPageID(page_id);
160
161 if (AsWebContents())
162 AsWebContents()->process()->UpdateMaxPageID(page_id);
163 else
164 max_page_id_ = std::max(max_page_id_, page_id);
165}
166
167const std::wstring TabContents::GetDefaultTitle() const {
168 return l10n_util::GetString(IDS_DEFAULT_TAB_TITLE);
169}
170
171SkBitmap TabContents::GetFavIcon() const {
172 // Like GetTitle(), we also want to use the favicon for the last committed
173 // entry rather than a pending navigation entry.
[email protected]cbab76d2008-10-13 22:42:47174 NavigationEntry* entry = controller_->GetTransientEntry();
175 if (entry)
176 return entry->favicon().bitmap();
177
178 entry = controller_->GetLastCommittedEntry();
[email protected]d5f942ba2008-09-26 19:30:34179 if (entry)
180 return entry->favicon().bitmap();
181 else if (controller_->LoadingURLLazily())
182 return controller_->GetLazyFavIcon();
183 return SkBitmap();
184}
185
186SecurityStyle TabContents::GetSecurityStyle() const {
187 // We may not have a navigation entry yet.
188 NavigationEntry* entry = controller_->GetActiveEntry();
189 return entry ? entry->ssl().security_style() : SECURITY_STYLE_UNKNOWN;
190}
191
192bool TabContents::GetSSLEVText(std::wstring* ev_text,
193 std::wstring* ev_tooltip_text) const {
194 DCHECK(ev_text && ev_tooltip_text);
195 ev_text->clear();
196 ev_tooltip_text->clear();
197
198 NavigationEntry* entry = controller_->GetActiveEntry();
199 if (!entry ||
200 net::IsCertStatusError(entry->ssl().cert_status()) ||
201 ((entry->ssl().cert_status() & net::CERT_STATUS_IS_EV) == 0))
202 return false;
203
204 scoped_refptr<net::X509Certificate> cert;
205 CertStore::GetSharedInstance()->RetrieveCert(entry->ssl().cert_id(), &cert);
206 if (!cert.get()) {
207 NOTREACHED();
208 return false;
209 }
210
211 return SSLManager::GetEVCertNames(*cert, ev_text, ev_tooltip_text);
212}
213
214void TabContents::SetIsCrashed(bool state) {
215 if (state == is_crashed_)
216 return;
217
218 is_crashed_ = state;
219 if (delegate_)
220 delegate_->ContentsStateChanged(this);
221}
222
223void TabContents::NotifyNavigationStateChanged(unsigned changed_flags) {
224 if (delegate_)
225 delegate_->NavigationStateChanged(this, changed_flags);
226}
227
228void TabContents::DidBecomeSelected() {
229 if (controller_)
230 controller_->SetActive(true);
231
232 // Invalidate all descendants. (take care to exclude invalidating ourselves!)
233 EnumChildWindows(GetContainerHWND(), InvalidateWindow, 0);
234}
235
236void TabContents::WasHidden() {
237 NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_HIDDEN,
238 Source<TabContents>(this),
239 NotificationService::NoDetails());
240}
241
242void TabContents::Activate() {
243 if (delegate_)
244 delegate_->ActivateContents(this);
245}
246
[email protected]c0588052008-10-27 23:01:50247void TabContents::OpenURL(const GURL& url, const GURL& referrer,
[email protected]d5f942ba2008-09-26 19:30:34248 WindowOpenDisposition disposition,
249 PageTransition::Type transition) {
250 if (delegate_)
[email protected]c0588052008-10-27 23:01:50251 delegate_->OpenURLFromTab(this, url, referrer, disposition, transition);
[email protected]d5f942ba2008-09-26 19:30:34252}
253
254bool TabContents::NavigateToPendingEntry(bool reload) {
255 // Our benavior is just to report that the entry was committed.
256 controller()->GetPendingEntry()->set_title(GetDefaultTitle());
257 controller()->CommitPendingEntry();
258 return true;
259}
260
initial.commit09911bf2008-07-26 23:55:29261ConstrainedWindow* TabContents::CreateConstrainedDialog(
[email protected]c2dacc92008-10-16 23:51:38262 views::WindowDelegate* window_delegate,
263 views::View* contents_view) {
initial.commit09911bf2008-07-26 23:55:29264 ConstrainedWindow* window =
265 ConstrainedWindow::CreateConstrainedDialog(
266 this, gfx::Rect(), contents_view, window_delegate);
267 child_windows_.push_back(window);
268 return window;
269}
270
271void TabContents::AddNewContents(TabContents* new_contents,
272 WindowOpenDisposition disposition,
273 const gfx::Rect& initial_pos,
274 bool user_gesture) {
275 if (!delegate_)
276 return;
277
[email protected]3b0a45e82008-10-13 21:01:03278 if ((disposition == NEW_POPUP) && !user_gesture) {
279 // Unrequested popups from normal pages are constrained.
280 TabContents* popup_owner = this;
281 TabContents* our_owner = delegate_->GetConstrainingContents(this);
282 if (our_owner)
283 popup_owner = our_owner;
284 popup_owner->AddConstrainedPopup(new_contents, initial_pos);
initial.commit09911bf2008-07-26 23:55:29285 } else {
[email protected]0aa55312008-10-17 21:53:08286 new_contents->DisassociateFromPopupCount();
287
initial.commit09911bf2008-07-26 23:55:29288 delegate_->AddNewContents(this, new_contents, disposition, initial_pos,
289 user_gesture);
290 }
291}
292
293void TabContents::AddConstrainedPopup(TabContents* new_contents,
294 const gfx::Rect& initial_pos) {
[email protected]d6598c052008-11-05 19:03:25295 if (!blocked_popups_) {
296 CRect client_rect;
297 GetClientRect(GetContainerHWND(), &client_rect);
298 gfx::Point anchor_position(
299 client_rect.Width() -
300 views::NativeScrollBar::GetVerticalScrollBarWidth(),
301 client_rect.Height());
initial.commit09911bf2008-07-26 23:55:29302
[email protected]d6598c052008-11-05 19:03:25303 blocked_popups_ = BlockedPopupContainer::Create(
304 this, profile(), anchor_position);
305 child_windows_.push_back(blocked_popups_);
306 }
307
308 blocked_popups_->AddTabContents(new_contents, initial_pos);
initial.commit09911bf2008-07-26 23:55:29309}
310
initial.commit09911bf2008-07-26 23:55:29311void TabContents::CloseAllSuppressedPopups() {
[email protected]d6598c052008-11-05 19:03:25312 if (blocked_popups_)
313 blocked_popups_->CloseAllPopups();
initial.commit09911bf2008-07-26 23:55:29314}
315
initial.commit09911bf2008-07-26 23:55:29316void TabContents::Focus() {
[email protected]c2dacc92008-10-16 23:51:38317 views::FocusManager* focus_manager =
318 views::FocusManager::GetFocusManager(GetContainerHWND());
initial.commit09911bf2008-07-26 23:55:29319 DCHECK(focus_manager);
[email protected]c2dacc92008-10-16 23:51:38320 views::View* v =
initial.commit09911bf2008-07-26 23:55:29321 focus_manager->GetViewForWindow(GetContainerHWND(), true);
322 DCHECK(v);
323 if (v)
324 v->RequestFocus();
325}
326
327void TabContents::StoreFocus() {
[email protected]c2dacc92008-10-16 23:51:38328 views::ViewStorage* view_storage =
329 views::ViewStorage::GetSharedInstance();
initial.commit09911bf2008-07-26 23:55:29330
331 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
332 view_storage->RemoveView(last_focused_view_storage_id_);
333
[email protected]c2dacc92008-10-16 23:51:38334 views::FocusManager* focus_manager =
335 views::FocusManager::GetFocusManager(GetContainerHWND());
initial.commit09911bf2008-07-26 23:55:29336 if (focus_manager) {
337 // |focus_manager| can be NULL if the tab has been detached but still
338 // exists.
[email protected]c2dacc92008-10-16 23:51:38339 views::View* focused_view = focus_manager->GetFocusedView();
initial.commit09911bf2008-07-26 23:55:29340 if (focused_view)
341 view_storage->StoreView(last_focused_view_storage_id_, focused_view);
342
343 // If the focus was on the page, explicitly clear the focus so that we
344 // don't end up with the focused HWND not part of the window hierarchy.
[email protected]c80c5632008-10-10 20:34:35345 // TODO(brettw) this should move to the view somehow.
initial.commit09911bf2008-07-26 23:55:29346 HWND container_hwnd = GetContainerHWND();
347 if (container_hwnd) {
[email protected]c2dacc92008-10-16 23:51:38348 views::View* focused_view = focus_manager->GetFocusedView();
initial.commit09911bf2008-07-26 23:55:29349 if (focused_view) {
[email protected]4d0bd102008-10-16 00:26:30350 HWND hwnd = focused_view->GetRootView()->GetContainer()->GetHWND();
initial.commit09911bf2008-07-26 23:55:29351 if (container_hwnd == hwnd || ::IsChild(container_hwnd, hwnd))
352 focus_manager->ClearFocus();
353 }
354 }
355 }
356}
357
358void TabContents::RestoreFocus() {
[email protected]c2dacc92008-10-16 23:51:38359 views::ViewStorage* view_storage =
360 views::ViewStorage::GetSharedInstance();
361 views::View* last_focused_view =
initial.commit09911bf2008-07-26 23:55:29362 view_storage->RetrieveView(last_focused_view_storage_id_);
363
364 if (!last_focused_view) {
365 SetInitialFocus();
366 } else {
[email protected]c2dacc92008-10-16 23:51:38367 views::FocusManager* focus_manager =
368 views::FocusManager::GetFocusManager(GetContainerHWND());
[email protected]6e2f2cec2008-09-23 22:59:06369
370 // If you hit this DCHECK, please report it to Jay (jcampan).
371 DCHECK(focus_manager != NULL) << "No focus manager when restoring focus.";
372
373 if (focus_manager && focus_manager->ContainsView(last_focused_view)) {
initial.commit09911bf2008-07-26 23:55:29374 last_focused_view->RequestFocus();
375 } else {
376 // The focused view may not belong to the same window hierarchy (for
377 // example if the location bar was focused and the tab is dragged out).
378 // In that case we default to the default focus.
379 SetInitialFocus();
380 }
381 view_storage->RemoveView(last_focused_view_storage_id_);
382 }
383}
384
[email protected]d5f942ba2008-09-26 19:30:34385void TabContents::SetInitialFocus() {
386 ::SetFocus(GetContainerHWND());
initial.commit09911bf2008-07-26 23:55:29387}
388
389void TabContents::SetDownloadShelfVisible(bool visible) {
390 if (shelf_visible_ != visible) {
391 if (visible) {
392 // Invoke GetDownloadShelfView to force the shelf to be created.
393 GetDownloadShelfView();
394 }
395 shelf_visible_ = visible;
396
[email protected]019d83502008-07-30 22:44:50397 if (delegate_)
398 delegate_->ContentsStateChanged(this);
initial.commit09911bf2008-07-26 23:55:29399 }
[email protected]ce2390b682008-08-08 22:24:51400
401 // SetShelfVisible can force-close the shelf, so make sure we lay out
402 // everything correctly, as if the animation had finished. This doesn't
403 // matter for showing the shelf, as the show animation will do it.
404 ToolbarSizeChanged(false);
initial.commit09911bf2008-07-26 23:55:29405}
406
initial.commit09911bf2008-07-26 23:55:29407void TabContents::ToolbarSizeChanged(bool is_animating) {
408 TabContentsDelegate* d = delegate();
409 if (d)
410 d->ToolbarSizeChanged(this, is_animating);
411}
412
[email protected]d5f942ba2008-09-26 19:30:34413void TabContents::OnStartDownload(DownloadItem* download) {
414 DCHECK(download);
415 TabContents* tab_contents = this;
416
417 // Download in a constrained popup is shown in the tab that opened it.
418 TabContents* constraining_tab = delegate()->GetConstrainingContents(this);
419 if (constraining_tab)
420 tab_contents = constraining_tab;
421
422 // GetDownloadShelfView creates the download shelf if it was not yet created.
423 tab_contents->GetDownloadShelfView()->AddDownload(download);
424 tab_contents->SetDownloadShelfVisible(true);
425
426 // This animation will delete itself when it finishes, or if we become hidden
427 // or destroyed.
428 if (IsWindowVisible(GetContainerHWND())) { // For minimized windows, unit
429 // tests, etc.
430 new DownloadStartedAnimation(tab_contents);
431 }
432}
433
initial.commit09911bf2008-07-26 23:55:29434DownloadShelfView* TabContents::GetDownloadShelfView() {
435 if (!download_shelf_view_.get()) {
436 download_shelf_view_.reset(new DownloadShelfView(this));
437 // The TabContents owns the download-shelf.
438 download_shelf_view_->SetParentOwned(false);
439 }
440 return download_shelf_view_.get();
441}
442
443void TabContents::MigrateShelfViewFrom(TabContents* tab_contents) {
444 download_shelf_view_.reset(tab_contents->GetDownloadShelfView());
445 download_shelf_view_->ChangeTabContents(tab_contents, this);
446 tab_contents->ReleaseDownloadShelfView();
447}
448
[email protected]d5f942ba2008-09-26 19:30:34449void TabContents::WillClose(ConstrainedWindow* window) {
450 ConstrainedWindowList::iterator it =
451 find(child_windows_.begin(), child_windows_.end(), window);
452 if (it != child_windows_.end())
453 child_windows_.erase(it);
454
[email protected]d6598c052008-11-05 19:03:25455 if (window == blocked_popups_)
456 blocked_popups_ = NULL;
457
[email protected]d5f942ba2008-09-26 19:30:34458 if (::IsWindow(GetContainerHWND())) {
459 CRect client_rect;
460 GetClientRect(GetContainerHWND(), &client_rect);
461 RepositionSupressedPopupsToFit(
462 gfx::Size(client_rect.Width(), client_rect.Height()));
463 }
464}
465
[email protected]d5f942ba2008-09-26 19:30:34466void TabContents::DidMoveOrResize(ConstrainedWindow* window) {
467 UpdateWindow(GetContainerHWND());
468}
469
initial.commit09911bf2008-07-26 23:55:29470// static
471void TabContents::MigrateShelfView(TabContents* from, TabContents* to) {
472 bool was_shelf_visible = from->IsDownloadShelfVisible();
473 if (was_shelf_visible)
474 to->MigrateShelfViewFrom(from);
475 to->SetDownloadShelfVisible(was_shelf_visible);
476}
477
[email protected]d5f942ba2008-09-26 19:30:34478void TabContents::SetIsLoading(bool is_loading,
479 LoadNotificationDetails* details) {
480 if (is_loading == is_loading_)
481 return;
482
483 is_loading_ = is_loading;
484 waiting_for_response_ = is_loading;
485
486 // Suppress notifications for this TabContents if we are not active.
487 if (!is_active_)
488 return;
489
490 if (delegate_)
491 delegate_->LoadingStateChanged(this);
492
493 NotificationService::current()->
494 Notify((is_loading ? NOTIFY_LOAD_START : NOTIFY_LOAD_STOP),
495 Source<NavigationController>(this->controller()),
496 details ? Details<LoadNotificationDetails>(details) :
497 NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:29498}
license.botbf09a502008-08-24 00:55:55499
[email protected]9e0534b2008-10-21 15:03:01500// TODO(brettw) This should be on the WebContentsView.
[email protected]d5f942ba2008-09-26 19:30:34501void TabContents::RepositionSupressedPopupsToFit(const gfx::Size& new_size) {
502 // TODO(erg): There's no way to detect whether scroll bars are
503 // visible, so for beta, we're just going to assume that the
504 // vertical scroll bar is visible, and not care about covering up
505 // the horizontal scroll bar. Fixing this is half of
506 // http://b/1118139.
507 gfx::Point anchor_position(
508 new_size.width() -
[email protected]c2dacc92008-10-16 23:51:38509 views::NativeScrollBar::GetVerticalScrollBarWidth(),
[email protected]d5f942ba2008-09-26 19:30:34510 new_size.height());
[email protected]d6598c052008-11-05 19:03:25511
512 if (blocked_popups_)
513 blocked_popups_->RepositionConstrainedWindowTo(anchor_position);
[email protected]d5f942ba2008-09-26 19:30:34514}
515
516void TabContents::ReleaseDownloadShelfView() {
517 download_shelf_view_.release();
518}