// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "chrome/browser/cocoa/location_bar_view_mac.h" #include "app/l10n_util_mac.h" #include "app/resource_bundle.h" #include "base/i18n/rtl.h" #include "base/nsimage_cache_mac.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" #include "chrome/browser/alternate_nav_url_fetcher.h" #import "chrome/browser/app_controller_mac.h" #import "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" #include "chrome/browser/browser_list.h" #import "chrome/browser/cocoa/autocomplete_text_field.h" #import "chrome/browser/cocoa/autocomplete_text_field_cell.h" #import "chrome/browser/cocoa/content_blocked_bubble_controller.h" #include "chrome/browser/cocoa/event_utils.h" #import "chrome/browser/cocoa/extensions/extension_action_context_menu.h" #import "chrome/browser/cocoa/extensions/extension_popup_controller.h" #include "chrome/browser/command_updater.h" #include "chrome/browser/content_setting_image_model.h" #include "chrome/browser/content_setting_bubble_model.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 "chrome/common/pref_names.h" #include "net/base/net_util.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "skia/ext/skia_utils_mac.h" #import "third_party/mozilla/NSPasteboard+Utils.h" // TODO(shess): This code is mostly copied from the gtk // implementation. Make sure it's all appropriate and flesh it out. namespace { // Returns the short name for a keyword. // TODO(shess): Copied from views/location_bar_view.cc. Try to share // it. std::wstring GetKeywordName(Profile* profile, const std::wstring& keyword) { // Make sure the TemplateURL still exists. // TODO(sky): Once LocationBarView adds a listener to the TemplateURLModel // to track changes to the model, this should become a DCHECK. const TemplateURL* template_url = profile->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword); if (template_url) return template_url->AdjustedShortNameForLocaleDirection(); return std::wstring(); } // Values for the label colors for different security states. static const CGFloat kEVSecureTextColorRedComponent = 0.03; static const CGFloat kEVSecureTextColorGreenComponent = 0.58; static const CGFloat kEVSecureTextColorBlueComponent = 0.0; static const CGFloat kSecurityErrorTextColorRedComponent = 0.63; static const CGFloat kSecurityErrorTextColorGreenComponent = 0.0; static const CGFloat kSecurityErrorTextColorBlueComponent = 0.0; // Build a short string to use in keyword-search when the field isn't // very big. // TODO(shess): Copied from views/location_bar_view.cc. Try to share. std::wstring CalculateMinString(const std::wstring& description) { // Chop at the first '.' or whitespace. const size_t dot_index = description.find(L'.'); const size_t ws_index = description.find_first_of(kWhitespaceWide); size_t chop_index = std::min(dot_index, ws_index); std::wstring min_string; if (chop_index == std::wstring::npos) { // No dot or whitespace, truncate to at most 3 chars. min_string = l10n_util::TruncateString(description, 3); } else { min_string = description.substr(0, chop_index); } base::i18n::AdjustStringForLocaleDirection(min_string, &min_string); return min_string; } } // namespace LocationBarViewMac::LocationBarViewMac( AutocompleteTextField* field, CommandUpdater* command_updater, ToolbarModel* toolbar_model, Profile* profile, Browser* browser) : edit_view_(new AutocompleteEditViewMac(this, toolbar_model, profile, command_updater, field)), command_updater_(command_updater), field_(field), disposition_(CURRENT_TAB), location_icon_view_(this), security_label_view_(), star_icon_view_(command_updater), page_action_views_(this, profile, toolbar_model), profile_(profile), browser_(browser), toolbar_model_(toolbar_model), transition_(PageTransition::TYPED) { for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { ContentSettingImageView* content_setting_view = new ContentSettingImageView(static_cast(i), this, profile_); content_setting_views_.push_back(content_setting_view); content_setting_view->SetVisible(false); } AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; [cell setLocationIconView:&location_icon_view_]; [cell setSecurityLabelView:&security_label_view_]; [cell setStarIconView:&star_icon_view_]; [cell setPageActionViewList:&page_action_views_]; [cell setContentSettingViewsList:&content_setting_views_]; registrar_.Add(this, NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, NotificationService::AllSources()); } LocationBarViewMac::~LocationBarViewMac() { // Disconnect from cell in case it outlives us. AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; [cell setPageActionViewList:NULL]; [cell setLocationIconView:NULL]; [cell setSecurityLabelView:NULL]; [cell setStarIconView:NULL]; } std::wstring LocationBarViewMac::GetInputString() const { return location_input_; } WindowOpenDisposition LocationBarViewMac::GetWindowOpenDisposition() const { return disposition_; } // TODO(thakis): Ping shess to verify what he wants to verify. // TODO(shess): Verify that this TODO is TODONE. // TODO(rohitrao): Fix this to return different types once autocomplete and // the onmibar are implemented. For now, any URL that comes from the // LocationBar has to have been entered by the user, and thus is of type // PageTransition::TYPED. PageTransition::Type LocationBarViewMac::GetPageTransition() const { return transition_; } void LocationBarViewMac::AcceptInput() { WindowOpenDisposition disposition = event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); AcceptInputWithDisposition(disposition); } void LocationBarViewMac::AcceptInputWithDisposition( WindowOpenDisposition disposition) { edit_view_->model()->AcceptInput(disposition, false); } void LocationBarViewMac::FocusLocation() { edit_view_->FocusLocation(); } void LocationBarViewMac::FocusSearch() { edit_view_->SetForcedQuery(); } void LocationBarViewMac::UpdateContentSettingsIcons() { RefreshContentSettingsViews(); [field_ updateCursorAndToolTipRects]; [field_ setNeedsDisplay:YES]; } void LocationBarViewMac::UpdatePageActions() { size_t count_before = page_action_views_.Count(); page_action_views_.RefreshViews(); [field_ resetFieldEditorFrameIfNeeded]; if (page_action_views_.Count() != count_before) { NotificationService::current()->Notify( NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED, Source(this), NotificationService::NoDetails()); } [field_ setNeedsDisplay:YES]; } void LocationBarViewMac::InvalidatePageActions() { size_t count_before = page_action_views_.Count(); page_action_views_.DeleteAll(); if (page_action_views_.Count() != count_before) { NotificationService::current()->Notify( NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED, Source(this), NotificationService::NoDetails()); } } void LocationBarViewMac::SaveStateToContents(TabContents* contents) { // TODO(shess): Why SaveStateToContents vs SaveStateToTab? edit_view_->SaveStateToTab(contents); } void LocationBarViewMac::Update(const TabContents* contents, bool should_restore_state) { SetIcon(edit_view_->GetIcon()); page_action_views_.RefreshViews(); RefreshContentSettingsViews(); // AutocompleteEditView restores state if the tab is non-NULL. edit_view_->Update(should_restore_state ? contents : NULL); } void LocationBarViewMac::OnAutocompleteAccept(const GURL& url, WindowOpenDisposition disposition, PageTransition::Type transition, const GURL& alternate_nav_url) { if (!url.is_valid()) return; location_input_ = UTF8ToWide(url.spec()); disposition_ = disposition; transition_ = transition; if (!command_updater_) return; if (!alternate_nav_url.is_valid()) { command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); return; } scoped_ptr fetcher( new AlternateNavURLFetcher(alternate_nav_url)); // The AlternateNavURLFetcher will listen for the pending navigation // notification that will be issued as a result of the "open URL." It // will automatically install itself into that navigation controller. command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { // I'm not sure this should be reachable, but I'm not also sure enough // that it shouldn't to stick in a NOTREACHED(). In any case, this is // harmless; we can simply let the fetcher get deleted here and it will // clean itself up properly. } else { fetcher.release(); // The navigation controller will delete the fetcher. } } void LocationBarViewMac::OnChangedImpl(AutocompleteTextField* field, const std::wstring& keyword, const std::wstring& short_name, const bool is_keyword_hint, NSImage* image) { AutocompleteTextFieldCell* cell = [field autocompleteTextFieldCell]; const CGFloat availableWidth([field availableDecorationWidth]); if (!keyword.empty() && !is_keyword_hint) { // Keyword search mode. The text will be like "Search Engine:". // "Engine" is a parameter to be replaced by text based on the // keyword. const std::wstring min_name(CalculateMinString(short_name)); NSString* partial_string = nil; if (!min_name.empty()) { partial_string = l10n_util::GetNSStringF(IDS_OMNIBOX_KEYWORD_TEXT, WideToUTF16(min_name)); } NSString* keyword_string = l10n_util::GetNSStringF(IDS_OMNIBOX_KEYWORD_TEXT, WideToUTF16(short_name)); [cell setKeywordString:keyword_string partialString:partial_string availableWidth:availableWidth]; } else if (!keyword.empty() && is_keyword_hint) { // Keyword is a hint, like "Press [Tab] to search Engine". [Tab] // is a parameter to be replaced by an image. "Engine" is a // parameter to be replaced by text based on the keyword. std::vector content_param_offsets; const std::wstring keyword_hint( l10n_util::GetStringF(IDS_OMNIBOX_KEYWORD_HINT, std::wstring(), short_name, &content_param_offsets)); // Should always be 2 offsets, see the comment in // location_bar_view.cc after IDS_OMNIBOX_KEYWORD_HINT fetch. DCHECK_EQ(content_param_offsets.size(), 2U); // Where to put the [TAB] image. const size_t split(content_param_offsets.front()); NSString* prefix = base::SysWideToNSString(keyword_hint.substr(0, split)); NSString* suffix = base::SysWideToNSString(keyword_hint.substr(split)); [cell setKeywordHintPrefix:prefix image:image suffix:suffix availableWidth:availableWidth]; } else { // Nothing interesting to show, plain old text field. [cell clearKeywordAndHint]; } // The field needs to re-layout if the visible decoration changed. [field resetFieldEditorFrameIfNeeded]; } void LocationBarViewMac::OnChanged() { // Unfortunately, the unit-test Profile doesn't have the right stuff // setup to do what GetKeywordName() needs to do. So do that out // here where we have a Profile and pass it into OnChangedImpl(). const std::wstring keyword(edit_view_->model()->keyword()); std::wstring short_name; if (!keyword.empty()) { short_name = GetKeywordName(profile_, keyword); } // TODO(shess): Implementation exported to a static so that it can // be unit tested without having to setup the entire object. This // makes me sad. I should fix that. OnChangedImpl(field_, keyword, short_name, edit_view_->model()->is_keyword_hint(), GetTabButtonImage()); } void LocationBarViewMac::OnInputInProgress(bool in_progress) { toolbar_model_->set_input_in_progress(in_progress); Update(NULL, false); } void LocationBarViewMac::OnSetFocus() { // Do nothing. } void LocationBarViewMac::OnKillFocus() { // Do nothing. } SkBitmap LocationBarViewMac::GetFavIcon() const { NOTIMPLEMENTED(); return SkBitmap(); } std::wstring LocationBarViewMac::GetTitle() const { NOTIMPLEMENTED(); return std::wstring(); } void LocationBarViewMac::Revert() { edit_view_->RevertAll(); } // TODO(pamg): Change all these, here and for other platforms, to size_t. int LocationBarViewMac::PageActionCount() { return static_cast(page_action_views_.Count()); } int LocationBarViewMac::PageActionVisibleCount() { return static_cast(page_action_views_.VisibleCount()); } TabContents* LocationBarViewMac::GetTabContents() const { return browser_->GetSelectedTabContents(); } void LocationBarViewMac::SetPreviewEnabledPageAction( ExtensionAction* page_action, bool preview_enabled) { DCHECK(page_action); TabContents* contents = GetTabContents(); if (!contents) return; page_action_views_.RefreshViews(); LocationBarViewMac::PageActionImageView* page_action_image_view = GetPageActionImageView(page_action); DCHECK(page_action_image_view); if (!page_action_image_view) return; page_action_image_view->set_preview_enabled(preview_enabled); page_action_image_view->UpdateVisibility(contents, GURL(WideToUTF8(toolbar_model_->GetText()))); } size_t LocationBarViewMac::GetPageActionIndex(ExtensionAction* page_action) { DCHECK(page_action); for (size_t i = 0; i < page_action_views_.Count(); ++i) { if (page_action_views_.ViewAt(i)->page_action() == page_action) return i; } NOTREACHED(); return 0; } LocationBarViewMac::PageActionImageView* LocationBarViewMac::GetPageActionImageView(ExtensionAction* page_action) { DCHECK(page_action); for (size_t i = 0; i < page_action_views_.Count(); ++i) { if (page_action_views_.ViewAt(i)->page_action() == page_action) return page_action_views_.ViewAt(i); } NOTREACHED(); return NULL; } ExtensionAction* LocationBarViewMac::GetPageAction(size_t index) { if (index < page_action_views_.Count()) return page_action_views_.ViewAt(index)->page_action(); NOTREACHED(); return NULL; } ExtensionAction* LocationBarViewMac::GetVisiblePageAction(size_t index) { 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; } void LocationBarViewMac::TestPageActionPressed(size_t index) { if (index >= page_action_views_.Count()) { NOTREACHED(); return; } page_action_views_.OnMousePressed(NSZeroRect, index); } void LocationBarViewMac::SetEditable(bool editable) { [field_ setEditable:editable ? YES : NO]; star_icon_view_.SetVisible(editable); UpdatePageActions(); } bool LocationBarViewMac::IsEditable() { return [field_ isEditable] ? true : false; } void LocationBarViewMac::SetStarred(bool starred) { star_icon_view_.SetStarred(starred); [field_ updateCursorAndToolTipRects]; [field_ resetFieldEditorFrameIfNeeded]; } NSImage* LocationBarViewMac::GetTabButtonImage() { if (!tab_button_image_) { SkBitmap* skiaBitmap = ResourceBundle::GetSharedInstance(). GetBitmapNamed(IDR_LOCATION_BAR_KEYWORD_HINT_TAB); if (skiaBitmap) { tab_button_image_.reset([gfx::SkBitmapToNSImage(*skiaBitmap) retain]); } } return tab_button_image_; } void LocationBarViewMac::SetIcon(int resource_id) { DCHECK(resource_id != 0); // The icon is always visible except when there is a keyword hint. if (!edit_view_->model()->keyword().empty() && !edit_view_->model()->is_keyword_hint()) { location_icon_view_.SetVisible(false); } else { NSImage* image = AutocompleteEditViewMac::ImageForResource(resource_id); location_icon_view_.SetImage(image); location_icon_view_.SetVisible(true); SetSecurityLabel(); } [field_ resetFieldEditorFrameIfNeeded]; } void LocationBarViewMac::SetSecurityLabel() { if (toolbar_model_->GetSecurityLevel() == ToolbarModel::EV_SECURE) { std::wstring security_info_text(toolbar_model_->GetEVCertName()); NSString* icon_label = base::SysWideToNSString(security_info_text); NSColor* color = [NSColor colorWithCalibratedRed:kEVSecureTextColorRedComponent green:kEVSecureTextColorGreenComponent blue:kEVSecureTextColorBlueComponent alpha:1.0]; security_label_view_.SetLabel(icon_label, [field_ font], color); security_label_view_.SetVisible(true); } else { security_label_view_.SetLabel(nil, nil, nil); security_label_view_.SetVisible(false); } } void LocationBarViewMac::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: { TabContents* contents = GetTabContents(); if (Details(contents) != details) return; [field_ updateCursorAndToolTipRects]; [field_ setNeedsDisplay:YES]; break; } default: NOTREACHED() << "Unexpected notification"; break; } } void LocationBarViewMac::PostNotification(const NSString* notification) { [[NSNotificationCenter defaultCenter] postNotificationName:notification object:[NSValue valueWithPointer:this]]; } void LocationBarViewMac::RefreshContentSettingsViews() { const TabContents* tab_contents = browser_->GetSelectedTabContents(); for (ContentSettingViews::iterator it(content_setting_views_.begin()); it != content_setting_views_.end(); ++it) { (*it)->UpdateFromTabContents( toolbar_model_->input_in_progress() ? NULL : tab_contents); } } // LocationBarImageView--------------------------------------------------------- void LocationBarViewMac::LocationBarImageView::SetImage(NSImage* image) { image_.reset([image retain]); } void LocationBarViewMac::LocationBarImageView::SetIcon(int resource_id) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); SetImage(rb.GetNSImageNamed(resource_id)); } void LocationBarViewMac::LocationBarImageView::SetLabel(NSString* text, NSFont* baseFont, NSColor* color) { // Create an attributed string for the label, if a label was given. label_.reset(); if (text) { DCHECK(color); DCHECK(baseFont); NSFont* font = [NSFont fontWithDescriptor:[baseFont fontDescriptor] size:[baseFont pointSize] - 2.0]; NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys: color, NSForegroundColorAttributeName, font, NSFontAttributeName, NULL]; NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:text attributes:attributes]; label_.reset(attrStr); } } void LocationBarViewMac::LocationBarImageView::SetVisible(bool visible) { visible_ = visible; } NSSize LocationBarViewMac::LocationBarImageView::GetDefaultImageSize() const { return NSZeroSize; } NSSize LocationBarViewMac::LocationBarImageView::GetImageSize() const { NSImage* image = GetImage(); if (image) return [image size]; return GetDefaultImageSize(); } // LocationIconView ------------------------------------------------------------ LocationBarViewMac::LocationIconView::LocationIconView( LocationBarViewMac* owner) : owner_(owner) { } LocationBarViewMac::LocationIconView::~LocationIconView() {} void LocationBarViewMac::LocationIconView::OnMousePressed(NSRect bounds) { // Do not show page info if the user has been editing the location // bar, or the location bar is at the NTP. if (owner_->location_entry()->IsEditingOrEmpty()) return; TabContents* tab = owner_->GetTabContents(); NavigationEntry* nav_entry = tab->controller().GetActiveEntry(); if (!nav_entry) { NOTREACHED(); return; } tab->ShowPageInfo(nav_entry->url(), nav_entry->ssl(), true); } bool LocationBarViewMac::LocationIconView::IsDraggable() { // Do not drag if the user has been editing the location bar, or the // location bar is at the NTP. if (owner_->location_entry()->IsEditingOrEmpty()) return false; return true; } NSPasteboard* LocationBarViewMac::LocationIconView::GetDragPasteboard() { TabContents* tab = owner_->GetTabContents(); DCHECK(tab); NSString* url = base::SysUTF8ToNSString(tab->GetURL().spec()); NSString* title = base::SysUTF16ToNSString(tab->GetTitle()); NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; [pboard declareURLPasteboardWithAdditionalTypes:[NSArray array] owner:nil]; [pboard setDataForURL:url title:title]; return pboard; } // StarIconView----------------------------------------------------------------- LocationBarViewMac::StarIconView::StarIconView(CommandUpdater* command_updater) : command_updater_(command_updater) { SetVisible(true); SetStarred(false); } void LocationBarViewMac::StarIconView::SetStarred(bool starred) { if (starred) { SetImage(AutocompleteEditViewMac::ImageForResource(IDR_OMNIBOX_STAR_LIT)); tooltip_.reset( [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_STARRED) retain]); } else { SetImage(AutocompleteEditViewMac::ImageForResource(IDR_OMNIBOX_STAR)); tooltip_.reset( [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_STAR) retain]); } } void LocationBarViewMac::StarIconView::OnMousePressed(NSRect bounds) { command_updater_->ExecuteCommand(IDC_BOOKMARK_PAGE); } const NSString* LocationBarViewMac::StarIconView::GetToolTip() { return tooltip_.get(); } // PageActionImageView---------------------------------------------------------- LocationBarViewMac::PageActionImageView::PageActionImageView( LocationBarViewMac* owner, Profile* profile, ExtensionAction* page_action) : owner_(owner), profile_(profile), page_action_(page_action), tracker_(this), 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 icon_paths(*page_action->icon_paths()); if (!page_action_->default_icon_path().empty()) icon_paths.push_back(page_action_->default_icon_path()); for (std::vector::iterator iter = icon_paths.begin(); iter != icon_paths.end(); ++iter) { tracker_.LoadImage(extension, extension->GetResource(*iter), gfx::Size(Extension::kPageActionIconMaxSize, Extension::kPageActionIconMaxSize), ImageLoadingTracker::DONT_CACHE); } registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, Source(profile_)); } LocationBarViewMac::PageActionImageView::~PageActionImageView() { } NSSize LocationBarViewMac::PageActionImageView::GetDefaultImageSize() const { return NSMakeSize(Extension::kPageActionIconMaxSize, Extension::kPageActionIconMaxSize); } // Overridden from LocationBarImageView. Either notify listeners or show a // popup depending on the Page Action. void LocationBarViewMac::PageActionImageView::OnMousePressed(NSRect bounds) { if (current_tab_id_ < 0) { NOTREACHED() << "No current tab."; // We don't want other code to try and handle this click. Returning true // prevents this by indicating that we handled it. return; } if (page_action_->HasPopup(current_tab_id_)) { AutocompleteTextField* textField = owner_->GetAutocompleteTextField(); NSWindow* window = [textField window]; NSRect relativeBounds = [[window contentView] convertRect:bounds fromView:textField]; NSPoint arrowPoint = NSMakePoint(NSMinX(relativeBounds), NSMinY(relativeBounds)); // Adjust the anchor point to be at the center of the page action icon. arrowPoint.x += [GetImage() size].width / 2; [ExtensionPopupController showURL:page_action_->GetPopupUrl(current_tab_id_) inBrowser:BrowserList::GetLastActive() anchoredAt:arrowPoint arrowLocation:kTopRight devMode:NO]; } else { ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted( profile_, page_action_->extension_id(), page_action_->id(), current_tab_id_, current_url_.spec(), 1); } } void LocationBarViewMac::PageActionImageView::OnImageLoaded( SkBitmap* image, ExtensionResource resource, int index) { // We loaded icons()->size() icons, plus one extra if the Page Action had // a default icon. int total_icons = static_cast(page_action_->icon_paths()->size()); if (!page_action_->default_icon_path().empty()) total_icons++; DCHECK(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 < static_cast(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; } 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) { SetToolTip(page_action_->GetTitle(current_tab_id_)); // 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)); } if (IsVisible() != visible) { SetVisible(visible); NotificationService::current()->Notify( NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, Source(page_action_), Details(contents)); } } void LocationBarViewMac::PageActionImageView::SetToolTip(NSString* tooltip) { if (!tooltip) { tooltip_.reset(); return; } tooltip_.reset([tooltip retain]); } void LocationBarViewMac::PageActionImageView::SetToolTip(std::string tooltip) { if (tooltip.empty()) { SetToolTip(nil); return; } SetToolTip(base::SysUTF8ToNSString(tooltip)); } const NSString* LocationBarViewMac::PageActionImageView::GetToolTip() { return tooltip_.get(); } NSMenu* LocationBarViewMac::PageActionImageView::GetMenu() { if (!profile_) return nil; ExtensionsService* service = profile_->GetExtensionsService(); if (!service) return nil; Extension* extension = service->GetExtensionById( page_action_->extension_id(), false); DCHECK(extension); if (!extension) return nil; return [[[ExtensionActionContextMenu alloc] initWithExtension:extension profile:profile_ extensionAction:page_action_] autorelease]; } void LocationBarViewMac::PageActionImageView::Observe( NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: { ExtensionPopupController* popup = [ExtensionPopupController popup]; if (popup && ![popup isClosing]) [popup close]; break; } default: NOTREACHED() << "Unexpected notification"; break; } } // ContentSettingsImageView----------------------------------------------------- LocationBarViewMac::ContentSettingImageView::ContentSettingImageView( ContentSettingsType settings_type, LocationBarViewMac* owner, Profile* profile) : content_setting_image_model_( ContentSettingImageModel::CreateContentSettingImageModel( settings_type)), owner_(owner), profile_(profile) { } LocationBarViewMac::ContentSettingImageView::~ContentSettingImageView() {} void LocationBarViewMac::ContentSettingImageView::OnMousePressed(NSRect bounds) { // Get host. This should be shared shared on linux/win/osx medium-term. TabContents* tabContents = BrowserList::GetLastActive()->GetSelectedTabContents(); if (!tabContents) return; GURL url = tabContents->GetURL(); std::wstring displayHost; net::AppendFormattedHost(url, profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), &displayHost, NULL, NULL); // Transform mouse coordinates to screen space. AutocompleteTextField* textField = owner_->GetAutocompleteTextField(); NSWindow* window = [textField window]; bounds = [textField convertRect:bounds toView:nil]; NSPoint anchor = NSMakePoint(NSMidX(bounds) + 1, NSMinY(bounds)); anchor = [window convertBaseToScreen:anchor]; // Open bubble. ContentSettingBubbleModel* model = ContentSettingBubbleModel::CreateContentSettingBubbleModel( tabContents, profile_, content_setting_image_model_->get_content_settings_type()); [[ContentBlockedBubbleController showForModel:model parentWindow:window anchoredAt:anchor] showWindow:nil]; } const NSString* LocationBarViewMac::ContentSettingImageView::GetToolTip() { return tooltip_.get(); } void LocationBarViewMac::ContentSettingImageView::UpdateFromTabContents( const TabContents* tab_contents) { content_setting_image_model_->UpdateFromTabContents(tab_contents); if (content_setting_image_model_->is_visible()) { // TODO(thakis): We should use pdfs for these icons on OSX. // http://crbug.com/35847 SetIcon(content_setting_image_model_->get_icon()); SetToolTip(base::SysUTF8ToNSString( content_setting_image_model_->get_tooltip())); SetVisible(true); } else { SetVisible(false); } } void LocationBarViewMac::ContentSettingImageView::SetToolTip(NSString* tooltip) { tooltip_.reset([tooltip retain]); } // PageActionViewList----------------------------------------------------------- void LocationBarViewMac::PageActionViewList::DeleteAll() { if (!views_.empty()) { STLDeleteContainerPointers(views_.begin(), views_.end()); views_.clear(); } } void LocationBarViewMac::PageActionViewList::RefreshViews() { if (!owner_->IsEditable()) { DeleteAll(); return; } std::vector page_actions; ExtensionsService* service = profile_->GetExtensionsService(); if (!service) return; 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; TabContents* contents = owner_->GetTabContents(); if (!contents) return; GURL url = GURL(WideToUTF8(toolbar_model_->GetText())); for (size_t i = 0; i < views_.size(); ++i) views_[i]->UpdateVisibility(contents, url); } 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); }