blob: f97c04cfc76b4a47d211ee32a55125550a6d8993 [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"
11#include "chrome/browser/web_contents.h"
12#include "chrome/browser/tab_contents_delegate.h"
[email protected]a4feef82008-10-02 15:11:2213#include "chrome/common/l10n_util.h"
initial.commit09911bf2008-07-26 23:55:2914#include "chrome/common/pref_names.h"
[email protected]1eb89e82008-08-15 12:27:0315#include "chrome/common/pref_service.h"
[email protected]4d0bd102008-10-16 00:26:3016#include "chrome/views/container.h"
initial.commit09911bf2008-07-26 23:55:2917#include "chrome/views/native_scroll_bar.h"
[email protected]1eb89e82008-08-15 12:27:0318#include "chrome/views/root_view.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/views/view.h"
20#include "chrome/views/view_storage.h"
21
22#include "generated_resources.h"
23
[email protected]d5f942ba2008-09-26 19:30:3424namespace {
25
26BOOL CALLBACK InvalidateWindow(HWND hwnd, LPARAM lparam) {
[email protected]4d0bd102008-10-16 00:26:3027 // Note: erase is required to properly paint some widgets borders. This can
28 // be seen with textfields.
[email protected]d5f942ba2008-09-26 19:30:3429 InvalidateRect(hwnd, NULL, TRUE);
30 return TRUE;
31}
32
33} // namespace
34
initial.commit09911bf2008-07-26 23:55:2935TabContents::TabContents(TabContentsType type)
[email protected]d5f942ba2008-09-26 19:30:3436 : type_(type),
initial.commit09911bf2008-07-26 23:55:2937 delegate_(NULL),
38 controller_(NULL),
[email protected]d5f942ba2008-09-26 19:30:3439 is_loading_(false),
40 is_active_(true),
initial.commit09911bf2008-07-26 23:55:2941 is_crashed_(false),
[email protected]d5f942ba2008-09-26 19:30:3442 waiting_for_response_(false),
43 saved_location_bar_state_(NULL),
44 shelf_visible_(false),
45 max_page_id_(-1),
46 capturing_contents_(false) {
initial.commit09911bf2008-07-26 23:55:2947 last_focused_view_storage_id_ =
[email protected]c2dacc92008-10-16 23:51:3848 views::ViewStorage::GetSharedInstance()->CreateStorageID();
initial.commit09911bf2008-07-26 23:55:2949}
50
51TabContents::~TabContents() {
52 // Makes sure to remove any stored view we may still have in the ViewStorage.
53 //
54 // It is possible the view went away before us, so we only do this if the
55 // view is registered.
[email protected]c2dacc92008-10-16 23:51:3856 views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance();
initial.commit09911bf2008-07-26 23:55:2957 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
58 view_storage->RemoveView(last_focused_view_storage_id_);
59}
60
[email protected]d5f942ba2008-09-26 19:30:3461// static
62void TabContents::RegisterUserPrefs(PrefService* prefs) {
63 prefs->RegisterBooleanPref(prefs::kBlockPopups, false);
initial.commit09911bf2008-07-26 23:55:2964}
65
initial.commit09911bf2008-07-26 23:55:2966
67void TabContents::CloseContents() {
68 // Destroy our NavigationController, which will Destroy all tabs it owns.
69 controller_->Destroy();
70 // Note that the controller may have deleted us at this point,
71 // so don't touch any member variables here.
72}
73
74void TabContents::Destroy() {
75 // First cleanly close all child windows.
76 // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
77 // some of these to close. CloseWindows is async, so it might get called
78 // twice before it runs.
79 int size = static_cast<int>(child_windows_.size());
80 for (int i = size - 1; i >= 0; --i) {
81 ConstrainedWindow* window = child_windows_[i];
82 if (window)
83 window->CloseConstrainedWindow();
84 }
85
86 // Notify any observer that have a reference on this tab contents.
87 NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_DESTROYED,
88 Source<TabContents>(this),
89 NotificationService::NoDetails());
90
91 // If we still have a window handle, destroy it. GetContainerHWND can return
92 // NULL if this contents was part of a window that closed.
93 if (GetContainerHWND())
94 ::DestroyWindow(GetContainerHWND());
95
96 // Notify our NavigationController. Make sure we are deleted first, so
97 // that the controller is the last to die.
98 NavigationController* controller = controller_;
99 TabContentsType type = this->type();
100
101 delete this;
102
103 controller->TabContentsWasDestroyed(type);
104}
105
[email protected]d5f942ba2008-09-26 19:30:34106void TabContents::SetupController(Profile* profile) {
107 DCHECK(!controller_);
108 controller_ = new NavigationController(this, profile);
109}
110
111bool TabContents::SupportsURL(GURL* url) {
112 GURL u(*url);
113 if (TabContents::TypeForURL(&u) == type()) {
114 *url = u;
115 return true;
116 }
117 return false;
118}
119
120const GURL& TabContents::GetURL() const {
121 // We may not have a navigation entry yet
122 NavigationEntry* entry = controller_->GetActiveEntry();
123 return entry ? entry->display_url() : GURL::EmptyGURL();
124}
125
126const std::wstring& TabContents::GetTitle() const {
[email protected]cbab76d2008-10-13 22:42:47127 // We use the title for the last committed entry rather than a pending
128 // navigation entry. For example, when the user types in a URL, we want to
129 // keep the old page's title until the new load has committed and we get a new
130 // title.
131 // The exception is with transient pages, for which we really want to use
132 // their title, as they are not committed.
133 NavigationEntry* entry = controller_->GetTransientEntry();
134 if (entry && !entry->title().empty())
135 return entry->title();
136
137 entry = controller_->GetLastCommittedEntry();
[email protected]d5f942ba2008-09-26 19:30:34138 if (entry)
139 return entry->title();
140 else if (controller_->LoadingURLLazily())
141 return controller_->GetLazyTitle();
142 return EmptyWString();
143}
144
145int32 TabContents::GetMaxPageID() {
146 if (GetSiteInstance())
147 return GetSiteInstance()->max_page_id();
148 else
149 return max_page_id_;
150}
151
152void TabContents::UpdateMaxPageID(int32 page_id) {
153 // Ensure both the SiteInstance and RenderProcessHost update their max page
154 // IDs in sync. Only WebContents will also have site instances, except during
155 // testing.
156 if (GetSiteInstance())
157 GetSiteInstance()->UpdateMaxPageID(page_id);
158
159 if (AsWebContents())
160 AsWebContents()->process()->UpdateMaxPageID(page_id);
161 else
162 max_page_id_ = std::max(max_page_id_, page_id);
163}
164
165const std::wstring TabContents::GetDefaultTitle() const {
166 return l10n_util::GetString(IDS_DEFAULT_TAB_TITLE);
167}
168
169SkBitmap TabContents::GetFavIcon() const {
170 // Like GetTitle(), we also want to use the favicon for the last committed
171 // entry rather than a pending navigation entry.
[email protected]cbab76d2008-10-13 22:42:47172 NavigationEntry* entry = controller_->GetTransientEntry();
173 if (entry)
174 return entry->favicon().bitmap();
175
176 entry = controller_->GetLastCommittedEntry();
[email protected]d5f942ba2008-09-26 19:30:34177 if (entry)
178 return entry->favicon().bitmap();
179 else if (controller_->LoadingURLLazily())
180 return controller_->GetLazyFavIcon();
181 return SkBitmap();
182}
183
184SecurityStyle TabContents::GetSecurityStyle() const {
185 // We may not have a navigation entry yet.
186 NavigationEntry* entry = controller_->GetActiveEntry();
187 return entry ? entry->ssl().security_style() : SECURITY_STYLE_UNKNOWN;
188}
189
190bool TabContents::GetSSLEVText(std::wstring* ev_text,
191 std::wstring* ev_tooltip_text) const {
192 DCHECK(ev_text && ev_tooltip_text);
193 ev_text->clear();
194 ev_tooltip_text->clear();
195
196 NavigationEntry* entry = controller_->GetActiveEntry();
197 if (!entry ||
198 net::IsCertStatusError(entry->ssl().cert_status()) ||
199 ((entry->ssl().cert_status() & net::CERT_STATUS_IS_EV) == 0))
200 return false;
201
202 scoped_refptr<net::X509Certificate> cert;
203 CertStore::GetSharedInstance()->RetrieveCert(entry->ssl().cert_id(), &cert);
204 if (!cert.get()) {
205 NOTREACHED();
206 return false;
207 }
208
209 return SSLManager::GetEVCertNames(*cert, ev_text, ev_tooltip_text);
210}
211
212void TabContents::SetIsCrashed(bool state) {
213 if (state == is_crashed_)
214 return;
215
216 is_crashed_ = state;
217 if (delegate_)
218 delegate_->ContentsStateChanged(this);
219}
220
221void TabContents::NotifyNavigationStateChanged(unsigned changed_flags) {
222 if (delegate_)
223 delegate_->NavigationStateChanged(this, changed_flags);
224}
225
226void TabContents::DidBecomeSelected() {
227 if (controller_)
228 controller_->SetActive(true);
229
230 // Invalidate all descendants. (take care to exclude invalidating ourselves!)
231 EnumChildWindows(GetContainerHWND(), InvalidateWindow, 0);
232}
233
234void TabContents::WasHidden() {
235 NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_HIDDEN,
236 Source<TabContents>(this),
237 NotificationService::NoDetails());
238}
239
240void TabContents::Activate() {
241 if (delegate_)
242 delegate_->ActivateContents(this);
243}
244
245void TabContents::OpenURL(const GURL& url,
246 WindowOpenDisposition disposition,
247 PageTransition::Type transition) {
248 if (delegate_)
249 delegate_->OpenURLFromTab(this, url, disposition, transition);
250}
251
252bool TabContents::NavigateToPendingEntry(bool reload) {
253 // Our benavior is just to report that the entry was committed.
254 controller()->GetPendingEntry()->set_title(GetDefaultTitle());
255 controller()->CommitPendingEntry();
256 return true;
257}
258
initial.commit09911bf2008-07-26 23:55:29259ConstrainedWindow* TabContents::CreateConstrainedDialog(
[email protected]c2dacc92008-10-16 23:51:38260 views::WindowDelegate* window_delegate,
261 views::View* contents_view) {
initial.commit09911bf2008-07-26 23:55:29262 ConstrainedWindow* window =
263 ConstrainedWindow::CreateConstrainedDialog(
264 this, gfx::Rect(), contents_view, window_delegate);
265 child_windows_.push_back(window);
266 return window;
267}
268
269void TabContents::AddNewContents(TabContents* new_contents,
270 WindowOpenDisposition disposition,
271 const gfx::Rect& initial_pos,
272 bool user_gesture) {
273 if (!delegate_)
274 return;
275
[email protected]3b0a45e82008-10-13 21:01:03276 if ((disposition == NEW_POPUP) && !user_gesture) {
277 // Unrequested popups from normal pages are constrained.
278 TabContents* popup_owner = this;
279 TabContents* our_owner = delegate_->GetConstrainingContents(this);
280 if (our_owner)
281 popup_owner = our_owner;
282 popup_owner->AddConstrainedPopup(new_contents, initial_pos);
initial.commit09911bf2008-07-26 23:55:29283 } else {
[email protected]0aa55312008-10-17 21:53:08284 new_contents->DisassociateFromPopupCount();
285
initial.commit09911bf2008-07-26 23:55:29286 delegate_->AddNewContents(this, new_contents, disposition, initial_pos,
287 user_gesture);
288 }
289}
290
291void TabContents::AddConstrainedPopup(TabContents* new_contents,
292 const gfx::Rect& initial_pos) {
293 ConstrainedWindow* window =
294 ConstrainedWindow::CreateConstrainedPopup(
295 this, initial_pos, new_contents);
296 child_windows_.push_back(window);
297
298 CRect client_rect;
299 GetClientRect(GetContainerHWND(), &client_rect);
300 gfx::Size new_size(client_rect.Width(), client_rect.Height());
301 RepositionSupressedPopupsToFit(new_size);
302}
303
initial.commit09911bf2008-07-26 23:55:29304void TabContents::CloseAllSuppressedPopups() {
305 // Close all auto positioned child windows to "clean up" the workspace.
306 int count = static_cast<int>(child_windows_.size());
307 for (int i = count - 1; i >= 0; --i) {
308 ConstrainedWindow* window = child_windows_.at(i);
309 if (window->IsSuppressedConstrainedWindow())
310 window->CloseConstrainedWindow();
311 }
312}
313
initial.commit09911bf2008-07-26 23:55:29314void TabContents::Focus() {
[email protected]c2dacc92008-10-16 23:51:38315 views::FocusManager* focus_manager =
316 views::FocusManager::GetFocusManager(GetContainerHWND());
initial.commit09911bf2008-07-26 23:55:29317 DCHECK(focus_manager);
[email protected]c2dacc92008-10-16 23:51:38318 views::View* v =
initial.commit09911bf2008-07-26 23:55:29319 focus_manager->GetViewForWindow(GetContainerHWND(), true);
320 DCHECK(v);
321 if (v)
322 v->RequestFocus();
323}
324
325void TabContents::StoreFocus() {
[email protected]c2dacc92008-10-16 23:51:38326 views::ViewStorage* view_storage =
327 views::ViewStorage::GetSharedInstance();
initial.commit09911bf2008-07-26 23:55:29328
329 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
330 view_storage->RemoveView(last_focused_view_storage_id_);
331
[email protected]c2dacc92008-10-16 23:51:38332 views::FocusManager* focus_manager =
333 views::FocusManager::GetFocusManager(GetContainerHWND());
initial.commit09911bf2008-07-26 23:55:29334 if (focus_manager) {
335 // |focus_manager| can be NULL if the tab has been detached but still
336 // exists.
[email protected]c2dacc92008-10-16 23:51:38337 views::View* focused_view = focus_manager->GetFocusedView();
initial.commit09911bf2008-07-26 23:55:29338 if (focused_view)
339 view_storage->StoreView(last_focused_view_storage_id_, focused_view);
340
341 // If the focus was on the page, explicitly clear the focus so that we
342 // don't end up with the focused HWND not part of the window hierarchy.
[email protected]c80c5632008-10-10 20:34:35343 // TODO(brettw) this should move to the view somehow.
initial.commit09911bf2008-07-26 23:55:29344 HWND container_hwnd = GetContainerHWND();
345 if (container_hwnd) {
[email protected]c2dacc92008-10-16 23:51:38346 views::View* focused_view = focus_manager->GetFocusedView();
initial.commit09911bf2008-07-26 23:55:29347 if (focused_view) {
[email protected]4d0bd102008-10-16 00:26:30348 HWND hwnd = focused_view->GetRootView()->GetContainer()->GetHWND();
initial.commit09911bf2008-07-26 23:55:29349 if (container_hwnd == hwnd || ::IsChild(container_hwnd, hwnd))
350 focus_manager->ClearFocus();
351 }
352 }
353 }
354}
355
356void TabContents::RestoreFocus() {
[email protected]c2dacc92008-10-16 23:51:38357 views::ViewStorage* view_storage =
358 views::ViewStorage::GetSharedInstance();
359 views::View* last_focused_view =
initial.commit09911bf2008-07-26 23:55:29360 view_storage->RetrieveView(last_focused_view_storage_id_);
361
362 if (!last_focused_view) {
363 SetInitialFocus();
364 } else {
[email protected]c2dacc92008-10-16 23:51:38365 views::FocusManager* focus_manager =
366 views::FocusManager::GetFocusManager(GetContainerHWND());
[email protected]6e2f2cec2008-09-23 22:59:06367
368 // If you hit this DCHECK, please report it to Jay (jcampan).
369 DCHECK(focus_manager != NULL) << "No focus manager when restoring focus.";
370
371 if (focus_manager && focus_manager->ContainsView(last_focused_view)) {
initial.commit09911bf2008-07-26 23:55:29372 last_focused_view->RequestFocus();
373 } else {
374 // The focused view may not belong to the same window hierarchy (for
375 // example if the location bar was focused and the tab is dragged out).
376 // In that case we default to the default focus.
377 SetInitialFocus();
378 }
379 view_storage->RemoveView(last_focused_view_storage_id_);
380 }
381}
382
[email protected]d5f942ba2008-09-26 19:30:34383void TabContents::SetInitialFocus() {
384 ::SetFocus(GetContainerHWND());
initial.commit09911bf2008-07-26 23:55:29385}
386
387void TabContents::SetDownloadShelfVisible(bool visible) {
388 if (shelf_visible_ != visible) {
389 if (visible) {
390 // Invoke GetDownloadShelfView to force the shelf to be created.
391 GetDownloadShelfView();
392 }
393 shelf_visible_ = visible;
394
[email protected]019d83502008-07-30 22:44:50395 if (delegate_)
396 delegate_->ContentsStateChanged(this);
initial.commit09911bf2008-07-26 23:55:29397 }
[email protected]ce2390b682008-08-08 22:24:51398
399 // SetShelfVisible can force-close the shelf, so make sure we lay out
400 // everything correctly, as if the animation had finished. This doesn't
401 // matter for showing the shelf, as the show animation will do it.
402 ToolbarSizeChanged(false);
initial.commit09911bf2008-07-26 23:55:29403}
404
initial.commit09911bf2008-07-26 23:55:29405void TabContents::ToolbarSizeChanged(bool is_animating) {
406 TabContentsDelegate* d = delegate();
407 if (d)
408 d->ToolbarSizeChanged(this, is_animating);
409}
410
[email protected]d5f942ba2008-09-26 19:30:34411void TabContents::OnStartDownload(DownloadItem* download) {
412 DCHECK(download);
413 TabContents* tab_contents = this;
414
415 // Download in a constrained popup is shown in the tab that opened it.
416 TabContents* constraining_tab = delegate()->GetConstrainingContents(this);
417 if (constraining_tab)
418 tab_contents = constraining_tab;
419
420 // GetDownloadShelfView creates the download shelf if it was not yet created.
421 tab_contents->GetDownloadShelfView()->AddDownload(download);
422 tab_contents->SetDownloadShelfVisible(true);
423
424 // This animation will delete itself when it finishes, or if we become hidden
425 // or destroyed.
426 if (IsWindowVisible(GetContainerHWND())) { // For minimized windows, unit
427 // tests, etc.
428 new DownloadStartedAnimation(tab_contents);
429 }
430}
431
initial.commit09911bf2008-07-26 23:55:29432DownloadShelfView* TabContents::GetDownloadShelfView() {
433 if (!download_shelf_view_.get()) {
434 download_shelf_view_.reset(new DownloadShelfView(this));
435 // The TabContents owns the download-shelf.
436 download_shelf_view_->SetParentOwned(false);
437 }
438 return download_shelf_view_.get();
439}
440
441void TabContents::MigrateShelfViewFrom(TabContents* tab_contents) {
442 download_shelf_view_.reset(tab_contents->GetDownloadShelfView());
443 download_shelf_view_->ChangeTabContents(tab_contents, this);
444 tab_contents->ReleaseDownloadShelfView();
445}
446
[email protected]d5f942ba2008-09-26 19:30:34447void TabContents::AddNewContents(ConstrainedWindow* window,
448 TabContents* new_contents,
449 WindowOpenDisposition disposition,
450 const gfx::Rect& initial_pos,
451 bool user_gesture) {
452 AddNewContents(new_contents, disposition, initial_pos, user_gesture);
453}
454
455void TabContents::OpenURL(ConstrainedWindow* window,
456 const GURL& url,
457 WindowOpenDisposition disposition,
458 PageTransition::Type transition) {
459 OpenURL(url, disposition, transition);
460}
461
462void TabContents::WillClose(ConstrainedWindow* window) {
463 ConstrainedWindowList::iterator it =
464 find(child_windows_.begin(), child_windows_.end(), window);
465 if (it != child_windows_.end())
466 child_windows_.erase(it);
467
468 if (::IsWindow(GetContainerHWND())) {
469 CRect client_rect;
470 GetClientRect(GetContainerHWND(), &client_rect);
471 RepositionSupressedPopupsToFit(
472 gfx::Size(client_rect.Width(), client_rect.Height()));
473 }
474}
475
476void TabContents::DetachContents(ConstrainedWindow* window,
477 TabContents* contents,
478 const gfx::Rect& contents_bounds,
479 const gfx::Point& mouse_pt,
480 int frame_component) {
481 WillClose(window);
482 if (delegate_) {
[email protected]0aa55312008-10-17 21:53:08483 contents->DisassociateFromPopupCount();
[email protected]d5f942ba2008-09-26 19:30:34484 delegate_->StartDraggingDetachedContents(
485 this, contents, contents_bounds, mouse_pt, frame_component);
486 }
487}
488
489void TabContents::DidMoveOrResize(ConstrainedWindow* window) {
490 UpdateWindow(GetContainerHWND());
491}
492
initial.commit09911bf2008-07-26 23:55:29493// static
494void TabContents::MigrateShelfView(TabContents* from, TabContents* to) {
495 bool was_shelf_visible = from->IsDownloadShelfVisible();
496 if (was_shelf_visible)
497 to->MigrateShelfViewFrom(from);
498 to->SetDownloadShelfVisible(was_shelf_visible);
499}
500
[email protected]d5f942ba2008-09-26 19:30:34501void TabContents::SetIsLoading(bool is_loading,
502 LoadNotificationDetails* details) {
503 if (is_loading == is_loading_)
504 return;
505
506 is_loading_ = is_loading;
507 waiting_for_response_ = is_loading;
508
509 // Suppress notifications for this TabContents if we are not active.
510 if (!is_active_)
511 return;
512
513 if (delegate_)
514 delegate_->LoadingStateChanged(this);
515
516 NotificationService::current()->
517 Notify((is_loading ? NOTIFY_LOAD_START : NOTIFY_LOAD_STOP),
518 Source<NavigationController>(this->controller()),
519 details ? Details<LoadNotificationDetails>(details) :
520 NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:29521}
license.botbf09a502008-08-24 00:55:55522
[email protected]9e0534b2008-10-21 15:03:01523// TODO(brettw) This should be on the WebContentsView.
[email protected]d5f942ba2008-09-26 19:30:34524void TabContents::RepositionSupressedPopupsToFit(const gfx::Size& new_size) {
525 // TODO(erg): There's no way to detect whether scroll bars are
526 // visible, so for beta, we're just going to assume that the
527 // vertical scroll bar is visible, and not care about covering up
528 // the horizontal scroll bar. Fixing this is half of
529 // http://b/1118139.
530 gfx::Point anchor_position(
531 new_size.width() -
[email protected]c2dacc92008-10-16 23:51:38532 views::NativeScrollBar::GetVerticalScrollBarWidth(),
[email protected]d5f942ba2008-09-26 19:30:34533 new_size.height());
534 int window_count = static_cast<int>(child_windows_.size());
535 for (int i = window_count - 1; i >= 0; --i) {
536 ConstrainedWindow* window = child_windows_.at(i);
537 if (window->IsSuppressedConstrainedWindow())
538 window->RepositionConstrainedWindowTo(anchor_position);
539 }
540}
541
542void TabContents::ReleaseDownloadShelfView() {
543 download_shelf_view_.release();
544}