blob: 419a796917c28009cb3d2cd0e2b5416a23781f9c [file] [log] [blame]
initial.commit09911bf2008-07-26 23:55:291// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "chrome/browser/tab_contents.h"
31
32#include "chrome/browser/cert_store.h"
33#include "chrome/browser/navigation_entry.h"
34#include "chrome/browser/views/download_shelf_view.h"
35#include "chrome/browser/views/download_started_animation.h"
36#include "chrome/browser/web_contents.h"
37#include "chrome/browser/tab_contents_delegate.h"
38#include "chrome/common/pref_names.h"
[email protected]1eb89e82008-08-15 12:27:0339#include "chrome/common/pref_service.h"
initial.commit09911bf2008-07-26 23:55:2940#include "chrome/views/native_scroll_bar.h"
[email protected]1eb89e82008-08-15 12:27:0341#include "chrome/views/root_view.h"
initial.commit09911bf2008-07-26 23:55:2942#include "chrome/views/view.h"
43#include "chrome/views/view_storage.h"
44
45#include "generated_resources.h"
46
47TabContents::TabContents(TabContentsType type)
48 : is_loading_(false),
49 response_started_(false),
50 is_active_(true),
51 type_(type),
52 delegate_(NULL),
53 controller_(NULL),
54 max_page_id_(-1),
55 saved_location_bar_state_(NULL),
56 is_crashed_(false),
57 shelf_visible_(false) {
58 last_focused_view_storage_id_ =
59 ChromeViews::ViewStorage::GetSharedInstance()->CreateStorageID();
60}
61
62TabContents::~TabContents() {
63 // Makes sure to remove any stored view we may still have in the ViewStorage.
64 //
65 // It is possible the view went away before us, so we only do this if the
66 // view is registered.
67 ChromeViews::ViewStorage* view_storage =
68 ChromeViews::ViewStorage::GetSharedInstance();
69 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
70 view_storage->RemoveView(last_focused_view_storage_id_);
71}
72
73void TabContents::HideContents() {
74 // Hide the contents before adjusting its parent to avoid a full desktop
75 // flicker.
76 ShowWindow(GetContainerHWND(), SW_HIDE);
77
78 // Reset the parent to NULL to ensure hidden tabs don't receive messages.
79 SetParent(GetContainerHWND(), NULL);
80
81 // Remove any focus manager related information.
82 ChromeViews::FocusManager::UninstallFocusSubclass(GetContainerHWND());
83
84 WasHidden();
85}
86
87int32 TabContents::GetMaxPageID() {
88 if (AsWebContents())
89 return AsWebContents()->site_instance()->max_page_id();
90 else
91 return max_page_id_;
92}
93
94void TabContents::UpdateMaxPageID(int32 page_id) {
95 if (AsWebContents()) {
96 // Ensure both the SiteInstance and RenderProcessHost update their max page
97 // IDs in sync.
98 AsWebContents()->site_instance()->UpdateMaxPageID(page_id);
99 AsWebContents()->process()->UpdateMaxPageID(page_id);
100 } else {
101 max_page_id_ = std::max(max_page_id_, page_id);
102 }
103}
104
105const std::wstring TabContents::GetDefaultTitle() const {
106 return l10n_util::GetString(IDS_DEFAULT_TAB_TITLE);
107}
108
109void TabContents::SetupController(Profile* profile) {
110 DCHECK(!controller_);
111 controller_ = new NavigationController(this, profile);
112}
113
114const GURL& TabContents::GetURL() const {
115 DCHECK(controller_);
116
117 static const GURL kEmptyURL;
118
119 // We may not have a navigation entry yet
120 NavigationEntry* entry = controller_->GetActiveEntry();
121 return entry ? entry->GetDisplayURL() : kEmptyURL;
122}
123
124const std::wstring& TabContents::GetTitle() const {
125 DCHECK(controller_);
126
127 // We always want to use the title for the last committed entry rather than
128 // a pending navigation entry. For example, when the user types in a URL, we
129 // want to keep the old page's title until the new load has committed and we
130 // get a new title.
131 NavigationEntry* entry = controller_->GetLastCommittedEntry();
132 if (entry)
133 return entry->GetTitle();
134 else if (controller_->LoadingURLLazily())
135 return controller_->GetLazyTitle();
136 return EmptyWString();
137}
138
139SkBitmap TabContents::GetFavIcon() const {
140 DCHECK(controller_);
141
142 // Like GetTitle(), we also want to use the favicon for the last committed
143 // entry rather than a pending navigation entry.
144 NavigationEntry* entry = controller_->GetLastCommittedEntry();
145 if (entry)
146 return entry->GetFavIcon();
147 else if (controller_->LoadingURLLazily())
148 return controller_->GetLazyFavIcon();
149 return SkBitmap();
150}
151
152SecurityStyle TabContents::GetSecurityStyle() const {
153 // We may not have a navigation entry yet.
154 NavigationEntry* entry = controller_->GetActiveEntry();
155 return entry ? entry->GetSecurityStyle() : SECURITY_STYLE_UNKNOWN;
156}
157
158bool TabContents::GetSSLEVText(std::wstring* ev_text,
159 std::wstring* ev_tooltip_text) const {
160 DCHECK(ev_text && ev_tooltip_text);
161 ev_text->clear();
162 ev_tooltip_text->clear();
163
164 NavigationEntry* entry = controller_->GetActiveEntry();
165 if (!entry ||
166 net::IsCertStatusError(entry->GetSSLCertStatus()) ||
167 ((entry->GetSSLCertStatus() & net::CERT_STATUS_IS_EV) == 0))
168 return false;
169
[email protected]8ac1a752008-07-31 19:40:37170 scoped_refptr<net::X509Certificate> cert;
initial.commit09911bf2008-07-26 23:55:29171 CertStore::GetSharedInstance()->RetrieveCert(entry->GetSSLCertID(), &cert);
172 if (!cert.get()) {
173 NOTREACHED();
174 return false;
175 }
176
177 return SSLManager::GetEVCertNames(*cert, ev_text, ev_tooltip_text);
178}
179
180void TabContents::CloseContents() {
181 // Destroy our NavigationController, which will Destroy all tabs it owns.
182 controller_->Destroy();
183 // Note that the controller may have deleted us at this point,
184 // so don't touch any member variables here.
185}
186
187void TabContents::Destroy() {
188 // First cleanly close all child windows.
189 // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
190 // some of these to close. CloseWindows is async, so it might get called
191 // twice before it runs.
192 int size = static_cast<int>(child_windows_.size());
193 for (int i = size - 1; i >= 0; --i) {
194 ConstrainedWindow* window = child_windows_[i];
195 if (window)
196 window->CloseConstrainedWindow();
197 }
198
199 // Notify any observer that have a reference on this tab contents.
200 NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_DESTROYED,
201 Source<TabContents>(this),
202 NotificationService::NoDetails());
203
204 // If we still have a window handle, destroy it. GetContainerHWND can return
205 // NULL if this contents was part of a window that closed.
206 if (GetContainerHWND())
207 ::DestroyWindow(GetContainerHWND());
208
209 // Notify our NavigationController. Make sure we are deleted first, so
210 // that the controller is the last to die.
211 NavigationController* controller = controller_;
212 TabContentsType type = this->type();
213
214 delete this;
215
216 controller->TabContentsWasDestroyed(type);
217}
218
219ConstrainedWindow* TabContents::CreateConstrainedDialog(
220 ChromeViews::WindowDelegate* window_delegate,
221 ChromeViews::View* contents_view) {
222 ConstrainedWindow* window =
223 ConstrainedWindow::CreateConstrainedDialog(
224 this, gfx::Rect(), contents_view, window_delegate);
225 child_windows_.push_back(window);
226 return window;
227}
228
229void TabContents::AddNewContents(TabContents* new_contents,
230 WindowOpenDisposition disposition,
231 const gfx::Rect& initial_pos,
232 bool user_gesture) {
233 if (!delegate_)
234 return;
235
236 if ((disposition == NEW_POPUP) && !delegate_->IsPopup(this)) {
237 if (user_gesture) {
[email protected]eb0c1e42008-08-04 17:58:00238 delegate_->AddNewContents(this, new_contents, disposition, initial_pos,
initial.commit09911bf2008-07-26 23:55:29239 user_gesture);
240 } else {
241 AddConstrainedPopup(new_contents, initial_pos);
242 }
243 } else {
244 delegate_->AddNewContents(this, new_contents, disposition, initial_pos,
245 user_gesture);
246 }
247}
248
249void TabContents::AddConstrainedPopup(TabContents* new_contents,
250 const gfx::Rect& initial_pos) {
251 ConstrainedWindow* window =
252 ConstrainedWindow::CreateConstrainedPopup(
253 this, initial_pos, new_contents);
254 child_windows_.push_back(window);
255
256 CRect client_rect;
257 GetClientRect(GetContainerHWND(), &client_rect);
258 gfx::Size new_size(client_rect.Width(), client_rect.Height());
259 RepositionSupressedPopupsToFit(new_size);
260}
261
262void TabContents::SetIsLoading(bool is_loading,
263 LoadNotificationDetails* details) {
264 if (is_loading == is_loading_)
265 return;
266
267 is_loading_ = is_loading;
268 response_started_ = is_loading;
269
270 // Suppress notifications for this TabContents if we are not active.
271 if (!is_active_)
272 return;
273
274 if (delegate_)
275 delegate_->LoadingStateChanged(this);
276
277 NotificationService::current()->
278 Notify((is_loading ? NOTIFY_LOAD_START : NOTIFY_LOAD_STOP),
279 Source<NavigationController>(this->controller()),
280 details ? Details<LoadNotificationDetails>(details) :
281 NotificationService::NoDetails());
282}
283
284void TabContents::DidNavigateToEntry(NavigationEntry* entry) {
285 // The entry may be deleted by DidNavigateToEntry...
286 int new_page_id = entry->GetPageID();
287
288 controller_->DidNavigateToEntry(entry);
289
290 // update after informing the navigation controller so it can check the
291 // previous value of the max page id.
292 UpdateMaxPageID(new_page_id);
293}
294
295bool TabContents::Navigate(const NavigationEntry& entry, bool reload) {
296 NavigationEntry* new_entry = new NavigationEntry(entry);
297 if (new_entry->GetPageID() == -1) {
298 // This is a new navigation. Our behavior is to always navigate to the
299 // same page (page 0) in response to a navigation.
300 new_entry->SetPageID(0);
301 new_entry->SetTitle(GetDefaultTitle());
302 }
303 DidNavigateToEntry(new_entry);
304 return true;
305}
306
307void TabContents::NotifyNavigationStateChanged(unsigned changed_flags) {
308 if (delegate_)
309 delegate_->NavigationStateChanged(this, changed_flags);
310}
311
312void TabContents::NotifyDidNavigate(NavigationType nav_type,
313 int relative_navigation_offset) {
314 if (delegate_)
315 delegate_->DidNavigate(nav_type, relative_navigation_offset);
316}
317
318static BOOL CALLBACK InvalidateWindow(HWND hwnd, LPARAM lparam) {
319 // Note: erase is required to properly paint some widgets borders. This can be
320 // seen with textfields.
321 InvalidateRect(hwnd, NULL, TRUE);
322 return TRUE;
323}
324
325void TabContents::DidBecomeSelected() {
326 if (controller_)
327 controller_->SetActive(true);
328
329 // Invalidate all descendants. (take care to exclude invalidating ourselves!)
330 EnumChildWindows(GetContainerHWND(), InvalidateWindow, 0);
331}
332
333void TabContents::WasHidden() {
334 NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_HIDDEN,
335 Source<TabContents>(this),
336 NotificationService::NoDetails());
337}
338
339void TabContents::Activate() {
340 if (delegate_)
341 delegate_->ActivateContents(this);
342}
343
344void TabContents::CloseAllSuppressedPopups() {
345 // Close all auto positioned child windows to "clean up" the workspace.
346 int count = static_cast<int>(child_windows_.size());
347 for (int i = count - 1; i >= 0; --i) {
348 ConstrainedWindow* window = child_windows_.at(i);
349 if (window->IsSuppressedConstrainedWindow())
350 window->CloseConstrainedWindow();
351 }
352}
353
354void TabContents::OnStartDownload(DownloadItem* download) {
355 DCHECK(download);
356 TabContents* tab_contents = this;
357
358 // Download in a constrained popup is shown in the tab that opened it.
359 TabContents* constraining_tab = delegate()->GetConstrainingContents(this);
360 if (constraining_tab)
361 tab_contents = constraining_tab;
362
363 // GetDownloadShelfView creates the download shelf if it was not yet created.
364 tab_contents->GetDownloadShelfView()->AddDownload(download);
365 tab_contents->SetDownloadShelfVisible(true);
366
367 // This animation will delete itself when it finishes, or if we become hidden
368 // or destroyed.
369 if (IsWindowVisible(GetContainerHWND())) { // For minimized windows, unit
370 // tests, etc.
371 new DownloadStartedAnimation(tab_contents);
372 }
373}
374
375
376///////////////////////////////////////////////////////////////////////////////
377// TabContents, ConstrainedTabContentsDelegate implementation:
378
379void TabContents::AddNewContents(ConstrainedWindow* window,
380 TabContents* new_contents,
381 WindowOpenDisposition disposition,
382 const gfx::Rect& initial_pos,
383 bool user_gesture) {
384 AddNewContents(new_contents, disposition, initial_pos, user_gesture);
385}
386
387void TabContents::OpenURL(ConstrainedWindow* window,
388 const GURL& url,
389 WindowOpenDisposition disposition,
390 PageTransition::Type transition) {
391 OpenURL(url, disposition, transition);
392}
393
394void TabContents::WillClose(ConstrainedWindow* window) {
395 ConstrainedWindowList::iterator it =
396 find(child_windows_.begin(), child_windows_.end(), window);
397 if (it != child_windows_.end())
398 child_windows_.erase(it);
399
400 if (::IsWindow(GetContainerHWND())) {
401 CRect client_rect;
402 GetClientRect(GetContainerHWND(), &client_rect);
403 RepositionSupressedPopupsToFit(
404 gfx::Size(client_rect.Width(), client_rect.Height()));
405 }
406}
407
408void TabContents::DetachContents(ConstrainedWindow* window,
409 TabContents* contents,
410 const gfx::Rect& contents_bounds,
411 const gfx::Point& mouse_pt,
412 int frame_component) {
413 WillClose(window);
414 if (delegate_) {
415 delegate_->StartDraggingDetachedContents(
416 this, contents, contents_bounds, mouse_pt, frame_component);
417 }
418}
419
420void TabContents::DidMoveOrResize(ConstrainedWindow* window) {
421 UpdateWindow(GetContainerHWND());
422}
423
424///////////////////////////////////////////////////////////////////////////////
425// PageNavigator methods
426
427void TabContents::OpenURL(const GURL& url,
428 WindowOpenDisposition disposition,
429 PageTransition::Type transition) {
430 if (delegate_)
431 delegate_->OpenURLFromTab(this, url, disposition, transition);
432}
433
434///////////////////////////////////////////////////////////////////////////////
435
436void TabContents::Focus() {
437 ChromeViews::FocusManager* focus_manager =
438 ChromeViews::FocusManager::GetFocusManager(GetContainerHWND());
439 DCHECK(focus_manager);
440 ChromeViews::View* v =
441 focus_manager->GetViewForWindow(GetContainerHWND(), true);
442 DCHECK(v);
443 if (v)
444 v->RequestFocus();
445}
446
447void TabContents::StoreFocus() {
448 ChromeViews::ViewStorage* view_storage =
449 ChromeViews::ViewStorage::GetSharedInstance();
450
451 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
452 view_storage->RemoveView(last_focused_view_storage_id_);
453
454 ChromeViews::FocusManager* focus_manager =
455 ChromeViews::FocusManager::GetFocusManager(GetContainerHWND());
456 if (focus_manager) {
457 // |focus_manager| can be NULL if the tab has been detached but still
458 // exists.
459 ChromeViews::View* focused_view = focus_manager->GetFocusedView();
460 if (focused_view)
461 view_storage->StoreView(last_focused_view_storage_id_, focused_view);
462
463 // If the focus was on the page, explicitly clear the focus so that we
464 // don't end up with the focused HWND not part of the window hierarchy.
465 HWND container_hwnd = GetContainerHWND();
466 if (container_hwnd) {
467 ChromeViews::View* focused_view = focus_manager->GetFocusedView();
468 if (focused_view) {
469 HWND hwnd = focused_view->GetRootView()->GetViewContainer()->GetHWND();
470 if (container_hwnd == hwnd || ::IsChild(container_hwnd, hwnd))
471 focus_manager->ClearFocus();
472 }
473 }
474 }
475}
476
477void TabContents::RestoreFocus() {
478 ChromeViews::ViewStorage* view_storage =
479 ChromeViews::ViewStorage::GetSharedInstance();
480 ChromeViews::View* last_focused_view =
481 view_storage->RetrieveView(last_focused_view_storage_id_);
482
483 if (!last_focused_view) {
484 SetInitialFocus();
485 } else {
486 ChromeViews::FocusManager* focus_manager =
487 ChromeViews::FocusManager::GetFocusManager(GetContainerHWND());
488 if (focus_manager->ContainsView(last_focused_view)) {
489 last_focused_view->RequestFocus();
490 } else {
491 // The focused view may not belong to the same window hierarchy (for
492 // example if the location bar was focused and the tab is dragged out).
493 // In that case we default to the default focus.
494 SetInitialFocus();
495 }
496 view_storage->RemoveView(last_focused_view_storage_id_);
497 }
498}
499
500void TabContents::RepositionSupressedPopupsToFit(const gfx::Size& new_size) {
501 // TODO(erg): There's no way to detect whether scroll bars are
502 // visible, so for beta, we're just going to assume that the
503 // vertical scroll bar is visible, and not care about covering up
504 // the horizontal scroll bar. Fixing this is half of
505 // http://b/1118139.
506 gfx::Point anchor_position(
507 new_size.width() - ChromeViews::NativeScrollBar::GetVerticalScrollBarWidth(),
508 new_size.height());
509 int window_count = static_cast<int>(child_windows_.size());
510 for (int i = window_count - 1; i >= 0; --i) {
511 ConstrainedWindow* window = child_windows_.at(i);
512 if (window->IsSuppressedConstrainedWindow())
513 window->RepositionConstrainedWindowTo(anchor_position);
514 }
515}
516
517void TabContents::SetDownloadShelfVisible(bool visible) {
518 if (shelf_visible_ != visible) {
519 if (visible) {
520 // Invoke GetDownloadShelfView to force the shelf to be created.
521 GetDownloadShelfView();
522 }
523 shelf_visible_ = visible;
524
[email protected]019d83502008-07-30 22:44:50525 if (delegate_)
526 delegate_->ContentsStateChanged(this);
initial.commit09911bf2008-07-26 23:55:29527 }
[email protected]ce2390b682008-08-08 22:24:51528
529 // SetShelfVisible can force-close the shelf, so make sure we lay out
530 // everything correctly, as if the animation had finished. This doesn't
531 // matter for showing the shelf, as the show animation will do it.
532 ToolbarSizeChanged(false);
initial.commit09911bf2008-07-26 23:55:29533}
534
535void TabContents::ReleaseDownloadShelfView() {
536 download_shelf_view_.release();
537}
538
539void TabContents::SetInitialFocus() {
540 ::SetFocus(GetContainerHWND());
541}
542
543void TabContents::SetIsCrashed(bool state) {
544 if (state == is_crashed_)
545 return;
546
547 is_crashed_ = state;
548 if (delegate_)
[email protected]019d83502008-07-30 22:44:50549 delegate_->ContentsStateChanged(this);
initial.commit09911bf2008-07-26 23:55:29550}
551
552bool TabContents::IsCrashed() const {
553 return is_crashed_;
554}
555
556bool TabContents::SupportsURL(GURL* url) {
557 GURL u(*url);
558 if (TabContents::TypeForURL(&u) == type()) {
559 *url = u;
560 return true;
561 }
562 return false;
563}
564
565void TabContents::ToolbarSizeChanged(bool is_animating) {
566 TabContentsDelegate* d = delegate();
567 if (d)
568 d->ToolbarSizeChanged(this, is_animating);
569}
570
571DownloadShelfView* TabContents::GetDownloadShelfView() {
572 if (!download_shelf_view_.get()) {
573 download_shelf_view_.reset(new DownloadShelfView(this));
574 // The TabContents owns the download-shelf.
575 download_shelf_view_->SetParentOwned(false);
576 }
577 return download_shelf_view_.get();
578}
579
580void TabContents::MigrateShelfViewFrom(TabContents* tab_contents) {
581 download_shelf_view_.reset(tab_contents->GetDownloadShelfView());
582 download_shelf_view_->ChangeTabContents(tab_contents, this);
583 tab_contents->ReleaseDownloadShelfView();
584}
585
586// static
587void TabContents::MigrateShelfView(TabContents* from, TabContents* to) {
588 bool was_shelf_visible = from->IsDownloadShelfVisible();
589 if (was_shelf_visible)
590 to->MigrateShelfViewFrom(from);
591 to->SetDownloadShelfVisible(was_shelf_visible);
592}
593
594// static
595void TabContents::RegisterUserPrefs(PrefService* prefs) {
596 prefs->RegisterBooleanPref(prefs::kBlockPopups, false);
597}