diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-22 17:48:25 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-22 17:48:25 +0000 |
commit | 6524b5f9cb595b601fe95396439508efc7ecc8b3 (patch) | |
tree | d3b028ebba0b8a223ce0508131d8b69fe5f85354 /chrome/browser/tab_contents | |
parent | c8159813d30739744a18e901f5ac27ac27424bb4 (diff) | |
download | chromium_src-6524b5f9cb595b601fe95396439508efc7ecc8b3.zip chromium_src-6524b5f9cb595b601fe95396439508efc7ecc8b3.tar.gz chromium_src-6524b5f9cb595b601fe95396439508efc7ecc8b3.tar.bz2 |
Move files out of browser and into either renderer_host or tab_contents.
This also fixes a crash in the web contents unit test in a commented-out test and re-enable it.
Review URL: http://codereview.chromium.org/18504
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8470 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/tab_contents')
18 files changed, 1552 insertions, 21 deletions
diff --git a/chrome/browser/tab_contents/interstitial_page.cc b/chrome/browser/tab_contents/interstitial_page.cc index 491b1cc..bc65504 100644 --- a/chrome/browser/tab_contents/interstitial_page.cc +++ b/chrome/browser/tab_contents/interstitial_page.cc @@ -8,7 +8,7 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_resources.h" #include "chrome/browser/dom_operation_notification_details.h" -#include "chrome/browser/render_widget_host_view_win.h" +#include "chrome/browser/renderer_host/render_widget_host_view_win.h" #include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/browser/tab_contents/web_contents.h" diff --git a/chrome/browser/tab_contents/interstitial_page.h b/chrome/browser/tab_contents/interstitial_page.h index 9a68f95..755ec65 100644 --- a/chrome/browser/tab_contents/interstitial_page.h +++ b/chrome/browser/tab_contents/interstitial_page.h @@ -7,7 +7,7 @@ #include <string> -#include "chrome/browser/render_view_host_delegate.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/common/notification_registrar.h" #include "googleurl/src/gurl.h" diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc new file mode 100644 index 0000000..4aecf00 --- /dev/null +++ b/chrome/browser/tab_contents/render_view_context_menu.cc @@ -0,0 +1,180 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/tab_contents/render_view_context_menu.h" + +#include "base/logging.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/search_engines/template_url_model.h" +#include "chrome/browser/spellchecker.h" +#include "chrome/common/l10n_util.h" +#include "webkit/glue/context_node_types.h" + +#include "generated_resources.h" + +RenderViewContextMenu::RenderViewContextMenu( + Menu::Delegate* delegate, + HWND owner, + ContextNode::Type type, + const std::wstring& misspelled_word, + const std::vector<std::wstring>& misspelled_word_suggestions, + Profile* profile) + : Menu(delegate, Menu::TOPLEFT, owner), + misspelled_word_(misspelled_word), + misspelled_word_suggestions_(misspelled_word_suggestions), + profile_(profile) { + InitMenu(type); +} + +RenderViewContextMenu::~RenderViewContextMenu() { +} + +void RenderViewContextMenu::InitMenu(ContextNode::Type type) { + switch (type) { + case ContextNode::PAGE: + AppendPageItems(); + break; + case ContextNode::FRAME: + AppendFrameItems(); + break; + case ContextNode::LINK: + AppendLinkItems(); + break; + case ContextNode::IMAGE: + AppendImageItems(); + break; + case ContextNode::IMAGE_LINK: + AppendLinkItems(); + AppendSeparator(); + AppendImageItems(); + break; + case ContextNode::SELECTION: + AppendSelectionItems(); + break; + case ContextNode::EDITABLE: + AppendEditableItems(); + break; + default: + NOTREACHED() << "Unknown ContextNode::Type"; + } + AppendSeparator(); + AppendDeveloperItems(); +} + +void RenderViewContextMenu::AppendDeveloperItems() { + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_INSPECTELEMENT); +} + +void RenderViewContextMenu::AppendLinkItems() { + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_OPENLINKNEWTAB); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_SAVELINKAS); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_COPYLINKLOCATION); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_COPY); +} + +void RenderViewContextMenu::AppendImageItems() { + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_SAVEIMAGEAS); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_COPYIMAGELOCATION); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_COPYIMAGE); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB); +} + +void RenderViewContextMenu::AppendPageItems() { + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_BACK); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_FORWARD); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_RELOAD); + AppendSeparator(); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_SAVEPAGEAS); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_PRINT); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_VIEWPAGESOURCE); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_VIEWPAGEINFO); +} + +void RenderViewContextMenu::AppendFrameItems() { + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_BACK); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_FORWARD); + AppendSeparator(); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_OPENFRAMENEWTAB); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_OPENFRAMENEWWINDOW); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_OPENFRAMEOFFTHERECORD); + AppendSeparator(); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_SAVEFRAMEAS); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_PRINTFRAME); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_VIEWFRAMEINFO); +} + +void RenderViewContextMenu::AppendSelectionItems() { + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_COPY); + DCHECK(profile_); + if (profile_->GetTemplateURLModel()->GetDefaultSearchProvider() != NULL) + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_SEARCHWEBFOR); +} + +void RenderViewContextMenu::AppendEditableItems() { + // Append Dictionary spell check suggestions. + for (size_t i = 0; i < misspelled_word_suggestions_.size() && + IDC_SPELLCHECK_SUGGESTION_0 + i <= IDC_SPELLCHECK_SUGGESTION_LAST; + i ++) { + AppendMenuItemWithLabel(IDC_SPELLCHECK_SUGGESTION_0 + static_cast<int>(i), + misspelled_word_suggestions_[i]); + } + if (misspelled_word_suggestions_.size() > 0) + AppendSeparator(); + + // If word is misspelled, give option for "Add to dictionary" + if (!misspelled_word_.empty()) { + if (misspelled_word_suggestions_.size() == 0) { + AppendMenuItemWithLabel(0, + l10n_util::GetString(IDS_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS)); + } + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY); + AppendSeparator(); + } + + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_UNDO); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_REDO); + AppendSeparator(); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_CUT); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_COPY); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_PASTE); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_DELETE); + AppendSeparator(); + + // Add Spell Check options sub menu. + spellchecker_sub_menu_ = AppendSubMenu(IDC_SPELLCHECK_MENU, + l10n_util::GetString(IDS_CONTENT_CONTEXT_SPELLCHECK_MENU)); + + // Add Spell Check languages to sub menu. + SpellChecker::Languages display_languages; + SpellChecker::GetSpellCheckLanguagesToDisplayInContextMenu(profile_, + &display_languages); + DCHECK(display_languages.size() < + IDC_SPELLCHECK_LANGUAGES_LAST - IDC_SPELLCHECK_LANGUAGES_FIRST); + const std::wstring app_locale = g_browser_process->GetApplicationLocale(); + for (size_t i = 0; i < display_languages.size(); ++i) { + std::wstring local_language(l10n_util::GetLocalName( + display_languages[i], app_locale, true)); + spellchecker_sub_menu_->AppendMenuItem( + IDC_SPELLCHECK_LANGUAGES_FIRST + i, local_language, RADIO); + } + + // Add item in the sub menu to pop up the fonts and languages options menu. + spellchecker_sub_menu_->AppendSeparator(); + spellchecker_sub_menu_->AppendDelegateMenuItem( + IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS); + + // Add 'Check the spelling of this field' item in the sub menu. + spellchecker_sub_menu_->AppendMenuItem( + IDC_CHECK_SPELLING_OF_THIS_FIELD, + l10n_util::GetString(IDS_CONTENT_CONTEXT_CHECK_SPELLING_OF_THIS_FIELD), + CHECKBOX); + + AppendSeparator(); + AppendDelegateMenuItem(IDS_CONTENT_CONTEXT_SELECTALL); +} diff --git a/chrome/browser/tab_contents/render_view_context_menu.h b/chrome/browser/tab_contents/render_view_context_menu.h new file mode 100644 index 0000000..6c95f57 --- /dev/null +++ b/chrome/browser/tab_contents/render_view_context_menu.h @@ -0,0 +1,44 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_CONTEXT_MENU_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_CONTEXT_MENU_H_ + +#include "chrome/views/menu.h" +#include "webkit/glue/context_node_types.h" + +class Profile; + +class RenderViewContextMenu : public Menu { + public: + RenderViewContextMenu( + Menu::Delegate* delegate, + HWND owner, + ContextNode::Type type, + const std::wstring& misspelled_word, + const std::vector<std::wstring>& misspelled_word_suggestions, + Profile* profile); + + virtual ~RenderViewContextMenu(); + + private: + void InitMenu(ContextNode::Type type); + void AppendDeveloperItems(); + void AppendLinkItems(); + void AppendImageItems(); + void AppendPageItems(); + void AppendFrameItems(); + void AppendSelectionItems(); + void AppendEditableItems(); + + std::wstring misspelled_word_; + std::vector<std::wstring> misspelled_word_suggestions_; + Profile* profile_; + Menu* spellchecker_sub_menu_; + + DISALLOW_COPY_AND_ASSIGN(RenderViewContextMenu); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_CONTEXT_MENU_H_ + diff --git a/chrome/browser/tab_contents/render_view_context_menu_controller.cc b/chrome/browser/tab_contents/render_view_context_menu_controller.cc new file mode 100644 index 0000000..4b3a245 --- /dev/null +++ b/chrome/browser/tab_contents/render_view_context_menu_controller.cc @@ -0,0 +1,516 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/tab_contents/render_view_context_menu_controller.h" + +#include "base/command_line.h" +#include "base/path_service.h" +#include "base/scoped_clipboard_writer.h" +#include "base/string_util.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/views/options/fonts_languages_window_view.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/spellchecker.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/download/save_package.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/search_engines/template_url_model.h" +#include "chrome/browser/views/page_info_window.h" +#include "chrome/browser/tab_contents/navigation_controller.h" +#include "chrome/browser/tab_contents/navigation_entry.h" +#include "chrome/browser/tab_contents/web_contents.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/clipboard_service.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/win_util.h" +#include "net/base/escape.h" +#include "net/base/net_util.h" +#include "net/url_request/url_request.h" + +#include "generated_resources.h" + +RenderViewContextMenuController::RenderViewContextMenuController( + WebContents* source_web_contents, + const ViewHostMsg_ContextMenu_Params& params) + : source_web_contents_(source_web_contents), + params_(params) { +} + +RenderViewContextMenuController::~RenderViewContextMenuController() { +} + +/////////////////////////////////////////////////////////////////////////////// +// Controller methods + +void RenderViewContextMenuController::OpenURL( + const GURL& url, + WindowOpenDisposition disposition, + PageTransition::Type transition) { + source_web_contents_->OpenURL(url, GURL(), disposition, transition); +} + +void RenderViewContextMenuController::CopyImageAt(int x, int y) { + source_web_contents_->render_view_host()->CopyImageAt(x, y); +} + +void RenderViewContextMenuController::Inspect(int x, int y) { + source_web_contents_->render_view_host()->InspectElementAt(x, y); +} + +void RenderViewContextMenuController::WriteTextToClipboard( + const std::wstring& text) { + ClipboardService* clipboard = g_browser_process->clipboard_service(); + + if (!clipboard) + return; + + ScopedClipboardWriter scw(clipboard); + scw.WriteText(text); +} + +void RenderViewContextMenuController::WriteURLToClipboard(const GURL& url) { + if (url.SchemeIs("mailto")) + WriteTextToClipboard(UTF8ToWide(url.path())); + else + WriteTextToClipboard(UTF8ToWide(url.spec())); +} + +/////////////////////////////////////////////////////////////////////////////// +// Menu::Delegate methods + +std::wstring RenderViewContextMenuController::GetLabel(int id) const { + switch (id) { + case IDS_CONTENT_CONTEXT_SEARCHWEBFOR: { + const TemplateURL* const default_provider = source_web_contents_-> + profile()->GetTemplateURLModel()->GetDefaultSearchProvider(); + DCHECK(default_provider); // The context menu should not contain this + // item when there is no provider. + return l10n_util::GetStringF(id, default_provider->short_name(), + l10n_util::TruncateString(params_.selection_text, 50)); + } + + case IDS_CONTENT_CONTEXT_COPYLINKLOCATION: + if (params_.link_url.SchemeIs("mailto")) + return l10n_util::GetString(IDS_CONTENT_CONTEXT_COPYEMAILADDRESS); + + default: + return l10n_util::GetString(id); + } +} + +bool RenderViewContextMenuController::IsCommandEnabled(int id) const { + // Allow Spell Check language items on sub menu for text area context menu. + if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) && + (id < IDC_SPELLCHECK_LANGUAGES_LAST)) { + return true; + } + + switch (id) { + case IDS_CONTENT_CONTEXT_BACK: + return source_web_contents_->controller()->CanGoBack(); + + case IDS_CONTENT_CONTEXT_FORWARD: + return source_web_contents_->controller()->CanGoForward(); + + case IDS_CONTENT_CONTEXT_VIEWPAGESOURCE: + case IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE: + case IDS_CONTENT_CONTEXT_INSPECTELEMENT: + return IsDevCommandEnabled(id); + + case IDS_CONTENT_CONTEXT_OPENLINKNEWTAB: + case IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW: + case IDS_CONTENT_CONTEXT_COPYLINKLOCATION: + return params_.link_url.is_valid(); + + case IDS_CONTENT_CONTEXT_SAVELINKAS: + return params_.link_url.is_valid() && + URLRequest::IsHandledURL(params_.link_url); + + case IDS_CONTENT_CONTEXT_SAVEIMAGEAS: + return params_.image_url.is_valid() && + URLRequest::IsHandledURL(params_.image_url); + + case IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB: + case IDS_CONTENT_CONTEXT_COPYIMAGELOCATION: + return params_.image_url.is_valid(); + + case IDS_CONTENT_CONTEXT_SAVEPAGEAS: + return SavePackage::IsSavableURL(source_web_contents_->GetURL()); + + case IDS_CONTENT_CONTEXT_OPENFRAMENEWTAB: + case IDS_CONTENT_CONTEXT_OPENFRAMENEWWINDOW: + return params_.frame_url.is_valid(); + + case IDS_CONTENT_CONTEXT_UNDO: + return !!(params_.edit_flags & ContextNode::CAN_UNDO); + + case IDS_CONTENT_CONTEXT_REDO: + return !!(params_.edit_flags & ContextNode::CAN_REDO); + + case IDS_CONTENT_CONTEXT_CUT: + return !!(params_.edit_flags & ContextNode::CAN_CUT); + + case IDS_CONTENT_CONTEXT_COPY: + return !!(params_.edit_flags & ContextNode::CAN_COPY); + + case IDS_CONTENT_CONTEXT_PASTE: + return !!(params_.edit_flags & ContextNode::CAN_PASTE); + + case IDS_CONTENT_CONTEXT_DELETE: + return !!(params_.edit_flags & ContextNode::CAN_DELETE); + + case IDS_CONTENT_CONTEXT_SELECTALL: + return !!(params_.edit_flags & ContextNode::CAN_SELECT_ALL); + + case IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: + return !source_web_contents_->profile()->IsOffTheRecord() && + params_.link_url.is_valid(); + + case IDS_CONTENT_CONTEXT_OPENFRAMEOFFTHERECORD: + return !source_web_contents_->profile()->IsOffTheRecord() && + params_.frame_url.is_valid(); + + case IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY: + return !params_.misspelled_word.empty(); + + case IDS_CONTENT_CONTEXT_VIEWPAGEINFO: + return (source_web_contents_->controller()->GetActiveEntry() != NULL); + + case IDS_CONTENT_CONTEXT_RELOAD: + case IDS_CONTENT_CONTEXT_COPYIMAGE: + case IDS_CONTENT_CONTEXT_PRINT: + case IDS_CONTENT_CONTEXT_SEARCHWEBFOR: + case IDC_SPELLCHECK_SUGGESTION_0: + case IDC_SPELLCHECK_SUGGESTION_1: + case IDC_SPELLCHECK_SUGGESTION_2: + case IDC_SPELLCHECK_SUGGESTION_3: + case IDC_SPELLCHECK_SUGGESTION_4: + case IDC_SPELLCHECK_MENU: + case IDC_CHECK_SPELLING_OF_THIS_FIELD: + case IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS: + case IDS_CONTENT_CONTEXT_VIEWFRAMEINFO: + return true; + + case IDS_CONTENT_CONTEXT_SAVEFRAMEAS: + case IDS_CONTENT_CONTEXT_PRINTFRAME: + case IDS_CONTENT_CONTEXT_ADDSEARCHENGINE: // Not implemented. + default: + return false; + } +} + +bool RenderViewContextMenuController::IsItemChecked(int id) const { + // Check box for 'Check the Spelling of this field'. + if (id == IDC_CHECK_SPELLING_OF_THIS_FIELD) + return params_.spellcheck_enabled; + + // Don't bother getting the display language vector if this isn't a spellcheck + // language. + if ((id < IDC_SPELLCHECK_LANGUAGES_FIRST) || + (id >= IDC_SPELLCHECK_LANGUAGES_LAST)) + return false; + + SpellChecker::Languages display_languages; + return SpellChecker::GetSpellCheckLanguagesToDisplayInContextMenu( + source_web_contents_->profile(), &display_languages) == + (id - IDC_SPELLCHECK_LANGUAGES_FIRST); +} + +bool RenderViewContextMenuController::GetAcceleratorInfo( + int id, + views::Accelerator* accel) { + // There are no formally defined accelerators we can query so we assume + // that Ctrl+C, Ctrl+V, Ctrl+X, Ctrl-A, etc do what they normally do. + switch (id) { + case IDS_CONTENT_CONTEXT_UNDO: + *accel = views::Accelerator(L'Z', false, true, false); + return true; + + case IDS_CONTENT_CONTEXT_REDO: + *accel = views::Accelerator(L'Z', true, true, false); + return true; + + case IDS_CONTENT_CONTEXT_CUT: + *accel = views::Accelerator(L'X', false, true, false); + return true; + + case IDS_CONTENT_CONTEXT_COPY: + *accel = views::Accelerator(L'C', false, true, false); + return true; + + case IDS_CONTENT_CONTEXT_PASTE: + *accel = views::Accelerator(L'V', false, true, false); + return true; + + case IDS_CONTENT_CONTEXT_SELECTALL: + *accel = views::Accelerator(L'A', false, true, false); + + default: + return false; + } +} + +void RenderViewContextMenuController::ExecuteCommand(int id) { + // Check to see if one of the spell check language ids have been clicked. + if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST && + id < IDC_SPELLCHECK_LANGUAGES_LAST) { + const size_t language_number = id - IDC_SPELLCHECK_LANGUAGES_FIRST; + SpellChecker::Languages display_languages; + SpellChecker::GetSpellCheckLanguagesToDisplayInContextMenu( + source_web_contents_->profile(), &display_languages); + if (language_number < display_languages.size()) { + StringPrefMember dictionary_language; + dictionary_language.Init(prefs::kSpellCheckDictionary, + source_web_contents_->profile()->GetPrefs(), NULL); + dictionary_language.SetValue(display_languages[language_number]); + } + + return; + } + + switch (id) { + case IDS_CONTENT_CONTEXT_OPENLINKNEWTAB: + OpenURL(params_.link_url, NEW_BACKGROUND_TAB, PageTransition::LINK); + break; + + case IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW: + OpenURL(params_.link_url, NEW_WINDOW, PageTransition::LINK); + break; + + case IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD: + OpenURL(params_.link_url, OFF_THE_RECORD, PageTransition::LINK); + break; + + // TODO(paulg): Prompt the user for file name when saving links and images. + case IDS_CONTENT_CONTEXT_SAVEIMAGEAS: + case IDS_CONTENT_CONTEXT_SAVELINKAS: { + const GURL& referrer = + params_.frame_url.is_empty() ? params_.page_url : params_.frame_url; + const GURL& url = id == IDS_CONTENT_CONTEXT_SAVELINKAS ? params_.link_url : + params_.image_url; + DownloadManager* dlm = + source_web_contents_->profile()->GetDownloadManager(); + dlm->DownloadUrl(url, referrer, source_web_contents_); + break; + } + + case IDS_CONTENT_CONTEXT_COPYLINKLOCATION: + WriteURLToClipboard(params_.link_url); + break; + + case IDS_CONTENT_CONTEXT_COPYIMAGELOCATION: + WriteURLToClipboard(params_.image_url); + break; + + case IDS_CONTENT_CONTEXT_COPYIMAGE: + CopyImageAt(params_.x, params_.y); + break; + + case IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB: + OpenURL(params_.image_url, NEW_BACKGROUND_TAB, PageTransition::LINK); + break; + + case IDS_CONTENT_CONTEXT_BACK: + source_web_contents_->controller()->GoBack(); + break; + + case IDS_CONTENT_CONTEXT_FORWARD: + source_web_contents_->controller()->GoForward(); + break; + + case IDS_CONTENT_CONTEXT_SAVEPAGEAS: + source_web_contents_->OnSavePage(); + break; + + case IDS_CONTENT_CONTEXT_RELOAD: + source_web_contents_->controller()->Reload(true); + break; + + case IDS_CONTENT_CONTEXT_PRINT: + source_web_contents_->PrintPreview(); + break; + + case IDS_CONTENT_CONTEXT_VIEWPAGESOURCE: + OpenURL(GURL("view-source:" + params_.page_url.spec()), + NEW_FOREGROUND_TAB, PageTransition::GENERATED); + break; + + case IDS_CONTENT_CONTEXT_INSPECTELEMENT: + Inspect(params_.x, params_.y); + break; + + case IDS_CONTENT_CONTEXT_VIEWPAGEINFO: { + NavigationEntry* nav_entry = + source_web_contents_->controller()->GetActiveEntry(); + PageInfoWindow::CreatePageInfo(source_web_contents_->profile(), + nav_entry, + source_web_contents_->GetContentHWND(), + PageInfoWindow::SECURITY); + break; + } + + case IDS_CONTENT_CONTEXT_OPENFRAMENEWTAB: + OpenURL(params_.frame_url, NEW_BACKGROUND_TAB, PageTransition::LINK); + break; + + case IDS_CONTENT_CONTEXT_OPENFRAMENEWWINDOW: + OpenURL(params_.frame_url, NEW_WINDOW, PageTransition::LINK); + break; + + case IDS_CONTENT_CONTEXT_OPENFRAMEOFFTHERECORD: + OpenURL(params_.frame_url, OFF_THE_RECORD, PageTransition::LINK); + break; + + case IDS_CONTENT_CONTEXT_SAVEFRAMEAS: + win_util::MessageBox(NULL, L"Context Menu Action", L"Save Frame As", + MB_OK); + break; + + case IDS_CONTENT_CONTEXT_PRINTFRAME: + win_util::MessageBox(NULL, L"Context Menu Action", L"Print Frame", + MB_OK); + break; + + case IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE: + OpenURL(GURL("view-source:" + params_.frame_url.spec()), + NEW_FOREGROUND_TAB, PageTransition::GENERATED); + break; + + case IDS_CONTENT_CONTEXT_VIEWFRAMEINFO: { + // Deserialize the SSL info. + NavigationEntry::SSLStatus ssl; + if (!params_.security_info.empty()) { + int cert_id, cert_status, security_bits; + SSLManager::DeserializeSecurityInfo(params_.security_info, + &cert_id, + &cert_status, + &security_bits); + ssl.set_cert_id(cert_id); + ssl.set_cert_status(cert_status); + ssl.set_security_bits(security_bits); + } + PageInfoWindow::CreateFrameInfo(source_web_contents_->profile(), + params_.frame_url, + ssl, + source_web_contents_->GetContentHWND(), + PageInfoWindow::SECURITY); + break; + } + + case IDS_CONTENT_CONTEXT_UNDO: + source_web_contents_->render_view_host()->Undo(); + break; + + case IDS_CONTENT_CONTEXT_REDO: + source_web_contents_->render_view_host()->Redo(); + break; + + case IDS_CONTENT_CONTEXT_CUT: + source_web_contents_->render_view_host()->Cut(); + break; + + case IDS_CONTENT_CONTEXT_COPY: + source_web_contents_->render_view_host()->Copy(); + break; + + case IDS_CONTENT_CONTEXT_PASTE: + source_web_contents_->render_view_host()->Paste(); + break; + + case IDS_CONTENT_CONTEXT_DELETE: + source_web_contents_->render_view_host()->Delete(); + break; + + case IDS_CONTENT_CONTEXT_SELECTALL: + source_web_contents_->render_view_host()->SelectAll(); + break; + + case IDS_CONTENT_CONTEXT_SEARCHWEBFOR: { + const TemplateURL* const default_provider = source_web_contents_-> + profile()->GetTemplateURLModel()->GetDefaultSearchProvider(); + DCHECK(default_provider); // The context menu should not contain this + // item when there is no provider. + const TemplateURLRef* const search_url = default_provider->url(); + DCHECK(search_url->SupportsReplacement()); + OpenURL(GURL(search_url->ReplaceSearchTerms(*default_provider, + params_.selection_text, TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, + std::wstring())), NEW_FOREGROUND_TAB, PageTransition::GENERATED); + break; + } + + case IDC_SPELLCHECK_SUGGESTION_0: + case IDC_SPELLCHECK_SUGGESTION_1: + case IDC_SPELLCHECK_SUGGESTION_2: + case IDC_SPELLCHECK_SUGGESTION_3: + case IDC_SPELLCHECK_SUGGESTION_4: + source_web_contents_->render_view_host()->Replace( + params_.dictionary_suggestions[id - IDC_SPELLCHECK_SUGGESTION_0]); + break; + + case IDC_CHECK_SPELLING_OF_THIS_FIELD: + source_web_contents_->render_view_host()->ToggleSpellCheck(); + break; + case IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY: + source_web_contents_->render_view_host()->AddToDictionary( + params_.misspelled_word); + break; + + case IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS: { + FontsLanguagesWindowView* window_ = new FontsLanguagesWindowView( + source_web_contents_->profile()); + views::Window::CreateChromeWindow(source_web_contents_->GetContentHWND(), + gfx::Rect(), window_)->Show(); + window_->SelectLanguagesTab(); + break; + } + + case IDS_CONTENT_CONTEXT_ADDSEARCHENGINE: // Not implemented. + default: + break; + } +} + +bool RenderViewContextMenuController::IsDevCommandEnabled(int id) const { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kAlwaysEnableDevTools)) + return true; + + NavigationEntry *active_entry = + source_web_contents_->controller()->GetActiveEntry(); + if (!active_entry) + return false; + + // Don't inspect HTML dialogs. + if (source_web_contents_->type() == TAB_CONTENTS_HTML_DIALOG) + return false; + + // Don't inspect view source. + if (source_web_contents_->type() == TAB_CONTENTS_VIEW_SOURCE) + return false; + + // Don't inspect inspector, new tab UI, etc. + if (active_entry->url().SchemeIs("chrome")) + return false; + + // Don't inspect about:network, about:memory, etc. + // However, we do want to inspect about:blank, which is often + // used by ordinary web pages. + if (active_entry->display_url().SchemeIs("about") && + !LowerCaseEqualsASCII(active_entry->display_url().path(), "blank")) + return false; + + // Don't enable the web inspector if JavaScript is disabled + if (id == IDS_CONTENT_CONTEXT_INSPECTELEMENT) { + PrefService* prefs = source_web_contents_->profile()->GetPrefs(); + if (!prefs->GetBoolean(prefs::kWebKitJavascriptEnabled) || + command_line.HasSwitch(switches::kDisableJavaScript)) + return false; + } + + return true; +} + diff --git a/chrome/browser/tab_contents/render_view_context_menu_controller.h b/chrome/browser/tab_contents/render_view_context_menu_controller.h new file mode 100644 index 0000000..89270b5 --- /dev/null +++ b/chrome/browser/tab_contents/render_view_context_menu_controller.h @@ -0,0 +1,56 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_CONTEXT_MENU_CONTROLLER_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_CONTEXT_MENU_CONTROLLER_H_ + +#include "chrome/common/pref_member.h" +#include "chrome/views/menu.h" +#include "chrome/common/render_messages.h" + +class WebContents; + +class RenderViewContextMenuController : public Menu::Delegate { + public: + RenderViewContextMenuController(WebContents* source_web_contents, + const ViewHostMsg_ContextMenu_Params& params); + virtual ~RenderViewContextMenuController(); + + // Overridden from Menu::Delegate + virtual std::wstring GetLabel(int id) const; + virtual bool IsCommandEnabled(int id) const; + virtual bool IsItemChecked(int id) const; + virtual void ExecuteCommand(int id); + virtual bool GetAcceleratorInfo(int id, views::Accelerator* accel); + + private: + // Opens the specified URL string in a new tab. If |in_current_window| is + // false, a new window is created to hold the new tab. + void OpenURL(const GURL& url, + WindowOpenDisposition disposition, + PageTransition::Type transition); + + // Copy to the clipboard an image located at a point in the RenderView + void CopyImageAt(int x, int y); + + // Launch the inspector targeting a point in the RenderView + void Inspect(int x, int y); + + // Writes the specified text/url to the system clipboard + void WriteTextToClipboard(const std::wstring& text); + void WriteURLToClipboard(const GURL& url); + + bool IsDevCommandEnabled(int id) const; + + DISALLOW_EVIL_CONSTRUCTORS(RenderViewContextMenuController); + + private: + WebContents* source_web_contents_; + ViewHostMsg_ContextMenu_Params params_; + StringPrefMember dictionary_language_; + int current_dictionary_language_index_; +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_VIEW_CONTEXT_MENU_CONTROLLER_H_ + diff --git a/chrome/browser/tab_contents/render_view_host_manager.cc b/chrome/browser/tab_contents/render_view_host_manager.cc new file mode 100644 index 0000000..3e235e2 --- /dev/null +++ b/chrome/browser/tab_contents/render_view_host_manager.cc @@ -0,0 +1,503 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/tab_contents/render_view_host_manager.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" +#include "chrome/browser/tab_contents/navigation_controller.h" +#include "chrome/browser/tab_contents/navigation_entry.h" +#include "chrome/browser/tab_contents/site_instance.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" + +namespace base { +class WaitableEvent; +} + +RenderViewHostManager::RenderViewHostManager( + RenderViewHostFactory* render_view_factory, + RenderViewHostDelegate* render_view_delegate, + Delegate* delegate) + : delegate_(delegate), + cross_navigation_pending_(false), + render_view_factory_(render_view_factory), + render_view_delegate_(render_view_delegate), + render_view_host_(NULL), + pending_render_view_host_(NULL), + interstitial_page_(NULL) { +} + +RenderViewHostManager::~RenderViewHostManager() { + // Shutdown should have been called which should have cleaned these up. + DCHECK(!render_view_host_); + DCHECK(!pending_render_view_host_); +} + +void RenderViewHostManager::Init(Profile* profile, + SiteInstance* site_instance, + int routing_id, + base::WaitableEvent* modal_dialog_event) { + // Create a RenderViewHost, once we have an instance. It is important to + // immediately give this SiteInstance to a RenderViewHost so that it is + // ref counted. + if (!site_instance) + site_instance = SiteInstance::CreateSiteInstance(profile); + render_view_host_ = CreateRenderViewHost( + site_instance, routing_id, modal_dialog_event); +} + +void RenderViewHostManager::Shutdown() { + if (pending_render_view_host_) + CancelPendingRenderView(); + + // We should always have a main RenderViewHost. + render_view_host_->Shutdown(); + render_view_host_ = NULL; +} + +RenderViewHost* RenderViewHostManager::Navigate(const NavigationEntry& entry) { + RenderViewHost* dest_render_view_host = UpdateRendererStateNavigate(entry); + if (!dest_render_view_host) + return NULL; // We weren't able to create a pending render view host. + + // If the current render_view_host_ isn't live, we should create it so + // that we don't show a sad tab while the dest_render_view_host fetches + // its first page. (Bug 1145340) + if (dest_render_view_host != render_view_host_ && + !render_view_host_->IsRenderViewLive()) { + delegate_->CreateRenderViewForRenderManager(render_view_host_); + } + + // If the renderer crashed, then try to create a new one to satisfy this + // navigation request. + if (!dest_render_view_host->IsRenderViewLive()) { + if (!delegate_->CreateRenderViewForRenderManager(dest_render_view_host)) + return NULL; + + // Now that we've created a new renderer, be sure to hide it if it isn't + // our primary one. Otherwise, we might crash if we try to call Show() + // on it later. + if (dest_render_view_host != render_view_host_ && + dest_render_view_host->view()) { + dest_render_view_host->view()->Hide(); + } else { + // This is our primary renderer, notify here as we won't be calling + // SwapToRenderView (which does the notify). + RenderViewHostSwitchedDetails details; + details.new_host = render_view_host_; + details.old_host = NULL; + NotificationService::current()->Notify( + NOTIFY_RENDER_VIEW_HOST_CHANGED, + Source<NavigationController>( + delegate_->GetControllerForRenderManager()), + Details<RenderViewHostSwitchedDetails>(&details)); + } + } + + return dest_render_view_host; +} + +void RenderViewHostManager::Stop() { + render_view_host_->Stop(); + + // If we are cross-navigating, we should stop the pending renderers. This + // will lead to a DidFailProvisionalLoad, which will properly destroy them. + if (cross_navigation_pending_) { + pending_render_view_host_->Stop(); + + } +} + +void RenderViewHostManager::SetIsLoading(bool is_loading) { + render_view_host_->SetIsLoading(is_loading); + if (pending_render_view_host_) + pending_render_view_host_->SetIsLoading(is_loading); +} + +bool RenderViewHostManager::ShouldCloseTabOnUnresponsiveRenderer() { + if (!cross_navigation_pending_) + return true; + + // If the tab becomes unresponsive during unload while doing a + // crosssite navigation, proceed with the navigation. + int pending_request_id = pending_render_view_host_->GetPendingRequestId(); + if (pending_request_id == -1) { + // Haven't gotten around to starting the request. + pending_render_view_host_->SetNavigationsSuspended(false); + } else { + current_host()->process()->CrossSiteClosePageACK( + pending_render_view_host_->site_instance()->process_host_id(), + pending_request_id); + DidNavigateMainFrame(pending_render_view_host_); + } + return false; +} + +void RenderViewHostManager::DidNavigateMainFrame( + RenderViewHost* render_view_host) { + if (!cross_navigation_pending_) { + // We should only hear this from our current renderer. + DCHECK(render_view_host == render_view_host_); + return; + } + + if (render_view_host == pending_render_view_host_) { + // The pending cross-site navigation completed, so show the renderer. + SwapToRenderView(&pending_render_view_host_, true); + cross_navigation_pending_ = false; + } else if (render_view_host == render_view_host_) { + // A navigation in the original page has taken place. Cancel the pending + // one. + CancelPendingRenderView(); + cross_navigation_pending_ = false; + } else { + // No one else should be sending us DidNavigate in this state. + DCHECK(false); + } +} + +void RenderViewHostManager::OnCrossSiteResponse(int new_render_process_host_id, + int new_request_id) { + // Should only see this while we have a pending renderer. + if (!cross_navigation_pending_) + return; + DCHECK(pending_render_view_host_); + + // Tell the old renderer to run its onunload handler. When it finishes, it + // will send a ClosePage_ACK to the ResourceDispatcherHost with the given + // IDs (of the pending RVH's request), allowing the pending RVH's response to + // resume. + render_view_host_->ClosePage(new_render_process_host_id, new_request_id); + + // ResourceDispatcherHost has told us to run the onunload handler, which + // means it is not a download or unsafe page, and we are going to perform the + // navigation. Thus, we no longer need to remember that the RenderViewHost + // is part of a pending cross-site request. + pending_render_view_host_->SetHasPendingCrossSiteRequest(false, + new_request_id); +} + +void RenderViewHostManager::RendererAbortedProvisionalLoad( + RenderViewHost* render_view_host) { + // We used to cancel the pending renderer here for cross-site downloads. + // However, it's not safe to do that because the download logic repeatedly + // looks for this TabContents based on a render view ID. Instead, we just + // leave the pending renderer around until the next navigation event + // (Navigate, DidNavigate, etc), which will clean it up properly. + // TODO(creis): All of this will go away when we move the cross-site logic + // to ResourceDispatcherHost, so that we intercept responses rather than + // navigation events. (That's necessary to support onunload anyway.) Once + // we've made that change, we won't create a pending renderer until we know + // the response is not a download. +} + +void RenderViewHostManager::ShouldClosePage(bool proceed) { + // Should only see this while we have a pending renderer. Otherwise, we + // should ignore. + if (!pending_render_view_host_) { + bool proceed_to_fire_unload; + delegate_->BeforeUnloadFiredFromRenderManager(proceed, + &proceed_to_fire_unload); + + if (proceed_to_fire_unload) { + // This is not a cross-site navigation, the tab is being closed. + render_view_host_->FirePageUnload(); + } + return; + } + + if (proceed) { + // Ok to unload the current page, so proceed with the cross-site navigate. + pending_render_view_host_->SetNavigationsSuspended(false); + } else { + // Current page says to cancel. + CancelPendingRenderView(); + cross_navigation_pending_ = false; + } +} + +void RenderViewHostManager::OnJavaScriptMessageBoxClosed( + IPC::Message* reply_msg, + bool success, + const std::wstring& prompt) { + render_view_host_->JavaScriptMessageBoxClosed(reply_msg, success, prompt); +} + + +bool RenderViewHostManager::ShouldTransitionCrossSite() { + // True if we are using process-per-site-instance (default) or + // process-per-site (kProcessPerSite). + return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab); +} + +SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( + const NavigationEntry& entry, + SiteInstance* curr_instance) { + // NOTE: This is only called when ShouldTransitionCrossSite is true. + + // If the entry has an instance already, we should use it. + if (entry.site_instance()) + return entry.site_instance(); + + // (UGLY) HEURISTIC, process-per-site only: + // + // If this navigation is generated, then it probably corresponds to a search + // query. Given that search results typically lead to users navigating to + // other sites, we don't really want to use the search engine hostname to + // determine the site instance for this navigation. + // + // NOTE: This can be removed once we have a way to transition between + // RenderViews in response to a link click. + // + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerSite) && + entry.transition_type() == PageTransition::GENERATED) + return curr_instance; + + const GURL& dest_url = entry.url(); + + // If we haven't used our SiteInstance (and thus RVH) yet, then we can use it + // for this entry. We won't commit the SiteInstance to this site until the + // navigation commits (in DidNavigate), unless the navigation entry was + // restored. As session restore loads all the pages immediately we need to set + // the site first, otherwise after a restore none of the pages would share + // renderers. + if (!curr_instance->has_site()) { + // If we've already created a SiteInstance for our destination, we don't + // want to use this unused SiteInstance; use the existing one. (We don't + // do this check if the curr_instance has a site, because for now, we want + // to compare against the current URL and not the SiteInstance's site. In + // this case, there is no current URL, so comparing against the site is ok. + // See additional comments below.) + if (curr_instance->HasRelatedSiteInstance(dest_url)) { + return curr_instance->GetRelatedSiteInstance(dest_url); + } else { + if (entry.restored()) + curr_instance->SetSite(dest_url); + return curr_instance; + } + } + + // Otherwise, only create a new SiteInstance for cross-site navigation. + + // TODO(creis): Once we intercept links and script-based navigations, we + // will be able to enforce that all entries in a SiteInstance actually have + // the same site, and it will be safe to compare the URL against the + // SiteInstance's site, as follows: + // const GURL& current_url = curr_instance->site(); + // For now, though, we're in a hybrid model where you only switch + // SiteInstances if you type in a cross-site URL. This means we have to + // compare the entry's URL to the last committed entry's URL. + NavigationController* controller = delegate_->GetControllerForRenderManager(); + NavigationEntry* curr_entry = controller->GetLastCommittedEntry(); + if (interstitial_page_) { + // The interstitial is currently the last committed entry, but we want to + // compare against the last non-interstitial entry. + curr_entry = controller->GetEntryAtOffset(-1); + } + // If there is no last non-interstitial entry (and curr_instance already + // has a site), then we must have been opened from another tab. We want + // to compare against the URL of the page that opened us, but we can't + // get to it directly. The best we can do is check against the site of + // the SiteInstance. This will be correct when we intercept links and + // script-based navigations, but for now, it could place some pages in a + // new process unnecessarily. We should only hit this case if a page tries + // to open a new tab to an interstitial-inducing URL, and then navigates + // the page to a different same-site URL. (This seems very unlikely in + // practice.) + const GURL& current_url = (curr_entry) ? curr_entry->url() : + curr_instance->site(); + + if (SiteInstance::IsSameWebSite(current_url, dest_url)) { + return curr_instance; + } else { + // Start the new renderer in a new SiteInstance, but in the current + // BrowsingInstance. It is important to immediately give this new + // SiteInstance to a RenderViewHost (if it is different than our current + // SiteInstance), so that it is ref counted. This will happen in + // CreatePendingRenderView. + return curr_instance->GetRelatedSiteInstance(dest_url); + } +} + +bool RenderViewHostManager::CreatePendingRenderView(SiteInstance* instance) { + NavigationEntry* curr_entry = + delegate_->GetControllerForRenderManager()->GetLastCommittedEntry(); + if (curr_entry && curr_entry->tab_type() == TAB_CONTENTS_WEB) { + DCHECK(!curr_entry->content_state().empty()); + + // TODO(creis): Should send a message to the RenderView to let it know + // we're about to switch away, so that it sends an UpdateState message. + } + + pending_render_view_host_ = + CreateRenderViewHost(instance, MSG_ROUTING_NONE, NULL); + + bool success = delegate_->CreateRenderViewForRenderManager( + pending_render_view_host_); + if (success) { + // Don't show the view until we get a DidNavigate from it. + pending_render_view_host_->view()->Hide(); + } else { + CancelPendingRenderView(); + } + return success; +} + +RenderViewHost* RenderViewHostManager::CreateRenderViewHost( + SiteInstance* instance, + int routing_id, + base::WaitableEvent* modal_dialog_event) { + if (render_view_factory_) { + return render_view_factory_->CreateRenderViewHost( + instance, render_view_delegate_, routing_id, modal_dialog_event); + } else { + return new RenderViewHost(instance, render_view_delegate_, routing_id, + modal_dialog_event); + } +} + +void RenderViewHostManager::SwapToRenderView( + RenderViewHost** new_render_view_host, + bool destroy_after) { + // Remember if the page was focused so we can focus the new renderer in + // that case. + bool focus_render_view = render_view_host_->view() && + render_view_host_->view()->HasFocus(); + + // Hide the current view and prepare to destroy it. + // TODO(creis): Get the old RenderViewHost to send us an UpdateState message + // before we destroy it. + if (render_view_host_->view()) + render_view_host_->view()->Hide(); + RenderViewHost* old_render_view_host = render_view_host_; + + // Swap in the pending view and make it active. + render_view_host_ = (*new_render_view_host); + (*new_render_view_host) = NULL; + + // If the view is gone, then this RenderViewHost died while it was hidden. + // We ignored the RendererGone call at the time, so we should send it now + // to make sure the sad tab shows up, etc. + if (render_view_host_->view()) + render_view_host_->view()->Show(); + else + delegate_->RendererGoneFromRenderManager(render_view_host_); + + // Make sure the size is up to date. (Fix for bug 1079768.) + delegate_->UpdateRenderViewSizeForRenderManager(); + + if (focus_render_view && render_view_host_->view()) + render_view_host_->view()->Focus(); + + RenderViewHostSwitchedDetails details; + details.new_host = render_view_host_; + details.old_host = old_render_view_host; + NotificationService::current()->Notify( + NOTIFY_RENDER_VIEW_HOST_CHANGED, + Source<NavigationController>(delegate_->GetControllerForRenderManager()), + Details<RenderViewHostSwitchedDetails>(&details)); + + if (destroy_after) + old_render_view_host->Shutdown(); + + // Let the task manager know that we've swapped RenderViewHosts, since it + // might need to update its process groupings. + delegate_->NotifySwappedFromRenderManager(); +} + +RenderViewHost* RenderViewHostManager::UpdateRendererStateNavigate( + const NavigationEntry& entry) { + // If we are cross-navigating, then we want to get back to normal and navigate + // as usual. + if (cross_navigation_pending_) { + if (pending_render_view_host_) + CancelPendingRenderView(); + cross_navigation_pending_ = false; + } + + // render_view_host_ will not be deleted before the end of this method, so we + // don't have to worry about this SiteInstance's ref count dropping to zero. + SiteInstance* curr_instance = render_view_host_->site_instance(); + + // Determine if we need a new SiteInstance for this entry. + // Again, new_instance won't be deleted before the end of this method, so it + // is safe to use a normal pointer here. + SiteInstance* new_instance = curr_instance; + if (ShouldTransitionCrossSite()) + new_instance = GetSiteInstanceForEntry(entry, curr_instance); + + if (new_instance != curr_instance) { + // New SiteInstance. + DCHECK(!cross_navigation_pending_); + + // Create a pending RVH and navigate it. + bool success = CreatePendingRenderView(new_instance); + if (!success) + return NULL; + + // Check if our current RVH is live before we set up a transition. + if (!render_view_host_->IsRenderViewLive()) { + if (!cross_navigation_pending_) { + // The current RVH is not live. There's no reason to sit around with a + // sad tab or a newly created RVH while we wait for the pending RVH to + // navigate. Just switch to the pending RVH now and go back to non + // cross-navigating (Note that we don't care about on{before}unload + // handlers if the current RVH isn't live.) + SwapToRenderView(&pending_render_view_host_, true); + return render_view_host_; + } else { + NOTREACHED(); + return render_view_host_; + } + } + // Otherwise, it's safe to treat this as a pending cross-site transition. + + // Make sure the old render view stops, in case a load is in progress. + render_view_host_->Stop(); + + // Suspend the new render view (i.e., don't let it send the cross-site + // Navigate message) until we hear back from the old renderer's + // onbeforeunload handler. If it returns false, we'll have to cancel the + // request. + pending_render_view_host_->SetNavigationsSuspended(true); + + // Tell the CrossSiteRequestManager that this RVH has a pending cross-site + // request, so that ResourceDispatcherHost will know to tell us to run the + // old page's onunload handler before it sends the response. + pending_render_view_host_->SetHasPendingCrossSiteRequest(true, -1); + + // We now have a pending RVH. + DCHECK(!cross_navigation_pending_); + cross_navigation_pending_ = true; + + // Tell the old render view to run its onbeforeunload handler, since it + // doesn't otherwise know that the cross-site request is happening. This + // will trigger a call to ShouldClosePage with the reply. + render_view_host_->FirePageBeforeUnload(); + + return pending_render_view_host_; + } + + // Same SiteInstance can be used. Navigate render_view_host_ if we are not + // cross navigating. + DCHECK(!cross_navigation_pending_); + return render_view_host_; +} + +void RenderViewHostManager::CancelPendingRenderView() { + pending_render_view_host_->Shutdown(); + pending_render_view_host_ = NULL; +} + +void RenderViewHostManager::CrossSiteNavigationCanceled() { + DCHECK(cross_navigation_pending_); + cross_navigation_pending_ = false; + if (pending_render_view_host_) + CancelPendingRenderView(); +} + diff --git a/chrome/browser/tab_contents/render_view_host_manager.h b/chrome/browser/tab_contents/render_view_host_manager.h new file mode 100644 index 0000000..29a9f8f --- /dev/null +++ b/chrome/browser/tab_contents/render_view_host_manager.h @@ -0,0 +1,234 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_TAB_CONTENTS_RENDER_VIEW_HOST_MANAGER_H_ +#define CHROME_BROWSER_TAB_CONTENTS_RENDER_VIEW_HOST_MANAGER_H_ + +#include <windows.h> + +#include <string> + +#include "base/basictypes.h" +#include "chrome/browser/renderer_host/render_view_host.h" + +class InterstitialPage; +class NavigationController; +class NavigationEntry; +class Profile; +class RenderViewHostDelegate; +class RenderViewHostFactory; +class RenderWidgetHostView; +class SiteInstance; + +// Manages RenderViewHosts for a WebContents. Normally there is only one and +// it is easy to do. But we can also have transitions of processes (and hence +// RenderViewHosts) that can get complex. +class RenderViewHostManager { + public: + // Functions implemented by our owner that we need. + // + // TODO(brettw) Clean this up! These are all the functions in WebContents that + // are required to run this class. The design should probably be better such + // that these are more clear. + // + // There is additional complexity that some of the functions we need in + // WebContents are inherited and non-virtual. These are named with + // "RenderManager" so that the duplicate implementation of them will be clear. + class Delegate { + public: + // See web_contents.h's implementation for more. + virtual bool CreateRenderViewForRenderManager( + RenderViewHost* render_view_host) = 0; + virtual void BeforeUnloadFiredFromRenderManager( + bool proceed, bool* proceed_to_fire_unload) = 0; + virtual void DidStartLoadingFromRenderManager( + RenderViewHost* render_view_host, int32 page_id) = 0; + virtual void RendererGoneFromRenderManager( + RenderViewHost* render_view_host) = 0; + virtual void UpdateRenderViewSizeForRenderManager() = 0; + virtual void NotifySwappedFromRenderManager() = 0; + virtual NavigationController* GetControllerForRenderManager() = 0; + }; + + // The factory is optional. It is used by unit tests to supply custom render + // view hosts. When NULL, the regular RenderViewHost will be created. + // + // Both delegate pointers must be non-NULL and are not owned by this class. + // They must outlive this class. The RenderViewHostDelegate is what will be + // installed into all RenderViewHosts that are created. + // + // You must call Init() before using this class and Shutdown() before + // deleting it. + RenderViewHostManager(RenderViewHostFactory* render_view_factory, + RenderViewHostDelegate* render_view_delegate, + Delegate* delegate); + ~RenderViewHostManager(); + + // For arguments, see WebContents constructor. + void Init(Profile* profile, + SiteInstance* site_instance, + int routing_id, + base::WaitableEvent* modal_dialog_event); + + // Schedules all RenderViewHosts for destruction. + void Shutdown(); + + // Returns the currently actuive RenderViewHost. + // + // This will be non-NULL between Init() and Shutdown(). You may want to NULL + // check it in many cases, however. Windows can send us messages during the + // destruction process after it has been shut down. + RenderViewHost* current_host() const { + return render_view_host_; + } + + // Returns the view associated with the current RenderViewHost, or NULL if + // there is no current one. + RenderWidgetHostView* current_view() const { + if (!render_view_host_) + return NULL; + return render_view_host_->view(); + } + + // Called when we want to instruct the renderer to navigate to the given + // navigation entry. It may create a new RenderViewHost or re-use an existing + // one. The RenderViewHost to navigate will be returned. Returns NULL if one + // could not be created. + RenderViewHost* Navigate(const NavigationEntry& entry); + + // Instructs the various live views to stop. Called when the user directed the + // page to stop loading. + void Stop(); + + // Notifies the regular and pending RenderViewHosts that a load is or is not + // happening. Even though the message is only for one of them, we don't know + // which one so we tell both. + void SetIsLoading(bool is_loading); + + // Whether to close the tab or not when there is a hang during an unload + // handler. If we are mid-crosssite navigation, then we should proceed + // with the navigation instead of closing the tab. + bool ShouldCloseTabOnUnresponsiveRenderer(); + + // Called when a renderer's main frame navigates. + void DidNavigateMainFrame(RenderViewHost* render_view_host); + + // 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); + + // Notifies that the navigation that initiated a cross-site transition has + // been canceled. + void CrossSiteNavigationCanceled(); + + // Called when a provisional load on the given renderer is aborted. + void RendererAbortedProvisionalLoad(RenderViewHost* render_view_host); + + // Actually implements this RenderViewHostDelegate function for the + // WebContents. + void ShouldClosePage(bool proceed); + + // Forwards the message to the RenderViewHost, which is the original one. + void OnJavaScriptMessageBoxClosed(IPC::Message* reply_msg, + bool success, + const std::wstring& prompt); + + // Sets the passed passed interstitial as the currently showing interstitial. + // |interstitial_page| should be non NULL (use the remove_interstitial_page + // method to unset the interstitial) and no interstitial page should be set + // when there is already a non NULL interstitial page set. + void set_interstitial_page(InterstitialPage* interstitial_page) { + DCHECK(!interstitial_page_ && interstitial_page); + interstitial_page_ = interstitial_page; + } + + // Unsets the currently showing interstitial. + void remove_interstitial_page() { + DCHECK(interstitial_page_); + interstitial_page_ = NULL; + } + + // Returns the currently showing interstitial, NULL if no interstitial is + // showing. + InterstitialPage* interstitial_page() const { + return interstitial_page_; + } + + private: + friend class TestWebContents; + + // Returns whether this tab should transition to a new renderer for + // cross-site URLs. Enabled unless we see the --process-per-tab command line + // switch. Can be overridden in unit tests. + bool ShouldTransitionCrossSite(); + + // Returns an appropriate SiteInstance object for the given NavigationEntry, + // possibly reusing the current SiteInstance. + // Never called if --process-per-tab is used. + SiteInstance* GetSiteInstanceForEntry(const NavigationEntry& entry, + SiteInstance* curr_instance); + + // Helper method to create a pending RenderViewHost for a cross-site + // navigation. + bool CreatePendingRenderView(SiteInstance* instance); + + // Creates a RenderViewHost using render_view_factory_ (or directly, if the + // factory is NULL). + RenderViewHost* CreateRenderViewHost(SiteInstance* instance, + int routing_id, + base::WaitableEvent* modal_dialog_event); + + // Replaces the currently shown render_view_host_ with the RenderViewHost in + // the field pointed to by |new_render_view_host|, and then NULLs the field. + // Callers should only pass pointers to the pending_render_view_host_, + // interstitial_render_view_host_, or original_render_view_host_ fields of + // this object. If |destroy_after|, this method will call + // ScheduleDeferredDestroy on the previous render_view_host_. + void SwapToRenderView(RenderViewHost** new_render_view_host, + bool destroy_after); + + // Helper method to terminate the pending RenderViewHost. + void CancelPendingRenderView(); + + RenderViewHost* UpdateRendererStateNavigate(const NavigationEntry& entry); + + // Our delegate, not owned by us. Guaranteed non-NULL. + Delegate* delegate_; + + // Allows tests to create their own render view host types. + RenderViewHostFactory* render_view_factory_; + + // Implemented by the owner of this class, this delegate is installed into all + // the RenderViewHosts that we create. + RenderViewHostDelegate* render_view_delegate_; + + // Our RenderView host. This object is responsible for all communication with + // a child RenderView instance. + RenderViewHost* render_view_host_; + + // A RenderViewHost used to load a cross-site page. This remains hidden + // while a cross-site request is pending until it calls DidNavigate. + RenderViewHost* pending_render_view_host_; + + // The intersitial page currently shown if any, not own by this class + // (the InterstitialPage is self-owned, it deletes itself when hidden). + InterstitialPage* interstitial_page_; + + // Whether a cross-site request is pending (in the new process model). + bool cross_navigation_pending_; + + DISALLOW_COPY_AND_ASSIGN(RenderViewHostManager); +}; + +// The "details" for a NOTIFY_RENDER_VIEW_HOST_CHANGED notification. The old +// host can be NULL when the first RenderViewHost is set. +struct RenderViewHostSwitchedDetails { + RenderViewHost* old_host; + RenderViewHost* new_host; +}; + +#endif // CHROME_BROWSER_TAB_CONTENTS_RENDER_VIEW_HOST_MANAGER_H_ + diff --git a/chrome/browser/tab_contents/tab_util.cc b/chrome/browser/tab_contents/tab_util.cc index 8cea595..9943279 100644 --- a/chrome/browser/tab_contents/tab_util.cc +++ b/chrome/browser/tab_contents/tab_util.cc @@ -4,8 +4,8 @@ #include "chrome/browser/tab_contents/tab_util.h" -#include "chrome/browser/render_view_host.h" #include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/browser/tab_contents/web_contents.h" #include "net/url_request/url_request.h" diff --git a/chrome/browser/tab_contents/view_source_contents.cc b/chrome/browser/tab_contents/view_source_contents.cc index 849a005..291b0c3 100644 --- a/chrome/browser/tab_contents/view_source_contents.cc +++ b/chrome/browser/tab_contents/view_source_contents.cc @@ -4,8 +4,8 @@ #include "chrome/browser/tab_contents/view_source_contents.h" +#include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/navigation_entry.h" -#include "chrome/browser/render_view_host.h" ViewSourceContents::ViewSourceContents(Profile* profile, SiteInstance* instance) : WebContents(profile, instance, NULL, MSG_ROUTING_NONE, NULL) { diff --git a/chrome/browser/tab_contents/web_contents.cc b/chrome/browser/tab_contents/web_contents.cc index 9062894..cf36173 100644 --- a/chrome/browser/tab_contents/web_contents.cc +++ b/chrome/browser/tab_contents/web_contents.cc @@ -28,8 +28,8 @@ #include "chrome/browser/plugin_installer.h" #include "chrome/browser/plugin_service.h" #include "chrome/browser/printing/print_job.h" -#include "chrome/browser/render_view_host.h" -#include "chrome/browser/render_widget_host_view_win.h" // TODO(brettw) delete me. +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view_win.h" // TODO(brettw) delete me. #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" diff --git a/chrome/browser/tab_contents/web_contents.h b/chrome/browser/tab_contents/web_contents.h index 9600a15..c3094a4 100644 --- a/chrome/browser/tab_contents/web_contents.h +++ b/chrome/browser/tab_contents/web_contents.h @@ -9,8 +9,8 @@ #include "chrome/browser/download/save_package.h" #include "chrome/browser/fav_icon_helper.h" #include "chrome/browser/printing/print_view_manager.h" -#include "chrome/browser/render_view_host_delegate.h" -#include "chrome/browser/render_view_host_manager.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" +#include "chrome/browser/tab_contents/render_view_host_manager.h" #include "chrome/browser/shell_dialogs.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/web_app.h" diff --git a/chrome/browser/tab_contents/web_contents_unittest.cc b/chrome/browser/tab_contents/web_contents_unittest.cc index 211d1e8..60f795e 100644 --- a/chrome/browser/tab_contents/web_contents_unittest.cc +++ b/chrome/browser/tab_contents/web_contents_unittest.cc @@ -3,9 +3,9 @@ // found in the LICENSE file. #include "base/logging.h" -#include "chrome/browser/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/renderer_host/test_render_view_host.h" -#include "chrome/browser/render_widget_host_view.h" #include "chrome/browser/tab_contents/interstitial_page.h" #include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/navigation_entry.h" @@ -887,8 +887,6 @@ TEST_F(WebContentsTest, ShowInterstitialThenNavigate) { EXPECT_EQ(TestInterstitialPage::CANCELED, state); } -// TODO(brettw) fix this test. -#if 0 // Test navigating to a page that shows an interstitial, then close the tab. TEST_F(WebContentsTest, ShowInterstitialThenCloseTab) { // Show interstitial. @@ -904,10 +902,10 @@ TEST_F(WebContentsTest, ShowInterstitialThenCloseTab) { // Now close the tab. contents()->CloseContents(); + ContentsCleanedUp(); EXPECT_TRUE(deleted); EXPECT_EQ(TestInterstitialPage::CANCELED, state); } -#endif // Test that after Proceed is called and an interstitial is still shown, no more // commands get executed. diff --git a/chrome/browser/tab_contents/web_contents_view.cc b/chrome/browser/tab_contents/web_contents_view.cc index c3c8e8b..eee0d98 100644 --- a/chrome/browser/tab_contents/web_contents_view.cc +++ b/chrome/browser/tab_contents/web_contents_view.cc @@ -4,7 +4,7 @@ #include "chrome/browser/tab_contents/web_contents_view.h" -#include "chrome/browser/render_widget_host.h" +#include "chrome/browser/renderer_host/render_widget_host.h" void WebContentsView::RenderWidgetHostDestroyed(RenderWidgetHost* host) { for (PendingWidgetViews::iterator i = pending_widget_views_.begin(); diff --git a/chrome/browser/tab_contents/web_contents_view.h b/chrome/browser/tab_contents/web_contents_view.h index bec1064..12f4ab8 100644 --- a/chrome/browser/tab_contents/web_contents_view.h +++ b/chrome/browser/tab_contents/web_contents_view.h @@ -13,7 +13,7 @@ #include "base/basictypes.h" #include "base/gfx/rect.h" #include "base/gfx/size.h" -#include "chrome/browser/render_view_host_delegate.h" +#include "chrome/browser/renderer_host/render_view_host_delegate.h" class Browser; class RenderViewHost; diff --git a/chrome/browser/tab_contents/web_contents_view_win.cc b/chrome/browser/tab_contents/web_contents_view_win.cc index 54eb162..5f1016a 100644 --- a/chrome/browser/tab_contents/web_contents_view_win.cc +++ b/chrome/browser/tab_contents/web_contents_view_win.cc @@ -10,10 +10,10 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_request_manager.h" -#include "chrome/browser/render_view_context_menu.h" -#include "chrome/browser/render_view_context_menu_controller.h" -#include "chrome/browser/render_view_host.h" -#include "chrome/browser/render_widget_host_view_win.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view_win.h" +#include "chrome/browser/tab_contents/render_view_context_menu.h" +#include "chrome/browser/tab_contents/render_view_context_menu_controller.h" #include "chrome/browser/tab_contents/interstitial_page.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" #include "chrome/browser/tab_contents/web_contents.h" diff --git a/chrome/browser/tab_contents/web_drag_source.cc b/chrome/browser/tab_contents/web_drag_source.cc index b47913b..c1061ee 100644 --- a/chrome/browser/tab_contents/web_drag_source.cc +++ b/chrome/browser/tab_contents/web_drag_source.cc @@ -8,7 +8,7 @@ #include "chrome/browser/tab_contents/web_drag_source.h" -#include "chrome/browser/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" namespace { diff --git a/chrome/browser/tab_contents/web_drop_target.cc b/chrome/browser/tab_contents/web_drop_target.cc index c3220e5..90ac002 100644 --- a/chrome/browser/tab_contents/web_drop_target.cc +++ b/chrome/browser/tab_contents/web_drop_target.cc @@ -9,7 +9,7 @@ #include "base/clipboard_util.h" #include "base/gfx/point.h" -#include "chrome/browser/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/web_contents.h" #include "chrome/common/os_exchange_data.h" #include "googleurl/src/gurl.h" |