summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field.mm18
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.h20
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.mm165
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm5
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_unittest.mm3
-rw-r--r--chrome/browser/cocoa/location_bar_view_mac.h125
-rw-r--r--chrome/browser/cocoa/location_bar_view_mac.mm282
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);
+}