diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-24 05:16:32 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-24 05:16:32 +0000 |
commit | 420ae012547bd2a176a0e0f5de27bc540b28a255 (patch) | |
tree | d20918df32ca7d86da1d9efb68e3da3cbd1df10b /chrome | |
parent | ee734f6a14db32a073d7694b0fb288793dfaf57e (diff) | |
download | chromium_src-420ae012547bd2a176a0e0f5de27bc540b28a255.zip chromium_src-420ae012547bd2a176a0e0f5de27bc540b28a255.tar.gz chromium_src-420ae012547bd2a176a0e0f5de27bc540b28a255.tar.bz2 |
Kill WebContents by moving the remaining methods to TabContents. WebContents
remains as a shell so I don't have to change all the callers in this CL.
Review URL: http://codereview.chromium.org/92115
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14414 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/browser_prefs.cc | 1 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents.cc | 1562 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents.h | 340 | ||||
-rw-r--r-- | chrome/browser/tab_contents/web_contents.cc | 1511 | ||||
-rw-r--r-- | chrome/browser/tab_contents/web_contents.h | 349 |
5 files changed, 1884 insertions, 1879 deletions
diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc index b7b1836..661ab6b 100644 --- a/chrome/browser/browser_prefs.cc +++ b/chrome/browser/browser_prefs.cc @@ -62,7 +62,6 @@ void RegisterAllPrefs(PrefService* user_prefs, PrefService* local_state) { AutofillManager::RegisterUserPrefs(user_prefs); TabContents::RegisterUserPrefs(user_prefs); TemplateURLPrepopulateData::RegisterUserPrefs(user_prefs); - WebContents::RegisterUserPrefs(user_prefs); NewTabUI::RegisterUserPrefs(user_prefs); } diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index a550414..e8eb9bd 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -4,16 +4,27 @@ #include "chrome/browser/tab_contents/tab_contents.h" +#include "base/file_version_info.h" +#include "base/process_util.h" #include "base/string16.h" #include "base/time.h" #include "chrome/browser/autofill_manager.h" +#include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/cert_store.h" #include "chrome/browser/debugger/devtools_manager.h" +#include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/dom_ui/dom_ui.h" #include "chrome/browser/dom_ui/dom_ui_factory.h" #include "chrome/browser/download/download_item_model.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/download/download_request_manager.h" #include "chrome/browser/download/download_shelf.h" +#include "chrome/browser/gears_integration.h" +#include "chrome/browser/google_util.h" +#include "chrome/browser/jsmessage_box_handler.h" +#include "chrome/browser/load_from_memory_cache_details.h" +#include "chrome/browser/load_notification_details.h" #include "chrome/browser/password_manager/password_manager.h" #include "chrome/browser/plugin_installer.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" @@ -22,28 +33,176 @@ #include "chrome/browser/tab_contents/tab_contents_delegate.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/browser/tab_contents/web_contents.h" +#include "chrome/browser/search_engines/template_url_fetcher.h" +#include "chrome/browser/search_engines/template_url_model.h" #include "chrome/common/l10n_util.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/resource_bundle.h" #include "chrome/common/url_constants.h" -#include "grit/generated_resources.h" +//#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "net/base/mime_util.h" +#include "net/base/net_errors.h" +#include "net/base/registry_controlled_domain.h" #if defined(OS_WIN) // TODO(port): some of these headers should be ported. +#include "chrome/browser/modal_html_dialog_delegate.h" #include "chrome/browser/tab_contents/infobar_delegate.h" -#include "chrome/browser/views/download_started_animation.h" #include "chrome/browser/views/blocked_popup_container.h" +#include "chrome/browser/views/download_started_animation.h" +#include "chrome/browser/views/hung_renderer_view.h" // TODO(brettw) delete me. #include "chrome/views/controls/scrollbar/native_scroll_bar.h" #endif +#include "grit/generated_resources.h" + +// Cross-Site Navigations +// +// If a TabContents is told to navigate to a different web site (as determined +// by SiteInstance), it will replace its current RenderViewHost with a new +// RenderViewHost dedicated to the new SiteInstance. This works as follows: +// +// - Navigate determines whether the destination is cross-site, and if so, +// it creates a pending_render_view_host_ and moves into the PENDING +// RendererState. +// - The pending RVH is "suspended," so that no navigation messages are sent to +// its renderer until the onbeforeunload JavaScript handler has a chance to +// run in the current RVH. +// - The pending RVH tells CrossSiteRequestManager (a thread-safe singleton) +// that it has a pending cross-site request. ResourceDispatcherHost will +// check for this when the response arrives. +// - The current RVH runs its onbeforeunload handler. If it returns false, we +// cancel all the pending logic and go back to NORMAL. Otherwise we allow +// the pending RVH to send the navigation request to its renderer. +// - ResourceDispatcherHost receives a ResourceRequest on the IO thread. It +// checks CrossSiteRequestManager to see that the RVH responsible has a +// pending cross-site request, and then installs a CrossSiteEventHandler. +// - When RDH receives a response, the BufferedEventHandler determines whether +// it is a download. If so, it sends a message to the new renderer causing +// it to cancel the request, and the download proceeds in the download +// thread. For now, we stay in a PENDING state (with a pending RVH) until +// the next DidNavigate event for this WebContents. This isn't ideal, but it +// doesn't affect any functionality. +// - After RDH receives a response and determines that it is safe and not a +// download, it pauses the response to first run the old page's onunload +// handler. It does this by asynchronously calling the OnCrossSiteResponse +// method of WebContents on the UI thread, which sends a ClosePage message +// to the current RVH. +// - Once the onunload handler is finished, a ClosePage_ACK message is sent to +// the ResourceDispatcherHost, who unpauses the response. Data is then sent +// to the pending RVH. +// - The pending renderer sends a FrameNavigate message that invokes the +// DidNavigate method. This replaces the current RVH with the +// pending RVH and goes back to the NORMAL RendererState. + +namespace { + +// Amount of time we wait between when a key event is received and the renderer +// is queried for its state and pushed to the NavigationEntry. +const int kQueryStateDelay = 5000; + +const int kSyncWaitDelay = 40; + +// If another javascript message box is displayed within +// kJavascriptMessageExpectedDelay of a previous javascript message box being +// dismissed, display an option to suppress future message boxes from this +// contents. +const int kJavascriptMessageExpectedDelay = 1000; + +// Minimum amount of time in ms that has to elapse since the download shelf was +// shown for us to hide it when navigating away from the current page. +const int kDownloadShelfHideDelay = 5000; + +const char kLinkDoctorBaseURL[] = + "http://linkhelp.clients.google.com/tbproxy/lh/fixurl"; + +// The printer icon in shell32.dll. That's a standard icon user will quickly +// recognize. +const int kShell32PrinterIcon = 17; + +// The list of prefs we want to observe. +const wchar_t* kPrefsToObserve[] = { + prefs::kAlternateErrorPagesEnabled, + prefs::kWebKitJavaEnabled, + prefs::kWebKitJavascriptEnabled, + prefs::kWebKitLoadsImagesAutomatically, + prefs::kWebKitPluginsEnabled, + prefs::kWebKitUsesUniversalDetector, + prefs::kWebKitSerifFontFamily, + prefs::kWebKitSansSerifFontFamily, + prefs::kWebKitFixedFontFamily, + prefs::kWebKitDefaultFontSize, + prefs::kWebKitDefaultFixedFontSize, + prefs::kDefaultCharset + // kWebKitStandardFontIsSerif needs to be added + // if we let users pick which font to use, serif or sans-serif when + // no font is specified or a CSS generic family (serif or sans-serif) + // is not specified. +}; + +const int kPrefsToObserveLength = arraysize(kPrefsToObserve); + +// Limit on the number of suggestions to appear in the pop-up menu under an +// text input element in a form. +const int kMaxAutofillMenuItems = 6; + +// Returns true if the entry's transition type is FORM_SUBMIT. +bool IsFormSubmit(const NavigationEntry* entry) { + return (PageTransition::StripQualifier(entry->transition_type()) == + PageTransition::FORM_SUBMIT); +} + +#if defined(OS_WIN) + +BOOL CALLBACK InvalidateWindow(HWND hwnd, LPARAM lparam) { + // Note: erase is required to properly paint some widgets borders. This can + // be seen with textfields. + InvalidateRect(hwnd, NULL, TRUE); + return TRUE; +} +#endif + +// TODO(brettw) WebContents should be removed, then this hack is unnecessary. +WebContents* AsWC(TabContents* tc) { + return static_cast<WebContents*>(tc); +} + +} // namespace + +// ----------------------------------------------------------------------------- + // static int TabContents::find_request_id_counter_ = -1; +class TabContents::GearsCreateShortcutCallbackFunctor { + public: + explicit GearsCreateShortcutCallbackFunctor(TabContents* contents) + : contents_(contents) {} + + void Run(const GearsShortcutData2& shortcut_data, bool success) { + if (contents_) + contents_->OnGearsCreateShortcutDone(shortcut_data, success); + delete this; + } + void Cancel() { + contents_ = NULL; + } + + private: + TabContents* contents_; +}; + // TODO(brettw) many of the data members here have casts to WebContents. // This object is the same as WebContents and is currently being merged. // When this merge is done, the casts can be removed. -TabContents::TabContents(Profile* profile) +TabContents::TabContents(Profile* profile, + SiteInstance* site_instance, + int routing_id, + base::WaitableEvent* modal_dialog_event) : delegate_(NULL), controller_(this, profile), view_(TabContentsView::Create(static_cast<WebContents*>(this))), @@ -93,16 +252,154 @@ TabContents::TabContents(Profile* profile) #endif last_javascript_message_dismissal_(), suppress_javascript_messages_(false) { + + pending_install_.page_id = 0; + pending_install_.callback_functor = NULL; + + render_manager_.Init(profile, site_instance, routing_id, modal_dialog_event); + + view_->CreateView(); + + // Register for notifications about all interested prefs change. + PrefService* prefs = profile->GetPrefs(); + if (prefs) { + for (int i = 0; i < kPrefsToObserveLength; ++i) + prefs->AddPrefObserver(kPrefsToObserve[i], this); + } + + // Register for notifications about URL starredness changing on any profile. + registrar_.Add(this, NotificationType::URLS_STARRED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED, + NotificationService::AllSources()); + + // Keep a global copy of the previous search string (if any). + static string16 global_last_search = string16(); + find_prepopulate_text_ = &global_last_search; } TabContents::~TabContents() { + is_being_destroyed_ = true; + + // We don't want any notifications while we're runnign our destructor. + registrar_.RemoveAll(); + + // Unregister the notifications of all observed prefs change. + PrefService* prefs = profile()->GetPrefs(); + if (prefs) { + for (int i = 0; i < kPrefsToObserveLength; ++i) + prefs->RemovePrefObserver(kPrefsToObserve[i], this); + } + + // Clean up subwindows like plugins and the find in page bar. + view_->OnContentsDestroy(); + + NotifyDisconnected(); + HungRendererWarning::HideForWebContents(AsWC(this)); + + if (pending_install_.callback_functor) + pending_install_.callback_functor->Cancel(); + + // First cleanly close all child windows. + // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked + // some of these to close. CloseWindows is async, so it might get called + // twice before it runs. + int size = static_cast<int>(child_windows_.size()); + for (int i = size - 1; i >= 0; --i) { + ConstrainedWindow* window = child_windows_[i]; + if (window) + window->CloseConstrainedWindow(); + } + + // Notify any lasting InfobarDelegates that have not yet been removed that + // whatever infobar they were handling in this TabContents has closed, + // because the TabContents is going away entirely. + for (int i = 0; i < infobar_delegate_count(); ++i) { + InfoBarDelegate* delegate = GetInfoBarDelegateAt(i); + delegate->InfoBarClosed(); + } + infobar_delegates_.clear(); + + // Notify any observer that have a reference on this tab contents. + NotificationService::current()->Notify( + NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(this), + NotificationService::NoDetails()); + + // TODO(brettw) this should be moved to the view. +#if defined(OS_WIN) + // If we still have a window handle, destroy it. GetNativeView can return + // NULL if this contents was part of a window that closed. + if (GetNativeView()) + ::DestroyWindow(GetNativeView()); +#endif } // static void TabContents::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterBooleanPref(prefs::kAlternateErrorPagesEnabled, true); + + WebPreferences pref_defaults; + prefs->RegisterBooleanPref(prefs::kWebKitJavascriptEnabled, + pref_defaults.javascript_enabled); + prefs->RegisterBooleanPref(prefs::kWebKitWebSecurityEnabled, + pref_defaults.web_security_enabled); + prefs->RegisterBooleanPref( + prefs::kWebKitJavascriptCanOpenWindowsAutomatically, true); + prefs->RegisterBooleanPref(prefs::kWebKitLoadsImagesAutomatically, + pref_defaults.loads_images_automatically); + prefs->RegisterBooleanPref(prefs::kWebKitPluginsEnabled, + pref_defaults.plugins_enabled); + prefs->RegisterBooleanPref(prefs::kWebKitDomPasteEnabled, + pref_defaults.dom_paste_enabled); + prefs->RegisterBooleanPref(prefs::kWebKitShrinksStandaloneImagesToFit, + pref_defaults.shrinks_standalone_images_to_fit); + prefs->RegisterBooleanPref(prefs::kWebKitDeveloperExtrasEnabled, + true); + prefs->RegisterBooleanPref(prefs::kWebKitTextAreasAreResizable, + pref_defaults.text_areas_are_resizable); + prefs->RegisterBooleanPref(prefs::kWebKitJavaEnabled, + pref_defaults.java_enabled); + + prefs->RegisterLocalizedStringPref(prefs::kAcceptLanguages, + IDS_ACCEPT_LANGUAGES); + prefs->RegisterLocalizedStringPref(prefs::kDefaultCharset, + IDS_DEFAULT_ENCODING); + prefs->RegisterLocalizedBooleanPref(prefs::kWebKitStandardFontIsSerif, + IDS_STANDARD_FONT_IS_SERIF); + prefs->RegisterLocalizedStringPref(prefs::kWebKitFixedFontFamily, + IDS_FIXED_FONT_FAMILY); + prefs->RegisterLocalizedStringPref(prefs::kWebKitSerifFontFamily, + IDS_SERIF_FONT_FAMILY); + prefs->RegisterLocalizedStringPref(prefs::kWebKitSansSerifFontFamily, + IDS_SANS_SERIF_FONT_FAMILY); + prefs->RegisterLocalizedStringPref(prefs::kWebKitCursiveFontFamily, + IDS_CURSIVE_FONT_FAMILY); + prefs->RegisterLocalizedStringPref(prefs::kWebKitFantasyFontFamily, + IDS_FANTASY_FONT_FAMILY); + prefs->RegisterLocalizedIntegerPref(prefs::kWebKitDefaultFontSize, + IDS_DEFAULT_FONT_SIZE); + prefs->RegisterLocalizedIntegerPref(prefs::kWebKitDefaultFixedFontSize, + IDS_DEFAULT_FIXED_FONT_SIZE); + prefs->RegisterLocalizedIntegerPref(prefs::kWebKitMinimumFontSize, + IDS_MINIMUM_FONT_SIZE); + prefs->RegisterLocalizedIntegerPref(prefs::kWebKitMinimumLogicalFontSize, + IDS_MINIMUM_LOGICAL_FONT_SIZE); + prefs->RegisterLocalizedBooleanPref(prefs::kWebKitUsesUniversalDetector, + IDS_USES_UNIVERSAL_DETECTOR); + prefs->RegisterLocalizedStringPref(prefs::kStaticEncodings, + IDS_STATIC_ENCODING_LIST); + prefs->RegisterBooleanPref(prefs::kBlockPopups, false); } + // TODO(brettw) Remove WebContents. +WebContents* TabContents::AsWebContents() { + return AsWC(this); +} + bool TabContents::SupportsURL(GURL* url) { // TODO(brettw) remove this function. return true; @@ -438,13 +735,37 @@ TabContents* TabContents::Clone() { // We create a new SiteInstance so that the new tab won't share processes // with the old one. This can be changed in the future if we need it to share // processes for some reason. - TabContents* tc = new WebContents(profile(), + TabContents* tc = new TabContents(profile(), SiteInstance::CreateSiteInstance(profile()), MSG_ROUTING_NONE, NULL); tc->controller().CopyStateFrom(controller_); return tc; } +void TabContents::CreateShortcut() { + NavigationEntry* entry = controller_.GetLastCommittedEntry(); + if (!entry) + return; + + // We only allow one pending install request. By resetting the page id we + // effectively cancel the pending install request. + pending_install_.page_id = entry->page_id(); + pending_install_.icon = GetFavIcon(); + pending_install_.title = UTF16ToWideHack(GetTitle()); + pending_install_.url = GetURL(); + if (pending_install_.callback_functor) { + pending_install_.callback_functor->Cancel(); + pending_install_.callback_functor = NULL; + } + DCHECK(!pending_install_.icon.isNull()) << "Menu item should be disabled."; + if (pending_install_.title.empty()) + pending_install_.title = UTF8ToWide(GetURL().spec()); + + // Request the application info. When done OnDidGetApplicationInfo is invoked + // and we'll create the shortcut. + render_view_host()->GetApplicationInfo(pending_install_.page_id); +} + #if defined(OS_WIN) ConstrainedWindow* TabContents::CreateConstrainedDialog( views::WindowDelegate* window_delegate, @@ -754,6 +1075,67 @@ void TabContents::StopFinding(bool clear_selection) { render_view_host()->StopFinding(clear_selection); } +void TabContents::OnJavaScriptMessageBoxClosed(IPC::Message* reply_msg, + bool success, + const std::wstring& prompt) { + last_javascript_message_dismissal_ = base::TimeTicks::Now(); + render_manager_.OnJavaScriptMessageBoxClosed(reply_msg, success, prompt); +} + +void TabContents::OnSavePage() { + // If we can not save the page, try to download it. + if (!SavePackage::IsSavableContents(contents_mime_type())) { + DownloadManager* dlm = profile()->GetDownloadManager(); + const GURL& current_page_url = GetURL(); + if (dlm && current_page_url.is_valid()) + dlm->DownloadUrl(current_page_url, GURL(), AsWC(this)); + return; + } + + Stop(); + + // Create the save package and possibly prompt the user for the name to save + // the page as. The user prompt is an asynchronous operation that runs on + // another thread. + save_package_ = new SavePackage(AsWC(this)); + save_package_->GetSaveInfo(); +} + +// Used in automated testing to bypass prompting the user for file names. +// Instead, the names and paths are hard coded rather than running them through +// file name sanitation and extension / mime checking. +void TabContents::SavePage(const std::wstring& main_file, + const std::wstring& dir_path, + SavePackage::SavePackageType save_type) { + // Stop the page from navigating. + Stop(); + + save_package_ = new SavePackage(AsWC(this), save_type, + FilePath::FromWStringHack(main_file), + FilePath::FromWStringHack(dir_path)); + save_package_->Init(); +} + +void TabContents::PrintPreview() { + // We don't show the print preview yet, only the print dialog. + PrintNow(); +} + +bool TabContents::PrintNow() { + // We can't print interstitial page for now. + if (showing_interstitial_page()) + return false; + + return render_view_host()->PrintPages(); +} + +bool TabContents::IsActiveEntry(int32 page_id) { + NavigationEntry* active_entry = controller_.GetActiveEntry(); + return (active_entry != NULL && + active_entry->site_instance() == GetSiteInstance() && + active_entry->page_id() == page_id); +} + // Notifies the RenderWidgetHost instance about the fact that the page is // loading, or done loading and calls the base implementation. void TabContents::SetIsLoading(bool is_loading, @@ -885,3 +1267,1175 @@ DOMUI* TabContents::GetDOMUIForCurrentState() { return render_manager_.pending_dom_ui(); return render_manager_.dom_ui(); } + +void TabContents::DidNavigateMainFramePostCommit( + const NavigationController::LoadCommittedDetails& details, + const ViewHostMsg_FrameNavigate_Params& params) { + // Hide the download shelf if all the following conditions are true: + // - there are no active downloads. + // - this is a navigation to a different TLD. + // - at least 5 seconds have elapsed since the download shelf was shown. + // TODO(jcampan): bug 1156075 when user gestures are reliable, they should + // be used to ensure we are hiding only on user initiated + // navigations. + DownloadManager* download_manager = profile()->GetDownloadManager(); + // download_manager can be NULL in unit test context. + if (download_manager && download_manager->in_progress_count() == 0 && + !details.previous_url.is_empty() && + !net::RegistryControlledDomainService::SameDomainOrHost( + details.previous_url, details.entry->url())) { + base::TimeDelta time_delta( + base::TimeTicks::Now() - last_download_shelf_show_); + if (time_delta > + base::TimeDelta::FromMilliseconds(kDownloadShelfHideDelay)) { + SetDownloadShelfVisible(false); + } + } + + if (details.is_user_initiated_main_frame_load()) { + // Clear the status bubble. This is a workaround for a bug where WebKit + // doesn't let us know that the cursor left an element during a + // transition (this is also why the mouse cursor remains as a hand after + // clicking on a link); see bugs 1184641 and 980803. We don't want to + // clear the bubble when a user navigates to a named anchor in the same + // page. + UpdateTargetURL(details.entry->page_id(), GURL()); + + // UpdateHelpersForDidNavigate will handle the case where the password_form + // origin is valid. + // TODO(brettw) bug 1343111: Password manager stuff in here needs to be + // cleaned up and covered by tests. + if (!params.password_form.origin.is_valid()) + GetPasswordManager()->DidNavigate(); + } + + // The keyword generator uses the navigation entries, so must be called after + // the commit. + GenerateKeywordIfNecessary(params); + + // Allow the new page to set the title again. + received_page_title_ = false; + + // Get the favicon, either from history or request it from the net. + fav_icon_helper_.FetchFavIcon(details.entry->url()); + + // Close constrained popups if necessary. + MaybeCloseChildWindows(details.previous_url, details.entry->url()); + + // Update the starred state. + UpdateStarredStateForCurrentURL(); +} + +void TabContents::DidNavigateAnyFramePostCommit( + RenderViewHost* render_view_host, + const NavigationController::LoadCommittedDetails& details, + const ViewHostMsg_FrameNavigate_Params& params) { + // If we navigate, start showing messages again. This does nothing to prevent + // a malicious script from spamming messages, since the script could just + // reload the page to stop blocking. + suppress_javascript_messages_ = false; + + // Update history. Note that this needs to happen after the entry is complete, + // which WillNavigate[Main,Sub]Frame will do before this function is called. + if (params.should_update_history) { + // Most of the time, the displayURL matches the loaded URL, but for about: + // URLs, we use a data: URL as the real value. We actually want to save + // the about: URL to the history db and keep the data: URL hidden. This is + // what the TabContents' URL getter does. + UpdateHistoryForNavigation(GetURL(), params); + } + + // Notify the password manager of the navigation or form submit. + // TODO(brettw) bug 1343111: Password manager stuff in here needs to be + // cleaned up and covered by tests. + if (params.password_form.origin.is_valid()) + GetPasswordManager()->ProvisionallySavePassword(params.password_form); +} + +void TabContents::MaybeCloseChildWindows(const GURL& previous_url, + const GURL& current_url) { + if (net::RegistryControlledDomainService::SameDomainOrHost( + previous_url, current_url)) + return; + + // Clear out any child windows since we are leaving this page entirely. + // We use indices instead of iterators in case CloseWindow does something + // that may invalidate an iterator. + int size = static_cast<int>(child_windows_.size()); + for (int i = size - 1; i >= 0; --i) { + ConstrainedWindow* window = child_windows_[i]; + if (window) + window->CloseConstrainedWindow(); + } +} + +void TabContents::UpdateStarredStateForCurrentURL() { + BookmarkModel* model = profile()->GetBookmarkModel(); + const bool old_state = is_starred_; + is_starred_ = (model && model->IsBookmarked(GetURL())); + + if (is_starred_ != old_state && delegate()) + delegate()->URLStarredChanged(this, is_starred_); +} + +void TabContents::UpdateAlternateErrorPageURL() { + GURL url = GetAlternateErrorPageURL(); + render_view_host()->SetAlternateErrorPageURL(url); +} + +void TabContents::UpdateWebPreferences() { + render_view_host()->UpdateWebPreferences(GetWebkitPrefs()); +} + +void TabContents::UpdateMaxPageIDIfNecessary(SiteInstance* site_instance, + RenderViewHost* rvh) { + // If we are creating a RVH for a restored controller, then we might + // have more page IDs than the SiteInstance's current max page ID. We must + // make sure that the max page ID is larger than any restored page ID. + // Note that it is ok for conflicting page IDs to exist in another tab + // (i.e., NavigationController), but if any page ID is larger than the max, + // the back/forward list will get confused. + int max_restored_page_id = controller_.max_restored_page_id(); + if (max_restored_page_id > 0) { + int curr_max_page_id = site_instance->max_page_id(); + if (max_restored_page_id > curr_max_page_id) { + // Need to update the site instance immediately. + site_instance->UpdateMaxPageID(max_restored_page_id); + + // Also tell the renderer to update its internal representation. We + // need to reserve enough IDs to make all restored page IDs less than + // the max. + if (curr_max_page_id < 0) + curr_max_page_id = 0; + rvh->ReservePageIDRange(max_restored_page_id - curr_max_page_id); + } + } +} + +void TabContents::UpdateHistoryForNavigation( + const GURL& display_url, + const ViewHostMsg_FrameNavigate_Params& params) { + if (profile()->IsOffTheRecord()) + return; + + // Add to history service. + HistoryService* hs = profile()->GetHistoryService(Profile::IMPLICIT_ACCESS); + if (hs) { + if (PageTransition::IsMainFrame(params.transition) && + display_url != params.url) { + // Hack on the "display" URL so that it will appear in history. For some + // types of URLs, we will display a magic URL that is different from where + // the page is actually navigated. We want the user to see in history + // what they saw in the URL bar, so we add the display URL as a redirect. + // This only applies to the main frame, as the display URL doesn't apply + // to sub-frames. + std::vector<GURL> redirects = params.redirects; + if (!redirects.empty()) + redirects.back() = display_url; + hs->AddPage(display_url, this, params.page_id, params.referrer, + params.transition, redirects); + } else { + hs->AddPage(params.url, this, params.page_id, params.referrer, + params.transition, params.redirects); + } + } +} + +bool TabContents::UpdateTitleForEntry(NavigationEntry* entry, + const std::wstring& title) { + // For file URLs without a title, use the pathname instead. In the case of a + // synthesized title, we don't want the update to count toward the "one set + // per page of the title to history." + std::wstring final_title; + bool explicit_set; + if (entry->url().SchemeIsFile() && title.empty()) { + final_title = UTF8ToWide(entry->url().ExtractFileName()); + explicit_set = false; // Don't count synthetic titles toward the set limit. + } else { + TrimWhitespace(title, TRIM_ALL, &final_title); + explicit_set = true; + } + + if (final_title == UTF16ToWideHack(entry->title())) + return false; // Nothing changed, don't bother. + + entry->set_title(WideToUTF16Hack(final_title)); + + // Update the history system for this page. + if (!profile()->IsOffTheRecord() && !received_page_title_) { + HistoryService* hs = + profile()->GetHistoryService(Profile::IMPLICIT_ACCESS); + if (hs) + hs->SetPageTitle(entry->display_url(), final_title); + + // Don't allow the title to be saved again for explicitly set ones. + received_page_title_ = explicit_set; + } + + // Lastly, set the title for the view. + view_->SetPageTitle(final_title); + + return true; +} + +void TabContents::NotifySwapped() { + // After sending out a swap notification, we need to send a disconnect + // notification so that clients that pick up a pointer to |this| can NULL the + // pointer. See Bug 1230284. + notify_disconnection_ = true; + NotificationService::current()->Notify( + NotificationType::WEB_CONTENTS_SWAPPED, + Source<WebContents>(AsWC(this)), + NotificationService::NoDetails()); +} + +void TabContents::NotifyConnected() { + notify_disconnection_ = true; + NotificationService::current()->Notify( + NotificationType::WEB_CONTENTS_CONNECTED, + Source<WebContents>(AsWC(this)), + NotificationService::NoDetails()); +} + +void TabContents::NotifyDisconnected() { + if (!notify_disconnection_) + return; + + notify_disconnection_ = false; + NotificationService::current()->Notify( + NotificationType::WEB_CONTENTS_DISCONNECTED, + Source<WebContents>(AsWC(this)), + NotificationService::NoDetails()); +} + +void TabContents::GenerateKeywordIfNecessary( + const ViewHostMsg_FrameNavigate_Params& params) { + if (!params.searchable_form_url.is_valid()) + return; + + if (profile()->IsOffTheRecord()) + return; + + int last_index = controller_.last_committed_entry_index(); + // When there was no previous page, the last index will be 0. This is + // normally due to a form submit that opened in a new tab. + // TODO(brettw) bug 916126: we should support keywords when form submits + // happen in new tabs. + if (last_index <= 0) + return; + const NavigationEntry* previous_entry = + controller_.GetEntryAtIndex(last_index - 1); + if (IsFormSubmit(previous_entry)) { + // Only generate a keyword if the previous page wasn't itself a form + // submit. + return; + } + + GURL keyword_url = previous_entry->user_typed_url().is_valid() ? + previous_entry->user_typed_url() : previous_entry->url(); + std::wstring keyword = + TemplateURLModel::GenerateKeyword(keyword_url, true); // autodetected + if (keyword.empty()) + return; + + TemplateURLModel* url_model = profile()->GetTemplateURLModel(); + if (!url_model) + return; + + if (!url_model->loaded()) { + url_model->Load(); + return; + } + + const TemplateURL* current_url; + std::wstring url = UTF8ToWide(params.searchable_form_url.spec()); + if (!url_model->CanReplaceKeyword(keyword, url, ¤t_url)) + return; + + if (current_url) { + if (current_url->originating_url().is_valid()) { + // The existing keyword was generated from an OpenSearch description + // document, don't regenerate. + return; + } + url_model->Remove(current_url); + } + TemplateURL* new_url = new TemplateURL(); + new_url->set_keyword(keyword); + new_url->set_short_name(keyword); + new_url->SetURL(url, 0, 0); + new_url->add_input_encoding(params.searchable_form_encoding); + DCHECK(controller_.GetLastCommittedEntry()); + const GURL& favicon_url = + controller_.GetLastCommittedEntry()->favicon().url(); + if (favicon_url.is_valid()) { + new_url->SetFavIconURL(favicon_url); + } else { + // The favicon url isn't valid. This means there really isn't a favicon, + // or the favicon url wasn't obtained before the load started. This assumes + // the later. + // TODO(sky): Need a way to set the favicon that doesn't involve generating + // its url. + new_url->SetFavIconURL(TemplateURL::GenerateFaviconURL(params.referrer)); + } + new_url->set_safe_for_autoreplace(true); + url_model->Add(new_url); +} + +RenderViewHostDelegate::View* TabContents::GetViewDelegate() const { + return view_.get(); +} + +RenderViewHostDelegate::Save* TabContents::GetSaveDelegate() const { + return save_package_.get(); // May be NULL, but we can return NULL. +} + +Profile* TabContents::GetProfile() const { + return profile(); +} + +WebContents* TabContents::GetAsWebContents() { + return AsWC(this); +} + +void TabContents::RenderViewCreated(RenderViewHost* render_view_host) { + NavigationEntry* entry = controller_.GetActiveEntry(); + if (!entry) + return; + + // When we're creating views, we're still doing initial setup, so we always + // use the pending DOM UI rather than any possibly existing committed one. + if (render_manager_.pending_dom_ui()) { + render_manager_.pending_dom_ui()->RenderViewCreated(render_view_host); + } + + if (entry->IsViewSourceMode()) { + // Put the renderer in view source mode. + render_view_host->Send( + new ViewMsg_EnableViewSourceMode(render_view_host->routing_id())); + } +} + +void TabContents::RenderViewReady(RenderViewHost* rvh) { + if (rvh != render_view_host()) { + // Don't notify the world, since this came from a renderer in the + // background. + return; + } + + NotifyConnected(); + SetIsCrashed(false); +} + +void TabContents::RenderViewGone(RenderViewHost* rvh) { + // Ask the print preview if this renderer was valuable. + if (!printing_.OnRenderViewGone(rvh)) + return; + if (rvh != render_view_host()) { + // The pending page's RenderViewHost is gone. + return; + } + + SetIsLoading(false, NULL); + NotifyDisconnected(); + SetIsCrashed(true); + + // Force an invalidation to render sad tab. The view will notice we crashed + // when it paints. + view_->Invalidate(); + + // Hide any visible hung renderer warning for this web contents' process. + HungRendererWarning::HideForWebContents(AsWC(this)); +} + +void TabContents::DidNavigate(RenderViewHost* rvh, + const ViewHostMsg_FrameNavigate_Params& params) { + if (PageTransition::IsMainFrame(params.transition)) + render_manager_.DidNavigateMainFrame(rvh); + + // Update the site of the SiteInstance if it doesn't have one yet. + if (!GetSiteInstance()->has_site()) + GetSiteInstance()->SetSite(params.url); + + // Need to update MIME type here because it's referred to in + // UpdateNavigationCommands() called by RendererDidNavigate() to + // determine whether or not to enable the encoding menu. + // It's updated only for the main frame. For a subframe, + // RenderView::UpdateURL does not set params.contents_mime_type. + // (see http://code.google.com/p/chromium/issues/detail?id=2929 ) + // TODO(jungshik): Add a test for the encoding menu to avoid + // regressing it again. + if (PageTransition::IsMainFrame(params.transition)) + contents_mime_type_ = params.contents_mime_type; + + NavigationController::LoadCommittedDetails details; + if (!controller_.RendererDidNavigate(params, &details)) + return; // No navigation happened. + + // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen + // for the appropriate notification (best) or you can add it to + // DidNavigateMainFramePostCommit / DidNavigateAnyFramePostCommit (only if + // necessary, please). + + // Run post-commit tasks. + if (details.is_main_frame) + DidNavigateMainFramePostCommit(details, params); + DidNavigateAnyFramePostCommit(rvh, details, params); +} + +void TabContents::UpdateState(RenderViewHost* rvh, + int32 page_id, + const std::string& state) { + DCHECK(rvh == render_view_host()); + + // We must be prepared to handle state updates for any page, these occur + // when the user is scrolling and entering form data, as well as when we're + // leaving a page, in which case our state may have already been moved to + // the next page. The navigation controller will look up the appropriate + // NavigationEntry and update it when it is notified via the delegate. + + int entry_index = controller_.GetEntryIndexWithPageID( + GetSiteInstance(), page_id); + if (entry_index < 0) + return; + NavigationEntry* entry = controller_.GetEntryAtIndex(entry_index); + + if (state == entry->content_state()) + return; // Nothing to update. + entry->set_content_state(state); + controller_.NotifyEntryChanged(entry, entry_index); +} + +void TabContents::UpdateTitle(RenderViewHost* rvh, + int32 page_id, const std::wstring& title) { + // If we have a title, that's a pretty good indication that we've started + // getting useful data. + SetNotWaitingForResponse(); + + DCHECK(rvh == render_view_host()); + NavigationEntry* entry = controller_.GetEntryWithPageID(GetSiteInstance(), + page_id); + if (!entry || !UpdateTitleForEntry(entry, title)) + return; + + // Broadcast notifications when the UI should be updated. + if (entry == controller_.GetEntryAtOffset(0)) + NotifyNavigationStateChanged(INVALIDATE_TITLE); +} + +void TabContents::UpdateFeedList( + RenderViewHost* rvh, const ViewHostMsg_UpdateFeedList_Params& params) { + // We might have an old RenderViewHost sending messages, and we should ignore + // those messages. + if (rvh != render_view_host()) + return; + + NavigationEntry* entry = controller_.GetEntryWithPageID(GetSiteInstance(), + params.page_id); + if (!entry) + return; + + entry->set_feedlist(params.feedlist); + + // Broadcast notifications when the UI should be updated. + if (entry == controller_.GetEntryAtOffset(0)) + NotifyNavigationStateChanged(INVALIDATE_FEEDLIST); +} + +void TabContents::UpdateEncoding(RenderViewHost* render_view_host, + const std::wstring& encoding) { + set_encoding(encoding); +} + +void TabContents::UpdateTargetURL(int32 page_id, const GURL& url) { + if (delegate()) + delegate()->UpdateTargetURL(this, url); +} + +void TabContents::UpdateThumbnail(const GURL& url, + const SkBitmap& bitmap, + const ThumbnailScore& score) { + // Tell History about this thumbnail + HistoryService* hs; + if (!profile()->IsOffTheRecord() && + (hs = profile()->GetHistoryService(Profile::IMPLICIT_ACCESS))) { + hs->SetPageThumbnail(url, bitmap, score); + } +} + +void TabContents::Close(RenderViewHost* rvh) { + // Ignore this if it comes from a RenderViewHost that we aren't showing. + if (delegate() && rvh == render_view_host()) + delegate()->CloseContents(this); +} + +void TabContents::RequestMove(const gfx::Rect& new_bounds) { + if (delegate() && delegate()->IsPopup(this)) + delegate()->MoveContents(this, new_bounds); +} + +void TabContents::DidStartLoading(RenderViewHost* rvh, int32 page_id) { + SetIsLoading(true, NULL); +} + +void TabContents::DidStopLoading(RenderViewHost* rvh, int32 page_id) { + scoped_ptr<LoadNotificationDetails> details; + + NavigationEntry* entry = controller_.GetActiveEntry(); + // An entry may not exist for a stop when loading an initial blank page or + // if an iframe injected by script into a blank page finishes loading. + if (entry) { + scoped_ptr<base::ProcessMetrics> metrics( + base::ProcessMetrics::CreateProcessMetrics( + process()->process().handle())); + + base::TimeDelta elapsed = base::TimeTicks::Now() - current_load_start_; + + details.reset(new LoadNotificationDetails( + entry->display_url(), + entry->transition_type(), + elapsed, + &controller_, + controller_.GetCurrentEntryIndex())); + } + + // Tell PasswordManager we've finished a page load, which serves as a + // green light to save pending passwords and reset itself. + GetPasswordManager()->DidStopLoading(); + + SetIsLoading(false, details.get()); +} + +void TabContents::DidStartProvisionalLoadForFrame( + RenderViewHost* render_view_host, + bool is_main_frame, + const GURL& url) { + ProvisionalLoadDetails details(is_main_frame, + controller_.IsURLInPageNavigation(url), + url, std::string(), false); + NotificationService::current()->Notify( + NotificationType::FRAME_PROVISIONAL_LOAD_START, + Source<NavigationController>(&controller_), + Details<ProvisionalLoadDetails>(&details)); +} + +void TabContents::DidRedirectProvisionalLoad(int32 page_id, + const GURL& source_url, + const GURL& target_url) { + NavigationEntry* entry; + if (page_id == -1) + entry = controller_.pending_entry(); + else + entry = controller_.GetEntryWithPageID(GetSiteInstance(), page_id); + if (!entry || entry->url() != source_url) + return; + entry->set_url(target_url); +} + +void TabContents::DidLoadResourceFromMemoryCache( + const GURL& url, + const std::string& frame_origin, + const std::string& main_frame_origin, + const std::string& security_info) { + // Send out a notification that we loaded a resource from our memory cache. + int cert_id = 0, cert_status = 0, security_bits = 0; + SSLManager::DeserializeSecurityInfo(security_info, + &cert_id, &cert_status, + &security_bits); + LoadFromMemoryCacheDetails details(url, frame_origin, main_frame_origin, + cert_id, cert_status); + + NotificationService::current()->Notify( + NotificationType::LOAD_FROM_MEMORY_CACHE, + Source<NavigationController>(&controller_), + Details<LoadFromMemoryCacheDetails>(&details)); +} + +void TabContents::DidFailProvisionalLoadWithError( + RenderViewHost* render_view_host, + bool is_main_frame, + int error_code, + const GURL& url, + bool showing_repost_interstitial) { + if (net::ERR_ABORTED == error_code) { + // EVIL HACK ALERT! Ignore failed loads when we're showing interstitials. + // This means that the interstitial won't be torn down properly, which is + // bad. But if we have an interstitial, go back to another tab type, and + // then load the same interstitial again, we could end up getting the first + // interstitial's "failed" message (as a result of the cancel) when we're on + // the second one. + // + // We can't tell this apart, so we think we're tearing down the current page + // which will cause a crash later one. There is also some code in + // RenderViewHostManager::RendererAbortedProvisionalLoad that is commented + // out because of this problem. + // + // http://code.google.com/p/chromium/issues/detail?id=2855 + // Because this will not tear down the interstitial properly, if "back" is + // back to another tab type, the interstitial will still be somewhat alive + // in the previous tab type. If you navigate somewhere that activates the + // tab with the interstitial again, you'll see a flash before the new load + // commits of the interstitial page. + if (showing_interstitial_page()) { + LOG(WARNING) << "Discarding message during interstitial."; + return; + } + + // This will discard our pending entry if we cancelled the load (e.g., if we + // decided to download the file instead of load it). Only discard the + // pending entry if the URLs match, otherwise the user initiated a navigate + // before the page loaded so that the discard would discard the wrong entry. + NavigationEntry* pending_entry = controller_.pending_entry(); + if (pending_entry && pending_entry->url() == url) + controller_.DiscardNonCommittedEntries(); + + render_manager_.RendererAbortedProvisionalLoad(render_view_host); + } + + // Send out a notification that we failed a provisional load with an error. + ProvisionalLoadDetails details(is_main_frame, + controller_.IsURLInPageNavigation(url), + url, std::string(), false); + details.set_error_code(error_code); + + NotificationService::current()->Notify( + NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR, + Source<NavigationController>(&controller_), + Details<ProvisionalLoadDetails>(&details)); +} + +void TabContents::UpdateFavIconURL(RenderViewHost* render_view_host, + int32 page_id, + const GURL& icon_url) { + fav_icon_helper_.SetFavIconURL(icon_url); +} + +void TabContents::DidDownloadImage( + RenderViewHost* render_view_host, + int id, + const GURL& image_url, + bool errored, + const SkBitmap& image) { + // A notification for downloading would be more flexible, but for now I'm + // forwarding to the two places that could possibly have initiated the + // request. If we end up with another place invoking DownloadImage, probably + // best to refactor out into notification service, or something similar. + if (errored) + fav_icon_helper_.FavIconDownloadFailed(id); + else + fav_icon_helper_.SetFavIcon(id, image_url, image); +} + +void TabContents::RequestOpenURL(const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition) { + if (render_manager_.dom_ui()) { + // When we're a DOM UI, it will provide a page transition type for us (this + // is so the new tab page can specify AUTO_BOOKMARK for automatically + // generated suggestions). + // + // Note also that we hide the referrer for DOM UI pages. We don't really + // want web sites to see a referrer of "chrome-ui://blah" (and some + // chrome-ui URLs might have search terms or other stuff we don't want to + // send to the site), so we send no referrer. + OpenURL(url, GURL(), disposition, + render_manager_.dom_ui()->link_transition_type()); + } else { + OpenURL(url, referrer, disposition, PageTransition::LINK); + } +} + +void TabContents::DomOperationResponse(const std::string& json_string, + int automation_id) { + DomOperationNotificationDetails details(json_string, automation_id); + NotificationService::current()->Notify( + NotificationType::DOM_OPERATION_RESPONSE, Source<WebContents>(AsWC(this)), + Details<DomOperationNotificationDetails>(&details)); +} + +void TabContents::ProcessDOMUIMessage(const std::string& message, + const std::string& content) { + if (!render_manager_.dom_ui()) { + // We shouldn't get a DOM UI message when we haven't enabled the DOM UI. + // Because the renderer might be owned and sending random messages, we need + // to ignore these inproper ones. + NOTREACHED(); + return; + } + render_manager_.dom_ui()->ProcessDOMUIMessage(message, content); +} + +void TabContents::ProcessExternalHostMessage(const std::string& message, + const std::string& origin, + const std::string& target) { + if (delegate()) + delegate()->ForwardMessageToExternalHost(message, origin, target); +} + +void TabContents::GoToEntryAtOffset(int offset) { + controller_.GoToOffset(offset); +} + +void TabContents::GetHistoryListCount(int* back_list_count, + int* forward_list_count) { + int current_index = controller_.last_committed_entry_index(); + *back_list_count = current_index; + *forward_list_count = controller_.entry_count() - current_index - 1; +} + +void TabContents::RunFileChooser(bool multiple_files, + const string16& title, + const FilePath& default_file) { + if (!select_file_dialog_.get()) + select_file_dialog_ = SelectFileDialog::Create(this); + SelectFileDialog::Type dialog_type = + multiple_files ? SelectFileDialog::SELECT_OPEN_MULTI_FILE : + SelectFileDialog::SELECT_OPEN_FILE; + select_file_dialog_->SelectFile(dialog_type, title, default_file, + NULL, 0, FILE_PATH_LITERAL(""), + view_->GetTopLevelNativeWindow(), NULL); +} + +void TabContents::RunJavaScriptMessage( + const std::wstring& message, + const std::wstring& default_prompt, + const GURL& frame_url, + const int flags, + IPC::Message* reply_msg, + bool* did_suppress_message) { + // Suppress javascript messages when requested and when inside a constrained + // popup window (because that activates them and breaks them out of the + // constrained window jail). + bool suppress_this_message = suppress_javascript_messages_; + if (delegate()) + suppress_this_message |= + (delegate()->GetConstrainingContents(this) != NULL); + + *did_suppress_message = suppress_this_message; + + if (!suppress_this_message) { + base::TimeDelta time_since_last_message( + base::TimeTicks::Now() - last_javascript_message_dismissal_); + bool show_suppress_checkbox = false; + // Show a checkbox offering to suppress further messages if this message is + // being displayed within kJavascriptMessageExpectedDelay of the last one. + if (time_since_last_message < + base::TimeDelta::FromMilliseconds(kJavascriptMessageExpectedDelay)) + show_suppress_checkbox = true; + + RunJavascriptMessageBox(AsWC(this), frame_url, flags, message, default_prompt, + show_suppress_checkbox, reply_msg); + } else { + // If we are suppressing messages, just reply as is if the user immediately + // pressed "Cancel". + OnJavaScriptMessageBoxClosed(reply_msg, false, std::wstring()); + } +} + +void TabContents::RunBeforeUnloadConfirm(const std::wstring& message, + IPC::Message* reply_msg) { + RunBeforeUnloadDialog(AsWC(this), message, reply_msg); +} + +void TabContents::ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + IPC::Message* reply_msg) { + if (delegate()) { + HtmlDialogUIDelegate* dialog_delegate = + new ModalHtmlDialogDelegate(url, width, height, json_arguments, + reply_msg, AsWC(this)); + delegate()->ShowHtmlDialog(dialog_delegate, NULL); + } +} + +void TabContents::PasswordFormsSeen( + const std::vector<PasswordForm>& forms) { + GetPasswordManager()->PasswordFormsSeen(forms); +} + +void TabContents::AutofillFormSubmitted( + const AutofillForm& form) { + GetAutofillManager()->AutofillFormSubmitted(form); +} + +void TabContents::GetAutofillSuggestions(const std::wstring& field_name, + const std::wstring& user_text, int64 node_id, int request_id) { + GetAutofillManager()->FetchValuesForName(field_name, user_text, + kMaxAutofillMenuItems, node_id, request_id); +} + +void TabContents::RemoveAutofillEntry(const std::wstring& field_name, + const std::wstring& value) { + GetAutofillManager()->RemoveValueForName(field_name, value); +} + +// Checks to see if we should generate a keyword based on the OSDD, and if +// necessary uses TemplateURLFetcher to download the OSDD and create a keyword. +void TabContents::PageHasOSDD(RenderViewHost* render_view_host, + int32 page_id, const GURL& url, + bool autodetected) { + // Make sure page_id is the current page, and the TemplateURLModel is loaded. + DCHECK(url.is_valid()); + if (!IsActiveEntry(page_id)) + return; + TemplateURLModel* url_model = profile()->GetTemplateURLModel(); + if (!url_model) + return; + if (!url_model->loaded()) { + url_model->Load(); + return; + } + if (!profile()->GetTemplateURLFetcher()) + return; + + if (profile()->IsOffTheRecord()) + return; + + const NavigationEntry* entry = controller_.GetLastCommittedEntry(); + DCHECK(entry); + + const NavigationEntry* base_entry = entry; + if (IsFormSubmit(base_entry)) { + // If the current page is a form submit, find the last page that was not + // a form submit and use its url to generate the keyword from. + int index = controller_.last_committed_entry_index() - 1; + while (index >= 0 && IsFormSubmit(controller_.GetEntryAtIndex(index))) + index--; + if (index >= 0) + base_entry = controller_.GetEntryAtIndex(index); + else + base_entry = NULL; + } + + // We want to use the user typed URL if available since that represents what + // the user typed to get here, and fall back on the regular URL if not. + if (!base_entry) + return; + GURL keyword_url = base_entry->user_typed_url().is_valid() ? + base_entry->user_typed_url() : base_entry->url(); + if (!keyword_url.is_valid()) + return; + std::wstring keyword = TemplateURLModel::GenerateKeyword(keyword_url, + autodetected); + if (keyword.empty()) + return; + const TemplateURL* template_url = + url_model->GetTemplateURLForKeyword(keyword); + if (template_url && (!template_url->safe_for_autoreplace() || + template_url->originating_url() == url)) { + // Either there is a user created TemplateURL for this keyword, or the + // keyword has the same OSDD url and we've parsed it. + return; + } + + // Download the OpenSearch description document. If this is successful a + // new keyword will be created when done. +#if defined(OS_WIN) + gfx::NativeView ancestor = GetAncestor(view_->GetNativeView(), GA_ROOT); +#else + gfx::NativeView ancestor = NULL; +#endif + profile()->GetTemplateURLFetcher()->ScheduleDownload( + keyword, + url, + base_entry->favicon().url(), + ancestor, + autodetected); +} + +void TabContents::InspectElementReply(int num_resources) { + // We have received reply from inspect element request. Notify the + // automation provider in case we need to notify automation client. + NotificationService::current()->Notify( + NotificationType::DOM_INSPECT_ELEMENT_RESPONSE, Source<WebContents>(AsWC(this)), + Details<int>(&num_resources)); +} + +void TabContents::DidGetPrintedPagesCount(int cookie, int number_pages) { + printing_.DidGetPrintedPagesCount(cookie, number_pages); +} + +void TabContents::DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params) { + printing_.DidPrintPage(params); +} + +GURL TabContents::GetAlternateErrorPageURL() const { + GURL url; + // Disable alternate error pages when in OffTheRecord/Incognito mode. + if (profile()->IsOffTheRecord()) + return url; + + PrefService* prefs = profile()->GetPrefs(); + DCHECK(prefs); + if (prefs->GetBoolean(prefs::kAlternateErrorPagesEnabled)) { + url = google_util::AppendGoogleLocaleParam(GURL(kLinkDoctorBaseURL)); + url = google_util::AppendGoogleTLDParam(url); + } + return url; +} + +WebPreferences TabContents::GetWebkitPrefs() { + PrefService* prefs = render_view_host()->process()->profile()->GetPrefs(); + bool isDomUI = false; + return RenderViewHostDelegateHelper::GetWebkitPrefs(prefs, isDomUI); +} + +void TabContents::OnMissingPluginStatus(int status) { +#if defined(OS_WIN) +// TODO(PORT): pull in when plug-ins work + GetPluginInstaller()->OnMissingPluginStatus(status); +#endif +} + +void TabContents::OnCrashedPlugin(const FilePath& plugin_path) { +#if defined(OS_WIN) +// TODO(PORT): pull in when plug-ins work + DCHECK(!plugin_path.value().empty()); + + std::wstring plugin_name = plugin_path.ToWStringHack(); + scoped_ptr<FileVersionInfo> version_info( + FileVersionInfo::CreateFileVersionInfo(plugin_path)); + if (version_info.get()) { + const std::wstring& product_name = version_info->product_name(); + if (!product_name.empty()) + plugin_name = product_name; + } + AddInfoBar(new SimpleAlertInfoBarDelegate( + this, l10n_util::GetStringF(IDS_PLUGIN_CRASHED_PROMPT, plugin_name), + NULL)); +#endif +} + +void TabContents::OnCrashedWorker() { + AddInfoBar(new SimpleAlertInfoBarDelegate( + this, l10n_util::GetString(IDS_WEBWORKER_CRASHED_PROMPT), + NULL)); +} + +void TabContents::OnJSOutOfMemory() { + AddInfoBar(new SimpleAlertInfoBarDelegate( + this, l10n_util::GetString(IDS_JS_OUT_OF_MEMORY_PROMPT), NULL)); +} + +void TabContents::ShouldClosePage(bool proceed) { + render_manager_.ShouldClosePage(proceed); +} + +void TabContents::OnCrossSiteResponse(int new_render_process_host_id, + int new_request_id) { + // Allows the WebContents to react when a cross-site response is ready to be + // delivered to a pending RenderViewHost. We must first run the onunload + // handler of the old RenderViewHost before we can allow it to proceed. + render_manager_.OnCrossSiteResponse(new_render_process_host_id, + new_request_id); +} + +bool TabContents::CanBlur() const { + return delegate() ? delegate()->CanBlur() : true; +} + +gfx::Rect TabContents::GetRootWindowResizerRect() const { + if (delegate()) + return delegate()->GetRootWindowResizerRect(); + return gfx::Rect(); +} + +void TabContents::RendererUnresponsive(RenderViewHost* rvh, + bool is_during_unload) { + if (is_during_unload) { + // Hang occurred while firing the beforeunload/unload handler. + // Pretend the handler fired so tab closing continues as if it had. + rvh->UnloadListenerHasFired(); + + if (!render_manager_.ShouldCloseTabOnUnresponsiveRenderer()) + return; + + // If the tab hangs in the beforeunload/unload handler there's really + // nothing we can do to recover. Pretend the unload listeners have + // all fired and close the tab. If the hang is in the beforeunload handler + // then the user will not have the option of cancelling the close. + Close(rvh); + return; + } + + if (render_view_host() && render_view_host()->IsRenderViewLive()) + HungRendererWarning::ShowForWebContents(AsWC(this)); +} + +void TabContents::RendererResponsive(RenderViewHost* render_view_host) { + HungRendererWarning::HideForWebContents(AsWC(this)); +} + +void TabContents::LoadStateChanged(const GURL& url, + net::LoadState load_state) { + load_state_ = load_state; + load_state_host_ = UTF8ToWide(url.host()); + if (load_state_ == net::LOAD_STATE_READING_RESPONSE) + SetNotWaitingForResponse(); + if (is_loading()) + NotifyNavigationStateChanged(INVALIDATE_LOAD | INVALIDATE_FAVICON); +} + +void TabContents::OnDidGetApplicationInfo( + int32 page_id, + const webkit_glue::WebApplicationInfo& info) { + if (pending_install_.page_id != page_id) + return; // The user clicked create on a separate page. Ignore this. + + pending_install_.callback_functor = + new GearsCreateShortcutCallbackFunctor(this); + GearsCreateShortcut( + info, pending_install_.title, pending_install_.url, pending_install_.icon, + NewCallback(pending_install_.callback_functor, + &GearsCreateShortcutCallbackFunctor::Run)); +} + +void TabContents::OnEnterOrSpace() { + // See comment in RenderViewHostDelegate::OnEnterOrSpace as to why we do this. +#if defined(OS_WIN) + // TODO(port): this is stubbed in BrowserProcess + DownloadRequestManager* drm = g_browser_process->download_request_manager(); + if (drm) + drm->OnUserGesture(this); +#endif +} + +void TabContents::OnFindReply(int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update) { + // Ignore responses for requests other than the one we have most recently + // issued. That way we won't act on stale results when the user has + // already typed in another query. + if (request_id != current_find_request_id_) + return; + + if (number_of_matches == -1) + number_of_matches = find_result_.number_of_matches(); + if (active_match_ordinal == -1) + active_match_ordinal = find_result_.active_match_ordinal(); + + // Notify the UI, automation and any other observers that a find result was + // found. + find_result_ = FindNotificationDetails(request_id, number_of_matches, + selection_rect, active_match_ordinal, + final_update); + NotificationService::current()->Notify( + NotificationType::FIND_RESULT_AVAILABLE, + Source<TabContents>(this), + Details<FindNotificationDetails>(&find_result_)); +} + +bool TabContents::CanTerminate() const { + if (!delegate()) + return true; + + return !delegate()->IsExternalTabContainer(); +} + +void TabContents::FileSelected(const FilePath& path, + int index, void* params) { + render_view_host()->FileSelected(path); +} + +void TabContents::MultiFilesSelected(const std::vector<FilePath>& files, + void* params) { + render_view_host()->MultiFilesSelected(files); +} + +void TabContents::FileSelectionCanceled(void* params) { + // If the user cancels choosing a file to upload we pass back an + // empty vector. + render_view_host()->MultiFilesSelected(std::vector<FilePath>()); +} + +void TabContents::BeforeUnloadFiredFromRenderManager( + bool proceed, + bool* proceed_to_fire_unload) { + if (delegate()) + delegate()->BeforeUnloadFired(this, proceed, proceed_to_fire_unload); +} + +void TabContents::UpdateRenderViewSizeForRenderManager() { + // TODO(brettw) this is a hack. See TabContentsView::SizeContents. + view_->SizeContents(view_->GetContainerSize()); +} + +DOMUI* TabContents::CreateDOMUIForRenderManager(const GURL& url) { + return DOMUIFactory::CreateDOMUIForURL(AsWC(this), url); +} + +NavigationEntry* +TabContents::GetLastCommittedNavigationEntryForRenderManager() { + return controller_.GetLastCommittedEntry(); +} + +bool TabContents::CreateRenderViewForRenderManager( + RenderViewHost* render_view_host) { + // When we're running a DOM UI, the RenderViewHost needs to be put in DOM UI + // mode before CreateRenderView is called. When we're asked to create a + // RenderView, that means it's for the pending entry, so we have to use the + // pending DOM UI. + if (render_manager_.pending_dom_ui()) + render_view_host->AllowDOMUIBindings(); + + RenderWidgetHostView* rwh_view = view_->CreateViewForWidget(render_view_host); + if (!render_view_host->CreateRenderView()) + return false; + + // Now that the RenderView has been created, we need to tell it its size. + rwh_view->SetSize(view_->GetContainerSize()); + + UpdateMaxPageIDIfNecessary(render_view_host->site_instance(), + render_view_host); + return true; +} + +void TabContents::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::BOOKMARK_MODEL_LOADED: + // BookmarkModel finished loading, fall through to update starred state. + case NotificationType::URLS_STARRED: { + // Somewhere, a URL has been starred. + // Ignore notifications for profiles other than our current one. + Profile* source_profile = Source<Profile>(source).ptr(); + if (!source_profile || !source_profile->IsSameProfile(profile())) + return; + + UpdateStarredStateForCurrentURL(); + break; + } + case NotificationType::PREF_CHANGED: { + std::wstring* pref_name_in = Details<std::wstring>(details).ptr(); + DCHECK(Source<PrefService>(source).ptr() == profile()->GetPrefs()); + if (*pref_name_in == prefs::kAlternateErrorPagesEnabled) { + UpdateAlternateErrorPageURL(); + } else if (*pref_name_in == prefs::kDefaultCharset || + StartsWithASCII(WideToUTF8(*pref_name_in), "webkit.webprefs.", true) + ) { + UpdateWebPreferences(); + } else { + NOTREACHED() << "unexpected pref change notification" << *pref_name_in; + } + break; + } + case NotificationType::RENDER_WIDGET_HOST_DESTROYED: + view_->RenderWidgetHostDestroyed(Source<RenderWidgetHost>(source).ptr()); + break; + + case NotificationType::NAV_ENTRY_COMMITTED: { + DCHECK(&controller_ == Source<NavigationController>(source).ptr()); + + NavigationController::LoadCommittedDetails& committed_details = + *(Details<NavigationController::LoadCommittedDetails>(details).ptr()); + ExpireInfoBars(committed_details); + break; + } + + default: + NOTREACHED(); + } +} + diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h index 71559cb..d45d1dc 100644 --- a/chrome/browser/tab_contents/tab_contents.h +++ b/chrome/browser/tab_contents/tab_contents.h @@ -102,6 +102,10 @@ class TabContents : public PageNavigator, INVALIDATE_EVERYTHING = 0xFFFFFFFF }; + TabContents(Profile* profile, + SiteInstance* site_instance, + int routing_id, + base::WaitableEvent* modal_dialog_event); virtual ~TabContents(); static void RegisterUserPrefs(PrefService* prefs); @@ -115,7 +119,8 @@ class TabContents : public PageNavigator, PropertyBag* property_bag() { return &property_bag_; } // Returns this object as a WebContents if it is one, and NULL otherwise. - virtual WebContents* AsWebContents() = 0; + // TODO(brettw) this should not be necessary. + WebContents* AsWebContents(); // Const version of above for situations where const TabContents*'s are used. WebContents* AsWebContents() const { @@ -317,6 +322,9 @@ class TabContents : public PageNavigator, // heap-allocated pointer is owned by the caller. virtual TabContents* Clone(); + // Tell Gears to create a shortcut for the current page. + void CreateShortcut(); + // Window management --------------------------------------------------------- #if defined(OS_WIN) @@ -500,12 +508,88 @@ class TabContents : public PageNavigator, // Accessor for find_result_. const FindNotificationDetails& find_result() const { return find_result_; } - protected: + // Misc state & callbacks ---------------------------------------------------- + + // Set whether the contents should block javascript message boxes or not. + // Default is not to block any message boxes. + void set_suppress_javascript_messages(bool suppress_javascript_messages) { + suppress_javascript_messages_ = suppress_javascript_messages; + } + + // AppModalDialog calls this when the dialog is closed. + void OnJavaScriptMessageBoxClosed(IPC::Message* reply_msg, + bool success, + const std::wstring& prompt); + + // Prepare for saving the current web page to disk. + void OnSavePage(); + + // Save page with the main HTML file path, the directory for saving resources, + // and the save type: HTML only or complete web page. + void SavePage(const std::wstring& main_file, + const std::wstring& dir_path, + SavePackage::SavePackageType save_type); + + // Displays asynchronously a print preview (generated by the renderer) if not + // already displayed and ask the user for its preferred print settings with + // the "Print..." dialog box. (managed by the print worker thread). + // TODO(maruel): Creates a snapshot of the renderer to be used for the new + // tab for the printing facility. + void PrintPreview(); + + // Prints the current document immediately. Since the rendering is + // asynchronous, the actual printing will not be completed on the return of + // this function. Returns false if printing is impossible at the moment. + bool PrintNow(); + + // Returns true if the active NavigationEntry's page_id equals page_id. + bool IsActiveEntry(int32 page_id); + + const std::string& contents_mime_type() const { + return contents_mime_type_; + } + + // Returns true if this WebContents will notify about disconnection. + bool notify_disconnection() const { return notify_disconnection_; } + + // Override the encoding and reload the page by sending down + // ViewMsg_SetPageEncoding to the renderer. |UpdateEncoding| is kinda + // the opposite of this, by which 'browser' is notified of + // the encoding of the current tab from 'renderer' (determined by + // auto-detect, http header, meta, bom detection, etc). + void override_encoding(const std::wstring& encoding) { + set_encoding(encoding); + render_view_host()->SetPageEncoding(encoding); + } + + void CrossSiteNavigationCanceled() { + render_manager_.CrossSiteNavigationCanceled(); + } + + void WindowMoveOrResizeStarted() { + render_view_host()->WindowMoveOrResizeStarted(); + } + + private: friend class NavigationController; // Used to access the child_windows_ (ConstrainedWindowList) for testing // automation purposes. friend class AutomationProvider; + FRIEND_TEST(WebContentsTest, UpdateTitle); + + // Temporary until the view/contents separation is complete. + friend class TabContentsView; +#if defined(OS_WIN) + friend class TabContentsViewWin; +#elif defined(OS_MACOSX) + friend class TabContentsViewMac; +#elif defined(OS_LINUX) + friend class TabContentsViewGtk; +#endif + + // So InterstitialPage can access SetIsLoading. + friend class InterstitialPage; // When CreateShortcut is invoked RenderViewHost::GetApplicationInfo is // invoked. CreateShortcut caches the state of the page needed to create the // shortcut in PendingInstall. When OnDidGetApplicationInfo is invoked, it @@ -522,28 +606,8 @@ class TabContents : public PageNavigator, GearsCreateShortcutCallbackFunctor* callback_functor; }; - // TODO(brettw) move thos to tab_contents.cc once WebContents and - // TabContents are merged. - class GearsCreateShortcutCallbackFunctor { - public: - explicit GearsCreateShortcutCallbackFunctor(TabContents* contents) - : contents_(contents) {} - - void Run(const GearsShortcutData2& shortcut_data, bool success) { - if (contents_) - contents_->OnGearsCreateShortcutDone(shortcut_data, success); - delete this; - } - void Cancel() { - contents_ = NULL; - } - - private: - TabContents* contents_; - }; - - - TabContents(Profile* profile); + // TODO(brettw) TestWebContents shouldn't exist! + friend class TestWebContents; RenderWidgetHostView* render_widget_host_view() const { return render_manager_.current_view(); @@ -592,6 +656,234 @@ class TabContents : public PageNavigator, // the pending DOMUI, the committed DOMUI, or NULL. DOMUI* GetDOMUIForCurrentState(); + // Navigation helpers -------------------------------------------------------- + // + // These functions are helpers for Navigate() and DidNavigate(). + + // Handles post-navigation tasks in DidNavigate AFTER the entry has been + // committed to the navigation controller. Note that the navigation entry is + // not provided since it may be invalid/changed after being committed. The + // current navigation entry is in the NavigationController at this point. + void DidNavigateMainFramePostCommit( + const NavigationController::LoadCommittedDetails& details, + const ViewHostMsg_FrameNavigate_Params& params); + void DidNavigateAnyFramePostCommit( + RenderViewHost* render_view_host, + const NavigationController::LoadCommittedDetails& details, + const ViewHostMsg_FrameNavigate_Params& params); + + // Closes all child windows (constrained popups) when the domain changes. + // Supply the new and old URLs, and this function will figure out when the + // domain changing conditions are met. + void MaybeCloseChildWindows(const GURL& previous_url, + const GURL& current_url); + + // Updates the starred state from the bookmark bar model. If the state has + // changed, the delegate is notified. + void UpdateStarredStateForCurrentURL(); + + // Send the alternate error page URL to the renderer. This method is virtual + // so special html pages can override this (e.g., the new tab page). + virtual void UpdateAlternateErrorPageURL(); + + // Send webkit specific settings to the renderer. + void UpdateWebPreferences(); + + // If our controller was restored and the page id is > than the site + // instance's page id, the site instances page id is updated as well as the + // renderers max page id. + void UpdateMaxPageIDIfNecessary(SiteInstance* site_instance, + RenderViewHost* rvh); + + // Called by OnMsgNavigate to update history state. Overridden by subclasses + // that don't want to be added to history. + virtual void UpdateHistoryForNavigation(const GURL& display_url, + const ViewHostMsg_FrameNavigate_Params& params); + + // Saves the given title to the navigation entry and does associated work. It + // will update history and the view for the new title, and also synthesize + // titles for file URLs that have none (so we require that the URL of the + // entry already be set). + // + // This is used as the backend for state updates, which include a new title, + // or the dedicated set title message. It returns true if the new title is + // different and was therefore updated. + bool UpdateTitleForEntry(NavigationEntry* entry, const std::wstring& title); + + // Misc non-view stuff ------------------------------------------------------- + + // Helper functions for sending notifications. + void NotifySwapped(); + void NotifyConnected(); + void NotifyDisconnected(); + + // If params has a searchable form, this tries to create a new keyword. + void GenerateKeywordIfNecessary( + const ViewHostMsg_FrameNavigate_Params& params); + + // RenderViewHostDelegate ---------------------------------------------------- + + virtual RenderViewHostDelegate::View* GetViewDelegate() const; + virtual RenderViewHostDelegate::Save* GetSaveDelegate() const; + virtual Profile* GetProfile() const; + virtual WebContents* GetAsWebContents(); + virtual void RenderViewCreated(RenderViewHost* render_view_host); + virtual void RenderViewReady(RenderViewHost* render_view_host); + virtual void RenderViewGone(RenderViewHost* render_view_host); + virtual void DidNavigate(RenderViewHost* render_view_host, + const ViewHostMsg_FrameNavigate_Params& params); + virtual void UpdateState(RenderViewHost* render_view_host, + int32 page_id, + const std::string& state); + virtual void UpdateTitle(RenderViewHost* render_view_host, + int32 page_id, + const std::wstring& title); + virtual void UpdateFeedList(RenderViewHost* render_view_host, + const ViewHostMsg_UpdateFeedList_Params& params); + virtual void UpdateEncoding(RenderViewHost* render_view_host, + const std::wstring& encoding); + virtual void UpdateTargetURL(int32 page_id, const GURL& url); + virtual void UpdateThumbnail(const GURL& url, + const SkBitmap& bitmap, + const ThumbnailScore& score); + virtual void Close(RenderViewHost* render_view_host); + virtual void RequestMove(const gfx::Rect& new_bounds); + virtual void DidStartLoading(RenderViewHost* render_view_host, int32 page_id); + virtual void DidStopLoading(RenderViewHost* render_view_host, int32 page_id); + virtual void DidStartProvisionalLoadForFrame(RenderViewHost* render_view_host, + bool is_main_frame, + const GURL& url); + virtual void DidRedirectProvisionalLoad(int32 page_id, + const GURL& source_url, + const GURL& target_url); + virtual void DidLoadResourceFromMemoryCache( + const GURL& url, + const std::string& frame_origin, + const std::string& main_frame_origin, + const std::string& security_info); + virtual void DidFailProvisionalLoadWithError( + RenderViewHost* render_view_host, + bool is_main_frame, + int error_code, + const GURL& url, + bool showing_repost_interstitial); + virtual void UpdateFavIconURL(RenderViewHost* render_view_host, + int32 page_id, const GURL& icon_url); + virtual void DidDownloadImage(RenderViewHost* render_view_host, + int id, + const GURL& image_url, + bool errored, + const SkBitmap& image); + virtual void RequestOpenURL(const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition); + virtual void DomOperationResponse(const std::string& json_string, + int automation_id); + virtual void ProcessDOMUIMessage(const std::string& message, + const std::string& content); + virtual void ProcessExternalHostMessage(const std::string& message, + const std::string& origin, + const std::string& target); + virtual void GoToEntryAtOffset(int offset); + virtual void GetHistoryListCount(int* back_list_count, + int* forward_list_count); + virtual void RunFileChooser(bool multiple_files, + const string16& title, + const FilePath& default_file); + virtual void RunJavaScriptMessage(const std::wstring& message, + const std::wstring& default_prompt, + const GURL& frame_url, + const int flags, + IPC::Message* reply_msg, + bool* did_suppress_message); + virtual void RunBeforeUnloadConfirm(const std::wstring& message, + IPC::Message* reply_msg); + virtual void ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + IPC::Message* reply_msg); + virtual void PasswordFormsSeen(const std::vector<PasswordForm>& forms); + virtual void AutofillFormSubmitted(const AutofillForm& form); + virtual void GetAutofillSuggestions(const std::wstring& field_name, + const std::wstring& user_text, int64 node_id, int request_id); + virtual void RemoveAutofillEntry(const std::wstring& field_name, + const std::wstring& value); + virtual void PageHasOSDD(RenderViewHost* render_view_host, + int32 page_id, const GURL& url, bool autodetected); + virtual void InspectElementReply(int num_resources); + virtual void DidGetPrintedPagesCount(int cookie, int number_pages); + virtual void DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params); + virtual GURL GetAlternateErrorPageURL() const; + virtual WebPreferences GetWebkitPrefs(); + virtual void OnMissingPluginStatus(int status); + virtual void OnCrashedPlugin(const FilePath& plugin_path); + virtual void OnCrashedWorker(); + virtual void OnJSOutOfMemory(); + virtual void ShouldClosePage(bool proceed); + void OnCrossSiteResponse(int new_render_process_host_id, + int new_request_id); + virtual bool CanBlur() const; + virtual gfx::Rect GetRootWindowResizerRect() const; + virtual void RendererUnresponsive(RenderViewHost* render_view_host, + bool is_during_unload); + virtual void RendererResponsive(RenderViewHost* render_view_host); + virtual void LoadStateChanged(const GURL& url, net::LoadState load_state); + virtual void OnDidGetApplicationInfo( + int32 page_id, + const webkit_glue::WebApplicationInfo& info); + virtual void OnEnterOrSpace(); + virtual void OnFindReply(int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update); + virtual bool CanTerminate() const; + + // SelectFileDialog::Listener ------------------------------------------------ + + virtual void FileSelected(const FilePath& path, int index, void* params); + virtual void MultiFilesSelected(const std::vector<FilePath>& files, + void* params); + virtual void FileSelectionCanceled(void* params); + + // RenderViewHostManager::Delegate ------------------------------------------- + + virtual void BeforeUnloadFiredFromRenderManager( + bool proceed, + bool* proceed_to_fire_unload); + virtual void DidStartLoadingFromRenderManager( + RenderViewHost* render_view_host, int32 page_id) { + DidStartLoading(render_view_host, page_id); + } + virtual void RenderViewGoneFromRenderManager( + RenderViewHost* render_view_host) { + RenderViewGone(render_view_host); + } + virtual void UpdateRenderViewSizeForRenderManager(); + virtual void NotifySwappedFromRenderManager() { + NotifySwapped(); + } + virtual NavigationController& GetControllerForRenderManager() { + return controller(); + } + virtual DOMUI* CreateDOMUIForRenderManager(const GURL& url); + virtual NavigationEntry* GetLastCommittedNavigationEntryForRenderManager(); + + // Initializes the given renderer if necessary and creates the view ID + // corresponding to this view host. If this method is not called and the + // process is not shared, then the WebContents will act as though the renderer + // is not running (i.e., it will render "sad tab"). This method is + // automatically called from LoadURL. + // + // If you are attaching to an already-existing RenderView, you should call + // InitWithExistingID. + virtual bool CreateRenderViewForRenderManager( + RenderViewHost* render_view_host); + + // NotificationObserver ------------------------------------------------------ + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + // Data for core operation --------------------------------------------------- // Delegate for notifying our owner about stuff. Not owned by us. diff --git a/chrome/browser/tab_contents/web_contents.cc b/chrome/browser/tab_contents/web_contents.cc index da8669b..255d605 100644 --- a/chrome/browser/tab_contents/web_contents.cc +++ b/chrome/browser/tab_contents/web_contents.cc @@ -7,23 +7,15 @@ #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/file_version_info.h" -#include "base/process_util.h" #include "base/string_util.h" #include "chrome/browser/autofill_manager.h" -#include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/character_encoding.h" #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/dom_ui/dom_ui.h" #include "chrome/browser/dom_ui/dom_ui_factory.h" -#include "chrome/browser/download/download_manager.h" -#include "chrome/browser/download/download_request_manager.h" -#include "chrome/browser/gears_integration.h" -#include "chrome/browser/google_util.h" -#include "chrome/browser/jsmessage_box_handler.h" #include "chrome/browser/load_from_memory_cache_details.h" -#include "chrome/browser/load_notification_details.h" #include "chrome/browser/password_manager/password_manager.h" #include "chrome/browser/plugin_installer.h" #include "chrome/browser/profile.h" @@ -31,8 +23,6 @@ #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/renderer_host/web_cache_manager.h" -#include "chrome/browser/search_engines/template_url_fetcher.h" -#include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/browser/tab_contents/provisional_load_details.h" #include "chrome/browser/tab_contents/tab_contents_view.h" @@ -41,18 +31,12 @@ #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" -#include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" -#include "grit/locale_settings.h" -#include "net/base/mime_util.h" -#include "net/base/net_errors.h" -#include "net/base/registry_controlled_domain.h" #include "webkit/glue/feed.h" #include "webkit/glue/webkit_glue.h" #if defined(OS_WIN) // TODO(port): fill these in as we flesh out the implementation of this class -#include "chrome/browser/modal_html_dialog_delegate.h" #include "chrome/browser/plugin_service.h" #include "chrome/browser/printing/print_job.h" #include "chrome/browser/views/hung_renderer_view.h" // TODO(brettw) delete me. @@ -60,1498 +44,3 @@ #endif #include "grit/generated_resources.h" - -// Cross-Site Navigations -// -// If a WebContents is told to navigate to a different web site (as determined -// by SiteInstance), it will replace its current RenderViewHost with a new -// RenderViewHost dedicated to the new SiteInstance. This works as follows: -// -// - Navigate determines whether the destination is cross-site, and if so, -// it creates a pending_render_view_host_ and moves into the PENDING -// RendererState. -// - The pending RVH is "suspended," so that no navigation messages are sent to -// its renderer until the onbeforeunload JavaScript handler has a chance to -// run in the current RVH. -// - The pending RVH tells CrossSiteRequestManager (a thread-safe singleton) -// that it has a pending cross-site request. ResourceDispatcherHost will -// check for this when the response arrives. -// - The current RVH runs its onbeforeunload handler. If it returns false, we -// cancel all the pending logic and go back to NORMAL. Otherwise we allow -// the pending RVH to send the navigation request to its renderer. -// - ResourceDispatcherHost receives a ResourceRequest on the IO thread. It -// checks CrossSiteRequestManager to see that the RVH responsible has a -// pending cross-site request, and then installs a CrossSiteEventHandler. -// - When RDH receives a response, the BufferedEventHandler determines whether -// it is a download. If so, it sends a message to the new renderer causing -// it to cancel the request, and the download proceeds in the download -// thread. For now, we stay in a PENDING state (with a pending RVH) until -// the next DidNavigate event for this WebContents. This isn't ideal, but it -// doesn't affect any functionality. -// - After RDH receives a response and determines that it is safe and not a -// download, it pauses the response to first run the old page's onunload -// handler. It does this by asynchronously calling the OnCrossSiteResponse -// method of WebContents on the UI thread, which sends a ClosePage message -// to the current RVH. -// - Once the onunload handler is finished, a ClosePage_ACK message is sent to -// the ResourceDispatcherHost, who unpauses the response. Data is then sent -// to the pending RVH. -// - The pending renderer sends a FrameNavigate message that invokes the -// DidNavigate method. This replaces the current RVH with the -// pending RVH and goes back to the NORMAL RendererState. - -using base::TimeDelta; -using base::TimeTicks; - -namespace { - -// Amount of time we wait between when a key event is received and the renderer -// is queried for its state and pushed to the NavigationEntry. -const int kQueryStateDelay = 5000; - -const int kSyncWaitDelay = 40; - -// If another javascript message box is displayed within -// kJavascriptMessageExpectedDelay of a previous javascript message box being -// dismissed, display an option to suppress future message boxes from this -// contents. -const int kJavascriptMessageExpectedDelay = 1000; - -// Minimum amount of time in ms that has to elapse since the download shelf was -// shown for us to hide it when navigating away from the current page. -const int kDownloadShelfHideDelay = 5000; - -const char kLinkDoctorBaseURL[] = - "http://linkhelp.clients.google.com/tbproxy/lh/fixurl"; - -// The printer icon in shell32.dll. That's a standard icon user will quickly -// recognize. -const int kShell32PrinterIcon = 17; - -// The list of prefs we want to observe. -const wchar_t* kPrefsToObserve[] = { - prefs::kAlternateErrorPagesEnabled, - prefs::kWebKitJavaEnabled, - prefs::kWebKitJavascriptEnabled, - prefs::kWebKitLoadsImagesAutomatically, - prefs::kWebKitPluginsEnabled, - prefs::kWebKitUsesUniversalDetector, - prefs::kWebKitSerifFontFamily, - prefs::kWebKitSansSerifFontFamily, - prefs::kWebKitFixedFontFamily, - prefs::kWebKitDefaultFontSize, - prefs::kWebKitDefaultFixedFontSize, - prefs::kDefaultCharset - // kWebKitStandardFontIsSerif needs to be added - // if we let users pick which font to use, serif or sans-serif when - // no font is specified or a CSS generic family (serif or sans-serif) - // is not specified. -}; - -const int kPrefsToObserveLength = arraysize(kPrefsToObserve); - -// Limit on the number of suggestions to appear in the pop-up menu under an -// text input element in a form. -const int kMaxAutofillMenuItems = 6; - -// Returns true if the entry's transition type is FORM_SUBMIT. -bool IsFormSubmit(const NavigationEntry* entry) { - return (PageTransition::StripQualifier(entry->transition_type()) == - PageTransition::FORM_SUBMIT); -} - -#if defined(OS_WIN) - -BOOL CALLBACK InvalidateWindow(HWND hwnd, LPARAM lparam) { - // Note: erase is required to properly paint some widgets borders. This can - // be seen with textfields. - InvalidateRect(hwnd, NULL, TRUE); - return TRUE; -} -#endif - -} // namespace - -WebContents::WebContents(Profile* profile, - SiteInstance* site_instance, - int routing_id, - base::WaitableEvent* modal_dialog_event) - : TabContents(profile) { - pending_install_.page_id = 0; - pending_install_.callback_functor = NULL; - - render_manager_.Init(profile, site_instance, routing_id, modal_dialog_event); - - view_->CreateView(); - - // Register for notifications about all interested prefs change. - PrefService* prefs = profile->GetPrefs(); - if (prefs) { - for (int i = 0; i < kPrefsToObserveLength; ++i) - prefs->AddPrefObserver(kPrefsToObserve[i], this); - } - - // Register for notifications about URL starredness changing on any profile. - registrar_.Add(this, NotificationType::URLS_STARRED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED, - NotificationService::AllSources()); - - // Keep a global copy of the previous search string (if any). - static string16 global_last_search = string16(); - find_prepopulate_text_ = &global_last_search; -} - -WebContents::~WebContents() { - is_being_destroyed_ = true; - - // We don't want any notifications while we're runnign our destructor. - registrar_.RemoveAll(); - - // Unregister the notifications of all observed prefs change. - PrefService* prefs = profile()->GetPrefs(); - if (prefs) { - for (int i = 0; i < kPrefsToObserveLength; ++i) - prefs->RemovePrefObserver(kPrefsToObserve[i], this); - } - - // Clean up subwindows like plugins and the find in page bar. - view_->OnContentsDestroy(); - - NotifyDisconnected(); - HungRendererWarning::HideForWebContents(this); - - if (pending_install_.callback_functor) - pending_install_.callback_functor->Cancel(); - - // First cleanly close all child windows. - // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked - // some of these to close. CloseWindows is async, so it might get called - // twice before it runs. - int size = static_cast<int>(child_windows_.size()); - for (int i = size - 1; i >= 0; --i) { - ConstrainedWindow* window = child_windows_[i]; - if (window) - window->CloseConstrainedWindow(); - } - - // Notify any lasting InfobarDelegates that have not yet been removed that - // whatever infobar they were handling in this TabContents has closed, - // because the TabContents is going away entirely. - for (int i = 0; i < infobar_delegate_count(); ++i) { - InfoBarDelegate* delegate = GetInfoBarDelegateAt(i); - delegate->InfoBarClosed(); - } - infobar_delegates_.clear(); - - // Notify any observer that have a reference on this tab contents. - NotificationService::current()->Notify( - NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(this), - NotificationService::NoDetails()); - - // TODO(brettw) this should be moved to the view. -#if defined(OS_WIN) - // If we still have a window handle, destroy it. GetNativeView can return - // NULL if this contents was part of a window that closed. - if (GetNativeView()) - ::DestroyWindow(GetNativeView()); -#endif -} - -// static -void WebContents::RegisterUserPrefs(PrefService* prefs) { - prefs->RegisterBooleanPref(prefs::kAlternateErrorPagesEnabled, true); - - WebPreferences pref_defaults; - prefs->RegisterBooleanPref(prefs::kWebKitJavascriptEnabled, - pref_defaults.javascript_enabled); - prefs->RegisterBooleanPref(prefs::kWebKitWebSecurityEnabled, - pref_defaults.web_security_enabled); - prefs->RegisterBooleanPref( - prefs::kWebKitJavascriptCanOpenWindowsAutomatically, true); - prefs->RegisterBooleanPref(prefs::kWebKitLoadsImagesAutomatically, - pref_defaults.loads_images_automatically); - prefs->RegisterBooleanPref(prefs::kWebKitPluginsEnabled, - pref_defaults.plugins_enabled); - prefs->RegisterBooleanPref(prefs::kWebKitDomPasteEnabled, - pref_defaults.dom_paste_enabled); - prefs->RegisterBooleanPref(prefs::kWebKitShrinksStandaloneImagesToFit, - pref_defaults.shrinks_standalone_images_to_fit); - prefs->RegisterBooleanPref(prefs::kWebKitDeveloperExtrasEnabled, - true); - prefs->RegisterBooleanPref(prefs::kWebKitTextAreasAreResizable, - pref_defaults.text_areas_are_resizable); - prefs->RegisterBooleanPref(prefs::kWebKitJavaEnabled, - pref_defaults.java_enabled); - - prefs->RegisterLocalizedStringPref(prefs::kAcceptLanguages, - IDS_ACCEPT_LANGUAGES); - prefs->RegisterLocalizedStringPref(prefs::kDefaultCharset, - IDS_DEFAULT_ENCODING); - prefs->RegisterLocalizedBooleanPref(prefs::kWebKitStandardFontIsSerif, - IDS_STANDARD_FONT_IS_SERIF); - prefs->RegisterLocalizedStringPref(prefs::kWebKitFixedFontFamily, - IDS_FIXED_FONT_FAMILY); - prefs->RegisterLocalizedStringPref(prefs::kWebKitSerifFontFamily, - IDS_SERIF_FONT_FAMILY); - prefs->RegisterLocalizedStringPref(prefs::kWebKitSansSerifFontFamily, - IDS_SANS_SERIF_FONT_FAMILY); - prefs->RegisterLocalizedStringPref(prefs::kWebKitCursiveFontFamily, - IDS_CURSIVE_FONT_FAMILY); - prefs->RegisterLocalizedStringPref(prefs::kWebKitFantasyFontFamily, - IDS_FANTASY_FONT_FAMILY); - prefs->RegisterLocalizedIntegerPref(prefs::kWebKitDefaultFontSize, - IDS_DEFAULT_FONT_SIZE); - prefs->RegisterLocalizedIntegerPref(prefs::kWebKitDefaultFixedFontSize, - IDS_DEFAULT_FIXED_FONT_SIZE); - prefs->RegisterLocalizedIntegerPref(prefs::kWebKitMinimumFontSize, - IDS_MINIMUM_FONT_SIZE); - prefs->RegisterLocalizedIntegerPref(prefs::kWebKitMinimumLogicalFontSize, - IDS_MINIMUM_LOGICAL_FONT_SIZE); - prefs->RegisterLocalizedBooleanPref(prefs::kWebKitUsesUniversalDetector, - IDS_USES_UNIVERSAL_DETECTOR); - prefs->RegisterLocalizedStringPref(prefs::kStaticEncodings, - IDS_STATIC_ENCODING_LIST); -} - -void WebContents::CreateShortcut() { - NavigationEntry* entry = controller_.GetLastCommittedEntry(); - if (!entry) - return; - - // We only allow one pending install request. By resetting the page id we - // effectively cancel the pending install request. - pending_install_.page_id = entry->page_id(); - pending_install_.icon = GetFavIcon(); - pending_install_.title = UTF16ToWideHack(GetTitle()); - pending_install_.url = GetURL(); - if (pending_install_.callback_functor) { - pending_install_.callback_functor->Cancel(); - pending_install_.callback_functor = NULL; - } - DCHECK(!pending_install_.icon.isNull()) << "Menu item should be disabled."; - if (pending_install_.title.empty()) - pending_install_.title = UTF8ToWide(GetURL().spec()); - - // Request the application info. When done OnDidGetApplicationInfo is invoked - // and we'll create the shortcut. - render_view_host()->GetApplicationInfo(pending_install_.page_id); -} - -void WebContents::OnJavaScriptMessageBoxClosed(IPC::Message* reply_msg, - bool success, - const std::wstring& prompt) { - last_javascript_message_dismissal_ = TimeTicks::Now(); - render_manager_.OnJavaScriptMessageBoxClosed(reply_msg, success, prompt); -} - -void WebContents::OnSavePage() { - // If we can not save the page, try to download it. - if (!SavePackage::IsSavableContents(contents_mime_type())) { - DownloadManager* dlm = profile()->GetDownloadManager(); - const GURL& current_page_url = GetURL(); - if (dlm && current_page_url.is_valid()) - dlm->DownloadUrl(current_page_url, GURL(), this); - return; - } - - Stop(); - - // Create the save package and possibly prompt the user for the name to save - // the page as. The user prompt is an asynchronous operation that runs on - // another thread. - save_package_ = new SavePackage(this); - save_package_->GetSaveInfo(); -} - -// Used in automated testing to bypass prompting the user for file names. -// Instead, the names and paths are hard coded rather than running them through -// file name sanitation and extension / mime checking. -void WebContents::SavePage(const std::wstring& main_file, - const std::wstring& dir_path, - SavePackage::SavePackageType save_type) { - // Stop the page from navigating. - Stop(); - - save_package_ = new SavePackage(this, save_type, - FilePath::FromWStringHack(main_file), - FilePath::FromWStringHack(dir_path)); - save_package_->Init(); -} - -void WebContents::PrintPreview() { - // We don't show the print preview yet, only the print dialog. - PrintNow(); -} - -bool WebContents::PrintNow() { - // We can't print interstitial page for now. - if (showing_interstitial_page()) - return false; - - return render_view_host()->PrintPages(); -} - -bool WebContents::IsActiveEntry(int32 page_id) { - NavigationEntry* active_entry = controller_.GetActiveEntry(); - return (active_entry != NULL && - active_entry->site_instance() == GetSiteInstance() && - active_entry->page_id() == page_id); -} - -RenderViewHostDelegate::View* WebContents::GetViewDelegate() const { - return view_.get(); -} - -RenderViewHostDelegate::Save* WebContents::GetSaveDelegate() const { - return save_package_.get(); // May be NULL, but we can return NULL. -} - -Profile* WebContents::GetProfile() const { - return profile(); -} - -void WebContents::RenderViewCreated(RenderViewHost* render_view_host) { - NavigationEntry* entry = controller_.GetActiveEntry(); - if (!entry) - return; - - // When we're creating views, we're still doing initial setup, so we always - // use the pending DOM UI rather than any possibly existing committed one. - if (render_manager_.pending_dom_ui()) { - render_manager_.pending_dom_ui()->RenderViewCreated(render_view_host); - } - - if (entry->IsViewSourceMode()) { - // Put the renderer in view source mode. - render_view_host->Send( - new ViewMsg_EnableViewSourceMode(render_view_host->routing_id())); - } -} - -void WebContents::RenderViewReady(RenderViewHost* rvh) { - if (rvh != render_view_host()) { - // Don't notify the world, since this came from a renderer in the - // background. - return; - } - - NotifyConnected(); - SetIsCrashed(false); -} - -void WebContents::RenderViewGone(RenderViewHost* rvh) { - // Ask the print preview if this renderer was valuable. - if (!printing_.OnRenderViewGone(rvh)) - return; - if (rvh != render_view_host()) { - // The pending page's RenderViewHost is gone. - return; - } - - SetIsLoading(false, NULL); - NotifyDisconnected(); - SetIsCrashed(true); - - // Force an invalidation to render sad tab. The view will notice we crashed - // when it paints. - view_->Invalidate(); - - // Hide any visible hung renderer warning for this web contents' process. - HungRendererWarning::HideForWebContents(this); -} - -void WebContents::DidNavigate(RenderViewHost* rvh, - const ViewHostMsg_FrameNavigate_Params& params) { - if (PageTransition::IsMainFrame(params.transition)) - render_manager_.DidNavigateMainFrame(rvh); - - // Update the site of the SiteInstance if it doesn't have one yet. - if (!GetSiteInstance()->has_site()) - GetSiteInstance()->SetSite(params.url); - - // Need to update MIME type here because it's referred to in - // UpdateNavigationCommands() called by RendererDidNavigate() to - // determine whether or not to enable the encoding menu. - // It's updated only for the main frame. For a subframe, - // RenderView::UpdateURL does not set params.contents_mime_type. - // (see http://code.google.com/p/chromium/issues/detail?id=2929 ) - // TODO(jungshik): Add a test for the encoding menu to avoid - // regressing it again. - if (PageTransition::IsMainFrame(params.transition)) - contents_mime_type_ = params.contents_mime_type; - - NavigationController::LoadCommittedDetails details; - if (!controller_.RendererDidNavigate(params, &details)) - return; // No navigation happened. - - // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen - // for the appropriate notification (best) or you can add it to - // DidNavigateMainFramePostCommit / DidNavigateAnyFramePostCommit (only if - // necessary, please). - - // Run post-commit tasks. - if (details.is_main_frame) - DidNavigateMainFramePostCommit(details, params); - DidNavigateAnyFramePostCommit(rvh, details, params); -} - -void WebContents::UpdateState(RenderViewHost* rvh, - int32 page_id, - const std::string& state) { - DCHECK(rvh == render_view_host()); - - // We must be prepared to handle state updates for any page, these occur - // when the user is scrolling and entering form data, as well as when we're - // leaving a page, in which case our state may have already been moved to - // the next page. The navigation controller will look up the appropriate - // NavigationEntry and update it when it is notified via the delegate. - - int entry_index = controller_.GetEntryIndexWithPageID( - GetSiteInstance(), page_id); - if (entry_index < 0) - return; - NavigationEntry* entry = controller_.GetEntryAtIndex(entry_index); - - if (state == entry->content_state()) - return; // Nothing to update. - entry->set_content_state(state); - controller_.NotifyEntryChanged(entry, entry_index); -} - -void WebContents::UpdateTitle(RenderViewHost* rvh, - int32 page_id, const std::wstring& title) { - // If we have a title, that's a pretty good indication that we've started - // getting useful data. - SetNotWaitingForResponse(); - - DCHECK(rvh == render_view_host()); - NavigationEntry* entry = controller_.GetEntryWithPageID(GetSiteInstance(), - page_id); - if (!entry || !UpdateTitleForEntry(entry, title)) - return; - - // Broadcast notifications when the UI should be updated. - if (entry == controller_.GetEntryAtOffset(0)) - NotifyNavigationStateChanged(INVALIDATE_TITLE); -} - -void WebContents::UpdateFeedList( - RenderViewHost* rvh, const ViewHostMsg_UpdateFeedList_Params& params) { - // We might have an old RenderViewHost sending messages, and we should ignore - // those messages. - if (rvh != render_view_host()) - return; - - NavigationEntry* entry = controller_.GetEntryWithPageID(GetSiteInstance(), - params.page_id); - if (!entry) - return; - - entry->set_feedlist(params.feedlist); - - // Broadcast notifications when the UI should be updated. - if (entry == controller_.GetEntryAtOffset(0)) - NotifyNavigationStateChanged(INVALIDATE_FEEDLIST); -} - -void WebContents::UpdateEncoding(RenderViewHost* render_view_host, - const std::wstring& encoding) { - set_encoding(encoding); -} - -void WebContents::UpdateTargetURL(int32 page_id, const GURL& url) { - if (delegate()) - delegate()->UpdateTargetURL(this, url); -} - -void WebContents::UpdateThumbnail(const GURL& url, - const SkBitmap& bitmap, - const ThumbnailScore& score) { - // Tell History about this thumbnail - HistoryService* hs; - if (!profile()->IsOffTheRecord() && - (hs = profile()->GetHistoryService(Profile::IMPLICIT_ACCESS))) { - hs->SetPageThumbnail(url, bitmap, score); - } -} - -void WebContents::Close(RenderViewHost* rvh) { - // Ignore this if it comes from a RenderViewHost that we aren't showing. - if (delegate() && rvh == render_view_host()) - delegate()->CloseContents(this); -} - -void WebContents::RequestMove(const gfx::Rect& new_bounds) { - if (delegate() && delegate()->IsPopup(this)) - delegate()->MoveContents(this, new_bounds); -} - -void WebContents::DidStartLoading(RenderViewHost* rvh, int32 page_id) { - SetIsLoading(true, NULL); -} - -void WebContents::DidStopLoading(RenderViewHost* rvh, int32 page_id) { - scoped_ptr<LoadNotificationDetails> details; - - NavigationEntry* entry = controller_.GetActiveEntry(); - // An entry may not exist for a stop when loading an initial blank page or - // if an iframe injected by script into a blank page finishes loading. - if (entry) { - scoped_ptr<base::ProcessMetrics> metrics( - base::ProcessMetrics::CreateProcessMetrics( - process()->process().handle())); - - TimeDelta elapsed = TimeTicks::Now() - current_load_start_; - - details.reset(new LoadNotificationDetails( - entry->display_url(), - entry->transition_type(), - elapsed, - &controller_, - controller_.GetCurrentEntryIndex())); - } - - // Tell PasswordManager we've finished a page load, which serves as a - // green light to save pending passwords and reset itself. - GetPasswordManager()->DidStopLoading(); - - SetIsLoading(false, details.get()); -} - -void WebContents::DidStartProvisionalLoadForFrame( - RenderViewHost* render_view_host, - bool is_main_frame, - const GURL& url) { - ProvisionalLoadDetails details(is_main_frame, - controller_.IsURLInPageNavigation(url), - url, std::string(), false); - NotificationService::current()->Notify( - NotificationType::FRAME_PROVISIONAL_LOAD_START, - Source<NavigationController>(&controller_), - Details<ProvisionalLoadDetails>(&details)); -} - -void WebContents::DidRedirectProvisionalLoad(int32 page_id, - const GURL& source_url, - const GURL& target_url) { - NavigationEntry* entry; - if (page_id == -1) - entry = controller_.pending_entry(); - else - entry = controller_.GetEntryWithPageID(GetSiteInstance(), page_id); - if (!entry || entry->url() != source_url) - return; - entry->set_url(target_url); -} - -void WebContents::DidLoadResourceFromMemoryCache( - const GURL& url, - const std::string& frame_origin, - const std::string& main_frame_origin, - const std::string& security_info) { - // Send out a notification that we loaded a resource from our memory cache. - int cert_id = 0, cert_status = 0, security_bits = 0; - SSLManager::DeserializeSecurityInfo(security_info, - &cert_id, &cert_status, - &security_bits); - LoadFromMemoryCacheDetails details(url, frame_origin, main_frame_origin, - cert_id, cert_status); - - NotificationService::current()->Notify( - NotificationType::LOAD_FROM_MEMORY_CACHE, - Source<NavigationController>(&controller_), - Details<LoadFromMemoryCacheDetails>(&details)); -} - -void WebContents::DidFailProvisionalLoadWithError( - RenderViewHost* render_view_host, - bool is_main_frame, - int error_code, - const GURL& url, - bool showing_repost_interstitial) { - if (net::ERR_ABORTED == error_code) { - // EVIL HACK ALERT! Ignore failed loads when we're showing interstitials. - // This means that the interstitial won't be torn down properly, which is - // bad. But if we have an interstitial, go back to another tab type, and - // then load the same interstitial again, we could end up getting the first - // interstitial's "failed" message (as a result of the cancel) when we're on - // the second one. - // - // We can't tell this apart, so we think we're tearing down the current page - // which will cause a crash later one. There is also some code in - // RenderViewHostManager::RendererAbortedProvisionalLoad that is commented - // out because of this problem. - // - // http://code.google.com/p/chromium/issues/detail?id=2855 - // Because this will not tear down the interstitial properly, if "back" is - // back to another tab type, the interstitial will still be somewhat alive - // in the previous tab type. If you navigate somewhere that activates the - // tab with the interstitial again, you'll see a flash before the new load - // commits of the interstitial page. - if (showing_interstitial_page()) { - LOG(WARNING) << "Discarding message during interstitial."; - return; - } - - // This will discard our pending entry if we cancelled the load (e.g., if we - // decided to download the file instead of load it). Only discard the - // pending entry if the URLs match, otherwise the user initiated a navigate - // before the page loaded so that the discard would discard the wrong entry. - NavigationEntry* pending_entry = controller_.pending_entry(); - if (pending_entry && pending_entry->url() == url) - controller_.DiscardNonCommittedEntries(); - - render_manager_.RendererAbortedProvisionalLoad(render_view_host); - } - - // Send out a notification that we failed a provisional load with an error. - ProvisionalLoadDetails details(is_main_frame, - controller_.IsURLInPageNavigation(url), - url, std::string(), false); - details.set_error_code(error_code); - - NotificationService::current()->Notify( - NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR, - Source<NavigationController>(&controller_), - Details<ProvisionalLoadDetails>(&details)); -} - -void WebContents::UpdateFavIconURL(RenderViewHost* render_view_host, - int32 page_id, - const GURL& icon_url) { - fav_icon_helper_.SetFavIconURL(icon_url); -} - -void WebContents::DidDownloadImage( - RenderViewHost* render_view_host, - int id, - const GURL& image_url, - bool errored, - const SkBitmap& image) { - // A notification for downloading would be more flexible, but for now I'm - // forwarding to the two places that could possibly have initiated the - // request. If we end up with another place invoking DownloadImage, probably - // best to refactor out into notification service, or something similar. - if (errored) - fav_icon_helper_.FavIconDownloadFailed(id); - else - fav_icon_helper_.SetFavIcon(id, image_url, image); -} - -void WebContents::RequestOpenURL(const GURL& url, const GURL& referrer, - WindowOpenDisposition disposition) { - if (render_manager_.dom_ui()) { - // When we're a DOM UI, it will provide a page transition type for us (this - // is so the new tab page can specify AUTO_BOOKMARK for automatically - // generated suggestions). - // - // Note also that we hide the referrer for DOM UI pages. We don't really - // want web sites to see a referrer of "chrome-ui://blah" (and some - // chrome-ui URLs might have search terms or other stuff we don't want to - // send to the site), so we send no referrer. - OpenURL(url, GURL(), disposition, - render_manager_.dom_ui()->link_transition_type()); - } else { - OpenURL(url, referrer, disposition, PageTransition::LINK); - } -} - -void WebContents::DomOperationResponse(const std::string& json_string, - int automation_id) { - DomOperationNotificationDetails details(json_string, automation_id); - NotificationService::current()->Notify( - NotificationType::DOM_OPERATION_RESPONSE, Source<WebContents>(this), - Details<DomOperationNotificationDetails>(&details)); -} - -void WebContents::ProcessDOMUIMessage(const std::string& message, - const std::string& content) { - if (!render_manager_.dom_ui()) { - // We shouldn't get a DOM UI message when we haven't enabled the DOM UI. - // Because the renderer might be owned and sending random messages, we need - // to ignore these inproper ones. - NOTREACHED(); - return; - } - render_manager_.dom_ui()->ProcessDOMUIMessage(message, content); -} - -void WebContents::ProcessExternalHostMessage(const std::string& message, - const std::string& origin, - const std::string& target) { - if (delegate()) - delegate()->ForwardMessageToExternalHost(message, origin, target); -} - -void WebContents::GoToEntryAtOffset(int offset) { - controller_.GoToOffset(offset); -} - -void WebContents::GetHistoryListCount(int* back_list_count, - int* forward_list_count) { - int current_index = controller_.last_committed_entry_index(); - *back_list_count = current_index; - *forward_list_count = controller_.entry_count() - current_index - 1; -} - -void WebContents::RunFileChooser(bool multiple_files, - const string16& title, - const FilePath& default_file) { - if (!select_file_dialog_.get()) - select_file_dialog_ = SelectFileDialog::Create(this); - SelectFileDialog::Type dialog_type = - multiple_files ? SelectFileDialog::SELECT_OPEN_MULTI_FILE : - SelectFileDialog::SELECT_OPEN_FILE; - select_file_dialog_->SelectFile(dialog_type, title, default_file, - NULL, 0, FILE_PATH_LITERAL(""), - view_->GetTopLevelNativeWindow(), NULL); -} - -void WebContents::RunJavaScriptMessage( - const std::wstring& message, - const std::wstring& default_prompt, - const GURL& frame_url, - const int flags, - IPC::Message* reply_msg, - bool* did_suppress_message) { - // Suppress javascript messages when requested and when inside a constrained - // popup window (because that activates them and breaks them out of the - // constrained window jail). - bool suppress_this_message = suppress_javascript_messages_; - if (delegate()) - suppress_this_message |= - (delegate()->GetConstrainingContents(this) != NULL); - - *did_suppress_message = suppress_this_message; - - if (!suppress_this_message) { - TimeDelta time_since_last_message( - TimeTicks::Now() - last_javascript_message_dismissal_); - bool show_suppress_checkbox = false; - // Show a checkbox offering to suppress further messages if this message is - // being displayed within kJavascriptMessageExpectedDelay of the last one. - if (time_since_last_message < - TimeDelta::FromMilliseconds(kJavascriptMessageExpectedDelay)) - show_suppress_checkbox = true; - - RunJavascriptMessageBox(this, frame_url, flags, message, default_prompt, - show_suppress_checkbox, reply_msg); - } else { - // If we are suppressing messages, just reply as is if the user immediately - // pressed "Cancel". - OnJavaScriptMessageBoxClosed(reply_msg, false, std::wstring()); - } -} - -void WebContents::RunBeforeUnloadConfirm(const std::wstring& message, - IPC::Message* reply_msg) { - RunBeforeUnloadDialog(this, message, reply_msg); -} - -void WebContents::ShowModalHTMLDialog(const GURL& url, int width, int height, - const std::string& json_arguments, - IPC::Message* reply_msg) { - if (delegate()) { - HtmlDialogUIDelegate* dialog_delegate = - new ModalHtmlDialogDelegate(url, width, height, json_arguments, - reply_msg, this); - delegate()->ShowHtmlDialog(dialog_delegate, NULL); - } -} - -void WebContents::PasswordFormsSeen( - const std::vector<PasswordForm>& forms) { - GetPasswordManager()->PasswordFormsSeen(forms); -} - -void WebContents::AutofillFormSubmitted( - const AutofillForm& form) { - GetAutofillManager()->AutofillFormSubmitted(form); -} - -void WebContents::GetAutofillSuggestions(const std::wstring& field_name, - const std::wstring& user_text, int64 node_id, int request_id) { - GetAutofillManager()->FetchValuesForName(field_name, user_text, - kMaxAutofillMenuItems, node_id, request_id); -} - -void WebContents::RemoveAutofillEntry(const std::wstring& field_name, - const std::wstring& value) { - GetAutofillManager()->RemoveValueForName(field_name, value); -} - -// Checks to see if we should generate a keyword based on the OSDD, and if -// necessary uses TemplateURLFetcher to download the OSDD and create a keyword. -void WebContents::PageHasOSDD(RenderViewHost* render_view_host, - int32 page_id, const GURL& url, - bool autodetected) { - // Make sure page_id is the current page, and the TemplateURLModel is loaded. - DCHECK(url.is_valid()); - if (!IsActiveEntry(page_id)) - return; - TemplateURLModel* url_model = profile()->GetTemplateURLModel(); - if (!url_model) - return; - if (!url_model->loaded()) { - url_model->Load(); - return; - } - if (!profile()->GetTemplateURLFetcher()) - return; - - if (profile()->IsOffTheRecord()) - return; - - const NavigationEntry* entry = controller_.GetLastCommittedEntry(); - DCHECK(entry); - - const NavigationEntry* base_entry = entry; - if (IsFormSubmit(base_entry)) { - // If the current page is a form submit, find the last page that was not - // a form submit and use its url to generate the keyword from. - int index = controller_.last_committed_entry_index() - 1; - while (index >= 0 && IsFormSubmit(controller_.GetEntryAtIndex(index))) - index--; - if (index >= 0) - base_entry = controller_.GetEntryAtIndex(index); - else - base_entry = NULL; - } - - // We want to use the user typed URL if available since that represents what - // the user typed to get here, and fall back on the regular URL if not. - if (!base_entry) - return; - GURL keyword_url = base_entry->user_typed_url().is_valid() ? - base_entry->user_typed_url() : base_entry->url(); - if (!keyword_url.is_valid()) - return; - std::wstring keyword = TemplateURLModel::GenerateKeyword(keyword_url, - autodetected); - if (keyword.empty()) - return; - const TemplateURL* template_url = - url_model->GetTemplateURLForKeyword(keyword); - if (template_url && (!template_url->safe_for_autoreplace() || - template_url->originating_url() == url)) { - // Either there is a user created TemplateURL for this keyword, or the - // keyword has the same OSDD url and we've parsed it. - return; - } - - // Download the OpenSearch description document. If this is successful a - // new keyword will be created when done. -#if defined(OS_WIN) - gfx::NativeView ancestor = GetAncestor(view_->GetNativeView(), GA_ROOT); -#else - gfx::NativeView ancestor = NULL; -#endif - profile()->GetTemplateURLFetcher()->ScheduleDownload( - keyword, - url, - base_entry->favicon().url(), - ancestor, - autodetected); -} - -void WebContents::InspectElementReply(int num_resources) { - // We have received reply from inspect element request. Notify the - // automation provider in case we need to notify automation client. - NotificationService::current()->Notify( - NotificationType::DOM_INSPECT_ELEMENT_RESPONSE, Source<WebContents>(this), - Details<int>(&num_resources)); -} - -void WebContents::DidGetPrintedPagesCount(int cookie, int number_pages) { - printing_.DidGetPrintedPagesCount(cookie, number_pages); -} - -void WebContents::DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params) { - printing_.DidPrintPage(params); -} - -GURL WebContents::GetAlternateErrorPageURL() const { - GURL url; - // Disable alternate error pages when in OffTheRecord/Incognito mode. - if (profile()->IsOffTheRecord()) - return url; - - PrefService* prefs = profile()->GetPrefs(); - DCHECK(prefs); - if (prefs->GetBoolean(prefs::kAlternateErrorPagesEnabled)) { - url = google_util::AppendGoogleLocaleParam(GURL(kLinkDoctorBaseURL)); - url = google_util::AppendGoogleTLDParam(url); - } - return url; -} - -WebPreferences WebContents::GetWebkitPrefs() { - PrefService* prefs = render_view_host()->process()->profile()->GetPrefs(); - bool isDomUI = false; - return RenderViewHostDelegateHelper::GetWebkitPrefs(prefs, isDomUI); -} - -void WebContents::OnMissingPluginStatus(int status) { -#if defined(OS_WIN) -// TODO(PORT): pull in when plug-ins work - GetPluginInstaller()->OnMissingPluginStatus(status); -#endif -} - -void WebContents::OnCrashedPlugin(const FilePath& plugin_path) { -#if defined(OS_WIN) -// TODO(PORT): pull in when plug-ins work - DCHECK(!plugin_path.value().empty()); - - std::wstring plugin_name = plugin_path.ToWStringHack(); - scoped_ptr<FileVersionInfo> version_info( - FileVersionInfo::CreateFileVersionInfo(plugin_path)); - if (version_info.get()) { - const std::wstring& product_name = version_info->product_name(); - if (!product_name.empty()) - plugin_name = product_name; - } - AddInfoBar(new SimpleAlertInfoBarDelegate( - this, l10n_util::GetStringF(IDS_PLUGIN_CRASHED_PROMPT, plugin_name), - NULL)); -#endif -} - -void WebContents::OnCrashedWorker() { - AddInfoBar(new SimpleAlertInfoBarDelegate( - this, l10n_util::GetString(IDS_WEBWORKER_CRASHED_PROMPT), - NULL)); -} - -void WebContents::OnJSOutOfMemory() { - AddInfoBar(new SimpleAlertInfoBarDelegate( - this, l10n_util::GetString(IDS_JS_OUT_OF_MEMORY_PROMPT), NULL)); -} - -bool WebContents::CanBlur() const { - return delegate() ? delegate()->CanBlur() : true; -} - -gfx::Rect WebContents::GetRootWindowResizerRect() const { - if (delegate()) - return delegate()->GetRootWindowResizerRect(); - return gfx::Rect(); -} - -void WebContents::RendererUnresponsive(RenderViewHost* rvh, - bool is_during_unload) { - if (is_during_unload) { - // Hang occurred while firing the beforeunload/unload handler. - // Pretend the handler fired so tab closing continues as if it had. - rvh->UnloadListenerHasFired(); - - if (!render_manager_.ShouldCloseTabOnUnresponsiveRenderer()) - return; - - // If the tab hangs in the beforeunload/unload handler there's really - // nothing we can do to recover. Pretend the unload listeners have - // all fired and close the tab. If the hang is in the beforeunload handler - // then the user will not have the option of cancelling the close. - Close(rvh); - return; - } - - if (render_view_host() && render_view_host()->IsRenderViewLive()) - HungRendererWarning::ShowForWebContents(this); -} - -void WebContents::RendererResponsive(RenderViewHost* render_view_host) { - HungRendererWarning::HideForWebContents(this); -} - -void WebContents::LoadStateChanged(const GURL& url, - net::LoadState load_state) { - load_state_ = load_state; - load_state_host_ = UTF8ToWide(url.host()); - if (load_state_ == net::LOAD_STATE_READING_RESPONSE) - SetNotWaitingForResponse(); - if (is_loading()) - NotifyNavigationStateChanged(INVALIDATE_LOAD | INVALIDATE_FAVICON); -} - -void WebContents::OnDidGetApplicationInfo( - int32 page_id, - const webkit_glue::WebApplicationInfo& info) { - if (pending_install_.page_id != page_id) - return; // The user clicked create on a separate page. Ignore this. - - pending_install_.callback_functor = - new GearsCreateShortcutCallbackFunctor(this); - GearsCreateShortcut( - info, pending_install_.title, pending_install_.url, pending_install_.icon, - NewCallback(pending_install_.callback_functor, - &GearsCreateShortcutCallbackFunctor::Run)); -} - -void WebContents::OnEnterOrSpace() { - // See comment in RenderViewHostDelegate::OnEnterOrSpace as to why we do this. -#if defined(OS_WIN) - // TODO(port): this is stubbed in BrowserProcess - DownloadRequestManager* drm = g_browser_process->download_request_manager(); - if (drm) - drm->OnUserGesture(this); -#endif -} - -void WebContents::OnFindReply(int request_id, - int number_of_matches, - const gfx::Rect& selection_rect, - int active_match_ordinal, - bool final_update) { - // Ignore responses for requests other than the one we have most recently - // issued. That way we won't act on stale results when the user has - // already typed in another query. - if (request_id != current_find_request_id_) - return; - - if (number_of_matches == -1) - number_of_matches = find_result_.number_of_matches(); - if (active_match_ordinal == -1) - active_match_ordinal = find_result_.active_match_ordinal(); - - // Notify the UI, automation and any other observers that a find result was - // found. - find_result_ = FindNotificationDetails(request_id, number_of_matches, - selection_rect, active_match_ordinal, - final_update); - NotificationService::current()->Notify( - NotificationType::FIND_RESULT_AVAILABLE, - Source<TabContents>(this), - Details<FindNotificationDetails>(&find_result_)); -} - -bool WebContents::CanTerminate() const { - if (!delegate()) - return true; - - return !delegate()->IsExternalTabContainer(); -} - -void WebContents::FileSelected(const FilePath& path, - int index, void* params) { - render_view_host()->FileSelected(path); -} - -void WebContents::MultiFilesSelected(const std::vector<FilePath>& files, - void* params) { - render_view_host()->MultiFilesSelected(files); -} - -void WebContents::FileSelectionCanceled(void* params) { - // If the user cancels choosing a file to upload we pass back an - // empty vector. - render_view_host()->MultiFilesSelected(std::vector<FilePath>()); -} - -void WebContents::BeforeUnloadFiredFromRenderManager( - bool proceed, - bool* proceed_to_fire_unload) { - if (delegate()) - delegate()->BeforeUnloadFired(this, proceed, proceed_to_fire_unload); -} - -void WebContents::UpdateRenderViewSizeForRenderManager() { - // TODO(brettw) this is a hack. See TabContentsView::SizeContents. - view_->SizeContents(view_->GetContainerSize()); -} - -DOMUI* WebContents::CreateDOMUIForRenderManager(const GURL& url) { - return DOMUIFactory::CreateDOMUIForURL(this, url); -} - -NavigationEntry* -WebContents::GetLastCommittedNavigationEntryForRenderManager() { - return controller_.GetLastCommittedEntry(); -} - -bool WebContents::CreateRenderViewForRenderManager( - RenderViewHost* render_view_host) { - // When we're running a DOM UI, the RenderViewHost needs to be put in DOM UI - // mode before CreateRenderView is called. When we're asked to create a - // RenderView, that means it's for the pending entry, so we have to use the - // pending DOM UI. - if (render_manager_.pending_dom_ui()) - render_view_host->AllowDOMUIBindings(); - - RenderWidgetHostView* rwh_view = view_->CreateViewForWidget(render_view_host); - if (!render_view_host->CreateRenderView()) - return false; - - // Now that the RenderView has been created, we need to tell it its size. - rwh_view->SetSize(view_->GetContainerSize()); - - UpdateMaxPageIDIfNecessary(render_view_host->site_instance(), - render_view_host); - return true; -} - -void WebContents::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - switch (type.value) { - case NotificationType::BOOKMARK_MODEL_LOADED: - // BookmarkModel finished loading, fall through to update starred state. - case NotificationType::URLS_STARRED: { - // Somewhere, a URL has been starred. - // Ignore notifications for profiles other than our current one. - Profile* source_profile = Source<Profile>(source).ptr(); - if (!source_profile || !source_profile->IsSameProfile(profile())) - return; - - UpdateStarredStateForCurrentURL(); - break; - } - case NotificationType::PREF_CHANGED: { - std::wstring* pref_name_in = Details<std::wstring>(details).ptr(); - DCHECK(Source<PrefService>(source).ptr() == profile()->GetPrefs()); - if (*pref_name_in == prefs::kAlternateErrorPagesEnabled) { - UpdateAlternateErrorPageURL(); - } else if (*pref_name_in == prefs::kDefaultCharset || - StartsWithASCII(WideToUTF8(*pref_name_in), "webkit.webprefs.", true) - ) { - UpdateWebPreferences(); - } else { - NOTREACHED() << "unexpected pref change notification" << *pref_name_in; - } - break; - } - case NotificationType::RENDER_WIDGET_HOST_DESTROYED: - view_->RenderWidgetHostDestroyed(Source<RenderWidgetHost>(source).ptr()); - break; - - case NotificationType::NAV_ENTRY_COMMITTED: { - DCHECK(&controller_ == Source<NavigationController>(source).ptr()); - - NavigationController::LoadCommittedDetails& committed_details = - *(Details<NavigationController::LoadCommittedDetails>(details).ptr()); - ExpireInfoBars(committed_details); - break; - } - - default: - NOTREACHED(); - } -} - -void WebContents::DidNavigateMainFramePostCommit( - const NavigationController::LoadCommittedDetails& details, - const ViewHostMsg_FrameNavigate_Params& params) { - // Hide the download shelf if all the following conditions are true: - // - there are no active downloads. - // - this is a navigation to a different TLD. - // - at least 5 seconds have elapsed since the download shelf was shown. - // TODO(jcampan): bug 1156075 when user gestures are reliable, they should - // be used to ensure we are hiding only on user initiated - // navigations. - DownloadManager* download_manager = profile()->GetDownloadManager(); - // download_manager can be NULL in unit test context. - if (download_manager && download_manager->in_progress_count() == 0 && - !details.previous_url.is_empty() && - !net::RegistryControlledDomainService::SameDomainOrHost( - details.previous_url, details.entry->url())) { - TimeDelta time_delta( - TimeTicks::Now() - last_download_shelf_show_); - if (time_delta > - TimeDelta::FromMilliseconds(kDownloadShelfHideDelay)) { - SetDownloadShelfVisible(false); - } - } - - if (details.is_user_initiated_main_frame_load()) { - // Clear the status bubble. This is a workaround for a bug where WebKit - // doesn't let us know that the cursor left an element during a - // transition (this is also why the mouse cursor remains as a hand after - // clicking on a link); see bugs 1184641 and 980803. We don't want to - // clear the bubble when a user navigates to a named anchor in the same - // page. - UpdateTargetURL(details.entry->page_id(), GURL()); - - // UpdateHelpersForDidNavigate will handle the case where the password_form - // origin is valid. - // TODO(brettw) bug 1343111: Password manager stuff in here needs to be - // cleaned up and covered by tests. - if (!params.password_form.origin.is_valid()) - GetPasswordManager()->DidNavigate(); - } - - // The keyword generator uses the navigation entries, so must be called after - // the commit. - GenerateKeywordIfNecessary(params); - - // Allow the new page to set the title again. - received_page_title_ = false; - - // Get the favicon, either from history or request it from the net. - fav_icon_helper_.FetchFavIcon(details.entry->url()); - - // Close constrained popups if necessary. - MaybeCloseChildWindows(details.previous_url, details.entry->url()); - - // Update the starred state. - UpdateStarredStateForCurrentURL(); -} - -void WebContents::DidNavigateAnyFramePostCommit( - RenderViewHost* render_view_host, - const NavigationController::LoadCommittedDetails& details, - const ViewHostMsg_FrameNavigate_Params& params) { - // If we navigate, start showing messages again. This does nothing to prevent - // a malicious script from spamming messages, since the script could just - // reload the page to stop blocking. - suppress_javascript_messages_ = false; - - // Update history. Note that this needs to happen after the entry is complete, - // which WillNavigate[Main,Sub]Frame will do before this function is called. - if (params.should_update_history) { - // Most of the time, the displayURL matches the loaded URL, but for about: - // URLs, we use a data: URL as the real value. We actually want to save - // the about: URL to the history db and keep the data: URL hidden. This is - // what the TabContents' URL getter does. - UpdateHistoryForNavigation(GetURL(), params); - } - - // Notify the password manager of the navigation or form submit. - // TODO(brettw) bug 1343111: Password manager stuff in here needs to be - // cleaned up and covered by tests. - if (params.password_form.origin.is_valid()) - GetPasswordManager()->ProvisionallySavePassword(params.password_form); -} - -void WebContents::MaybeCloseChildWindows(const GURL& previous_url, - const GURL& current_url) { - if (net::RegistryControlledDomainService::SameDomainOrHost( - previous_url, current_url)) - return; - - // Clear out any child windows since we are leaving this page entirely. - // We use indices instead of iterators in case CloseWindow does something - // that may invalidate an iterator. - int size = static_cast<int>(child_windows_.size()); - for (int i = size - 1; i >= 0; --i) { - ConstrainedWindow* window = child_windows_[i]; - if (window) - window->CloseConstrainedWindow(); - } -} - -void WebContents::UpdateStarredStateForCurrentURL() { - BookmarkModel* model = profile()->GetBookmarkModel(); - const bool old_state = is_starred_; - is_starred_ = (model && model->IsBookmarked(GetURL())); - - if (is_starred_ != old_state && delegate()) - delegate()->URLStarredChanged(this, is_starred_); -} - -void WebContents::UpdateAlternateErrorPageURL() { - GURL url = GetAlternateErrorPageURL(); - render_view_host()->SetAlternateErrorPageURL(url); -} - -void WebContents::UpdateWebPreferences() { - render_view_host()->UpdateWebPreferences(GetWebkitPrefs()); -} - -void WebContents::UpdateMaxPageIDIfNecessary(SiteInstance* site_instance, - RenderViewHost* rvh) { - // If we are creating a RVH for a restored controller, then we might - // have more page IDs than the SiteInstance's current max page ID. We must - // make sure that the max page ID is larger than any restored page ID. - // Note that it is ok for conflicting page IDs to exist in another tab - // (i.e., NavigationController), but if any page ID is larger than the max, - // the back/forward list will get confused. - int max_restored_page_id = controller_.max_restored_page_id(); - if (max_restored_page_id > 0) { - int curr_max_page_id = site_instance->max_page_id(); - if (max_restored_page_id > curr_max_page_id) { - // Need to update the site instance immediately. - site_instance->UpdateMaxPageID(max_restored_page_id); - - // Also tell the renderer to update its internal representation. We - // need to reserve enough IDs to make all restored page IDs less than - // the max. - if (curr_max_page_id < 0) - curr_max_page_id = 0; - rvh->ReservePageIDRange(max_restored_page_id - curr_max_page_id); - } - } -} - -void WebContents::UpdateHistoryForNavigation( - const GURL& display_url, - const ViewHostMsg_FrameNavigate_Params& params) { - if (profile()->IsOffTheRecord()) - return; - - // Add to history service. - HistoryService* hs = profile()->GetHistoryService(Profile::IMPLICIT_ACCESS); - if (hs) { - if (PageTransition::IsMainFrame(params.transition) && - display_url != params.url) { - // Hack on the "display" URL so that it will appear in history. For some - // types of URLs, we will display a magic URL that is different from where - // the page is actually navigated. We want the user to see in history - // what they saw in the URL bar, so we add the display URL as a redirect. - // This only applies to the main frame, as the display URL doesn't apply - // to sub-frames. - std::vector<GURL> redirects = params.redirects; - if (!redirects.empty()) - redirects.back() = display_url; - hs->AddPage(display_url, this, params.page_id, params.referrer, - params.transition, redirects); - } else { - hs->AddPage(params.url, this, params.page_id, params.referrer, - params.transition, params.redirects); - } - } -} - -bool WebContents::UpdateTitleForEntry(NavigationEntry* entry, - const std::wstring& title) { - // For file URLs without a title, use the pathname instead. In the case of a - // synthesized title, we don't want the update to count toward the "one set - // per page of the title to history." - std::wstring final_title; - bool explicit_set; - if (entry->url().SchemeIsFile() && title.empty()) { - final_title = UTF8ToWide(entry->url().ExtractFileName()); - explicit_set = false; // Don't count synthetic titles toward the set limit. - } else { - TrimWhitespace(title, TRIM_ALL, &final_title); - explicit_set = true; - } - - if (final_title == UTF16ToWideHack(entry->title())) - return false; // Nothing changed, don't bother. - - entry->set_title(WideToUTF16Hack(final_title)); - - // Update the history system for this page. - if (!profile()->IsOffTheRecord() && !received_page_title_) { - HistoryService* hs = - profile()->GetHistoryService(Profile::IMPLICIT_ACCESS); - if (hs) - hs->SetPageTitle(entry->display_url(), final_title); - - // Don't allow the title to be saved again for explicitly set ones. - received_page_title_ = explicit_set; - } - - // Lastly, set the title for the view. - view_->SetPageTitle(final_title); - - return true; -} - -void WebContents::NotifySwapped() { - // After sending out a swap notification, we need to send a disconnect - // notification so that clients that pick up a pointer to |this| can NULL the - // pointer. See Bug 1230284. - notify_disconnection_ = true; - NotificationService::current()->Notify( - NotificationType::WEB_CONTENTS_SWAPPED, - Source<WebContents>(this), - NotificationService::NoDetails()); -} - -void WebContents::NotifyConnected() { - notify_disconnection_ = true; - NotificationService::current()->Notify( - NotificationType::WEB_CONTENTS_CONNECTED, - Source<WebContents>(this), - NotificationService::NoDetails()); -} - -void WebContents::NotifyDisconnected() { - if (!notify_disconnection_) - return; - - notify_disconnection_ = false; - NotificationService::current()->Notify( - NotificationType::WEB_CONTENTS_DISCONNECTED, - Source<WebContents>(this), - NotificationService::NoDetails()); -} - -void WebContents::GenerateKeywordIfNecessary( - const ViewHostMsg_FrameNavigate_Params& params) { - if (!params.searchable_form_url.is_valid()) - return; - - if (profile()->IsOffTheRecord()) - return; - - int last_index = controller_.last_committed_entry_index(); - // When there was no previous page, the last index will be 0. This is - // normally due to a form submit that opened in a new tab. - // TODO(brettw) bug 916126: we should support keywords when form submits - // happen in new tabs. - if (last_index <= 0) - return; - const NavigationEntry* previous_entry = - controller_.GetEntryAtIndex(last_index - 1); - if (IsFormSubmit(previous_entry)) { - // Only generate a keyword if the previous page wasn't itself a form - // submit. - return; - } - - GURL keyword_url = previous_entry->user_typed_url().is_valid() ? - previous_entry->user_typed_url() : previous_entry->url(); - std::wstring keyword = - TemplateURLModel::GenerateKeyword(keyword_url, true); // autodetected - if (keyword.empty()) - return; - - TemplateURLModel* url_model = profile()->GetTemplateURLModel(); - if (!url_model) - return; - - if (!url_model->loaded()) { - url_model->Load(); - return; - } - - const TemplateURL* current_url; - std::wstring url = UTF8ToWide(params.searchable_form_url.spec()); - if (!url_model->CanReplaceKeyword(keyword, url, ¤t_url)) - return; - - if (current_url) { - if (current_url->originating_url().is_valid()) { - // The existing keyword was generated from an OpenSearch description - // document, don't regenerate. - return; - } - url_model->Remove(current_url); - } - TemplateURL* new_url = new TemplateURL(); - new_url->set_keyword(keyword); - new_url->set_short_name(keyword); - new_url->SetURL(url, 0, 0); - new_url->add_input_encoding(params.searchable_form_encoding); - DCHECK(controller_.GetLastCommittedEntry()); - const GURL& favicon_url = - controller_.GetLastCommittedEntry()->favicon().url(); - if (favicon_url.is_valid()) { - new_url->SetFavIconURL(favicon_url); - } else { - // The favicon url isn't valid. This means there really isn't a favicon, - // or the favicon url wasn't obtained before the load started. This assumes - // the later. - // TODO(sky): Need a way to set the favicon that doesn't involve generating - // its url. - new_url->SetFavIconURL(TemplateURL::GenerateFaviconURL(params.referrer)); - } - new_url->set_safe_for_autoreplace(true); - url_model->Add(new_url); -} diff --git a/chrome/browser/tab_contents/web_contents.h b/chrome/browser/tab_contents/web_contents.h index b798bd3..c0e3600 100644 --- a/chrome/browser/tab_contents/web_contents.h +++ b/chrome/browser/tab_contents/web_contents.h @@ -7,353 +7,24 @@ #include "chrome/browser/tab_contents/tab_contents.h" -// WebContents represents the contents of a tab that shows web pages. It embeds -// a RenderViewHost (via RenderViewHostManager) to actually display the page. +// TODO(brettw) this class is a placeholder until I can remove all references +// to WebContents. class WebContents : public TabContents { public: - // If instance is NULL, then creates a new process for this view. Otherwise - // initialize with a process already created for a different WebContents. - // This will share the process between views in the same instance. WebContents(Profile* profile, - SiteInstance* instance, + SiteInstance* site_instance, int routing_id, - base::WaitableEvent* modal_dialog_event); - - virtual ~WebContents(); - - static void RegisterUserPrefs(PrefService* prefs); - - // Window stuff -------------------------------------------------------------- - - // TabContents (public overrides) -------------------------------------------- - - virtual WebContents* AsWebContents() { return this; } - - // Web apps ------------------------------------------------------------------ - - // Tell Gears to create a shortcut for the current page. - void CreateShortcut(); - - // Misc state & callbacks ---------------------------------------------------- - - // Set whether the contents should block javascript message boxes or not. - // Default is not to block any message boxes. - void set_suppress_javascript_messages(bool suppress_javascript_messages) { - suppress_javascript_messages_ = suppress_javascript_messages; - } - - // AppModalDialog calls this when the dialog is closed. - void OnJavaScriptMessageBoxClosed(IPC::Message* reply_msg, - bool success, - const std::wstring& prompt); - - // Prepare for saving the current web page to disk. - void OnSavePage(); - - // Save page with the main HTML file path, the directory for saving resources, - // and the save type: HTML only or complete web page. - void SavePage(const std::wstring& main_file, - const std::wstring& dir_path, - SavePackage::SavePackageType save_type); - - // Displays asynchronously a print preview (generated by the renderer) if not - // already displayed and ask the user for its preferred print settings with - // the "Print..." dialog box. (managed by the print worker thread). - // TODO(maruel): Creates a snapshot of the renderer to be used for the new - // tab for the printing facility. - void PrintPreview(); - - // Prints the current document immediately. Since the rendering is - // asynchronous, the actual printing will not be completed on the return of - // this function. Returns false if printing is impossible at the moment. - bool PrintNow(); - - // Returns true if the active NavigationEntry's page_id equals page_id. - bool IsActiveEntry(int32 page_id); - - const std::string& contents_mime_type() const { - return contents_mime_type_; - } - - // Returns true if this WebContents will notify about disconnection. - bool notify_disconnection() const { return notify_disconnection_; } - - // Override the encoding and reload the page by sending down - // ViewMsg_SetPageEncoding to the renderer. |UpdateEncoding| is kinda - // the opposite of this, by which 'browser' is notified of - // the encoding of the current tab from 'renderer' (determined by - // auto-detect, http header, meta, bom detection, etc). - void override_encoding(const std::wstring& encoding) { - set_encoding(encoding); - render_view_host()->SetPageEncoding(encoding); - } - - void CrossSiteNavigationCanceled() { - render_manager_.CrossSiteNavigationCanceled(); - } - - void WindowMoveOrResizeStarted() { - render_view_host()->WindowMoveOrResizeStarted(); - } - - protected: - - // RenderViewHostDelegate ---------------------------------------------------- - - virtual RenderViewHostDelegate::View* GetViewDelegate() const; - virtual RenderViewHostDelegate::Save* GetSaveDelegate() const; - virtual Profile* GetProfile() const; - virtual WebContents* GetAsWebContents() { return this; } - virtual void RenderViewCreated(RenderViewHost* render_view_host); - virtual void RenderViewReady(RenderViewHost* render_view_host); - virtual void RenderViewGone(RenderViewHost* render_view_host); - virtual void DidNavigate(RenderViewHost* render_view_host, - const ViewHostMsg_FrameNavigate_Params& params); - virtual void UpdateState(RenderViewHost* render_view_host, - int32 page_id, - const std::string& state); - virtual void UpdateTitle(RenderViewHost* render_view_host, - int32 page_id, - const std::wstring& title); - virtual void UpdateFeedList(RenderViewHost* render_view_host, - const ViewHostMsg_UpdateFeedList_Params& params); - virtual void UpdateEncoding(RenderViewHost* render_view_host, - const std::wstring& encoding); - virtual void UpdateTargetURL(int32 page_id, const GURL& url); - virtual void UpdateThumbnail(const GURL& url, - const SkBitmap& bitmap, - const ThumbnailScore& score); - virtual void Close(RenderViewHost* render_view_host); - virtual void RequestMove(const gfx::Rect& new_bounds); - virtual void DidStartLoading(RenderViewHost* render_view_host, int32 page_id); - virtual void DidStopLoading(RenderViewHost* render_view_host, int32 page_id); - virtual void DidStartProvisionalLoadForFrame(RenderViewHost* render_view_host, - bool is_main_frame, - const GURL& url); - virtual void DidRedirectProvisionalLoad(int32 page_id, - const GURL& source_url, - const GURL& target_url); - virtual void DidLoadResourceFromMemoryCache( - const GURL& url, - const std::string& frame_origin, - const std::string& main_frame_origin, - const std::string& security_info); - virtual void DidFailProvisionalLoadWithError( - RenderViewHost* render_view_host, - bool is_main_frame, - int error_code, - const GURL& url, - bool showing_repost_interstitial); - virtual void UpdateFavIconURL(RenderViewHost* render_view_host, - int32 page_id, const GURL& icon_url); - virtual void DidDownloadImage(RenderViewHost* render_view_host, - int id, - const GURL& image_url, - bool errored, - const SkBitmap& image); - virtual void RequestOpenURL(const GURL& url, const GURL& referrer, - WindowOpenDisposition disposition); - virtual void DomOperationResponse(const std::string& json_string, - int automation_id); - virtual void ProcessDOMUIMessage(const std::string& message, - const std::string& content); - virtual void ProcessExternalHostMessage(const std::string& message, - const std::string& origin, - const std::string& target); - virtual void GoToEntryAtOffset(int offset); - virtual void GetHistoryListCount(int* back_list_count, - int* forward_list_count); - virtual void RunFileChooser(bool multiple_files, - const string16& title, - const FilePath& default_file); - virtual void RunJavaScriptMessage(const std::wstring& message, - const std::wstring& default_prompt, - const GURL& frame_url, - const int flags, - IPC::Message* reply_msg, - bool* did_suppress_message); - virtual void RunBeforeUnloadConfirm(const std::wstring& message, - IPC::Message* reply_msg); - virtual void ShowModalHTMLDialog(const GURL& url, int width, int height, - const std::string& json_arguments, - IPC::Message* reply_msg); - virtual void PasswordFormsSeen(const std::vector<PasswordForm>& forms); - virtual void AutofillFormSubmitted(const AutofillForm& form); - virtual void GetAutofillSuggestions(const std::wstring& field_name, - const std::wstring& user_text, int64 node_id, int request_id); - virtual void RemoveAutofillEntry(const std::wstring& field_name, - const std::wstring& value); - virtual void PageHasOSDD(RenderViewHost* render_view_host, - int32 page_id, const GURL& url, bool autodetected); - virtual void InspectElementReply(int num_resources); - virtual void DidGetPrintedPagesCount(int cookie, int number_pages); - virtual void DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params); - virtual GURL GetAlternateErrorPageURL() const; - virtual WebPreferences GetWebkitPrefs(); - virtual void OnMissingPluginStatus(int status); - virtual void OnCrashedPlugin(const FilePath& plugin_path); - virtual void OnCrashedWorker(); - virtual void OnJSOutOfMemory(); - virtual void ShouldClosePage(bool proceed) { - render_manager_.ShouldClosePage(proceed); + base::WaitableEvent* modal_dialog_event) + : TabContents(profile, + site_instance, + routing_id, + modal_dialog_event) { } - // Allows the WebContents to react when a cross-site response is ready to be - // delivered to a pending RenderViewHost. We must first run the onunload - // handler of the old RenderViewHost before we can allow it to proceed. - void OnCrossSiteResponse(int new_render_process_host_id, - int new_request_id) { - render_manager_.OnCrossSiteResponse(new_render_process_host_id, - new_request_id); - } - virtual bool CanBlur() const; - virtual gfx::Rect GetRootWindowResizerRect() const; - virtual void RendererUnresponsive(RenderViewHost* render_view_host, - bool is_during_unload); - virtual void RendererResponsive(RenderViewHost* render_view_host); - virtual void LoadStateChanged(const GURL& url, net::LoadState load_state); - virtual void OnDidGetApplicationInfo( - int32 page_id, - const webkit_glue::WebApplicationInfo& info); - virtual void OnEnterOrSpace(); - virtual void OnFindReply(int request_id, - int number_of_matches, - const gfx::Rect& selection_rect, - int active_match_ordinal, - bool final_update); - virtual bool CanTerminate() const; - - - // SelectFileDialog::Listener ------------------------------------------------ - - virtual void FileSelected(const FilePath& path, int index, void* params); - virtual void MultiFilesSelected(const std::vector<FilePath>& files, - void* params); - virtual void FileSelectionCanceled(void* params); - - // RenderViewHostManager::Delegate ------------------------------------------- - - virtual void BeforeUnloadFiredFromRenderManager( - bool proceed, - bool* proceed_to_fire_unload); - virtual void DidStartLoadingFromRenderManager( - RenderViewHost* render_view_host, int32 page_id) { - DidStartLoading(render_view_host, page_id); - } - virtual void RenderViewGoneFromRenderManager( - RenderViewHost* render_view_host) { - RenderViewGone(render_view_host); - } - virtual void UpdateRenderViewSizeForRenderManager(); - virtual void NotifySwappedFromRenderManager() { - NotifySwapped(); - } - virtual NavigationController& GetControllerForRenderManager() { - return controller(); - } - virtual DOMUI* CreateDOMUIForRenderManager(const GURL& url); - virtual NavigationEntry* GetLastCommittedNavigationEntryForRenderManager(); - // Initializes the given renderer if necessary and creates the view ID - // corresponding to this view host. If this method is not called and the - // process is not shared, then the WebContents will act as though the renderer - // is not running (i.e., it will render "sad tab"). This method is - // automatically called from LoadURL. - // - // If you are attaching to an already-existing RenderView, you should call - // InitWithExistingID. - virtual bool CreateRenderViewForRenderManager( - RenderViewHost* render_view_host); + virtual ~WebContents() {} private: - FRIEND_TEST(WebContentsTest, UpdateTitle); - friend class TestWebContents; - - // Temporary until the view/contents separation is complete. - friend class TabContentsView; -#if defined(OS_WIN) - friend class TabContentsViewWin; -#elif defined(OS_MACOSX) - friend class TabContentsViewMac; -#elif defined(OS_LINUX) - friend class TabContentsViewGtk; -#endif - - // So InterstitialPage can access SetIsLoading. - friend class InterstitialPage; - - - // NotificationObserver ------------------------------------------------------ - - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - // Navigation helpers -------------------------------------------------------- - // - // These functions are helpers for Navigate() and DidNavigate(). - - // Handles post-navigation tasks in DidNavigate AFTER the entry has been - // committed to the navigation controller. Note that the navigation entry is - // not provided since it may be invalid/changed after being committed. The - // current navigation entry is in the NavigationController at this point. - void DidNavigateMainFramePostCommit( - const NavigationController::LoadCommittedDetails& details, - const ViewHostMsg_FrameNavigate_Params& params); - void DidNavigateAnyFramePostCommit( - RenderViewHost* render_view_host, - const NavigationController::LoadCommittedDetails& details, - const ViewHostMsg_FrameNavigate_Params& params); - - // Closes all child windows (constrained popups) when the domain changes. - // Supply the new and old URLs, and this function will figure out when the - // domain changing conditions are met. - void MaybeCloseChildWindows(const GURL& previous_url, - const GURL& current_url); - - // Updates the starred state from the bookmark bar model. If the state has - // changed, the delegate is notified. - void UpdateStarredStateForCurrentURL(); - - // Send the alternate error page URL to the renderer. This method is virtual - // so special html pages can override this (e.g., the new tab page). - virtual void UpdateAlternateErrorPageURL(); - - // Send webkit specific settings to the renderer. - void UpdateWebPreferences(); - - // If our controller was restored and the page id is > than the site - // instance's page id, the site instances page id is updated as well as the - // renderers max page id. - void UpdateMaxPageIDIfNecessary(SiteInstance* site_instance, - RenderViewHost* rvh); - - // Called by OnMsgNavigate to update history state. Overridden by subclasses - // that don't want to be added to history. - virtual void UpdateHistoryForNavigation(const GURL& display_url, - const ViewHostMsg_FrameNavigate_Params& params); - - // Saves the given title to the navigation entry and does associated work. It - // will update history and the view for the new title, and also synthesize - // titles for file URLs that have none (so we require that the URL of the - // entry already be set). - // - // This is used as the backend for state updates, which include a new title, - // or the dedicated set title message. It returns true if the new title is - // different and was therefore updated. - bool UpdateTitleForEntry(NavigationEntry* entry, const std::wstring& title); - - // Misc non-view stuff ------------------------------------------------------- - - // Helper functions for sending notifications. - void NotifySwapped(); - void NotifyConnected(); - void NotifyDisconnected(); - - // If params has a searchable form, this tries to create a new keyword. - void GenerateKeywordIfNecessary( - const ViewHostMsg_FrameNavigate_Params& params); - - DISALLOW_COPY_AND_ASSIGN(WebContents); + DISALLOW_COPY_AND_ASSIGN(WebContents); }; #endif // CHROME_BROWSER_TAB_CONTENTS_WEB_CONTENTS_H_ |