diff options
author | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-01 15:50:51 +0000 |
---|---|---|
committer | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-01 15:50:51 +0000 |
commit | dd716ab80774cba276e8b5eb61041f9497ddf642 (patch) | |
tree | 1e408f488b0a8b2fb8f38481e042f1f6db12e658 /chrome/browser/cocoa | |
parent | 31a9bdff16526415c4c00aba0cb41cc6654d3e69 (diff) | |
download | chromium_src-dd716ab80774cba276e8b5eb61041f9497ddf642.zip chromium_src-dd716ab80774cba276e8b5eb61041f9497ddf642.tar.gz chromium_src-dd716ab80774cba276e8b5eb61041f9497ddf642.tar.bz2 |
First cut at popup blocking for Mac. Remove ifdefs in cross-platform code. Implement displaying of notification, menu of popups still to come.
BUG=13160
TEST=popup notification should display and popups blocked accordingly. Can close notification widget to prevent more notifications for this tab.
Review URL: http://codereview.chromium.org/150132
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19735 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
3 files changed, 353 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/blocked_popup_container_controller.h b/chrome/browser/cocoa/blocked_popup_container_controller.h new file mode 100644 index 0000000..39cba6f --- /dev/null +++ b/chrome/browser/cocoa/blocked_popup_container_controller.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef CHROME_BROWSER_VIEWS_BLOCKED_POPUP_CONTAINER_CONTROLLER_H_ +#define CHROME_BROWSER_VIEWS_BLOCKED_POPUP_CONTAINER_CONTROLLER_H_ + +#import <Cocoa/Cocoa.h> + +#include "base/scoped_nsobject.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/blocked_popup_container.h" + +// Controller for the blocked popup view. Communicates with the cross-platform +// code via a C++ bridge class, below. The BlockedPopupContainer class doesn't +// really "own" the bridge, it just keeps a pointer to it and calls Destroy() on +// it when it's supposed to go away. As a result, this class needs to own itself +// (always keep an extra retain), and will autorelease itself (and the bridge +// which it owns) when the bridge gets a Destroy() message. +// TODO(pinkerton): Reverse the ownership if it makes more sense. I'm leaving +// it this way because I assume we eventually want this to be a +// NSViewController, and we usually have the Obj-C controller owning the +// bridge (rather than the other way around). +@interface BlockedPopupContainerController : NSObject { + @private + scoped_ptr<BlockedPopupContainerView> bridge_; + BlockedPopupContainer* container_; // Weak. "owns" me. + scoped_nsobject<NSView> view_; + IBOutlet NSTextField* label_; +} + +// Initialize with the given popup container. Creates the C++ bridge object +// used to represet the "view". +- (id)initWithContainer:(BlockedPopupContainer*)container; + +// Returns the C++ brige object. +- (BlockedPopupContainerView*)bridge; + +// Called by the bridge to perform certain actions from the back-end code. +- (void)show; +- (void)hide; +- (void)update; + +@end + +@interface BlockedPopupContainerController(ForTesting) +- (NSView*)view; +- (NSView*)label; +- (IBAction)closePopup:(id)sender; +@end + +#endif // CHROME_BROWSER_VIEWS_BLOCKED_POPUP_CONTAINER_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/blocked_popup_container_controller.mm b/chrome/browser/cocoa/blocked_popup_container_controller.mm new file mode 100644 index 0000000..93c3e49 --- /dev/null +++ b/chrome/browser/cocoa/blocked_popup_container_controller.mm @@ -0,0 +1,216 @@ +// 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. + +#import "chrome/browser/cocoa/blocked_popup_container_controller.h" + +#include "app/l10n_util.h" +#include "base/sys_string_conversions.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "grit/generated_resources.h" + +#import "chrome/browser/cocoa/background_gradient_view.h" + +// A C++ bridge class that manages the interaction between the C++ interface +// and the Objective-C view controller that implements the popup blocker. +class BlockedPopupContainerViewBridge : public BlockedPopupContainerView { + public: + BlockedPopupContainerViewBridge(BlockedPopupContainerController* controller); + virtual ~BlockedPopupContainerViewBridge(); + + // Overrides from BlockedPopupContainerView + virtual void SetPosition(); + virtual void ShowView(); + virtual void UpdateLabel(); + virtual void HideView(); + virtual void Destroy(); + + private: + BlockedPopupContainerController* controller_; // Weak, owns us. +}; + +@interface BlockedPopupContainerController(Private) +- (void)initPopupView; +- (NSView*)containingView; +@end + +@implementation BlockedPopupContainerController + +// Initialize with the given popup container. Creates the C++ bridge object +// used to represent the "view". +- (id)initWithContainer:(BlockedPopupContainer*)container { + if ((self = [super init])) { + container_ = container; + bridge_.reset(new BlockedPopupContainerViewBridge(self)); + [self initPopupView]; + } + return self; +} + +- (void)dealloc { + [view_ removeFromSuperview]; + [super dealloc]; +} + +- (IBAction)closePopup:(id)sender { + container_->set_dismissed(); + container_->CloseAll(); +} + +// Create and initialize the popup view and its label, close box, etc. +- (void)initPopupView { + static const float kWidth = 200.0; + static const float kHeight = 20.0; + static const float kCloseBoxSize = 16.0; + static const float kCloseBoxPaddingY = 2.0; + static const float kLabelPaddingX = 5.0; + + // Create it below the parent's bottom edge so we can animate it into place. + NSRect startFrame = NSMakeRect(0.0, -kHeight, kWidth, kHeight); + view_.reset([[BackgroundGradientView alloc] initWithFrame:startFrame]); + [view_ setAutoresizingMask:NSViewMinXMargin | NSViewMaxYMargin]; + + // Create the text label and position it. We'll resize it later when the + // label gets updated. The view owns the label, we only hold a weak reference. + NSRect labelFrame = NSMakeRect(kLabelPaddingX, + 0, + startFrame.size.width - kCloseBoxSize, + startFrame.size.height); + label_ = [[[NSTextField alloc] initWithFrame:labelFrame] autorelease]; + [label_ setSelectable:NO]; + [label_ setAutoresizingMask:NSViewWidthSizable]; + [label_ setBordered:NO]; + [label_ setBezeled:NO]; + [label_ setDrawsBackground:NO]; + [view_ addSubview:label_]; + + // Create the close box and position at the left of the view. + NSRect closeFrame = NSMakeRect(startFrame.size.width - kCloseBoxSize, + kCloseBoxPaddingY, + kCloseBoxSize, + kCloseBoxSize); + NSButton* close = [[[NSButton alloc] initWithFrame:closeFrame] autorelease]; + [close setAutoresizingMask:NSViewMinXMargin]; + [close setImage:[NSImage imageNamed:@"close_bar"]]; + [close setAlternateImage:[NSImage imageNamed:@"close_bar_p"]]; + [close setBordered:NO]; + [close setTarget:self]; + [close setAction:@selector(closePopup:)]; + [view_ addSubview:close]; +} + +// Returns the C++ brige object. +- (BlockedPopupContainerView*)bridge { + return bridge_.get(); +} + +// Returns the parent of the RWHVMac. We insert our popup blocker as a sibling +// so that it stays around as the RWHVMac is created/destroyed during +// navigation. +- (NSView*)containingView { + return container_->GetConstrainingContents(NULL)->view()->GetNativeView(); +} + +- (void)show { + const float kLeftPadding = 20; // Leave room for the scrollbar. + + // No need to do anything if it's already on screen. + if ([view_ superview]) return; + + // Position the view at the bottom right corner, always leaving room for the + // scrollbar. This is what window does. It also doesn't care about covering + // up the horizontal scrollbar. + NSView* parent = [self containingView]; + NSRect frame = [view_ frame]; + frame.origin.x = [parent frame].size.width - frame.size.width - kLeftPadding; + [view_ setFrame:frame]; + + // Add the view and animate it sliding up into view. + [parent addSubview:view_.get()]; + frame.origin.y = 0; + [[view_ animator] setFrame:frame]; +} + +- (void)hide { + [view_ removeFromSuperview]; +} + +// Resize the view based on the new label contents. The autoresize mask will +// take care of resizing everything else. +- (void)resizeWithLabel:(NSString*)label { +#if 0 +// TODO(pinkerton): fix this once the popup gets put in. + NSSize stringSize = [label sizeWithAttributes:nil]; + NSRect frame = [view_ frame]; + float originalWidth = frame.size.width; + frame.size.width = stringSize.width + 16 + 5; + frame.origin.x -= frame.size.width - originalWidth; + [view_ setFrame:frame]; +#endif +} + +- (void)update { + size_t blockedPopups = container_->GetBlockedPopupCount(); + NSString* label = nil; + if (blockedPopups) { + label = base::SysUTF16ToNSString( + l10n_util::GetStringFUTF16(IDS_POPUPS_BLOCKED_COUNT, + UintToString16(blockedPopups))); + } else { + label = base::SysUTF16ToNSString( + l10n_util::GetStringUTF16(IDS_POPUPS_UNBLOCKED)); + } + [self resizeWithLabel:label]; + [label_ setStringValue:label]; +} + +- (NSView*)view { + return view_.get(); +} + +- (NSView*)label { + return label_; +} + +@end + +//--------------------------------------------------------------------------- + +BlockedPopupContainerView* BlockedPopupContainerView::Create( + BlockedPopupContainer* container) { + // We "leak" |blocker| for now, we'll release it when the bridge class + // gets a Destroy() message. + BlockedPopupContainerController* blocker = + [[BlockedPopupContainerController alloc] initWithContainer:container]; + return [blocker bridge]; +} + +BlockedPopupContainerViewBridge::BlockedPopupContainerViewBridge( + BlockedPopupContainerController* controller) { + controller_ = controller; +} + +BlockedPopupContainerViewBridge::~BlockedPopupContainerViewBridge() { +} + +void BlockedPopupContainerViewBridge::SetPosition() { + // Doesn't ever get called, also a no-op on GTK. + NOTIMPLEMENTED(); +} + +void BlockedPopupContainerViewBridge::ShowView() { + [controller_ show]; +} + +void BlockedPopupContainerViewBridge::UpdateLabel() { + [controller_ update]; +} + +void BlockedPopupContainerViewBridge::HideView() { + [controller_ hide]; +} + +void BlockedPopupContainerViewBridge::Destroy() { + [controller_ autorelease]; +} diff --git a/chrome/browser/cocoa/blocked_popup_container_controller_unittest.mm b/chrome/browser/cocoa/blocked_popup_container_controller_unittest.mm new file mode 100644 index 0000000..e2f5a7e --- /dev/null +++ b/chrome/browser/cocoa/blocked_popup_container_controller_unittest.mm @@ -0,0 +1,85 @@ +// 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. + +#import <Cocoa/Cocoa.h> + +#include "app/app_paths.h" +#include "base/path_service.h" +#import "chrome/browser/cocoa/blocked_popup_container_controller.h" +#include "chrome/browser/cocoa/browser_test_helper.h" +#import "chrome/browser/cocoa/cocoa_test_helper.h" +#include "chrome/browser/renderer_host/test_render_view_host.h" +#include "net/base/net_util.h" +#include "testing/gtest/include/gtest/gtest.h" + + +namespace { +const std::string host1 = "host1"; +} // namespace + +class BlockedPopupContainerControllerTest : public RenderViewHostTestHarness { + public: + CocoaTestHelper cocoa_helper_; // Inits Cocoa, creates window, etc... + + virtual void SetUp() { + RenderViewHostTestHarness::SetUp(); + container_ = BlockedPopupContainer::Create(contents(), profile()); + cocoa_controller_ = [[BlockedPopupContainerController alloc] + initWithContainer:container_]; + EXPECT_TRUE([cocoa_controller_ bridge]); + container_->set_view([cocoa_controller_ bridge]); + contents_->set_blocked_popup_container(container_); + } + + virtual void TearDown() { + // This will also signal the Cocoa controller to delete itself with a + // Destroy() mesage to the bridge. + container_->Destroy(); + contents_->set_blocked_popup_container(NULL); + } + + TabContents* BuildTabContents() { + // This will be deleted when the TabContents goes away. + SiteInstance* instance = SiteInstance::CreateSiteInstance(profile_.get()); + + // Set up and use TestTabContents here. + return new TestTabContents(profile_.get(), instance); + } + + GURL GetTestCase(const std::string& file) { + FilePath filename; + PathService::Get(app::DIR_TEST_DATA, &filename); + filename = filename.AppendASCII("constrained_files"); + filename = filename.AppendASCII(file); + return net::FilePathToFileURL(filename); + } + + BlockedPopupContainer* container_; + BlockedPopupContainerController* cocoa_controller_; +}; + +TEST_F(BlockedPopupContainerControllerTest, BasicPopupBlock) { + // This is taken from the popup blocker unit test. + TabContents* popup = BuildTabContents(); + popup->controller().LoadURLLazily(GetTestCase("error"), GURL(), + PageTransition::LINK, + L"", NULL); + container_->AddTabContents(popup, gfx::Rect(), host1); + EXPECT_EQ(container_->GetBlockedPopupCount(), static_cast<size_t>(1)); + EXPECT_EQ(container_->GetTabContentsAt(0), popup); + EXPECT_FALSE(container_->IsHostWhitelisted(0)); + + // Ensure the view has been displayed. If it has a superview, then ShowView() + // has been called on the bridge. If the label has a string, then + // UpdateLabel() has been called. + EXPECT_TRUE([cocoa_controller_ view]); + EXPECT_TRUE([[cocoa_controller_ view] superview]); + EXPECT_TRUE([[(NSTextField*)[cocoa_controller_ label] + stringValue] length] > 0); + + // Close the popup and verify it's no longer in the view hierarchy. This + // means HideView() has been called. + [cocoa_controller_ closePopup:nil]; + EXPECT_FALSE([[cocoa_controller_ view] superview]); +} |