Commit 0610ab5b authored by tapted@chromium.org's avatar tapted@chromium.org

[mac] Implement a windowed extension install/permissions prompt.

This allows extension install dialogs, and permissions upgrade prompts,
to be shown on mac without requiring a parent WebContents to attach a
sheet to. Currently, they hit NOTIMPLEMENTED().

The window is a titled NSPanel, centred on screen using [NSWindow
center]. The implementation mostly uses the existing
ExtensionInstallViewController, but puts it in a new, windowed-version
of ExtensionInstallDialogController.

Adds a test:
WindowedInstallDialogControllerBrowserTest.ShowInstallDialog that mimics
the way extension install and upgrade prompts are invoked via the app
launcher.

Screenshot at http://crbug.com/325030#c2

BUG=325030, 229094, 271809, 269151

Review URL: https://codereview.chromium.org/65043015

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238542 0039d316-1c4b-4281-b951-d872f2087c98
parent 496dbbec
...@@ -31,6 +31,15 @@ enum { ...@@ -31,6 +31,15 @@ enum {
}; };
typedef NSUInteger NSEventSwipeTrackingOptions; typedef NSUInteger NSEventSwipeTrackingOptions;
enum {
NSWindowAnimationBehaviorDefault = 0,
NSWindowAnimationBehaviorNone = 2,
NSWindowAnimationBehaviorDocumentWindow = 3,
NSWindowAnimationBehaviorUtilityWindow = 4,
NSWindowAnimationBehaviorAlertPanel = 5
};
typedef NSInteger NSWindowAnimationBehavior;
@interface NSEvent (LionSDK) @interface NSEvent (LionSDK)
+ (BOOL)isSwipeTrackingFromScrollEventsEnabled; + (BOOL)isSwipeTrackingFromScrollEventsEnabled;
...@@ -61,6 +70,8 @@ typedef NSUInteger NSEventSwipeTrackingOptions; ...@@ -61,6 +70,8 @@ typedef NSUInteger NSEventSwipeTrackingOptions;
@interface NSWindow (LionSDK) @interface NSWindow (LionSDK)
- (CGFloat)backingScaleFactor; - (CGFloat)backingScaleFactor;
- (NSWindowAnimationBehavior)animationBehavior;
- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
@end @end
#endif // MAC_OS_X_VERSION_10_7 #endif // MAC_OS_X_VERSION_10_7
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/mac_util.h" #include "base/mac/mac_util.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/prefs/pref_service.h" #include "base/prefs/pref_service.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
...@@ -67,26 +68,6 @@ using content::NativeWebKeyboardEvent; ...@@ -67,26 +68,6 @@ using content::NativeWebKeyboardEvent;
using content::SSLStatus; using content::SSLStatus;
using content::WebContents; using content::WebContents;
// Replicate specific 10.7 SDK declarations for building with prior SDKs.
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
enum {
NSWindowAnimationBehaviorDefault = 0,
NSWindowAnimationBehaviorNone = 2,
NSWindowAnimationBehaviorDocumentWindow = 3,
NSWindowAnimationBehaviorUtilityWindow = 4,
NSWindowAnimationBehaviorAlertPanel = 5
};
typedef NSInteger NSWindowAnimationBehavior;
@interface NSWindow (LionSDKDeclarations)
- (NSWindowAnimationBehavior)animationBehavior;
- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
@end
#endif // MAC_OS_X_VERSION_10_7
namespace { namespace {
NSPoint GetPointForBubble(content::WebContents* web_contents, NSPoint GetPointForBubble(content::WebContents* web_contents,
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/mac/bundle_locations.h" #include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h" #include "base/mac/mac_util.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h" // IDC_* #include "chrome/app/chrome_command_ids.h" // IDC_*
...@@ -180,15 +181,6 @@ using web_modal::WebContentsModalDialogManager; ...@@ -180,15 +181,6 @@ using web_modal::WebContentsModalDialogManager;
#if !defined(MAC_OS_X_VERSION_10_7) || \ #if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
enum {
NSWindowAnimationBehaviorDefault = 0,
NSWindowAnimationBehaviorNone = 2,
NSWindowAnimationBehaviorDocumentWindow = 3,
NSWindowAnimationBehaviorUtilityWindow = 4,
NSWindowAnimationBehaviorAlertPanel = 5
};
typedef NSInteger NSWindowAnimationBehavior;
enum { enum {
NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7, NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8 NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8
...@@ -200,7 +192,6 @@ enum { ...@@ -200,7 +192,6 @@ enum {
@interface NSWindow (LionSDKDeclarations) @interface NSWindow (LionSDKDeclarations)
- (void)setRestorable:(BOOL)flag; - (void)setRestorable:(BOOL)flag;
- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
@end @end
#endif // MAC_OS_X_VERSION_10_7 #endif // MAC_OS_X_VERSION_10_7
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h" #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h" #include "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h"
#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h" #import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
#import "chrome/browser/ui/cocoa/extensions/windowed_install_dialog_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
...@@ -21,13 +22,12 @@ void ShowExtensionInstallDialogImpl( ...@@ -21,13 +22,12 @@ void ShowExtensionInstallDialogImpl(
const ExtensionInstallPrompt::ShowParams& show_params, const ExtensionInstallPrompt::ShowParams& show_params,
ExtensionInstallPrompt::Delegate* delegate, ExtensionInstallPrompt::Delegate* delegate,
const ExtensionInstallPrompt::Prompt& prompt) { const ExtensionInstallPrompt::Prompt& prompt) {
// These objects will delete themselves when the dialog closes.
if (!show_params.parent_web_contents) { if (!show_params.parent_web_contents) {
// TODO(sail): Add support for showing the dialog without a parent window. new WindowedInstallDialogController(show_params, delegate, prompt);
NOTIMPLEMENTED();
return; return;
} }
// This object will delete itself when the dialog closes.
new ExtensionInstallDialogController(show_params, delegate, prompt); new ExtensionInstallDialogController(show_params, delegate, prompt);
} }
......
...@@ -30,7 +30,12 @@ class MockExtensionInstallPromptDelegate ...@@ -30,7 +30,12 @@ class MockExtensionInstallPromptDelegate
int abort_count_; int abort_count_;
}; };
// Loads the install prompt test extension. // Loads the test extension from the given test directory and manifest file.
scoped_refptr<extensions::Extension> LoadInstallPromptExtension(
const char* extension_dir_name,
const char* manifest_file);
// Loads the default install_prompt test extension.
scoped_refptr<extensions::Extension> LoadInstallPromptExtension(); scoped_refptr<extensions::Extension> LoadInstallPromptExtension();
// Loads the icon for the install prompt extension. // Loads the icon for the install prompt extension.
......
...@@ -23,14 +23,16 @@ void MockExtensionInstallPromptDelegate::InstallUIAbort(bool user_initiated) { ...@@ -23,14 +23,16 @@ void MockExtensionInstallPromptDelegate::InstallUIAbort(bool user_initiated) {
++abort_count_; ++abort_count_;
} }
scoped_refptr<Extension> LoadInstallPromptExtension() { scoped_refptr<extensions::Extension> LoadInstallPromptExtension(
const char* extension_dir_name,
const char* manifest_file) {
scoped_refptr<Extension> extension; scoped_refptr<Extension> extension;
base::FilePath path; base::FilePath path;
PathService::Get(chrome::DIR_TEST_DATA, &path); PathService::Get(chrome::DIR_TEST_DATA, &path);
path = path.AppendASCII("extensions") path = path.AppendASCII("extensions")
.AppendASCII("install_prompt") .AppendASCII(extension_dir_name)
.AppendASCII("extension.json"); .AppendASCII(manifest_file);
std::string error; std::string error;
JSONFileValueSerializer serializer(path); JSONFileValueSerializer serializer(path);
...@@ -50,6 +52,10 @@ scoped_refptr<Extension> LoadInstallPromptExtension() { ...@@ -50,6 +52,10 @@ scoped_refptr<Extension> LoadInstallPromptExtension() {
return extension; return extension;
} }
scoped_refptr<Extension> LoadInstallPromptExtension() {
return LoadInstallPromptExtension("install_prompt", "extension.json");
}
gfx::Image LoadInstallPromptIcon() { gfx::Image LoadInstallPromptIcon() {
base::FilePath path; base::FilePath path;
PathService::Get(chrome::DIR_TEST_DATA, &path); PathService::Get(chrome::DIR_TEST_DATA, &path);
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_WINDOWED_INSTALL_DIALOG_CONTROLLER_H_
#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_WINDOWED_INSTALL_DIALOG_CONTROLLER_H_
#import <Cocoa/Cocoa.h>
#include "base/gtest_prod_util.h"
#include "base/mac/scoped_nsobject.h"
#include "chrome/browser/extensions/extension_install_prompt.h"
@class ExtensionInstallViewController;
@class WindowedInstallController;
// Displays an app or extension install or permissions prompt as a standalone
// NSPanel.
class WindowedInstallDialogController
: public ExtensionInstallPrompt::Delegate {
public:
// Initializes the ExtensionInstallViewController and shows the window. This
// object will delete itself when the window is closed.
WindowedInstallDialogController(
const ExtensionInstallPrompt::ShowParams& show_params,
ExtensionInstallPrompt::Delegate* delegate,
const ExtensionInstallPrompt::Prompt& prompt);
virtual ~WindowedInstallDialogController();
// Invoked by the -[NSWindow windowWillClose:] notification after a dialog
// choice is invoked. Releases owned resources, then deletes |this|.
void OnWindowClosing();
// ExtensionInstallPrompt::Delegate:
virtual void InstallUIProceed() OVERRIDE;
virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
private:
FRIEND_TEST_ALL_PREFIXES(WindowedInstallDialogControllerBrowserTest,
ShowInstallDialog);
ExtensionInstallViewController* GetViewController();
ExtensionInstallPrompt::Delegate* delegate_;
base::scoped_nsobject<WindowedInstallController> install_controller_;
DISALLOW_COPY_AND_ASSIGN(WindowedInstallDialogController);
};
#endif // CHROME_BROWSER_UI_COCOA_EXTENSIONS_WINDOWED_INSTALL_DIALOG_CONTROLLER_H_
// Copyright 2013 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/ui/cocoa/extensions/windowed_install_dialog_controller.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/sys_string_conversions.h"
#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
#include "ui/base/cocoa/window_size_constants.h"
@interface WindowedInstallController
: NSWindowController<NSWindowDelegate> {
@private
base::scoped_nsobject<ExtensionInstallViewController> installViewController_;
WindowedInstallDialogController* dialogController_; // Weak. Owns us.
}
@property(readonly, nonatomic) ExtensionInstallViewController* viewController;
- (id)initWithNavigator:(content::PageNavigator*)navigator
delegate:(WindowedInstallDialogController*)delegate
prompt:(const ExtensionInstallPrompt::Prompt&)prompt;
@end
WindowedInstallDialogController::WindowedInstallDialogController(
const ExtensionInstallPrompt::ShowParams& show_params,
ExtensionInstallPrompt::Delegate* delegate,
const ExtensionInstallPrompt::Prompt& prompt)
: delegate_(delegate) {
install_controller_.reset([[WindowedInstallController alloc]
initWithNavigator:show_params.navigator
delegate:this
prompt:prompt]);
[[install_controller_ window] makeKeyAndOrderFront:nil];
}
WindowedInstallDialogController::~WindowedInstallDialogController() {
DCHECK(!install_controller_);
DCHECK(!delegate_);
}
void WindowedInstallDialogController::OnWindowClosing() {
install_controller_.reset();
if (delegate_) {
delegate_->InstallUIAbort(false);
delegate_ = NULL;
}
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
ExtensionInstallViewController*
WindowedInstallDialogController::GetViewController() {
return [install_controller_ viewController];
}
void WindowedInstallDialogController::InstallUIProceed() {
delegate_->InstallUIProceed();
delegate_ = NULL;
[[install_controller_ window] close];
}
void WindowedInstallDialogController::InstallUIAbort(bool user_initiated) {
delegate_->InstallUIAbort(user_initiated);
delegate_ = NULL;
[[install_controller_ window] close];
}
@implementation WindowedInstallController
- (id)initWithNavigator:(content::PageNavigator*)navigator
delegate:(WindowedInstallDialogController*)delegate
prompt:(const ExtensionInstallPrompt::Prompt&)prompt {
base::scoped_nsobject<NSWindow> controlledPanel(
[[NSPanel alloc] initWithContentRect:ui::kWindowSizeDeterminedLater
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO]);
if ((self = [super initWithWindow:controlledPanel])) {
dialogController_ = delegate;
installViewController_.reset([[ExtensionInstallViewController alloc]
initWithNavigator:navigator
delegate:delegate
prompt:prompt]);
NSWindow* window = [self window];
// Ensure the window does not display behind the app launcher window, and is
// otherwise hard to lose behind other windows (since it is not modal).
[window setLevel:NSDockWindowLevel];
// Animate the window when ordered in, the same way as an NSAlert.
if ([window respondsToSelector:@selector(setAnimationBehavior:)])
[window setAnimationBehavior:NSWindowAnimationBehaviorAlertPanel];
[window setTitle:base::SysUTF16ToNSString(prompt.GetDialogTitle())];
NSRect viewFrame = [[installViewController_ view] frame];
[window setFrame:[window frameRectForContentRect:viewFrame]
display:NO];
[window setContentView:[installViewController_ view]];
[window setDelegate:self];
[window center];
}
return self;
}
- (ExtensionInstallViewController*)viewController {
return installViewController_;
}
- (void)windowWillClose:(NSNotification*)notification {
[[self window] setDelegate:nil];
dialogController_->OnWindowClosing();
}
@end
// Copyright 2013 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/ui/cocoa/extensions/windowed_install_dialog_controller.h"
#include "base/run_loop.h"
#include "chrome/browser/ui/browser.h"
#import "chrome/browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h"
#import "chrome/browser/ui/cocoa/extensions/extension_install_view_controller.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/extension.h"
namespace {
// Similar to ShowExtensionInstallDialogImpl except this allows the created
// dialog controller to be captured and manipulated for tests.
void TestingShowAppListInstallDialogController(
WindowedInstallDialogController** controller,
const ExtensionInstallPrompt::ShowParams& show_params,
ExtensionInstallPrompt::Delegate* delegate,
const ExtensionInstallPrompt::Prompt& prompt) {
*controller =
new WindowedInstallDialogController(show_params, delegate, prompt);
}
typedef InProcessBrowserTest WindowedInstallDialogControllerBrowserTest;
} // namespace
// Test for showing an extension install prompt with no parent WebContents.
IN_PROC_BROWSER_TEST_F(WindowedInstallDialogControllerBrowserTest,
ShowInstallDialog) {
// Construct a prompt with a NULL parent window, the way ExtensionEnableFlow
// will for the Mac app list. For testing, sets a NULL PageNavigator as well.
scoped_ptr<ExtensionInstallPrompt> prompt(
new ExtensionInstallPrompt(browser()->profile(), NULL, NULL));
WindowedInstallDialogController* controller = NULL;
chrome::MockExtensionInstallPromptDelegate delegate;
scoped_refptr<extensions::Extension> extension =
chrome::LoadInstallPromptExtension("permissions", "many-apis.json");
prompt->ConfirmInstall(
&delegate,
extension.get(),
base::Bind(&TestingShowAppListInstallDialogController, &controller));
// The prompt needs to load the image, which happens on the blocking pool.
content::BrowserThread::GetBlockingPool()->FlushForTesting();
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(controller);
base::scoped_nsobject<NSWindow> window(
[[[controller->GetViewController() view] window] retain]);
EXPECT_TRUE([window isVisible]);
EXPECT_TRUE([window delegate]);
EXPECT_EQ(0, delegate.abort_count());
// Press cancel to close the window.
[[controller->GetViewController() cancelButton] performClick:nil];
EXPECT_FALSE([window delegate]);
EXPECT_EQ(1, delegate.abort_count());
// Ensure the window is closed.
EXPECT_FALSE([window isVisible]);
}
...@@ -6,31 +6,12 @@ ...@@ -6,31 +6,12 @@
#import "base/mac/mac_util.h" #import "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
#import "base/mac/sdk_forward_declarations.h"
#import "chrome/browser/ui/cocoa/tabs/tab_controller.h" #import "chrome/browser/ui/cocoa/tabs/tab_controller.h"
#import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h" #import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h"
#import "chrome/browser/ui/cocoa/tabs/tab_view.h" #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
#import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h" #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
// Replicate specific 10.7 SDK declarations for building with prior SDKs.
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
enum {
NSWindowAnimationBehaviorDefault = 0,
NSWindowAnimationBehaviorNone = 2,
NSWindowAnimationBehaviorDocumentWindow = 3,
NSWindowAnimationBehaviorUtilityWindow = 4,
NSWindowAnimationBehaviorAlertPanel = 5
};
typedef NSInteger NSWindowAnimationBehavior;
@interface NSWindow (LionSDKDeclarations)
- (NSWindowAnimationBehavior)animationBehavior;
- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
@end
#endif // MAC_OS_X_VERSION_10_7
const CGFloat kTearDistance = 36.0; const CGFloat kTearDistance = 36.0;
const NSTimeInterval kTearDuration = 0.333; const NSTimeInterval kTearDuration = 0.333;
......
...@@ -762,6 +762,8 @@ ...@@ -762,6 +762,8 @@
'browser/ui/cocoa/extensions/extension_view_mac.mm', 'browser/ui/cocoa/extensions/extension_view_mac.mm',
'browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.h', 'browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.h',
'browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.mm', 'browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.mm',
'browser/ui/cocoa/extensions/windowed_install_dialog_controller.h',
'browser/ui/cocoa/extensions/windowed_install_dialog_controller.mm',
'browser/ui/cocoa/external_protocol_dialog.h', 'browser/ui/cocoa/external_protocol_dialog.h',
'browser/ui/cocoa/external_protocol_dialog.mm', 'browser/ui/cocoa/external_protocol_dialog.mm',
'browser/ui/cocoa/fast_resize_view.h', 'browser/ui/cocoa/fast_resize_view.h',
......
...@@ -1411,6 +1411,7 @@ ...@@ -1411,6 +1411,7 @@
'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h', 'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h',
'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm', 'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm',
'browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_browsertest.mm', 'browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_browsertest.mm',
'browser/ui/cocoa/extensions/windowed_install_dialog_controller_browsertest.mm',
'browser/ui/cocoa/find_bar/find_bar_browsertest.mm', 'browser/ui/cocoa/find_bar/find_bar_browsertest.mm',
'browser/ui/cocoa/location_bar/zoom_decoration_browsertest.mm', 'browser/ui/cocoa/location_bar/zoom_decoration_browsertest.mm',
'browser/ui/cocoa/omnibox/omnibox_view_mac_browsertest.mm', 'browser/ui/cocoa/omnibox/omnibox_view_mac_browsertest.mm',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment