diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-16 00:59:04 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-16 00:59:04 +0000 |
commit | 10eaf87fb4e1f7677001d16c1296b7305143f633 (patch) | |
tree | ca3cd34d91a302174a43561a37340ec7227a2ac5 /chrome/browser | |
parent | 9b9c9b5cf3693654c4fec4a058dfaa62fee9737b (diff) | |
download | chromium_src-10eaf87fb4e1f7677001d16c1296b7305143f633.zip chromium_src-10eaf87fb4e1f7677001d16c1296b7305143f633.tar.gz chromium_src-10eaf87fb4e1f7677001d16c1296b7305143f633.tar.bz2 |
Add a bare-bones extension shelf that displays extension items on OS X.
This brings our extension support to about the level it has on linux.
One issue is that the toolstrips are webpages with a background image that just happens to look like the shelf they are on. But the background images are not updated on key->nonkey window changes, so the toolstrip backgrounds look slightly off in one of the two cases. If we decide to keep the shelf, we should fix this, but see the bug for erikkay's stance on this.
Also, the NTP is only loaded after all toolstrips have been loaded for some reason. That's what happens on the other platforms too, I believe.
The extension shelf uses the DownloadShelfView as background view for now.
Screenie: http://imgur.com/wSHgU.png
BUG=19073
TEST=Extensions that live in the shelf should show up. They should be clickable, resize correctly (e.g. the build status extension), and the shelf should interact in a sane way with the status bubble.
Review URL: http://codereview.chromium.org/175025
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26311 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.h | 3 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 39 | ||||
-rw-r--r-- | chrome/browser/cocoa/extension_shelf_controller.h | 49 | ||||
-rw-r--r-- | chrome/browser/cocoa/extension_shelf_controller.mm | 356 | ||||
-rw-r--r-- | chrome/browser/cocoa/extension_shelf_controller_unittest.mm | 56 | ||||
-rw-r--r-- | chrome/browser/cocoa/extension_view_mac.h | 65 | ||||
-rw-r--r-- | chrome/browser/cocoa/extension_view_mac.mm | 68 | ||||
-rw-r--r-- | chrome/browser/cocoa/status_bubble_mac.h | 3 | ||||
-rw-r--r-- | chrome/browser/cocoa/status_bubble_mac.mm | 12 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_host.cc | 18 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_host.h | 6 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_mac.h | 3 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_mac.mm | 10 |
13 files changed, 658 insertions, 30 deletions
diff --git a/chrome/browser/cocoa/browser_window_controller.h b/chrome/browser/cocoa/browser_window_controller.h index e7d9471..339aa61 100644 --- a/chrome/browser/cocoa/browser_window_controller.h +++ b/chrome/browser/cocoa/browser_window_controller.h @@ -25,6 +25,7 @@ class BrowserWindow; class BrowserWindowCocoa; class ConstrainedWindowMac; @class DownloadShelfController; +@class ExtensionShelfController; @class FindBarCocoaController; @class GTMWindowSheetController; @class InfoBarContainerController; @@ -65,10 +66,12 @@ class TabStripModelObserverBridge; scoped_nsobject<InfoBarContainerController> infoBarContainerController_; scoped_ptr<StatusBubble> statusBubble_; scoped_nsobject<DownloadShelfController> downloadShelfController_; + scoped_nsobject<ExtensionShelfController> extensionShelfController_; scoped_nsobject<BookmarkBubbleController> bookmarkBubbleController_; scoped_nsobject<GTMTheme> theme_; BOOL ownsBrowser_; // Only ever NO when testing BOOL fullscreen_; + CGFloat verticalOffsetForStatusBubble_; } // Load the browser window nib and do any Cocoa-specific initialization. diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index 2e98d78..38c5b4c 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -25,6 +25,7 @@ #import "chrome/browser/cocoa/browser_window_cocoa.h" #import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/download_shelf_controller.h" +#import "chrome/browser/cocoa/extension_shelf_controller.h" #import "chrome/browser/cocoa/find_bar_cocoa_controller.h" #include "chrome/browser/cocoa/find_bar_bridge.h" #import "chrome/browser/cocoa/fullscreen_window.h" @@ -189,6 +190,16 @@ willPositionSheet:(NSWindow*)sheet } [[[self window] contentView] addSubview:[toolbarController_ view]]; + if (browser_->SupportsWindowFeature(Browser::FEATURE_EXTENSIONSHELF)) { + // Create the extension shelf. + extensionShelfController_.reset([[ExtensionShelfController alloc] + initWithBrowser:browser_.get() + resizeDelegate:self]); + [[[self window] contentView] addSubview:[extensionShelfController_ view]]; + [extensionShelfController_ wasInsertedIntoWindow]; + [extensionShelfController_ show:nil]; + } + [self fixWindowGradient]; // Force a relayout of all the various bars. @@ -466,12 +477,13 @@ willPositionSheet:(NSWindow*)sheet // directly. If the view is already the correct height, does not force a // relayout. - (void)resizeView:(NSView*)view newHeight:(float)height { - // We should only ever be called for one of the following three views. + // We should only ever be called for one of the following four views. // |downloadShelfController_| may be nil. DCHECK(view); DCHECK(view == [toolbarController_ view] || view == [infoBarContainerController_ view] || - view == [downloadShelfController_ view]); + view == [downloadShelfController_ view] || + view == [extensionShelfController_ view]); // Change the height of the view and call layoutViews. We set the height here // without regard to where the view is on the screen or whether it needs to @@ -615,13 +627,7 @@ willPositionSheet:(NSWindow*)sheet // StatusBubble delegate method: tell the status bubble how far above the bottom // of the window it should position itself. - (float)verticalOffsetForStatusBubble { - float offset = 0.0; - - // Don't create a download shelf if there isn't one. - if (downloadShelfController_.get() && [[self downloadShelf] isVisible]) - offset += [[self downloadShelf] height]; - - return offset; + return verticalOffsetForStatusBubble_; } - (GTMWindowSheetController*)sheetController { @@ -827,7 +833,6 @@ willPositionSheet:(NSWindow*)sheet - (BOOL)isBookmarkBarVisible { return [[toolbarController_ bookmarkBarController] isBookmarkBarVisible]; - } - (void)toggleBookmarkBar { @@ -1242,7 +1247,17 @@ willPositionSheet:(NSWindow*)sheet [infoBarView setFrame:infoBarFrame]; maxY -= NSHeight(infoBarFrame); - // Place the download shelf at the bottom of the view, if it exists. + // Place the extension shelf at the bottom of the view, if it exists. + if (extensionShelfController_.get()) { + NSView* extensionView = [extensionShelfController_ view]; + NSRect extensionFrame = [extensionView frame]; + extensionFrame.origin.y = minY; + extensionFrame.size.width = NSWidth(contentFrame); + [extensionView setFrame:extensionFrame]; + minY += NSHeight(extensionFrame); + } + + // Place the download shelf above the extension shelf, if it exists. if (downloadShelfController_.get()) { NSView* downloadView = [downloadShelfController_ view]; NSRect downloadFrame = [downloadView frame]; @@ -1263,6 +1278,8 @@ willPositionSheet:(NSWindow*)sheet // Position the find bar relative to the infobar container. [findBarCocoaController_ positionFindBarView:[infoBarContainerController_ view]]; + + verticalOffsetForStatusBubble_ = minY; } @end diff --git a/chrome/browser/cocoa/extension_shelf_controller.h b/chrome/browser/cocoa/extension_shelf_controller.h new file mode 100644 index 0000000..a533c7a --- /dev/null +++ b/chrome/browser/cocoa/extension_shelf_controller.h @@ -0,0 +1,49 @@ +// 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 "base/scoped_ptr.h" +#import "chrome/browser/cocoa/view_resizer.h" + +class Browser; +class ExtensionShelfMac; + +// A controller for the extension shelf. After creating on object of this class, +// insert its |view| into a superview and call |wasInsertedIntoWindow|. After +// that, the controller automatically registers itself in the extensions +// subsystem and manages displaying toolstrips in the extension shelf. +@interface ExtensionShelfController : NSViewController { + @private + CGFloat shelfHeight_; + + scoped_ptr<ExtensionShelfMac> bridge_; + + // Delegate that handles resizing our view. + id<ViewResizer> resizeDelegate_; + + Browser* browser_; +} + +// Initializes a new ExtensionShelfController. +- (id)initWithBrowser:(Browser*)browser + resizeDelegate:(id<ViewResizer>)resizeDelegate; + +// Makes the extension shelf view managed by this class visible. +- (IBAction)show:(id)sender; + +// Makes the extension shelf view managed by this class invisible. +- (IBAction)hide:(id)sender; + +// Returns the height this shelf has when it's visible (which is different from +// the frame's height if the shelf is hidden). +- (CGFloat)height; + +// Call this once this shelf's view has been inserted into a superview. It will +// create the internal bridge object to chrome's extension system and call +// cacheDisplayInRect:toBitmapImageRep: on the |view|, which requires that it is +// in a superview. +- (void)wasInsertedIntoWindow; + +@end diff --git a/chrome/browser/cocoa/extension_shelf_controller.mm b/chrome/browser/cocoa/extension_shelf_controller.mm new file mode 100644 index 0000000..ec7a40b --- /dev/null +++ b/chrome/browser/cocoa/extension_shelf_controller.mm @@ -0,0 +1,356 @@ +// 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 "extension_shelf_controller.h" + +#include "base/mac_util.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/extensions/extension_shelf_model.h" +#include "skia/ext/skia_utils_mac.h" + +namespace { + +const int kExtensionShelfPaddingTop = 1; +const int kToolstripPadding = 2; + +} + +// This class manages the extensions ("toolstrips") on the shelf. It listens to +// events sent by the extension system, and acts as a bridge between that and +// the cocoa world. +class ExtensionShelfMac : public ExtensionShelfModelObserver { + public: + ExtensionShelfMac(Browser* browser, ExtensionShelfController* controller); + virtual ~ExtensionShelfMac(); + + // ExtensionShelfModelObserver + virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index); + virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index); + virtual void ToolstripMoved(ExtensionHost* toolstrip, + int from_index, + int to_index); + virtual void ToolstripChangedAt(ExtensionHost* toolstrip, int index); + virtual void ExtensionShelfEmpty(); + virtual void ShelfModelReloaded(); + virtual void ShelfModelDeleting(); + + // Determines what is our target height and sets it. + void AdjustHeight(); + + private: + class Toolstrip; + + void Show(); + void Hide(); + + // Create the contents of the extension shelf. + void Init(Profile* profile); + + // Loads the background image into memory, or does nothing if already loaded. + void InitBackground(); + + // Re-inserts all toolstrips from the model. Must be called when the shelf + // contains no toolstrips. + void LoadFromModel(); + + void DeleteToolstrips(); + + Toolstrip* ToolstripAtIndex(int index); + + ExtensionShelfController* controller_; // weak, owns us + + Browser* browser_; // weak + + // Lazily-initialized background for toolstrips. + scoped_ptr<SkBitmap> background_; + + // The model representing the toolstrips on the shelf. + ExtensionShelfModel* model_; // weak + + // Set of toolstrip views which are really on the shelf. + std::set<Toolstrip*> toolstrips_; + + // Stores if we are currently layouting items. + bool is_adjusting_height_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionShelfMac); +}; + +// This class represents a single extension ("toolstrip") on the extension +// shelf. +class ExtensionShelfMac::Toolstrip { + public: + explicit Toolstrip(ExtensionHost* host) + : host_(host) { + DCHECK(host_->view()); + Init(); + } + + // Inserts the native NSView belonging to this extension into the view that + // belongs to |controller|. Makes sure the controller is notified when the + // extension's |frame| changes. + void AddToolstripToController(ExtensionShelfController* controller); + + // Removes the native NSView belonging to this extension from the view that + // belongs to |controller|. Removes |controller| as a frame size observer. + void RemoveToolstripFromController(ExtensionShelfController* controller); + + // Sets the image that is used by the extension. + void SetBackground(const SkBitmap& background) { + host_->view()->SetBackground(background); + } + + // Returns the native NSView belonging to this extension. + gfx::NativeView native_view() { + return host_->view()->native_view(); + } + + private: + void Init(); + + ExtensionHost* host_; // weak + + const std::string extension_name_; + + private: + DISALLOW_COPY_AND_ASSIGN(Toolstrip); +}; + +void ExtensionShelfMac::Toolstrip::AddToolstripToController( + ExtensionShelfController* controller) { + NSView* toolstrip_view = host_->view()->native_view(); + [[controller view] addSubview:toolstrip_view]; + + [[NSNotificationCenter defaultCenter] + addObserver:controller + selector:@selector(updateVisibility:) + name:NSViewFrameDidChangeNotification + object:toolstrip_view]; +} + +void ExtensionShelfMac::Toolstrip::RemoveToolstripFromController( + ExtensionShelfController* controller) { + [host_->view()->native_view() removeFromSuperview]; + + [[NSNotificationCenter defaultCenter] + removeObserver:controller + name:NSViewFrameDidChangeNotification + object:host_->view()->native_view()]; +} + +void ExtensionShelfMac::Toolstrip::Init() { + host_->view()->set_is_toolstrip(true); +} + +ExtensionShelfMac::ExtensionShelfMac(Browser* browser, + ExtensionShelfController* controller) + : controller_(controller), + browser_(browser), + model_(browser->extension_shelf_model()), + is_adjusting_height_(false) { + if (model_) // Can be NULL in tests. + Init(browser_->profile()); +} + +ExtensionShelfMac::~ExtensionShelfMac() { + DeleteToolstrips(); + if (model_) + model_->RemoveObserver(this); +} + +void ExtensionShelfMac::Show() { + [controller_ show:nil]; +} + +void ExtensionShelfMac::Hide() { + [controller_ hide:nil]; +} + +void ExtensionShelfMac::ToolstripInsertedAt(ExtensionHost* host, + int index) { + InitBackground(); + Toolstrip* toolstrip = new Toolstrip(host); + toolstrip->SetBackground(*background_.get()); + toolstrip->AddToolstripToController(controller_); + toolstrips_.insert(toolstrip); + model_->SetToolstripDataAt(index, toolstrip); + + AdjustHeight(); +} + +void ExtensionShelfMac::ToolstripRemovingAt(ExtensionHost* host, + int index) { + Toolstrip* toolstrip = ToolstripAtIndex(index); + toolstrip->RemoveToolstripFromController(controller_); + toolstrips_.erase(toolstrip); + model_->SetToolstripDataAt(index, NULL); + delete toolstrip; + + AdjustHeight(); +} + +void ExtensionShelfMac::ToolstripMoved(ExtensionHost* host, + int from_index, + int to_index) { + // TODO(thakis): Implement reordering toolstrips. + AdjustHeight(); +} + +void ExtensionShelfMac::ToolstripChangedAt( + ExtensionHost* toolstrip, int index) { + // TODO(thakis): Implement changing toolstrips. + AdjustHeight(); +} + +void ExtensionShelfMac::ExtensionShelfEmpty() { + AdjustHeight(); +} + +void ExtensionShelfMac::ShelfModelReloaded() { + DeleteToolstrips(); + LoadFromModel(); +} + +void ExtensionShelfMac::ShelfModelDeleting() { + DeleteToolstrips(); + model_->RemoveObserver(this); + model_ = NULL; +} + +void ExtensionShelfMac::Init(Profile* profile) { + LoadFromModel(); + model_->AddObserver(this); +} + +void ExtensionShelfMac::InitBackground() { + if (background_.get()) + return; + + // If this is called while the shelf is invisible, shortly resize the shelf so + // that it can paint itself. + NSRect current_frame = [[controller_ view] frame]; + if (current_frame.size.height < [controller_ height]) { + NSRect new_frame = current_frame; + new_frame.size.height = [controller_ height]; + [[controller_ view] setFrame:new_frame]; + } + + // The background is tiled horizontally in the toolstrip. Hence, its width + // should not be too small so that tiling is fast, and not too large, so that + // not too much memory is needed -- but the exact width doesn't really matter. + const CGFloat kBackgroundTileWidth = 100; + + // Paint shelf background into an SkBitmap. If we decide to keep the shelf, we + // need to do this for both the "main window" and "not main window" shadings. + NSRect background_rect = NSMakeRect( + 0, 0, + kBackgroundTileWidth, [controller_ height] - kExtensionShelfPaddingTop); + NSBitmapImageRep* bitmap_rep = [[controller_ view] + bitmapImageRepForCachingDisplayInRect:background_rect]; + + [[controller_ view] cacheDisplayInRect:background_rect + toBitmapImageRep:bitmap_rep]; + background_.reset(new SkBitmap(gfx::CGImageToSkBitmap([bitmap_rep CGImage]))); + + // Restore old frame. + [[controller_ view] setFrame:current_frame]; +} + +void ExtensionShelfMac::AdjustHeight() { + if (model_->empty() || toolstrips_.empty()) { + // It's possible that |model_| is not empty, but |toolstrips_| are empty + // when removing the last toolstrip. + DCHECK(toolstrips_.empty()); + Hide(); + return; + } + + if (is_adjusting_height_) + return; + is_adjusting_height_ = true; + + Show(); + + // Lay out items horizontally from left to right. This method's name is + // misleading, but matches linux and windows for now. + CGFloat x = 0; + for (std::set<Toolstrip*>::iterator iter = toolstrips_.begin(); + iter != toolstrips_.end(); ++iter) { + NSView* view = (*iter)->native_view(); + NSRect frame = [view frame]; + frame.origin.x = x; + frame.origin.y = 0; + frame.size.height = [controller_ height] - kExtensionShelfPaddingTop; + [view setFrame:frame]; + x += frame.size.width + kToolstripPadding; + } + + is_adjusting_height_ = false; +} + +void ExtensionShelfMac::LoadFromModel() { + DCHECK(toolstrips_.empty()); + int count = model_->count(); + for (int i = 0; i < count; ++i) + ToolstripInsertedAt(model_->ToolstripAt(i).host, i); + AdjustHeight(); +} + +void ExtensionShelfMac::DeleteToolstrips() { + for (std::set<Toolstrip*>::iterator iter = toolstrips_.begin(); + iter != toolstrips_.end(); ++iter) { + (*iter)->RemoveToolstripFromController(controller_); + delete *iter; + } + toolstrips_.clear(); +} + +ExtensionShelfMac::Toolstrip* ExtensionShelfMac::ToolstripAtIndex(int index) { + return static_cast<Toolstrip*>(model_->ToolstripAt(index).data); +} + + +@implementation ExtensionShelfController + +- (id)initWithBrowser:(Browser*)browser + resizeDelegate:(id<ViewResizer>)resizeDelegate { + if ((self = [super initWithNibName:@"ExtensionShelf" + bundle:mac_util::MainAppBundle()])) { + resizeDelegate_ = resizeDelegate; + browser_ = browser; + shelfHeight_ = [[self view] bounds].size.height; + + NSRect frame = [[self view] frame]; + frame.size.height = 0; + [[self view] setFrame:frame]; + } + return self; +} + +- (void)wasInsertedIntoWindow { + // The bridge_ calls cacheDisplayInRect:toBitmapImageRep:, which requires that + // the view is in a superview to work. Hence, create the bridge object no + // sooner. + DCHECK(bridge_.get() == NULL); + bridge_.reset(new ExtensionShelfMac(browser_, self)); +} + +- (IBAction)show:(id)sender { + [resizeDelegate_ resizeView:[self view] newHeight:shelfHeight_]; +} + +- (IBAction)hide:(id)sender { + [resizeDelegate_ resizeView:[self view] newHeight:0]; +} + +- (CGFloat)height { + return shelfHeight_; +} + +- (void)updateVisibility:(id)sender { + if(bridge_.get()) + bridge_->AdjustHeight(); +} + +@end diff --git a/chrome/browser/cocoa/extension_shelf_controller_unittest.mm b/chrome/browser/cocoa/extension_shelf_controller_unittest.mm new file mode 100644 index 0000000..ddbca2a --- /dev/null +++ b/chrome/browser/cocoa/extension_shelf_controller_unittest.mm @@ -0,0 +1,56 @@ +// 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 "base/scoped_nsobject.h" +#include "chrome/browser/cocoa/browser_test_helper.h" +#import "chrome/browser/cocoa/cocoa_test_helper.h" +#import "chrome/browser/cocoa/extension_shelf_controller.h" +#import "chrome/browser/cocoa/view_resizer_pong.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +class ExtensionShelfControllerTest : public PlatformTest { + public: + ExtensionShelfControllerTest() { + resizeDelegate_.reset([[ViewResizerPong alloc] init]); + + NSRect frame = NSMakeRect(0, 0, 100, 30); + controller_.reset([[ExtensionShelfController alloc] + initWithBrowser:helper_.browser() + resizeDelegate:resizeDelegate_.get()]); + } + + CocoaTestHelper cocoa_helper_; // Inits Cocoa, creates window, etc... + BrowserTestHelper helper_; + scoped_nsobject<ExtensionShelfController> controller_; + scoped_nsobject<ViewResizerPong> resizeDelegate_; +}; + +// Check that |hide:| tells the delegate to set the shelf's height to zero. +TEST_F(ExtensionShelfControllerTest, HideSetsHeightToZero) { + [resizeDelegate_ setHeight:10]; + [controller_ hide:nil]; + EXPECT_EQ(0, [resizeDelegate_ height]); +} + +// Check that |show:| tells the delegate to set the shelf's height to the +// shelf's desired height. +TEST_F(ExtensionShelfControllerTest, ShowSetsHeightToHeight) { + [resizeDelegate_ setHeight:0]; + [controller_ show:nil]; + EXPECT_GT([controller_ height], 0); + EXPECT_EQ([controller_ height], [resizeDelegate_ height]); +} + +// Test adding to the view hierarchy, mostly to ensure nothing leaks or crashes. +TEST_F(ExtensionShelfControllerTest, Add) { + [cocoa_helper_.contentView() addSubview:[controller_ view]]; + [controller_ wasInsertedIntoWindow]; +} + +} // namespace diff --git a/chrome/browser/cocoa/extension_view_mac.h b/chrome/browser/cocoa/extension_view_mac.h new file mode 100644 index 0000000..e358d1b --- /dev/null +++ b/chrome/browser/cocoa/extension_view_mac.h @@ -0,0 +1,65 @@ +// 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_COCOA_EXTENSION_VIEW_MAC_H_ +#define CHROME_BROWSER_COCOA_EXTENSION_VIEW_MAC_H_ + +#include "base/basictypes.h" +#include "base/gfx/native_widget_types.h" + +class Browser; +class ExtensionHost; +class RenderViewHost; +class RenderWidgetHostViewMac; +class SkBitmap; + +// This class represents extension views. An extension view internally contains +// a bridge to an extension process, which draws to the extension view's +// native view object through IPC. +class ExtensionViewMac { + public: + ExtensionViewMac(ExtensionHost* extension_host, Browser* browser); + ~ExtensionViewMac(); + + // Starts the extension process and creates the native view. You must call + // this method before calling any of this class's other methods. + void Init(); + + // Returns the extension's native view. + gfx::NativeView native_view(); + + // Returns the browser the extension belongs to. + Browser* browser() const { return browser_; } + + // Does this extension live as a toolstrip in an extension shelf? + bool is_toolstrip() const { return is_toolstrip_; } + void set_is_toolstrip(bool is_toolstrip) { is_toolstrip_ = is_toolstrip; } + + // Sets the extensions's background image. + void SetBackground(const SkBitmap& background); + + // Method for the ExtensionHost to notify us about the correct width for + // extension contents. + void UpdatePreferredWidth(int pref_width); + + private: + RenderViewHost* render_view_host() const; + + void CreateWidgetHostView(); + + // True if the contents are being displayed inside the extension shelf. + bool is_toolstrip_; + + Browser* browser_; // weak + + ExtensionHost* extension_host_; // weak + + // Created by us, but owned by its |native_view()|. We |release| the + // rwhv's native view in our destructor, effectively freeing this. + RenderWidgetHostViewMac* render_widget_host_view_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionViewMac); +}; + +#endif // CHROME_BROWSER_COCOA_EXTENSION_VIEW_MAC_H_ diff --git a/chrome/browser/cocoa/extension_view_mac.mm b/chrome/browser/cocoa/extension_view_mac.mm new file mode 100644 index 0000000..a64eee8 --- /dev/null +++ b/chrome/browser/cocoa/extension_view_mac.mm @@ -0,0 +1,68 @@ +// 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/cocoa/extension_view_mac.h" + +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view_mac.h" + +ExtensionViewMac::ExtensionViewMac(ExtensionHost* extension_host, + Browser* browser) + : is_toolstrip_(true), + browser_(browser), + extension_host_(extension_host), + render_widget_host_view_(NULL) { + DCHECK(extension_host_); +} + +ExtensionViewMac::~ExtensionViewMac() { + if (render_widget_host_view_) + [render_widget_host_view_->native_view() release]; +} + +void ExtensionViewMac::Init() { + CreateWidgetHostView(); +} + +gfx::NativeView ExtensionViewMac::native_view() { + DCHECK(render_widget_host_view_); + return render_widget_host_view_->native_view(); +} + +RenderViewHost* ExtensionViewMac::render_view_host() const { + return extension_host_->render_view_host(); +} + +void ExtensionViewMac::SetBackground(const SkBitmap& background) { + DCHECK(render_widget_host_view_); + render_widget_host_view_->SetBackground(background); +} + +void ExtensionViewMac::UpdatePreferredWidth(int pref_width) { + // TODO(thakis, erikkay): Windows does some tricks to resize the extension + // view not before it's visible. Do something similar here. + + // No need to use CA here, our caller calls us repeatedly to animate the + // resizing. + NSView* view = native_view(); + NSRect frame = [view frame]; + frame.size.width = pref_width; + + // RenderWidgetHostViewCocoa overrides setFrame but not setFrameSize. + [view setFrame:frame]; + [view setNeedsDisplay:YES]; +} + +void ExtensionViewMac::CreateWidgetHostView() { + DCHECK(!render_widget_host_view_); + render_widget_host_view_ = new RenderWidgetHostViewMac(render_view_host()); + + // The RenderWidgetHostViewMac is owned by its native view, which is created + // in an autoreleased state. retain it, so that it doesn't immediately + // disappear. + [render_widget_host_view_->native_view() retain]; + + extension_host_->CreateRenderView(render_widget_host_view_); +} diff --git a/chrome/browser/cocoa/status_bubble_mac.h b/chrome/browser/cocoa/status_bubble_mac.h index 54899c2..de305de 100644 --- a/chrome/browser/cocoa/status_bubble_mac.h +++ b/chrome/browser/cocoa/status_bubble_mac.h @@ -55,9 +55,6 @@ class StatusBubbleMac : public StatusBubble { // How vertically offset the bubble is from its root position. int offset_; - - // Is the download shelf visible. - bool is_download_shelf_visible_; }; // Delegate interface that allows the StatusBubble to query its delegate about diff --git a/chrome/browser/cocoa/status_bubble_mac.mm b/chrome/browser/cocoa/status_bubble_mac.mm index 1f2ecfa..7b592c6 100644 --- a/chrome/browser/cocoa/status_bubble_mac.mm +++ b/chrome/browser/cocoa/status_bubble_mac.mm @@ -39,8 +39,7 @@ StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) delegate_(delegate), window_(nil), status_text_(nil), - url_text_(nil), - is_download_shelf_visible_(false) { + url_text_(nil) { } StatusBubbleMac::~StatusBubbleMac() { @@ -127,12 +126,10 @@ void StatusBubbleMac::MouseMoved() { NSRect window_frame = [window_ frame]; window_frame.origin = [parent_ frame].origin; - // Adjust the position to sit on top of download shelf. + // Adjust the position to sit on top of download and extension shelves. // |delegate_| can be nil during unit tests. - if (is_download_shelf_visible_) { - if ([delegate_ respondsToSelector:@selector(verticalOffsetForStatusBubble)]) - window_frame.origin.y += [delegate_ verticalOffsetForStatusBubble]; - } + if ([delegate_ respondsToSelector:@selector(verticalOffsetForStatusBubble)]) + window_frame.origin.y += [delegate_ verticalOffsetForStatusBubble]; // Get the cursor position relative to the popup. cursor_location.x -= NSMaxX(window_frame); @@ -179,7 +176,6 @@ void StatusBubbleMac::MouseMoved() { } void StatusBubbleMac::UpdateDownloadShelfVisibility(bool visible) { - is_download_shelf_visible_ = visible; } void StatusBubbleMac::Create() { diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index 0bafeec..f8503f3 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -71,6 +71,9 @@ void ExtensionHost::CreateView(Browser* browser) { #elif defined(OS_LINUX) view_.reset(new ExtensionViewGtk(this, browser)); view_->Init(); +#elif defined(OS_MACOSX) + view_.reset(new ExtensionViewMac(this, browser)); + view_->Init(); #else // TODO(port) NOTREACHED(); @@ -130,10 +133,8 @@ void ExtensionHost::Observe(NotificationType type, } void ExtensionHost::UpdatePreferredWidth(int pref_width) { -#if defined(TOOLKIT_VIEWS) || defined(OS_LINUX) if (view_.get()) view_->UpdatePreferredWidth(pref_width); -#endif } void ExtensionHost::RenderViewGone(RenderViewHost* render_view_host) { @@ -180,6 +181,8 @@ void ExtensionHost::DidNavigate(RenderViewHost* render_view_host, } void ExtensionHost::InsertCssIfToolstrip() { + + // TODO(erikkay): Make these ifdefs go away -- http://crbug.com/21939 #if defined(TOOLKIT_VIEWS) ExtensionView* view = view_.get(); if (!view) @@ -190,8 +193,12 @@ void ExtensionHost::InsertCssIfToolstrip() { view->SetDidInsertCSS(true); return; } -#elif defined(OS_LINUX) +#elif defined(OS_LINUX) || defined(OS_MACOSX) +#if defined(OS_LINUX) ExtensionViewGtk* view = view_.get(); +#else + ExtensionViewMac* view = view_.get(); +#endif if (!view || !view->is_toolstrip()) return; #endif @@ -218,14 +225,12 @@ void ExtensionHost::InsertCssIfToolstrip() { pos = css.find(kToolstripTextColorSubstitution); } -#if defined(TOOLKIT_VIEWS) || defined(OS_LINUX) // TODO(erikkay) this injection should really happen in the renderer. // When the Jerry's view type change lands, investigate moving this there. // As a toolstrip, inject our toolstrip CSS to make it easier for toolstrips // to blend in with the chrome UI. render_view_host()->InsertCSSInWebFrame(L"", css, "ToolstripDefaultCss"); -#endif } void ExtensionHost::DidStopLoading(RenderViewHost* render_view_host) { @@ -362,10 +367,9 @@ void ExtensionHost::HandleMouseLeave() { } Browser* ExtensionHost::GetBrowser() { -#if defined(OS_WIN) || defined(OS_LINUX) if (view_.get()) return view_->browser(); -#endif + Profile* profile = render_view_host()->process()->profile(); Browser* browser = BrowserList::GetLastActiveWithProfile(profile); diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h index b6e25e5..49a75f1 100644 --- a/chrome/browser/extensions/extension_host.h +++ b/chrome/browser/extensions/extension_host.h @@ -15,6 +15,8 @@ #include "chrome/browser/views/extensions/extension_view.h" #elif defined(OS_LINUX) #include "chrome/browser/gtk/extension_view_gtk.h" +#elif defined(OS_MACOSX) +#include "chrome/browser/cocoa/extension_view_mac.h" #endif #include "chrome/common/notification_registrar.h" @@ -48,6 +50,8 @@ class ExtensionHost : public RenderViewHostDelegate, ExtensionView* view() const { return view_.get(); } #elif defined(OS_LINUX) ExtensionViewGtk* view() const { return view_.get(); } +#elif defined(OS_MACOSX) + ExtensionViewMac* view() const { return view_.get(); } #else // TODO(port): implement void* view() const { return NULL; } @@ -158,6 +162,8 @@ class ExtensionHost : public RenderViewHostDelegate, scoped_ptr<ExtensionView> view_; #elif defined(OS_LINUX) scoped_ptr<ExtensionViewGtk> view_; +#elif defined(OS_MACOSX) + scoped_ptr<ExtensionViewMac> view_; #endif // The host for our HTML content. diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h index b8f4146..0e76e95 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.h +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h @@ -32,7 +32,7 @@ class RWHVMEditCommandHelper; @interface RenderWidgetHostViewCocoa : BaseView <RenderWidgetHostViewMacOwner, NSTextInput, NSChangeSpelling> { @private - RenderWidgetHostViewMac* renderWidgetHostView_; + RenderWidgetHostViewMac* renderWidgetHostView_; // Owned by us. BOOL canBeKeyView_; BOOL closeOnDeactivate_; scoped_ptr<RWHVMEditCommandHelper> editCommand_helper_; @@ -107,6 +107,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { virtual gfx::Rect GetWindowRect(); virtual gfx::Rect GetRootWindowRect(); virtual void SetActive(bool active); + virtual void SetBackground(const SkBitmap& background); void KillSelf(); diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm index 3587616..21fce5f 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm @@ -14,6 +14,7 @@ #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/spellchecker_platform_engine.h" #include "chrome/common/native_web_keyboard_event.h" +#include "chrome/common/render_messages.h" #include "skia/ext/platform_canvas.h" #include "webkit/api/public/mac/WebInputEventFactory.h" #include "webkit/api/public/WebInputEvent.h" @@ -56,6 +57,9 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) is_hidden_(false), shutdown_factory_(this), parent_view_(NULL) { + // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| goes away. + // Since we autorelease it, our caller must put |native_view()| into the view + // hierarchy right after calling us. cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc] initWithRenderWidgetHostViewMac:this] autorelease]; render_widget_host_->set_view(this); @@ -419,6 +423,12 @@ void RenderWidgetHostViewMac::SetActive(bool active) { render_widget_host_->SetActive(active); } +void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { + RenderWidgetHostView::SetBackground(background); + if (render_widget_host_) + render_widget_host_->Send(new ViewMsg_SetBackground( + render_widget_host_->routing_id(), background)); +} // RenderWidgetHostViewCocoa --------------------------------------------------- |