Commit a215ef7a authored by dcaiafa@chromium.org's avatar dcaiafa@chromium.org

Implement Desktop Media Picker (Mac version) for the Desktop Capture API

This CL implements DesktopMediaPicker for the Mac used by the Desktop Capture API to present the user with a list of sources (including live thumbnails) to use.

BUG=237907

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=221874

Review URL: https://chromiumcodereview.appspot.com/23944003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222156 0039d316-1c4b-4281-b951-d872f2087c98
parent e0c6a8d0
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/api/desktop_capture/desktop_capture_api.h" #include "chrome/browser/extensions/api/desktop_capture/desktop_capture_api.h"
#include "base/compiler_specific.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/media/desktop_streams_registry.h" #include "chrome/browser/media/desktop_streams_registry.h"
#include "chrome/browser/media/media_capture_devices_dispatcher.h" #include "chrome/browser/media/media_capture_devices_dispatcher.h"
...@@ -87,9 +88,8 @@ bool DesktopCaptureChooseDesktopMediaFunction::RunImpl() { ...@@ -87,9 +88,8 @@ bool DesktopCaptureChooseDesktopMediaFunction::RunImpl() {
screen_capturer.Pass(), window_capturer.Pass()); screen_capturer.Pass(), window_capturer.Pass());
picker_ = g_picker_factory->CreatePicker(); picker_ = g_picker_factory->CreatePicker();
} else { } else {
// Currently DesktopMediaPicker is implemented only for platforms that // DesktopMediaPicker is not implented for all platforms yet.
// use Views. #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
#if defined(TOOLKIT_VIEWS)
model.reset(new DesktopMediaPickerModelImpl( model.reset(new DesktopMediaPickerModelImpl(
screen_capturer.Pass(), window_capturer.Pass())); screen_capturer.Pass(), window_capturer.Pass()));
picker_ = DesktopMediaPicker::Create(); picker_ = DesktopMediaPicker::Create();
......
// 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_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_BRIDGE_H_
#define CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_BRIDGE_H_
#include "base/basictypes.h"
#include "chrome/browser/media/desktop_media_picker_model.h"
// Protocol corresponding to |DesktopMediaPickerModel::Observer|.
@protocol DesktopMediaPickerObserver
- (void)sourceAddedAtIndex:(int)index;
- (void)sourceRemovedAtIndex:(int)index;
- (void)sourceNameChangedAtIndex:(int)index;
- (void)sourceThumbnailChangedAtIndex:(int)index;
@end
// Provides a |DesktopMediaPickerModel::Observer| implementation that forwards
// notifications to a objective-c object implementing the
// |DesktopMediaPickerObserver| protocol.
class DesktopMediaPickerBridge : public DesktopMediaPickerModel::Observer {
public:
DesktopMediaPickerBridge(id<DesktopMediaPickerObserver> observer);
virtual ~DesktopMediaPickerBridge();
// DesktopMediaPickerModel::Observer overrides
virtual void OnSourceAdded(int index) OVERRIDE;
virtual void OnSourceRemoved(int index) OVERRIDE;
virtual void OnSourceNameChanged(int index) OVERRIDE;
virtual void OnSourceThumbnailChanged(int index) OVERRIDE;
private:
id<DesktopMediaPickerObserver> observer_; // weak; owns this
DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerBridge);
};
#endif // CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_BRIDGE_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/media_picker/desktop_media_picker_bridge.h"
DesktopMediaPickerBridge::DesktopMediaPickerBridge(
id<DesktopMediaPickerObserver> observer)
: observer_(observer) {
}
DesktopMediaPickerBridge::~DesktopMediaPickerBridge() {
}
void DesktopMediaPickerBridge::OnSourceAdded(int index) {
[observer_ sourceAddedAtIndex:index];
}
void DesktopMediaPickerBridge::OnSourceRemoved(int index) {
[observer_ sourceRemovedAtIndex:index];
}
void DesktopMediaPickerBridge::OnSourceNameChanged(int index) {
[observer_ sourceNameChangedAtIndex:index];
}
void DesktopMediaPickerBridge::OnSourceThumbnailChanged(int index) {
[observer_ sourceThumbnailChangedAtIndex:index];
}
// 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_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_COCOA_H_
#define CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_COCOA_H_
#import "base/mac/scoped_nsobject.h"
#include "chrome/browser/media/desktop_media_picker.h"
@class DesktopMediaPickerController;
// Cocoa's DesktopMediaPicker implementation.
class DesktopMediaPickerCocoa : public DesktopMediaPicker {
public:
DesktopMediaPickerCocoa();
virtual ~DesktopMediaPickerCocoa();
// Overridden from DesktopMediaPicker:
virtual void Show(gfx::NativeWindow context,
gfx::NativeWindow parent,
const string16& app_name,
scoped_ptr<DesktopMediaPickerModel> model,
const DoneCallback& done_callback) OVERRIDE;
private:
base::scoped_nsobject<DesktopMediaPickerController> controller_;
};
#endif // CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_COCOA_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.
#include "chrome/browser/ui/cocoa/media_picker/desktop_media_picker_cocoa.h"
#import "chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.h"
DesktopMediaPickerCocoa::DesktopMediaPickerCocoa() {
}
DesktopMediaPickerCocoa::~DesktopMediaPickerCocoa() {
}
void DesktopMediaPickerCocoa::Show(gfx::NativeWindow context,
gfx::NativeWindow parent,
const string16& app_name,
scoped_ptr<DesktopMediaPickerModel> model,
const DoneCallback& done_callback) {
controller_.reset(
[[DesktopMediaPickerController alloc] initWithModel:model.Pass()
callback:done_callback
appName:app_name]);
[controller_ showWindow:nil];
}
// static
scoped_ptr<DesktopMediaPicker> DesktopMediaPicker::Create() {
return scoped_ptr<DesktopMediaPicker>(new DesktopMediaPickerCocoa());
}
// 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_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_CONTROLLER_H_
#define CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_CONTROLLER_H_
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>
#include "base/callback.h"
#import "base/mac/scoped_nsobject.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "chrome/browser/media/desktop_media_picker.h"
#include "chrome/browser/media/desktop_media_picker_model.h"
#import "chrome/browser/ui/cocoa/media_picker/desktop_media_picker_bridge.h"
// A controller for the Desktop Media Picker. Presents the user with a list of
// media sources for screen-capturing, and reports the result.
@interface DesktopMediaPickerController
: NSWindowController<NSWindowDelegate, DesktopMediaPickerObserver> {
@private
// The image browser view to present sources to the user (thumbnails and
// names).
base::scoped_nsobject<IKImageBrowserView> sourceBrowser_;
// The button used to confirm the selection.
NSButton* okButton_; // weak; owned by contentView
// The button used to cancel and close the dialog.
NSButton* cancelButton_; // weak; owned by contentView
// Provides source information (including thumbnails) to fill up |items_| and
// to render in |sourceBrowser_|.
scoped_ptr<DesktopMediaPickerModel> model_;
// To be called with the user selection.
DesktopMediaPicker::DoneCallback doneCallback_;
// Array of |DesktopMediaPickerItem| used as data for |sourceBrowser_|.
base::scoped_nsobject<NSMutableArray> items_;
// C++ bridge to use as an observer to |model_|, that forwards obj-c
// notifications to this object.
scoped_ptr<DesktopMediaPickerBridge> bridge_;
// Used to create |DesktopMediaPickerItem|s with unique IDs.
int lastImageUID_;
}
// Designated initializer.
// To show the dialog, use |NSWindowController|'s |showWindow:|.
// |callback| will be called to report the user's selection.
// |appName| will be used to format the dialog's title and the label.
- (id)initWithModel:(scoped_ptr<DesktopMediaPickerModel>)model
callback:(const DesktopMediaPicker::DoneCallback&)callback
appName:(const string16&)appName;
@end
#endif // CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_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/media_picker/desktop_media_picker_controller.h"
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/run_loop.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest_mac.h"
@interface DesktopMediaPickerController (ExposedForTesting)
- (IKImageBrowserView*)sourceBrowser;
- (NSButton*)okButton;
- (NSArray*)items;
@end
@implementation DesktopMediaPickerController (ExposedForTesting)
- (IKImageBrowserView*)sourceBrowser {
return sourceBrowser_;
}
- (NSButton*)okButton {
return okButton_;
}
- (NSButton*)cancelButton {
return cancelButton_;
}
- (NSArray*)items {
return items_;
}
@end
class FakeDesktopMediaPickerModel : public DesktopMediaPickerModel {
public:
FakeDesktopMediaPickerModel() : observer_(NULL) {
}
void AddSource(int id) {
Source source(
content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW, id),
base::Int64ToString16(id));
sources_.push_back(source);
observer_->OnSourceAdded(sources_.size() - 1);
}
void RemoveSource(int index) {
sources_.erase(sources_.begin() + index);
observer_->OnSourceRemoved(sources_.size() - 1);
}
void SetSourceThumbnail(int index) {
sources_[index].thumbnail = thumbnail_;
observer_->OnSourceThumbnailChanged(index);
}
void SetSourceName(int index, string16 name) {
sources_[index].name = name;
observer_->OnSourceNameChanged(index);
}
// DesktopMediaPickerModel implementation:
virtual void SetUpdatePeriod(base::TimeDelta period) OVERRIDE {
}
virtual void SetThumbnailSize(const gfx::Size& thumbnail_size) OVERRIDE {
}
virtual void StartUpdating(Observer* observer) OVERRIDE {
observer_ = observer;
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150, 150);
bitmap.allocPixels();
bitmap.eraseRGB(0, 255, 0);
thumbnail_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
}
virtual int source_count() const OVERRIDE {
return sources_.size();
}
virtual const Source& source(int index) const OVERRIDE {
return sources_[index];
}
private:
std::vector<Source> sources_;
Observer* observer_;
gfx::ImageSkia thumbnail_;
};
class DesktopMediaPickerControllerTest : public CocoaTest {
public:
DesktopMediaPickerControllerTest() : callback_called_(false), model_(NULL) {
}
virtual void SetUp() OVERRIDE {
CocoaTest::SetUp();
model_ = new FakeDesktopMediaPickerModel();
DesktopMediaPicker::DoneCallback callback =
base::Bind(&DesktopMediaPickerControllerTest::OnResult,
base::Unretained(this));
controller_.reset(
[[DesktopMediaPickerController alloc]
initWithModel:scoped_ptr<DesktopMediaPickerModel>(model_)
callback:callback
appName:ASCIIToUTF16("Screenshare Test")]);
}
virtual void TearDown() OVERRIDE {
controller_.reset();
CocoaTest::TearDown();
}
bool WaitForCallback() {
if (!callback_called_) {
base::RunLoop().RunUntilIdle();
}
return callback_called_;
}
protected:
void OnResult(content::DesktopMediaID source) {
EXPECT_FALSE(callback_called_);
callback_called_ = true;
source_reported_ = source;
}
content::TestBrowserThreadBundle thread_bundle_;
bool callback_called_;
content::DesktopMediaID source_reported_;
FakeDesktopMediaPickerModel* model_;
base::scoped_nsobject<DesktopMediaPickerController> controller_;
};
TEST_F(DesktopMediaPickerControllerTest, ShowAndDismiss) {
[controller_ showWindow:nil];
model_->AddSource(0);
model_->AddSource(1);
model_->SetSourceThumbnail(1);
NSArray* items = [controller_ items];
EXPECT_EQ(2U, [items count]);
EXPECT_NSEQ(@"0", [[items objectAtIndex:0] imageTitle]);
EXPECT_EQ(nil, [[items objectAtIndex:0] imageRepresentation]);
EXPECT_NSEQ(@"1", [[items objectAtIndex:1] imageTitle]);
EXPECT_TRUE([[items objectAtIndex:1] imageRepresentation] != nil);
}
TEST_F(DesktopMediaPickerControllerTest, ClickOK) {
[controller_ showWindow:nil];
model_->AddSource(0);
model_->SetSourceThumbnail(0);
model_->AddSource(1);
model_->SetSourceThumbnail(1);
EXPECT_EQ(2U, [[controller_ items] count]);
EXPECT_FALSE([[controller_ okButton] isEnabled]);
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:1];
[[controller_ sourceBrowser] setSelectionIndexes:indexSet
byExtendingSelection:NO];
EXPECT_TRUE([[controller_ okButton] isEnabled]);
[[controller_ okButton] performClick:nil];
EXPECT_TRUE(WaitForCallback());
EXPECT_EQ(model_->source(1).id, source_reported_);
}
TEST_F(DesktopMediaPickerControllerTest, ClickCancel) {
[controller_ showWindow:nil];
model_->AddSource(0);
model_->SetSourceThumbnail(0);
model_->AddSource(1);
model_->SetSourceThumbnail(1);
[[controller_ cancelButton] performClick:nil];
EXPECT_TRUE(WaitForCallback());
EXPECT_EQ(content::DesktopMediaID(), source_reported_);
}
TEST_F(DesktopMediaPickerControllerTest, CloseWindow) {
[controller_ showWindow:nil];
model_->AddSource(0);
model_->SetSourceThumbnail(0);
model_->AddSource(1);
model_->SetSourceThumbnail(1);
[controller_ close];
EXPECT_TRUE(WaitForCallback());
EXPECT_EQ(content::DesktopMediaID(), source_reported_);
}
TEST_F(DesktopMediaPickerControllerTest, UpdateThumbnail) {
[controller_ showWindow:nil];
model_->AddSource(0);
model_->SetSourceThumbnail(0);
model_->AddSource(1);
model_->SetSourceThumbnail(1);
NSArray* items = [controller_ items];
EXPECT_EQ(2U, [items count]);
NSUInteger version = [[items objectAtIndex:0] imageVersion];
model_->SetSourceThumbnail(0);
EXPECT_NE(version, [[items objectAtIndex:0] imageVersion]);
}
TEST_F(DesktopMediaPickerControllerTest, UpdateName) {
[controller_ showWindow:nil];
model_->AddSource(0);
model_->SetSourceThumbnail(0);
model_->AddSource(1);
model_->SetSourceThumbnail(1);
NSArray* items = [controller_ items];
EXPECT_EQ(2U, [items count]);
NSUInteger version = [[items objectAtIndex:0] imageVersion];
model_->SetSourceThumbnail(0);
EXPECT_NE(version, [[items objectAtIndex:0] imageVersion]);
}
TEST_F(DesktopMediaPickerControllerTest, RemoveSource) {
[controller_ showWindow:nil];
model_->AddSource(0);
model_->AddSource(1);
model_->AddSource(2);
model_->SetSourceName(1, ASCIIToUTF16("foo"));
NSArray* items = [controller_ items];
EXPECT_EQ(3U, [items count]);
EXPECT_NSEQ(@"foo", [[items objectAtIndex:1] imageTitle]);
}
// 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_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_ITEM_H_
#define CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_ITEM_H_
#import <AppKit/AppKit.h>
#import "base/mac/scoped_nsobject.h"
#include "chrome/browser/media/desktop_media_picker_model.h"
// Stores the data representing a |DesktopMediaPicker| source for displaying in
// a |IKImageBrowserView|. Implements the |IKImageBrowserItem| informal
// protocol.
@interface DesktopMediaPickerItem : NSObject {
@private
content::DesktopMediaID sourceID_;
base::scoped_nsobject<NSString> imageUID_;
base::scoped_nsobject<NSString> imageTitle_;
base::scoped_nsobject<NSImage> image_;
NSUInteger imageVersion_;
}
// Designated initializer.
// |sourceID| is the corresponding source's ID as provided by the model.
// |imageUID| is a unique number in the context of the |IKImageBrowserView|
// instance.
// |imageTitle| is the source's name to be used as the label in
// |IKImageBrowserView|.
- (id)initWithSourceId:(content::DesktopMediaID)sourceID
imageUID:(int)imageUID
imageTitle:(NSString*)imageTitle;
// Returns the source's ID.
- (content::DesktopMediaID)sourceID;
// Sets the image of the item to be displayed in |IKImageBrowserView|.
- (void)setImageRepresentation:(NSImage*)image;
// Sets the label of the item to be displayed in |IKImageBrowserView|.
- (void)setImageTitle:(NSString*)imageTitle;
@end
#endif // CHROME_BROWSER_UI_COCOA_MEDIA_PICKER_DESKTOP_MEDIA_PICKER_ITEM_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/media_picker/desktop_media_picker_item.h"
#import <Quartz/Quartz.h>
#include "chrome/browser/media/desktop_media_picker_model.h"
@implementation DesktopMediaPickerItem
- (id)initWithSourceId:(content::DesktopMediaID)sourceID
imageUID:(int)imageUID
imageTitle:(NSString*)imageTitle {
if ((self = [super init])) {
sourceID_ = sourceID;
imageUID_.reset([[NSString stringWithFormat:@"%d", imageUID] retain]);
imageTitle_.reset([imageTitle retain]);
}
return self;
}
- (content::DesktopMediaID)sourceID {
return sourceID_;
}
- (void)setImageRepresentation:(NSImage*)image {
image_.reset([image retain]);
++imageVersion_;
}
- (void)setImageTitle:(NSString*)imageTitle {
imageTitle_.reset([imageTitle copy]);
}
#pragma mark IKImageBrowserItem
- (NSString*)imageUID {
return imageUID_;
}
- (NSString*)imageRepresentationType {
return IKImageBrowserNSImageRepresentationType;
}
- (NSString*)imageTitle {
return imageTitle_.get();
}
- (NSUInteger)imageVersion {
return imageVersion_;
}
- (id)imageRepresentation {
return image_.get();
}
@end // @interface DesktopMediaPickerItem
...@@ -804,6 +804,14 @@ ...@@ -804,6 +804,14 @@
'browser/ui/cocoa/login_prompt_cocoa.h', 'browser/ui/cocoa/login_prompt_cocoa.h',
'browser/ui/cocoa/login_prompt_cocoa.mm', 'browser/ui/cocoa/login_prompt_cocoa.mm',
'browser/ui/cocoa/main_menu_item.h', 'browser/ui/cocoa/main_menu_item.h',
'browser/ui/cocoa/media_picker/desktop_media_picker_bridge.h',
'browser/ui/cocoa/media_picker/desktop_media_picker_bridge.mm',
'browser/ui/cocoa/media_picker/desktop_media_picker_cocoa.h',
'browser/ui/cocoa/media_picker/desktop_media_picker_cocoa.mm',
'browser/ui/cocoa/media_picker/desktop_media_picker_controller.h',
'browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm',
'browser/ui/cocoa/media_picker/desktop_media_picker_item.h',
'browser/ui/cocoa/media_picker/desktop_media_picker_item.mm',
'browser/ui/cocoa/menu_button.h', 'browser/ui/cocoa/menu_button.h',
'browser/ui/cocoa/menu_button.mm', 'browser/ui/cocoa/menu_button.mm',
'browser/ui/cocoa/multi_key_equivalent_button.h', 'browser/ui/cocoa/multi_key_equivalent_button.h',
...@@ -2994,6 +3002,11 @@ ...@@ -2994,6 +3002,11 @@
'include_dirs': [ 'include_dirs': [
'../third_party/GTM', '../third_party/GTM',
], ],
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/Quartz.framework',
],
},
'actions': [ 'actions': [
{ {
# This action is used to extract the localization data from xib # This action is used to extract the localization data from xib
......
...@@ -1521,6 +1521,7 @@ ...@@ -1521,6 +1521,7 @@
'browser/ui/cocoa/location_bar/image_decoration_unittest.mm', 'browser/ui/cocoa/location_bar/image_decoration_unittest.mm',
'browser/ui/cocoa/location_bar/keyword_hint_decoration_unittest.mm', 'browser/ui/cocoa/location_bar/keyword_hint_decoration_unittest.mm',
'browser/ui/cocoa/location_bar/selected_keyword_decoration_unittest.mm', 'browser/ui/cocoa/location_bar/selected_keyword_decoration_unittest.mm',
'browser/ui/cocoa/media_picker/desktop_media_picker_controller_unittest.mm',
'browser/ui/cocoa/menu_button_unittest.mm', 'browser/ui/cocoa/menu_button_unittest.mm',
'browser/ui/cocoa/nine_part_button_cell_unittest.mm', 'browser/ui/cocoa/nine_part_button_cell_unittest.mm',
'browser/ui/cocoa/notifications/balloon_controller_unittest.mm', 'browser/ui/cocoa/notifications/balloon_controller_unittest.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