diff options
-rw-r--r-- | chrome/browser/cocoa/autocomplete_text_field.mm | 18 | ||||
-rw-r--r-- | chrome/browser/cocoa/autocomplete_text_field_cell.h | 20 | ||||
-rw-r--r-- | chrome/browser/cocoa/autocomplete_text_field_cell.mm | 165 | ||||
-rw-r--r-- | chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm | 5 | ||||
-rw-r--r-- | chrome/browser/cocoa/autocomplete_text_field_unittest.mm | 3 | ||||
-rw-r--r-- | chrome/browser/cocoa/location_bar_view_mac.h | 125 | ||||
-rw-r--r-- | chrome/browser/cocoa/location_bar_view_mac.mm | 282 |
7 files changed, 551 insertions, 67 deletions
diff --git a/chrome/browser/cocoa/autocomplete_text_field.mm b/chrome/browser/cocoa/autocomplete_text_field.mm index d0b61dd..797fb50 100644 --- a/chrome/browser/cocoa/autocomplete_text_field.mm +++ b/chrome/browser/cocoa/autocomplete_text_field.mm @@ -65,7 +65,7 @@ // decoration area. This allows the user to click-drag starting from // a decoration area and get the expected selection behaviour, // likewise for multiple clicks in those areas. -- (void)mouseDown:(NSEvent *)theEvent { +- (void)mouseDown:(NSEvent*)theEvent { const NSPoint locationInWindow = [theEvent locationInWindow]; const NSPoint location = [self convertPoint:locationInWindow fromView:nil]; @@ -108,14 +108,26 @@ return; } - // Check to see if the user clicked the security hint icon in the cell. If so, - // we need to display the page info window. + // If the user clicked the security hint icon in the cell, display the page + // info window. const NSRect hintIconFrame = [cell securityImageFrameForFrame:[self bounds]]; if (NSMouseInRect(location, hintIconFrame, [self isFlipped])) { [cell onSecurityIconMousePressed]; return; } + // If the user clicked a Page Action icon, execute its action. + const NSRect iconFrame([self bounds]); + const size_t pageActionCount = [cell pageActionCount]; + for (size_t i = 0; i < pageActionCount; ++i) { + NSRect pageActionFrame = [cell pageActionFrameForIndex:i inFrame:iconFrame]; + if (NSMouseInRect(location, pageActionFrame, [self isFlipped])) { + // TODO(pamg): Do we need to send the event? + [cell onPageActionMousePressedIn:pageActionFrame forIndex:i]; + return; + } + } + NSText* editor = [self currentEditor]; // We should only be here if we accepted first-responder status and diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.h b/chrome/browser/cocoa/autocomplete_text_field_cell.h index 65749a6..01e0468 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_cell.h +++ b/chrome/browser/cocoa/autocomplete_text_field_cell.h @@ -28,6 +28,11 @@ // Display is exclusive WRT the |hintString_| and |keywordString_|. // This may be NULL during testing. LocationBarViewMac::SecurityImageView* security_image_view_; + + // List of views showing visible Page Actions. Owned by the location bar. + // Display is exclusive WRT the |hintString_| and |keywordString_|. + // This may be NULL during testing. + LocationBarViewMac::PageActionViewList* page_action_views_; } // Chooses |partialString| if |width| won't fit |fullString|. Strings @@ -50,15 +55,28 @@ - (void)clearKeywordAndHint; - (void)setSecurityImageView:(LocationBarViewMac::SecurityImageView*)view; +- (void)setPageActionViewList:(LocationBarViewMac::PageActionViewList*)list; + +// Returns the total number of installed Page Actions, visible or not. +- (size_t)pageActionCount; // Called when the security icon is visible and clicked. Passed through to the // security_image_view_ to handle the click (i.e., show the page info dialog). - (void)onSecurityIconMousePressed; -// Return the portion of the cell to use for displaying the security (SSL lock) +// Returns the portion of the cell to use for displaying the security (SSL lock) // icon, leaving space for its label if any. - (NSRect)securityImageFrameForFrame:(NSRect)cellFrame; +// Returns the portion of the cell to use for displaying the Page Action icon +// at the given index. May be NSZeroRect if the index's action is not visible. +- (NSRect)pageActionFrameForIndex:(size_t)index inFrame:(NSRect)cellFrame; + +// Called when the Page Action at the given index, whose icon is drawn in the +// iconFrame, is visible and clicked. Passed through to the list of views to +// handle the click. +- (void)onPageActionMousePressedIn:(NSRect)iconFrame forIndex:(size_t)index; + @end // Internal methods here exposed for unit testing. diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.mm b/chrome/browser/cocoa/autocomplete_text_field_cell.mm index 2104a31..5afcf10 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_cell.mm +++ b/chrome/browser/cocoa/autocomplete_text_field_cell.mm @@ -194,6 +194,10 @@ CGFloat WidthForKeyword(NSAttributedString* keywordString) { hintString_.reset(); } +- (void)setPageActionViewList:(LocationBarViewMac::PageActionViewList*)list { + page_action_views_ = list; +} + - (void)setSecurityImageView:(LocationBarViewMac::SecurityImageView*)view { security_image_view_ = view; } @@ -202,6 +206,10 @@ CGFloat WidthForKeyword(NSAttributedString* keywordString) { security_image_view_->OnMousePressed(); } +- (void)onPageActionMousePressedIn:(NSRect)iconFrame forIndex:(size_t)index { + page_action_views_->OnMousePressed(iconFrame, index); +} + // Overriden to account for the hint strings and hint icons. - (NSRect)textFrameForFrame:(NSRect)cellFrame { NSRect textFrame([super textFrameForFrame:cellFrame]); @@ -226,14 +234,30 @@ CGFloat WidthForKeyword(NSAttributedString* keywordString) { textFrame.origin.x += keywordWidth; textFrame.size.width = NSMaxX(cellFrame) - NSMinX(textFrame); } - } else if (security_image_view_ && security_image_view_->IsVisible()) { - NSImage* image = security_image_view_->GetImage(); - CGFloat width = [image size].width; - width += kIconHorizontalPad * 2; - NSAttributedString* label = security_image_view_->GetLabel(); - if (label) { - width += ceil([label size].width) + kHintXOffset; + } else { + // Account for the lock icon, if any, and any visible Page Action icons. + CGFloat width = 0; + const size_t iconCount = [self pageActionCount]; + for (size_t i = 0; i < iconCount; ++i) { + LocationBarViewMac::PageActionImageView* view = + page_action_views_->ViewAt(i); + NSImage* image = view->GetImage(); + if (image && view->IsVisible()) { + width += [image size].width + kIconHorizontalPad; + } + } + + if (security_image_view_ && security_image_view_->IsVisible()) { + width += [security_image_view_->GetImage() size].width + + kIconHorizontalPad; + NSAttributedString* label = security_image_view_->GetLabel(); + if (label) { + width += ceil([label size].width) + kHintXOffset; + } } + if (width > 0) + width += kIconHorizontalPad; + if (width < NSWidth(cellFrame)) { textFrame.size.width -= width; } @@ -242,40 +266,76 @@ CGFloat WidthForKeyword(NSAttributedString* keywordString) { return textFrame; } -- (NSRect)imageFrameForFrame:(NSRect)cellFrame - withImageView:(LocationBarViewMac::LocationBarImageView*)image_view { - if (!image_view->IsVisible()) { +// Returns a rect of size |imageSize| centered vertically and right-justified in +// the |box|, with its top left corner |margin| pixels from the right end of the +// box. (The image thus occupies part of the |margin|.) +- (NSRect)rightJustifyImage:(NSSize)imageSize + inRect:(NSRect)box + withMargin:(CGFloat)margin { + box.origin.x += box.size.width - margin; + box.origin.y += floor((box.size.height - imageSize.height) / 2); + box.size = imageSize; + return box; +} + +- (NSRect)securityImageFrameForFrame:(NSRect)cellFrame { + if (!security_image_view_ || !security_image_view_->IsVisible()) { return NSZeroRect; } - const NSSize imageRect = [image_view->GetImage() size]; - CGFloat labelWidth = 0; - NSAttributedString* label = image_view->GetLabel(); + + // Calculate the total width occupied by the image, label, and padding. + NSSize imageSize = [security_image_view_->GetImage() size]; + CGFloat widthUsed = imageSize.width + kIconHorizontalPad; + NSAttributedString* label = security_image_view_->GetLabel(); if (label) { - labelWidth = ceil([label size].width) + kHintXOffset; + widthUsed += ceil([label size].width) + kHintXOffset; } - // Move the rect that we're drawing into to the far right, minus - // enough space for the label (if present). - cellFrame.origin.x += cellFrame.size.width - imageRect.width; - cellFrame.origin.x -= labelWidth; - // Add back the padding - cellFrame.origin.x -= kIconHorizontalPad; - - // Center the image vertically in the frame - cellFrame.origin.y += - floor((cellFrame.size.height - imageRect.height) / 2); - - // Set the drawing size to the image size - cellFrame.size = imageRect; + return [self rightJustifyImage:imageSize + inRect:cellFrame + withMargin:widthUsed]; +} - return cellFrame; +- (size_t)pageActionCount { + // page_action_views_ may be NULL during testing. + if (!page_action_views_) + return 0; + return page_action_views_->Count(); } -- (NSRect)securityImageFrameForFrame:(NSRect)cellFrame { - if (!security_image_view_) { +- (NSRect)pageActionFrameForIndex:(size_t)index inFrame:(NSRect)cellFrame { + LocationBarViewMac::PageActionImageView* view = + page_action_views_->ViewAt(index); + const NSImage* icon = view->GetImage(); + if (!icon || !view->IsVisible()) { return NSZeroRect; } - return [self imageFrameForFrame:cellFrame withImageView:security_image_view_]; + + // Compute the amount of space used by this icon plus any other icons to its + // right. It's terribly inefficient to do this anew every time, but easy to + // understand. It should be fine for 5 or 10 installed Page Actions, perhaps + // too slow for 100. + // TODO(pamg): Refactor to avoid this if performance is a problem. + const NSRect securityIconRect = [self securityImageFrameForFrame:cellFrame]; + CGFloat widthUsed = 0.0; + if (NSWidth(securityIconRect) > 0) { + widthUsed += NSMaxX(cellFrame) - NSMinX(securityIconRect); + } + for (size_t i = 0; i <= index; ++i) { + view = page_action_views_->ViewAt(i); + if (view->IsVisible()) { + NSImage* image = view->GetImage(); + if (image) { + // Page Action icons don't have labels. Don't compute space for them. + widthUsed += [image size].width + kIconHorizontalPad; + } + } + } + widthUsed += kIconHorizontalPad; + + return [self rightJustifyImage:[icon size] + inRect:cellFrame + withMargin:widthUsed]; } - (void)drawHintWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { @@ -319,27 +379,25 @@ CGFloat WidthForKeyword(NSAttributedString* keywordString) { [keywordString_.get() drawInRect:infoFrame]; } -- (void)drawImageView:(LocationBarViewMac::LocationBarImageView*)image_view - withFrame:(NSRect)cellFrame +- (void)drawImageView:(LocationBarViewMac::LocationBarImageView*)imageView + inFrame:(NSRect)imageFrame inView:(NSView*)controlView { - // If there's a label, draw it to the right of the icon. - CGFloat labelWidth = 0; - NSAttributedString* label = image_view->GetLabel(); + // If there's a label, draw it to the right of the icon. The caller must have + // left sufficient space. + NSAttributedString* label = imageView->GetLabel(); if (label) { - labelWidth = ceil([label size].width) + kHintXOffset; - NSRect textFrame(NSMakeRect(NSMaxX(cellFrame) - labelWidth, - cellFrame.origin.y + kIconLabelYOffset, + CGFloat labelWidth = ceil([label size].width) + kHintXOffset; + NSRect textFrame(NSMakeRect(NSMaxX(imageFrame) + kIconHorizontalPad, + imageFrame.origin.y + kIconLabelYOffset, labelWidth, - cellFrame.size.height - kIconLabelYOffset)); + imageFrame.size.height - kIconLabelYOffset)); [label drawInRect:textFrame]; } // Draw the entire image. NSRect imageRect = NSZeroRect; - NSImage* image = image_view->GetImage(); + NSImage* image = imageView->GetImage(); image.size = [image size]; - NSRect imageFrame([self imageFrameForFrame:cellFrame - withImageView:image_view]); [image setFlipped:[controlView isFlipped]]; [image drawInRect:imageFrame fromRect:imageRect @@ -352,10 +410,23 @@ CGFloat WidthForKeyword(NSAttributedString* keywordString) { [self drawHintWithFrame:cellFrame inView:controlView]; } else if (keywordString_) { [self drawKeywordWithFrame:cellFrame inView:controlView]; - } else if (security_image_view_ && security_image_view_->IsVisible()) { - [self drawImageView:security_image_view_ - withFrame:cellFrame - inView:controlView]; + } else { + if (security_image_view_ && security_image_view_->IsVisible()) { + [self drawImageView:security_image_view_ + inFrame:[self securityImageFrameForFrame:cellFrame] + inView:controlView]; + } + + const size_t pageActionCount = [self pageActionCount]; + for (size_t i = 0; i < pageActionCount; ++i) { + LocationBarViewMac::PageActionImageView* view = + page_action_views_->ViewAt(i); + if (view && view->IsVisible()) { + [self drawImageView:view + inFrame:[self pageActionFrameForIndex:i inFrame:cellFrame] + inView:controlView]; + } + } } [super drawInteriorWithFrame:cellFrame inView:controlView]; diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm b/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm index 79f0ecb..b7f324c 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm +++ b/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm @@ -21,7 +21,8 @@ const CGFloat kNarrowWidth(5.0); class AutocompleteTextFieldCellTest : public CocoaTest { public: - AutocompleteTextFieldCellTest() : security_image_view_(NULL, NULL) { + AutocompleteTextFieldCellTest() : security_image_view_(NULL, NULL), + page_action_views_(NULL, NULL, NULL) { // Make sure this is wide enough to play games with the cell // decorations. const NSRect frame = NSMakeRect(0, 0, kWidth, 30); @@ -35,6 +36,7 @@ class AutocompleteTextFieldCellTest : public CocoaTest { [cell setEditable:YES]; [cell setBordered:YES]; [cell setSecurityImageView:&security_image_view_]; + [cell setPageActionViewList:&page_action_views_]; [view_ setCell:cell.get()]; [[test_window() contentView] addSubview:view_]; @@ -42,6 +44,7 @@ class AutocompleteTextFieldCellTest : public CocoaTest { NSTextField* view_; LocationBarViewMac::SecurityImageView security_image_view_; + LocationBarViewMac::PageActionViewList page_action_views_; }; // Basic view tests (AddRemove, Display). diff --git a/chrome/browser/cocoa/autocomplete_text_field_unittest.mm b/chrome/browser/cocoa/autocomplete_text_field_unittest.mm index afbc474..b52012b 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_unittest.mm +++ b/chrome/browser/cocoa/autocomplete_text_field_unittest.mm @@ -20,11 +20,10 @@ using ::testing::Return; using ::testing::StrictMock; namespace { -// Mock a SecurityImageView. class MockSecurityImageView : public LocationBarViewMac::SecurityImageView { public: MockSecurityImageView(Profile* profile, ToolbarModel* model) - : LocationBarViewMac::SecurityImageView(profile, model) {} + : LocationBarViewMac::SecurityImageView(profile, model) {} MOCK_METHOD0(OnMousePressed, bool()); }; diff --git a/chrome/browser/cocoa/location_bar_view_mac.h b/chrome/browser/cocoa/location_bar_view_mac.h index 9d4f3b5..6426172 100644 --- a/chrome/browser/cocoa/location_bar_view_mac.h +++ b/chrome/browser/cocoa/location_bar_view_mac.h @@ -5,12 +5,17 @@ #ifndef CHROME_BROWSER_COCOA_LOCATION_BAR_VIEW_MAC_H_ #define CHROME_BROWSER_COCOA_LOCATION_BAR_VIEW_MAC_H_ +#include <string> +#include <map> +#include <vector> + #import <Cocoa/Cocoa.h> #include "base/scoped_nsobject.h" #include "base/scoped_ptr.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" +#include "chrome/browser/extensions/image_loading_tracker.h" #include "chrome/browser/location_bar.h" #include "chrome/browser/toolbar_model.h" @@ -44,8 +49,8 @@ class LocationBarViewMac : public AutocompleteEditController, virtual void AcceptInputWithDisposition(WindowOpenDisposition disposition); virtual void FocusLocation(); virtual void FocusSearch(); - virtual void UpdatePageActions() { /* http://crbug.com/12281 */ } - virtual void InvalidatePageActions() { /* TODO(port): implement this */ } + virtual void UpdatePageActions(); + virtual void InvalidatePageActions(); virtual void SaveStateToContents(TabContents* contents); virtual void Revert(); virtual AutocompleteEditView* location_entry() { @@ -98,6 +103,7 @@ class LocationBarViewMac : public AutocompleteEditController, // Sets the image. void SetImage(NSImage* image); + void SetImage(SkBitmap* image); // Sets the label text, font, and color. |text| may be nil; |color| and // |font| are ignored if |text| is nil. @@ -111,8 +117,6 @@ class LocationBarViewMac : public AutocompleteEditController, const NSAttributedString* GetLabel() const { return label_; } bool IsVisible() const { return visible_; } - virtual bool OnMousePressed() = 0; - private: scoped_nsobject<NSImage> image_; @@ -139,7 +143,7 @@ class LocationBarViewMac : public AutocompleteEditController, // Sets the image to the appropriate icon. void SetImageShown(Image image); - // Shows the page info dialog. + // Shows the page info dialog. Virtual so it can be overridden for testing. virtual bool OnMousePressed(); private: @@ -157,6 +161,114 @@ class LocationBarViewMac : public AutocompleteEditController, DISALLOW_COPY_AND_ASSIGN(SecurityImageView); }; + // PageActionImageView is used to display the icon for a given Page Action + // and notify the extension when the icon is clicked. + class PageActionImageView : public LocationBarImageView, + public ImageLoadingTracker::Observer, + public NotificationObserver { + public: + PageActionImageView(LocationBarViewMac* owner, + Profile* profile, + ExtensionAction* page_action); + virtual ~PageActionImageView(); + + ExtensionAction* page_action() { return page_action_; } + + int current_tab_id() { return current_tab_id_; } + + void set_preview_enabled(bool preview_enabled) { + preview_enabled_ = preview_enabled; + } + + // Either notify listeners or show a popup depending on the Page Action. + // Virtual so it can be overridden for testing. + virtual bool OnMousePressed(NSRect bounds); + + // Overridden from ImageLoadingTracker. + virtual void OnImageLoaded(SkBitmap* image, size_t index); + + // Called to notify the Page Action that it should determine whether to be + // visible or hidden. |contents| is the TabContents that is active, |url| + // is the current page URL. + void UpdateVisibility(TabContents* contents, const GURL& url); + + private: + // Overridden from NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // The location bar view that owns us. + LocationBarViewMac* owner_; + + // The current profile (not owned by us). + Profile* profile_; + + // The Page Action that this view represents. The Page Action is not owned + // by us, it resides in the extension of this particular profile. + ExtensionAction* page_action_; + + // A cache of images the Page Actions might need to show, mapped by path. + typedef std::map<std::string, SkBitmap> PageActionMap; + PageActionMap page_action_icons_; + + // The object that is waiting for the image loading to complete + // asynchronously. + ImageLoadingTracker* tracker_; + + // The tab id we are currently showing the icon for. + int current_tab_id_; + + // The URL we are currently showing the icon for. + GURL current_url_; + + // The string to show for a tooltip. + std::string tooltip_; + + // This is used for post-install visual feedback. The page_action icon + // is briefly shown even if it hasn't been enabled by it's extension. + bool preview_enabled_; + + // Used to register for notifications received by NotificationObserver. + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(PageActionImageView); + }; + + class PageActionViewList { + public: + PageActionViewList(LocationBarViewMac* location_bar, + Profile* profile, + ToolbarModel* toolbar_model) + : owner_(location_bar), + profile_(profile), + toolbar_model_(toolbar_model) {} + + void DeleteAll(); + void RefreshViews(); + + PageActionImageView* ViewAt(size_t index); + + size_t Count(); + size_t VisibleCount(); + + // Called when the action at |index| is clicked. The |iconFrame| should + // describe the bounds of the affected action's icon. + void OnMousePressed(NSRect iconFrame, size_t index); + + private: + // Any installed Page Actions. + std::vector<PageActionImageView*> views_; + + // The location bar view that owns us. + LocationBarViewMac* owner_; + + Profile* profile_; + ToolbarModel* toolbar_model_; + + DISALLOW_COPY_AND_ASSIGN(PageActionViewList); + }; + private: // Sets the SSL icon we should be showing. void SetSecurityIcon(ToolbarModel::Icon icon); @@ -181,6 +293,9 @@ class LocationBarViewMac : public AutocompleteEditController, // The view that shows the lock/warning when in HTTPS mode. SecurityImageView security_image_view_; + // Any installed Page Actions. + PageActionViewList* page_action_views_; + Profile* profile_; ToolbarModel* toolbar_model_; // Weak, owned by Browser. diff --git a/chrome/browser/cocoa/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar_view_mac.mm index 5390d20..7e13ff3 100644 --- a/chrome/browser/cocoa/location_bar_view_mac.mm +++ b/chrome/browser/cocoa/location_bar_view_mac.mm @@ -6,6 +6,7 @@ #include "app/l10n_util_mac.h" #include "app/resource_bundle.h" +#include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "chrome/app/chrome_dll_resource.h" @@ -17,11 +18,17 @@ #import "chrome/browser/cocoa/autocomplete_text_field_cell.h" #include "chrome/browser/cocoa/event_utils.h" #include "chrome/browser/command_updater.h" +#include "chrome/browser/extensions/extension_browser_event_router.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/profile.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_action.h" +#include "chrome/common/notification_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "skia/ext/skia_utils_mac.h" @@ -85,11 +92,13 @@ LocationBarViewMac::LocationBarViewMac( field_(field), disposition_(CURRENT_TAB), security_image_view_(profile, toolbar_model), + page_action_views_(new PageActionViewList(this, profile, toolbar_model)), profile_(profile), toolbar_model_(toolbar_model), transition_(PageTransition::TYPED) { AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; [cell setSecurityImageView:&security_image_view_]; + [cell setPageActionViewList:page_action_views_]; } LocationBarViewMac::~LocationBarViewMac() { @@ -133,6 +142,15 @@ void LocationBarViewMac::FocusSearch() { // TODO(pkasting): Focus the edit a la Linux/Win } +void LocationBarViewMac::UpdatePageActions() { + page_action_views_->RefreshViews(); + [field_ resetFieldEditorFrameIfNeeded]; +} + +void LocationBarViewMac::InvalidatePageActions() { + page_action_views_->DeleteAll(); +} + void LocationBarViewMac::SaveStateToContents(TabContents* contents) { // TODO(shess): Why SaveStateToContents vs SaveStateToTab? edit_view_->SaveStateToTab(contents); @@ -141,6 +159,7 @@ void LocationBarViewMac::SaveStateToContents(TabContents* contents) { void LocationBarViewMac::Update(const TabContents* contents, bool should_restore_state) { SetSecurityIcon(toolbar_model_->GetIcon()); + page_action_views_->RefreshViews(); // AutocompleteEditView restores state if the tab is non-NULL. edit_view_->Update(should_restore_state ? contents : NULL); } @@ -291,23 +310,34 @@ void LocationBarViewMac::Revert() { edit_view_->RevertAll(); } +// TODO(pamg): Change all these, here and for other platforms, to size_t. int LocationBarViewMac::PageActionCount() { - NOTIMPLEMENTED(); - return -1; + return static_cast<int>(page_action_views_->Count()); } int LocationBarViewMac::PageActionVisibleCount() { - NOTIMPLEMENTED(); - return -1; + return static_cast<int>(page_action_views_->VisibleCount()); } ExtensionAction* LocationBarViewMac::GetPageAction(size_t index) { - NOTIMPLEMENTED(); + if (index < page_action_views_->Count()) + return page_action_views_->ViewAt(index)->page_action(); + NOTREACHED(); return NULL; } ExtensionAction* LocationBarViewMac::GetVisiblePageAction(size_t index) { - NOTIMPLEMENTED(); + size_t current = 0; + for (size_t i = 0; i < page_action_views_->Count(); ++i) { + if (page_action_views_->ViewAt(i)->IsVisible()) { + if (current == index) + return page_action_views_->ViewAt(i)->page_action(); + + ++current; + } + } + + NOTREACHED(); return NULL; } @@ -372,6 +402,10 @@ void LocationBarViewMac::LocationBarImageView::SetImage(NSImage* image) { image_.reset([image retain]); } +void LocationBarViewMac::LocationBarImageView::SetImage(SkBitmap* image) { + SetImage(gfx::SkBitmapToNSImage(*image)); +} + void LocationBarViewMac::LocationBarImageView::SetLabel(NSString* text, NSFont* baseFont, NSColor* color) { @@ -394,7 +428,6 @@ void LocationBarViewMac::LocationBarImageView::SetLabel(NSString* text, } void LocationBarViewMac::LocationBarImageView::SetVisible(bool visible) { - DCHECK(!visible || image_); visible_ = visible; } @@ -432,7 +465,6 @@ void LocationBarViewMac::SecurityImageView::SetImageShown(Image image) { } } - bool LocationBarViewMac::SecurityImageView::OnMousePressed() { TabContents* tab = BrowserList::GetLastActive()->GetSelectedTabContents(); NavigationEntry* nav_entry = tab->controller().GetActiveEntry(); @@ -443,3 +475,237 @@ bool LocationBarViewMac::SecurityImageView::OnMousePressed() { tab->ShowPageInfo(nav_entry->url(), nav_entry->ssl(), true); return true; } + +// PageActionImageView---------------------------------------------------------- + +LocationBarViewMac::PageActionImageView::PageActionImageView( + LocationBarViewMac* owner, + Profile* profile, + ExtensionAction* page_action) + : owner_(owner), + profile_(profile), + page_action_(page_action), + current_tab_id_(-1), + preview_enabled_(false) { + Extension* extension = profile->GetExtensionsService()->GetExtensionById( + page_action->extension_id(), false); + DCHECK(extension); + + // Load all the icons declared in the manifest. This is the contents of the + // icons array, plus the default_icon property, if any. + std::vector<std::string> icon_paths(*page_action->icon_paths()); + if (!page_action_->default_icon_path().empty()) + icon_paths.push_back(page_action_->default_icon_path()); + + tracker_ = new ImageLoadingTracker(this, icon_paths.size()); + for (std::vector<std::string>::iterator iter = icon_paths.begin(); + iter != icon_paths.end(); ++iter) { + tracker_->PostLoadImageTask(extension->GetResource(*iter), + gfx::Size(Extension::kPageActionIconMaxSize, + Extension::kPageActionIconMaxSize)); + } + + registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, + Source<Profile>(profile_)); +} + +LocationBarViewMac::PageActionImageView::~PageActionImageView() { + if (tracker_) + tracker_->StopTrackingImageLoad(); +} + +// Overridden from LocationBarImageView. Either notify listeners or show a +// popup depending on the Page Action. +bool LocationBarViewMac::PageActionImageView::OnMousePressed(NSRect bounds) { + if (page_action_->has_popup()) { + NOTIMPLEMENTED(); + } else { + ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted( + profile_, page_action_->extension_id(), page_action_->id(), + current_tab_id_, current_url_.spec(), + 1); // TODO(pamg): Add support for middle and right buttons. + } + return true; +} + +void LocationBarViewMac::PageActionImageView::OnImageLoaded(SkBitmap* image, + size_t index) { + // We loaded icons()->size() icons, plus one extra if the Page Action had + // a default icon. + int total_icons = page_action_->icon_paths()->size(); + if (!page_action_->default_icon_path().empty()) + total_icons++; + DCHECK(static_cast<int>(index) < total_icons); + + // Map the index of the loaded image back to its name. If we ever get an + // index greater than the number of icons, it must be the default icon. + if (image) { + if (index < page_action_->icon_paths()->size()) + page_action_icons_[page_action_->icon_paths()->at(index)] = *image; + else + page_action_icons_[page_action_->default_icon_path()] = *image; + } + + // If we are done, release the tracker. + if (static_cast<int>(index) == (total_icons - 1)) + tracker_ = NULL; + + owner_->UpdatePageActions(); +} + +void LocationBarViewMac::PageActionImageView::UpdateVisibility( + TabContents* contents, const GURL& url) { + // Save this off so we can pass it back to the extension when the action gets + // executed. See PageActionImageView::OnMousePressed. + current_tab_id_ = ExtensionTabUtil::GetTabId(contents); + current_url_ = url; + + bool visible = preview_enabled_ || + page_action_->GetIsVisible(current_tab_id_); + if (visible) { + // Set the tooltip. + tooltip_ = page_action_->GetTitle(current_tab_id_); + // TODO(port): implement tooltips + //SetTooltipText(ASCIIToWide(tooltip_)); + + // Set the image. + // It can come from three places. In descending order of priority: + // - The developer can set it dynamically by path or bitmap. It will be in + // page_action_->GetIcon(). + // - The developer can set it dynamically by index. It will be in + // page_action_->GetIconIndex(). + // - It can be set in the manifest by path. It will be in page_action_-> + // default_icon_path(). + + // First look for a dynamically set bitmap. + SkBitmap skia_icon = page_action_->GetIcon(current_tab_id_); + if (skia_icon.isNull()) { + int icon_index = page_action_->GetIconIndex(current_tab_id_); + std::string icon_path; + if (icon_index >= 0) + icon_path = page_action_->icon_paths()->at(icon_index); + else + icon_path = page_action_->default_icon_path(); + + if (!icon_path.empty()) { + PageActionMap::iterator iter = page_action_icons_.find(icon_path); + if (iter != page_action_icons_.end()) + skia_icon = iter->second; + } + } + + if (!skia_icon.isNull()) + SetImage(gfx::SkBitmapToNSImage(skia_icon)); + } + SetVisible(visible); +} + +void LocationBarViewMac::PageActionImageView::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: + // If we aren't the host of the popup, then disregard the notification. + //if (!popup_ || Details<ExtensionHost>(popup_->host()) != details) + // return; + + //HidePopup(); + NOTIMPLEMENTED(); + break; + default: + NOTREACHED() << "Unexpected notification"; + break; + } +} + +// PageActionViewList----------------------------------------------------------- + +void LocationBarViewMac::PageActionViewList::DeleteAll() { + if (!views_.empty()) { + STLDeleteContainerPointers(views_.begin(), views_.end()); + views_.clear(); + } +} + +void LocationBarViewMac::PageActionViewList::RefreshViews() { + std::vector<ExtensionAction*> page_actions; + ExtensionsService* service = profile_->GetExtensionsService(); + if (!service) + return; + + // Remember the previous visibility of the Page Actions so that we can + // notify when this changes. + std::map<ExtensionAction*, bool> old_visibility; + for (size_t i = 0; i < views_.size(); ++i) { + old_visibility[views_[i]->page_action()] = views_[i]->IsVisible(); + } + + for (size_t i = 0; i < service->extensions()->size(); ++i) { + if (service->extensions()->at(i)->page_action()) + page_actions.push_back(service->extensions()->at(i)->page_action()); + } + + // On startup we sometimes haven't loaded any extensions. This makes sure + // we catch up when the extensions (and any Page Actions) load. + if (page_actions.size() != views_.size()) { + DeleteAll(); // Delete the old views (if any). + + views_.resize(page_actions.size()); + + for (size_t i = 0; i < page_actions.size(); ++i) { + views_[i] = new PageActionImageView(owner_, profile_, page_actions[i]); + views_[i]->SetVisible(false); + } + } + + if (views_.empty()) + return; + + Browser* browser = BrowserList::GetLastActive(); + // The last-active browser can be NULL during startup. + if (!browser) + return; + + TabContents* contents = browser->GetSelectedTabContents(); + if (!contents) + return; + + GURL url = GURL(WideToUTF8(toolbar_model_->GetText())); + for (size_t i = 0; i < views_.size(); ++i) { + views_[i]->UpdateVisibility(contents, url); + // Check if the visibility of the action changed and notify if it did. + ExtensionAction* action = views_[i]->page_action(); + if (old_visibility.find(action) == old_visibility.end() || + old_visibility[action] != views_[i]->IsVisible()) { + NotificationService::current()->Notify( + NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, + Source<ExtensionAction>(action), + Details<TabContents>(contents)); + } + } +} + +LocationBarViewMac::PageActionImageView* + LocationBarViewMac::PageActionViewList::ViewAt(size_t index) { + CHECK(index < Count()); + return views_[index]; +} + +size_t LocationBarViewMac::PageActionViewList::Count() { + return views_.size(); +} + +size_t LocationBarViewMac::PageActionViewList::VisibleCount() { + size_t result = 0; + for (size_t i = 0; i < views_.size(); ++i) { + if (views_[i]->IsVisible()) + ++result; + } + return result; +} + +void LocationBarViewMac::PageActionViewList::OnMousePressed(NSRect iconFrame, + size_t index) { + ViewAt(index)->OnMousePressed(iconFrame); +} |