Commit de26280d authored by sergeyu@chromium.org's avatar sergeyu@chromium.org

Remove NPAPI plugin from chromoting webapp.

BUG=134215
R=weitaosu@chromium.org, wez@chromium.org

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278198 0039d316-1c4b-4281-b951-d872f2087c98
parent 9ca6bd26
APK_FILE_NAME=ChromeRemoteDesktop
APK_PACKAGE_NAME=com.google.chromeremotedesktop
DAEMON_FILE_NAME=Chrome Remote Desktop Host Service
HOST_PLUGIN_FILE_NAME=Chrome Remote Desktop Host
IT2ME_HOST_DESCRIPTION=Remote Assistance Host for Chrome Remote Desktop
MAC_BUNDLE_ID=com.google.Chrome
MAC_CREATOR=rimZ
MAC_HOST_BUNDLE_ID=com.google.chromeremotedesktop.me2me-host
MAC_HOST_BUNDLE_NAME=ChromeRemoteDesktopHost.bundle
MAC_HOST_PACKAGE_NAME=Chrome Remote Desktop Host
MAC_NATIVE_MESSAGING_HOST_BUNDLE_ID=com.google.chrome.remote_desktop.native-messaging-host
MAC_NATIVE_MESSAGING_HOST_BUNDLE_NAME=NativeMessagingHost.bundle
MAC_PREFPANE_BUNDLE_ID=com.google.chromeremotedesktop.preferences
......
APK_FILE_NAME=Chromoting
APK_PACKAGE_NAME=org.chromium.chromoting
DAEMON_FILE_NAME=Chromoting Host Service
HOST_PLUGIN_FILE_NAME=Chromoting Host
IT2ME_HOST_DESCRIPTION=Remote Assistance Host for Chromoting
MAC_BUNDLE_ID=org.chromium.Chromium
MAC_CREATOR=Cr24
MAC_HOST_BUNDLE_ID=org.chromium.chromoting.me2me-host
MAC_HOST_BUNDLE_NAME=ChromotingHost.bundle
MAC_HOST_PACKAGE_NAME=Chromoting Host
MAC_NATIVE_MESSAGING_HOST_BUNDLE_ID=org.chromium.chromoting.native-messaging-host
MAC_NATIVE_MESSAGING_HOST_BUNDLE_NAME=NativeMessagingHost.bundle
MAC_PREFPANE_BUNDLE_ID=org.chromium.remoting.preferences
......
......@@ -12,7 +12,7 @@ namespace {
// TODO(lambroslambrou): The default locations should depend on whether Chrome
// branding is enabled - this means also modifying the Python daemon script.
// The actual location of the files is ultimately determined by the service
// daemon and NPAPI implementation - these defaults are only used in case the
// daemon and native messaging host - these defaults are only used in case the
// command-line switches are absent.
#if defined(OS_WIN)
#ifdef OFFICIAL_BUILD
......
......@@ -49,8 +49,7 @@ class ChromotingHostContext {
// libjingle code may be run.
scoped_refptr<AutoThreadTaskRunner> network_task_runner();
// Task runner for the thread that is used for the UI. In the NPAPI
// plugin this corresponds to the main plugin thread.
// Task runner for the thread that is used for the UI.
scoped_refptr<AutoThreadTaskRunner> ui_task_runner();
// Task runner for the thread used by the ScreenRecorder to capture
......
// Copyright (c) 2012 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/host/plugin/host_log_handler.h"
#include "base/lazy_instance.h"
#include "remoting/base/logging.h"
#include "remoting/base/util.h"
#include "remoting/host/plugin/host_script_object.h"
namespace remoting {
// Records whether or not we have a scriptable object registered for logging.
// This is set inside the lock, but is read (in LogToUI) outside of a lock so
// that we don't needlessly slow down the system when we log.
static bool g_has_logging_scriptable_object = false;
// The lock that protects the logging globals.
static base::LazyInstance<base::Lock>::Leaky
g_logging_lock = LAZY_INSTANCE_INITIALIZER;
// The scriptable object that will display the log information to the user.
static HostNPScriptObject* g_logging_scriptable_object = NULL;
// The previously registered LogMessageHandler. If not NULL, we call this after
// we're doing processing the log message.
static logging::LogMessageHandlerFunction g_logging_old_handler = NULL;
// Set to true when we register our global log handler so that we don't try
// to register it twice.
static bool g_has_registered_log_handler = false;
// static
void HostLogHandler::RegisterLogMessageHandler() {
base::AutoLock lock(g_logging_lock.Get());
if (g_has_registered_log_handler)
return;
HOST_LOG << "Registering global log handler";
// Record previous handler so we can call it in a chain.
g_logging_old_handler = logging::GetLogMessageHandler();
// Set up log message handler.
// This is not thread-safe so we need it within our lock.
// Note that this will not log anything until a scriptable object instance
// has been created to handle the log message display.
logging::SetLogMessageHandler(&LogToUI);
g_has_registered_log_handler = true;
}
// static
void HostLogHandler::RegisterLoggingScriptObject(
HostNPScriptObject* script_object) {
base::AutoLock lock(g_logging_lock.Get());
VLOG(1) << "Registering log handler scriptable object";
// Register this script object as the one that will handle all logging calls
// and display them to the user.
// If multiple plugins are run, then the last one registered will handle all
// logging for all instances.
g_logging_scriptable_object = script_object;
g_has_logging_scriptable_object = true;
}
// static
void HostLogHandler::UnregisterLoggingScriptObject(
HostNPScriptObject* script_object) {
base::AutoLock lock(g_logging_lock.Get());
// Ignore unless we're the currently registered script object.
if (script_object != g_logging_scriptable_object)
return;
// Unregister this script object for logging.
g_has_logging_scriptable_object = false;
g_logging_scriptable_object = NULL;
VLOG(1) << "Unregistering log handler scriptable object";
}
// static
bool HostLogHandler::LogToUI(int severity, const char* file, int line,
size_t message_start,
const std::string& str) {
// Note: We're reading |g_has_logging_scriptable_object| outside of a lock.
// This lockless read is done so that we don't needlessly slow down global
// logging with a lock for each log message.
//
// This lockless read is safe because:
//
// Misreading a false value (when it should be true) means that we'll simply
// skip processing a few log messages.
//
// Misreading a true value (when it should be false) means that we'll take
// the lock and check |g_logging_scriptable_object| unnecessarily. This is not
// problematic because we always set |g_logging_scriptable_object| inside a
// lock.
//
// Misreading an old cached value is also not problematic for the same
// reasons: a mis-read either skips a log message or causes us to take a lock
// unnecessarily.
if (g_has_logging_scriptable_object) {
base::AutoLock lock(g_logging_lock.Get());
if (g_logging_scriptable_object) {
std::string message = remoting::GetTimestampString();
message += (str.c_str() + message_start);
g_logging_scriptable_object->PostLogDebugInfo(message);
}
}
// Call the next log handler in the chain.
if (g_logging_old_handler)
return (g_logging_old_handler)(severity, file, line, message_start, str);
return false;
}
} // namespace remoting
// Copyright (c) 2011 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_HOST_PLUGIN_HOST_LOG_HANDLER_H_
#define REMOTING_HOST_PLUGIN_HOST_LOG_HANDLER_H_
#include <string>
namespace remoting {
class HostNPScriptObject;
class HostLogHandler {
public:
// Register the log handler.
// These should be called from the plugin init/destroy methods so that they
// are only called once per plugin process (not once per plugin instance).
static void RegisterLogMessageHandler();
// We don't have the corresponding UnregisterLogMessageHandler because it
// is not safe to call it when there are multiple threads that might be
// logging.
static void RegisterLoggingScriptObject(HostNPScriptObject* script_object);
static void UnregisterLoggingScriptObject(HostNPScriptObject* script_object);
private:
// A Log Message Handler that is called after each LOG message has been
// processed. This must be of type LogMessageHandlerFunction defined in
// base/logging.h.
static bool LogToUI(int severity, const char* file, int line,
size_t message_start, const std::string& str);
};
} // namespace remoting
#endif // REMOTING_HOST_PLUGIN_HOST_LOG_HANDLER_H_
<?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>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${CHROMIUM_BUNDLE_ID}.remoting.host.plugin</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BRPL</string>
<key>CFBundleShortVersionString</key>
<string>VERSION_SHORT</string>
<key>CFBundleVersion</key>
<string>VERSION_FULL</string>
<key>CFBundleSignature</key>
<string>${CHROMIUM_CREATOR}</string>
<key>LSMinimumSystemVersion</key>
<string>10.5.0</string>
<key>WebPluginMIMETypes</key>
<dict>
<key>HOST_PLUGIN_MIME_TYPE</key>
<dict/>
</dict>
</dict>
</plist>
NSHumanReadableCopyright = "{% trans %}COPYRIGHT{% endtrans %}";
WebPluginDescription = "{% trans %}REMOTING_HOST_PLUGIN_NAME{% endtrans %}";
WebPluginName = "{% trans %}REMOTING_HOST_PLUGIN_DESCRIPTION{% endtrans %}";
This diff is collapsed.
LIBRARY remoting_host_plugin
EXPORTS
NP_GetEntryPoints
NP_Initialize
NP_Shutdown
// Copyright (c) 2012 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/host/plugin/host_plugin_utils.h"
namespace remoting {
NPNetscapeFuncs* g_npnetscape_funcs = NULL;
std::string StringFromNPIdentifier(NPIdentifier identifier) {
if (!g_npnetscape_funcs->identifierisstring(identifier))
return std::string();
NPUTF8* np_string = g_npnetscape_funcs->utf8fromidentifier(identifier);
std::string string(np_string);
g_npnetscape_funcs->memfree(np_string);
return string;
}
std::string StringFromNPVariant(const NPVariant& variant) {
if (!NPVARIANT_IS_STRING(variant))
return std::string();
const NPString& np_string = NPVARIANT_TO_STRING(variant);
return std::string(np_string.UTF8Characters, np_string.UTF8Length);
}
NPVariant NPVariantFromString(const std::string& val) {
size_t len = val.length();
NPUTF8* chars =
reinterpret_cast<NPUTF8*>(g_npnetscape_funcs->memalloc(len + 1));
strcpy(chars, val.c_str());
NPVariant variant;
STRINGN_TO_NPVARIANT(chars, len, variant);
return variant;
}
NPObject* ObjectFromNPVariant(const NPVariant& variant) {
if (!NPVARIANT_IS_OBJECT(variant))
return NULL;
return NPVARIANT_TO_OBJECT(variant);
}
ScopedRefNPObject::ScopedRefNPObject() : object_(NULL) { }
ScopedRefNPObject::ScopedRefNPObject(NPObject* object)
: object_(NULL) {
*this = object;
}
ScopedRefNPObject::ScopedRefNPObject(const ScopedRefNPObject& object)
: object_(NULL) {
*this = object;
}
ScopedRefNPObject::~ScopedRefNPObject() {
*this = NULL;
}
ScopedRefNPObject& ScopedRefNPObject::operator=(NPObject* object) {
if (object) {
g_npnetscape_funcs->retainobject(object);
}
if (object_) {
g_npnetscape_funcs->releaseobject(object_);
}
object_ = object;
return *this;
}
ScopedRefNPObject& ScopedRefNPObject::operator=(
const ScopedRefNPObject& object) {
return *this = object.get();
}
} // namespace remoting
// Copyright (c) 2012 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_HOST_PLUGIN_HOST_PLUGIN_UTILS_H_
#define REMOTING_HOST_PLUGIN_HOST_PLUGIN_UTILS_H_
#include <string>
#include "third_party/npapi/bindings/npapi.h"
#include "third_party/npapi/bindings/npfunctions.h"
#include "third_party/npapi/bindings/npruntime.h"
namespace remoting {
// Global netscape functions initialized in NP_Initialize.
extern NPNetscapeFuncs* g_npnetscape_funcs;
// Convert an NPIdentifier into a std::string.
std::string StringFromNPIdentifier(NPIdentifier identifier);
// Convert an NPVariant into a std::string.
std::string StringFromNPVariant(const NPVariant& variant);
// Convert a std::string into an NPVariant.
// Caller is responsible for making sure that NPN_ReleaseVariantValue is
// called on returned value.
NPVariant NPVariantFromString(const std::string& val);
// Convert an NPVariant into an NSPObject.
NPObject* ObjectFromNPVariant(const NPVariant& variant);
// Scoped reference pointer for NPObjects. All objects using this class
// must be owned by g_npnetscape_funcs.
class ScopedRefNPObject {
public:
ScopedRefNPObject();
explicit ScopedRefNPObject(NPObject* object);
explicit ScopedRefNPObject(const ScopedRefNPObject& object);
~ScopedRefNPObject();
// Release the held reference and replace it with |object|, incrementing
// its reference count.
ScopedRefNPObject& operator=(NPObject* object);
ScopedRefNPObject& operator=(const ScopedRefNPObject& object);
NPObject* get() const { return object_; }
private:
NPObject* object_;
};
} // namespace remoting
#endif // REMOTING_HOST_PLUGIN_HOST_PLUGIN_UTILS_H_
This diff is collapsed.
This diff is collapsed.
......@@ -29,7 +29,7 @@ VS_VERSION_INFO VERSIONINFO
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
#if (BINARY == BINARY_CORE) || (BINARY == BINARY_HOST_PLUGIN)
#if (BINARY == BINARY_CORE)
FILETYPE VFT_DLL
#else
FILETYPE VFT_APP
......@@ -59,11 +59,6 @@ BEGIN
VALUE "FileDescription", "{% trans %}REMOTING_HOST_DESCRIPTION{% endtrans %}"
VALUE "InternalName", "remoting_host.exe"
VALUE "OriginalFilename", "remoting_host.exe"
#elif (BINARY == BINARY_HOST_PLUGIN)
VALUE "FileDescription", "{% trans %}REMOTING_HOST_PLUGIN_DESCRIPTION{% endtrans %}"
VALUE "InternalName", "remoting_host_plugin.dll"
VALUE "OriginalFilename", "remoting_host_plugin.dll"
VALUE "MIMEType", STRINGIZE(HOST_PLUGIN_MIME_TYPE)
#elif (BINARY == BINARY_NATIVE_MESSAGING_HOST)
VALUE "FileDescription", "{% trans %}REMOTING_NATIVE_MESSAGING_HOST_DESCRIPTION{% endtrans %}"
VALUE "InternalName", "remoting_native_messaging_host.exe"
......
......@@ -29,41 +29,12 @@
'webapp_locale_dir': '<(SHARED_INTERMEDIATE_DIR)/remoting/webapp/_locales',
'host_plugin_mime_type': 'application/vnd.chromium.remoting-host',
'conditions': [
['OS=="mac"', {
'mac_bundle_id': '<!(python <(version_py_path) -f <(branding_path) -t "@MAC_BUNDLE_ID@")',
'mac_creator': '<!(python <(version_py_path) -f <(branding_path) -t "@MAC_CREATOR@")',
'host_plugin_extension': 'plugin',
'host_plugin_prefix': '',
}],
['os_posix == 1 and OS != "mac" and target_arch == "ia32"', {
# linux 32 bit
'host_plugin_extension': 'ia32.so',
'host_plugin_prefix': 'lib',
}],
['os_posix == 1 and OS != "mac" and target_arch == "x64"', {
# linux 64 bit
'host_plugin_extension': 'x64.so',
'host_plugin_prefix': 'lib',
}],
['os_posix == 1 and OS != "mac" and target_arch == "arm"', {
'host_plugin_extension': 'arm.so',
'host_plugin_prefix': 'lib',
}],
['os_posix == 1 and OS != "mac" and target_arch == "arm64"', {
'host_plugin_extension': 'arm64.so',
'host_plugin_prefix': 'lib',
}],
['os_posix == 1 and OS != "mac" and target_arch == "mipsel"', {
'host_plugin_extension': 'mipsel.so',
'host_plugin_prefix': 'lib',
}],
['OS=="win"', {
'host_plugin_extension': 'dll',
'host_plugin_prefix': '',
# Each CLSID is a hash of the current version string salted with an
# arbitrary GUID. This ensures that the newly installed COM classes will
# be used during/after upgrade even if there are old instances running
......@@ -112,9 +83,8 @@
'BINARY_CORE=1',
'BINARY_DESKTOP=2',
'BINARY_HOST_ME2ME=3',
'BINARY_HOST_PLUGIN=4',
'BINARY_NATIVE_MESSAGING_HOST=5',
'BINARY_REMOTE_ASSISTANCE_HOST=6',
'BINARY_NATIVE_MESSAGING_HOST=4',
'BINARY_REMOTE_ASSISTANCE_HOST=5',
],
'include_dirs': [
'..', # Root of Chrome checkout
......@@ -191,7 +161,6 @@
'host/disconnect_window_mac.mm',
'host/installer/mac/uninstaller/remoting_uninstaller-InfoPlist.strings.jinja2',
'host/mac/me2me_preference_pane-InfoPlist.strings.jinja2',
'host/plugin/host_plugin-InfoPlist.strings.jinja2',
'host/win/core.rc.jinja2',
'host/win/host_messages.mc.jinja2',
'host/win/version.rc.jinja2',
......
......@@ -108,7 +108,6 @@
'type': 'none',
'variables': {
'webapp_type': 'v1',
'include_host_plugin': '<(enable_remoting_host)',
'output_dir': '<(PRODUCT_DIR)/remoting/remoting.webapp',
'zip_path': '<(PRODUCT_DIR)/remoting-webapp.zip',
},
......
......@@ -450,106 +450,6 @@
],
}, # end of target 'remoting_host_setup_base'
{
'target_name': 'remoting_host_plugin',
'type': 'loadable_module',
'variables': { 'enable_wexit_time_destructors': 1, },
'product_extension': '<(host_plugin_extension)',
'product_prefix': '<(host_plugin_prefix)',
'defines': [
'HOST_PLUGIN_MIME_TYPE=<(host_plugin_mime_type)',
],
'dependencies': [
'../base/base.gyp:base_i18n',
'../net/net.gyp:net',
'../third_party/npapi/npapi.gyp:npapi',
'remoting_base',
'remoting_host',
'remoting_host_setup_base',
'remoting_infoplist_strings',
'remoting_it2me_host_static',
'remoting_protocol',
'remoting_resources',
],
'sources': [
'base/dispatch_win.h',
'host/plugin/host_log_handler.cc',
'host/plugin/host_log_handler.h',
'host/plugin/host_plugin.cc',
'host/plugin/host_plugin_utils.cc',
'host/plugin/host_plugin_utils.h',
'host/plugin/host_script_object.cc',
'host/plugin/host_script_object.h',
'host/win/core_resource.h',
],
'conditions': [
['OS=="mac"', {
'mac_bundle': 1,
'xcode_settings': {
'CHROMIUM_BUNDLE_ID': '<(mac_bundle_id)',
'INFOPLIST_FILE': 'host/plugin/host_plugin-Info.plist',
'INFOPLIST_PREPROCESS': 'YES',
# TODO(maruel): Use INFOPLIST_PREFIX_HEADER to remove the need to
# duplicate string once
# http://code.google.com/p/gyp/issues/detail?id=243 is fixed.
'INFOPLIST_PREPROCESSOR_DEFINITIONS': 'HOST_PLUGIN_MIME_TYPE="<(host_plugin_mime_type)" VERSION_FULL="<(version_full)" VERSION_SHORT="<(version_short)"',
},
# TODO(mark): Come up with a fancier way to do this. It should
# only be necessary to list host_plugin-Info.plist once, not the
# three times it is listed here.
'mac_bundle_resources': [
'host/disconnect_window.xib',
'host/plugin/host_plugin-Info.plist',
'resources/chromoting16.png',
'resources/chromoting48.png',
'resources/chromoting128.png',
'<!@pymod_do_main(remoting_copy_locales -o -p <(OS) -x <(PRODUCT_DIR) <(remoting_locales))',
# Localized strings for 'Info.plist'
'<!@pymod_do_main(remoting_localize --locale_output '
'"<(SHARED_INTERMEDIATE_DIR)/remoting/host_plugin-InfoPlist.strings/@{json_suffix}.lproj/InfoPlist.strings" '
'--print_only <(remoting_locales))',
],
'mac_bundle_resources!': [
'host/plugin/host_plugin-Info.plist',
],
'conditions': [
['mac_breakpad==1', {
'variables': {
# A real .dSYM is needed for dump_syms to operate on.
'mac_real_dsym': 1,
},
}],
], # conditions
}], # OS=="mac"
[ 'OS=="win"', {
'defines': [
'BINARY=BINARY_HOST_PLUGIN',
'ISOLATION_AWARE_ENABLED=1',
],
'dependencies': [
'remoting_lib_idl',
'remoting_windows_resources',
],
'include_dirs': [
'<(INTERMEDIATE_DIR)',
],
'sources': [
'<(SHARED_INTERMEDIATE_DIR)/remoting/core.rc',
'<(SHARED_INTERMEDIATE_DIR)/remoting/version.rc',
'host/plugin/host_plugin.def',
],
'msvs_settings': {
'VCManifestTool': {
'EmbedManifest': 'true',
'AdditionalManifestFiles': [
'host/win/common-controls.manifest',
],
},
},
}],
],
}, # end of target 'remoting_host_plugin'
{
'target_name': 'remoting_it2me_host_static',
'type': 'static_library',
......@@ -638,7 +538,6 @@
'remoting_resources',
],
'sources': [
'host/plugin/host_plugin-InfoPlist.strings.jinja2',
'host/remoting_me2me_host-InfoPlist.strings.jinja2',
'host/mac/me2me_preference_pane-InfoPlist.strings.jinja2',
'host/installer/mac/uninstaller/remoting_uninstaller-InfoPlist.strings.jinja2',
......
......@@ -89,13 +89,10 @@
'actions': [
{
'action_name': 'dump_symbols',
'variables': {
'plugin_file': '<(host_plugin_prefix)remoting_host_plugin.<(host_plugin_extension)',
},
'inputs': [
'<(DEPTH)/build/linux/dump_app_syms',
'<(PRODUCT_DIR)/dump_syms',
'<(PRODUCT_DIR)/<(plugin_file)',
'<(PRODUCT_DIR)/remoting_me2me_host',
],
'outputs': [
'<(PRODUCT_DIR)/<(plugin_file).breakpad.<(target_arch)',
......@@ -103,14 +100,14 @@
'action': ['<(DEPTH)/build/linux/dump_app_syms',
'<(PRODUCT_DIR)/dump_syms',
'<(linux_strip_binary)',
'<(PRODUCT_DIR)/<(plugin_file)',
'<(PRODUCT_DIR)/remoting_me2me_host',
'<@(_outputs)'],
'message': 'Dumping breakpad symbols to <(_outputs)',
'process_outputs_as_sources': 1,
},
],
'dependencies': [
'remoting_host_plugin',
'remoting_me2me_host',
'../breakpad/breakpad.gyp:dump_syms',
],
}], # 'linux_dump_symbols==1'
......
......@@ -88,7 +88,7 @@
'remoting_native_messaging_manifests',
],
'variables': {
'host_name': '<!(python <(version_py_path) -f <(branding_path) -t "@HOST_PLUGIN_FILE_NAME@")',
'host_name': '<!(python <(version_py_path) -f <(branding_path) -t "@MAC_HOST_PACKAGE_NAME@")',
'host_service_name': '<!(python <(version_py_path) -f <(branding_path) -t "@DAEMON_FILE_NAME@")',
'host_uninstaller_name': '<!(python <(version_py_path) -f <(branding_path) -t "@MAC_UNINSTALLER_NAME@")',
'bundle_prefix': '<!(python <(version_py_path) -f <(branding_path) -t "@MAC_UNINSTALLER_BUNDLE_PREFIX@")',
......
......@@ -7,7 +7,6 @@
{
'type': 'none',
'variables': {
'include_host_plugin%': 0,
'extra_files%': [],
'generated_html_files': [
'<(SHARED_INTERMEDIATE_DIR)/main.html',
......@@ -19,22 +18,6 @@
'remoting_webapp_html',
],
'conditions': [
['include_host_plugin==1', {
'dependencies': [
'remoting_host_plugin',
],
'variables': {
'plugin_path': '<(PRODUCT_DIR)/<(host_plugin_prefix)remoting_host_plugin.<(host_plugin_extension)',
'plugin_args': [
'--locales', '<@(remoting_host_locale_files)',
'--plugin', '<(plugin_path)',
],
},
}, {
'variables': {
'plugin_args': [],
},
}],
['webapp_type=="v2_pnacl"', {
'dependencies': [
'remoting_nacl.gyp:remoting_client_plugin_nacl',
......@@ -83,14 +66,6 @@
'<@(remoting_webapp_locale_files)',
'<@(extra_files)',
],
'conditions': [
['include_host_plugin==1', {
'inputs': [
'<(plugin_path)',
'<@(remoting_host_locale_files)',
],
}],
],
'outputs': [
'<(output_dir)',
'<(zip_path)',
......@@ -99,7 +74,6 @@
'python', 'webapp/build-webapp.py',
'<(buildtype)',
'<(version_full)',
'<(host_plugin_mime_type)',
'<(output_dir)',
'<(zip_path)',
'webapp/manifest.json.jinja2',
......@@ -107,7 +81,6 @@
'<@(generated_html_files)',
'<@(remoting_webapp_files)',
'<@(extra_files)',
'<@(plugin_args)',
'--locales', '<@(remoting_webapp_locale_files)',
],
},
......
......@@ -214,7 +214,7 @@
<message desc="Error displayed if authentication fails. This can be caused by stale credentials, in which logging out of the web-app and retrying can fix the problem." name="IDS_ERROR_AUTHENTICATION_FAILED" formatter_data="android_java">
Authentication failed. Please sign in to Chrome Remote Desktop again.
</message>
<message desc="Error displayed if the host or client plugin are missing or if they could not be loaded." name="IDS_ERROR_BAD_PLUGIN_VERSION">
<message desc="Error displayed if the client plugin are missing or if they could not be loaded." name="IDS_ERROR_BAD_PLUGIN_VERSION">
Some components required for Chrome Remote Desktop are missing. Please make sure you have installed the latest version and try again.
</message>
<message desc="Error that is shown on the client side when incompatible Chrome Remote Desktop versions are installed on host and client." name="IDS_ERROR_INCOMPATIBLE_PROTOCOL" formatter_data="android_java">
......@@ -280,9 +280,6 @@
<message name="IDS_VERIFY_PIN_DIALOG_MESSAGE" desc="The message displayed by the PIN verification dialog.">
Please confirm your account and PIN below to allow access by Chrome Remote Desktop.
</message>
<message name="IDS_REMOTING_HOST_PLUGIN_NAME" desc="Name of Chrome Remote Desktop Host plugin displayed by Chrome at about:plugins.">
Chrome Remote Desktop Host
</message>
<message name="IDS_MAC_PREFPANE_BUNDLE_NAME" desc="The bundle name specified in the property list of Chrome Remote Desktop Host Preferences bundle on MacOS.">
Chrome Remote Desktop Host Preferences
</message>
......@@ -319,7 +316,7 @@
<message desc="Error displayed if authentication fails. This can be caused by stale credentials, in which logging out of the web-app and retrying can fix the problem." name="IDS_ERROR_AUTHENTICATION_FAILED" formatter_data="android_java">
Authentication failed. Please sign in to Chromoting again.
</message>
<message desc="Error displayed if the host or client plugin are missing or if they could not be loaded." name="IDS_ERROR_BAD_PLUGIN_VERSION">
<message desc="Error displayed if the client plugin are missing or if they could not be loaded." name="IDS_ERROR_BAD_PLUGIN_VERSION">
Some components required for Chromoting are missing. Please make sure you have installed the latest version and try again.
</message>
<message desc="Error that is shown on the client side when incompatible Chromoting versions are installed on host and client." name="IDS_ERROR_INCOMPATIBLE_PROTOCOL" formatter_data="android_java">
......@@ -385,9 +382,6 @@
<message name="IDS_VERIFY_PIN_DIALOG_MESSAGE" desc="The message displayed by the PIN verification dialog.">
Please confirm your account and PIN below to allow access by Chromoting.
</message>
<message name="IDS_REMOTING_HOST_PLUGIN_NAME" desc="Name of Chromoting Host plugin displayed by Chrome at about:plugins.">
Chromoting Host
</message>
<message name="IDS_MAC_PREFPANE_BUNDLE_NAME" desc="The bundle name specified in the property list of Chromoting Host Preferences bundle on MacOS.">
Chromoting Host Preferences
</message>
......@@ -779,9 +773,6 @@ For information about privacy, please see the Google Privacy Policy (http://goo.
<message name="IDS_REMOTING_HOST_DESCRIPTION" desc="The file description specified in the version information of remoting_host.exe.">
Host Process
</message>
<message name="IDS_REMOTING_HOST_PLUGIN_DESCRIPTION" desc="The file description specified in the version information of remoting_host_plugin.dll.">
Allow another user to access your computer securely over the Internet.
</message>
<message name="IDS_REMOTING_NATIVE_MESSAGING_HOST_DESCRIPTION" desc="The file description specified in the version information of remoting_native_messaging_host.exe.">
Native messaging host for remoting host management
</message>
......
......@@ -75,20 +75,18 @@ def processJinjaTemplate(input_file, output_file, context):
def buildWebApp(buildtype, version, mimetype, destination, zip_path,
manifest_template, webapp_type, plugin, files, locales):
def buildWebApp(buildtype, version, destination, zip_path,
manifest_template, webapp_type, files, locales):
"""Does the main work of building the webapp directory and zipfile.
Args:
buildtype: the type of build ("Official" or "Dev").
mimetype: A string with mimetype of plugin.
destination: A string with path to directory where the webapp will be
written.
zipfile: A string with path to the zipfile to create containing the
contents of |destination|.
manifest_template: jinja2 template file for manifest.
webapp_type: webapp type ("v1", "v2" or "v2_pnacl").
plugin: A string with path to the binary plugin for this webapp.
files: An array of strings listing the paths for resources to include
in this webapp.
locales: An array of strings listing locales, which are copied, along
......@@ -151,44 +149,6 @@ def buildWebApp(buildtype, version, mimetype, destination, zip_path,
else:
raise Exception("Unknown extension: " + current_locale);
# Create fake plugin files to appease the manifest checker.
# It requires that if there is a plugin listed in the manifest that
# there be a file in the plugin with that name.
names = [
'remoting_host_plugin.dll', # Windows
'remoting_host_plugin.plugin', # Mac
'libremoting_host_plugin.ia32.so', # Linux 32
'libremoting_host_plugin.x64.so' # Linux 64
]
pluginName = os.path.basename(plugin)
for name in names:
if name != pluginName:
path = os.path.join(destination, name)
f = open(path, 'w')
f.write("placeholder for %s" % (name))
f.close()
# Copy the plugin. On some platforms (e.g. ChromeOS) plugin compilation may be
# disabled, in which case we don't need to copy anything.
if plugin:
newPluginPath = os.path.join(destination, pluginName)
if os.path.isdir(plugin):
# On Mac we have a directory.
shutil.copytree(plugin, newPluginPath)
else:
shutil.copy2(plugin, newPluginPath)
# Strip the linux build.
if ((platform.system() == 'Linux') and (buildtype == 'Official')):
subprocess.call(["strip", newPluginPath])
# Set the correct mimetype.
hostPluginMimeType = os.environ.get(
'HOST_PLUGIN_MIMETYPE', 'application/vnd.chromium.remoting-host')
findAndReplace(os.path.join(destination, 'plugin_settings.js'),
'HOST_PLUGIN_MIMETYPE', hostPluginMimeType)
# Set client plugin type.
client_plugin = 'pnacl' if webapp_type == 'v2_pnacl' else 'native'
findAndReplace(os.path.join(destination, 'plugin_settings.js'),
......@@ -304,29 +264,24 @@ def buildWebApp(buildtype, version, mimetype, destination, zip_path,
def main():
if len(sys.argv) < 6:
print ('Usage: build-webapp.py '
'<build-type> <version> <mime-type> <dst> <zip-path> '
'<manifest_template> <webapp_type> <other files...> '
'[--plugin <plugin>] [--locales <locales...>]')
'<build-type> <version> <dst> <zip-path> <manifest_template> '
'<webapp_type> <other files...> '
'[--locales <locales...>]')
return 1
arg_type = ''
files = []
locales = []
plugin = ""
for arg in sys.argv[8:]:
if arg in ['--locales', '--plugin']:
if arg in ['--locales']:
arg_type = arg
elif arg_type == '--locales':
locales.append(arg)
elif arg_type == '--plugin':
plugin = arg
arg_type = ''
else:
files.append(arg)
return buildWebApp(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4],
sys.argv[5], sys.argv[6], sys.argv[7], plugin,
files, locales)
sys.argv[5], sys.argv[6], files, locales)
if __name__ == '__main__':
......
......@@ -68,15 +68,8 @@ remoting.HostController.AsyncResult.fromString = function(result) {
* @private
*/
remoting.HostController.prototype.createDispatcher_ = function() {
/** @return {remoting.HostPlugin} */
var createPluginForMe2Me = function() {
/** @type {HTMLElement} @private */
var container = document.getElementById('daemon-plugin-container');
return remoting.createNpapiPlugin(container);
};
/** @type {remoting.HostDispatcher} @private */
var hostDispatcher = new remoting.HostDispatcher(createPluginForMe2Me);
var hostDispatcher = new remoting.HostDispatcher();
/** @param {string} version */
var printVersion = function(version) {
......@@ -125,27 +118,6 @@ remoting.HostController.prototype.getConsent = function(onDone, onError) {
this.hostDispatcher_.getUsageStatsConsent(onDone, onError);
};
/**
* @param {function(remoting.HostController.AsyncResult):void} onDone
* @param {function(remoting.Error):void} onError
* @return {void}
*/
remoting.HostController.prototype.installHost = function(onDone, onError) {
/** @type {remoting.HostController} */
var that = this;
/** @param {remoting.HostController.AsyncResult} asyncResult */
var onHostInstalled = function(asyncResult) {
// Refresh the dispatcher after the host has been installed.
if (asyncResult == remoting.HostController.AsyncResult.OK) {
that.hostDispatcher_ = that.createDispatcher_();
}
onDone(asyncResult);
};
this.hostDispatcher_.installHost(onHostInstalled, onError);
};
/**
* Registers and starts the host.
*
......@@ -530,13 +502,5 @@ remoting.HostController.prototype.clearPairedClients = function(
this.hostDispatcher_.clearPairedClients(onDone, onError);
};
/**
* Returns true if the NPAPI plugin is being used.
* @return {boolean}
*/
remoting.HostController.prototype.usingNpapiPlugin = function() {
return this.hostDispatcher_.usingNpapiPlugin();
}
/** @type {remoting.HostController} */
remoting.hostController = null;
This diff is collapsed.
......@@ -5,12 +5,9 @@
/**
* @fileoverview
* This class provides an interface between the HostSession and either the
* NativeMessaging Host or the Host NPAPI plugin, depending on whether or not
* NativeMessaging is supported. Since the test for NativeMessaging support is
* asynchronous, the connection is attemped on either the the NativeMessaging
* host or the NPAPI plugin once the test is complete.
* NativeMessaging Host
*
* TODO(sergeyu): Remove this class once the NPAPI plugin is dropped.
* TODO(sergeyu): Remove this class.
*/
'use strict';
......@@ -27,11 +24,6 @@ remoting.HostIt2MeDispatcher = function() {
* @private */
this.nativeMessagingHost_ = null;
/**
* @type {remoting.HostPlugin}
* @private */
this.npapiHost_ = null;
/**
* @param {remoting.Error} error
* @private */
......@@ -39,9 +31,6 @@ remoting.HostIt2MeDispatcher = function() {
};
/**
* @param {function():remoting.HostPlugin} createPluginCallback Callback to
* instantiate the NPAPI plugin when NativeMessaging is determined to be
* unsupported.
* @param {function():void} onDispatcherInitialized Callback to be called after
* initialization has finished successfully.
* @param {function(remoting.Error):void} onDispatcherInitializationFailed
......@@ -49,31 +38,18 @@ remoting.HostIt2MeDispatcher = function() {
* plugin works.
*/
remoting.HostIt2MeDispatcher.prototype.initialize =
function(createPluginCallback, onDispatcherInitialized,
function(onDispatcherInitialized,
onDispatcherInitializationFailed) {
/** @type {remoting.HostIt2MeDispatcher} */
var that = this;
function onNativeMessagingStarted() {
console.log('Native Messaging supported.');
that.npapiHost_ = null;
onDispatcherInitialized();
}
function onNativeMessagingInitFailed() {
console.log('Native Messaging unsupported, falling back to NPAPI.');
that.nativeMessagingHost_ = null;
that.npapiHost_ = createPluginCallback();
// TODO(weitaosu): is there a better way to check whether NPAPI plugin is
// supported?
if (that.npapiHost_) {
onDispatcherInitialized();
} else {
onDispatcherInitializationFailed(remoting.Error.MISSING_PLUGIN);
}
onDispatcherInitializationFailed(remoting.Error.MISSING_PLUGIN);
}
this.nativeMessagingHost_ = new remoting.HostIt2MeNativeMessaging();
......@@ -91,20 +67,6 @@ remoting.HostIt2MeDispatcher.prototype.onNativeMessagingError_ =
this.onErrorHandler_(error);
}
/**
* @return {boolean}
*/
remoting.HostIt2MeDispatcher.prototype.usingNpapi = function() {
return this.npapiHost_ != null;
}
/**
* @return {remoting.HostPlugin}
*/
remoting.HostIt2MeDispatcher.prototype.getNpapiHost = function() {
return this.npapiHost_;
}
/**
* @param {string} email The user's email address.
* @param {string} authServiceWithToken Concatenation of the auth service
......@@ -133,15 +95,6 @@ remoting.HostIt2MeDispatcher.prototype.connect =
this.nativeMessagingHost_.connect(
email, authServiceWithToken, onStateChanged, onNatPolicyChanged,
xmppServerAddress, xmppServerUseTls, directoryBotJid);
} else if (this.npapiHost_) {
this.npapiHost_.xmppServerAddress = xmppServerAddress;
this.npapiHost_.xmppServerUseTls = xmppServerUseTls;
this.npapiHost_.directoryBotJid = directoryBotJid;
this.npapiHost_.onStateChanged = onStateChanged;
this.npapiHost_.onNatTraversalPolicyChanged = onNatPolicyChanged;
this.npapiHost_.logDebugInfo = logDebugInfo;
this.npapiHost_.localize(chrome.i18n.getMessage);
this.npapiHost_.connect(email, authServiceWithToken);
} else {
console.error(
'remoting.HostIt2MeDispatcher.connect() without initialization.');
......@@ -153,51 +106,26 @@ remoting.HostIt2MeDispatcher.prototype.connect =
* @return {void}
*/
remoting.HostIt2MeDispatcher.prototype.disconnect = function() {
if (this.npapiHost_) {
this.npapiHost_.disconnect();
} else {
this.nativeMessagingHost_.disconnect();
}
this.nativeMessagingHost_.disconnect();
};
/**
* @return {string} The access code generated by the it2me host.
*/
remoting.HostIt2MeDispatcher.prototype.getAccessCode = function() {
if (this.npapiHost_) {
return this.npapiHost_.accessCode;
} else {
return this.nativeMessagingHost_.getAccessCode();
}
return this.nativeMessagingHost_.getAccessCode();
};
/**
* @return {number} The access code lifetime, in seconds.
*/
remoting.HostIt2MeDispatcher.prototype.getAccessCodeLifetime = function() {
if (this.npapiHost_) {
return this.npapiHost_.accessCodeLifetime;
} else {
return this.nativeMessagingHost_.getAccessCodeLifetime();
}
return this.nativeMessagingHost_.getAccessCodeLifetime();
};
/**
* @return {string} The client's email address.
*/
remoting.HostIt2MeDispatcher.prototype.getClient = function() {
if (this.npapiHost_) {
return this.npapiHost_.client;
} else {
return this.nativeMessagingHost_.getClient();
}
};
/**
* @return {void}
*/
remoting.HostIt2MeDispatcher.prototype.cleanup = function() {
if (this.npapiHost_) {
this.npapiHost_.parentNode.removeChild(this.npapiHost_);
}
return this.nativeMessagingHost_.getClient();
};
......@@ -33,25 +33,13 @@ remoting.tryShare = function() {
var hostInstallDialog = null;
var tryInitializeDispatcher = function() {
hostDispatcher.initialize(createPluginForIt2Me,
onDispatcherInitialized,
hostDispatcher.initialize(onDispatcherInitialized,
onDispatcherInitializationFailed);
}
/** @return {remoting.HostPlugin} */
var createPluginForIt2Me = function() {
return remoting.createNpapiPlugin(
document.getElementById('host-plugin-container'));
}
var onDispatcherInitialized = function () {
if (hostDispatcher.usingNpapi()) {
hostInstallDialog = new remoting.HostInstallDialog();
hostInstallDialog.show(tryInitializeDispatcher, onInstallError);
} else {
// Host alrady installed.
remoting.startHostUsingDispatcher_(hostDispatcher);
}
// Host already installed.
remoting.startHostUsingDispatcher_(hostDispatcher);
};
/** @param {remoting.Error} error */
......@@ -181,8 +169,6 @@ function onHostStateChanged_(state) {
remoting.setMode(remoting.AppMode.HOST_SHARE_FINISHED);
}
}
remoting.hostSession.cleanup();
} else if (state == remoting.HostSession.State.ERROR) {
console.error('Host state: ERROR');
showShareError_(remoting.Error.UNEXPECTED);
......
......@@ -115,11 +115,3 @@ remoting.HostSession.prototype.getClient = function() {
remoting.HostSession.prototype.disconnect = function() {
this.hostDispatcher_.disconnect();
};
/**
* @return {void} Nothing.
*/
remoting.HostSession.prototype.cleanup = function() {
this.hostDispatcher_.cleanup();
};
......@@ -32,135 +32,6 @@ remoting.ClipboardEvent.prototype.clipboardData;
/** @type {function(): void} */
remoting.ClipboardEvent.prototype.preventDefault;
/** @constructor
* @extends HTMLElement
*/
remoting.HostPlugin = function() {};
/** @param {string} email The email address of the connector.
* @param {string} token The access token for the connector.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.connect = function(email, token) {};
/** @return {void} Nothing. */
remoting.HostPlugin.prototype.disconnect = function() {};
/** @param {function(string):string} callback Pointer to chrome.i18n.getMessage.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.localize = function(callback) {};
/** @param {function(string):void} callback Callback to be called with the
* local hostname.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.getHostName = function(callback) {};
/** @param {string} hostId The host ID.
* @param {string} pin The PIN.
* @param {function(string):void} callback Callback to be called with the hash
* encoded with Base64.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.getPinHash = function(hostId, pin, callback) {};
/** @param {function(string, string):void} callback Callback to be called
* after new key is generated.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.generateKeyPair = function(callback) {};
/**
* Updates host config with the values specified in |config|. All
* fields that are not specified in |config| remain
* unchanged. Following parameters cannot be changed using this
* function: host_id, xmpp_login. Error is returned if |config|
* includes these paramters. Changes take effect before the callback
* is called.
*
* @param {string} config The new config parameters, JSON encoded dictionary.
* @param {function(remoting.HostController.AsyncResult):void} callback
* Callback to be called when finished.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.updateDaemonConfig =
function(config, callback) {};
/** @param {function(string):void} callback Callback to be called with
* the config.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.getDaemonConfig = function(callback) {};
/** @param {function(string):void} callback Callback to be called with
* the version, as a dotted string.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.getDaemonVersion = function(callback) {};
/** @param {function(boolean, boolean, boolean):void} callback Callback to be
* called with the consent.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.getUsageStatsConsent = function(callback) {};
/** @param {function(remoting.HostController.AsyncResult):void} callback
* Callback to be called when finished.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.installHost = function(callback) {};
/** @param {string} config Host configuration.
* @param {function(remoting.HostController.AsyncResult):void} callback
* Callback to be called when finished.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.startDaemon = function(
config, consent, callback) {};
/** @param {function(remoting.HostController.AsyncResult):void} callback
* Callback to be called when finished.
* @return {void} Nothing. */
remoting.HostPlugin.prototype.stopDaemon = function(callback) {};
/** @param {function(string):void} callback Callback to be called with the
* JSON-encoded list of paired clients.
* @return {void} Nothing.
*/
remoting.HostPlugin.prototype.getPairedClients = function(callback) {};
/** @param {function(boolean):void} callback Callback to be called when
* finished.
* @return {void} Nothing.
*/
remoting.HostPlugin.prototype.clearPairedClients = function(callback) {};
/** @param {string} client Client id of the pairing to be deleted.
* @param {function(boolean):void} callback Callback to be called when
* finished.
* @return {void} Nothing.
*/
remoting.HostPlugin.prototype.deletePairedClient = function(
client, callback) {};
/** @type {number} */ remoting.HostPlugin.prototype.state;
/** @type {number} */ remoting.HostPlugin.prototype.STARTING;
/** @type {number} */ remoting.HostPlugin.prototype.REQUESTED_ACCESS_CODE;
/** @type {number} */ remoting.HostPlugin.prototype.RECEIVED_ACCESS_CODE;
/** @type {number} */ remoting.HostPlugin.prototype.CONNECTED;
/** @type {number} */ remoting.HostPlugin.prototype.DISCONNECTED;
/** @type {number} */ remoting.HostPlugin.prototype.DISCONNECTING;
/** @type {number} */ remoting.HostPlugin.prototype.ERROR;
/** @type {string} */ remoting.HostPlugin.prototype.accessCode;
/** @type {number} */ remoting.HostPlugin.prototype.accessCodeLifetime;
/** @type {string} */ remoting.HostPlugin.prototype.client;
/** @type {remoting.HostController.State} */
remoting.HostPlugin.prototype.daemonState;
/** @type {function(boolean):void} */
remoting.HostPlugin.prototype.onNatTraversalPolicyChanged;
/** @type {string} */ remoting.HostPlugin.prototype.xmppServerAddress;
/** @type {boolean} */ remoting.HostPlugin.prototype.xmppServerUseTls;
/** @type {string} */ remoting.HostPlugin.prototype.directoryBotJid;
/** @type {string} */ remoting.HostPlugin.prototype.supportedFeatures;
/** @constructor
* @extends HTMLEmbedElement
*/
......
......@@ -19,8 +19,6 @@ remoting.Settings = function() {};
// The settings on this file are automatically substituted by build-webapp.py.
// Do not override them manually, except for running local tests.
/** @type {string} MIME type for the host plugin.*/
remoting.Settings.prototype.PLUGIN_MIMETYPE = 'HOST_PLUGIN_MIMETYPE';
/** @type {string} API client ID.*/
remoting.Settings.prototype.OAUTH2_CLIENT_ID = 'API_CLIENT_ID';
/** @type {string} API client secret.*/
......
......@@ -178,28 +178,6 @@ function isIT2MeSupported_() {
return !remoting.runningOnChromeOS();
}
/**
* Create an instance of the NPAPI plugin.
* @param {Element} container The element to add the plugin to.
* @return {remoting.HostPlugin} The new plugin instance or null if it failed to
* load.
*/
remoting.createNpapiPlugin = function(container) {
var plugin = document.createElement('embed');
plugin.type = remoting.settings.PLUGIN_MIMETYPE;
// Hiding the plugin means it doesn't load, so make it size zero instead.
plugin.width = 0;
plugin.height = 0;
// Verify if the plugin was loaded successfully.
if (plugin.hasOwnProperty('REQUESTED_ACCESS_CODE')) {
container.appendChild(plugin);
return /** @type {remoting.HostPlugin} */ (plugin);
}
return null;
};
/**
* Returns true if the current platform is fully supported. It's only used when
* we detect that host native messaging components are not installed. In that
......
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