summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.h5
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.mm11
-rw-r--r--chrome/browser/cocoa/extensions/browser_action_button.mm4
-rw-r--r--chrome/browser/cocoa/extensions/browser_actions_controller.mm3
-rw-r--r--chrome/browser/cocoa/extensions/extension_action_context_menu.h17
-rw-r--r--chrome/browser/cocoa/extensions/extension_action_context_menu.mm126
-rw-r--r--chrome/browser/cocoa/extensions/extension_popup_controller.h18
-rw-r--r--chrome/browser/cocoa/extensions/extension_popup_controller.mm74
-rw-r--r--chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm3
-rw-r--r--chrome/browser/cocoa/location_bar_view_mac.mm7
10 files changed, 252 insertions, 16 deletions
diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.h b/chrome/browser/cocoa/autocomplete_text_field_cell.h
index a57a2ad..b92692c 100644
--- a/chrome/browser/cocoa/autocomplete_text_field_cell.h
+++ b/chrome/browser/cocoa/autocomplete_text_field_cell.h
@@ -113,6 +113,11 @@ class ExtensionAction;
// page actions. Use |-layedOutIcons:| instead in that case.
- (NSRect)pageActionFrameForIndex:(size_t)index inFrame:(NSRect)cellFrame;
+// Similar to |pageActionFrameForIndex:inFrame| but accepts an
+// ExtensionAction for when the index is not known.
+- (NSRect)pageActionFrameForExtensionAction:(ExtensionAction*)action
+ inFrame:(NSRect)cellFrame;
+
// Find the icon under the event. |nil| if |theEvent| is not over
// anything.
- (AutocompleteTextFieldIcon*)iconForEvent:(NSEvent*)theEvent
diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.mm b/chrome/browser/cocoa/autocomplete_text_field_cell.mm
index 3c29f8b..7c0c27f 100644
--- a/chrome/browser/cocoa/autocomplete_text_field_cell.mm
+++ b/chrome/browser/cocoa/autocomplete_text_field_cell.mm
@@ -367,6 +367,17 @@ void DrawImageInRect(NSImage* image, NSView* view, const NSRect& rect) {
return NSZeroRect;
}
+- (NSRect)pageActionFrameForExtensionAction:(ExtensionAction*)action
+ inFrame:(NSRect)cellFrame {
+ const size_t pageActionCount = [self pageActionCount];
+ size_t pos = 0;
+ while (pos < pageActionCount &&
+ action != page_action_views_->ViewAt(pos)->page_action())
+ ++pos;
+ return (pos == pageActionCount) ? NSZeroRect :
+ [self pageActionFrameForIndex:pos inFrame:cellFrame];
+}
+
- (void)drawHintWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
DCHECK(hintString_);
diff --git a/chrome/browser/cocoa/extensions/browser_action_button.mm b/chrome/browser/cocoa/extensions/browser_action_button.mm
index a6b8da5..863b56b 100644
--- a/chrome/browser/cocoa/extensions/browser_action_button.mm
+++ b/chrome/browser/cocoa/extensions/browser_action_button.mm
@@ -140,7 +140,9 @@ class ExtensionImageTrackerBridge : public NotificationObserver,
[self setShowsBorderOnlyWhileMouseInside:YES];
[self setMenu:[[[ExtensionActionContextMenu alloc]
- initWithExtension:extension profile:profile] autorelease]];
+ initWithExtension:extension
+ profile:profile
+ extensionAction:extension->browser_action()] autorelease]];
tabId_ = tabId;
extension_ = extension;
diff --git a/chrome/browser/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/cocoa/extensions/browser_actions_controller.mm
index 90600dc..db65ec0 100644
--- a/chrome/browser/cocoa/extensions/browser_actions_controller.mm
+++ b/chrome/browser/cocoa/extensions/browser_actions_controller.mm
@@ -702,7 +702,8 @@ class ExtensionsServiceObserverBridge : public NotificationObserver,
[ExtensionPopupController showURL:popupUrl
inBrowser:browser_
anchoredAt:arrowPoint
- arrowLocation:kTopRight];
+ arrowLocation:kTopRight
+ devMode:NO];
} else {
ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
profile_, action->extension_id(), browser_);
diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.h b/chrome/browser/cocoa/extensions/extension_action_context_menu.h
index 2d3f698..13fede3 100644
--- a/chrome/browser/cocoa/extensions/extension_action_context_menu.h
+++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.h
@@ -6,11 +6,14 @@
#define CHROME_BROWSER_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
#include "base/scoped_ptr.h"
+#include "base/scoped_nsobject.h"
#import <Cocoa/Cocoa.h>
class AsyncUninstaller;
+class DevmodeObserver;
class Extension;
+class ExtensionAction;
class Profile;
// A context menu used by the Browser and Page Action components that appears
@@ -20,17 +23,29 @@ class Profile;
// The extension that this menu belongs to. Weak.
Extension* extension_;
+ // The extension action this menu belongs to. Weak.
+ ExtensionAction* action_;
+
// The browser profile of the window that contains this extension. Weak.
Profile* profile_;
+ // The inspector menu item. Need to keep this around to add and remove it.
+ scoped_nsobject<NSMenuItem> inspectorItem_;
+
+ scoped_ptr<DevmodeObserver> observer_;
+
// Used to load the extension icon asynchronously on the I/O thread then show
// the uninstall confirmation dialog.
scoped_ptr<AsyncUninstaller> uninstaller_;
}
// Initializes and returns a context menu for the given extension and profile.
-- (id)initWithExtension:(Extension*)extension profile:(Profile*)profile;
+- (id)initWithExtension:(Extension*)extension
+ profile:(Profile*)profile
+ extensionAction:(ExtensionAction*)action;
+// Show or hide the inspector menu item.
+- (void)updateInspectorItem;
@end
typedef ExtensionActionContextMenu ExtensionActionContextMenuMac;
diff --git a/chrome/browser/cocoa/extensions/extension_action_context_menu.mm b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm
index eca2e8e..a7283e6 100644
--- a/chrome/browser/cocoa/extensions/extension_action_context_menu.mm
+++ b/chrome/browser/cocoa/extensions/extension_action_context_menu.mm
@@ -8,11 +8,25 @@
#include "base/sys_string_conversions.h"
#include "base/task.h"
#include "chrome/browser/browser_list.h"
+#import "chrome/browser/cocoa/autocomplete_text_field_cell.h"
+#include "chrome/browser/cocoa/browser_window_cocoa.h"
+#include "chrome/browser/cocoa/browser_window_controller.h"
+#include "chrome/browser/cocoa/extensions/browser_actions_controller.h"
+#include "chrome/browser/cocoa/extensions/extension_popup_controller.h"
+#include "chrome/browser/cocoa/info_bubble_view.h"
+#include "chrome/browser/cocoa/toolbar_controller.h"
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/extensions/extension_tabs_module.h"
+#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_action.h"
#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"
@@ -51,6 +65,21 @@ class AsyncUninstaller : public ExtensionInstallUI::Delegate {
DISALLOW_COPY_AND_ASSIGN(AsyncUninstaller);
};
+class DevmodeObserver : public NotificationObserver {
+ public:
+ DevmodeObserver(ExtensionActionContextMenu* menu)
+ : menu_(menu) {}
+
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::PREF_CHANGED);
+ [menu_ updateInspectorItem];
+ }
+ private:
+ ExtensionActionContextMenu* menu_;
+};
+
@interface ExtensionActionContextMenu(Private)
// Callback for the context menu items.
- (void)dispatch:(id)menuItem;
@@ -66,12 +95,27 @@ enum {
kExtensionContextOptions = 2,
kExtensionContextDisable = 3,
kExtensionContextUninstall = 4,
- kExtensionContextManage = 6
+ kExtensionContextManage = 6,
+ kExtensionContextInspect = 7
};
+
+int CurrentTabId() {
+ Browser* browser = BrowserList::GetLastActive();
+ if(!browser)
+ return -1;
+ TabContents* contents = browser->GetSelectedTabContents();
+ if (!contents)
+ return -1;
+ return ExtensionTabUtil::GetTabId(contents);
+}
+
} // namespace
-- (id)initWithExtension:(Extension*)extension profile:(Profile*)profile {
+- (id)initWithExtension:(Extension*)extension
+ profile:(Profile*)profile
+ extensionAction:(ExtensionAction*)action{
if ((self = [super initWithTitle:@""])) {
+ action_ = action;
extension_ = extension;
profile_ = profile;
@@ -108,11 +152,38 @@ enum {
}
}
+ NSString* inspectorTitle =
+ l10n_util::GetNSStringWithFixup(IDS_EXTENSION_ACTION_INSPECT_POPUP);
+ inspectorItem_.reset([[NSMenuItem alloc] initWithTitle:inspectorTitle
+ action:@selector(dispatch:)
+ keyEquivalent:@""]);
+ [inspectorItem_.get() setTarget:self];
+ [inspectorItem_.get() setTag:kExtensionContextInspect];
+
+ PrefService* service = profile_->GetPrefs();
+ if (service) {
+ observer_.reset(new DevmodeObserver(self));
+ service->AddPrefObserver(prefs::kExtensionsUIDeveloperMode,
+ observer_.get());
+ }
+ [self updateInspectorItem];
return self;
}
return nil;
}
+- (void)updateInspectorItem {
+ PrefService* service = profile_->GetPrefs();
+ bool devmode = service->GetBoolean(prefs::kExtensionsUIDeveloperMode);
+ if (devmode) {
+ if ([self indexOfItem:inspectorItem_.get()] == -1)
+ [self addItem:inspectorItem_.get()];
+ } else {
+ if ([self indexOfItem:inspectorItem_.get()] != -1)
+ [self removeItem:inspectorItem_.get()];
+ }
+}
+
- (void)dispatch:(id)menuItem {
Browser* browser = BrowserList::FindBrowserWithProfile(profile_);
if (!browser)
@@ -148,10 +219,61 @@ enum {
NEW_FOREGROUND_TAB, PageTransition::LINK);
break;
}
+ case kExtensionContextInspect: {
+ NSPoint popupPoint;
+ BrowserWindowCocoa* window =
+ static_cast<BrowserWindowCocoa*>(browser->window());
+ LocationBar* locationBar = window->GetLocationBar();
+ AutocompleteTextField* field =
+ (AutocompleteTextField*)locationBar->location_entry()->
+ GetNativeView();
+ AutocompleteTextFieldCell* fieldCell = [field autocompleteTextFieldCell];
+ NSRect popupRect =
+ [fieldCell pageActionFrameForExtensionAction:action_
+ inFrame:[field bounds]];
+ if (!NSEqualRects(popupRect, NSZeroRect)) {
+ popupRect = [[field superview] convertRect:popupRect toView:nil];
+ popupPoint = popupRect.origin;
+ NSRect fieldFrame = [field bounds];
+ fieldFrame = [field convertRect:fieldFrame toView:nil];
+ popupPoint.x += fieldFrame.origin.x + popupRect.size.width / 2;
+ } else {
+ ToolbarController* toolbarController =
+ [window->cocoa_controller() toolbarController];
+ BrowserActionsController* controller =
+ [toolbarController browserActionsController];
+ popupPoint = [controller popupPointForBrowserAction:extension_];
+ }
+ int tabId = CurrentTabId();
+ GURL url = action_->GetPopupUrl(tabId);
+ DCHECK(url.is_valid());
+ [ExtensionPopupController showURL:url
+ inBrowser:BrowserList::GetLastActive()
+ anchoredAt:popupPoint
+ arrowLocation:kTopRight
+ devMode:YES];
+ break;
+ }
default:
NOTREACHED();
break;
}
}
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
+ if([menuItem isEqualTo: inspectorItem_.get()]) {
+ return (action_->HasPopup(CurrentTabId()));
+ }
+ return YES;
+}
+
+- (void)dealloc {
+ PrefService* service = profile_->GetPrefs();
+ if (service) {
+ service->RemovePrefObserver(prefs::kExtensionsUIDeveloperMode,
+ observer_.get());
+ }
+ [super dealloc];
+}
+
@end
diff --git a/chrome/browser/cocoa/extensions/extension_popup_controller.h b/chrome/browser/cocoa/extensions/extension_popup_controller.h
index 61d020a..efb5c6d 100644
--- a/chrome/browser/cocoa/extensions/extension_popup_controller.h
+++ b/chrome/browser/cocoa/extensions/extension_popup_controller.h
@@ -13,9 +13,12 @@
#include "chrome/browser/cocoa/info_bubble_view.h"
#include "googleurl/src/gurl.h"
+
class Browser;
+class DevtoolsNotificationBridge;
class ExtensionHost;
@class InfoBubbleWindow;
+class NotificationRegistrar;
// This controller manages a single browser action popup that can appear once a
// user has clicked on a browser action button. It instantiates the extension
@@ -42,6 +45,12 @@ class ExtensionHost;
// The extension host object.
scoped_ptr<ExtensionHost> host_;
+
+ scoped_ptr<NotificationRegistrar> registrar_;
+ scoped_ptr<DevtoolsNotificationBridge> notificationBridge_;
+
+ // Whether the popup has a devtools window attached to it.
+ bool beingInspected_;
}
// Returns the ExtensionHost object associated with this popup.
@@ -55,10 +64,14 @@ class ExtensionHost;
// center of the browser action button.
// The actual display of the popup is delayed until the page contents finish
// loading in order to minimize UI flashing and resizing.
+// Passing YES to |devMode| will launch the webkit inspector for the popup,
+// and prevent the popup from closing when focus is lost. It will be closed
+// after the inspector is closed, or another popup is opened.
+ (ExtensionPopupController*)showURL:(GURL)url
inBrowser:(Browser*)browser
anchoredAt:(NSPoint)anchoredAt
- arrowLocation:(BubbleArrowLocation)arrowLocation;
+ arrowLocation:(BubbleArrowLocation)arrowLocation
+ devMode:(BOOL)devMode;
// Returns the controller used to display the popup being shown. If no popup is
// currently open, then nil is returned. Static because only one extension popup
@@ -67,6 +80,9 @@ class ExtensionHost;
// Whether the popup is in the process of closing (via Core Animation).
- (BOOL)isClosing;
+
+// Show the dev tools attached to the popup.
+- (void)showDevTools;
@end
@interface ExtensionPopupController(TestingAPI)
diff --git a/chrome/browser/cocoa/extensions/extension_popup_controller.mm b/chrome/browser/cocoa/extensions/extension_popup_controller.mm
index 592f6e9..0e656b8 100644
--- a/chrome/browser/cocoa/extensions/extension_popup_controller.mm
+++ b/chrome/browser/cocoa/extensions/extension_popup_controller.mm
@@ -10,9 +10,11 @@
#import "chrome/browser/cocoa/browser_window_cocoa.h"
#import "chrome/browser/cocoa/extension_view_mac.h"
#import "chrome/browser/cocoa/info_bubble_window.h"
+#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/profile.h"
+#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
namespace {
@@ -30,13 +32,50 @@ CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) {
} // namespace
+class DevtoolsNotificationBridge : public NotificationObserver {
+ public:
+ explicit DevtoolsNotificationBridge(ExtensionPopupController* controller)
+ : controller_(controller) {}
+
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
+ if (Details<ExtensionHost>([controller_ extensionHost]) ==
+ details)
+ [controller_ showDevTools];
+ break;
+ }
+ case NotificationType::DEVTOOLS_WINDOW_CLOSING: {
+ RenderViewHost* rvh =
+ [controller_ extensionHost]->render_view_host();
+ if (Details<RenderViewHost>(rvh) == details)
+ // Allow the devtools to finish detaching before we close the popup
+ [controller_ performSelector:@selector(close)
+ withObject:nil
+ afterDelay:0.0];
+ break;
+ }
+ default: {
+ NOTREACHED() << "Received unexpected notification";
+ break;
+ }
+ };
+ }
+
+ private:
+ ExtensionPopupController* controller_;
+};
+
@interface ExtensionPopupController(Private)
// Callers should be using the public static method for initialization.
// NOTE: This takes ownership of |host|.
- (id)initWithHost:(ExtensionHost*)host
parentWindow:(NSWindow*)parentWindow
anchoredAt:(NSPoint)anchoredAt
- arrowLocation:(BubbleArrowLocation)arrowLocation;
+ arrowLocation:(BubbleArrowLocation)arrowLocation
+ devMode:(BOOL)devMode;
// Called when the extension's hosted NSView has been resized.
- (void)extensionViewFrameChanged;
@@ -47,7 +86,9 @@ CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) {
- (id)initWithHost:(ExtensionHost*)host
parentWindow:(NSWindow*)parentWindow
anchoredAt:(NSPoint)anchoredAt
- arrowLocation:(BubbleArrowLocation)arrowLocation {
+ arrowLocation:(BubbleArrowLocation)arrowLocation
+ devMode:(BOOL)devMode {
+
parentWindow_ = parentWindow;
anchor_ = [parentWindow convertBaseToScreen:anchoredAt];
host_.reset(host);
@@ -86,10 +127,28 @@ CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) {
[window setDelegate:self];
[window setContentView:view];
self = [super initWithWindow:window];
-
+ if (devMode) {
+ beingInspected_ = true;
+ // Listen for the the devtools window closing.
+ notificationBridge_.reset(new DevtoolsNotificationBridge(self));
+ registrar_.reset(new NotificationRegistrar);
+ registrar_->Add(notificationBridge_.get(),
+ NotificationType::DEVTOOLS_WINDOW_CLOSING,
+ Source<Profile>(host->profile()));
+ registrar_->Add(notificationBridge_.get(),
+ NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
+ Source<Profile>(host->profile()));
+ } else {
+ beingInspected_ = false;
+ }
return self;
}
+- (void)showDevTools {
+ DevToolsManager::GetInstance()->OpenDevToolsWindow(
+ host_->render_view_host());
+}
+
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
@@ -110,7 +169,7 @@ CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) {
DCHECK_EQ([notification object], window);
// If the window isn't visible, it is already closed, and this notification
// has been sent as part of the closing operation, so no need to close.
- if ([window isVisible]) {
+ if ([window isVisible] && !beingInspected_) {
[self close];
}
}
@@ -131,7 +190,8 @@ CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) {
+ (ExtensionPopupController*)showURL:(GURL)url
inBrowser:(Browser*)browser
anchoredAt:(NSPoint)anchoredAt
- arrowLocation:(BubbleArrowLocation)arrowLocation {
+ arrowLocation:(BubbleArrowLocation)arrowLocation
+ devMode:(BOOL)devMode {
DCHECK([NSThread isMainThread]);
DCHECK(browser);
if (!browser)
@@ -164,8 +224,8 @@ CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) {
initWithHost:host
parentWindow:browser->window()->GetNativeHandle()
anchoredAt:anchoredAt
- arrowLocation:arrowLocation];
-
+ arrowLocation:arrowLocation
+ devMode:devMode];
return gPopup;
}
diff --git a/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm b/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm
index 16f2fcf..c6335dd 100644
--- a/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm
+++ b/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm
@@ -66,7 +66,8 @@ class ExtensionPopupControllerTest : public CocoaTest {
[ExtensionPopupController showURL:GURL("http://google.com")
inBrowser:browser_.get()
anchoredAt:NSZeroPoint
- arrowLocation:kTopRight];
+ arrowLocation:kTopRight
+ devMode:NO];
}
virtual void TearDown() {
profile_->ShutdownExtensionProfile();
diff --git a/chrome/browser/cocoa/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar_view_mac.mm
index 7e5cef4..c9584a0 100644
--- a/chrome/browser/cocoa/location_bar_view_mac.mm
+++ b/chrome/browser/cocoa/location_bar_view_mac.mm
@@ -665,7 +665,8 @@ void LocationBarViewMac::PageActionImageView::OnMousePressed(NSRect bounds) {
[ExtensionPopupController showURL:page_action_->GetPopupUrl(current_tab_id_)
inBrowser:BrowserList::GetLastActive()
anchoredAt:arrowPoint
- arrowLocation:kTopRight];
+ arrowLocation:kTopRight
+ devMode:NO];
} else {
ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted(
profile_, page_action_->extension_id(), page_action_->id(),
@@ -777,7 +778,9 @@ NSMenu* LocationBarViewMac::PageActionImageView::GetMenu() {
if (!extension)
return nil;
return [[[ExtensionActionContextMenu alloc]
- initWithExtension:extension profile:profile_] autorelease];
+ initWithExtension:extension
+ profile:profile_
+ extensionAction:page_action_] autorelease];
}
void LocationBarViewMac::PageActionImageView::Observe(