diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-04 19:22:13 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-04 19:22:13 +0000 |
commit | 66ba493b842d92a7d9e515302d585d1c56848ee0 (patch) | |
tree | 59f303d8f54c0447268302fdabce7c873fe2c09b /chrome | |
parent | 33b28a75192e79cff41ecbe82defd65ad5da9535 (diff) | |
download | chromium_src-66ba493b842d92a7d9e515302d585d1c56848ee0.zip chromium_src-66ba493b842d92a7d9e515302d585d1c56848ee0.tar.gz chromium_src-66ba493b842d92a7d9e515302d585d1c56848ee0.tar.bz2 |
Relanding the first of probably several patches trying to clean up the BlockedPopupContainer into something that can be cross-platform.
- BlokedPopupContainers are no longer ConstrainedWindows.
- There is now a cross platform base class that contains most of the model/controller logic. The view now inherits from it. This is an improvement.
This version has minor windows compile fixes that changed from under me.
Original Review URL: http://codereview.chromium.org/119006
TBR=beng
TEST=Popup notification still works.
Review URL: http://codereview.chromium.org/119107
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17647 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
17 files changed, 797 insertions, 674 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 7fce063..e61882d 100644..100755 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -21,6 +21,7 @@ #include "chrome/browser/automation/url_request_failed_dns_job.h" #include "chrome/browser/automation/url_request_mock_http_job.h" #include "chrome/browser/automation/url_request_slow_download_job.h" +#include "chrome/browser/blocked_popup_container.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/download/download_manager.h" @@ -1084,6 +1085,7 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(AutomationMsg_SetEnableExtensionAutomation, SetEnableExtensionAutomation) IPC_MESSAGE_HANDLER(AutomationMsg_SetShelfVisibility, SetShelfVisibility) + IPC_MESSAGE_HANDLER(AutomationMsg_BlockedPopupCount, GetBlockedPopupCount) IPC_END_MESSAGE_MAP() } @@ -3004,3 +3006,22 @@ void AutomationProvider::GetWindowTitle(int handle, string16* text) { gfx::NativeWindow window = window_tracker_->GetResource(handle); text->assign(platform_util::GetWindowTitle(window)); } + +void AutomationProvider::GetBlockedPopupCount(int handle, int* count) { + *count = -1; // -1 is the error code + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* nav_controller = tab_tracker_->GetResource(handle); + TabContents* tab_contents = nav_controller->tab_contents(); + if (tab_contents) { + BlockedPopupContainer* container = + tab_contents->blocked_popup_container(); + if (container) { + *count = static_cast<int>(container->GetBlockedPopupCount()); + } else { + // If we don't have a container, we don't have any blocked popups to + // contain! + *count = 0; + } + } + } +} diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 8673bed..04a6bd8 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -452,6 +452,9 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, void GetWindowTitle(int handle, string16* text); + // Returns the number of blocked popups in the tab |handle|. + void GetBlockedPopupCount(int handle, int* count); + // Convert a tab handle into a TabContents. If |tab| is non-NULL a pointer // to the tab is also returned. Returns NULL in case of failure or if the tab // is not of the TabContents type. diff --git a/chrome/browser/blocked_popup_container.cc b/chrome/browser/blocked_popup_container.cc new file mode 100644 index 0000000..ad9c7c6 --- /dev/null +++ b/chrome/browser/blocked_popup_container.cc @@ -0,0 +1,337 @@ +// Copyright (c) 2009 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/blocked_popup_container.h" + +#include "chrome/browser/extensions/extension_function_dispatcher.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/common/notification_service.h" + +// static +void BlockedPopupContainer::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterListPref(prefs::kPopupWhitelistedHosts); +} + +void BlockedPopupContainer::AddTabContents(TabContents* tab_contents, + const gfx::Rect& bounds, + const std::string& host) { + // Show whitelisted popups immediately. + bool whitelisted = !!whitelist_.count(host); + if (whitelisted) + owner_->AddNewContents(tab_contents, NEW_POPUP, bounds, true, GURL()); + + if (has_been_dismissed_) { + // Don't want to show any other UI. + if (!whitelisted) + delete tab_contents; // Discard blocked popups entirely. + return; + } + + if (whitelisted) { + // Listen for this popup's destruction, so if the user closes it manually, + // we'll know to stop caring about it. + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(tab_contents)); + + unblocked_popups_[tab_contents] = host; + } else { + if (blocked_popups_.size() >= kImpossibleNumberOfPopups) { + delete tab_contents; + LOG(INFO) << "Warning: Renderer is sending more popups to us than should " + "be possible. Renderer compromised?"; + return; + } + blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); + + tab_contents->set_delegate(this); + } + + PopupHosts::const_iterator i(popup_hosts_.find(host)); + if (i == popup_hosts_.end()) + popup_hosts_[host] = whitelisted; + else + DCHECK_EQ(whitelisted, i->second); + + // Update UI. + UpdateLabel(); + ShowSelf(); + owner_->PopupNotificationVisibilityChanged(true); +} + +void BlockedPopupContainer::LaunchPopupAtIndex(size_t index) { + if (index >= blocked_popups_.size()) + return; + + // Open the popup. + BlockedPopups::iterator i(blocked_popups_.begin() + index); + TabContents* tab_contents = i->tab_contents; + tab_contents->set_delegate(NULL); + owner_->AddNewContents(tab_contents, NEW_POPUP, i->bounds, true, GURL()); + + const std::string& host = i->host; + if (!host.empty()) { + // Listen for this popup's destruction, so if the user closes it manually, + // we'll know to stop caring about it. + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(tab_contents)); + + // Add the popup to the unblocked list. (Do this before the below call!) + unblocked_popups_[tab_contents] = i->host; + } + + // Remove the popup from the blocked list. + EraseDataForPopupAndUpdateUI(i); +} + +size_t BlockedPopupContainer::GetBlockedPopupCount() const { + return blocked_popups_.size(); +} + +bool BlockedPopupContainer::IsHostWhitelisted(size_t index) const { + PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); + return (i == popup_hosts_.end()) ? false : i->second; +} + +void BlockedPopupContainer::ToggleWhitelistingForHost(size_t index) { + PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); + const std::string& host = i->first; + bool should_whitelist = !i->second; + popup_hosts_[host] = should_whitelist; + + ListValue* whitelist_pref = + prefs_->GetMutableList(prefs::kPopupWhitelistedHosts); + if (should_whitelist) { + whitelist_.insert(host); + whitelist_pref->Append(new StringValue(host)); + + // Open the popups in order. + for (size_t j = 0; j < blocked_popups_.size(); ) { + if (blocked_popups_[j].host == host) + LaunchPopupAtIndex(j); // This shifts the rest of the entries down. + else + ++j; + } + } else { + // Remove from whitelist. + whitelist_.erase(host); + StringValue host_as_string(host); + whitelist_pref->Remove(host_as_string); + + for (UnblockedPopups::iterator i(unblocked_popups_.begin()); + i != unblocked_popups_.end(); ) { + TabContents* tab_contents = i->first; + TabContentsDelegate* delegate = tab_contents->delegate(); + if ((i->second == host) && delegate->IsPopup(tab_contents)) { + // Convert the popup back into a blocked popup. + delegate->DetachContents(tab_contents); + tab_contents->set_delegate(this); + + // Add the popup to the blocked list. (Do this before the below call!) + gfx::Rect bounds; + tab_contents->GetContainerBounds(&bounds); + blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); + + // Remove the popup from the unblocked list. + UnblockedPopups::iterator to_erase = i; + ++i; + EraseDataForPopupAndUpdateUI(to_erase); + } else { + ++i; + } + } + } +} + +void BlockedPopupContainer::CloseAll() { + ClearData(); + HideSelf(); +} + +// Overridden from TabContentsDelegate: +void BlockedPopupContainer::OpenURLFromTab(TabContents* source, + const GURL& url, + const GURL& referrer, + WindowOpenDisposition disposition, + PageTransition::Type transition) { + owner_->OpenURL(url, referrer, disposition, transition); +} + +void BlockedPopupContainer::AddNewContents(TabContents* source, + TabContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_position, + bool user_gesture) { + owner_->AddNewContents(new_contents, disposition, initial_position, + user_gesture, GURL()); +} + +void BlockedPopupContainer::CloseContents(TabContents* source) { + for (BlockedPopups::iterator it = blocked_popups_.begin(); + it != blocked_popups_.end(); ++it) { + TabContents* tab_contents = it->tab_contents; + if (tab_contents == source) { + tab_contents->set_delegate(NULL); + EraseDataForPopupAndUpdateUI(it); + delete tab_contents; + break; + } + } +} + +void BlockedPopupContainer::MoveContents(TabContents* source, + const gfx::Rect& new_bounds) { + for (BlockedPopups::iterator it = blocked_popups_.begin(); + it != blocked_popups_.end(); ++it) { + if (it->tab_contents == source) { + it->bounds = new_bounds; + break; + } + } +} + +bool BlockedPopupContainer::IsPopup(TabContents* source) { + return true; +} + +TabContents* BlockedPopupContainer::GetConstrainingContents( + TabContents* source) { + return owner_; +} + +ExtensionFunctionDispatcher* BlockedPopupContainer:: + CreateExtensionFunctionDispatcher(RenderViewHost* render_view_host, + const std::string& extension_id) { + return new ExtensionFunctionDispatcher(render_view_host, NULL, extension_id); +} + +void BlockedPopupContainer::HideSelf() { + owner_->PopupNotificationVisibilityChanged(false); +} + +void BlockedPopupContainer::ClearData() { + for (BlockedPopups::iterator i(blocked_popups_.begin()); + i != blocked_popups_.end(); ++i) { + TabContents* tab_contents = i->tab_contents; + tab_contents->set_delegate(NULL); + delete tab_contents; + } + blocked_popups_.clear(); + + registrar_.RemoveAll(); + unblocked_popups_.clear(); + + popup_hosts_.clear(); +} + +BlockedPopupContainer::PopupHosts::const_iterator + BlockedPopupContainer::ConvertHostIndexToIterator(size_t index) const { + if (index >= popup_hosts_.size()) + return popup_hosts_.end(); + // If only there was a std::map::const_iterator::operator +=() ... + PopupHosts::const_iterator i(popup_hosts_.begin()); + for (size_t j = 0; j < index; ++j) + ++i; + return i; +} + +void BlockedPopupContainer::EraseDataForPopupAndUpdateUI( + BlockedPopups::iterator i) { + // Erase the host if this is the last popup for that host. + const std::string& host = i->host; + if (!host.empty()) { + bool should_erase_host = true; + for (BlockedPopups::const_iterator j(blocked_popups_.begin()); + j != blocked_popups_.end(); ++j) { + if ((j != i) && (j->host == host)) { + should_erase_host = false; + break; + } + } + if (should_erase_host) { + for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); + j != unblocked_popups_.end(); ++j) { + if (j->second == host) { + should_erase_host = false; + break; + } + } + if (should_erase_host) + popup_hosts_.erase(host); + } + } + + // Erase the popup and update the UI. + blocked_popups_.erase(i); + UpdateLabel(); +} + +void BlockedPopupContainer::EraseDataForPopupAndUpdateUI( + UnblockedPopups::iterator i) { + // Stop listening for this popup's destruction. + registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(i->first)); + + // Erase the host if this is the last popup for that host. + const std::string& host = i->second; + if (!host.empty()) { + bool should_erase_host = true; + for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); + j != unblocked_popups_.end(); ++j) { + if ((j != i) && (j->second == host)) { + should_erase_host = false; + break; + } + } + if (should_erase_host) { + for (BlockedPopups::const_iterator j(blocked_popups_.begin()); + j != blocked_popups_.end(); ++j) { + if (j->host == host) { + should_erase_host = false; + break; + } + } + if (should_erase_host) + popup_hosts_.erase(host); + } + } + + // Erase the popup and update the UI. + unblocked_popups_.erase(i); + UpdateLabel(); +} + + +// private: + +BlockedPopupContainer::BlockedPopupContainer(TabContents* owner, + PrefService* prefs) + : owner_(owner), + prefs_(prefs), + has_been_dismissed_(false) { + // Copy whitelist pref into local member that's easier to use. + const ListValue* whitelist_pref = + prefs_->GetList(prefs::kPopupWhitelistedHosts); + // Careful: The returned value could be NULL if the pref has never been set. + if (whitelist_pref != NULL) { + for (ListValue::const_iterator i(whitelist_pref->begin()); + i != whitelist_pref->end(); ++i) { + std::string host; + (*i)->GetAsString(&host); + whitelist_.insert(host); + } + } +} + +void BlockedPopupContainer::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); + TabContents* tab_contents = Source<TabContents>(source).ptr(); + UnblockedPopups::iterator i(unblocked_popups_.find(tab_contents)); + DCHECK(i != unblocked_popups_.end()); + EraseDataForPopupAndUpdateUI(i); +} diff --git a/chrome/browser/blocked_popup_container.h b/chrome/browser/blocked_popup_container.h new file mode 100644 index 0000000..f9b5d14 --- /dev/null +++ b/chrome/browser/blocked_popup_container.h @@ -0,0 +1,258 @@ +// Copyright (c) 2009 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. + +// Defines the public interface for the blocked popup notifications. This +// interface should only be used by TabContents. Users and subclasses of +// TabContents should use the appropriate methods on TabContents to access +// information about blocked popups. + +#ifndef CHROME_BROWSER_BLOCKED_POPUP_CONTAINER_H_ +#define CHROME_BROWSER_BLOCKED_POPUP_CONTAINER_H_ + +#include <map> +#include <set> + +#include "base/gfx/native_widget_types.h" +#include "base/gfx/rect.h" +#include "chrome/browser/tab_contents/constrained_window.h" +#include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "chrome/common/notification_registrar.h" + +class BlockedPopupContainerImpl; +class PrefService; +class Profile; +class TabContents; + +// Takes ownership of TabContents that are unrequested popup windows and +// presents an interface to the user for launching them. (Or never showing them +// again). This class contains all the cross-platform bits that can be used in +// all ports. +// +// Currently, BlockedPopupContainer only exists as a cross platform model +// extracted from browser/views/blocked_popup_container.cc. This is what it +// used to look like before: +// +// +- BlockedPopupContainer -------------------------------+ +// | <views::WidgetWin> | +// | All model logic. | +// | All views code. | +// +-------------------------------------------------------+ +// +// As of now, it looks like this: +// +// +- BlockedPopupContainer -------------------------------+ +// | All model logic | +// +-------------------------------------------------------+ +// ^ +// | +// +- BlockedPopupContainerImpl ---------------------------+ +// | views::WidgetWin | +// | All views code. | +// | | +// +-------------------------------------------------------+ +// +// TODO(erg): While it is not in this state yet, I want it to look like this: +// +// +- BlockedPopupContainer ---+ +- BlockedPopupContainerView -----+ +// | All model logic | +--->| Abstract cross platform | +// | | | | interface | +// | | | | | +// | Owns a platform view_ +----+ | | +// +---------------------------+ +---------------------------------+ +// ^ +// | +// +-------------------------------+-----------+ +// | | +// +- BPCViewGtk -----------+ +- BPCViewWin ----------------------+ +// | Gtk UI | | Views UI | +// | | | | +// +------------------------+ +-----------------------------------+ +// +// Getting here will take multiple patches. +class BlockedPopupContainer : public TabContentsDelegate, + public NotificationObserver { + public: + // Creates a BlockedPopupContainer, anchoring the container to the lower + // right corner. + static BlockedPopupContainer* Create( + TabContents* owner, Profile* profile, const gfx::Point& initial_anchor); + + static void RegisterUserPrefs(PrefService* prefs); + + // Adds a popup to this container. |bounds| are the window bounds requested by + // the popup window. + void AddTabContents(TabContents* tab_contents, + const gfx::Rect& bounds, + const std::string& host); + + // Shows the blocked popup at index |index|. + void LaunchPopupAtIndex(size_t index); + + // Returns the number of blocked popups + size_t GetBlockedPopupCount() const; + + // Returns true if host |index| is whitelisted. Returns false if |index| is + // invalid. + bool IsHostWhitelisted(size_t index) const; + + // If host |index| is currently whitelisted, un-whitelists it. Otherwise, + // whitelists it and opens all blocked popups from it. + void ToggleWhitelistingForHost(size_t index); + + // Deletes all popups and hides the interface parts. + void CloseAll(); + + // Sets this object up to delete itself. + virtual void Destroy() = 0; + + // Message called when a BlockedPopupContainer should move itself to the + // bottom right corner of |view|. + virtual void RepositionBlockedPopupContainer(gfx::NativeView view) = 0; + + // Called to force this container to never show itself again. + void set_dismissed() { has_been_dismissed_ = true; } + + // Overridden from TabContentsDelegate: + + // Forwards OpenURLFromTab to our |owner_|. + virtual void OpenURLFromTab(TabContents* source, + const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition, + PageTransition::Type transition); + + // Ignored; BlockedPopupContainer doesn't display a throbber. + virtual void NavigationStateChanged(const TabContents* source, + unsigned changed_flags) { } + + // Forwards AddNewContents to our |owner_|. + virtual void AddNewContents(TabContents* source, + TabContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_position, + bool user_gesture); + + // Ignore activation requests from the TabContents we're blocking. + virtual void ActivateContents(TabContents* contents) { } + + // Ignored; BlockedPopupContainer doesn't display a throbber. + virtual void LoadingStateChanged(TabContents* source) { } + + // Removes |source| from our internal list of blocked popups. + virtual void CloseContents(TabContents* source); + + // Changes the opening rectangle associated with |source|. + virtual void MoveContents(TabContents* source, const gfx::Rect& new_bounds); + + // Always returns true. + virtual bool IsPopup(TabContents* source); + + // Returns our |owner_|. + virtual TabContents* GetConstrainingContents(TabContents* source); + + // Ignored; BlockedPopupContainer doesn't display a toolbar. + virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) { } + + // Ignored; BlockedPopupContainer doesn't display a bookmarking star. + virtual void URLStarredChanged(TabContents* source, bool starred) { } + + // Ignored; BlockedPopupContainer doesn't display a URL bar. + virtual void UpdateTargetURL(TabContents* source, const GURL& url) { } + + // Creates an ExtensionFunctionDispatcher that has no browser + virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher( + RenderViewHost* render_view_host, + const std::string& extension_id); + + // A number larger than the internal popup count on the Renderer; meant for + // preventing a compromised renderer from exhausting GDI memory by spawning + // infinite windows. + static const size_t kImpossibleNumberOfPopups = 30; + + protected: + struct BlockedPopup { + BlockedPopup(TabContents* tab_contents, + const gfx::Rect& bounds, + const std::string& host) + : tab_contents(tab_contents), bounds(bounds), host(host) { + } + + TabContents* tab_contents; + gfx::Rect bounds; + std::string host; + }; + typedef std::vector<BlockedPopup> BlockedPopups; + + // TabContents is the popup contents. string is opener hostname. + typedef std::map<TabContents*, std::string> UnblockedPopups; + + // string is hostname. bool is whitelisted status. + typedef std::map<std::string, bool> PopupHosts; + + // Shows the UI. + virtual void ShowSelf() = 0; + + // Hides the UI portion of the container. + virtual void HideSelf(); + + // Updates the text on the label on the notification. + virtual void UpdateLabel() = 0; + + // Deletes all local state. + virtual void ClearData(); + + // Helper function to convert a host index (which the view uses) into an + // iterator into |popup_hosts_|. Returns popup_hosts_.end() if |index| is + // invalid. + PopupHosts::const_iterator ConvertHostIndexToIterator(size_t index) const; + + // Removes the popup at |i| from the blocked popup list. If this popup's host + // is not otherwised referenced on either popup list, removes the host from + // the host list. Updates the view's label to match the new state. + void EraseDataForPopupAndUpdateUI(BlockedPopups::iterator i); + + // Same as above, but works on the unblocked popup list. + void EraseDataForPopupAndUpdateUI(UnblockedPopups::iterator i); + + private: + friend class BlockedPopupContainerImpl; + + // string is hostname. + typedef std::set<std::string> Whitelist; + + // Creates a container for a certain TabContents: + BlockedPopupContainer(TabContents* owner, PrefService* prefs); + + // Overridden from notificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // The TabContents that owns and constrains this BlockedPopupContainer. + TabContents* owner_; + + // The PrefService we can query to find out what's on the whitelist. + PrefService* prefs_; + + // Once the container is hidden, this is set to prevent it from reappearing. + bool has_been_dismissed_; + + // Registrar to handle notifications we care about. + NotificationRegistrar registrar_; + + // The whitelisted hosts, which we allow to open popups directly. + Whitelist whitelist_; + + // Information about all blocked popups. + BlockedPopups blocked_popups_; + + // Information about all unblocked popups. + UnblockedPopups unblocked_popups_; + + // Information about all popup hosts. + PopupHosts popup_hosts_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(BlockedPopupContainer); +}; + +#endif // CHROME_BROWSER_BLOCKED_POPUP_CONTAINER_H_ diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 9407918..1d1b3b6 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -2689,6 +2689,14 @@ </File> </Filter> <File + RelativePath=".\blocked_popup_container.cc" + > + </File> + <File + RelativePath=".\blocked_popup_container.h" + > + </File> + <File RelativePath=".\browser_trial.cc" > </File> diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index 4bc4366..59034a5 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -11,6 +11,7 @@ #include "base/string16.h" #include "base/time.h" #include "chrome/browser/autofill_manager.h" +#include "chrome/browser/blocked_popup_container.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/cert_store.h" @@ -53,9 +54,10 @@ #include "net/base/registry_controlled_domain.h" #if defined(OS_WIN) +// For CRect +#include <atlmisc.h> // TODO(port): some of these headers should be ported. #include "chrome/browser/modal_html_dialog_delegate.h" -#include "chrome/browser/views/blocked_popup_container.h" #include "views/controls/scrollbar/native_scroll_bar.h" #endif @@ -200,14 +202,16 @@ TabContents::TabContents(Profile* profile, int routing_id, base::WaitableEvent* modal_dialog_event) : delegate_(NULL), - controller_(this, profile), - view_(TabContentsView::Create(static_cast<TabContents*>(this))), + ALLOW_THIS_IN_INITIALIZER_LIST(controller_(this, profile)), + ALLOW_THIS_IN_INITIALIZER_LIST(view_( + TabContentsView::Create(static_cast<TabContents*>(this)))), ALLOW_THIS_IN_INITIALIZER_LIST(render_manager_( static_cast<TabContents*>(this), static_cast<TabContents*>(this))), property_bag_(), registrar_(), - printing_(*static_cast<TabContents*>(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(printing_( + *static_cast<TabContents*>(this))), save_package_(), cancelable_consumer_(), autofill_manager_(), @@ -306,6 +310,9 @@ TabContents::~TabContents() { window->CloseConstrainedWindow(); } + if (blocked_popups_) + blocked_popups_->Destroy(); + // Notify any observer that have a reference on this tab contents. NotificationService::current()->Notify( NotificationType::TAB_CONTENTS_DESTROYED, @@ -815,12 +822,10 @@ void TabContents::AddNewContents(TabContents* new_contents, #endif } -#if defined(OS_WIN) void TabContents::CloseAllSuppressedPopups() { if (blocked_popups_) blocked_popups_->CloseAll(); } -#endif void TabContents::PopupNotificationVisibilityChanged(bool visible) { render_view_host()->PopupNotificationVisibilityChanged(visible); @@ -937,18 +942,12 @@ void TabContents::WillClose(ConstrainedWindow* window) { find(child_windows_.begin(), child_windows_.end(), window); if (it != child_windows_.end()) child_windows_.erase(it); +} -#if defined(OS_WIN) - if (window == blocked_popups_) - blocked_popups_ = NULL; - - if (::IsWindow(GetNativeView())) { - CRect client_rect; - GetClientRect(GetNativeView(), &client_rect); - RepositionSupressedPopupsToFit( - gfx::Size(client_rect.Width(), client_rect.Height())); - } -#endif +void TabContents::WillCloseBlockedPopupContainer( + BlockedPopupContainer* container) { + DCHECK(blocked_popups_ == container); + blocked_popups_ = NULL; } void TabContents::DidMoveOrResize(ConstrainedWindow* window) { @@ -1112,7 +1111,6 @@ void TabContents::CreateBlockedPopupContainerIfNecessary() { client_rect.Height()); blocked_popups_ = BlockedPopupContainer::Create(this, profile(), anchor_position); - child_windows_.push_back(blocked_popups_); } void TabContents::AddPopup(TabContents* new_contents, @@ -1121,28 +1119,18 @@ void TabContents::AddPopup(TabContents* new_contents, CreateBlockedPopupContainerIfNecessary(); blocked_popups_->AddTabContents(new_contents, initial_pos, host); } +#endif // TODO(brettw) This should be on the TabContentsView. -void TabContents::RepositionSupressedPopupsToFit(const gfx::Size& new_size) { - // TODO(erg): There's no way to detect whether scroll bars are - // visible, so for beta, we're just going to assume that the - // vertical scroll bar is visible, and not care about covering up - // the horizontal scroll bar. Fixing this is half of - // http://b/1118139. - gfx::Point anchor_position( - new_size.width() - - views::NativeScrollBar::GetVerticalScrollBarWidth(), - new_size.height()); - +void TabContents::RepositionSupressedPopupsToFit() { if (blocked_popups_) - blocked_popups_->RepositionConstrainedWindowTo(anchor_position); + blocked_popups_->RepositionBlockedPopupContainer(GetNativeView()); } bool TabContents::ShowingBlockedPopupNotification() const { return blocked_popups_ != NULL && blocked_popups_->GetBlockedPopupCount() != 0; } -#endif // defined(OS_WIN) namespace { bool TransitionIsReload(PageTransition::Type transition) { diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h index 026cf3d..2853ceb 100644 --- a/chrome/browser/tab_contents/tab_contents.h +++ b/chrome/browser/tab_contents/tab_contents.h @@ -413,6 +413,9 @@ class TabContents : public PageNavigator, // Called when a ConstrainedWindow we own is about to be closed. void WillClose(ConstrainedWindow* window); + // Called when a BlockedPopupContainer we own is about to be closed. + void WillCloseBlockedPopupContainer(BlockedPopupContainer* container); + // Called when a ConstrainedWindow we own is moved or resized. void DidMoveOrResize(ConstrainedWindow* window); @@ -555,6 +558,10 @@ class TabContents : public PageNavigator, render_view_host()->WindowMoveOrResizeStarted(); } + BlockedPopupContainer* blocked_popup_container() const { + return blocked_popups_; + } + private: friend class NavigationController; // Used to access the child_windows_ (ConstrainedWindowList) for testing @@ -615,7 +622,7 @@ class TabContents : public PageNavigator, // Called by a derived class when the TabContents is resized, causing // suppressed constrained web popups to be repositioned to the new bounds // if necessary. - void RepositionSupressedPopupsToFit(const gfx::Size& new_size); + void RepositionSupressedPopupsToFit(); // Whether we have a notification AND the notification owns popups windows. // (We keep the notification object around even when it's not shown since it @@ -971,8 +978,7 @@ class TabContents : public PageNavigator, // Data for shelves and stuff ------------------------------------------------ // ConstrainedWindow with additional methods for managing blocked - // popups. This pointer also goes in |child_windows_| for ownership, - // repositioning, etc. + // popups. BlockedPopupContainer* blocked_popups_; // Delegates for InfoBars associated with this TabContents. diff --git a/chrome/browser/tab_contents/tab_contents_view_win.cc b/chrome/browser/tab_contents/tab_contents_view_win.cc index 15481b5..a5ec7f4 100644 --- a/chrome/browser/tab_contents/tab_contents_view_win.cc +++ b/chrome/browser/tab_contents/tab_contents_view_win.cc @@ -570,7 +570,7 @@ void TabContentsViewWin::WasSized(const gfx::Size& size) { tab_contents()->render_widget_host_view()->SetSize(size); // TODO(brettw) this function can probably be moved to this class. - tab_contents()->RepositionSupressedPopupsToFit(size); + tab_contents()->RepositionSupressedPopupsToFit(); } bool TabContentsViewWin::ScrollZoom(int scroll_type) { diff --git a/chrome/browser/views/blocked_popup_container.cc b/chrome/browser/views/blocked_popup_container.cc index fba9780..38fa3a5 100644 --- a/chrome/browser/views/blocked_popup_container.cc +++ b/chrome/browser/views/blocked_popup_container.cc @@ -11,6 +11,7 @@ #include "chrome/browser/views/blocked_popup_container.h" #include <math.h> +#include <windows.h> #include "app/gfx/canvas.h" #include "app/gfx/path.h" @@ -21,19 +22,14 @@ #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/notification_service.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/pref_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/background.h" #include "views/controls/button/image_button.h" -namespace { -// A number larger than the internal popup count on the Renderer; meant for -// preventing a compromised renderer from exhausting GDI memory by spawning -// infinite windows. -const int kImpossibleNumberOfPopups = 30; +#include "views/controls/scrollbar/native_scroll_bar.h" +namespace { // The minimal border around the edge of the notification. const int kSmallPadding = 2; @@ -78,10 +74,10 @@ const SkScalar kRoundedCornerRad[8] = { 0 }; -} +} // namespace BlockedPopupContainerView::BlockedPopupContainerView( - BlockedPopupContainer* container) + BlockedPopupContainerImpl* container) : container_(container) { ResourceBundle &resource_bundle = ResourceBundle::GetSharedInstance(); @@ -200,7 +196,8 @@ void BlockedPopupContainerView::ButtonPressed(views::Button* sender) { if (!hosts.empty() && (popup_count > 0)) launch_menu_->AppendSeparator(); for (size_t i = 0; i < hosts.size(); ++i) { - launch_menu_->AppendMenuItem(kImpossibleNumberOfPopups + i + 1, + launch_menu_->AppendMenuItem( + BlockedPopupContainer::kImpossibleNumberOfPopups + i + 1, l10n_util::GetStringF(IDS_POPUP_HOST_FORMAT, hosts[i]), views::Menu::NORMAL); } @@ -215,9 +212,9 @@ void BlockedPopupContainerView::ButtonPressed(views::Button* sender) { } bool BlockedPopupContainerView::IsItemChecked(int id) const { - if (id > kImpossibleNumberOfPopups) { + if (id > BlockedPopupContainer::kImpossibleNumberOfPopups) { return container_->IsHostWhitelisted(static_cast<size_t>( - id - kImpossibleNumberOfPopups - 1)); + id - BlockedPopupContainer::kImpossibleNumberOfPopups - 1)); } return false; @@ -226,114 +223,29 @@ bool BlockedPopupContainerView::IsItemChecked(int id) const { void BlockedPopupContainerView::ExecuteCommand(int id) { DCHECK_GT(id, 0); size_t id_size_t = static_cast<size_t>(id); - if (id_size_t > kImpossibleNumberOfPopups) { + if (id_size_t > BlockedPopupContainer::kImpossibleNumberOfPopups) { // Decrement id since all index based commands have 1 added to them. (See // ButtonPressed() for detail). container_->ToggleWhitelistingForHost( - id_size_t - kImpossibleNumberOfPopups - 1); + id_size_t - BlockedPopupContainer::kImpossibleNumberOfPopups - 1); } else { container_->LaunchPopupAtIndex(id_size_t - 1); } } -BlockedPopupContainer::~BlockedPopupContainer() { -} - -// static -void BlockedPopupContainer::RegisterUserPrefs(PrefService* prefs) { - prefs->RegisterListPref(prefs::kPopupWhitelistedHosts); +BlockedPopupContainerImpl::~BlockedPopupContainerImpl() { } // static BlockedPopupContainer* BlockedPopupContainer::Create( TabContents* owner, Profile* profile, const gfx::Point& initial_anchor) { - BlockedPopupContainer* container = - new BlockedPopupContainer(owner, profile->GetPrefs()); + BlockedPopupContainerImpl* container = + new BlockedPopupContainerImpl(owner, profile->GetPrefs()); container->Init(initial_anchor); return container; } -void BlockedPopupContainer::AddTabContents(TabContents* tab_contents, - const gfx::Rect& bounds, - const std::string& host) { - // Show whitelisted popups immediately. - bool whitelisted = !!whitelist_.count(host); - if (whitelisted) - owner_->AddNewContents(tab_contents, NEW_POPUP, bounds, true, GURL()); - - if (has_been_dismissed_) { - // Don't want to show any other UI. - if (!whitelisted) - delete tab_contents; // Discard blocked popups entirely. - return; - } - - if (whitelisted) { - // Listen for this popup's destruction, so if the user closes it manually, - // we'll know to stop caring about it. - registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(tab_contents)); - - unblocked_popups_[tab_contents] = host; - } else { - if (blocked_popups_.size() >= kImpossibleNumberOfPopups) { - delete tab_contents; - LOG(INFO) << "Warning: Renderer is sending more popups to us than should " - "be possible. Renderer compromised?"; - return; - } - blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); - - tab_contents->set_delegate(this); - } - - PopupHosts::const_iterator i(popup_hosts_.find(host)); - if (i == popup_hosts_.end()) - popup_hosts_[host] = whitelisted; - else - DCHECK_EQ(whitelisted, i->second); - - // Update UI. - container_view_->UpdateLabel(); - SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - if (!Animation::IsAnimating() && visibility_percentage_ < 1.0) { - in_show_animation_ = true; - Animation::SetDuration(kShowAnimationDurationMS); - Animation::Start(); - } - owner_->PopupNotificationVisibilityChanged(true); -} - -void BlockedPopupContainer::LaunchPopupAtIndex(size_t index) { - if (index >= blocked_popups_.size()) - return; - - // Open the popup. - BlockedPopups::iterator i(blocked_popups_.begin() + index); - TabContents* tab_contents = i->tab_contents; - tab_contents->set_delegate(NULL); - owner_->AddNewContents(tab_contents, NEW_POPUP, i->bounds, true, GURL()); - - const std::string& host = i->host; - if (!host.empty()) { - // Listen for this popup's destruction, so if the user closes it manually, - // we'll know to stop caring about it. - registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(tab_contents)); - - // Add the popup to the unblocked list. (Do this before the below call!) - unblocked_popups_[tab_contents] = i->host; - } - - // Remove the popup from the blocked list. - EraseDataForPopupAndUpdateUI(i); -} - -size_t BlockedPopupContainer::GetBlockedPopupCount() const { - return blocked_popups_.size(); -} - -void BlockedPopupContainer::GetURLAndTitleForPopup(size_t index, +void BlockedPopupContainerImpl::GetURLAndTitleForPopup(size_t index, std::wstring* url, std::wstring* title) const { DCHECK(url); @@ -344,7 +256,7 @@ void BlockedPopupContainer::GetURLAndTitleForPopup(size_t index, *title = UTF16ToWideHack(tab_contents->GetTitle()); } -std::vector<std::wstring> BlockedPopupContainer::GetHosts() const { +std::vector<std::wstring> BlockedPopupContainerImpl::GetHosts() const { std::vector<std::wstring> hosts; for (PopupHosts::const_iterator i(popup_hosts_.begin()); i != popup_hosts_.end(); ++i) @@ -352,187 +264,61 @@ std::vector<std::wstring> BlockedPopupContainer::GetHosts() const { return hosts; } -bool BlockedPopupContainer::IsHostWhitelisted(size_t index) const { - PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); - return (i == popup_hosts_.end()) ? false : i->second; -} - -void BlockedPopupContainer::ToggleWhitelistingForHost(size_t index) { - PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); - const std::string& host = i->first; - bool should_whitelist = !i->second; - popup_hosts_[host] = should_whitelist; - - ListValue* whitelist_pref = - prefs_->GetMutableList(prefs::kPopupWhitelistedHosts); - if (should_whitelist) { - whitelist_.insert(host); - whitelist_pref->Append(new StringValue(host)); - - // Open the popups in order. - for (size_t j = 0; j < blocked_popups_.size(); ) { - if (blocked_popups_[j].host == host) - LaunchPopupAtIndex(j); // This shifts the rest of the entries down. - else - ++j; - } - } else { - // Remove from whitelist. - whitelist_.erase(host); - whitelist_pref->Remove(StringValue(host)); - - for (UnblockedPopups::iterator i(unblocked_popups_.begin()); - i != unblocked_popups_.end(); ) { - TabContents* tab_contents = i->first; - TabContentsDelegate* delegate = tab_contents->delegate(); - if ((i->second == host) && delegate->IsPopup(tab_contents)) { - // Convert the popup back into a blocked popup. - delegate->DetachContents(tab_contents); - tab_contents->set_delegate(this); - - // Add the popup to the blocked list. (Do this before the below call!) - gfx::Rect bounds; - tab_contents->GetContainerBounds(&bounds); - blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); - - // Remove the popup from the unblocked list. - i = EraseDataForPopupAndUpdateUI(i); - } else { - ++i; - } - } - } -} - -void BlockedPopupContainer::CloseAll() { - ClearData(); - HideSelf(); -} - // Overridden from ConstrainedWindow: -void BlockedPopupContainer::CloseConstrainedWindow() { +void BlockedPopupContainerImpl::Destroy() { ClearData(); - - // Broadcast to all observers of NOTIFY_CWINDOW_CLOSED. - // One example of such an observer is AutomationCWindowTracker in the - // automation component. - NotificationService::current()->Notify(NotificationType::CWINDOW_CLOSED, - Source<ConstrainedWindow>(this), - NotificationService::NoDetails()); - Close(); } -void BlockedPopupContainer::RepositionConstrainedWindowTo( - const gfx::Point& anchor_point) { - anchor_point_ = anchor_point; - SetPosition(); -} +void BlockedPopupContainerImpl::RepositionBlockedPopupContainer( + gfx::NativeView view) { + if (::IsWindow(view)) { + CRect client_rect; + ::GetClientRect(view, &client_rect); -// Overridden from TabContentsDelegate: -void BlockedPopupContainer::OpenURLFromTab(TabContents* source, - const GURL& url, - const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition) { - owner_->OpenURL(url, referrer, disposition, transition); -} + // TODO(erg): There's no way to detect whether scroll bars are + // visible, so for beta, we're just going to assume that the + // vertical scroll bar is visible, and not care about covering up + // the horizontal scroll bar. Fixing this is half of + // http://b/1118139. + gfx::Point anchor_position( + client_rect.Width() - + views::NativeScrollBar::GetVerticalScrollBarWidth(), + client_rect.Height()); -void BlockedPopupContainer::AddNewContents(TabContents* source, - TabContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_position, - bool user_gesture) { - owner_->AddNewContents(new_contents, disposition, initial_position, - user_gesture, GURL()); -} - -void BlockedPopupContainer::CloseContents(TabContents* source) { - for (BlockedPopups::iterator it = blocked_popups_.begin(); - it != blocked_popups_.end(); ++it) { - TabContents* tab_contents = it->tab_contents; - if (tab_contents == source) { - tab_contents->set_delegate(NULL); - EraseDataForPopupAndUpdateUI(it); - delete tab_contents; - break; - } - } -} - -void BlockedPopupContainer::MoveContents(TabContents* source, - const gfx::Rect& new_bounds) { - for (BlockedPopups::iterator it = blocked_popups_.begin(); - it != blocked_popups_.end(); ++it) { - if (it->tab_contents == source) { - it->bounds = new_bounds; - break; - } + RepositionWindowTo(anchor_position); } } -bool BlockedPopupContainer::IsPopup(TabContents* source) { - return true; -} - -TabContents* BlockedPopupContainer::GetConstrainingContents( - TabContents* source) { - return owner_; -} - -ExtensionFunctionDispatcher* BlockedPopupContainer:: - CreateExtensionFunctionDispatcher(RenderViewHost* render_view_host, - const std::string& extension_id) { - return new ExtensionFunctionDispatcher(render_view_host, NULL, extension_id); -} - // private: -BlockedPopupContainer::BlockedPopupContainer(TabContents* owner, - PrefService* prefs) - : Animation(kFramerate, NULL), - owner_(owner), - prefs_(prefs), +BlockedPopupContainerImpl::BlockedPopupContainerImpl(TabContents* owner, + PrefService* prefs) + : BlockedPopupContainer(owner, prefs), + Animation(kFramerate, NULL), container_view_(NULL), - has_been_dismissed_(false), in_show_animation_(false), visibility_percentage_(0) { - // Copy whitelist pref into local member that's easier to use. - const ListValue* whitelist_pref = - prefs_->GetList(prefs::kPopupWhitelistedHosts); - // Careful: The returned value could be NULL if the pref has never been set. - if (whitelist_pref != NULL) { - for (ListValue::const_iterator i(whitelist_pref->begin()); - i != whitelist_pref->end(); ++i) { - std::string host; - (*i)->GetAsString(&host); - whitelist_.insert(host); - } - } } -void BlockedPopupContainer::AnimateToState(double state) { - visibility_percentage_ = in_show_animation_ ? state : (1 - state); +void BlockedPopupContainerImpl::RepositionWindowTo( + const gfx::Point& anchor_point) { + anchor_point_ = anchor_point; SetPosition(); } -void BlockedPopupContainer::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); - TabContents* tab_contents = Source<TabContents>(source).ptr(); - UnblockedPopups::iterator i(unblocked_popups_.find(tab_contents)); - DCHECK(i != unblocked_popups_.end()); - EraseDataForPopupAndUpdateUI(i); +void BlockedPopupContainerImpl::AnimateToState(double state) { + visibility_percentage_ = in_show_animation_ ? state : (1 - state); + SetPosition(); } -void BlockedPopupContainer::OnFinalMessage(HWND window) { - owner_->WillClose(this); +void BlockedPopupContainerImpl::OnFinalMessage(HWND window) { + GetConstrainingContents(NULL)->WillCloseBlockedPopupContainer(this); ClearData(); WidgetWin::OnFinalMessage(window); } -void BlockedPopupContainer::OnSize(UINT param, const CSize& size) { +void BlockedPopupContainerImpl::OnSize(UINT param, const CSize& size) { // Set the window region so we have rounded corners on the top. SkRect rect; rect.set(0, 0, SkIntToScalar(size.cx), SkIntToScalar(size.cy)); @@ -543,24 +329,41 @@ void BlockedPopupContainer::OnSize(UINT param, const CSize& size) { ChangeSize(param, size); } -void BlockedPopupContainer::Init(const gfx::Point& initial_anchor) { +void BlockedPopupContainerImpl::Init(const gfx::Point& initial_anchor) { container_view_ = new BlockedPopupContainerView(this); container_view_->SetVisible(true); set_window_style(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); - WidgetWin::Init(owner_->GetNativeView(), gfx::Rect(), false); + WidgetWin::Init(GetConstrainingContents(NULL)->GetNativeView(), gfx::Rect(), + false); SetContentsView(container_view_); - RepositionConstrainedWindowTo(initial_anchor); + RepositionWindowTo(initial_anchor); } -void BlockedPopupContainer::HideSelf() { +void BlockedPopupContainerImpl::ShowSelf() { + SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + if (!Animation::IsAnimating() && visibility_percentage_ < 1.0) { + in_show_animation_ = true; + Animation::SetDuration(kShowAnimationDurationMS); + Animation::Start(); + } +} + +void BlockedPopupContainerImpl::HideSelf() { in_show_animation_ = false; Animation::SetDuration(kHideAnimationDurationMS); Animation::Start(); - owner_->PopupNotificationVisibilityChanged(false); + BlockedPopupContainer::HideSelf(); +} + +void BlockedPopupContainerImpl::UpdateLabel() { + if (blocked_popups_.empty() && unblocked_popups_.empty()) + HideSelf(); + else + container_view_->UpdateLabel(); } -void BlockedPopupContainer::SetPosition() { +void BlockedPopupContainerImpl::SetPosition() { gfx::Size size = container_view_->GetPreferredSize(); int base_x = anchor_point_.x() - size.width(); int base_y = anchor_point_.y() - size.height(); @@ -590,103 +393,3 @@ void BlockedPopupContainer::SetPosition() { SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); } } - -void BlockedPopupContainer::ClearData() { - for (BlockedPopups::iterator i(blocked_popups_.begin()); - i != blocked_popups_.end(); ++i) { - TabContents* tab_contents = i->tab_contents; - tab_contents->set_delegate(NULL); - delete tab_contents; - } - blocked_popups_.clear(); - - registrar_.RemoveAll(); - unblocked_popups_.clear(); - - popup_hosts_.clear(); -} - -BlockedPopupContainer::PopupHosts::const_iterator - BlockedPopupContainer::ConvertHostIndexToIterator(size_t index) const { - if (index >= popup_hosts_.size()) - return popup_hosts_.end(); - // If only there was a std::map::const_iterator::operator +=() ... - PopupHosts::const_iterator i(popup_hosts_.begin()); - for (size_t j = 0; j < index; ++j) - ++i; - return i; -} - -void BlockedPopupContainer::EraseDataForPopupAndUpdateUI( - BlockedPopups::iterator i) { - // Erase the host if this is the last popup for that host. - const std::string& host = i->host; - if (!host.empty()) { - bool should_erase_host = true; - for (BlockedPopups::const_iterator j(blocked_popups_.begin()); - j != blocked_popups_.end(); ++j) { - if ((j != i) && (j->host == host)) { - should_erase_host = false; - break; - } - } - if (should_erase_host) { - for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); - j != unblocked_popups_.end(); ++j) { - if (j->second == host) { - should_erase_host = false; - break; - } - } - if (should_erase_host) - popup_hosts_.erase(host); - } - } - - // Erase the popup and update the UI. - blocked_popups_.erase(i); - if (blocked_popups_.empty() && unblocked_popups_.empty()) - HideSelf(); - else - container_view_->UpdateLabel(); -} - -BlockedPopupContainer::UnblockedPopups::iterator - BlockedPopupContainer::EraseDataForPopupAndUpdateUI( - UnblockedPopups::iterator i) { - // Stop listening for this popup's destruction. - registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(i->first)); - - // Erase the host if this is the last popup for that host. - const std::string& host = i->second; - if (!host.empty()) { - bool should_erase_host = true; - for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); - j != unblocked_popups_.end(); ++j) { - if ((j != i) && (j->second == host)) { - should_erase_host = false; - break; - } - } - if (should_erase_host) { - for (BlockedPopups::const_iterator j(blocked_popups_.begin()); - j != blocked_popups_.end(); ++j) { - if (j->host == host) { - should_erase_host = false; - break; - } - } - if (should_erase_host) - popup_hosts_.erase(host); - } - } - - // Erase the popup and update the UI. - UnblockedPopups::iterator next_popup = unblocked_popups_.erase(i); - if (blocked_popups_.empty() && unblocked_popups_.empty()) - HideSelf(); - else - container_view_->UpdateLabel(); - return next_popup; -} diff --git a/chrome/browser/views/blocked_popup_container.h b/chrome/browser/views/blocked_popup_container.h index 291b0ee..74d3504 100644 --- a/chrome/browser/views/blocked_popup_container.h +++ b/chrome/browser/views/blocked_popup_container.h @@ -2,11 +2,6 @@ // source code is governed by a BSD-style license that can be found in the // LICENSE file. -// Defines the public interface for the blocked popup notifications. This -// interface should only be used by TabContents. Users and subclasses of -// TabContents should use the appropriate methods on TabContents to access -// information about blocked popups. - #ifndef CHROME_BROWSER_VIEWS_BLOCKED_POPUP_CONTAINER_H_ #define CHROME_BROWSER_VIEWS_BLOCKED_POPUP_CONTAINER_H_ @@ -15,17 +10,17 @@ #include <vector> #include "app/animation.h" +#include "base/gfx/native_widget_types.h" #include "base/gfx/rect.h" -#include "chrome/browser/tab_contents/constrained_window.h" +#include "chrome/browser/blocked_popup_container.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" -#include "chrome/common/notification_registrar.h" #include "views/controls/button/button.h" #include "views/controls/button/menu_button.h" #include "views/controls/menu/menu.h" #include "views/view.h" #include "views/widget/widget_win.h" -class BlockedPopupContainer; +class BlockedPopupContainerImpl; class PrefService; class Profile; class TabContents; @@ -41,7 +36,7 @@ class BlockedPopupContainerView : public views::View, public views::ButtonListener, public views::Menu::Delegate { public: - explicit BlockedPopupContainerView(BlockedPopupContainer* container); + explicit BlockedPopupContainerView(BlockedPopupContainerImpl* container); ~BlockedPopupContainerView(); // Sets the label on the menu button @@ -70,7 +65,7 @@ class BlockedPopupContainerView : public views::View, private: // Our owner and HWND parent. - BlockedPopupContainer* container_; + BlockedPopupContainerImpl* container_; // The button which brings up the popup menu. views::MenuButton* popup_count_label_; @@ -87,32 +82,14 @@ class BlockedPopupContainerView : public views::View, // Takes ownership of TabContents that are unrequested popup windows and // presents an interface to the user for launching them. (Or never showing them // again). -class BlockedPopupContainer : public Animation, - public ConstrainedWindow, - public NotificationObserver, - public TabContentsDelegate, - public views::WidgetWin { +// +// TODO(erg): When this class goes away, whatever is replaced shouldn't +// multiply inherit. +class BlockedPopupContainerImpl : public BlockedPopupContainer, + public Animation, + public views::WidgetWin { public: - virtual ~BlockedPopupContainer(); - - static void RegisterUserPrefs(PrefService* prefs); - - // Creates a BlockedPopupContainer, anchoring the container to the lower - // right corner. - static BlockedPopupContainer* Create( - TabContents* owner, Profile* profile, const gfx::Point& initial_anchor); - - // Adds a popup to this container. |bounds| are the window bounds requested by - // the popup window. - void AddTabContents(TabContents* tab_contents, - const gfx::Rect& bounds, - const std::string& host); - - // Shows the blocked popup at index |index|. - void LaunchPopupAtIndex(size_t index); - - // Returns the number of blocked popups - size_t GetBlockedPopupCount() const; + virtual ~BlockedPopupContainerImpl(); // Returns the URL and title for popup |index|, used to construct a string for // display. @@ -123,127 +100,25 @@ class BlockedPopupContainer : public Animation, // Returns the names of hosts showing popups. std::vector<std::wstring> GetHosts() const; - // Returns true if host |index| is whitelisted. Returns false if |index| is - // invalid. - bool IsHostWhitelisted(size_t index) const; - - // If host |index| is currently whitelisted, un-whitelists it. Otherwise, - // whitelists it and opens all blocked popups from it. - void ToggleWhitelistingForHost(size_t index); + virtual void Destroy(); - // Deletes all popups and hides the interface parts. - void CloseAll(); + virtual void RepositionBlockedPopupContainer(gfx::NativeView view); - // Called to force this container to never show itself again. - void set_dismissed() { has_been_dismissed_ = true; } - - // Overridden from ConstrainedWindow: + private: + friend class BlockedPopupContainer; - // Closes all of our blocked popups and then closes the BlockedPopupContainer. - virtual void CloseConstrainedWindow(); + // Creates a container for a certain TabContents. + BlockedPopupContainerImpl(TabContents* owner, PrefService* prefs); // Repositions our blocked popup notification so that the lower right corner // is at |anchor_point|. - virtual void RepositionConstrainedWindowTo(const gfx::Point& anchor_point); - - // A BlockedPopupContainer is part of the HWND heiarchy and therefore doesn't - // need to manually respond to hide and show events. - virtual void WasHidden() { } - virtual void DidBecomeSelected() { } - - // Debugging accessors only called from the unit tests. - virtual std::wstring GetWindowTitle() const { - return container_view_->label(); - } - virtual const gfx::Rect& GetCurrentBounds() const { return bounds_; } - - // Overridden from TabContentsDelegate: - - // Forwards OpenURLFromTab to our |owner_|. - virtual void OpenURLFromTab(TabContents* source, - const GURL& url, const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition); - - // Ignored; BlockedPopupContainer doesn't display a throbber. - virtual void NavigationStateChanged(const TabContents* source, - unsigned changed_flags) { } - - // Forwards AddNewContents to our |owner_|. - virtual void AddNewContents(TabContents* source, - TabContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_position, - bool user_gesture); - - // Ignore activation requests from the TabContents we're blocking. - virtual void ActivateContents(TabContents* contents) { } - - // Ignored; BlockedPopupContainer doesn't display a throbber. - virtual void LoadingStateChanged(TabContents* source) { } - - // Removes |source| from our internal list of blocked popups. - virtual void CloseContents(TabContents* source); - - // Changes the opening rectangle associated with |source|. - virtual void MoveContents(TabContents* source, const gfx::Rect& new_bounds); - - // Always returns true. - virtual bool IsPopup(TabContents* source); - - // Returns our |owner_|. - virtual TabContents* GetConstrainingContents(TabContents* source); - - // Ignored; BlockedPopupContainer doesn't display a toolbar. - virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) { } - - // Ignored; BlockedPopupContainer doesn't display a bookmarking star. - virtual void URLStarredChanged(TabContents* source, bool starred) { } - - // Ignored; BlockedPopupContainer doesn't display a URL bar. - virtual void UpdateTargetURL(TabContents* source, const GURL& url) { } - - // Creates an ExtensionFunctionDispatcher that has no browser - virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher( - RenderViewHost* render_view_host, - const std::string& extension_id); - - private: - struct BlockedPopup { - BlockedPopup(TabContents* tab_contents, - const gfx::Rect& bounds, - const std::string& host) - : tab_contents(tab_contents), bounds(bounds), host(host) { - } - - TabContents* tab_contents; - gfx::Rect bounds; - std::string host; - }; - typedef std::vector<BlockedPopup> BlockedPopups; - - // TabContents is the popup contents. string is opener hostname. - typedef std::map<TabContents*, std::string> UnblockedPopups; - - // string is hostname. bool is whitelisted status. - typedef std::map<std::string, bool> PopupHosts; - - // string is hostname. - typedef std::set<std::string> Whitelist; - - // Creates a container for a certain TabContents. - BlockedPopupContainer(TabContents* owner, PrefService* prefs); + void RepositionWindowTo(const gfx::Point& anchor_point); // Overridden from Animation: - // Changes the visibility percentage of the BlockedPopupContainer. This is + // Changes the visibility percentage of the BlockedPopupContainerImpl. This is // called while animating in or out. virtual void AnimateToState(double state); - // Overridden from notificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - // Overridden from views::WidgetWin: // Alerts our |owner_| that we are closing ourselves. Cleans up any remaining @@ -257,59 +132,22 @@ class BlockedPopupContainer : public Animation, // browser window. void Init(const gfx::Point& initial_anchor); + // Shows the UI. + virtual void ShowSelf(); + // Hides the UI portion of the container. - void HideSelf(); + virtual void HideSelf(); + + virtual void UpdateLabel(); // Sets our position, based on our |anchor_point_| and on our // |visibility_percentage_|. This method is called whenever either of those // change. void SetPosition(); - // Deletes all local state. - void ClearData(); - - // Helper function to convert a host index (which the view uses) into an - // iterator into |popup_hosts_|. Returns popup_hosts_.end() if |index| is - // invalid. - PopupHosts::const_iterator ConvertHostIndexToIterator(size_t index) const; - - // Removes the popup at |i| from the blocked popup list. If this popup's host - // is not otherwised referenced on either popup list, removes the host from - // the host list. Updates the view's label to match the new state. - void EraseDataForPopupAndUpdateUI(BlockedPopups::iterator i); - - // Same as above, but works on the unblocked popup list, and returns the - // iterator that results from calling erase(). - UnblockedPopups::iterator EraseDataForPopupAndUpdateUI( - UnblockedPopups::iterator i); - - // The TabContents that owns and constrains this BlockedPopupContainer. - TabContents* owner_; - - // The PrefService we can query to find out what's on the whitelist. - PrefService* prefs_; - - // Registrar to handle notifications we care about. - NotificationRegistrar registrar_; - - // The whitelisted hosts, which we allow to open popups directly. - Whitelist whitelist_; - - // Information about all blocked popups. - BlockedPopups blocked_popups_; - - // Information about all unblocked popups. - UnblockedPopups unblocked_popups_; - - // Information about all popup hosts. - PopupHosts popup_hosts_; - // Our associated view object. BlockedPopupContainerView* container_view_; - // Once the container is hidden, this is set to prevent it from reappearing. - bool has_been_dismissed_; - // True while animating in; false while animating out. bool in_show_animation_; @@ -322,8 +160,6 @@ class BlockedPopupContainer : public Animation, // The bottom right corner of where we should appear in our parent window. gfx::Point anchor_point_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(BlockedPopupContainer); }; #endif diff --git a/chrome/browser/views/constrained_window_impl_interactive_uitest.cc b/chrome/browser/views/constrained_window_impl_interactive_uitest.cc index df8dd60..fcd7639 100644 --- a/chrome/browser/views/constrained_window_impl_interactive_uitest.cc +++ b/chrome/browser/views/constrained_window_impl_interactive_uitest.cc @@ -132,14 +132,6 @@ TEST_F(InteractiveConstrainedWindowTest, DontSpawnEndlessPopups) { scoped_refptr<TabProxy> popup_tab(popup_browser->GetTab(0)); ASSERT_TRUE(popup_tab.get()); - int constrained_window_count = 0; - ASSERT_TRUE(popup_tab->WaitForChildWindowCountToChange( - 0, &constrained_window_count, 10000)); - ASSERT_EQ(1, constrained_window_count); - scoped_refptr<ConstrainedWindowProxy> constrained_window( - popup_tab->GetConstrainedWindow(0)); - ASSERT_TRUE(constrained_window.get()); - // And now we spin, waiting to make sure that we don't spawn popup // windows endlessly. The current limit is 25, so allowing for possible race // conditions and one off errors, don't break out until we go over 30 popup @@ -151,9 +143,7 @@ TEST_F(InteractiveConstrainedWindowTest, DontSpawnEndlessPopups) { int times_slept = 0; bool continuing = true; while (continuing && popup_window_count < kMaxPopupWindows) { - std::wstring title; - ASSERT_TRUE(constrained_window->GetTitle(&title)); - ASSERT_TRUE(ParseCountOutOfTitle(title, &new_popup_window_count)); + ASSERT_TRUE(popup_tab->GetBlockedPopupCount(&new_popup_window_count)); if (new_popup_window_count == popup_window_count) { if (times_slept == 10) { continuing = false; @@ -180,8 +170,6 @@ TEST_F(InteractiveConstrainedWindowTest, WindowOpenWindowClosePopup) { ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2, 5000)); - PlatformThread::Sleep(1000); - // Make sure we have a blocked popup notification scoped_refptr<BrowserProxy> popup_browser(automation()->GetBrowserWindow(1)); ASSERT_TRUE(popup_browser.get()); @@ -189,14 +177,7 @@ TEST_F(InteractiveConstrainedWindowTest, WindowOpenWindowClosePopup) { ASSERT_TRUE(popup_window.get()); scoped_refptr<TabProxy> popup_tab(popup_browser->GetTab(0)); ASSERT_TRUE(popup_tab.get()); - scoped_refptr<ConstrainedWindowProxy> popup_notification( - popup_tab->GetConstrainedWindow(0)); - ASSERT_TRUE(popup_notification.get()); - std::wstring title; - ASSERT_TRUE(popup_notification->GetTitle(&title)); - int count = 0; - ASSERT_TRUE(ParseCountOutOfTitle(title, &count)); - ASSERT_EQ(1, count); + ASSERT_TRUE(popup_tab->WaitForBlockedPopupCountToChangeTo(1, 1000)); // Ensure we didn't close the first popup window. ASSERT_FALSE(automation()->WaitForWindowCountToBecome(1, 3000)); @@ -214,13 +195,8 @@ TEST_F(InteractiveConstrainedWindowTest, BlockAlertFromBlockedPopup) { ASSERT_EQ(1, browser_window_count); // Ensure one blocked popup window: the popup didn't escape. - scoped_refptr<ConstrainedWindowProxy> popup_notification( - tab_->GetConstrainedWindow(0)); - ASSERT_TRUE(popup_notification.get()); - std::wstring title; - ASSERT_TRUE(popup_notification->GetTitle(&title)); int popup_count = 0; - ASSERT_TRUE(ParseCountOutOfTitle(title, &popup_count)); + ASSERT_TRUE(tab_->GetBlockedPopupCount(&popup_count)); ASSERT_EQ(1, popup_count); } diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 60e8f25..331135b 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -586,6 +586,8 @@ 'browser/back_forward_menu_model.h', 'browser/back_forward_menu_model_views.cc', 'browser/back_forward_menu_model_views.h', + 'browser/blocked_popup_container.cc', + 'browser/blocked_popup_container.h', 'browser/bookmarks/bookmark_codec.cc', 'browser/bookmarks/bookmark_codec.h', 'browser/bookmarks/bookmark_context_menu_gtk.cc', diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h index eb8397f..7682807 100644..100755 --- a/chrome/test/automation/automation_messages_internal.h +++ b/chrome/test/automation/automation_messages_internal.h @@ -932,4 +932,11 @@ IPC_BEGIN_MESSAGES(Automation) int /* browser_handle */, bool /* is_visible */) + // This message requests the number of blocked popups in a certain tab with + // the given handle. The return value is the number of blocked popups, or -1 + // if this request failed. + IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_BlockedPopupCount, + int /* tab_handle */, + int /* blocked_popup_count */) + IPC_END_MESSAGES(Automation) diff --git a/chrome/test/automation/automation_proxy_uitest.cc b/chrome/test/automation/automation_proxy_uitest.cc index cefef1fd..bb838dd 100644 --- a/chrome/test/automation/automation_proxy_uitest.cc +++ b/chrome/test/automation/automation_proxy_uitest.cc @@ -621,48 +621,9 @@ TEST_F(AutomationProxyTest, ConstrainedWindowTest) { ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename))); - int count; - ASSERT_TRUE(tab->WaitForChildWindowCountToChange(0, &count, 5000)); - - ASSERT_EQ(1, count); - - scoped_refptr<ConstrainedWindowProxy> cwindow = tab->GetConstrainedWindow(0); - ASSERT_TRUE(cwindow.get()); - - std::wstring title; - ASSERT_TRUE(cwindow->GetTitle(&title)); - std::wstring window_title = L"Pop-ups Blocked: 2"; - ASSERT_STREQ(window_title.c_str(), title.c_str()); + ASSERT_TRUE(tab->WaitForBlockedPopupCountToChangeTo(2, 5000)); } -TEST_F(AutomationProxyTest, CantEscapeByOnloadMoveto) { - scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); - ASSERT_TRUE(window.get()); - - scoped_refptr<TabProxy> tab(window->GetTab(0)); - ASSERT_TRUE(tab.get()); - - FilePath filename(test_data_directory_); - filename = filename.AppendASCII("constrained_files"); - filename = filename.AppendASCII("constrained_window_onload_moveto.html"); - - ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename))); - - int count; - ASSERT_TRUE(tab->WaitForChildWindowCountToChange(0, &count, 5000)); - - ASSERT_EQ(1, count); - - scoped_refptr<ConstrainedWindowProxy> cwindow = tab->GetConstrainedWindow(0); - ASSERT_TRUE(cwindow.get()); - - gfx::Rect rect; - bool is_timeout = false; - ASSERT_TRUE(cwindow->GetBoundsWithTimeout(&rect, 1000, &is_timeout)); - ASSERT_FALSE(is_timeout); - ASSERT_NE(20, rect.x()); - ASSERT_NE(20, rect.y()); -} #endif // defined(OS_WIN) // TODO(port): Remove HWND if possible diff --git a/chrome/test/automation/tab_proxy.cc b/chrome/test/automation/tab_proxy.cc index 0313196..1a2d1144 100644 --- a/chrome/test/automation/tab_proxy.cc +++ b/chrome/test/automation/tab_proxy.cc @@ -364,6 +364,33 @@ bool TabProxy::WaitForChildWindowCountToChange(int count, int* new_count, return false; } +bool TabProxy::GetBlockedPopupCount(int* count) const { + if (!is_valid()) + return false; + + if (!count) { + NOTREACHED(); + return false; + } + + return sender_->Send(new AutomationMsg_BlockedPopupCount( + 0, handle_, count)); +} + +bool TabProxy::WaitForBlockedPopupCountToChangeTo(int target_count, + int wait_timeout) { + int intervals = std::min(wait_timeout/automation::kSleepTime, 1); + for (int i = 0; i < intervals; ++i) { + PlatformThread::Sleep(automation::kSleepTime); + int new_count = -1; + bool succeeded = GetBlockedPopupCount(&new_count); + if (!succeeded) return false; + if (target_count == new_count) return true; + } + // Constrained Window count did not change, return false. + return false; +} + bool TabProxy::GetCookies(const GURL& url, std::string* cookies) { if (!is_valid()) return false; diff --git a/chrome/test/automation/tab_proxy.h b/chrome/test/automation/tab_proxy.h index be6f301..2b142b1 100644 --- a/chrome/test/automation/tab_proxy.h +++ b/chrome/test/automation/tab_proxy.h @@ -202,6 +202,13 @@ class TabProxy : public AutomationResourceProxy { bool WaitForChildWindowCountToChange(int count, int* new_count, int wait_timeout); + // Gets the number of popups blocked from this tab. + bool GetBlockedPopupCount(int* count) const; + + // Blocks the thread until the number of blocked popup is equal to + // |target_count|. + bool WaitForBlockedPopupCountToChangeTo(int target_count, int wait_timeout); + bool GetDownloadDirectory(std::wstring* download_directory); // Shows an interstitial page. Blocks until the interstitial page diff --git a/chrome/test/data/constrained_files/constrained_window_onload_moveto.html b/chrome/test/data/constrained_files/constrained_window_onload_moveto.html deleted file mode 100644 index 9da3aa6..0000000 --- a/chrome/test/data/constrained_files/constrained_window_onload_moveto.html +++ /dev/null @@ -1,17 +0,0 @@ -<HTML> - <HEAD> - <TITLE> - Parent Window - </TITLE> -<SCRIPT TYPE="text/javascript" LANGUAGE="JavaScript"> -<!-- Begin -function enter(url, x, y) { - window.open(url,'','height=320,width=300,left='+x+',top='+y); -} -// End --> -</SCRIPT> - - <BODY - onload="enter('target_onload_moveto.html', 100, 100);"> - </BODY> - </HTML> |