Commit 8f40e257 authored by dcaiafa@chromium.org's avatar dcaiafa@chromium.org

Remove old Chromoting iOS client

BUG=

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

Cr-Commit-Position: refs/heads/master@{#290035}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290035 0039d316-1c4b-4281-b951-d872f2087c98
parent 0fb9c01a
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>net.fusionlabs.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>2.4</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>ChromotingModel.xcdatamodel</string>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="5063" systemVersion="13C64" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="HostPreferences" representedClassName="HostPreferences" syncable="YES">
<attribute name="askForPin" optional="YES" attributeType="Boolean" syncable="YES"/>
<attribute name="hostId" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="hostPin" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="pairId" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="pairSecret" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<elements>
<element name="HostPreferences" positionX="0" positionY="0" width="128" height="118"/>
</elements>
</model>
\ No newline at end of file
This diff is collapsed.
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "57x57",
"scale" : "1x"
},
{
"idiom" : "iphone",
"size" : "57x57",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "chromoting120.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "50x50",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "50x50",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "72x72",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "72x72",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "chromoting76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "chromoting152.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
/* This file is required */
/* Localized versions of Info.plist keys */
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import <UIKit/UIKit.h>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "media/base/yuv_convert.h"
#include "net/socket/ssl_server_socket.h"
#import "app_delegate.h"
int main(int argc, char* argv[]) {
// This class is designed to fulfill the dependents needs when it goes out of
// scope and gets destructed
base::AtExitManager exitManager;
// Publicize the CommandLine
CommandLine::Init(argc, argv);
#ifdef DEBUG
// Set min log level for debug builds. For some reason this has to be
// negative.
logging::SetMinLogLevel(-1);
#endif
// Allows later decoding of video frames.
media::InitializeCPUSpecificYUVConversions();
// Enable support for SSL server sockets, which must be done as early as
// possible, preferably before any NSS SSL sockets (client or server) have
// been created.
net::EnableSSLServerSockets();
@autoreleasepool {
return UIApplicationMain(
argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>net.fusionlabs.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>2.4</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Chromoting_unittests.xcdatamodel</string>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="Test1.xcdatamodel" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<elements/>
</model>
\ No newline at end of file
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"subtype" : "retina4",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import <UIKit/UIKit.h>
#include "base/memory/scoped_ptr.h"
#include "base/test/test_suite.h"
#include "testing/gtest_mac.h"
#import "remoting/ios/Chromoting_unittests/main_no_arc.h"
int main(int argc, char* argv[]) {
// Initialization that must occur with no Automatic Reference Counting (ARC)
remoting::main_no_arc::init();
testing::InitGoogleTest(&argc, argv);
scoped_ptr<base::TestSuite> runner(new base::TestSuite(argc, argv));
runner.get()->Run();
}
// Copyright 2014 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 "remoting/ios/Chromoting_unittests/main_no_arc.h"
#include "base/message_loop/message_loop.h"
namespace remoting {
namespace main_no_arc {
void init() {
// Declare the pump factory before the test suite can declare it. The test
// suite assumed we are running in x86 simulator, but this test project runs
// on an actual device
base::MessageLoop::InitMessagePumpForUIFactory(&base::MessagePumpMac::Create);
}
} // main_no_arc
} // remoting
// Copyright 2014 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.
namespace remoting {
namespace main_no_arc {
void init();
} // main_no_arc
} // remoting
include_rules = [
"+remoting/host",
]
// Copyright 2014 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 REMOTING_IOS_APP_DELEGATE_H_
#define REMOTING_IOS_APP_DELEGATE_H_
#import <UIKit/UIKit.h>
// Default created delegate class for the entire application
@interface AppDelegate : UIResponder<UIApplicationDelegate>
@property(strong, nonatomic) UIWindow* window;
@end
#endif // REMOTING_IOS_APP_DELEGATE_H_
\ No newline at end of file
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/app_delegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return YES;
}
@end
// Copyright 2014 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 REMOTING_IOS_AUTHORIZE_H_
#define REMOTING_IOS_AUTHORIZE_H_
#import <UIKit/UIKit.h>
// TODO (aboone) This include is for The Google Toolbox for Mac OAuth 2
// https://code.google.com/p/gtm-oauth2/ This may need to be added as a
// third-party or locate the proper project in Chromium.
#import "GTMOAuth2Authentication.h"
@interface Authorize : NSObject
+ (GTMOAuth2Authentication*)getAnyExistingAuthorization;
+ (void)beginRequest:(GTMOAuth2Authentication*)authorization
delegate:self
didFinishSelector:(SEL)sel;
+ (void)appendCredentials:(NSMutableURLRequest*)request;
+ (UINavigationController*)createLoginController:(id)delegate
finishedSelector:(SEL)finishedSelector;
@end
#endif // REMOTING_IOS_AUTHORIZE_H_
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// TODO (aboone) This include is for The Google Toolbox for Mac OAuth 2
// Controllers https://code.google.com/p/gtm-oauth2/ This may need to be added
// as a third-party or locate the proper project in Chromium.
#import "GTMOAuth2ViewControllerTouch.h"
#include "google_apis/google_api_keys.h"
#import "remoting/ios/authorize.h"
#include "remoting/base/service_urls.h"
// TODO (aboone) Pulling in some service values from the host side. The cc's
// are also compiled as part of this project because the target remoting_host
// does not build on iOS right now.
#include "remoting/host/setup/oauth_helper.h"
namespace {
static NSString* const kKeychainItemName = @"Google Chromoting iOS";
NSString* ClientId() {
return
[NSString stringWithUTF8String:google_apis::GetOAuth2ClientID(
google_apis::CLIENT_REMOTING).c_str()];
}
NSString* ClientSecret() {
return
[NSString stringWithUTF8String:google_apis::GetOAuth2ClientSecret(
google_apis::CLIENT_REMOTING).c_str()];
}
NSString* Scopes() {
return [NSString stringWithUTF8String:remoting::GetOauthScope().c_str()];
}
NSMutableString* HostURL() {
return
[NSMutableString stringWithUTF8String:remoting::ServiceUrls::GetInstance()
->directory_hosts_url()
.c_str()];
}
NSString* APIKey() {
return [NSString stringWithUTF8String:google_apis::GetAPIKey().c_str()];
}
} // namespace
@implementation Authorize
+ (GTMOAuth2Authentication*)getAnyExistingAuthorization {
// Ensure the google_apis lib has keys
// If this check fails then google_apis was not built right
// TODO (aboone) For now we specify the preprocessor macros for
// GOOGLE_CLIENT_SECRET_REMOTING and GOOGLE_CLIENT_ID_REMOTING when building
// the google_apis target. The values may be developer specific, and should
// be well know to the project staff.
// See http://www.chromium.org/developers/how-tos/api-keys for more general
// information.
DCHECK(![ClientId() isEqualToString:@"dummytoken"]);
return [GTMOAuth2ViewControllerTouch
authForGoogleFromKeychainForName:kKeychainItemName
clientID:ClientId()
clientSecret:ClientSecret()];
}
+ (void)beginRequest:(GTMOAuth2Authentication*)authReq
delegate:(id)delegate
didFinishSelector:(SEL)sel {
// Build request URL using API HTTP endpoint, and our api key
NSMutableString* hostsUrl = HostURL();
[hostsUrl appendString:@"?key="];
[hostsUrl appendString:APIKey()];
NSMutableURLRequest* theRequest =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:hostsUrl]];
// Add scopes if needed
NSString* scope = authReq.scope;
if ([scope rangeOfString:Scopes()].location == NSNotFound) {
scope = [GTMOAuth2Authentication scopeWithStrings:scope, Scopes(), nil];
authReq.scope = scope;
}
// Execute request async
[authReq authorizeRequest:theRequest delegate:delegate didFinishSelector:sel];
}
+ (void)appendCredentials:(NSMutableURLRequest*)request {
// Add credentials for service
[request addValue:ClientId() forHTTPHeaderField:@"client_id"];
[request addValue:ClientSecret() forHTTPHeaderField:@"client_secret"];
}
+ (UINavigationController*)createLoginController:(id)delegate
finishedSelector:(SEL)finishedSelector {
[GTMOAuth2ViewControllerTouch
removeAuthFromKeychainForName:kKeychainItemName];
// When the sign in is complete a http redirection occurs, and the
// user would see the output. We do not want the user to notice this
// transition. Wrapping the oAuth2 Controller in a
// UINavigationController causes the view to render as a blank/black
// page when a http redirection occurs.
return [[UINavigationController alloc]
initWithRootViewController:[[GTMOAuth2ViewControllerTouch alloc]
initWithScope:Scopes()
clientID:ClientId()
clientSecret:ClientSecret()
keychainItemName:kKeychainItemName
delegate:delegate
finishedSelector:finishedSelector]];
}
@end
include_rules = [
"+net/url_request",
"-remoting/host",
"+remoting/client",
"+remoting/protocol",
"+remoting/signaling",
]
This diff is collapsed.
// Copyright 2014 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 REMOTING_IOS_BRIDGE_CLIENT_INSTANCE_H_
#define REMOTING_IOS_BRIDGE_CLIENT_INSTANCE_H_
#include <string>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "net/url_request/url_request_context_getter.h"
#include "remoting/base/auto_thread.h"
#include "remoting/client/chromoting_client.h"
#include "remoting/client/client_context.h"
#include "remoting/client/client_user_interface.h"
#include "remoting/client/frame_consumer_proxy.h"
#include "remoting/client/software_video_renderer.h"
#include "remoting/ios/bridge/frame_consumer_bridge.h"
#include "remoting/protocol/clipboard_stub.h"
#include "remoting/protocol/cursor_shape_stub.h"
#include "remoting/protocol/network_settings.h"
#include "remoting/signaling/xmpp_signal_strategy.h"
namespace remoting {
class ClientProxy;
// ClientUserInterface that indirectly makes and receives OBJ_C calls from the
// UI application
class ClientInstance : public ClientUserInterface,
public protocol::ClipboardStub,
public protocol::CursorShapeStub,
public base::RefCountedThreadSafe<ClientInstance> {
public:
// Initiates a connection with the specified host. Call from the UI thread. To
// connect with an unpaired host, pass in |pairing_id| and |pairing_secret| as
// empty strings.
ClientInstance(const base::WeakPtr<ClientProxy>& proxy,
const std::string& username,
const std::string& auth_token,
const std::string& host_jid,
const std::string& host_id,
const std::string& host_pubkey,
const std::string& pairing_id,
const std::string& pairing_secret);
// Begins the connection process. Should not be called again until after
// |CleanUp|
void Start();
// Terminates the current connection (if it hasn't already failed) and cleans
// up. Must be called before destruction can occur or a memory leak may occur.
void Cleanup();
// Notifies the user interface that the user needs to enter a PIN. The
// current authentication attempt is put on hold until |callback| is invoked.
// May be called on any thread.
void FetchSecret(bool pairable,
const protocol::SecretFetchedCallback& callback);
// Provides the user's PIN and resumes the host authentication attempt. Call
// on the UI thread once the user has finished entering this PIN into the UI,
// but only after the UI has been asked to provide a PIN (via FetchSecret()).
void ProvideSecret(const std::string& pin, bool create_pair);
// Moves the host's cursor to the specified coordinates, optionally with some
// mouse button depressed. If |button| is BUTTON_UNDEFINED, no click is made.
void PerformMouseAction(
const webrtc::DesktopVector& position,
const webrtc::DesktopVector& wheel_delta,
int /* protocol::MouseEvent_MouseButton */ whichButton,
bool button_down);
// Sends the provided keyboard scan code to the host.
void PerformKeyboardAction(int key_code, bool key_down);
// ClientUserInterface implementation.
virtual void OnConnectionState(protocol::ConnectionToHost::State state,
protocol::ErrorCode error) OVERRIDE;
virtual void OnConnectionReady(bool ready) OVERRIDE;
virtual void OnRouteChanged(const std::string& channel_name,
const protocol::TransportRoute& route) OVERRIDE;
virtual void SetCapabilities(const std::string& capabilities) OVERRIDE;
virtual void SetPairingResponse(const protocol::PairingResponse& response)
OVERRIDE;
virtual void DeliverHostMessage(const protocol::ExtensionMessage& message)
OVERRIDE;
virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE;
virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE;
// CursorShapeStub implementation.
virtual void InjectClipboardEvent(const protocol::ClipboardEvent& event)
OVERRIDE;
// ClipboardStub implementation.
virtual void SetCursorShape(const protocol::CursorShapeInfo& shape) OVERRIDE;
private:
// This object is ref-counted, so it cleans itself up.
virtual ~ClientInstance();
void ConnectToHostOnNetworkThread(
scoped_refptr<FrameConsumerProxy> consumer_proxy,
const base::Closure& done);
void DisconnectFromHostOnNetworkThread(const base::Closure& done);
// Proxy to exchange messages between the
// common Chromoting protocol and UI Application.
base::WeakPtr<ClientProxy> proxyToClient_;
// ID of the host we are connecting to.
std::string host_id_;
std::string host_jid_;
// This group of variables is to be used on the display thread.
scoped_ptr<SoftwareVideoRenderer> video_renderer_;
scoped_ptr<FrameConsumerBridge> view_;
// This group of variables is to be used on the network thread.
scoped_ptr<ClientContext> client_context_;
scoped_ptr<protocol::Authenticator> authenticator_;
scoped_ptr<ChromotingClient> client_;
XmppSignalStrategy::XmppServerConfig xmpp_config_;
scoped_ptr<XmppSignalStrategy> signaling_; // Must outlive client_
// Pass this the user's PIN once we have it. To be assigned and accessed on
// the UI thread, but must be posted to the network thread to call it.
protocol::SecretFetchedCallback pin_callback_;
// Indicates whether to establish a new pairing with this host. This is
// modified in ProvideSecret(), but thereafter to be used only from the
// network thread. (This is safe because ProvideSecret() is invoked at most
// once per run, and always before any reference to this flag.)
bool create_pairing_;
// Chromium code's connection to the OBJ_C message loop. Once created the
// MessageLoop will live for the life of the program. An attempt was made to
// create the primary message loop earlier in the programs life, but a
// MessageLoop requires ARC to be disabled.
base::MessageLoopForUI* ui_loop_;
// References to native threads.
scoped_refptr<AutoThreadTaskRunner> ui_task_runner_;
scoped_refptr<AutoThreadTaskRunner> network_task_runner_;
scoped_refptr<net::URLRequestContextGetter> url_requester_;
friend class base::RefCountedThreadSafe<ClientInstance>;
DISALLOW_COPY_AND_ASSIGN(ClientInstance);
};
} // namespace remoting
#endif // REMOTING_IOS_BRIDGE_CLIENT_INSTANCE_H_
This diff is collapsed.
// Copyright 2014 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 REMOTING_IOS_BRIDGE_HOST_PROXY_H_
#define REMOTING_IOS_BRIDGE_HOST_PROXY_H_
#include <string>
#include <objc/objc.h>
#include "base/memory/weak_ptr.h"
#include "remoting/protocol/connection_to_host.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#if defined(__OBJC__)
@class ClientProxyDelegateWrapper;
#else // __OBJC__
class ClientProxyDelegateWrapper;
#endif // __OBJC__
namespace remoting {
// Proxies incoming common Chromoting protocol (HOST) to the UI Application
// (CLIENT). The HOST will have a Weak reference to call member functions on
// the UI Thread.
class ClientProxy : public base::SupportsWeakPtr<ClientProxy> {
public:
ClientProxy(ClientProxyDelegateWrapper* wrapper);
// Notifies the user of the current connection status.
void ReportConnectionStatus(protocol::ConnectionToHost::State state,
protocol::ErrorCode error);
// Display a dialog box asking the user to enter a PIN.
void DisplayAuthenticationPrompt(bool pairing_supported);
// Saves new pairing credentials to permanent storage.
void CommitPairingCredentials(const std::string& hostId,
const std::string& pairId,
const std::string& pairSecret);
// Delivers the latest image buffer for the canvas.
void RedrawCanvas(const webrtc::DesktopSize& view_size,
webrtc::DesktopFrame* buffer,
const webrtc::DesktopRegion& region);
// Updates cursor.
void UpdateCursorShape(const protocol::CursorShapeInfo& cursor_shape);
private:
// Pointer to the UI application which implements the ClientProxyDelegate.
// (id) is similar to a (void*) |delegate_| is set from accepting a
// strongly typed @interface which wraps the @protocol ClientProxyDelegate.
// see comments for host_proxy_delegate_wrapper.h
id delegate_;
DISALLOW_COPY_AND_ASSIGN(ClientProxy);
};
} // namespace remoting
#endif // REMOTING_IOS_BRIDGE_HOST_PROXY_H_
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#include "remoting/ios/bridge/client_proxy.h"
#import "remoting/ios/data_store.h"
#import "remoting/ios/host_preferences.h"
#import "remoting/ios/bridge/client_proxy_delegate_wrapper.h"
namespace {
// The value indicating a successful connection has been established via a call
// to ReportConnectionStatus
const static int kSuccessfulConnection = 3;
// Translate a connection status code integer to a NSString description
NSString* GetStatusMsgFromInteger(NSInteger code) {
switch (code) {
case 0: // INITIALIZING
return @"Initializing connection";
case 1: // CONNECTING
return @"Connecting";
case 2: // AUTHENTICATED
return @"Authenticated";
case 3: // CONNECTED
return @"Connected";
case 4: // FAILED
return @"Connection Failed";
case 5: // CLOSED
return @"Connection closed";
default:
return @"Unknown connection state";
}
}
// Translate a connection error code integer to a NSString description
NSString* GetErrorMsgFromInteger(NSInteger code) {
switch (code) {
case 1: // PEER_IS_OFFLINE
return @"Requested host is offline.";
case 2: // SESSION_REJECTED
return @"Session was rejected by the host.";
case 3: // INCOMPATIBLE_PROTOCOL
return @"Incompatible Protocol.";
case 4: // AUTHENTICATION_FAILED
return @"Authentication Failed.";
case 5: // CHANNEL_CONNECTION_ERROR
return @"Channel Connection Error";
case 6: // SIGNALING_ERROR
return @"Signaling Error";
case 7: // SIGNALING_TIMEOUT
return @"Signaling Timeout";
case 8: // HOST_OVERLOAD
return @"Host Overload";
case 9: // UNKNOWN_ERROR
return @"An unknown error has occurred, preventing the session "
"from opening.";
default:
return @"An unknown error code has occurred.";
}
}
} // namespace
namespace remoting {
ClientProxy::ClientProxy(ClientProxyDelegateWrapper* wrapper) {
delegate_ = [wrapper delegate];
}
void ClientProxy::ReportConnectionStatus(
protocol::ConnectionToHost::State state,
protocol::ErrorCode error) {
DCHECK(delegate_);
if (state <= kSuccessfulConnection && error == protocol::ErrorCode::OK) {
// Report Progress
[delegate_ connectionStatus:GetStatusMsgFromInteger(state)];
if (state == kSuccessfulConnection) {
[delegate_ connected];
}
} else {
[delegate_ connectionStatus:GetStatusMsgFromInteger(state)];
if (error != protocol::ErrorCode::OK) {
[delegate_ connectionFailed:GetErrorMsgFromInteger(error)];
}
}
}
void ClientProxy::DisplayAuthenticationPrompt(bool pairing_supported) {
DCHECK(delegate_);
[delegate_ requestHostPin:pairing_supported];
}
void ClientProxy::CommitPairingCredentials(const std::string& hostId,
const std::string& pairId,
const std::string& pairSecret) {
DCHECK(delegate_);
NSString* nsHostId = [[NSString alloc] initWithUTF8String:hostId.c_str()];
NSString* nsPairId = [[NSString alloc] initWithUTF8String:pairId.c_str()];
NSString* nsPairSecret =
[[NSString alloc] initWithUTF8String:pairSecret.c_str()];
const HostPreferences* hostPrefs =
[[DataStore sharedStore] getHostForId:nsHostId];
if (hostPrefs == nil) {
hostPrefs = [[DataStore sharedStore] createHost:nsHostId];
}
if (hostPrefs) {
hostPrefs.pairId = nsPairId;
hostPrefs.pairSecret = nsPairSecret;
[[DataStore sharedStore] saveChanges];
}
}
void ClientProxy::RedrawCanvas(const webrtc::DesktopSize& view_size,
webrtc::DesktopFrame* buffer,
const webrtc::DesktopRegion& region) {
DCHECK(delegate_);
std::vector<webrtc::DesktopRect> regions;
for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) {
const webrtc::DesktopRect& rect(i.rect());
regions.push_back(webrtc::DesktopRect::MakeXYWH(
rect.left(), rect.top(), rect.width(), rect.height()));
}
[delegate_ applyFrame:view_size
stride:buffer->stride()
data:buffer->data()
regions:regions];
}
void ClientProxy::UpdateCursorShape(
const protocol::CursorShapeInfo& cursor_shape) {
DCHECK(delegate_);
[delegate_ applyCursor:webrtc::DesktopSize(cursor_shape.width(),
cursor_shape.height())
hotspot:webrtc::DesktopVector(cursor_shape.hotspot_x(),
cursor_shape.hotspot_y())
cursorData:(uint8_t*)cursor_shape.data().c_str()];
}
} // namespace remoting
// Copyright 2014 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 REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_H_
#define REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_H_
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include <vector>
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
// Contract to provide for callbacks from the common Chromoting protocol to the
// UI Application.
@protocol ClientProxyDelegate<NSObject>
// HOST request for client to input their PIN.
- (void)requestHostPin:(BOOL)pairingSupported;
// HOST notification that a connection has been successfully opened.
- (void)connected;
// HOST notification for a change in connections status.
- (void)connectionStatus:(NSString*)statusMessage;
// HOST notification that a connection has failed.
- (void)connectionFailed:(NSString*)errorMessage;
// A new Canvas (desktop) update has arrived.
- (void)applyFrame:(const webrtc::DesktopSize&)size
stride:(NSInteger)stride
data:(uint8_t*)data
regions:(const std::vector<webrtc::DesktopRect>&)regions;
// A new Cursor (mouse) update has arrived.
- (void)applyCursor:(const webrtc::DesktopSize&)size
hotspot:(const webrtc::DesktopVector&)hotspot
cursorData:(uint8_t*)data;
@end
#endif // REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_H_
\ No newline at end of file
// Copyright 2014 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 REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_WRAPPER_H_
#define REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_WRAPPER_H_
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include <vector>
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#import "remoting/ios/bridge/client_proxy_delegate.h"
// Wraps ClientProxyDelegate in a class so C++ can accept a strongly typed
// pointer. C++ does not understand the id<> convention of passing around a
// OBJ_C @protocol pointer. So the @protocol is wrapped and the class is passed
// around. After accepting an instance of ClientProxyDelegateWrapper, the
// @protocol can be referenced as type (id), which is similar to a (void*),
@interface ClientProxyDelegateWrapper : NSObject
@property(nonatomic, retain, readonly) id<ClientProxyDelegate> delegate;
- (id)init __unavailable;
+ (id)wrapDelegate:(id<ClientProxyDelegate>)delegate;
@end
#endif // REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_WRAPPER_H_
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/bridge/client_proxy_delegate_wrapper.h"
@interface ClientProxyDelegateWrapper (Private)
- (id)initWithDelegate:(id<ClientProxyDelegate>)delegate;
@end
@implementation ClientProxyDelegateWrapper
@synthesize delegate = _delegate;
- (id)initWithDelegate:(id<ClientProxyDelegate>)delegate {
self = [super init];
if (self) {
_delegate = delegate;
}
return self;
}
+ (id)wrapDelegate:(id<ClientProxyDelegate>)delegate {
return [[ClientProxyDelegateWrapper alloc] initWithDelegate:delegate];
}
@end
This diff is collapsed.
// Copyright 2014 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 "remoting/ios/bridge/frame_consumer_bridge.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/synchronization/waitable_event.h"
#include "remoting/base/util.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
namespace remoting {
FrameConsumerBridge::FrameConsumerBridge(OnFrameCallback callback)
: callback_(callback), frame_producer_(NULL) {}
FrameConsumerBridge::~FrameConsumerBridge() {
// The producer should now return any pending buffers. At this point, however,
// the buffers are returned via tasks which may not be scheduled before the
// producer, so we free all the buffers once the producer's queue is empty.
// And the scheduled tasks will die quietly.
if (frame_producer_) {
base::WaitableEvent done_event(true, false);
frame_producer_->RequestReturnBuffers(base::Bind(
&base::WaitableEvent::Signal, base::Unretained(&done_event)));
done_event.Wait();
}
}
void FrameConsumerBridge::Initialize(FrameProducer* producer) {
DCHECK(!frame_producer_);
frame_producer_ = producer;
DCHECK(frame_producer_);
}
void FrameConsumerBridge::ApplyBuffer(const webrtc::DesktopSize& view_size,
const webrtc::DesktopRect& clip_area,
webrtc::DesktopFrame* buffer,
const webrtc::DesktopRegion& region,
const webrtc::DesktopRegion& shape) {
DCHECK(frame_producer_);
if (!view_size_.equals(view_size)) {
// Drop the frame, since the data belongs to the previous generation,
// before SetSourceSize() called SetOutputSizeAndClip().
ReturnBuffer(buffer);
return;
}
// This call completes synchronously.
callback_.Run(view_size, buffer, region);
// Recycle |buffer| by returning it to |frame_producer_| as the next buffer
frame_producer_->DrawBuffer(buffer);
}
void FrameConsumerBridge::ReturnBuffer(webrtc::DesktopFrame* buffer) {
DCHECK(frame_producer_);
ScopedVector<webrtc::DesktopFrame>::iterator it =
std::find(buffers_.begin(), buffers_.end(), buffer);
DCHECK(it != buffers_.end());
buffers_.erase(it);
}
void FrameConsumerBridge::SetSourceSize(const webrtc::DesktopSize& source_size,
const webrtc::DesktopVector& dpi) {
DCHECK(frame_producer_);
view_size_ = source_size;
webrtc::DesktopRect clip_area = webrtc::DesktopRect::MakeSize(view_size_);
frame_producer_->SetOutputSizeAndClip(view_size_, clip_area);
// Now that the size is well known, ask the producer to start drawing
DrawWithNewBuffer();
}
FrameConsumerBridge::PixelFormat FrameConsumerBridge::GetPixelFormat() {
return FORMAT_RGBA;
}
void FrameConsumerBridge::DrawWithNewBuffer() {
DCHECK(frame_producer_);
webrtc::DesktopFrame* buffer = new webrtc::BasicDesktopFrame(view_size_);
buffers_.push_back(buffer);
frame_producer_->DrawBuffer(buffer);
}
} // namespace remoting
// Copyright 2014 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 REMOTING_IOS_BRIDGE_FRAME_CONSUMER_BRIDGE_H_
#define REMOTING_IOS_BRIDGE_FRAME_CONSUMER_BRIDGE_H_
#include <list>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "remoting/client/frame_consumer.h"
#include "remoting/client/frame_producer.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
namespace remoting {
class FrameConsumerBridge : public base::SupportsWeakPtr<FrameConsumerBridge>,
public FrameConsumer {
public:
typedef base::Callback<void(const webrtc::DesktopSize& view_size,
webrtc::DesktopFrame* buffer,
const webrtc::DesktopRegion& region)>
OnFrameCallback;
// A callback is provided to return frame updates asynchronously.
explicit FrameConsumerBridge(OnFrameCallback callback);
virtual ~FrameConsumerBridge();
// This must be called before any other functional use.
void Initialize(FrameProducer* producer);
// FrameConsumer implementation.
virtual void ApplyBuffer(const webrtc::DesktopSize& view_size,
const webrtc::DesktopRect& clip_area,
webrtc::DesktopFrame* buffer,
const webrtc::DesktopRegion& region,
const webrtc::DesktopRegion& shape) OVERRIDE;
virtual void ReturnBuffer(webrtc::DesktopFrame* buffer) OVERRIDE;
virtual void SetSourceSize(const webrtc::DesktopSize& source_size,
const webrtc::DesktopVector& dpi) OVERRIDE;
virtual PixelFormat GetPixelFormat() OVERRIDE;
private:
// Allocates a new buffer of |view_size_|, and tells the producer to draw onto
// it. This can be called as soon as the producer is known, but is not
// required until ready to receive frames.
void DrawWithNewBuffer();
OnFrameCallback callback_;
FrameProducer* frame_producer_;
webrtc::DesktopSize view_size_;
// List of allocated image buffers.
ScopedVector<webrtc::DesktopFrame> buffers_;
DISALLOW_COPY_AND_ASSIGN(FrameConsumerBridge);
};
} // namespace remoting
#endif // REMOTING_IOS_BRIDGE_FRAME_CONSUMER_BRIDGE_H_
// Copyright 2014 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 "remoting/ios/bridge/frame_consumer_bridge.h"
#include <queue>
#include <gtest/gtest.h>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
namespace {
const webrtc::DesktopSize kFrameSize(100, 100);
const webrtc::DesktopVector kDpi(100, 100);
const webrtc::DesktopRect FrameRect() {
return webrtc::DesktopRect::MakeSize(kFrameSize);
}
webrtc::DesktopRegion FrameRegion() {
return webrtc::DesktopRegion(FrameRect());
}
void FrameDelivery(const webrtc::DesktopSize& view_size,
webrtc::DesktopFrame* buffer,
const webrtc::DesktopRegion& region) {
ASSERT_TRUE(view_size.equals(kFrameSize));
ASSERT_TRUE(region.Equals(FrameRegion()));
};
} // namespace
namespace remoting {
class FrameProducerTester : public FrameProducer {
public:
virtual ~FrameProducerTester() {};
virtual void DrawBuffer(webrtc::DesktopFrame* buffer) OVERRIDE {
frames.push(buffer);
};
virtual void InvalidateRegion(const webrtc::DesktopRegion& region) OVERRIDE {
NOTIMPLEMENTED();
};
virtual void RequestReturnBuffers(const base::Closure& done) OVERRIDE {
// Don't have to actually return the buffers. This function is really
// saying don't use the references anymore, they are now invalid.
while (!frames.empty()) {
frames.pop();
}
done.Run();
};
virtual void SetOutputSizeAndClip(const webrtc::DesktopSize& view_size,
const webrtc::DesktopRect& clip_area)
OVERRIDE {
viewSize = view_size;
clipArea = clip_area;
};
std::queue<webrtc::DesktopFrame*> frames;
webrtc::DesktopSize viewSize;
webrtc::DesktopRect clipArea;
};
class FrameConsumerBridgeTest : public ::testing::Test {
protected:
virtual void SetUp() OVERRIDE {
frameProducer_.reset(new FrameProducerTester());
frameConsumer_.reset(new FrameConsumerBridge(base::Bind(&FrameDelivery)));
frameConsumer_->Initialize(frameProducer_.get());
}
virtual void TearDown() OVERRIDE {}
scoped_ptr<FrameProducerTester> frameProducer_;
scoped_ptr<FrameConsumerBridge> frameConsumer_;
};
TEST(FrameConsumerBridgeTest_NotInitialized, CreateAndRelease) {
scoped_ptr<FrameConsumerBridge> frameConsumer_(
new FrameConsumerBridge(base::Bind(&FrameDelivery)));
ASSERT_TRUE(frameConsumer_.get() != NULL);
frameConsumer_.reset();
ASSERT_TRUE(frameConsumer_.get() == NULL);
}
TEST_F(FrameConsumerBridgeTest, ApplyBuffer) {
webrtc::DesktopFrame* frame = NULL;
ASSERT_EQ(0, frameProducer_->frames.size());
frameConsumer_->SetSourceSize(kFrameSize, kDpi);
ASSERT_EQ(1, frameProducer_->frames.size());
// Return the frame, and ensure we get it back
frame = frameProducer_->frames.front();
frameProducer_->frames.pop();
ASSERT_EQ(0, frameProducer_->frames.size());
frameConsumer_->ApplyBuffer(
kFrameSize, FrameRect(), frame, FrameRegion(), FrameRegion());
ASSERT_EQ(1, frameProducer_->frames.size());
ASSERT_TRUE(frame == frameProducer_->frames.front());
ASSERT_TRUE(frame->data() == frameProducer_->frames.front()->data());
// Change the SourceSize, we should get a new frame, but when the old frame is
// submitted we will not get it back.
frameConsumer_->SetSourceSize(webrtc::DesktopSize(1, 1), kDpi);
ASSERT_EQ(2, frameProducer_->frames.size());
frame = frameProducer_->frames.front();
frameProducer_->frames.pop();
ASSERT_EQ(1, frameProducer_->frames.size());
frameConsumer_->ApplyBuffer(
kFrameSize, FrameRect(), frame, FrameRegion(), FrameRegion());
ASSERT_EQ(1, frameProducer_->frames.size());
}
TEST_F(FrameConsumerBridgeTest, SetSourceSize) {
frameConsumer_->SetSourceSize(webrtc::DesktopSize(0, 0),
webrtc::DesktopVector(0, 0));
ASSERT_TRUE(frameProducer_->viewSize.equals(webrtc::DesktopSize(0, 0)));
ASSERT_TRUE(frameProducer_->clipArea.equals(
webrtc::DesktopRect::MakeLTRB(0, 0, 0, 0)));
ASSERT_EQ(1, frameProducer_->frames.size());
ASSERT_TRUE(
frameProducer_->frames.front()->size().equals(webrtc::DesktopSize(0, 0)));
frameConsumer_->SetSourceSize(kFrameSize, kDpi);
ASSERT_TRUE(frameProducer_->viewSize.equals(kFrameSize));
ASSERT_TRUE(frameProducer_->clipArea.equals(FrameRect()));
ASSERT_EQ(2, frameProducer_->frames.size());
frameProducer_->frames.pop();
ASSERT_TRUE(frameProducer_->frames.front()->size().equals(kFrameSize));
}
} // namespace remoting
\ No newline at end of file
// Copyright 2014 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 REMOTING_IOS_BRIDGE_CLIENT_PROXY_H_
#define REMOTING_IOS_BRIDGE_CLIENT_PROXY_H_
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#import "remoting/ios/bridge/client_proxy_delegate_wrapper.h"
namespace remoting {
class ClientInstance;
class ClientProxy;
} // namespace remoting
// HostProxy is one channel of a bridge from the UI Application (CLIENT) and the
// common Chromoting protocol (HOST). HostProxy proxies message from the UI
// application to the host. The reverse channel, ClientProxy, is owned by the
// HostProxy to control deconstruction order, but is shared with the
// ClientInstance to perform work.
@interface HostProxy : NSObject {
@private
// Host to Client channel
scoped_ptr<remoting::ClientProxy> _hostToClientChannel;
// Client to Host channel, must be released before |_hostToClientChannel|
scoped_refptr<remoting::ClientInstance> _clientToHostChannel;
// Connection state
BOOL _isConnected;
}
// TRUE when a connection has been established successfully.
- (BOOL)isConnected;
// Forwards credentials from CLIENT and to HOST and begins establishing a
// connection.
- (void)connectToHost:(NSString*)username
authToken:(NSString*)token
jabberId:(NSString*)jid
hostId:(NSString*)hostId
publicKey:(NSString*)hostPublicKey
delegate:(id<ClientProxyDelegate>)delegate;
// Report from CLIENT with the user's PIN.
- (void)authenticationResponse:(NSString*)pin createPair:(BOOL)createPair;
// CLIENT initiated disconnection
- (void)disconnectFromHost;
// Report from CLIENT of mouse input
- (void)mouseAction:(const webrtc::DesktopVector&)position
wheelDelta:(const webrtc::DesktopVector&)wheelDelta
whichButton:(NSInteger)buttonPressed
buttonDown:(BOOL)buttonIsDown;
// Report from CLIENT of keyboard input
- (void)keyboardAction:(NSInteger)keyCode keyDown:(BOOL)keyIsDown;
@end
#endif // REMOTING_IOS_BRIDGE_CLIENT_PROXY_H_
// Copyright 2014 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 "remoting/ios/bridge/host_proxy.h"
#import "remoting/ios/data_store.h"
#import "remoting/ios/host_preferences.h"
#import "remoting/ios/bridge/client_instance.h"
#import "remoting/ios/bridge/client_proxy.h"
@implementation HostProxy
// Override default constructor and initialize internals
- (id)init {
self = [super init];
if (self) {
_isConnected = false;
}
return self;
}
// Override default destructor
- (void)dealloc {
if (_isConnected) {
[self disconnectFromHost];
}
[super dealloc];
}
- (BOOL)isConnected {
return _isConnected;
}
- (void)connectToHost:(NSString*)username
authToken:(NSString*)token
jabberId:(NSString*)jid
hostId:(NSString*)hostId
publicKey:(NSString*)hostPublicKey
delegate:(id<ClientProxyDelegate>)delegate {
// Implicitly, if currently connected, discard the connection and begin a new
// connection.
[self disconnectFromHost];
NSString* pairId = @"";
NSString* pairSecret = @"";
const HostPreferences* hostPrefs =
[[DataStore sharedStore] getHostForId:hostId];
// Use the pairing id and secret when known
if (hostPrefs && hostPrefs.pairId && hostPrefs.pairSecret) {
pairId = [hostPrefs.pairId copy];
pairSecret = [hostPrefs.pairSecret copy];
}
_hostToClientChannel.reset(new remoting::ClientProxy(
[ClientProxyDelegateWrapper wrapDelegate:delegate]));
DCHECK(!_clientToHostChannel);
_clientToHostChannel =
new remoting::ClientInstance(_hostToClientChannel->AsWeakPtr(),
[username UTF8String],
[token UTF8String],
[jid UTF8String],
[hostId UTF8String],
[hostPublicKey UTF8String],
[pairId UTF8String],
[pairSecret UTF8String]);
_clientToHostChannel->Start();
_isConnected = YES;
}
- (void)authenticationResponse:(NSString*)pin createPair:(BOOL)createPair {
if (_isConnected) {
_clientToHostChannel->ProvideSecret([pin UTF8String], createPair);
}
}
- (void)disconnectFromHost {
if (_isConnected) {
VLOG(1) << "Disconnecting from Host";
// |_clientToHostChannel| must be closed before releasing
// |_hostToClientChannel|
// |_clientToHostChannel| owns several objects that have references to
// itself. These objects need to be cleaned up before we can release
// |_clientToHostChannel|.
_clientToHostChannel->Cleanup();
// All other references to |_clientToHostChannel| should now be free. When
// the next statement is executed the destructor is called automatically.
_clientToHostChannel = NULL;
_hostToClientChannel.reset();
_isConnected = NO;
}
}
- (void)mouseAction:(const webrtc::DesktopVector&)position
wheelDelta:(const webrtc::DesktopVector&)wheelDelta
whichButton:(NSInteger)buttonPressed
buttonDown:(BOOL)buttonIsDown {
if (_isConnected) {
_clientToHostChannel->PerformMouseAction(
position, wheelDelta, buttonPressed, buttonIsDown);
}
}
- (void)keyboardAction:(NSInteger)keyCode keyDown:(BOOL)keyIsDown {
if (_isConnected) {
_clientToHostChannel->PerformKeyboardAction(keyCode, keyIsDown);
}
}
@end
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/bridge/host_proxy.h"
#import "base/compiler_specific.h"
#import "testing/gtest_mac.h"
namespace remoting {
class HostProxyTest : public ::testing::Test {
protected:
virtual void SetUp() OVERRIDE { hostProxy_ = [[HostProxy alloc] init]; }
void CallPassThroughFunctions() {
[hostProxy_ mouseAction:webrtc::DesktopVector(0, 0)
wheelDelta:webrtc::DesktopVector(0, 0)
whichButton:0
buttonDown:NO];
[hostProxy_ keyboardAction:0 keyDown:NO];
}
HostProxy* hostProxy_;
};
TEST_F(HostProxyTest, ConnectDisconnect) {
CallPassThroughFunctions();
ASSERT_FALSE([hostProxy_ isConnected]);
[hostProxy_ connectToHost:@""
authToken:@""
jabberId:@""
hostId:@""
publicKey:@""
delegate:nil];
ASSERT_TRUE([hostProxy_ isConnected]);
CallPassThroughFunctions();
[hostProxy_ disconnectFromHost];
ASSERT_FALSE([hostProxy_ isConnected]);
CallPassThroughFunctions();
}
} // namespace remoting
\ No newline at end of file
// Copyright 2014 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 REMOTING_IOS_DATA_STORE_H_
#define REMOTING_IOS_DATA_STORE_H_
#import <CoreData/CoreData.h>
#import "remoting/ios/host_preferences.h"
// A local data store backed by SQLLite to hold instances of HostPreferences.
// HostPreference is defined by the Core Data Model templates see
// ChromotingModel.xcdatamodel
@interface DataStore : NSObject
// Static pointer to the managed data store
+ (DataStore*)sharedStore;
// General methods
- (BOOL)saveChanges;
// Access methods for Hosts
- (NSArray*)allHosts;
- (const HostPreferences*)createHost:(NSString*)hostId;
- (void)removeHost:(const HostPreferences*)p;
- (const HostPreferences*)getHostForId:(NSString*)hostId;
@end
#endif // REMOTING_IOS_DATA_STORE_H_
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/data_store.h"
@interface DataStore (Private)
- (NSString*)itemArchivePath;
@end
@implementation DataStore {
@private
NSMutableArray* _allHosts;
NSManagedObjectContext* _context;
NSManagedObjectModel* _model;
}
// Create or Get a static data store
+ (DataStore*)sharedStore {
static DataStore* sharedStore = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{ sharedStore = [[super allocWithZone:nil] init]; });
return sharedStore;
}
// General methods
+ (id)allocWithZone:(NSZone*)zone {
return [self sharedStore];
}
// Load data store from SQLLite backing store
- (id)init {
self = [super init];
if (self) {
// Read in ChromotingModel.xdatamodeld
_model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator* psc = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:_model];
NSString* path = [self itemArchivePath];
NSURL* storeUrl = [NSURL fileURLWithPath:path];
NSError* error = nil;
NSDictionary* tryOptions = @{
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES
};
NSDictionary* makeOptions =
@{NSMigratePersistentStoresAutomaticallyOption : @YES};
if (![psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeUrl
options:tryOptions
error:&error]) {
// An incompatible version of the store exists, delete it and start over
[[NSFileManager defaultManager] removeItemAtURL:storeUrl error:nil];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeUrl
options:makeOptions
error:&error];
[NSException raise:@"Open failed"
format:@"Reason: %@", [error localizedDescription]];
}
// Create the managed object context
_context = [[NSManagedObjectContext alloc] init];
[_context setPersistentStoreCoordinator:psc];
// The managed object context can manage undo, but we don't need it
[_context setUndoManager:nil];
_allHosts = nil;
}
return self;
}
// Committing to backing store
- (BOOL)saveChanges {
NSError* err = nil;
BOOL successful = [_context save:&err];
return successful;
}
// Looking up the backing store path
- (NSString*)itemArchivePath {
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
// Get one and only document directory from that list
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:@"store.data"];
}
// Return an array of all known hosts, if the list hasn't been loaded yet, then
// load it now
- (NSArray*)allHosts {
if (!_allHosts) {
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* e =
[[_model entitiesByName] objectForKey:@"HostPreferences"];
[request setEntity:e];
NSError* error;
NSArray* result = [_context executeFetchRequest:request error:&error];
if (!result) {
[NSException raise:@"Fetch failed"
format:@"Reason: %@", [error localizedDescription]];
}
_allHosts = [result mutableCopy];
}
return _allHosts;
}
// Return a HostPreferences if it already exists, otherwise create a new
// HostPreferences to use
- (const HostPreferences*)createHost:(NSString*)hostId {
const HostPreferences* p = [self getHostForId:hostId];
if (p == nil) {
p = [NSEntityDescription insertNewObjectForEntityForName:@"HostPreferences"
inManagedObjectContext:_context];
p.hostId = hostId;
[_allHosts addObject:p];
}
return p;
}
- (void)removeHost:(HostPreferences*)p {
[_context deleteObject:p];
[_allHosts removeObjectIdenticalTo:p];
}
// Search the store for any matching HostPreferences
// return the 1st match or nil
- (const HostPreferences*)getHostForId:(NSString*)hostId {
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* e =
[[_model entitiesByName] objectForKey:@"HostPreferences"];
[request setEntity:e];
NSPredicate* predicate =
[NSPredicate predicateWithFormat:@"(hostId = %@)", hostId];
[request setPredicate:predicate];
NSError* error;
NSArray* result = [_context executeFetchRequest:request error:&error];
if (!result) {
[NSException raise:@"Fetch failed"
format:@"Reason: %@", [error localizedDescription]];
}
for (HostPreferences* curHost in result) {
return curHost;
}
return nil;
}
@end
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/data_store.h"
#import "base/compiler_specific.h"
#import "testing/gtest_mac.h"
namespace remoting {
namespace {
NSString* kHostId = @"testHost";
NSString* kHostPin = @"testHostPin";
NSString* kPairId = @"testPairId";
NSString* kPairSecret = @"testPairSecret";
} // namespace
class DataStoreTest : public ::testing::Test {
protected:
virtual void SetUp() OVERRIDE {
store_ = [[DataStore allocWithZone:nil] init];
RemoveAllHosts();
EXPECT_EQ(0, HostCount());
}
virtual void TearDown() OVERRIDE { RemoveAllHosts(); }
int HostCount() { return [[store_ allHosts] count]; }
void RemoveAllHosts() {
while (HostCount() > 0) {
[store_ removeHost:[store_ allHosts].firstObject];
}
[store_ saveChanges];
}
DataStore* store_;
};
TEST(DataStoreTest_Static, IsSingleInstance) {
DataStore* firstStore = [DataStore sharedStore];
ASSERT_NSEQ(firstStore, [DataStore sharedStore]);
}
TEST(DataStoreTest_Static, RemoveAllHost) {
// Test this functionality independently before expecting the fixture to do
// this correctly during cleanup
DataStore* store = [DataStore sharedStore];
while ([[store allHosts] count]) {
[store removeHost:[store allHosts].firstObject];
}
ASSERT_EQ(0, [[store allHosts] count]);
store = nil;
}
TEST_F(DataStoreTest, CreateHost) {
const HostPreferences* host = [store_ createHost:kHostId];
ASSERT_STREQ([kHostId UTF8String], [host.hostId UTF8String]);
ASSERT_EQ(1, HostCount());
}
TEST_F(DataStoreTest, GetHostForId) {
const HostPreferences* host = [store_ getHostForId:kHostId];
ASSERT_TRUE(host == nil);
[store_ createHost:kHostId];
host = [store_ getHostForId:kHostId];
ASSERT_TRUE(host != nil);
ASSERT_STREQ([kHostId UTF8String], [host.hostId UTF8String]);
}
TEST_F(DataStoreTest, SaveChanges) {
const HostPreferences* newHost = [store_ createHost:kHostId];
ASSERT_EQ(1, HostCount());
// Default values for a new host
ASSERT_TRUE([newHost.askForPin boolValue] == NO);
ASSERT_TRUE(newHost.hostPin == nil);
ASSERT_TRUE(newHost.pairId == nil);
ASSERT_TRUE(newHost.pairSecret == nil);
// Set new values and save
newHost.askForPin = [NSNumber numberWithBool:YES];
newHost.hostPin = kHostPin;
newHost.pairId = kPairId;
newHost.pairSecret = kPairSecret;
[store_ saveChanges];
// The next time the store is loaded the host will still be present, even
// though we are about to release and reinit a new object
store_ = nil;
store_ = [[DataStore allocWithZone:nil] init];
ASSERT_EQ(1, HostCount());
const HostPreferences* host = [store_ getHostForId:kHostId];
ASSERT_TRUE(host != nil);
ASSERT_STREQ([kHostId UTF8String], [host.hostId UTF8String]);
ASSERT_TRUE([host.askForPin boolValue] == YES);
ASSERT_STREQ([kHostPin UTF8String], [host.hostPin UTF8String]);
ASSERT_STREQ([kPairId UTF8String], [host.pairId UTF8String]);
ASSERT_STREQ([kPairSecret UTF8String], [host.pairSecret UTF8String]);
}
} // namespace remoting
// Copyright 2014 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 REMOTING_IOS_HOST_H_
#define REMOTING_IOS_HOST_H_
#import <Foundation/Foundation.h>
// A detail record for a Chromoting Host
@interface Host : NSObject
// Various properties of the Chromoting Host
@property(nonatomic, copy) NSString* createdTime;
@property(nonatomic, copy) NSString* hostId;
@property(nonatomic, copy) NSString* hostName;
@property(nonatomic, copy) NSString* hostVersion;
@property(nonatomic, copy) NSString* jabberId;
@property(nonatomic, copy) NSString* kind;
@property(nonatomic, copy) NSString* publicKey;
@property(nonatomic, copy) NSString* status;
@property(nonatomic, copy) NSString* updatedTime;
+ (NSMutableArray*)parseListFromJSON:(NSMutableData*)data;
@end
#endif // REMOTING_IOS_HOST_H_
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/host.h"
@implementation Host
@synthesize createdTime = _createdTime;
@synthesize hostId = _hostId;
@synthesize hostName = _hostName;
@synthesize hostVersion = _hostVersion;
@synthesize jabberId = _jabberId;
@synthesize kind = _kind;
@synthesize publicKey = _publicKey;
@synthesize status = _status;
@synthesize updatedTime = _updatedTime;
// Parse jsonData into Host list
+ (NSMutableArray*)parseListFromJSON:(NSMutableData*)data {
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSDictionary* dataDict = [json objectForKey:@"data"];
NSArray* availableServers = [dataDict objectForKey:@"items"];
NSMutableArray* serverList = [[NSMutableArray alloc] init];
NSUInteger idx = 0;
NSDictionary* svr;
NSUInteger count = [availableServers count];
while (idx < count) {
svr = [availableServers objectAtIndex:idx++];
Host* host = [[Host alloc] init];
host.createdTime = [svr objectForKey:@"createdTime"];
host.hostId = [svr objectForKey:@"hostId"];
host.hostName = [svr objectForKey:@"hostName"];
host.hostVersion = [svr objectForKey:@"hostVersion"];
host.jabberId = [svr objectForKey:@"jabberId"];
host.kind = [svr objectForKey:@"kind"];
host.publicKey = [svr objectForKey:@"publicKey"];
host.status = [svr objectForKey:@"status"];
host.updatedTime = [svr objectForKey:@"updatedTime"];
[serverList addObject:host];
}
return serverList;
}
@end
// Copyright 2014 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 REMOTING_IOS_HOST_CELL_H_
#define REMOTING_IOS_HOST_CELL_H_
#import <UIKit/UIKit.h>
// HostCell represents a Host as a row in a tableView, where the row
// contains a single cell. Several button and outlet are reserved here for
// future functionality
@interface HostCell : UITableViewCell
@property(weak, nonatomic) IBOutlet UILabel* labelHostName;
@property(weak, nonatomic) IBOutlet UILabel* labelStatus;
@end
#endif // REMOTING_IOS_HOST_CELL_H_
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/host_cell.h"
@implementation HostCell
// Override UITableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString*)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
return self;
}
@end
// Copyright 2014 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 REMOTING_IOS_HOST_PREFERENCES_H_
#define REMOTING_IOS_HOST_PREFERENCES_H_
#import <CoreData/CoreData.h>
// A HostPreferences contains details to negotiate and maintain a connection
// to a remote Chromoting host. This is a entity in a backing store. The
// implementation file is ChromotingModel.xcdatamodeld. If this file is
// updated, also update the model. The model MUST be properly versioned to
// ensure backwards compatibility.
// https://developer.apple.com/library/ios/recipes/xcode_help-core_data_modeling_tool/Articles/creating_new_version.html
// Or the app must be uninstalled, and reinstalled which will erase the previous
// version of the backing store.
@interface HostPreferences : NSManagedObject
// Is a prompt is needed to reconnect or continue the connection to
// the host
@property(nonatomic, copy) NSNumber* askForPin;
// Several properties are populated from the jabber jump server
@property(nonatomic, copy) NSString* hostId;
// Supplied by client via UI interaction
@property(nonatomic, copy) NSString* hostPin;
@property(nonatomic, copy) NSString* pairId;
@property(nonatomic, copy) NSString* pairSecret;
@end
#endif // REMOTING_IOS_HOST_PREFERENCES_H_
\ No newline at end of file
// Copyright 2014 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 REMOTING_IOS_HOST_REFRESH_H_
#define REMOTING_IOS_HOST_REFRESH_H_
#import <Foundation/Foundation.h>
#import "GTMOAuth2Authentication.h"
// HostRefresh encapsulates a fetch of the Chromoting host list from
// the jabber service.
// Contract to handle the host list result of a Chromoting host list fetch.
@protocol HostRefreshDelegate<NSObject>
- (void)hostListRefresh:(NSArray*)hostList errorMessage:(NSString*)errorMessage;
@end
// Fetches the host list from the jabber service async. Authenticates,
// and parses the results to provide to a HostListViewController
@interface HostRefresh : NSObject<NSURLConnectionDataDelegate>
// Store data read while the connection is active, and can be used after the
// connection has been discarded
@property(nonatomic, copy) NSMutableData* jsonData;
@property(nonatomic, copy) NSString* errorMessage;
@property(nonatomic, assign) id<HostRefreshDelegate> delegate;
- (void)refreshHostList:(GTMOAuth2Authentication*)authReq
delegate:(id<HostRefreshDelegate>)delegate;
@end
#endif // REMOTING_IOS_HOST_REFRESH_H_
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/host_refresh.h"
#import "remoting/ios/authorize.h"
#import "remoting/ios/host.h"
#import "remoting/ios/utility.h"
namespace {
NSString* kDefaultErrorMessage = @"The Host list refresh is not available at "
@"this time. Please try again later.";
} // namespace
@interface HostRefresh (Private)
- (void)authentication:(GTMOAuth2Authentication*)auth
request:(NSMutableURLRequest*)request
error:(NSError*)error;
- (void)formatErrorMessage:(NSString*)error;
- (void)notifyDelegate;
@end
// Logic flow begins with refreshHostList, and continues until an error occurs,
// or the host list is returned to the delegate
@implementation HostRefresh
@synthesize jsonData = _jsonData;
@synthesize errorMessage = _errorMessage;
@synthesize delegate = _delegate;
// Override default constructor and initialize internals
- (id)init {
self = [super init];
if (self) {
_jsonData = [[NSMutableData alloc] init];
}
return self;
}
// Begin the authentication and authorization process. Begin the process by
// creating an oAuth2 request to google api's including the needed scopes to
// fetch the users host list.
- (void)refreshHostList:(GTMOAuth2Authentication*)authReq
delegate:(id<HostRefreshDelegate>)delegate {
CHECK(_delegate == nil); // Do not reuse an instance of this class
_delegate = delegate;
[Authorize beginRequest:authReq
delegate:self
didFinishSelector:@selector(authentication:request:error:)];
}
// Handle completion of the authorization process. Append service credentials
// for jabber. If an error occurred, notify user.
- (void)authentication:(NSObject*)auth
request:(NSMutableURLRequest*)request
error:(NSError*)error {
if (error != nil) {
[self formatErrorMessage:error.localizedDescription];
} else {
// Add credentials for service
[Authorize appendCredentials:request];
// Begin connection, the returned reference is not useful right now and
// marked as __unused
__unused NSURLConnection* connection =
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
}
// @protocol NSURLConnectionDelegate, handle any error during connection
- (void)connection:(NSURLConnection*)connection
didFailWithError:(NSError*)error {
[self formatErrorMessage:[error localizedDescription]];
[self notifyDelegate];
}
// @protocol NSURLConnectionDataDelegate, may be called async multiple times.
// Each call appends the new data to the known data until completed.
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
[_jsonData appendData:data];
}
// @protocol NSURLConnectionDataDelegate
// Ensure connection succeeded: HTTP 200 OK
- (void)connection:(NSURLConnection*)connection
didReceiveResponse:(NSURLResponse*)response {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
if ([response respondsToSelector:@selector(allHeaderFields)]) {
NSNumber* responseCode =
[[NSNumber alloc] initWithInteger:[httpResponse statusCode]];
if (responseCode.intValue != 200) {
[self formatErrorMessage:[NSString
stringWithFormat:@"HTTP STATUS CODE: %d",
[httpResponse statusCode]]];
}
}
}
// @protocol NSURLConnectionDataDelegate handle a completed connection, parse
// received data, and return host list to delegate
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
[self notifyDelegate];
}
// Store a formatted error message to return later
- (void)formatErrorMessage:(NSString*)error {
_errorMessage = kDefaultErrorMessage;
if (error != nil && error.length > 0) {
_errorMessage = [_errorMessage
stringByAppendingString:[@" " stringByAppendingString:error]];
}
}
// The connection has finished, call to delegate
- (void)notifyDelegate {
if (_jsonData.length == 0 && _errorMessage == nil) {
[self formatErrorMessage:nil];
}
[_delegate hostListRefresh:[Host parseListFromJSON:_jsonData]
errorMessage:_errorMessage];
}
@end
// Copyright 2014 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 REMOTING_IOS_HOST_REFRESH_TEST_HELPER_H_
#define REMOTING_IOS_HOST_REFRESH_TEST_HELPER_H_
#import <Foundation/Foundation.h>
namespace remoting {
class HostRefreshTestHelper {
public:
constexpr static NSString* CloseTag = @"\",";
constexpr static NSString* CreatedTimeTag = @"\"createdTime\":\"";
constexpr static NSString* HostIdTag = @"\"hostId\":\"";
constexpr static NSString* HostNameTag = @"\"hostName\":\"";
constexpr static NSString* HostVersionTag = @"\"hostVersion\":\"";
constexpr static NSString* KindTag = @"\"kind\":\"";
constexpr static NSString* JabberIdTag = @"\"jabberId\":\"";
constexpr static NSString* PublicKeyTag = @"\"publicKey\":\"";
constexpr static NSString* StatusTag = @"\"status\":\"";
constexpr static NSString* UpdatedTimeTag = @"\"updatedTime\":\"";
constexpr static NSString* CreatedTimeTest = @"2000-01-01T00:00:01.000Z";
constexpr static NSString* HostIdTest = @"Host1";
constexpr static NSString* HostNameTest = @"HostName1";
constexpr static NSString* HostVersionTest = @"2.22.5.4";
constexpr static NSString* KindTest = @"chromoting#host";
constexpr static NSString* JabberIdTest = @"JabberingOn";
constexpr static NSString* PublicKeyTest = @"AAAAABBBBBZZZZZ";
constexpr static NSString* StatusTest = @"TESTING";
constexpr static NSString* UpdatedTimeTest = @"2004-01-01T00:00:01.000Z";
static NSMutableData* GetHostList(int numHosts) {
return [NSMutableData
dataWithData:[GetMultipleHosts(numHosts)
dataUsingEncoding:NSUTF8StringEncoding]];
}
static NSMutableData* GetHostList(NSString* hostList) {
return [NSMutableData
dataWithData:[hostList dataUsingEncoding:NSUTF8StringEncoding]];
}
static NSString* GetMultipleHosts(int numHosts) {
NSString* client = [NSString
stringWithFormat:
@"%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@",
@"{",
CreatedTimeTag,
CreatedTimeTest,
CloseTag,
HostIdTag,
HostIdTest,
CloseTag,
HostNameTag,
HostNameTest,
CloseTag,
HostNameTag,
HostNameTest,
CloseTag,
HostVersionTag,
HostVersionTest,
CloseTag,
KindTag,
KindTest,
CloseTag,
JabberIdTag,
JabberIdTest,
CloseTag,
PublicKeyTag,
PublicKeyTest,
CloseTag,
StatusTag,
StatusTest,
CloseTag,
UpdatedTimeTag,
UpdatedTimeTest,
@"\"}"];
NSMutableString* hostList = [NSMutableString
stringWithString:
@"{\"data\":{\"kind\":\"chromoting#hostList\",\"items\":["];
for (int i = 0; i < numHosts; i++) {
[hostList appendString:client];
if (i < numHosts - 1) {
[hostList appendString:@","]; // common separated
}
}
[hostList appendString:@"]}}"];
return [hostList copy];
}
};
} // namespace remoting
#endif // REMOTING_IOS_HOST_REFRESH_TEST_HELPER_H_
\ No newline at end of file
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/host_refresh.h"
#import "base/compiler_specific.h"
#import "testing/gtest_mac.h"
#import "remoting/ios/host.h"
#import "remoting/ios/host_refresh_test_helper.h"
@interface HostRefreshDelegateTester : NSObject<HostRefreshDelegate>
@property(nonatomic) NSArray* hostList;
@property(nonatomic) NSString* errorMessage;
@end
@implementation HostRefreshDelegateTester
@synthesize hostList = _hostList;
@synthesize errorMessage = _errorMessage;
- (void)hostListRefresh:(NSArray*)hostList
errorMessage:(NSString*)errorMessage {
_hostList = hostList;
_errorMessage = errorMessage;
}
- (bool)receivedHosts {
return (_hostList.count > 0);
}
- (bool)receivedErrorMessage {
return (_errorMessage != nil);
}
@end
namespace remoting {
namespace {
NSString* kErrorMessageTest = @"TestErrorMessage";
} // namespace
class HostRefreshTest : public ::testing::Test {
protected:
virtual void SetUp() OVERRIDE {
hostRefreshProcessor_ = [[HostRefresh allocWithZone:nil] init];
delegateTester_ = [[HostRefreshDelegateTester alloc] init];
[hostRefreshProcessor_ setDelegate:delegateTester_];
}
void CreateHostList(int numHosts) {
[hostRefreshProcessor_
setJsonData:HostRefreshTestHelper::GetHostList(numHosts)];
}
NSError* CreateErrorFromString(NSString* message) {
NSDictionary* errorDictionary = nil;
if (message != nil) {
errorDictionary = @{NSLocalizedDescriptionKey : message};
}
return [[NSError alloc] initWithDomain:@"HostRefreshTest"
code:EPERM
userInfo:errorDictionary];
}
HostRefresh* hostRefreshProcessor_;
HostRefreshDelegateTester* delegateTester_;
};
TEST_F(HostRefreshTest, ErrorFormatter) {
[hostRefreshProcessor_ connection:nil
didFailWithError:CreateErrorFromString(nil)];
ASSERT_FALSE(hostRefreshProcessor_.errorMessage == nil);
[hostRefreshProcessor_ connection:nil
didFailWithError:CreateErrorFromString(@"")];
ASSERT_FALSE(hostRefreshProcessor_.errorMessage == nil);
[hostRefreshProcessor_ connection:nil
didFailWithError:CreateErrorFromString(kErrorMessageTest)];
ASSERT_TRUE([hostRefreshProcessor_.errorMessage
rangeOfString:kErrorMessageTest].location != NSNotFound);
}
TEST_F(HostRefreshTest, JSONParsing) {
// There were no hosts returned
CreateHostList(0);
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_TRUE(delegateTester_.hostList.count == 0);
CreateHostList(1);
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_TRUE(delegateTester_.hostList.count == 1);
Host* host = static_cast<Host*>([delegateTester_.hostList objectAtIndex:0]);
ASSERT_NSEQ(HostRefreshTestHelper::CreatedTimeTest, host.createdTime);
ASSERT_NSEQ(HostRefreshTestHelper::HostIdTest, host.hostId);
ASSERT_NSEQ(HostRefreshTestHelper::HostNameTest, host.hostName);
ASSERT_NSEQ(HostRefreshTestHelper::HostVersionTest, host.hostVersion);
ASSERT_NSEQ(HostRefreshTestHelper::KindTest, host.kind);
ASSERT_NSEQ(HostRefreshTestHelper::JabberIdTest, host.jabberId);
ASSERT_NSEQ(HostRefreshTestHelper::PublicKeyTest, host.publicKey);
ASSERT_NSEQ(HostRefreshTestHelper::StatusTest, host.status);
ASSERT_NSEQ(HostRefreshTestHelper::UpdatedTimeTest, host.updatedTime);
CreateHostList(11);
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_TRUE(delegateTester_.hostList.count == 11);
// An error in parsing returns no hosts
[hostRefreshProcessor_
setJsonData:
[NSMutableData
dataWithData:
[@"{\"dataaaaaafa\":{\"kiffffnd\":\"chromoting#hostList\"}}"
dataUsingEncoding:NSUTF8StringEncoding]]];
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_TRUE(delegateTester_.hostList.count == 0);
}
TEST_F(HostRefreshTest, HostListDelegateNoList) {
// Hosts were not processed at all
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_FALSE([delegateTester_ receivedHosts]);
ASSERT_TRUE([delegateTester_ receivedErrorMessage]);
// There were no hosts returned
CreateHostList(0);
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_FALSE([delegateTester_ receivedHosts]);
ASSERT_TRUE([delegateTester_ receivedErrorMessage]);
}
TEST_F(HostRefreshTest, HostListDelegateHasList) {
CreateHostList(1);
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_TRUE([delegateTester_ receivedHosts]);
ASSERT_FALSE([delegateTester_ receivedErrorMessage]);
}
TEST_F(HostRefreshTest, HostListDelegateHasListWithError) {
CreateHostList(1);
[hostRefreshProcessor_ connection:nil
didFailWithError:CreateErrorFromString(kErrorMessageTest)];
[hostRefreshProcessor_ connectionDidFinishLoading:nil];
ASSERT_TRUE([delegateTester_ receivedHosts]);
ASSERT_TRUE([delegateTester_ receivedErrorMessage]);
}
TEST_F(HostRefreshTest, ConnectionFailed) {
[hostRefreshProcessor_ connection:nil didFailWithError:nil];
ASSERT_FALSE([delegateTester_ receivedHosts]);
ASSERT_TRUE([delegateTester_ receivedErrorMessage]);
}
} // namespace remoting
\ No newline at end of file
// Copyright 2014 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 REMOTING_IOS_KEY_INPUT_H_
#define REMOTING_IOS_KEY_INPUT_H_
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
// Key codes are translated from the on screen keyboard to the scan codes
// needed for Chromoting input. We don't have a good automated approach to do
// this. Instead we have created a mapping manually via trial and error. To
// support other keyboards in this context we would have to test and create a
// mapping for each keyboard manually.
// Contract to handle translated key presses from the on-screen keyboard to
// the format required for Chromoting keyboard input
@protocol KeyInputDelegate<NSObject>
- (void)keyboardActionKeyCode:(uint32_t)keyPressed isKeyDown:(BOOL)keyDown;
- (void)keyboardDismissed;
@end
@interface KeyInput : UIView<UIKeyInput>
@property(weak, nonatomic) id<KeyInputDelegate> delegate;
- (void)ctrlAltDel;
@end
#endif // REMOTING_IOS_KEY_INPUT_H_
\ No newline at end of file
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/key_input.h"
#import "remoting/ios/key_map_us.h"
@interface KeyInput (Private)
- (void)transmitAppropriateKeyCode:(NSString*)text;
- (void)transmitKeyCode:(NSInteger)keyCode needShift:(bool)needShift;
@end
@implementation KeyInput
@synthesize delegate = _delegate;
// Override UIKeyInput::UITextInputTraits property
- (UIKeyboardType)keyboardType {
return UIKeyboardTypeAlphabet;
}
// Override UIView::UIResponder, when this interface is the first responder
// on-screen keyboard input will create events for Chromoting keyboard input
- (BOOL)canBecomeFirstResponder {
return YES;
}
// Override UIView::UIResponder
// Keyboard was dismissed
- (BOOL)resignFirstResponder {
BOOL wasFirstResponder = self.isFirstResponder;
BOOL didResignFirstReponder =
[super resignFirstResponder]; // I'm not sure that this returns YES when
// first responder was resigned, but for
// now I don't actually need to know what
// the return from super means.
if (wasFirstResponder) {
[_delegate keyboardDismissed];
}
return didResignFirstReponder;
}
// @protocol UIKeyInput, Send backspace
- (void)deleteBackward {
[self transmitKeyCode:kKeyCodeUS[kBackspaceIndex] needShift:false];
}
// @protocol UIKeyInput, Assume this is a text input
- (BOOL)hasText {
return YES;
}
// @protocol UIKeyInput, Translate inserted text to key presses, one char at a
// time
- (void)insertText:(NSString*)text {
[self transmitAppropriateKeyCode:text];
}
- (void)ctrlAltDel {
if (_delegate) {
[_delegate keyboardActionKeyCode:kKeyCodeUS[kCtrlIndex] isKeyDown:YES];
[_delegate keyboardActionKeyCode:kKeyCodeUS[kAltIndex] isKeyDown:YES];
[_delegate keyboardActionKeyCode:kKeyCodeUS[kDelIndex] isKeyDown:YES];
[_delegate keyboardActionKeyCode:kKeyCodeUS[kDelIndex] isKeyDown:NO];
[_delegate keyboardActionKeyCode:kKeyCodeUS[kAltIndex] isKeyDown:NO];
[_delegate keyboardActionKeyCode:kKeyCodeUS[kCtrlIndex] isKeyDown:NO];
}
}
// When inserting multiple characters, process them one at a time. |text| is as
// it was output on the device. The shift key is not naturally presented in the
// input stream, and must be inserted by inspecting each char and considering
// that if the key was input on a traditional keyboard that the character would
// have required a shift. Assume caps lock does not exist.
- (void)transmitAppropriateKeyCode:(NSString*)text {
for (int i = 0; i < [text length]; ++i) {
NSInteger charToSend = [text characterAtIndex:i];
if (charToSend <= kKeyboardKeyMaxUS) {
[self transmitKeyCode:kKeyCodeUS[charToSend]
needShift:kIsShiftRequiredUS[charToSend]];
}
}
}
// |charToSend| is as it was output on the device. Some call this a
// 'key press'. For Chromoting this must be transferred as a key down (press
// down with a finger), followed by a key up (finger is removed from the
// keyboard)
//
// The delivery may be an upper case or special character. Chromoting is just
// interested in the button that was pushed, so to create an upper case
// character, first send a shift press, then the button, then release shift
- (void)transmitKeyCode:(NSInteger)keyCode needShift:(bool)needShift {
if (keyCode > 0 && _delegate) {
if (needShift) {
[_delegate keyboardActionKeyCode:kKeyCodeUS[kShiftIndex] isKeyDown:YES];
}
[_delegate keyboardActionKeyCode:keyCode isKeyDown:YES];
[_delegate keyboardActionKeyCode:keyCode isKeyDown:NO];
if (needShift) {
[_delegate keyboardActionKeyCode:kKeyCodeUS[kShiftIndex] isKeyDown:NO];
}
}
}
@end
// Copyright 2014 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/key_input.h"
#import "remoting/ios/key_map_us.h"
#include <vector>
#import "base/compiler_specific.h"
#import "testing/gtest_mac.h"
@interface KeyInputDelegateTester : NSObject<KeyInputDelegate> {
@private
std::vector<uint32_t> _keyList;
}
@property(nonatomic, assign) int numKeysDown;
@property(nonatomic, assign) BOOL wasDismissed;
- (std::vector<uint32_t>&)getKeyList;
@end
@implementation KeyInputDelegateTester
- (std::vector<uint32_t>&)getKeyList {
return _keyList;
}
- (void)keyboardDismissed {
// This can not be tested, because we can not set |keyInput_| as
// FirstResponder in this test harness
_wasDismissed = true;
}
- (void)keyboardActionKeyCode:(uint32_t)keyPressed isKeyDown:(BOOL)keyDown {
if (keyDown) {
_keyList.push_back(keyPressed);
_numKeysDown++;
} else {
_numKeysDown--;
}
}
@end
namespace remoting {
class KeyInputTest : public ::testing::Test {
protected:
virtual void SetUp() OVERRIDE {
keyInput_ = [[KeyInput allocWithZone:nil] init];
delegateTester_ = [[KeyInputDelegateTester alloc] init];
keyInput_.delegate = delegateTester_;
}
KeyInput* keyInput_;
KeyInputDelegateTester* delegateTester_;
};
TEST_F(KeyInputTest, SendKey) {
// Empty
[keyInput_ insertText:@""];
ASSERT_EQ(0, delegateTester_.numKeysDown);
ASSERT_EQ(0, [delegateTester_ getKeyList].size());
// Value is out of bounds
[keyInput_ insertText:@"ó"];
ASSERT_EQ(0, delegateTester_.numKeysDown);
ASSERT_EQ(0, [delegateTester_ getKeyList].size());
// Lower case
[keyInput_ insertText:@"a"];
ASSERT_EQ(0, delegateTester_.numKeysDown);
ASSERT_EQ(1, [delegateTester_ getKeyList].size());
ASSERT_EQ(kKeyCodeUS['a'], [delegateTester_ getKeyList][0]);
// Upper Case
[delegateTester_ getKeyList].clear();
[keyInput_ insertText:@"A"];
ASSERT_EQ(0, delegateTester_.numKeysDown);
ASSERT_EQ(2, [delegateTester_ getKeyList].size());
ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][0]);
ASSERT_EQ(kKeyCodeUS['A'], [delegateTester_ getKeyList][1]);
// Multiple characters and mixed case
[delegateTester_ getKeyList].clear();
[keyInput_ insertText:@"ABCabc"];
ASSERT_EQ(0, delegateTester_.numKeysDown);
ASSERT_EQ(9, [delegateTester_ getKeyList].size());
ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][0]);
ASSERT_EQ(kKeyCodeUS['A'], [delegateTester_ getKeyList][1]);
ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][2]);
ASSERT_EQ(kKeyCodeUS['B'], [delegateTester_ getKeyList][3]);
ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][4]);
ASSERT_EQ(kKeyCodeUS['C'], [delegateTester_ getKeyList][5]);
ASSERT_EQ(kKeyCodeUS['a'], [delegateTester_ getKeyList][6]);
ASSERT_EQ(kKeyCodeUS['b'], [delegateTester_ getKeyList][7]);
ASSERT_EQ(kKeyCodeUS['c'], [delegateTester_ getKeyList][8]);
}
TEST_F(KeyInputTest, CtrlAltDel) {
[keyInput_ ctrlAltDel];
ASSERT_EQ(0, delegateTester_.numKeysDown);
ASSERT_EQ(3, [delegateTester_ getKeyList].size());
ASSERT_EQ(kKeyCodeUS[kCtrlIndex], [delegateTester_ getKeyList][0]);
ASSERT_EQ(kKeyCodeUS[kAltIndex], [delegateTester_ getKeyList][1]);
ASSERT_EQ(kKeyCodeUS[kDelIndex], [delegateTester_ getKeyList][2]);
}
TEST_F(KeyInputTest, Backspace) {
[keyInput_ deleteBackward];
ASSERT_EQ(0, delegateTester_.numKeysDown);
ASSERT_EQ(1, [delegateTester_ getKeyList].size());
ASSERT_EQ(kKeyCodeUS[kBackspaceIndex], [delegateTester_ getKeyList][0]);
}
} // namespace remoting
\ No newline at end of file
This diff is collapsed.
// Copyright 2014 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 REMOTING_IOS_UI_CURSOR_TEXTURE_H_
#define REMOTING_IOS_UI_CURSOR_TEXTURE_H_
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
#import "base/memory/scoped_ptr.h"
#import "remoting/ios/utility.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
@interface CursorTexture : NSObject {
@private
// GL name
GLuint _textureId;
webrtc::DesktopSize _textureSize;
BOOL _needInitialize;
// The current cursor
scoped_ptr<webrtc::MouseCursor> _cursor;
BOOL _needCursorRedraw;
// Rectangle of the most recent cursor drawn to a GL Texture. On each
// successive frame when a new cursor is available this region is cleared on
// the GL Texture, so that the GL Texture is completely transparent again, and
// the cursor is then redrawn.
webrtc::DesktopRect _cursorDrawnToGL;
}
- (const webrtc::DesktopSize&)textureSize;
- (void)setTextureSize:(const webrtc::DesktopSize&)size;
- (const webrtc::MouseCursor&)cursor;
- (void)setCursor:(webrtc::MouseCursor*)cursor;
// bind this object to an effect's via the effects properties
- (void)bindToEffect:(GLKEffectPropertyTexture*)effectProperty;
// True if the cursor has changed in a way that requires it to be redrawn
- (BOOL)needDrawAtPosition:(const webrtc::DesktopVector&)position;
// needDrawAtPosition must be checked prior to calling drawWithMousePosition.
// Draw mouse at the new position.
- (void)drawWithMousePosition:(const webrtc::DesktopVector&)position;
- (void)releaseTexture;
@end
#endif // REMOTING_IOS_UI_CURSOR_TEXTURE_H_
\ No newline at end of file
This diff is collapsed.
// Copyright 2014 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 REMOTING_IOS_UI_DESKTOP_TEXTURE_H_
#define REMOTING_IOS_UI_DESKTOP_TEXTURE_H_
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
#import "remoting/ios/utility.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
@interface DesktopTexture : NSObject {
@private
// GL name
GLuint _textureId;
webrtc::DesktopSize _textureSize;
BOOL _needInitialize;
}
- (const webrtc::DesktopSize&)textureSize;
- (void)setTextureSize:(const webrtc::DesktopSize&)size;
// bind this object to an effect's via the effects properties
- (void)bindToEffect:(GLKEffectPropertyTexture*)effectProperty;
- (BOOL)needDraw;
// draw a region of the texture
- (void)drawRegion:(GLRegion*)region rect:(CGRect)rect;
- (void)releaseTexture;
@end
#endif // REMOTING_IOS_UI_DESKTOP_TEXTURE_H_
\ No newline at end of file
This diff is collapsed.
// Copyright 2014 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 REMOTING_IOS_UI_HELP_VIEW_CONTROLLER_H_
#define REMOTING_IOS_UI_HELP_VIEW_CONTROLLER_H_
#import <UIKit/UIKit.h>
@interface HelpViewController : UIViewController {
@private
IBOutlet UIWebView* _webView;
}
@end
#endif // REMOTING_IOS_UI_HELP_VIEW_CONTROLLER_H_
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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