diff options
author | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
---|---|---|
committer | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
commit | 05b47f7a8c5451f858dc220df0e3a97542edace6 (patch) | |
tree | a2273d619f0625c9d44d40842845ccce2eac1045 /o3d/plugin/npapi_host_control | |
parent | 5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff) | |
download | chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2 |
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't
yet build in it's new home. We'll change that shortly.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/plugin/npapi_host_control')
26 files changed, 5742 insertions, 0 deletions
diff --git a/o3d/plugin/npapi_host_control/build.scons b/o3d/plugin/npapi_host_control/build.scons new file mode 100644 index 0000000..74597a6 --- /dev/null +++ b/o3d/plugin/npapi_host_control/build.scons @@ -0,0 +1,100 @@ +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Import('env') + +env.Append( + CPPDEFINES = [ + '_MIDL_USE_GUIDDEF_', + 'DLL_NPAPI_HOST_CONTROL_EXPORT' + ], + CCFLAGS = [ + '/Wp64', + '/TP', + '/FIplugin/npapi_host_control/win/precompile.h' + ], + CPPPATH = [ + 'win', + '$SCONSTRUCT_DIR/plugin/npapi_host_control/win', + ], + LINKFLAGS = [ + '/DEF:$SCONSTRUCT_DIR/plugin/npapi_host_control/win/npapi_host_control.def' + ], +) + +env.Append( + CPPDEFINES = [ + ('O3D_PLUGIN_NAME', '\\"$O3D_PLUGIN_NAME\\"'), + ('O3D_PLUGIN_DESCRIPTION', '\\"$O3D_PLUGIN_DESCRIPTION\\"'), + ('O3D_PLUGIN_VERSION', '\\"$O3D_PLUGIN_VERSION\\"') + ]) + +resource_files = env.RES('win/npapi_host_control.rc'), + +inputs = [ + 'win/npapi_host_control_i.c', + 'win/npapi_host_control.cc', + 'win/host_control.cc', + 'win/np_browser_proxy.cc', + 'win/np_object_proxy.cc', + 'win/np_plugin_proxy.cc', + 'win/dispatch_proxy.cc', + 'win/stream_operation.cc', + 'win/variant_utils.cc', + resource_files, +] + +if env['TARGET_PLATFORM'] == 'WINDOWS': + env['PCH'], obj = env.PCH('win/precompile.cc') + env['PCHSTOP'] = 'plugin/npapi_host_control/win/precompile.h' + inputs += [obj] +else: + inputs += ['precompile.cc'] + + +type_lib = env.TypeLibrary(source=['win/npapi_host_control.idl']) + +# Must not register the plugin while building. Only installing should +# register the plugin. +o3d_host_control = env.SharedLibrary('o3d_host', inputs) +env.Requires(o3d_host_control, type_lib) +env.Requires(resource_files, type_lib) + +env.Replicate('$ARTIFACTS_DIR', o3d_host_control) + +# TODO: have a common way to do colliding installs like this. +# Do the install step only if this variant is the first target. +if env.Bit('windows'): + if (env['BUILD_TYPE'] == ARGUMENTS.get('MODE') or + (ARGUMENTS.get('MODE', 'default') == 'default' and + env['BUILD_TYPE'] == 'dbg-d3d')): + i = env.Replicate('$IE_PLUGIN_DIR', o3d_host_control[0]) + c = env.AddPostAction(i, '$REGSVR $REGSVRFLAGS $TARGET') + env.Alias('install', c) diff --git a/o3d/plugin/npapi_host_control/win/dispatch_proxy.cc b/o3d/plugin/npapi_host_control/win/dispatch_proxy.cc new file mode 100644 index 0000000..334bda4 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/dispatch_proxy.cc @@ -0,0 +1,321 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "plugin/npapi_host_control/win/dispatch_proxy.h" + +#include <atlstr.h> +#include <dispex.h> +#include <oaidl.h> + +#include <base/scoped_ptr.h> +#include <algorithm> +#include <vector> + +#include "plugin/npapi_host_control/win/np_browser_proxy.h" +#include "plugin/npapi_host_control/win/variant_utils.h" + +namespace { + +// Helper routine that invokes an IDispatchEx interface with argument values +// provided by NPAPI variant objects. +HRESULT DispatchInvoke(NPBrowserProxy* browser_proxy, + IDispatchEx *dispatch, + DWORD flags, + DISPID member, + const NPVariant* arguments, + int arg_count, + NPVariant* result) { + // Convert the NPAPI arguments to COM variant objects. + scoped_array<CComVariant> local_args(new CComVariant[arg_count]); + for (int x = 0; x < arg_count; ++x) { + // Note that IDispatch expects arguments in reverse order. + NPVariantToVariant(browser_proxy, + &arguments[x], + &local_args[arg_count - x - 1]); + } + + HRESULT hr = S_OK; + CComVariant return_arg; + DISPPARAMS disp_arguments = {0}; + disp_arguments.cArgs = arg_count; + disp_arguments.rgvarg = local_args.get(); + hr = dispatch->InvokeEx(member, LOCALE_SYSTEM_DEFAULT, + flags, &disp_arguments, &return_arg, NULL, + NULL); + + // If the invoke succeeded, then convert and store the return argument. + if (SUCCEEDED(hr)) { + VariantToNPVariant(browser_proxy, &return_arg, result); + } + + return hr; +} + +} // unnamed namespace + +DispatchProxy::DispatchProxy(IDispatchEx* dispatch, + NPBrowserProxy* browser_proxy) + : dispatch_(dispatch), + browser_proxy_(browser_proxy) { + _class = &kNPClass; + referenceCount = 1; +} + +DispatchProxy::~DispatchProxy() { + ATLASSERT(referenceCount == 0); + CComPtr<IDispatchEx> dispatchex_object; + if (browser_proxy_) { + browser_proxy_->UnregisterDispatchProxy(dispatch_); + } +} + +DISPID DispatchProxy::GetDispatchId(NPIdentifier name, DWORD flags) const { + DISPID dispatch_id = -1; + + NPUTF8* method_name = + browser_proxy_->GetBrowserFunctions()->utf8fromidentifier(name); + + // Convert the UTF8-NPAPI string to a wide string. + int required_size = 0; + required_size = MultiByteToWideChar(CP_UTF8, 0, method_name, + -1, NULL, 0); + + CComBSTR wide_name(required_size - 1); + MultiByteToWideChar(CP_UTF8, 0, method_name, + -1, (BSTR) wide_name, required_size); + dispatch_->GetDispID(wide_name, flags, &dispatch_id); + + browser_proxy_->GetBrowserFunctions()->memfree(method_name); + + return dispatch_id; +} + +bool DispatchProxy::HasMethod(NPObject *header, NPIdentifier name) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + return proxy->GetDispatchId(name, 0) != -1; +} + +bool DispatchProxy::InvokeEntry(NPObject *header, + NPIdentifier name, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + + DISPID entry_dispid = proxy->GetDispatchId(name, 0); + if (entry_dispid == -1) { + return false; + } + + HRESULT hr = DispatchInvoke(proxy->browser_proxy_, proxy->dispatch_, + DISPATCH_METHOD, entry_dispid, args, arg_count, + result); + + return SUCCEEDED(hr); +} + +bool DispatchProxy::InvokeDefault(NPObject *header, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + + HRESULT hr = DispatchInvoke(proxy->browser_proxy_, proxy->dispatch_, + DISPATCH_METHOD, DISPID_VALUE, args, + arg_count, result); + + return SUCCEEDED(hr); +} + +bool DispatchProxy::Construct(NPObject *header, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + + HRESULT hr = DispatchInvoke(proxy->browser_proxy_, proxy->dispatch_, + DISPATCH_CONSTRUCT, DISPID_VALUE, args, + arg_count, result); + + return SUCCEEDED(hr); +} + +bool DispatchProxy::HasProperty(NPObject *header, NPIdentifier name) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + return proxy->GetDispatchId(name, 0) != -1; +} + +bool DispatchProxy::GetPropertyEntry(NPObject *header, + NPIdentifier name, + NPVariant *variant) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + DISPID dispatch_id = proxy->GetDispatchId(name, 0); + if (dispatch_id == -1) { + return false; + } + + CComVariant result_value; + DISPPARAMS invoke_args = {0}; + HRESULT hr = proxy->dispatch_->Invoke(dispatch_id, IID_NULL, + LOCALE_SYSTEM_DEFAULT, + DISPATCH_PROPERTYGET, + &invoke_args, + &result_value, NULL, 0); + if (SUCCEEDED(hr)) { + VariantToNPVariant(proxy->browser_proxy_, &result_value, variant); + } + + return SUCCEEDED(hr); +} + +bool DispatchProxy::SetPropertyEntry(NPObject *header, + NPIdentifier name, + const NPVariant *variant) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + DISPID dispatch_id = proxy->GetDispatchId(name, fdexNameEnsure); + + // Indicate failure if the property does not exist. + if (dispatch_id == -1) { + return false; + } + + CComVariant dispatch_variant; + NPVariantToVariant(proxy->browser_proxy_, const_cast<NPVariant*>(variant), + &dispatch_variant); + + // Prepare the dispatch arguments for the call. Note that the named + // argument DISPID_PROPERTYPUT is required. + DISPID put_id = DISPID_PROPERTYPUT; + DISPPARAMS invoke_args = {0}; + invoke_args.cArgs = 1; + invoke_args.rgvarg = &dispatch_variant; + invoke_args.cNamedArgs = 1; + invoke_args.rgdispidNamedArgs = &put_id; + + CComVariant return_arg; + HRESULT hr = proxy->dispatch_->Invoke(dispatch_id, IID_NULL, + LOCALE_SYSTEM_DEFAULT, + DISPATCH_PROPERTYPUT, &invoke_args, + &return_arg, NULL, NULL); + + return SUCCEEDED(hr); +} + +bool DispatchProxy::RemovePropertyEntry(NPObject *header, + NPIdentifier name) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + DISPID dispatch_id = proxy->GetDispatchId(name, 0); + if (dispatch_id == -1) { + return true; + } + + HRESULT hr = proxy->dispatch_->DeleteMemberByDispID(dispatch_id); + + return SUCCEEDED(hr); +} + +bool DispatchProxy::EnumeratePropertyEntries(NPObject *header, + NPIdentifier **result, + uint32_t *count) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(header); + *result = NULL; + *count = 0; + + std::vector<NPIdentifier> np_identifiers; + DISPID dispatch_id = DISPID_STARTENUM; + for (;;) { + HRESULT hr = proxy->dispatch_->GetNextDispID(fdexEnumAll, dispatch_id, + &dispatch_id); + if (hr == S_FALSE) { + *count = np_identifiers.size(); + *result = static_cast<NPIdentifier*>( + proxy->browser_proxy_->GetBrowserFunctions()->memalloc( + np_identifiers.size() * sizeof(NPIdentifier))); + std::copy(np_identifiers.begin(), np_identifiers.end(), *result); + return true; + } + + if (FAILED(hr)) + break; + + CComBSTR name_bstr; + hr = proxy->dispatch_->GetMemberName(dispatch_id, &name_bstr); + if (FAILED(hr)) + break; + + CString name_wide((BSTR) name_bstr); + + int utf8_bytes = WideCharToMultiByte(CP_UTF8, 0, name_wide.GetBuffer(), + name_wide.GetLength() + 1, + NULL, 0, NULL, NULL); + scoped_array<NPUTF8> name_utf8(new NPUTF8[utf8_bytes]); + WideCharToMultiByte(CP_UTF8, 0, name_wide.GetBuffer(), + name_wide.GetLength() + 1, + name_utf8.get(), utf8_bytes, NULL, NULL); + + NPIdentifier np_identifier = + proxy->browser_proxy_->GetBrowserFunctions()-> + getstringidentifier(name_utf8.get()); + np_identifiers.push_back(np_identifier); + } + + return false; +} + +NPObject * DispatchProxy::Allocate(NPP npp, NPClass *aClass) { + DispatchProxy *instance = new DispatchProxy(); + instance->_class = aClass; + instance->referenceCount = 1; + return instance; +} + +void DispatchProxy::Deallocate(NPObject *obj) { + DispatchProxy *proxy = static_cast<DispatchProxy*>(obj); + ATLASSERT(proxy->referenceCount == 0); + delete proxy; +} + +NPClass DispatchProxy::kNPClass = { + NP_CLASS_STRUCT_VERSION, + Allocate, + Deallocate, + NULL, + HasMethod, + InvokeEntry, + InvokeDefault, + HasProperty, + GetPropertyEntry, + SetPropertyEntry, + RemovePropertyEntry, + EnumeratePropertyEntries, + Construct +}; diff --git a/o3d/plugin/npapi_host_control/win/dispatch_proxy.h b/o3d/plugin/npapi_host_control/win/dispatch_proxy.h new file mode 100644 index 0000000..bb12cc8 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/dispatch_proxy.h @@ -0,0 +1,197 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// File declaring a class providing a wrapper between the NPAPI NPObject +// interface, and COM's IDispatchEx interface. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_DISPATCH_PROXY_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_DISPATCH_PROXY_H_ + +#include <atlctl.h> +#include <dispex.h> + +#include "third_party/npapi/files/include/npupp.h" +#include "plugin/npapi_host_control/win/np_browser_proxy.h" + +class NPBrowserProxy; + +// Class implementing a NPAPI interface wrapper around IDispatchEx automation +// objects. +class DispatchProxy : public NPObject { + public: + DispatchProxy(IDispatchEx* dispatch, + NPBrowserProxy* browser_proxy); + virtual ~DispatchProxy(); + + // Returns the NPAPI interface for accessing the instance of the object. + static NPClass* GetNPClass() { + return &kNPClass; + } + + CComPtr<IDispatchEx> GetDispatchEx() const { + return dispatch_; + } + + void SetBrowserProxy(NPBrowserProxy* browser_proxy) { + browser_proxy_ = browser_proxy; + } + + private: + DispatchProxy() : browser_proxy_(NULL) {} + + // COM object of which this is a proxy. + CComPtr<IDispatchEx> dispatch_; + + // Back pointer to the NPAPI browser environment in which the plugin resides. + NPBrowserProxy* browser_proxy_; + + // Function to convert NPAPI automation identifiers to COM dispatch ID's. + // Parameters: + // name: NPAPI identifier for which we are to find the corresponding + // dispatch ID. + // flags: Flags to pass to GetDispID. + // Returns: + // A valid dispatch-id if the corresponding member or property exists on + // the hosted automation object, -1 otherwise. + DISPID GetDispatchId(NPIdentifier name, DWORD flags) const; + + // The following set of static methods implement the NPAPI object interface + // for DispatchProxy objects. + + // Function to determine the existance of a scriptable method. + // Parameters: + // header: 'this' for the function call. + // name: NPAPI dispatch identifier associated with the method name. + // Returns: + // true if the method is found. + static bool HasMethod(NPObject *header, NPIdentifier name); + + // Invokes a scriptable method on the object with the given arguments, and + // returns a variant result. + // Parameters: + // header: 'this' for the function call. + // name: NPAPI dispatch identifier associated with the method name. + // args: Array of NPVariant objects used as arguments for the method call. + // arg_count: Number of arguments in args. + // result: Pointer to variant receiving the return value of the method. + // Returns: + // true on successfull invocation of an existing method, false otherwise. + static bool InvokeEntry(NPObject *header, NPIdentifier name, + const NPVariant *args, uint32_t arg_count, + NPVariant *result); + + // Invokes the object with the given arguments, and returns a variant result. + // Parameters: + // header: function object to call. + // args: Array of NPVariant objects used as arguments for the method call. + // arg_count: Number of arguments in args. + // result: Pointer to variant receiving the return value of the method. + // Returns: + // true on successfull invocation of an existing method, false otherwise. + static bool InvokeDefault(NPObject *header, const NPVariant *args, + uint32_t argument_count, NPVariant *result); + + // Invokes an object as a constructor function with the given arguments, and + // returns a variant result. + // Parameters: + // header: function object to call. + // args: Array of NPVariant objects used as arguments for the method call. + // arg_count: Number of arguments in args. + // result: Pointer to variant receiving the return value of the method. + // Returns: + // true on successfull invocation of an existing method, false otherwise. + static bool Construct(NPObject *header, const NPVariant *args, + uint32_t argument_count, NPVariant *result); + + // Determines the existence of a scriptable property on the object instance. + // Parameters: + // header: 'this' for the function call. + // name: NPAPI dispatch identifier associated with the propertyname. + // Returns: + // true if the property exists. + static bool HasProperty(NPObject *header, NPIdentifier name); + + // Returns the value of a scriptable property. + // Parameters: + // header: 'this' for the function call. + // name: NPAPI dispatch identifier associated with the propertyname. + // variant: Contains the value of the property upon return. + // Returns: + // true if the property is successfully assigned. + static bool GetPropertyEntry(NPObject *header, NPIdentifier name, + NPVariant *variant); + + // Assigns the value of a scriptable property. + // Parameters: + // header: 'this' for the function call. + // name: NPAPI dispatch identifier associated with the propertyname. + // variant: Value to which the property is to be assigned. + // Returns: + // true if the property is successfully assigned. + static bool SetPropertyEntry(NPObject *header, NPIdentifier name, + const NPVariant *variant); + + // Removes a scriptable property. + // Parameters: + // header: object to remove property from. + // name: NPAPI dispatch identifier associated with the propertyname. + // Returns: + // true if the property is successfully assigned. + static bool RemovePropertyEntry(NPObject *header, NPIdentifier name); + + // Returns an enumeration of all properties present on the instance object. + // Parameters: + // header: 'this' for the function call. + // value: Returned pointer to an array of NPidentifers for all of the + // properties on the object. + // count: The number of elements in the value array. + // Returns: + // false always. This routine is not implemented. + static bool EnumeratePropertyEntries(NPObject *header, NPIdentifier **value, + uint32_t *count); + + // Custom class allocation routine. + // Parameters: + // npp: The plugin instance data. + // class_functions: The NPClass function table for the class to construct. + static NPObject * Allocate(NPP npp, NPClass *class_functions); + + // Custom destruction routine. + static void Deallocate(NPObject *obj); + + // Static V-Table instance for the NPAPI interface for DispatchProxy objects. + static NPClass kNPClass; + + DISALLOW_COPY_AND_ASSIGN(DispatchProxy); +}; + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_DISPATCH_PROXY_H_ diff --git a/o3d/plugin/npapi_host_control/win/host_control.cc b/o3d/plugin/npapi_host_control/win/host_control.cc new file mode 100644 index 0000000..87f2303 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/host_control.cc @@ -0,0 +1,590 @@ +// Copyright 2009, Google Inc. All rights reserved. +// Portions of this file were adapted from the Mozilla project. +// See https://developer.mozilla.org/en/ActiveX_Control_for_Hosting_Netscape_Plug-ins_in_IE +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adam Lock <adamlock@eircom.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "plugin/npapi_host_control/win/host_control.h" + +#include <mshtml.h> + +#include "plugin/npapi_host_control/win/np_plugin_proxy.h" +#include "plugin/npapi_host_control/win/np_object_proxy.h" +#include "plugin/npapi_host_control/win/stream_operation.h" + +namespace { + +// Helper function converting UTF-8 encoded input-string to a NULL-terminated +// wide-character array. +HRESULT ConvertMultiByteToWideChar(const char* input_string, + scoped_array<wchar_t>* wide_string) { + if (!wide_string || !input_string) { + return E_INVALIDARG; + } + + int required_size = 0; + required_size = MultiByteToWideChar(CP_UTF8, 0, input_string, -1, NULL, 0); + // Add one extra element so that we may explicitly null-terminate the string. + wide_string->reset(new wchar_t[required_size + 1]); + if (!wide_string->get()) { + return E_FAIL; + } + MultiByteToWideChar(CP_UTF8, 0, input_string, -1, wide_string->get(), + required_size + 1); + (*wide_string)[required_size] = 0; + return S_OK; +} + +// Helper function that checks a user agent string for 'MSIE', the indicator for +// Internet Explorer. +bool IsMSIE(const char* user_agent) { + ATLASSERT(user_agent); + if (!user_agent) { + return false; + } + static const char* kMSIETag = "MSIE"; + return strstr(user_agent, kMSIETag) != NULL; +} + +} // unnamed namespace + +CHostControl::CHostControl() + : browser_proxy_(NULL), + plugin_proxy_(NULL), + embedded_name_(NULL), + user_agent_(NULL) { + // Request that this control be windowed. + m_bWindowOnly = true; +} + +CHostControl::~CHostControl() { +} + +STDMETHODIMP CHostControl::InterfaceSupportsErrorInfo(REFIID riid) { + static const IID* arr[] = { + &IID_IHostControl, + }; + + for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { + if (InlineIsEqualGUID(*arr[i], riid)) { + return S_OK; + } + } + return S_FALSE; +} + + +const char* CHostControl::GetUserAgent() const { + // Capture the user agent on the first call. + if (!user_agent_.get()) { + CComBSTR user_agent; + HRESULT hr = E_FAIL; + if (NULL != static_cast<IOmNavigator*>(navigator_)) { + hr = navigator_->get_userAgent(&user_agent); + } + if (SUCCEEDED(hr)) { + // Convert to UTF-8 and null terminate the string. + int size_required = WideCharToMultiByte(CP_UTF8, 0, user_agent, -1, + NULL, NULL, NULL, NULL); + user_agent_.reset(new char[size_required + 1]); + WideCharToMultiByte(CP_UTF8, 0, user_agent, -1, + user_agent_.get(), size_required + 1, NULL, NULL); + user_agent_[size_required] = 0; + } + } + + return user_agent_.get(); +} + +LRESULT CHostControl::OnCreate(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL &bHandled) { + NPWindow window = {0}; + window.window = m_hWnd; + CREATESTRUCT *create_struct = reinterpret_cast<CREATESTRUCT*>(lParam); + window.type = NPWindowTypeWindow; + window.x = create_struct->x; + window.y = create_struct->y; + window.width = create_struct->cx; + window.height = create_struct->cy; + + // Get the web browser through the site the control is attached to. + // Note: The control could be running in some other container than IE + // so code shouldn't expect this function to work all the time. + service_provider_ = m_spClientSite; + if (service_provider_ == NULL) + return -1; + + if (FAILED(service_provider_->QueryService(IID_IWebBrowserApp, + &web_browser_app_))) { + return -1; + } + + // Navigate the ActiveX interface hierarchy to the IOmNavigator interface. + CComPtr<IDispatch> dispatch; + if (FAILED(web_browser_app_->get_Document(&dispatch))) { + return -1; + } + + if (FAILED(dispatch->QueryInterface(&document_dispatch_))) { + return -1; + } + + if (FAILED(document_dispatch_->QueryInterface(&html_document2_))) { + return -1; + } + + if (FAILED(document_dispatch_->QueryInterface(&html_document3_))) { + return -1; + } + + if (FAILED(html_document2_->get_parentWindow(&html_window_))) { + return -1; + } + + if (FAILED(html_window_->get_navigator(&navigator_))) { + return -1; + } + + // Only permit the control to create an instance of the hosted plug-in iff + // we are presently running in Internet Explorer. + if (!IsMSIE(GetUserAgent())) { + TearDown(); + return -1; + } + + if (FAILED(html_window_->QueryInterface(&window_dispatch_))) { + return -1; + } + + // Construct and cache a moniker for the url of the page where the plugin + // is hosted. + CComBSTR url_string; + if (FAILED(html_document2_->get_URL(&url_string))) { + return -1; + } + + if (FAILED(CreateURLMonikerEx(NULL, url_string, &url_moniker_, + URL_MK_UNIFORM))) { + return -1; + } + + browser_proxy_.reset(new NPBrowserProxy(this, window_dispatch_)); + if (!plugin_proxy_->Init(browser_proxy_.get(), + window, + plugin_argument_names_, + plugin_argument_values_)) { + browser_proxy_.reset(); + return -1; + } + + return 0; +} + +void CHostControl::TearDown() { + if (embedded_name_) { + SysFreeString(embedded_name_); + embedded_name_ = NULL; + } + + // Note: We do not delete the plug-in instance here, because we can + // re-initialize it on the subsequent WM_CREATE message. + if (plugin_proxy_.get()) { + plugin_proxy_->TearDown(); + } + + if (browser_proxy_.get()) { + browser_proxy_->TearDown(); + } + + browser_proxy_.reset(); + user_agent_.reset(); + + url_moniker_ = NULL; + window_dispatch_ = NULL; + navigator_ = NULL; + html_window_ = NULL; + html_document3_ = NULL; + html_document2_ = NULL; + document_dispatch_ = NULL; + web_browser_app_ = NULL; + service_provider_.Release(); +} + +LRESULT CHostControl::OnDestroy(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL &bHandled) { + // OnDestroy processing does not imply that the plug-in is to be permanently + // destroyed - IE will send WM_CREATE, WM_DESTROY message pairs multiple times + // to the same control instance as it is moved throughout the DOM. + // We tear-down the object entirely here, so that it can be fully + // reconstructed, if necessary, on the next WM_CREATE. + TearDown(); + + return 0; +} + + +HRESULT CHostControl::FinalConstruct() { + return ConstructPluginProxy(); +} + + +void CHostControl::FinalRelease() { + plugin_proxy_.reset(); +} + +HRESULT CHostControl::OpenUrlStream(const wchar_t *url, void *notify_data) { + return StreamOperation::OpenURL(plugin_proxy_.get(), url, notify_data); +} + +STDMETHODIMP CHostControl::GetTypeInfoCount(UINT *pctinfo) { + if (!pctinfo) { + return E_POINTER; + } else { + *pctinfo = 0; + } + return S_OK; +} + +STDMETHODIMP CHostControl::GetTypeInfo(UINT itinfo, + LCID lcid, + ITypeInfo **pptinfo) { + return E_NOTIMPL; +} + +STDMETHODIMP CHostControl::GetIDsOfNames(REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgdispid) { + // Forward all requests through the typelib before defaulting to the + // NPAPI plugin. + HRESULT hr = DispatchImpl::GetIDsOfNames(riid, rgszNames, cNames, + lcid, rgdispid); + if (SUCCEEDED(hr)) { + return hr; + } + + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->GetIDsOfNames(riid, rgszNames, cNames, lcid, + rgdispid); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::Invoke(DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pdispparams, + VARIANT *pvarResult, + EXCEPINFO *pexcepinfo, + UINT *puArgErr) { + // Forward all Invoke requests through the typelib first. + HRESULT hr = DispatchImpl::Invoke(dispidMember, riid, lcid, wFlags, + pdispparams, pvarResult, pexcepinfo, + puArgErr); + if (SUCCEEDED(hr)) { + return hr; + } + + // Disregard reserved dispatch-ids corresponding to VB/OLE. + if (static_cast<int>(dispidMember) < 0) + return E_FAIL; + + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->Invoke(dispidMember, riid, lcid, wFlags, + pdispparams, pvarResult, pexcepinfo, + puArgErr); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::DeleteMemberByDispID(DISPID id) { + HRESULT hr; + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->DeleteMemberByDispID(id); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::DeleteMemberByName(BSTR bstrName, DWORD grfdex) { + HRESULT hr; + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->DeleteMemberByName(bstrName, grfdex); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::GetDispID(BSTR bstrName, + DWORD grfdex, + DISPID* pid) { + // Forward all DISPID requests through the typelib before defaulting to the + // to the NPAPI plugin. + HRESULT hr; + if (SUCCEEDED(hr = DispatchImpl::GetIDsOfNames(IID_NULL, + &bstrName, + 1, + LOCALE_SYSTEM_DEFAULT, + pid))) { + return hr; + } + + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->GetDispID(bstrName, grfdex, pid); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::GetMemberName(DISPID id, + BSTR* pbstrName) { + HRESULT hr; + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->GetMemberName(id, pbstrName); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::GetMemberProperties(DISPID id, + DWORD grfdexFetch, + DWORD* pgrfdex) { + HRESULT hr; + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->GetMemberProperties(id, grfdexFetch, pgrfdex); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::GetNameSpaceParent(IUnknown** punk) { + HRESULT hr; + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->GetNameSpaceParent(punk); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::GetNextDispID(DWORD grfdex, + DISPID id, + DISPID* pid) { + HRESULT hr; + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->GetNextDispID(grfdex, id, pid); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +STDMETHODIMP CHostControl::InvokeEx(DISPID id, + LCID lcid, + WORD wFlags, + DISPPARAMS* pdb, + VARIANT* pVarRes, + EXCEPINFO* pei, + IServiceProvider* pspCaller) { + // Forward all InvokeEx requests through the typelib + HRESULT hr = DispatchImpl::Invoke(id, IID_NULL, lcid, wFlags, + pdb, pVarRes, pei, + NULL); + if (SUCCEEDED(hr)) { + return hr; + } + + if (plugin_proxy_.get()) { + CComPtr<INPObjectProxy> script_object; + hr = plugin_proxy_->GetScriptableObject(&script_object); + if (SUCCEEDED(hr)) { + return script_object->InvokeEx(id, lcid, wFlags, pdb, pVarRes, pei, + pspCaller); + } else { + return hr; + } + } else { + return E_FAIL; + } +} + +// Receive the arguments provided to the plug-in in the param-tag. +STDMETHODIMP CHostControl::Load(IPropertyBag* property_bag, + IErrorLog* error_log) { + if (!property_bag) { + return E_INVALIDARG; + } + + // Iterate through all of the properties provided, and register them, in + // ASCII-string form with the control. + CComQIPtr<IPropertyBag2> property_bag2 = property_bag; + if (property_bag2) { + ULONG property_count; + if (SUCCEEDED(property_bag2->CountProperties(&property_count))) { + for (int x = 0; x < property_count; ++x) { + PROPBAG2 property = {0}; + ULONG properties_read = 0; + if (SUCCEEDED(property_bag2->GetPropertyInfo(x, 1, &property, + &properties_read))) { + CComVariant variant; + HRESULT prop_hr; + if (SUCCEEDED(property_bag2->Read(1, &property, NULL, + &variant, &prop_hr))) { + if (SUCCEEDED(variant.ChangeType(VT_BSTR))) { + USES_CONVERSION; + RegisterPluginParameter(OLE2A(property.pstrName), + OLE2A(variant.bstrVal)); + } + } + // According to the msdn documentation, the name of the property + // must be freed through CoTaskMemFree. + // See: http://msdn.microsoft.com/en-us/library/aa768191(VS.85).aspx + CoTaskMemFree(property.pstrName); + } + } + } + } + + return IPersistPropertyBagImpl<CHostControl>::Load(property_bag, error_log); +} + +HRESULT CHostControl::GetStringProperty(NPPVariable np_property_variable, + BSTR* string) { + HRESULT hr; + if (FAILED(hr = ConstructPluginProxy())) { + return hr; + } + + char* property = NULL; + if (NPERR_NO_ERROR != plugin_proxy_->GetPluginFunctions()->getvalue( + NULL, + np_property_variable, + &property)) { + return E_FAIL; + } + + scoped_array<wchar_t> wide_property; + if (FAILED(hr = ConvertMultiByteToWideChar(property, &wide_property))) { + return hr; + } + *string = SysAllocString(wide_property.get()); + return S_OK; +} + +STDMETHODIMP CHostControl::get_description(BSTR *returned_description) { + return GetStringProperty(NPPVpluginDescriptionString, returned_description); +} + +STDMETHODIMP CHostControl::get_name(BSTR *returned_name) { + return GetStringProperty(NPPVpluginNameString, returned_name); +} + +HRESULT CHostControl::ConstructPluginProxy() { + // If the plugin has already been constructed, then exit early. + if (plugin_proxy_.get()) { + return S_OK; + } + + HRESULT hr; + NPPluginProxy* plugin_proxy = NULL; + if (FAILED(hr = NPPluginProxy::Create(&plugin_proxy))) { + return hr; + } + + plugin_proxy_.reset(plugin_proxy); + return S_OK; +} diff --git a/o3d/plugin/npapi_host_control/win/host_control.h b/o3d/plugin/npapi_host_control/win/host_control.h new file mode 100644 index 0000000..0cb0075 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/host_control.h @@ -0,0 +1,268 @@ +// Copyright 2009, Google Inc. All rights reserved. +// Portions of this file were adapted from the Mozilla project. +// See https://developer.mozilla.org/en/ActiveX_Control_for_Hosting_Netscape_Plug-ins_in_IE +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adam Lock <adamlock@eircom.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +// Declares a COM class implementing an ActiveX control capable of hosting +// an NPAPI plugin on an OLE site. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_HOST_CONTROL_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_HOST_CONTROL_H_ + +#include <atlctl.h> +#include <dispex.h> +#include <vector> + +#include "base/scoped_ptr.h" + +// Directory not included, as this file is auto-generated from the +// type library. +#include "npapi_host_control.h" +#include "plugin/npapi_host_control/win/np_browser_proxy.h" + +class NPPluginProxy; + +// Class implementing an ActiveX control for containing NPAPI plugin-objects. +class ATL_NO_VTABLE CHostControl + : public CComObjectRootEx<CComSingleThreadModel>, + public CComCoClass<CHostControl, &CLSID_HostControl>, + public CComControl<CHostControl>, + // IMPORTANT IMPLEMENTATION NOTE: + // Pass 0xFFFF to the major and minor versions of the IDispatchImpl + // for trigger the behaviour in CComTypeInfoHolder::GetTI that forces + // the type-library to be loaded from the module, not through the + // registry. + // Without this behaviour, the plug-in fails to load on Vista with UAC + // disabled. This is because all processes run at elevated integrity + // with UAC disabled. Because the plug-in is registered as a per-user + // control (under HKCU), it will fail to load the type-library through + // the registry: Elevated processes do not view the contents of the HKCU + // hive, so it will appear as if the control was not installed properly. + public IDispatchImpl<IHostControl, &IID_IHostControl, + &LIBID_npapi_host_controlLib, + 0xFFFF, + 0xFFFF>, + public IOleControlImpl<CHostControl>, + public IOleObjectImpl<CHostControl>, + public IOleInPlaceActiveObjectImpl<CHostControl>, + public IOleInPlaceObjectWindowlessImpl<CHostControl>, + public ISupportErrorInfo, + public IProvideClassInfo2Impl<&CLSID_HostControl, NULL, + &LIBID_npapi_host_controlLib>, + public IObjectSafetyImpl<CHostControl, + INTERFACESAFE_FOR_UNTRUSTED_CALLER | + INTERFACESAFE_FOR_UNTRUSTED_DATA>, + public IPersistStreamInitImpl<CHostControl>, + public IPersistPropertyBagImpl<CHostControl>, + public IPersistStorageImpl<CHostControl>, + public IConnectionPointContainerImpl<CHostControl>, + public IPropertyNotifySinkCP<CHostControl> { + public: + CHostControl(); + virtual ~CHostControl(); + +DECLARE_OLEMISC_STATUS(OLEMISC_RECOMPOSEONRESIZE | + OLEMISC_CANTLINKINSIDE | + OLEMISC_INSIDEOUT | + OLEMISC_ACTIVATEWHENVISIBLE | + OLEMISC_SETCLIENTSITEFIRST +) + +DECLARE_REGISTRY_RESOURCEID(IDR_HOSTCONTROL) + +BEGIN_MSG_MAP(CHostControl) + MESSAGE_HANDLER(WM_CREATE, OnCreate) + MESSAGE_HANDLER(WM_DESTROY, OnDestroy) +END_MSG_MAP() + +BEGIN_CONNECTION_POINT_MAP(CHostControl) + CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) +END_CONNECTION_POINT_MAP() + +// Register this control as safe for initialization and scripting. If these +// categories are skipped, IE will force the user to permit the control to +// allow scripting at every page view. +BEGIN_CATEGORY_MAP(CHostControl) + IMPLEMENTED_CATEGORY(CATID_SafeForScripting) + IMPLEMENTED_CATEGORY(CATID_SafeForInitializing) +END_CATEGORY_MAP() + +BEGIN_COM_MAP(CHostControl) + COM_INTERFACE_ENTRY(IHostControl) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IDispatchEx) + COM_INTERFACE_ENTRY(IOleInPlaceObject) + COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless) + COM_INTERFACE_ENTRY(IOleInPlaceActiveObject) + COM_INTERFACE_ENTRY(IOleControl) + COM_INTERFACE_ENTRY(IOleObject) + COM_INTERFACE_ENTRY(ISupportErrorInfo) + COM_INTERFACE_ENTRY(IProvideClassInfo) + COM_INTERFACE_ENTRY(IProvideClassInfo2) + COM_INTERFACE_ENTRY(IPersistStreamInit) + COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit) + COM_INTERFACE_ENTRY(IPersistPropertyBag) + COM_INTERFACE_ENTRY(IPersistStorage) + COM_INTERFACE_ENTRY(IConnectionPointContainer) +END_COM_MAP() + +BEGIN_PROP_MAP(CHostControl) +END_PROP_MAP() + + STDMETHOD(GetTypeInfoCount)(UINT* pctinfo); + STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); + STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, + LCID lcid, DISPID* rgdispid); + STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS* pdispparams, VARIANT* pvarResult, + EXCEPINFO* pexcepinfo, UINT* puArgErr); + STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid); + + STDMETHOD(DeleteMemberByDispID)(DISPID id); + STDMETHOD(DeleteMemberByName)(BSTR bstrName, DWORD grfdex); + STDMETHOD(GetDispID)(BSTR bstrName, DWORD grfdex, DISPID* pid); + STDMETHOD(GetMemberName)(DISPID id, BSTR* pbstrName); + STDMETHOD(GetMemberProperties)(DISPID id, DWORD grfdexFertch, DWORD* pgrfdex); + STDMETHOD(GetNameSpaceParent)(IUnknown** ppunk); + STDMETHOD(GetNextDispID)(DWORD grfdex, DISPID id, DISPID* pid); + STDMETHOD(InvokeEx)(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS* pdp, + VARIANT* pVarRes, EXCEPINFO* pei, + IServiceProvider* pspCaller); + + // Method overridden from IPersistPropertyBagImpl. + STDMETHOD(Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog)); + + // Returns the properties associated with the NPPVpluginNameString, and + // NPPVpluginDescriptionString identifiers of the loaded plug-in. These + // properties can be used for plug-in introspection and version-dependent + // behaviour. + STDMETHOD(get_description)(BSTR *returned_description); + STDMETHOD(get_name)(BSTR *returned_name); + + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + + // Initiates a data transfer, calling back into the hosted plug-in instance + // on status updates. Does not block on the transfer. + // Parameters: + // url: The url from which to receive data. + // notify_data: Opaque handle to data provided by the plug-in instance. + // This data will be passed back to the plug-in during + // all call-backs. + // Returns: + // S_OK on success. + HRESULT OpenUrlStream(const wchar_t *url, void *notify_data); + + // Returns the user-agent string of the browser hosting the control. + // Returns NULL on failure. + const char* GetUserAgent() const; + + // Return a moniker representing the url of the page in which the plugin is + // contained. + IMoniker* GetURLMoniker() const { + return url_moniker_; + } + + DECLARE_PROTECT_FINAL_CONSTRUCT() + HRESULT FinalConstruct(); + void FinalRelease(); + + private: + // Performs all of the basic construction of the hosted NPPluginProxy object, + // but does not initialize an active instance of the plug-in. + HRESULT ConstructPluginProxy(); + + // Returns an NPAPI property from the hosted plug-in. + // Parameters: + // np_property_variable: NPPVariable value corresponding to the property + // to return. + // string: Pointer to BString that receives the property. + // Returns: + // S_OK on success. + HRESULT GetStringProperty(NPPVariable np_property_variable, BSTR* string); + + // Free all resources allocated when constructing the windowed instance + // of the hosted plug-in in OnCreate(...). + void TearDown(); + + void RegisterPluginParameter(const char *name, const char *value) { + ATLASSERT(name && value); + plugin_argument_names_.push_back(CStringA(name)); + plugin_argument_values_.push_back(CStringA(value)); + } + + // Browser proxy instance used to communicate with the hosted NPAPI plugin. + scoped_ptr<NPBrowserProxy> browser_proxy_; + + // Pointer to the plugin being hosted by the control. + scoped_ptr<NPPluginProxy> plugin_proxy_; + + // Cached value of the name of the control as it exists in the HTML DOM. + BSTR embedded_name_; + + // Cached string representation of the user-agent, initialized by first call + // to GetUserAgent. + mutable scoped_array<char> user_agent_; + + CComPtr<IWebBrowserApp> web_browser_app_; + CComQIPtr<IServiceProvider, &IID_IServiceProvider> service_provider_; + CComPtr<IDispatchEx> document_dispatch_; + CComPtr<IHTMLDocument2> html_document2_; + CComPtr<IHTMLDocument3> html_document3_; + CComPtr<IDispatchEx> window_dispatch_; + CComPtr<IHTMLWindow2> html_window_; + CComPtr<IOmNavigator> navigator_; + CComPtr<IMoniker> url_moniker_; + + // Array of strings to be passed as name/value arguments to the NPAPI + // plug-in instance during construction in NPP_New. + std::vector<CStringA> plugin_argument_names_; + std::vector<CStringA> plugin_argument_values_; + + typedef IDispatchImpl<IHostControl, &IID_IHostControl, + &LIBID_npapi_host_controlLib, + 0xFFFF, 0xFFFF> DispatchImpl; + + DISALLOW_COPY_AND_ASSIGN(CHostControl); +}; + +// Register this COM class with the COM module. +OBJECT_ENTRY_AUTO(__uuidof(HostControl), CHostControl); + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_HOST_CONTROL_H_ diff --git a/o3d/plugin/npapi_host_control/win/host_control.rgs b/o3d/plugin/npapi_host_control/win/host_control.rgs new file mode 100644 index 0000000..5aa4a7e --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/host_control.rgs @@ -0,0 +1,42 @@ +HKCU +{ + Software + { + Classes + { + o3d_host.O3DHostControl.1 = s 'O3DHostControl Class' + { + CLSID = s '{9666A772-407E-4F90-BC37-982E8160EB2D}' + 'Insertable' + } + o3d_host.O3DHostControl = s 'O3DHostControl Class' + { + CLSID = s '{9666A772-407E-4F90-BC37-982E8160EB2D}' + CurVer = s 'o3d_host.O3DHostControl.1' + } + NoRemove CLSID + { + ForceRemove {9666A772-407E-4F90-BC37-982E8160EB2D} = s 'O3DHostControl Class' + { + ProgID = s 'o3d_host.O3DHostControl.1' + VersionIndependentProgID = s 'o3d_host.O3DHostControl' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + val AppID = s '%APPID%' + ForceRemove 'Control' + ForceRemove 'Insertable' + ForceRemove 'ToolboxBitmap32' = s '%MODULE%, 102' + 'MiscStatus' = s '0' + { + '1' = s '%OLEMISC%' + } + 'TypeLib' = s '{D4F6E31C-E952-48FE-9833-6AE308BD79C6}' + 'Version' = s '1.0' + } + } + } + } +} diff --git a/o3d/plugin/npapi_host_control/win/module.h b/o3d/plugin/npapi_host_control/win/module.h new file mode 100644 index 0000000..5074504 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/module.h @@ -0,0 +1,68 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the ATL module class used by the O3D host ActiveX control. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_MODULE_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_MODULE_H_ + +#include "npapi_host_control.h" + +class NPAPIHostControlModule : public CAtlDllModuleT<NPAPIHostControlModule> { + public: + NPAPIHostControlModule() { InitializeCriticalSection(&cs_); } + virtual ~NPAPIHostControlModule() { DeleteCriticalSection(&cs_); } + + // Routine used to serialize threads executing within the control. Enters + // a critical section shared for the process hosting the control. + static void LockModule() { + EnterCriticalSection(&GetGlobalInstance()->cs_); + } + + // Routine used to serialize threads executing within the control. Leaves + // the critical section entered in lock_module(). + static void UnlockModule() { + LeaveCriticalSection(&GetGlobalInstance()->cs_); + } + + // Accessor routine for the global pointer _pAtlModule maintained by ATL. + static NPAPIHostControlModule* GetGlobalInstance() { + return static_cast<NPAPIHostControlModule*>(_pAtlModule); + } + + DECLARE_LIBID(LIBID_npapi_host_controlLib) + private: + CRITICAL_SECTION cs_; + DISALLOW_COPY_AND_ASSIGN(NPAPIHostControlModule); +}; + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_MODULE_H_ diff --git a/o3d/plugin/npapi_host_control/win/np_browser_proxy.cc b/o3d/plugin/npapi_host_control/win/np_browser_proxy.cc new file mode 100644 index 0000000..8d93be9 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/np_browser_proxy.cc @@ -0,0 +1,812 @@ +// Copyright 2009, Google Inc. All rights reserved. +// Portions of this file were adapted from the Mozilla project. +// See https://developer.mozilla.org/en/ActiveX_Control_for_Hosting_Netscape_Plug-ins_in_IE +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adam Lock <adamlock@eircom.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <atlstr.h> + +#include <string> +#include <set> + +#include "plugin/npapi_host_control/win/host_control.h" +#include "plugin/npapi_host_control/win/variant_utils.h" + +namespace { + +// Helper routine that invokes the host-control stream request function. +NPError OpenURL(NPBrowserProxy* browser_proxy, + const char *szURL, + const char *szTarget, + void *pNotifyData) { + CHostControl* host_control = browser_proxy->GetHostingControl(); + + USES_CONVERSION; + HRESULT hr = host_control->OpenUrlStream(A2CW(szURL), pNotifyData); + return SUCCEEDED(hr) ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR; +} +} // unnamed namespace + +NPNetscapeFuncs NPBrowserProxy::kNetscapeFunctions = { + sizeof(kNetscapeFunctions), + NP_VERSION_MAJOR << 8 | NP_VERSION_MINOR, + NPN_GetURL, + NPN_PostURL, + NPN_RequestRead, + NPN_NewStream, + NPN_Write, + NPN_DestroyStream, + NPN_Status, + NPN_UserAgent, + NPN_MemAlloc, + NPN_MemFree, + NPN_MemFlush, + NPN_ReloadPlugins, + NPN_GetJavaEnv, + NPN_GetJavaPeer, + NPN_GetURLNotify, + NPN_PostURLNotify, + NPN_GetValue, + NPN_SetValue, + NPN_InvalidateRect, + NPN_InvalidateRegion, + NPN_ForceRedraw, + NPN_GetStringIdentifier, + NPN_GetStringIdentifiers, + NPN_GetIntIdentifier, + NPN_IdentifierIsString, + NPN_UTF8FromIdentifier, + NPN_IntFromIdentifier, + NPN_CreateObject, + NPN_RetainObject, + NPN_ReleaseObject, + NPN_Invoke, + NPN_InvokeDefault, + NPN_Evaluate, + NPN_GetProperty, + NPN_SetProperty, + NPN_RemoveProperty, + NPN_HasProperty, + NPN_HasMethod, + NPN_ReleaseVariantValue, + NPN_SetException, + NULL, + NULL, + NPN_Enumerate, +}; + +NPBrowserProxy::NPBrowserProxy(CHostControl* host, IDispatchEx* window_dispatch) + : host_control_(host) { + vwindow_object_ = new DispatchProxy(window_dispatch, this); + + CComPtr<IUnknown> unknown_identity; + HRESULT hr = window_dispatch->QueryInterface(&unknown_identity); + ATLASSERT(SUCCEEDED(hr)); + + dispatch_proxy_map_[unknown_identity] = vwindow_object_; + call_identifier_ = NPN_GetStringIdentifier("call"); +} + +NPBrowserProxy::~NPBrowserProxy() { + for (NPObjectProxyMap::iterator it = np_object_proxy_map_.begin(); + it != np_object_proxy_map_.end(); ++it) { + it->second->SetBrowserProxy(NULL); + } + for (DispatchProxyMap::iterator it = dispatch_proxy_map_.begin(); + it != dispatch_proxy_map_.end(); ++it) { + it->second->SetBrowserProxy(NULL); + GetBrowserFunctions()->releaseobject(it->second); + } +} + +CComPtr<IDispatchEx> NPBrowserProxy::GetDispatchObject(NPObject* np_object) { + if (np_object == NULL) { + return CComPtr<IDispatchEx>(); + } + + // If the NPObject is already wrapping an IDispatchEx interface, then + // return that interface directly. + if (np_object->_class == DispatchProxy::GetNPClass()) { + DispatchProxy *dispatch_proxy = + static_cast<DispatchProxy*>(np_object); + return dispatch_proxy->GetDispatchEx(); + } else { + // If the NPObject already has a proxy then return that. + NPObjectProxyMap::iterator it = np_object_proxy_map_.find(np_object); + if (it != np_object_proxy_map_.end()) { + CComPtr<IDispatchEx> dispatch_ex(NULL); + HRESULT hr = it->second.QueryInterface(&dispatch_ex); + if (SUCCEEDED(hr)) { + return dispatch_ex; + } else { + return NULL; + } + } + + // Create a new NPObject proxy, register it for future use and return it. + CComPtr<INPObjectProxy> proxy_wrapper; + HRESULT hr = NPObjectProxy::CreateInstance(&proxy_wrapper); + if (SUCCEEDED(hr)) { + proxy_wrapper->SetBrowserProxy(this); + proxy_wrapper->SetHostedObject(np_object); + RegisterNPObjectProxy(np_object, proxy_wrapper); + + CComPtr<IDispatchEx> dispatch_proxy_wrapper; + hr = proxy_wrapper.QueryInterface(&dispatch_proxy_wrapper); + ATLASSERT(SUCCEEDED(hr)); + return dispatch_proxy_wrapper; + } + } + return CComPtr<IDispatchEx>(); +} + +NPObject* NPBrowserProxy::GetNPObject(IDispatch* dispatch_object) { + if (dispatch_object == NULL) + return NULL; + + // If the COM object is already wrapping an NPObject then return that NPObject + // directly. + NPObject* np_object; + CComPtr<INPObjectProxy> np_object_proxy; + if (SUCCEEDED(dispatch_object->QueryInterface(&np_object_proxy))) { + if (SUCCEEDED(np_object_proxy->GetNPObjectInstance( + reinterpret_cast<void**>(&np_object)))) + return np_object; + else + return NULL; + } else { + CComPtr<IUnknown> unknown_identity; + if (FAILED(dispatch_object->QueryInterface(&unknown_identity))) { + return NULL; + } + + // If the COM object already has a proxy then return that. Note that the + // map is keyed on IUnknown ptrs - this is because COM explicitly states + // that the IUnknown interface is the only reliable identity mechanism. + DispatchProxyMap::iterator it = dispatch_proxy_map_.find(unknown_identity); + if (it != dispatch_proxy_map_.end()) { + GetBrowserFunctions()->retainobject(it->second); + return it->second; + } + + // Create a new DispatchProxy. + CComPtr<IDispatchEx> dispatchex_object; + if (FAILED(dispatch_object->QueryInterface(&dispatchex_object))) { + return NULL; + } + DispatchProxy* dispatch_proxy = new DispatchProxy(dispatchex_object, + this); + dispatch_proxy_map_[unknown_identity] = dispatch_proxy; + return dispatch_proxy; + } +} + +void NPBrowserProxy::RegisterNPObjectProxy( + NPObject* np_object, + const CComPtr<INPObjectProxy>& proxy_wrapper) { + np_object_proxy_map_[np_object] = proxy_wrapper; +} + +void NPBrowserProxy::UnregisterNPObjectProxy(NPObject* np_object) { + np_object_proxy_map_.erase(np_object); +} + +void NPBrowserProxy::UnregisterDispatchProxy(IDispatchEx* dispatch_object) { + CComPtr<IUnknown> unknown_identity; + HRESULT hr = dispatch_object->QueryInterface(&unknown_identity); + ATLASSERT(SUCCEEDED(hr)); + if (!SUCCEEDED(hr)) { + return; + } + + DispatchProxyMap::iterator it = dispatch_proxy_map_.find(unknown_identity); + ATLASSERT(it != dispatch_proxy_map_.end()); + GetBrowserFunctions()->releaseobject(it->second); + dispatch_proxy_map_.erase(it); +} + +NPError NPBrowserProxy::NPN_GetURL(NPP npp, + const char* relativeURL, + const char* target) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + ATLASSERT(false && "NPN_GetURL not implemented"); + return NPERR_NO_ERROR; +} + + +NPError NPBrowserProxy::NPN_GetURLNotify(NPP npp, + const char* relativeURL, + const char* target, + void* notifyData) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + + NPBrowserProxy *browser_proxy = static_cast<NPBrowserProxy*>(npp->ndata); + return OpenURL(browser_proxy, relativeURL, target, notifyData); +} + + + +NPError NPBrowserProxy::NPN_PostURLNotify(NPP npp, + const char *relativeURL, + const char *target, + uint32 len, + const char *buf, + NPBool file, + void *notifyData) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + ATLASSERT(false && "NPN_PostURLNotify not implemented."); + return NPERR_NO_ERROR; +} + +NPError NPBrowserProxy::NPN_PostURL(NPP npp, + const char *relativeURL, + const char *target, + uint32 len, + const char *buf, + NPBool file) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + ATLASSERT(false && "NPN_PostURL not implemented."); + return NPERR_NO_ERROR; +} + +NPError NPBrowserProxy::NPN_NewStream(NPP npp, + NPMIMEType type, + const char* window, + NPStream* *result) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + ATLASSERT(false && "NPN_NewStream not implemented."); + return NPERR_GENERIC_ERROR; +} + + +int32 NPBrowserProxy::NPN_Write(NPP npp, + NPStream *pstream, + int32 len, + void *buffer) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + ATLASSERT(false && "NPN_Write not implemented."); + return NPERR_GENERIC_ERROR; +} + +NPError NPBrowserProxy::NPN_DestroyStream(NPP npp, + NPStream *pstream, + NPError reason) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + ATLASSERT(false && "NPN_DestroyStream not implemented."); + return NPERR_GENERIC_ERROR; +} + +void NPBrowserProxy::NPN_Status(NPP npp, const char *message) { + if (!npp) { + return; + } +} + +void *NPBrowserProxy::NPN_MemAlloc(uint32 size) { + return malloc(size); +} + +void NPBrowserProxy::NPN_MemFree(void *ptr) { + if (ptr) { + free(ptr); + } +} + +uint32 NPBrowserProxy::NPN_MemFlush(uint32 size) { + return 0; +} + +void NPBrowserProxy::NPN_ReloadPlugins(NPBool reloadPages) { + ATLASSERT(false && "NPN_ReloadPlugins not implemented."); +} + +void NPBrowserProxy::NPN_InvalidateRect(NPP npp, NPRect *invalidRect) { + if (!npp) { + return; + } + ATLASSERT(false && "NPN_InvalidateRect not implemented."); +} + +void NPBrowserProxy::NPN_InvalidateRegion(NPP npp, NPRegion invalidRegion) { + if (!npp) { + return; + } + ATLASSERT(false && "NPN_InvalidateRect not implemented."); +} + +void NPBrowserProxy::NPN_ForceRedraw(NPP npp) { + if (!npp) { + return; + } + ATLASSERT(false && "NPN_ForceRedraw not implemented."); +} + +NPError NPBrowserProxy::NPN_GetValue(NPP npp, + NPNVariable variable, + void *result) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + + if (!result) { + return NPERR_INVALID_PARAM; + } + + NPBrowserProxy *browser_proxy = static_cast<NPBrowserProxy*>(npp->ndata); + ATLASSERT(browser_proxy); + + switch (variable) { + case NPNVxDisplay : + return NPERR_GENERIC_ERROR; + case NPNVnetscapeWindow: + *(static_cast<HWND*>(result)) = + browser_proxy->GetHostingControl()->m_hWnd; + break; + case NPNVjavascriptEnabledBool : + *(static_cast<NPBool*>(result)) = TRUE; + break; + case NPNVasdEnabledBool : + *(static_cast<NPBool*>(result)) = FALSE; + break; + case NPNVisOfflineBool : + *(reinterpret_cast<NPBool*>(result)) = FALSE; + break; + case NPNVWindowNPObject : + ++browser_proxy->GetVWindowObject()->referenceCount; + *(static_cast<NPObject**>(result)) = + browser_proxy->GetVWindowObject(); + break; + case NPNVPluginElementNPObject : + ATLASSERT(false && "NPNVPluginElementNPObject not supported."); + return NPERR_GENERIC_ERROR; + default: + ATLASSERT(false && "Unrecognized NPN_GetValue request."); + return NPERR_GENERIC_ERROR; + } + + return NPERR_NO_ERROR; +} + +NPError NPBrowserProxy::NPN_SetValue(NPP npp, + NPPVariable variable, + void *result) { + if (!npp) { + return NPERR_INVALID_INSTANCE_ERROR; + } + ATLASSERT(false && "NPN_SetValue not implemented."); + return NPERR_GENERIC_ERROR; +} + +NPError NPBrowserProxy::NPN_RequestRead(NPStream *pstream, + NPByteRange *rangeList) { + if (!pstream || !rangeList || !pstream->ndata) { + return NPERR_INVALID_PARAM; + } + ATLASSERT(false && "NPN_RequestRead not implemented."); + return NPERR_GENERIC_ERROR; +} + +void* NPBrowserProxy::NPN_GetJavaEnv() { + return NULL; +} + + +const char* NPBrowserProxy::NPN_UserAgent(NPP npp) { + if (!npp) { + return ""; + } + + NPBrowserProxy *browser_proxy = static_cast<NPBrowserProxy*>(npp->ndata); + ATLASSERT(browser_proxy); + return browser_proxy->GetHostingControl()->GetUserAgent(); +} + +void* NPBrowserProxy::NPN_GetJavaClass(void* handle) { + return NULL; +} + +void* NPBrowserProxy::NPN_GetJavaPeer(NPP npp) { + return NULL; +} + +NPObject* NPBrowserProxy::NPN_CreateObject(NPP npp, + NPClass *aClass) { + if (!npp || !aClass) { + return NULL; + } + + NPObject *new_object = NULL; + + // If the class exports a custom allocation routine, then invoke that. + if (aClass->allocate) { + new_object = aClass->allocate(npp, aClass); + new_object->_class = aClass; + } else { + new_object = new NPObject; + new_object->_class = aClass; + } + + new_object->referenceCount = 1; + + return new_object; +} + +NPObject * NPBrowserProxy::NPN_RetainObject(NPObject *obj) { + if (obj) { + ++obj->referenceCount; + } + return obj; +} + + +void NPBrowserProxy::NPN_ReleaseObject(NPObject *object) { + if (object) { + if (0 == --object->referenceCount) { + if (object->_class->deallocate) { + object->_class->deallocate(object); + } else { + delete object; + } + } + } +} + +// Disable warnings concerning reinterpret casts to un-related pointer types +// for the below functions. +#pragma warning(push) +#pragma warning(disable:4312) +#pragma warning(disable:4311) + +namespace { +const uint32_t kNPIdentifierIntFlag = 0x1; +} + +NPIdentifier NPBrowserProxy::NPN_GetStringIdentifier(const NPUTF8 *name) { + // Note that this routine returns the address of the string as it is stored + // in a set. This implies that any virtual address value could be the + // returned identifier. The name & description properties of the CHostControl + // object will not conflict with these, because they are within the VMA region + // that is unmapped. + static std::set<std::string> identifiers; + std::string std_name(name); + std::pair<std::set<std::string>::iterator, bool> result = + identifiers.insert(std_name); + const std::string& key = *(result.first); + uint32_t tag = reinterpret_cast<uint32_t>(&key); + ATLASSERT(0 == (tag & kNPIdentifierIntFlag)); + return reinterpret_cast<NPIdentifier>(tag); +} + +void NPBrowserProxy::NPN_GetStringIdentifiers(const NPUTF8 **names, + int32_t nameCount, + NPIdentifier *identifiers) { + for (int x = 0; x < nameCount; ++x) { + identifiers[x] = NPN_GetStringIdentifier(names[x]); + } +} + +NPUTF8 * NPBrowserProxy::NPN_UTF8FromIdentifier(NPIdentifier identifier) { + ATLASSERT(identifier != NULL); + int32_t tag = reinterpret_cast<uint32_t>(identifier); + if (0 == (tag & kNPIdentifierIntFlag)) { + const std::string* key = reinterpret_cast<const std::string*>(tag); + NPUTF8* identifier_value = static_cast<NPUTF8*>( + NPN_MemAlloc(static_cast<uint32_t>(key->length() + 1))); + memcpy(identifier_value, key->c_str(), key->length() + 1); + return identifier_value; + } else { + // This is not a standard feature of NPN_UTF8FromIdentifier. Normally you + // cannot convert an integer identifier to a string. We support it here + // because IE and COM represent integer identifiers as strings in places. + // For example, if IDispathEx::GetMemberName is invoked with an id for + // an integer indexed property, the only things to do are return a + // string representation of the integer or an error. + CStringA string; + string.Format("%d", tag >> 1); + NPUTF8* identifier_value = static_cast<NPUTF8*>( + NPN_MemAlloc(string.GetLength() + 1)); + memcpy(identifier_value, string.GetBuffer(), string.GetLength() + 1); + return identifier_value; + } +} + +NPIdentifier NPBrowserProxy::NPN_GetIntIdentifier(int32_t intid) { + ATLASSERT(intid <= 0x7FFFFFFF); + return reinterpret_cast<NPIdentifier>((intid << 1) | kNPIdentifierIntFlag); +} + +int32_t NPBrowserProxy::NPN_IntFromIdentifier(NPIdentifier identifier) { + ATLASSERT(identifier != NULL); + int32_t tag = reinterpret_cast<int32_t>(identifier); + ATLASSERT(kNPIdentifierIntFlag == (tag & kNPIdentifierIntFlag)); + return tag >> 1; +} + +bool NPBrowserProxy::NPN_IdentifierIsString(NPIdentifier identifier) { + ATLASSERT(identifier != NULL); + uint32_t tag = reinterpret_cast<uint32_t>(identifier); + return (tag & kNPIdentifierIntFlag) == 0; +} + +#pragma warning(pop) + +void NPBrowserProxy::NPN_ReleaseVariantValue(NPVariant *variant) { + switch (variant->type) { + case NPVariantType_Void: + case NPVariantType_Null: + case NPVariantType_Bool: + case NPVariantType_Int32: + case NPVariantType_Double: + break; + case NPVariantType_String: + NPN_MemFree( + const_cast<NPUTF8*>(variant->value.stringValue.utf8characters)); + break; + case NPVariantType_Object: + NPN_ReleaseObject(variant->value.objectValue); + break; + default: + ATLASSERT(false && "Unrecognized NPVariant type."); + break; + } +} + +bool NPBrowserProxy::NPN_GetProperty(NPP npp, + NPObject *obj, + NPIdentifier propertyName, + NPVariant *result) { + if (!npp || !obj || !obj->_class->getProperty) { + return false; + } + return obj->_class->getProperty(obj, propertyName, result); +} + +bool NPBrowserProxy::NPN_SetProperty(NPP npp, + NPObject *obj, + NPIdentifier propertyName, + const NPVariant *value) { + if (!npp || !obj || !obj->_class->setProperty) { + return false; + } + return obj->_class->setProperty(obj, propertyName, value); +} + +bool NPBrowserProxy::NPN_HasProperty(NPP npp, + NPObject *npobj, + NPIdentifier propertyName) { + if (!npp || !npobj || !npobj->_class->hasProperty) { + return false; + } + return npobj->_class->hasProperty(npobj, propertyName); +} + +bool NPBrowserProxy::NPN_RemoveProperty(NPP npp, + NPObject *npobj, + NPIdentifier propertyName) { + if (!npp || !npobj || !npobj->_class->removeProperty) { + return false; + } + return npobj->_class->removeProperty(npobj, propertyName); +} + +bool NPBrowserProxy::NPN_HasMethod(NPP npp, + NPObject *npobj, + NPIdentifier methodName) { + if (!npp || !npobj || !npobj->_class->hasMethod) { + return false; + } + return npobj->_class->hasMethod(npobj, methodName); +} + +bool NPBrowserProxy::NPN_Invoke(NPP npp, + NPObject *obj, + NPIdentifier methodName, + const NPVariant *args, + unsigned argCount, + NPVariant *result) { + if (!npp || !obj || !obj->_class->invoke) { + return false; + } + return obj->_class->invoke(obj, methodName, args, argCount, result); +} + +bool NPBrowserProxy::NPN_InvokeDefault(NPP npp, + NPObject *obj, + const NPVariant *args, + unsigned argCount, + NPVariant *result) { + if (!npp || !obj || !obj->_class->invokeDefault) { + return false; + } + return obj->_class->invokeDefault(obj, args, argCount, result); +} + +bool NPBrowserProxy::NPN_Construct(NPP npp, + NPObject *obj, + const NPVariant *args, + unsigned argCount, + NPVariant *result) { + if (!npp || !obj || !obj->_class->construct) { + return false; + } + return obj->_class->construct(obj, args, argCount, result); +} + +bool NPBrowserProxy::NPN_Enumerate(NPP npp, + NPObject* obj, + NPIdentifier** ids, + uint32_t* idCount) { + if (!npp || !obj || !obj->_class->enumerate || !ids || !idCount) { + return false; + } + return obj->_class->enumerate(obj, ids, idCount); +} + +// Construct a new JavaScript object using the given global constructor +// and argument values. +bool NPBrowserProxy::ConstructObject(NPP npp, + NPObject* window_object, + NPUTF8* constructor_name, + NPVariant* args, + uint32_t numArgs, + NPObject** result) { + bool success = false; + NPIdentifier constructor_identifier = NPN_GetStringIdentifier( + constructor_name); + NPVariant constructor_variant; + if (NPN_GetProperty(npp, window_object, constructor_identifier, + &constructor_variant)) { + if (NPVARIANT_IS_OBJECT(constructor_variant)) { + NPObject* constructor_object = NPVARIANT_TO_OBJECT(constructor_variant); + if (constructor_object != NULL) { + NPVariant object_variant; + if (NPN_InvokeDefault(npp, constructor_object, args, numArgs, + &object_variant)) { + if (NPVARIANT_IS_OBJECT(object_variant)) { + *result = NPVARIANT_TO_OBJECT(object_variant); + NPN_RetainObject(*result); + success = true; + } + NPN_ReleaseVariantValue(&object_variant); + } + } + } + NPN_ReleaseVariantValue(&constructor_variant); + } + return success; +} + +bool NPBrowserProxy::NPN_Evaluate(NPP npp, + NPObject *obj, + NPString *script, + NPVariant *result) { + if (!npp || !obj || !script) { + return false; + } + + NPBrowserProxy *browser_proxy = static_cast<NPBrowserProxy*>(npp->ndata); + CHostControl *host_control = browser_proxy->GetHostingControl(); + ATLASSERT(host_control); + + NPObject* window_object = browser_proxy->GetVWindowObject(); + if (obj != window_object) { + return false; + } + + // Causing IE to run JavaScript code is straightforward if you don't need the + // result of the evaluation. You can just call IHTMLWindow::execScript. It + // does not return a valid result though (it specifically says that on MSDN). + // + // I tried two other approaches, both of which had the same issue: they didn't + // return the result. The first unsuccessul approach was to get the "eval" + // propery of the window object and invoke it as a function with the JS code + // as the argument. That returns no result. The second unsuccessful approach + // was to get the "Function" constructor from the window object and invoke it + // with the JS code as an argument. That should give you a function object and + // invoking it should evaluate the JS code and return the result. It does not + // return a result. + // + // The final approach (which worked) is to create a Function that additionally + // takes a temporary object as an argument. The JS code is modified to assign + // its result to a property of that temporary object called "result". After + // evaluating the function, the result can then be retrieved from the + // temporary object. + bool success = false; + NPObject* result_object; + if (ConstructObject(npp, window_object, "Object", NULL, 0, &result_object)) { + CStringA function_code; + function_code.Format("result_object.result = (%s);", + script->utf8characters); + + NPVariant args[2]; + STRINGZ_TO_NPVARIANT("result_object", args[0]); + STRINGN_TO_NPVARIANT(function_code.GetBuffer(), + (uint32_t) function_code.GetLength(), + args[1]); + NPObject* function_object; + if (ConstructObject(npp, window_object, "Function", args, 2, + &function_object)) { + OBJECT_TO_NPVARIANT(result_object, args[0]); + NPVariant dummy_result; + if (NPN_InvokeDefault(npp, function_object, args, 1, &dummy_result)) { + NPIdentifier result_identifier = NPN_GetStringIdentifier("result"); + if (NPN_GetProperty(npp, result_object, result_identifier, result)) { + success = true; + } + NPN_ReleaseVariantValue(&dummy_result); + } + NPN_ReleaseObject(function_object); + } + NPN_ReleaseObject(result_object); + } + return success; +} + +void NPBrowserProxy::NPN_SetException(NPObject *obj, + const NPUTF8 *message) { + ATLASSERT(false && "NPN_SetException not implemented"); +} + +NPNetscapeFuncs* NPBrowserProxy::GetBrowserFunctions() { + return &kNetscapeFunctions; +} + +void NPBrowserProxy::TearDown() { + // All NPObjectProxy instances stored in the java-script environment must + // be marked so that scripted operations on these operations fail after + // the plug-in has been torn down. We release the hosted object on all of + // these wrappers to prevent access, and allow deletion of the NPAPI objects. + NPObjectProxyMap::iterator np_object_iter(np_object_proxy_map_.begin()), + np_object_end(np_object_proxy_map_.end()); + for (; np_object_iter != np_object_end; ++np_object_iter) { + np_object_iter->second->ReleaseHosted(); + } +} diff --git a/o3d/plugin/npapi_host_control/win/np_browser_proxy.h b/o3d/plugin/npapi_host_control/win/np_browser_proxy.h new file mode 100644 index 0000000..19f3245 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/np_browser_proxy.h @@ -0,0 +1,278 @@ +// Copyright 2009, Google Inc. All rights reserved. +// Portions of this file were adapted from the Mozilla project. +// See https://developer.mozilla.org/en/ActiveX_Control_for_Hosting_Netscape_Plug-ins_in_IE +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adam Lock <adamlock@eircom.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +// File declaring NPBrowserProxy class providing a subset of the NPAPI browser +// entry points for hosting Mozilla NPAPI plugin objects. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_BROWSER_PROXY_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_BROWSER_PROXY_H_ + +#include <dispex.h> +#include <map> +#include "third_party/npapi/files/include/npupp.h" +#include "plugin/npapi_host_control/win/dispatch_proxy.h" +#include "plugin/npapi_host_control/win/np_object_proxy.h" + +class CHostControl; +class DispatchProxy; + +// Class implementing the NPAPI browser interface for an ActiveX environment. +class NPBrowserProxy { + public: + explicit NPBrowserProxy(CHostControl* host, IDispatchEx* window_dispatch); + ~NPBrowserProxy(); + + // Returns the 'v-table' object for interacting with the NPAPI interface + // of the hosted browser environment. + static NPNetscapeFuncs* GetBrowserFunctions(); + + // Returns the hosting COM control. + CHostControl* GetHostingControl() { + return host_control_; + } + + // Returns a place-holder object for the browser property. Used in + // conjunction with NPN_GetValue and NPNVWindowNPObject. + DispatchProxy* GetVWindowObject() { + return vwindow_object_; + } + + // Create or get the existing COM object for the given NPObject. Ensures + // each NPObject only has a single proxy. + CComPtr<IDispatchEx> GetDispatchObject(NPObject* np_object); + + // Create or get the existing NPObject for the given COM object. Ensures + // each COM object only has a single proxy. Caller must release the object. + NPObject* GetNPObject(IDispatch* dispatch_object); + + // Registers an NPObject with its associated INPObjectProxy. + void RegisterNPObjectProxy( + NPObject* np_object, + const CComPtr<INPObjectProxy>& proxy_wrapper); + + // Called by the NPObjectProxy when it is destroyed. + void UnregisterNPObjectProxy(NPObject* np_object); + + // Called by the DispatchProxy when it is destroyed. + void UnregisterDispatchProxy(IDispatchEx* dispatch_object); + + NPIdentifier call_identifier() const { return call_identifier_; } + + // Prepares all allocated resources for the destruction of the NPBrowserProxy + // instance. Ensures that all objects returned to the IE scripting + // environment become unaccessable. + void TearDown(); + + private: + typedef std::map<NPObject*, CComPtr<INPObjectProxy> > NPObjectProxyMap; + + typedef std::map<IUnknown*, DispatchProxy*> DispatchProxyMap; + + // Back-pointer to the COM control hosting the NPAPI plug-in. + CHostControl* host_control_; + + // Pointer to place-holder object for the NPNVWindowNPObject value + // accessible through NPN_GetValue. + DispatchProxy* vwindow_object_; + + // Map of all NPObjects wrapped with NPObjectProxys. + NPObjectProxyMap np_object_proxy_map_; + + // Map of all IDispatchEx objects wrapped with DispatchProxies. + DispatchProxyMap dispatch_proxy_map_; + + NPIdentifier call_identifier_; + + // The following functions implement a sub-set of the NPAPI browser object + // interface. The function naming has been preserved from that in the + // NPAPI interface headers. For documentation on the expected behaviour + // of these routines, please refer to the following: + // http://developer.mozilla.org/en/Gecko_Plugin_API_Reference/Plug-in_Side_Plug-in_API + static NPError NPN_RequestRead(NPStream *pstream, NPByteRange *rangeList); + + static NPError NPN_GetURLNotify(NPP npp, + const char* relativeURL, + const char* target, + void* notifyData); + + static NPError NPN_GetValue(NPP npp, NPNVariable variable, void *r_value); + + static NPError NPN_SetValue(NPP npp, NPPVariable variable, void *r_value); + + static NPError NPN_GetURL(NPP npp, + const char* relativeURL, + const char* target); + + static NPError NPN_PostURLNotify(NPP npp, + const char* relativeURL, + const char *target, + uint32 len, + const char *buf, + NPBool file, + void* notifyData); + + static NPError NPN_PostURL(NPP npp, + const char* relativeURL, + const char *target, + uint32 len, + const char *buf, + NPBool file); + + static NPError NPN_NewStream(NPP npp, + NPMIMEType type, + const char* window, + NPStream **pstream); + + static int32 NPN_Write(NPP npp, NPStream *pstream, int32 len, void *buffer); + + static NPError NPN_DestroyStream(NPP npp, NPStream *pstream, NPError reason); + + static void NPN_Status(NPP npp, const char *message); + + static void* NPN_MemAlloc(uint32 size); + + static void NPN_MemFree(void *ptr); + + static uint32 NPN_MemFlush(uint32 size); + + static void NPN_ReloadPlugins(NPBool reloadPages); + + static void NPN_InvalidateRect(NPP npp, NPRect *invalidRect); + + static void NPN_InvalidateRegion(NPP npp, NPRegion invalidRegion); + + static const char* NPN_UserAgent(NPP npp); + + static void* NPN_GetJavaEnv(void); + + static void* NPN_GetJavaPeer(NPP npp); + + static void* NPN_GetJavaClass(void* handle); + + static void NPN_ForceRedraw(NPP npp); + + static NPObject* NPN_CreateObject(NPP npp, NPClass *aClass); + + static NPObject* NPN_RetainObject(NPObject *object); + + static void NPN_ReleaseObject(NPObject *object); + + static NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name); + + static void NPN_GetStringIdentifiers(const NPUTF8 **names, + int32_t nameCount, + NPIdentifier *identifiers); + + static NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier); + + static NPIdentifier NPN_GetIntIdentifier(int32_t intid); + static int32_t NPN_IntFromIdentifier(NPIdentifier identifier); + static bool NPN_IdentifierIsString(NPIdentifier identifier); + + static void NPN_ReleaseVariantValue(NPVariant *variant); + + static bool NPN_GetProperty(NPP npp, NPObject *obj, + NPIdentifier propertyName, + NPVariant *result); + + static bool NPN_SetProperty(NPP npp, + NPObject *obj, + NPIdentifier propertyName, + const NPVariant *value); + + static bool NPN_HasProperty(NPP npp, + NPObject *npobj, + NPIdentifier propertyName); + + static bool NPN_RemoveProperty(NPP npp, + NPObject *npobj, + NPIdentifier propertyName); + + static bool NPN_HasMethod(NPP npp, + NPObject *npobj, + NPIdentifier methodName); + + static bool NPN_Invoke(NPP npp, + NPObject *obj, + NPIdentifier methodName, + const NPVariant *args, + unsigned argCount, + NPVariant *result); + + static bool NPN_InvokeDefault(NPP npp, + NPObject *obj, + const NPVariant *args, + unsigned argCount, + NPVariant *result); + + static bool NPN_Construct(NPP npp, + NPObject *obj, + const NPVariant *args, + unsigned argCount, + NPVariant *result); + + static bool NPN_Enumerate(NPP npp, + NPObject* obj, + NPIdentifier** ids, + uint32_t* idCOunt); + + static bool ConstructObject(NPP npp, + NPObject* window_object, + NPUTF8* constructor_name, + NPVariant* args, + uint32_t numArgs, + NPObject** result); + + static bool NPN_Evaluate(NPP npp, + NPObject *obj, + NPString *script, + NPVariant *result); + + static void NPN_SetException(NPObject *obj, const NPUTF8 *message); + + // Static table of function pointers to the member function entry points + // for the NPAPI browser environment interface. + static NPNetscapeFuncs kNetscapeFunctions; + + DISALLOW_COPY_AND_ASSIGN(NPBrowserProxy); +}; + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_BROWSER_PROXY_H_ diff --git a/o3d/plugin/npapi_host_control/win/np_object_proxy.cc b/o3d/plugin/npapi_host_control/win/np_object_proxy.cc new file mode 100644 index 0000000..da7f018 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/np_object_proxy.cc @@ -0,0 +1,518 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <oaidl.h> +#include <atlstr.h> +#include <vector> + +#include "base/scoped_ptr.h" +#include "plugin/npapi_host_control/win/np_object_proxy.h" +#include "plugin/npapi_host_control/win/np_browser_proxy.h" +#include "plugin/npapi_host_control/win/variant_utils.h" +#include "third_party/npapi/files/include/npupp.h" + +namespace { + +// Helper routine copying NPVariant object to a destination COM Variant. +// Both arguments may be null. Argument com_result must be a properly +// initialized VARIANT instance. +void CopyToCOMResult(NPBrowserProxy* browser_proxy, + NPVariant* np_result, + VARIANT* com_result) { + if (com_result && np_result) { + CComVariant intermediate_result; + NPVariantToVariant(browser_proxy, np_result, &intermediate_result); + VariantCopy(com_result, &intermediate_result); + } +} + +} // unnamed namespace + + +NPObjectProxy::NPObjectProxy() + : hosted_(NULL), + browser_proxy_(NULL) { +} + +NPObjectProxy::~NPObjectProxy() { + if (hosted_) { + if (browser_proxy_) { + browser_proxy_->UnregisterNPObjectProxy(hosted_); + } + NPBrowserProxy::GetBrowserFunctions()->releaseobject(hosted_); + } +} + +STDMETHODIMP NPObjectProxy::SetHostedObject(void* hosted) { + ATLASSERT(hosted); + if (hosted_) { + NPBrowserProxy::GetBrowserFunctions()->releaseobject(hosted_); + } + hosted_ = static_cast<NPObject*>(hosted); + NPBrowserProxy::GetBrowserFunctions()->retainobject(hosted_); + return S_OK; +} + +STDMETHODIMP NPObjectProxy::ReleaseHosted() { + if (hosted_) { + NPBrowserProxy::GetBrowserFunctions()->releaseobject(hosted_); + hosted_ = NULL; + } + return S_OK; +} + +STDMETHODIMP NPObjectProxy::GetTypeInfoCount(UINT* pctinfo) { + // This class does not support type info. + if (!pctinfo) { + return E_POINTER; + } else { + *pctinfo = 0; + } + return S_OK; +} + +STDMETHODIMP NPObjectProxy::GetTypeInfo(UINT itinfo, + LCID lcid, + ITypeInfo** pptinfo) { + // This class does not support type info. + return E_NOTIMPL; +} + +STDMETHODIMP NPObjectProxy::GetIDsOfNames(REFIID riid, + LPOLESTR* rgszNames, + UINT cNames, + LCID lcid, + DISPID* rgdispid) { + if (!hosted_) { + return E_FAIL; + } + + // Use the ids from the plugin to return dispatch ids + NPIdentifier *supported_ids = NULL; + uint32 id_count = 0; + + if (!hosted_->_class->enumerate(hosted_, &supported_ids, &id_count)) { + return E_FAIL; + } + + // Convert all of the wide string arguments to UTF-8. + scoped_array<char *> utf8_names(new char*[cNames]); + for (int x = 0; x < cNames; ++x) { + size_t name_length = wcstombs(NULL, rgszNames[x], 0) + 1; + utf8_names[x] = new char[name_length]; + wcstombs(utf8_names[x], rgszNames[x], name_length); + rgdispid[x] = DISPID_UNKNOWN; + } + + int ids_found = 0; + // For each string in the input arguments, look for a match in the set of + // ids supported by the object instance. + NPUTF8 *string_id = NULL; + for (int x = 0; x < id_count; ++x) { + string_id = NPBrowserProxy::GetBrowserFunctions()-> + utf8fromidentifier(supported_ids[x]); + ATLASSERT(string_id); + for (int y = 0; y < cNames; ++y) { + if (strcmp(utf8_names[y], string_id) == 0) { + // Return the ADDRESS of the supported ids string as the DISPID + // for the method. + rgdispid[y] = reinterpret_cast<DISPID>(supported_ids[x]); + ++ids_found; + break; + } + } + NPBrowserProxy::GetBrowserFunctions()->memfree(string_id); + } + + // Free all intermediate string resources. + for (int x = 0; x < cNames; ++x) { + delete[] utf8_names[x]; + } + NPBrowserProxy::GetBrowserFunctions()->memfree(supported_ids); + + return (ids_found == cNames) ? S_OK : DISP_E_UNKNOWNNAME; +} + +STDMETHODIMP NPObjectProxy::Invoke(DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pdispparams, + VARIANT* pvarResult, + EXCEPINFO* pexcepinfo, + UINT* puArgErr) { + if (!hosted_) { + return E_FAIL; + } + + return InvokeEx(dispidMember, lcid, wFlags, pdispparams, pvarResult, + pexcepinfo, NULL); +} + +STDMETHODIMP NPObjectProxy::DeleteMemberByDispID(DISPID id) { + if (!hosted_) { + return E_FAIL; + } + + NPIdentifier np_identifier = reinterpret_cast<NPIdentifier>(id); + if (hosted_->_class->removeProperty != NULL && + hosted_->_class->removeProperty(hosted_, np_identifier)) { + return S_OK; + } + return S_FALSE; +} + +STDMETHODIMP NPObjectProxy::DeleteMemberByName(BSTR bstrName, DWORD grfdex) { + if (!hosted_) { + return E_FAIL; + } + DISPID id; + HRESULT hr = GetDispID(bstrName, grfdex, &id); + if (hr == DISP_E_UNKNOWNNAME) { + // The semantics of JavaScript are that deleting a property that does not + // exist succeeds. + return S_OK; + } else if (FAILED(hr)) { + // Otherwise fail. + return S_FALSE; + } else { + return DeleteMemberByDispID(id); + } +} + +STDMETHODIMP NPObjectProxy::GetDispID(BSTR bstrName, + DWORD grfdex, + DISPID* pid) { + if (!hosted_) { + return E_FAIL; + } + + *pid = NULL; + CString name(bstrName); + int num_utf8_bytes = WideCharToMultiByte(CP_UTF8, 0, name.GetBuffer(), + name.GetLength() + 1, NULL, 0, + NULL, NULL); + std::vector<NPUTF8> utf8_name(num_utf8_bytes); + WideCharToMultiByte(CP_UTF8, 0, name.GetBuffer(), name.GetLength() + 1, + &utf8_name[0], num_utf8_bytes, NULL, NULL); + + NPIdentifier np_identifier = + NPBrowserProxy::GetBrowserFunctions()->getstringidentifier( + &utf8_name[0]); + + // This method can be called to determine whether an object has a property + // with the given name. So check that before converting to an NPIdentifier. + if (!HasPropertyOrMethod(np_identifier)) + return DISP_E_UNKNOWNNAME; + + *pid = reinterpret_cast<DISPID>(np_identifier); + return S_OK; +} + +STDMETHODIMP NPObjectProxy::GetMemberName(DISPID id, + BSTR* pbstrName) { + if (!hosted_) { + return E_FAIL; + } + NPIdentifier np_identifier = reinterpret_cast<NPIdentifier>(id); + + // Make sure the id is valid on this object. It might have been deleted since + // it was returned by GetDispID. + if (!HasPropertyOrMethod(np_identifier)) + return DISP_E_UNKNOWNNAME; + + NPUTF8* utf8_name = NPBrowserProxy::GetBrowserFunctions()->utf8fromidentifier( + np_identifier); + int num_wide_chars = MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, NULL, 0); + CString name; + MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, + name.GetBuffer(num_wide_chars), num_wide_chars); + name.ReleaseBuffer(num_wide_chars - 1); + *pbstrName = name.AllocSysString(); + NPBrowserProxy::GetBrowserFunctions()->memfree(utf8_name); + return S_OK; +} + +STDMETHODIMP NPObjectProxy::GetMemberProperties(DISPID id, + DWORD grfdexFetch, + DWORD* pgrfdex) { + if (!hosted_) { + return E_FAIL; + } + + // NPAPI does not provide a way to get all the information this function + // expects to be returned. This is what IE7 returns for some native objects. + return E_NOTIMPL; +} + +STDMETHODIMP NPObjectProxy::GetNameSpaceParent(IUnknown** punk) { + if (!hosted_) { + return E_FAIL; + } + + // JavaScript does not have namespaces. An alternative would be to return + // an error code. + *punk = NULL; + return S_OK; +} + +STDMETHODIMP NPObjectProxy::GetNextDispID(DWORD grfdex, + DISPID id, + DISPID* pid) { + if (!hosted_) { + return E_FAIL; + } + + HRESULT hr = S_FALSE; + NPIdentifier* ids; + uint32_t num_ids = 0; + if (hosted_->_class->enumerate != NULL && + hosted_->_class->enumerate(hosted_, &ids, &num_ids)) { + if (id == DISPID_STARTENUM && num_ids > 0) { + *pid = reinterpret_cast<DISPID>(ids[0]); + hr = S_OK; + } else { + if (!ids) { + return S_FALSE; + } + uint32_t i; + for (i = 0; i != num_ids; ++i) { + if (ids[i] == reinterpret_cast<NPIdentifier>(id)) + break; + } + if (i + 1 < num_ids) { + *pid = reinterpret_cast<DISPID>(ids[i + 1]); + hr = S_OK; + } + } + NPBrowserProxy::GetBrowserFunctions()->memfree(ids); + } + return hr; +} + +STDMETHODIMP NPObjectProxy::InvokeEx(DISPID id, + LCID lcid, + WORD wFlags, + DISPPARAMS* pdb, + VARIANT* pVarRes, + EXCEPINFO* pei, + IServiceProvider* pspCaller) { + if (!hosted_) { + return E_FAIL; + } + HRESULT hr = E_FAIL; + NPIdentifier np_identifier = reinterpret_cast<NPIdentifier>(id); + + if (wFlags & (DISPATCH_METHOD | DISPATCH_CONSTRUCT)) { + // Get the "this" pointer if provided or default to the hosted object. + // We cannot support more general bindings for "this" through npruntime. + // This might arise if the function is invoked through + // my_function.call(my_this, args) from JScript. + if (pdb->cNamedArgs == 1 && pdb->rgdispidNamedArgs[0] == DISPID_THIS) { + NPVariant np_this_variant; + VariantToNPVariant( + browser_proxy_, + &pdb->rgvarg[0], + &np_this_variant); + NPObject* np_this_object = NULL; + if (NPVARIANT_IS_OBJECT(np_this_variant)) { + np_this_object = NPVARIANT_TO_OBJECT(np_this_variant); + } + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue( + &np_this_variant); + if (np_this_object != hosted_) { + return E_FAIL; + } + } else if (pdb->cNamedArgs != 0) { + return DISP_E_NONAMEDARGS; + } + + // Convert the arguments to NPVariants. + int num_unnamed_arguments = pdb->cArgs - pdb->cNamedArgs; + scoped_array<NPVariant> np_arguments(new NPVariant[num_unnamed_arguments]); + for (int x = 0; x < num_unnamed_arguments; ++x) { + // Note that IDispatch expects arguments in the reverse order. + VariantToNPVariant( + browser_proxy_, + &pdb->rgvarg[pdb->cArgs - x - 1], + &np_arguments[x]); + } + + // IDispatch supports the notion of default methods with the DISPID value + // DISPID_VALUE. + NPVariant result; + if (DISPID_VALUE == id) { + if (wFlags & DISPATCH_CONSTRUCT) { + if (hosted_->_class->construct != NULL && + hosted_->_class->construct(hosted_, + np_arguments.get(), + num_unnamed_arguments, + &result)) { + CopyToCOMResult(browser_proxy_, &result, pVarRes); + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue(&result); + hr = S_OK; + } + } else { + if (hosted_->_class->invokeDefault != NULL && + hosted_->_class->invokeDefault(hosted_, + np_arguments.get(), + num_unnamed_arguments, + &result)) { + CopyToCOMResult(browser_proxy_, &result, pVarRes); + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue(&result); + hr = S_OK; + } + } + } else if (hosted_->_class->hasMethod != NULL && + hosted_->_class->hasMethod(hosted_, np_identifier)) { + if (hosted_->_class->invoke != NULL && + hosted_->_class->invoke(hosted_, + np_identifier, + np_arguments.get(), + num_unnamed_arguments, + &result)) { + CopyToCOMResult(browser_proxy_, &result, pVarRes); + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue(&result); + hr = S_OK; + } + } else if (hosted_->_class->hasProperty != NULL && + hosted_->_class->hasProperty(hosted_, np_identifier)) { + // If the object does not have a method with the given identifier, + // it may have a property with that id that we can invoke the default + // method upon. + NPVariant np_property_variant; + if (hosted_->_class->getProperty != NULL && + hosted_->_class->getProperty(hosted_, np_identifier, + &np_property_variant)) { + if (NPVARIANT_IS_OBJECT(np_property_variant)) { + NPObject* np_property_object = NPVARIANT_TO_OBJECT( + np_property_variant); + if (np_property_object->_class->invokeDefault != NULL) { + if (np_property_object->_class->invokeDefault( + np_property_object, + np_arguments.get(), + num_unnamed_arguments, + &result)) { + CopyToCOMResult(browser_proxy_, &result, pVarRes); + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue( + &result); + hr = S_OK; + } + } else { + hr = DISP_E_TYPEMISMATCH; + } + } else { + hr = DISP_E_TYPEMISMATCH; + } + NPBrowserProxy::GetBrowserFunctions()-> + releasevariantvalue(&np_property_variant); + } + } else { + hr = DISP_E_MEMBERNOTFOUND; + } + + // Release all of the converted arguments. + for (int x = 0; x < num_unnamed_arguments; ++x) { + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue( + &np_arguments[x]); + } + } else if (wFlags & DISPATCH_PROPERTYPUT) { + if (pdb->cArgs == 1) { + if (id == DISPID_VALUE) { + hr = DISP_E_MEMBERNOTFOUND; + } else { + // Convert the COM variant to the corresponding NPVariant. + NPVariant property_in; + VariantToNPVariant(browser_proxy_, &pdb->rgvarg[0], &property_in); + if (hosted_->_class->setProperty != NULL && + hosted_->_class->setProperty(hosted_, + np_identifier, + &property_in)) { + hr = S_OK; + } + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue( + &property_in); + } + } else { + hr = DISP_E_BADPARAMCOUNT; + } + } else if (wFlags & DISPATCH_PROPERTYGET) { + if (pdb->cArgs == 0) { + // Sometimes JScript asks an object for its default value. Returning + // itself appears to be ther right thing to do. + if (id == DISPID_VALUE) { + pVarRes->vt = VT_DISPATCH; + pVarRes->pdispVal = this; + AddRef(); + hr = S_OK; + } else { + NPVariant property_out; + if (hosted_->_class->hasProperty != NULL && + !hosted_->_class->hasProperty(hosted_, np_identifier)) { + hr = DISP_E_MEMBERNOTFOUND; + } else if (hosted_->_class->getProperty != NULL && + hosted_->_class->getProperty(hosted_, + np_identifier, + &property_out)) { + CopyToCOMResult(browser_proxy_, &property_out, pVarRes); + NPBrowserProxy::GetBrowserFunctions()->releasevariantvalue( + &property_out); + hr = S_OK; + } + } + } else { + hr = DISP_E_BADPARAMCOUNT; + } + } + return hr; +} + +STDMETHODIMP NPObjectProxy::GetNPObjectInstance(void **np_instance) { + if (!hosted_) { + return E_FAIL; + } + + *np_instance = hosted_; + NPBrowserProxy::GetBrowserFunctions()->retainobject(hosted_); + return S_OK; +} + +bool NPObjectProxy::HasPropertyOrMethod(NPIdentifier np_identifier) { + if (!hosted_) { + return E_FAIL; + } + + return (hosted_->_class->hasProperty != NULL && + hosted_->_class->hasProperty(hosted_, np_identifier)) || + (hosted_->_class->hasMethod != NULL && + hosted_->_class->hasMethod(hosted_, np_identifier)); +} diff --git a/o3d/plugin/npapi_host_control/win/np_object_proxy.h b/o3d/plugin/npapi_host_control/win/np_object_proxy.h new file mode 100644 index 0000000..4d80676 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/np_object_proxy.h @@ -0,0 +1,130 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// File declaring NPObjectProxy class. This class wraps the NPAPI scripting +// interface with a COM IDispatchEx interface to allow interop between ActiveX +// and NPObject instances. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_OBJECT_PROXY_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_OBJECT_PROXY_H_ + +#include <atlctl.h> +#include <dispex.h> + +// File included without directory because it is auto-generated by the +// type-lib. +#include "npapi_host_control.h" + +#include "third_party/npapi/files/include/npupp.h" + +struct NPObject; +class NPBrowserProxy; + +// COM class implementing a basic IDispatchEx interface that wraps the NPAPI +// NPObject scripting functionality. +class ATL_NO_VTABLE NPObjectProxy : + public CComObjectRootEx<CComSingleThreadModel>, + public CComCoClass<NPObjectProxy, &CLSID_NPObjectProxy>, + public IDispatchImpl<INPObjectProxy, &IID_INPObjectProxy, + &LIBID_npapi_host_controlLib>, + public IObjectSafetyImpl<NPObjectProxy, + INTERFACESAFE_FOR_UNTRUSTED_CALLER> { + public: + NPObjectProxy(); + virtual ~NPObjectProxy(); + +DECLARE_REGISTRY_RESOURCEID(IDR_NPOBJECTPROXY) + +BEGIN_COM_MAP(NPObjectProxy) + COM_INTERFACE_ENTRY(INPObjectProxy) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IDispatchEx) +END_COM_MAP() + + STDMETHOD(SetBrowserProxy)(void* browser_proxy) { + browser_proxy_ = static_cast<NPBrowserProxy*>(browser_proxy); + return S_OK; + } + + // Routine implementing INPObjectProxy interface method, returning a raw + // pointer to a NPObject instance. Note that the reference count of the + // returned NPObject has been incremented. The returned object should + // be released by the hosting browser proxy to prevent memory leaks. + STDMETHOD(GetNPObjectInstance)(void **np_instance); + STDMETHOD(SetHostedObject)(void* hosted_object); + STDMETHOD(ReleaseHosted)(); + + // Routines implementing the IDispatchEx COM interface. + STDMETHOD(GetTypeInfoCount)(UINT* pctinfo); + STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); + STDMETHOD(GetIDsOfNames)(REFIID riid, + LPOLESTR* rgszNames, + UINT cNames, + LCID lcid, + DISPID* rgdispid); + STDMETHOD(Invoke)(DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pdispparams, + VARIANT* pvarResult, + EXCEPINFO* pexcepinfo, + UINT* puArgErr); + + STDMETHOD(DeleteMemberByDispID)(DISPID id); + STDMETHOD(DeleteMemberByName)(BSTR bstrName, DWORD grfdex); + STDMETHOD(GetDispID)(BSTR bstrName, DWORD grfdex, DISPID* pid); + STDMETHOD(GetMemberName)(DISPID id, BSTR* pbstrName); + STDMETHOD(GetMemberProperties)(DISPID id, DWORD grfdexFetch, DWORD* pgrfdex); + STDMETHOD(GetNameSpaceParent)(IUnknown** ppunk); + STDMETHOD(GetNextDispID)(DWORD grfdex, DISPID id, DISPID* pid); + STDMETHOD(InvokeEx)(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS* pdp, + VARIANT* pVarRes, EXCEPINFO* pei, + IServiceProvider* pspCaller); + + DECLARE_PROTECT_FINAL_CONSTRUCT(); + private: + bool HasPropertyOrMethod(NPIdentifier np_identifier); + + // Pointer to NPObject for which this instance is a proxy IDispatchEx. + NPObject *hosted_; + + // Back-pointer to the NPAPI browser proxy. + NPBrowserProxy* browser_proxy_; + + DISALLOW_COPY_AND_ASSIGN(NPObjectProxy); +}; + +// Register this COM class with the COM module. +OBJECT_ENTRY_AUTO(__uuidof(NPObjectProxy), NPObjectProxy); + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_OBJECT_PROXY_H_ diff --git a/o3d/plugin/npapi_host_control/win/np_object_proxy.rgs b/o3d/plugin/npapi_host_control/win/np_object_proxy.rgs new file mode 100644 index 0000000..3660c52 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/np_object_proxy.rgs @@ -0,0 +1,37 @@ +HKCU +{ + Software + { + Classes + { + o3d_host.NPObjectProxy.1 = s 'NPObjectProxy Class' + { + CLSID = s '{1D68424D-7A71-4b61-AE5C-56DBCD8B0E53}' + 'Insertable' + } + o3d_host.NPObjectProxy = s 'NPObjectProxy Class' + { + CLSID = s '{1D68424D-7A71-4b61-AE5C-56DBCD8B0E53}' + CurVer = s 'o3d_host.NPObjectProxy.1' + } + NoRemove CLSID + { + ForceRemove {1D68424D-7A71-4b61-AE5C-56DBCD8B0E53} = s 'NPObjectProxy Class' + { + ProgID = s 'o3d_host.NPObjectProxy.1' + VersionIndependentProgID = s 'o3d_host.NPObjectProxy' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + val AppID = s '%APPID%' + ForceRemove 'Control' + ForceRemove 'Insertable' + 'TypeLib' = s '{D4F6E31C-E952-48FE-9833-6AE308BD79C6}' + 'Version' = s '1.0' + } + } + } + } +} diff --git a/o3d/plugin/npapi_host_control/win/np_plugin_proxy.cc b/o3d/plugin/npapi_host_control/win/np_plugin_proxy.cc new file mode 100644 index 0000000..449e6b7 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/np_plugin_proxy.cc @@ -0,0 +1,437 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "plugin/npapi_host_control/win/np_plugin_proxy.h" + +#include <shlobj.h> +#include <shlwapi.h> + +#include <algorithm> +#include "base/scoped_ptr.h" +#include "plugin/npapi_host_control/win/module.h" +#include "plugin/npapi_host_control/win/np_browser_proxy.h" +#include "plugin/npapi_host_control/win/np_object_proxy.h" +#include "plugin/npapi_host_control/win/stream_operation.h" + +namespace { + +const wchar_t kPluginName[] = L"npo3dautoplugin.dll"; +const wchar_t kAppDataPluginLocation[] = + L"Mozilla\\plugins\\npo3dautoplugin.dll"; + +// Returns the path to the O3D plug-in located in the current user's +// Application Data directory. Returns NULL on failure. +// Note: The caller does not need to free the returned string. +const wchar_t* GetApplicationDataPluginPath() { + static wchar_t kAppDataPath[MAX_PATH] = {0}; + HRESULT hr = SHGetFolderPath(0, CSIDL_APPDATA, NULL, 0, kAppDataPath); + if (SUCCEEDED(hr)) { + PathAppend(kAppDataPath, kAppDataPluginLocation); + return kAppDataPath; + } else { + return NULL; + } +} + +// Returns a path to the O3D plug-in corresponding to the value of the +// MOZ_PLUGIN_PATH environment variable. This variable is used to override +// the default directory where FireFox will search for plug-ins. +// Note: The caller does not need to free the returned string. +const wchar_t* GetMozillaPluginPath() { + static wchar_t kMozillaPluginPath[MAX_PATH] = {0}; + DWORD chars_written = GetEnvironmentVariable(L"MOZ_PLUGIN_PATH", + kMozillaPluginPath, + MAX_PATH); + if (chars_written == 0) { + return NULL; + } else if (chars_written > MAX_PATH) { + ATLASSERT(false && "MOZ_PLUGIN_PATH too large to represent a path."); + return NULL; + } else { + PathAppend(kMozillaPluginPath, kPluginName); + return kMozillaPluginPath; + } +} + +const wchar_t kProgramFilesPluginLocation[] = + L"Mozilla Firefox\\plugins\\npo3dautoplugin.dll"; + +// Returns the path to the O3D plug-in located in the Program +// Files directory. Returns NULL on failure. +// Note: The caller does not need to free the returned string. +const wchar_t* GetProgramFilesPluginPath() { + static wchar_t kProgramFilesPath[MAX_PATH] = {0}; + HRESULT hr = SHGetFolderPath(0, + CSIDL_PROGRAM_FILES, + NULL, + 0, + kProgramFilesPath); + if (SUCCEEDED(hr)) { + PathAppend(kProgramFilesPath, kProgramFilesPluginLocation); + return kProgramFilesPath; + } else { + return NULL; + } +} + +// Helper class implementing RAII semantics for locking the ATL module. +class AutoModuleLock { + public: + AutoModuleLock() { + NPAPIHostControlModule::LockModule(); + } + ~AutoModuleLock() { + NPAPIHostControlModule::UnlockModule(); + } + private: + DISALLOW_COPY_AND_ASSIGN(AutoModuleLock); +}; + +// Helper routine that populates nested scoped arrays of characters +// from std vectors of CStringA objects. This routine is used to make +// a local copy of the name/value arguments to the plug-in instance, +// so that any local modifications on the arguments performed during +// plug-in initialization won't propagate to future instantiations of +// the plug-in. +void ConstructLocalPluginArgs(const std::vector<CStringA>& names, + const std::vector<CStringA>& values, + short* argc, + scoped_array<scoped_array<char> >* argn, + scoped_array<scoped_array<char> >* argv) { + ATLASSERT(argc && argn && argv); + ATLASSERT(names.size() == values.size()); + + *argc = static_cast<short>(names.size()); + if (names.empty()) { + argn->reset(NULL); + argv->reset(NULL); + return; + } + + // Copy the contents of the name and value arrays to the scoped_array + // parameters. + argn->reset(new scoped_array<char>[*argc]); + argv->reset(new scoped_array<char>[*argc]); + for (int x = 0; x < *argc; ++x) { + char* name = new char[names[x].GetLength() + 1]; + char* value = new char[values[x].GetLength() + 1]; + + strcpy(name, static_cast<const char*>(names[x])); + strcpy(value, static_cast<const char*>(values[x])); + + (*argn)[x].reset(name); + (*argv)[x].reset(value); + } +} + +} // unnamed namespace + +int NPPluginProxy::kPluginInstanceCount = 0; + +NPPluginProxy::NPPluginProxy() + : browser_proxy_(NULL), + scriptable_object_(NULL), + NP_Initialize_(NULL), + NP_GetEntryPoints_(NULL), + NP_Shutdown_(NULL), + plugin_module_(0) { + npp_data_.ndata = npp_data_.pdata = NULL; + memset(&plugin_funcs_, NULL, sizeof(plugin_funcs_)); +} + +NPPluginProxy::~NPPluginProxy() { + // Serialize the destruction of instances so that there are no races on + // the instance count, and library loads. + AutoModuleLock lock; + if (0 == --kPluginInstanceCount) { + if (NP_Shutdown_) { + NP_Shutdown_(); + } + } + + FreeLibrary(plugin_module_); + ATLASSERT(active_stream_ops_.empty() && + "Destruction of plugin proxy with still-pending streaming ops."); +} + +bool NPPluginProxy::MapEntryPoints(HMODULE loaded_module) { + // Initialize the function pointers to the plugin entry points. + NP_Initialize_ = reinterpret_cast<NP_InitializeFunc>( + GetProcAddress(loaded_module, "NP_Initialize")); + NP_GetEntryPoints_ = reinterpret_cast<NP_GetEntryPointsFunc>( + GetProcAddress(loaded_module, "NP_GetEntryPoints")); + NP_Shutdown_ = reinterpret_cast<NP_ShutdownFunc>( + GetProcAddress(loaded_module, "NP_Shutdown")); + + if (!NP_Initialize_ || !NP_GetEntryPoints_ || !NP_Shutdown_) { + ATLASSERT(false && "NPAPI DLL exports not present."); + return false; + } + + // Plugin-initialization is to be performed once, at initial plug-in + // loading time. Note that this routine must be accessed serially to + // protect against races on kPluginInstanceCount. + if (0 == kPluginInstanceCount) { + if (NPERR_NO_ERROR != NP_Initialize_( + browser_proxy_->GetBrowserFunctions())) { + ATLASSERT(false && "NPAPI initialization failure."); + return false; + } + } + ++kPluginInstanceCount; + + if (NPERR_NO_ERROR != NP_GetEntryPoints_(&plugin_funcs_)) { + ATLASSERT(false && "Unknown failure getting NPAPI entry points."); + return false; + } + + plugin_module_ = loaded_module; + return true; +} + +bool NPPluginProxy::Init(NPBrowserProxy* browser_proxy, + const NPWindow& window, + const std::vector<CStringA>& argument_names, + const std::vector<CStringA>& argument_values) { + ATLASSERT(plugin_module_ && + "Plugin module not loaded before initialization."); + ATLASSERT(browser_proxy && "Browser environment required for plugin init."); + browser_proxy_ = browser_proxy; + + // Store a pointer to the browser proxy instance in the netscape data + // of the plugin data. This will be the only access point to the browser + // instance from within the NPBrowserProxy NPAPI functions. + npp_data_.ndata = static_cast<void*>(browser_proxy_); + + scoped_array<scoped_array<char> > argn, argv; + short argc; + + // Build a local-copy of the plug-in arguments, so that any modifications + // on the name/value pairs will not be propagated to future instantiations. + ConstructLocalPluginArgs(argument_names, + argument_values, + &argc, + &argn, + &argv); + + if (NPERR_NO_ERROR != plugin_funcs_.newp( + "No mime type", + GetNPP(), + NP_EMBED, + argc, + reinterpret_cast<char**>(argn.get()), + reinterpret_cast<char**>(argv.get()), + NULL)) { + NP_Shutdown_(); + ATLASSERT(false && "Unknown failure creating NPAPI plugin instance."); + return false; + } + + if (NPERR_NO_ERROR != plugin_funcs_.setwindow( + GetNPP(), + const_cast<NPWindow*>(&window))) { + plugin_funcs_.destroy(GetNPP(), NULL); + NP_Shutdown_(); + ATLASSERT(false && "Unknown failure binding plugin window."); + return false; + } + + // We assume that the plugin is scripted, so get the scripting entry points + // from the plugin. + NPObject *np_object = NULL; + if (NPERR_NO_ERROR != plugin_funcs_.getvalue( + GetNPP(), + NPPVpluginScriptableNPObject, + static_cast<void*>(&np_object))) { + plugin_funcs_.destroy(GetNPP(), NULL); + NP_Shutdown_(); + ATLASSERT(false && "Unable to initialize NPAPI scripting interface."); + return false; + } + ATLASSERT(np_object); + + HRESULT hr = NPObjectProxy::CreateInstance(&scriptable_object_); + ATLASSERT(SUCCEEDED(hr)); + + scriptable_object_->SetBrowserProxy(browser_proxy_); + scriptable_object_->SetHostedObject(np_object); + + browser_proxy_->RegisterNPObjectProxy(np_object, scriptable_object_); + + NPBrowserProxy::GetBrowserFunctions()->releaseobject(np_object); + + return true; +} + +void NPPluginProxy::TearDown() { + // Block until all stream operations requested by this plug-in have + // completed. + HRESULT hr; + std::vector<HANDLE> stream_handles; + for (int x = 0; x < active_stream_ops_.size(); ++x) { + // Request that the stream finish early - so that large file transfers do + // not block leaving the page. + hr = active_stream_ops_[x]->RequestCancellation(); + ATLASSERT(SUCCEEDED(hr) && + "Failed to request cancellation of pending data stream."); + stream_handles.push_back(active_stream_ops_[x]->GetThreadHandle()); + } + + static const unsigned int kWaitTimeOut = 120000; + while (!stream_handles.empty()) { + DWORD wait_code = MsgWaitForMultipleObjects( + static_cast<DWORD>(stream_handles.size()), + &stream_handles[0], + FALSE, + kWaitTimeOut, + QS_ALLINPUT); + wait_code -= WAIT_OBJECT_0; + if (wait_code == stream_handles.size()) { + MSG msg; + GetMessage(&msg, NULL, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + } else if (wait_code >= 0 && wait_code < stream_handles.size()) { + // A thread has completed, so remove the handle and continue. + stream_handles.erase(stream_handles.begin() + wait_code); + } else { + if (wait_code == WAIT_TIMEOUT + WAIT_OBJECT_0) { + ATLASSERT(false && + "Time-out waiting for completion of streaming operation."); + } else { + ATLASSERT(false && + "Unknown error waiting on streaming operation completion."); + } + // There has been a catastropic error waiting for the pending transfers. + // Kill all of the threads and leave the loop. + // Note: This approach will potentially leak resources allocated by + // the plug-in, but it prevents access to stale data by the threads + // once the plug-in has been unloaded. + for (int x = 0; x < active_stream_ops_.size(); ++x) { + BOOL thread_kill = TerminateThread(stream_handles[x], 0); + ATLASSERT(thread_kill && "Failure killing stalled download thread."); + } + break; + } + } + + if (plugin_module_) { + scriptable_object_ = NULL; + plugin_funcs_.destroy(GetNPP(), NULL); + } +} + +void NPPluginProxy::RegisterStreamOperation(StreamOperation* stream_op) { +#ifndef NDEBUG + StreamOpArray::iterator iter = std::find(active_stream_ops_.begin(), + active_stream_ops_.end(), + stream_op); + ATLASSERT(iter == active_stream_ops_.end() && + "Duplicate registration of a StreamOperation."); +#endif + active_stream_ops_.push_back(stream_op); +} + +void NPPluginProxy::UnregisterStreamOperation(StreamOperation* stream_op) { + StreamOpArray::iterator iter = std::find(active_stream_ops_.begin(), + active_stream_ops_.end(), + stream_op); + ATLASSERT(iter != active_stream_ops_.end() && + "Unregistration of an unrecognized StreamOperation."); + active_stream_ops_.erase(iter); +} + +HRESULT NPPluginProxy::GetScriptableObject( + INPObjectProxy** scriptable_object) const { + ATLASSERT(scriptable_object); + + if (!scriptable_object_) { + return E_FAIL; + } + + *scriptable_object = scriptable_object_; + (*scriptable_object)->AddRef(); + return S_OK; +} + +HRESULT NPPluginProxy::Create(NPPluginProxy** proxy_instance) { + ATLASSERT(proxy_instance); + // Lock the module so that there are no races against the NP_Initialize + // and NP_Shutdown calls. NP_Initialize and NP_Shutdown parallel the + // behaviour of DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH. + // We serialize all construction and destruction to ensure that any + // plug-in initialization mimics this behaviour. + AutoModuleLock lock; + + // First attempt to load the plug-in from the directory specified by the + // MOZ_PLUGIN_PATH directory. + HMODULE np_plugin = NULL; + const wchar_t *plugin_path = GetMozillaPluginPath(); + if (plugin_path) { + np_plugin = LoadLibrary(plugin_path); + } + + if (!np_plugin) { + // Attempt to load the plug-in from the installation directory. + plugin_path = GetApplicationDataPluginPath(); + if (plugin_path) { + np_plugin = LoadLibrary(plugin_path); + } + + if (!np_plugin) { + plugin_path = GetProgramFilesPluginPath(); + if (plugin_path) { + np_plugin = LoadLibrary(plugin_path); + } + + if (!np_plugin) { + // As a last-ditch attempt, try to load the plug-in using the system + // library path. + np_plugin = LoadLibrary(kPluginName); + if (!np_plugin) { + ATLASSERT(false && "Unable to load plugin module."); + return E_FAIL; + } + } + } + } + + // Load and initialize the plug-in with the current window settings. + scoped_ptr<NPPluginProxy> plugin_proxy(new NPPluginProxy); + if (!plugin_proxy->MapEntryPoints(np_plugin)) { + FreeLibrary(np_plugin); + return E_FAIL; + } + + *proxy_instance = plugin_proxy.release(); + return S_OK; +} diff --git a/o3d/plugin/npapi_host_control/win/np_plugin_proxy.h b/o3d/plugin/npapi_host_control/win/np_plugin_proxy.h new file mode 100644 index 0000000..0c81367 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/np_plugin_proxy.h @@ -0,0 +1,153 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// File declaring a class wrapping the raw npapi interface as exported from +// a Mozilla plug-in. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_PLUGIN_PROXY_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_PLUGIN_PROXY_H_ + +#include <vector> +#include "third_party/npapi/files/include/npupp.h" + +class NPBrowserProxy; +struct INPObjectProxy; +class StreamOperation; + +typedef NPError (__stdcall *NP_InitializeFunc)(NPNetscapeFuncs* functions); +typedef NPError (__stdcall *NP_GetEntryPointsFunc)(NPPluginFuncs* functions); +typedef NPError (__stdcall *NP_ShutdownFunc)(); + +class NPPluginProxy { + public: + ~NPPluginProxy(); + + // Initializes and binds this instance to the npapi plugin exported by + // the given module. Note that the object takes control of the lifetime of + // module, and will unload it at instance destruction time. + // Parameters: + // browser_proxy: Browser environment in which the plug-in will reside. + // window: NPWindow structure initialized for the plug-in. + // argument_names: Array of string-argument names to be passed to the + // construction routine NPP_New. + // argument_values: Array of string-argument values to be passed to the + // construction routine NPP_New. + // Returns: + // true if the plugin successfully loaded and initialized in the provided + // window. + bool Init(NPBrowserProxy* browser_proxy, + const NPWindow& window, + const std::vector<CStringA>& argument_names, + const std::vector<CStringA>& argument_values); + + // Frees all resources allocated in Init, and blocks on all pending stream + // operations. + void TearDown(); + + // Get the 'v-table' interface for the hosted plugin member functions. + const NPPluginFuncs* GetPluginFunctions() const { + return &plugin_funcs_; + } + + // Get the plugin data associated with this instance. + NPP_t* GetNPP() { + return &npp_data_; + } + + // Return the NPAPI object containing the scripting entry points for the + // plugin. + HRESULT GetScriptableObject(INPObjectProxy** scriptable_object) const; + + // Return a pointer to the NPAPI browser environment hosting the plugin. + NPBrowserProxy* browser_proxy() const { + return browser_proxy_; + } + + // Registers stream_op with the list of active stream operations. + void RegisterStreamOperation(StreamOperation* stream_op); + + // Removes stream_op from the set of active stream operations. + void UnregisterStreamOperation(StreamOperation* stream_op); + + static HRESULT Create(NPPluginProxy** instance); + + private: + // Basic constructor that does not perform any plugin-specific operations. + // Simply prepares the structure for initialization. + NPPluginProxy(); + + // Stores pointers to the NPAPI entry points present in the passed in module. + // This routine also performs one-time initialization of the plug-in, + // but does not create a live instance. + // loaded_module: Handle to a loaded module containing exports for a npapi + // plugin. + // Returns: + // true if the NPAPI plugin entry points were present in the module. + bool MapEntryPoints(HMODULE loaded_module); + + // Pointer to the npapi browser environment in which the plugin lives. + // A smart pointer is not used, as this is a back-pointer. + NPBrowserProxy* browser_proxy_; + + // Cached scritable object for interacting with the plugin. + CComPtr<INPObjectProxy> scriptable_object_; + + // Cache of plugin instance member functions. + NPPluginFuncs plugin_funcs_; + + // Pointers to the three main entry points of the plug-in. + NP_InitializeFunc NP_Initialize_; + NP_GetEntryPointsFunc NP_GetEntryPoints_; + NP_ShutdownFunc NP_Shutdown_; + + // The handle to the loaded plugin module. The plugin unloads this module + // upon destruction. + HMODULE plugin_module_; + + // Plugin instance data passed to all plugin-calls. + NPP_t npp_data_; + + typedef std::vector<StreamOperation*> StreamOpArray; + + // The set of currently pending/downloading streaming operations spawned + // by the plugin. + StreamOpArray active_stream_ops_; + + // Global count of the number of currently live plugin instances. Used + // to ensure that NP_Initialize and NP_Shutdown are called only once + // per loading of the plugin module. + static int kPluginInstanceCount; + + DISALLOW_COPY_AND_ASSIGN(NPPluginProxy); +}; + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_NP_PLUGIN_PROXY_H_ diff --git a/o3d/plugin/npapi_host_control/win/npapi_host_control.cc b/o3d/plugin/npapi_host_control/win/npapi_host_control.cc new file mode 100644 index 0000000..4d1223e --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/npapi_host_control.cc @@ -0,0 +1,73 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// File implements the ActiveX entry points and module class for the +// NPAPI ActiveX host control + +#include "plugin/npapi_host_control/win/module.h" +#include "plugin/npapi_host_control/win/resource.h" + +namespace { +NPAPIHostControlModule atl_module; +} // unnamed namespace + +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, + LPVOID lpReserved) { + return atl_module.DllMain(dwReason, lpReserved); +} + + +// Used to determine whether the DLL can be unloaded by OLE. +STDAPI DllCanUnloadNow(void) { + return atl_module.DllCanUnloadNow(); +} + + +// Returns a class factory to create an object of the requested type +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { + return atl_module.DllGetClassObject(rclsid, riid, ppv); +} + + +// Adds entries to the system registry. +STDAPI DllRegisterServer(void) { + // Registers object, typelib and all interfaces in typelib + HRESULT hr = atl_module.DllRegisterServer(); + return hr; +} + + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer(void) { + HRESULT hr = atl_module.DllUnregisterServer(); + return hr; +} diff --git a/o3d/plugin/npapi_host_control/win/npapi_host_control.def b/o3d/plugin/npapi_host_control/win/npapi_host_control.def new file mode 100644 index 0000000..fe80c77 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/npapi_host_control.def @@ -0,0 +1,9 @@ +; npapi_host_control.def : Declares the module parameters. + +LIBRARY "o3d_host.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/o3d/plugin/npapi_host_control/win/npapi_host_control.idl b/o3d/plugin/npapi_host_control/win/npapi_host_control.idl new file mode 100644 index 0000000..f1f079b --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/npapi_host_control.idl @@ -0,0 +1,100 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// npapi_host2.idl : IDL source for npapi_host2 +// + +// This file will be processed by the MIDL tool to +// produce the type library (npapi_host2.tlb) and marshalling code. + +#include "olectl.h" +import "dispex.idl"; +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(56D79537-181C-4A38-ADF5-E12EC24D7FC7), + dual, + nonextensible, + helpstring("IHostControl Interface"), + pointer_default(unique) +] +interface IHostControl : IDispatchEx { + // Note the assignement of the ids here: These values will not conflict + // with the auto-generated ids for the hosted NPAPI plugin object. + [propget, helpstring("The description of the installed plugin."), id(1)] + HRESULT description([out, retval] BSTR* returned_description); + [propget, helpstring("The name of the installed plugin."), id(2)] + HRESULT name([out, retval] BSTR* returned_name); +}; + +[ + object, + uuid(89681DED-6CE8-407f-989C-C4FEDE5330A8), + pointer_default(unique) +] +interface INPObjectProxy : IDispatchEx { + // The following set of routines are not remoteable, as they all reference + // a void pointer, which is relevant to the in-proc instance of classes + // implementing this interface. + [local] HRESULT GetNPObjectInstance([out] void ** instance); + [local] HRESULT SetBrowserProxy([in] void* browser_proxy); + [local] HRESULT SetHostedObject([in] void* hosted_object); + [local] HRESULT ReleaseHosted(); +}; + +[ + uuid(D4F6E31C-E952-48FE-9833-6AE308BD79C6), + version(1.0), + helpstring("npapi_host2 1.0 Type Library") +] +library npapi_host_controlLib +{ + importlib("stdole2.tlb"); + [ + uuid(9666A772-407E-4F90-BC37-982E8160EB2D), + //control, + helpstring("HostControl Class") + ] + coclass HostControl + { + [default] interface IHostControl; + }; + + [ + uuid(1D68424D-7A71-4b61-AE5C-56DBCD8B0E53), + ] + coclass NPObjectProxy + { + [default] interface INPObjectProxy; + }; +}; diff --git a/o3d/plugin/npapi_host_control/win/npapi_host_control.rc b/o3d/plugin/npapi_host_control/win/npapi_host_control.rc new file mode 100644 index 0000000..5eee891 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/npapi_host_control.rc @@ -0,0 +1,122 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "1 TYPELIB ""npapi_host_control.tlb""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Google Inc." + VALUE "FileDescription", O3D_PLUGIN_DESCRIPTION + VALUE "FileVersion", "1.0.0.0" + VALUE "LegalCopyright", "Copyright 2009 Google Inc. All Rights Reserved." + VALUE "InternalName", "o3d_host.dll" + VALUE "OriginalFilename", "o3d_host.dll" + VALUE "ProductName", O3D_PLUGIN_NAME + VALUE "ProductVersion", O3D_PLUGIN_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_NPAPI_HOST_CONTROL REGISTRY "npapi_host_control.rgs" +IDR_HOSTCONTROL REGISTRY "host_control.rgs" +IDR_NPOBJECTPROXY REGISTRY "np_object_proxy.rgs" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PROJNAME "npapi_host_control" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +1 TYPELIB "npapi_host_control.tlb" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/o3d/plugin/npapi_host_control/win/npapi_host_control.rgs b/o3d/plugin/npapi_host_control/win/npapi_host_control.rgs new file mode 100644 index 0000000..1db1fc5 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/npapi_host_control.rgs @@ -0,0 +1,17 @@ +HKCU +{ + Software + { + Classes + { + NoRemove AppID + { + '%APPID%' = s 'o3d_host' + 'o3d_host.DLL' + { + val AppID = s '%APPID%' + } + } + } + } +} diff --git a/o3d/plugin/npapi_host_control/win/precompile.cc b/o3d/plugin/npapi_host_control/win/precompile.cc new file mode 100644 index 0000000..832211b --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/precompile.cc @@ -0,0 +1,33 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "plugin/npapi_host_control/win/precompile.h" diff --git a/o3d/plugin/npapi_host_control/win/precompile.h b/o3d/plugin/npapi_host_control/win/precompile.h new file mode 100644 index 0000000..ef3d0b0 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/precompile.h @@ -0,0 +1,83 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// Precompiled header generated by Visual Studio 2008. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_PRECOMPILE_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_PRECOMPILE_H_ + +#ifndef STRICT +#define STRICT +#endif + +// Modify the following defines if you have to target a platform prior to the +// ones specified below. +// Refer to MSDN for the latest info on corresponding values for +// different platforms. +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target + // other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target + // other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or + // later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to + // target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target + // other versions of IE. +#endif + +#define _ATL_APARTMENT_THREADED +#define _ATL_NO_AUTOMATIC_NAMESPACE + +// Some CString constructors will be explicit. +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS + + +#include <atlbase.h> +#include <atlcom.h> +#include <atlstr.h> + +#include "base/basictypes.h" +#include "plugin/npapi_host_control/win/resource.h" + +using namespace ATL; + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_PRECOMPILE_H_ diff --git a/o3d/plugin/npapi_host_control/win/resource.h b/o3d/plugin/npapi_host_control/win/resource.h new file mode 100644 index 0000000..010ae5e --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/resource.h @@ -0,0 +1,55 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_RESOURCE_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_RESOURCE_H_ + +// Microsoft Visual C++ generated include file. Used by npapi_host2.rc +#define IDS_PROJNAME 100 +#define IDR_NPAPI_HOST_CONTROL 101 +#define IDB_HOSTCONTROL 102 +#define IDR_HOSTCONTROL 103 +#define IDB_NPOBJECTPROXY 104 +#define IDR_NPOBJECTPROXY 105 + + +// Next default values for new objects. +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 106 +#endif +#endif + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_RESOURCE_H_ diff --git a/o3d/plugin/npapi_host_control/win/stream_operation.cc b/o3d/plugin/npapi_host_control/win/stream_operation.cc new file mode 100644 index 0000000..9cfed9e --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/stream_operation.cc @@ -0,0 +1,756 @@ +// Copyright 2009, Google Inc. All rights reserved. +// Portions of this file were adapted from the Mozilla project. +// See https://developer.mozilla.org/en/ActiveX_Control_for_Hosting_Netscape_Plug-ins_in_IE +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adam Lock <adamlock@eircom.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#include "plugin/npapi_host_control/win/stream_operation.h" +#include "plugin/npapi_host_control/win/host_control.h" +#include "plugin/npapi_host_control/win/np_plugin_proxy.h" + +namespace { + +// The following classes are used to package arguments for interacting with +// the hosted NPAPI plug-in. +struct NPPDestroyStreamArgs { + NPP npp_; + NPStream *stream_; + NPReason reason_; + NPError *return_code_; +}; + +struct NPPNewStreamArgs { + NPP npp_; + NPMIMEType type_; + NPStream *stream_; + NPBool seekable_; + uint16 *stype_; + NPError *return_code_; +}; + +struct NPPAsFileArgs { + NPP npp_; + NPStream *stream_; + const char* fname_; +}; + +struct NPPUrlNotifyArgs { + NPP npp_; + const char* url_; + NPReason reason_; + void *notify_data_; +}; + +struct NPPWriteReadyArgs { + NPP npp_; + NPStream *stream_; + int32 *return_value_; +}; + +struct NPPWriteArgs { + NPP npp_; + NPStream *stream_; + int32 offset_; + int32 len_; + void* buffer_; + int32 *return_value_; +}; + +// Helper function that constructs the full url from a moniker associated with +// a base 'left-side' url prefix, and a stream operation. +HRESULT ConstructFullURLPath(const StreamOperation& stream_operation, + IMoniker* base_moniker, + CString* out_string) { + ATLASSERT(base_moniker && out_string); + + HRESULT hr = S_OK; + CComPtr<IMoniker> full_url_moniker; + if (FAILED(hr = CreateURLMonikerEx(base_moniker, + stream_operation.GetURL(), + &full_url_moniker, + URL_MK_UNIFORM))) { + return hr; + } + + // Determine if the monikers share a common prefix. If they do, then + // we can allow the data fetch to proceed - The same origin criteria has been + // satisfied. + CComPtr<IMoniker> prefix_moniker; + bool urls_contain_prefix = false; + hr = MonikerCommonPrefixWith(base_moniker, full_url_moniker, &prefix_moniker); + if (SUCCEEDED(hr)) { + urls_contain_prefix = true; + } + + CComPtr<IBindCtx> bind_context; + if (FAILED(hr = CreateBindCtx(0, &bind_context))) { + return hr; + } + + CComPtr<IMalloc> malloc_interface; + if (FAILED(hr = CoGetMalloc(1, &malloc_interface))) { + return hr; + } + + LPOLESTR full_url_path = NULL; + if (FAILED(hr = full_url_moniker->GetDisplayName(bind_context, + NULL, + &full_url_path))) { + return hr; + } + + if (!urls_contain_prefix) { + // If the urls do not contain a common prefix, validate the access request + // based on the fully qualified uri's. + LPOLESTR base_path_name = NULL; + if (FAILED(hr = base_moniker->GetDisplayName(bind_context, + NULL, + &base_path_name))) { + malloc_interface->Free(full_url_path); + return hr; + } + } + + *out_string = full_url_path; + malloc_interface->Free(full_url_path); + + return S_OK; +} + +// Helper routine implementing a custom version of SendMessage(...). +// The StreamingOperation class uses windows messages to communicate transfer +// notifications to the plug-in. This is required so that the plug-in will +// receive notifications synchronously on the main-browser thread. +// SendMessage is not appropriate, because messages 'sent' to a window are NOT +// processed during DispatchMessage, but instead during GetMessage, PeekMessage +// and others. Because the JScript engine periodically peeks the message +// queue during JavaScript evaluation, the plug-in would be notified, and +// potentially call back into the JavaScript environment causing unexpected +// reentrancy. +HRESULT CustomSendMessage(HWND window_handle, UINT message, LPARAM l_param) { + // Mimic the behaviour of SendMessage by posting to the window, and then + // blocking on an event. Note that the message handlers must set + // the event. + HANDLE local_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!local_event) { + return E_FAIL; + } + + if (!PostMessage(window_handle, message, + reinterpret_cast<WPARAM>(&local_event), l_param)) { + CloseHandle(local_event); + return E_FAIL; + } + + HRESULT hr; + static const unsigned int kWaitTimeOut = 120000; + bool done = false; + while (!done) { + DWORD wait_code = MsgWaitForMultipleObjects(1, + &local_event, + FALSE, + kWaitTimeOut, + QS_ALLINPUT); + switch (wait_code) { + case WAIT_OBJECT_0: + hr = S_OK; + done = true; + break; + case WAIT_OBJECT_0 + 1: + MSG msg; + GetMessage(&msg, NULL, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + case WAIT_TIMEOUT: + // If the plug-in is busy processing JavaScript code, it's possible + // that we may time-out here. We don't break out of the loop, because + // the event will eventually be signaled when the JS is done + // processing. + ATLASSERT(false && "Time out waiting for response from main thread."); + break; + default: + ATLASSERT(false && + "Critical failure waiting for response from main thread."); + hr = E_FAIL; + done = true; + break; + } + } + + CloseHandle(local_event); + return hr; +} + +} // unnamed namespace + +StreamOperation::StreamOperation() + : stream_size_(0), + stream_received_(0), + stream_type_(NP_NORMAL), + temp_file_(NULL), + thread_handle_(NULL), + cancel_requested_(false) { + memset(&np_stream_, 0, sizeof(np_stream_)); +} + +StreamOperation::~StreamOperation() { +} + +void StreamOperation::OnFinalMessage(HWND hWnd) { + CWindowImplBase::OnFinalMessage(hWnd); + if (owner_) { + owner_->UnregisterStreamOperation(this); + } + + // The binding holds a reference to the stream operation, which forms + // a cyclic reference chain. Release the binding so that both objects + // can be destroyed. + binding_ = NULL; + + // The object has an artificially boosted reference count to ensure that it + // stays alive as long as it may receive window messages. Release this + // reference here, and potentially free the instance. + Release(); +} + +HRESULT STDMETHODCALLTYPE StreamOperation::OnStartBinding(DWORD dwReserved, + IBinding *pib) { + binding_ = pib; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOperation::GetPriority(LONG *pnPriority) { + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE StreamOperation::OnLowResource(DWORD reserved) { + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOperation::OnProgress(ULONG ulProgress, + ULONG ulProgressMax, + ULONG ulStatusCode, + LPCWSTR szStatusText) { + // Capture URL re-directs and MIME-type status notifications. + switch (ulStatusCode) { + case BINDSTATUS_BEGINDOWNLOADDATA: + case BINDSTATUS_REDIRECTING: + url_ = szStatusText; + break; + case BINDSTATUS_MIMETYPEAVAILABLE: + content_type_ = szStatusText; + break; + default: + break; + } + + // Track the current progress of the streaming transfer. + stream_size_ = ulProgressMax; + stream_received_ = ulProgress; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOperation::OnStopBinding(HRESULT hresult, + LPCWSTR szError) { + NPReason reason = SUCCEEDED(hresult) ? NPRES_DONE : NPRES_NETWORK_ERR; + USES_CONVERSION; + + // Notify the calling plug-in that the transfer has completed. + if (stream_type_ == NP_ASFILE || stream_type_ == NP_ASFILEONLY) { + if (temp_file_) { + fclose(temp_file_); + } + + if (reason == NPRES_DONE) { + NPPAsFileArgs arguments = { + owner_->GetNPP(), + GetNPStream(), + W2A(temp_file_name_) + }; + CustomSendMessage(m_hWnd, WM_NPP_ASFILE, + reinterpret_cast<LPARAM>(&arguments)); + } + } + + if (reason == NPRES_DONE) { + NPError error_return; + NPPDestroyStreamArgs destroy_stream_args = { + owner_->GetNPP(), + GetNPStream(), + reason, + &error_return + }; + CustomSendMessage(m_hWnd, WM_NPP_DESTROYSTREAM, + reinterpret_cast<LPARAM>(&destroy_stream_args)); + ATLASSERT(NPERR_NO_ERROR == error_return); + } + + NPPUrlNotifyArgs url_args = { + owner_->GetNPP(), + W2A(url_), + reason, + GetNotifyData() + }; + CustomSendMessage(m_hWnd, WM_NPP_URLNOTIFY, + reinterpret_cast<LPARAM>(&url_args)); + + // Clear the intermediate file from the cache. + _wremove(temp_file_name_); + temp_file_name_ = L""; + + // The operation has completed, so tear-down the intermediate window, and + // exit the worker thread. + CustomSendMessage(m_hWnd, WM_TEAR_DOWN, 0); + PostQuitMessage(0); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOperation::GetBindInfo(DWORD *grfBINDF, + BINDINFO *pbindinfo) { + // Request an asynchronous transfer of the data. + *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | + BINDF_GETNEWESTVERSION; + + int cbSize = pbindinfo->cbSize; + memset(pbindinfo, 0, cbSize); + pbindinfo->cbSize = cbSize; + pbindinfo->dwBindVerb = BINDVERB_GET; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOperation::OnDataAvailable( + DWORD grfBSCF, + DWORD dwSize, + FORMATETC *pformatetc, + STGMEDIUM *pstgmed) { + if (pstgmed->tymed != TYMED_ISTREAM || !pstgmed->pstm) { + return S_OK; + } + + // Don't bother processing any data if the stream has been canceled. + if (cancel_requested_) { + return S_OK; + } + + // Notify the plugin that a new stream has been opened. + if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) { + USES_CONVERSION; + np_stream_.url = W2CA(url_); + np_stream_.end = stream_size_; + np_stream_.lastmodified = 0; + np_stream_.notifyData = GetNotifyData(); + + uint16 stream_type = NP_NORMAL; + + NPError np_error; + NPPNewStreamArgs new_stream_args = { + owner_->GetNPP(), + const_cast<char*>(W2CA(GetContentType())), + GetNPStream(), + FALSE, + &stream_type, + &np_error + }; + CustomSendMessage(m_hWnd, WM_NPP_NEWSTREAM, + reinterpret_cast<LPARAM>(&new_stream_args)); + if (np_error != NPERR_NO_ERROR) { + return E_FAIL; + } + + // Cache the stream type requested by the plug-in. + stream_type_ = stream_type; + } + + if (grfBSCF & BSCF_INTERMEDIATEDATANOTIFICATION || + grfBSCF & BSCF_LASTDATANOTIFICATION) { + // Read all of the available data, and pass it to the plug-in, if requested. + HRESULT hr; + char local_data[16384]; + int bytes_received_total = 0; + // If a large number of bytes have been received, then this loop can + // take a long time to complete - which will block the user from leaving + // the page as the plug-in waits for all transfers to complete. We + // add a check on cancel_requested_ to allow for early bail-out. + while (bytes_received_total < dwSize && + !cancel_requested_) { + int bytes_to_read = dwSize - bytes_received_total; + unsigned long bytes_read = 0; + + if (bytes_to_read > sizeof(local_data)) { + bytes_to_read = sizeof(local_data); + } + + if (stream_type_ == NP_NORMAL || stream_type_ == NP_ASFILE) { + int32 bytes_to_accept; + NPPWriteReadyArgs write_ready_args = { + owner_->GetNPP(), + GetNPStream(), + &bytes_to_accept + }; + CustomSendMessage(m_hWnd, WM_NPP_WRITEREADY, + reinterpret_cast<LPARAM>(&write_ready_args)); + + if (bytes_to_read > bytes_to_accept) { + bytes_to_read = bytes_to_accept; + } + } + + // If the plug-in has indicated that it is not prepared to read any data, + // then bail early. + if (bytes_to_read == 0) { + break; + } + + hr = pstgmed->pstm->Read(local_data, bytes_to_read, &bytes_read); + if (FAILED(hr) || S_FALSE == hr) { + break; + } + + // Pass the data to the plug-in. + if (stream_type_ == NP_NORMAL || stream_type_ == NP_ASFILE) { + int consumed_bytes; + NPPWriteArgs write_args = { + owner_->GetNPP(), + GetNPStream(), + bytes_received_total, + bytes_read, + local_data, + &consumed_bytes + }; + CustomSendMessage(m_hWnd, WM_NPP_WRITE, + reinterpret_cast<LPARAM>(&write_args)); + ATLASSERT(consumed_bytes == bytes_read); + } + + if (stream_type_ == NP_ASFILE || stream_type_ == NP_ASFILEONLY) { + // If the plug-in requested access to the data through a file, then + // create a temporary file and write the data to it. + if (!temp_file_) { + temp_file_name_= _wtempnam(NULL, L"npapi_host_temp"); + _wfopen_s(&temp_file_, temp_file_name_, L"wb"); + } + fwrite(local_data, bytes_read, 1, temp_file_); + } + bytes_received_total += bytes_read; + } + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOperation::OnObjectAvailable(REFIID riid, + IUnknown *punk) { + return S_OK; +} + +HRESULT StreamOperation::OpenURL(NPPluginProxy *owning_plugin, + const wchar_t *url, + void *notify_data) { + // The StreamOperation instance is created with a ref-count of zero, + // so we explicitly attach a CComPtr to the object to boost the count, and + // manage the lifetime of the object. + CComObject<StreamOperation> *stream_ptr; + CComObject<StreamOperation>::CreateInstance(&stream_ptr); + CComPtr<CComObject<StreamOperation> > stream_object = stream_ptr; + if (!stream_object) { + return E_OUTOFMEMORY; + } + + CComPtr<CHostControl> host_control = + owning_plugin->browser_proxy()->GetHostingControl(); + CComPtr<IMoniker> base_url_moniker = host_control->GetURLMoniker(); + + stream_object->SetURL(url); + stream_object->SetNotifyData(notify_data); + stream_object->SetOwner(owning_plugin); + + CString full_path; + HRESULT hr; + if (FAILED(hr = ConstructFullURLPath(*stream_object, + base_url_moniker, + &full_path))) { + return hr; + } + + stream_object->SetFullURL(full_path); + + // Create an object window on this thread that will be sent messages when + // something happens on the worker thread. + HWND temporary_window = stream_object->Create(HWND_DESKTOP); + ATLASSERT(temporary_window); + if (!temporary_window) { + return E_FAIL; + } + + // Artificially increment the reference count of the stream_object instance + // to ensure that the object will not be deleted until WM_NC_DESTROY is + // processed and OnFinalMessage is invoked. + // Note: The operator-> is not used, because it returns a type overloading + // the public access of AddRef/Release. + (*stream_object).AddRef(); + + stream_object->thread_handle_ = reinterpret_cast<HANDLE>( + _beginthreadex(NULL, + 0, + WorkerProc, + static_cast<void*>(stream_object), + CREATE_SUSPENDED, + NULL)); + ATLASSERT(stream_object->thread_handle_); + if (!stream_object->thread_handle_) { + stream_object->DestroyWindow(); + return E_FAIL; + } + + owning_plugin->RegisterStreamOperation(stream_object); + if (!ResumeThread(stream_object->thread_handle_)) { + owning_plugin->UnregisterStreamOperation(stream_object); + stream_object->DestroyWindow(); + // If the thread never resumed, then we can safely terminate it here - it + // has not had a chance to allocate any resources that would be leaked. + TerminateThread(stream_object->thread_handle_, 0); + return E_FAIL; + } + + return S_OK; +} + +unsigned int __stdcall StreamOperation::WorkerProc(void* worker_arguments) { + CComObject<StreamOperation> *stream_object = + static_cast<CComObject<StreamOperation> *>(worker_arguments); + ATLASSERT(stream_object); + + // Initialize the COM run-time for this new thread. + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + ATLASSERT(SUCCEEDED(hr) && "Failure to initialize worker COM apartment."); + if (FAILED(hr)) { + CustomSendMessage(stream_object->m_hWnd, WM_TEAR_DOWN, 0); + CoUninitialize(); + return 0; + } + + { + // Get the ActiveX control so the request is within the context of the + // plugin. Among other things, this lets the browser reject file:// uris + // when the page is loaded over http://. + CComPtr<IUnknown> caller; + stream_object->owner_->browser_proxy()->GetHostingControl()->QueryInterface( + IID_IUnknown, + reinterpret_cast<void**>(&caller)); + + // Note that the OnStopBinding(...) routine, which is always called, will + // post WM_QUIT to this thread. + hr = URLOpenStream(caller, stream_object->GetFullURL(), 0, + static_cast<IBindStatusCallback*>(stream_object)); + + // Pump messages until WM_QUIT arrives + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + CoUninitialize(); + return 0; +} + +LRESULT StreamOperation::OnNPPNewStream(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled) { + NPPNewStreamArgs *args = reinterpret_cast<NPPNewStreamArgs*>(lParam); + ATLASSERT(args); + + // If the stream was canceled, don't pass the notification to the plug-in. + if (!cancel_requested_) { + *args->return_code_ = owner_->GetPluginFunctions()->newstream( + args->npp_, + args->type_, + args->stream_, + args->seekable_, + args->stype_); + } else { + *args->return_code_ = NPERR_GENERIC_ERROR; + } + + if (wParam) { + HANDLE* event_handle = reinterpret_cast<HANDLE*>(wParam); + SetEvent(*event_handle); + } + return 0; +} + +LRESULT StreamOperation::OnNPPDestroyStream(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled) { + NPPDestroyStreamArgs *args = reinterpret_cast<NPPDestroyStreamArgs*>(lParam); + ATLASSERT(args); + + // If the stream was canceled, don't pass the notification to the plug-in. + if (!cancel_requested_) { + *args->return_code_ = owner_->GetPluginFunctions()->destroystream( + args->npp_, + args->stream_, + args->reason_); + } else { + *args->return_code_ = NPERR_NO_ERROR; + } + + if (wParam) { + HANDLE* event_handle = reinterpret_cast<HANDLE*>(wParam); + SetEvent(*event_handle); + } + return 0; +} + +LRESULT StreamOperation::OnNPPAsFile(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled) { + NPPAsFileArgs *args = reinterpret_cast<NPPAsFileArgs*>(lParam); + ATLASSERT(args); + + // If the stream was canceled, don't pass the notification to the plug-in. + if (!cancel_requested_) { + owner_->GetPluginFunctions()->asfile(args->npp_, args->stream_, + args->fname_); + } + + if (wParam) { + HANDLE* event_handle = reinterpret_cast<HANDLE*>(wParam); + SetEvent(*event_handle); + } + return 0; +} + +LRESULT StreamOperation::OnNPPUrlNotify(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled) { + NPPUrlNotifyArgs *args = reinterpret_cast<NPPUrlNotifyArgs*>(lParam); + ATLASSERT(args); + + // If the stream was canceled, don't pass the notification to the plug-in. + if (!cancel_requested_) { + owner_->GetPluginFunctions()->urlnotify(args->npp_, args->url_, + args->reason_, args->notify_data_); + } + if (wParam) { + HANDLE* event_handle = reinterpret_cast<HANDLE*>(wParam); + SetEvent(*event_handle); + } + return 0; +} + +LRESULT StreamOperation::OnNPPWriteReady(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled) { + NPPWriteReadyArgs *args = reinterpret_cast<NPPWriteReadyArgs*>(lParam); + ATLASSERT(args); + + // If the stream was canceled, don't pass the notification to the plug-in. + if (!cancel_requested_) { + *args->return_value_ = owner_->GetPluginFunctions()->writeready( + args->npp_, + args->stream_); + } else { + // Indicate to the download thread that 0 bytes are ready to be received. + *args->return_value_ = 0; + } + + if (wParam) { + HANDLE* event_handle = reinterpret_cast<HANDLE*>(wParam); + SetEvent(*event_handle); + } + return 0; +} + +LRESULT StreamOperation::OnNPPWrite(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled) { + NPPWriteArgs *args = reinterpret_cast<NPPWriteArgs*>(lParam); + ATLASSERT(args); + + // If the stream was canceled, don't pass the notification to the plug-in. + if (!cancel_requested_) { + *args->return_value_ = owner_->GetPluginFunctions()->write( + args->npp_, + args->stream_, + args->offset_, + args->len_, + args->buffer_); + } else { + *args->return_value_ = args->len_; + } + + if (wParam) { + HANDLE* event_handle = reinterpret_cast<HANDLE*>(wParam); + SetEvent(*event_handle); + } + return 0; +} + +LRESULT StreamOperation::OnTearDown(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled) { + // DestroyWindow must be called on the same thread as where the window was + // constructed, so make the call here. + DestroyWindow(); + + if (wParam) { + HANDLE* event_handle = reinterpret_cast<HANDLE*>(wParam); + SetEvent(*event_handle); + } + return 0; +} + +HRESULT StreamOperation::RequestCancellation() { + ATLASSERT(binding_ && + "Cancellation request on a stream that has not been bound."); + cancel_requested_ = true; + if (binding_) { + return binding_->Abort(); + } + return S_OK; +} diff --git a/o3d/plugin/npapi_host_control/win/stream_operation.h b/o3d/plugin/npapi_host_control/win/stream_operation.h new file mode 100644 index 0000000..8149b81 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/stream_operation.h @@ -0,0 +1,271 @@ +// Copyright 2009, Google Inc. All rights reserved. +// Portions of this file were adapted from the Mozilla project. +// See https://developer.mozilla.org/en/ActiveX_Control_for_Hosting_Netscape_Plug-ins_in_IE +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adam Lock <adamlock@eircom.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +// File declaring StreamOperation class encapsulating basic support +// for the NPAPI GetURL streaming interface. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_STREAM_OPERATION_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_STREAM_OPERATION_H_ + +#include <atlwin.h> +#include <atlstr.h> +#include <urlmon.h> + +#include "third_party/npapi/files/include/npupp.h" + +class NPPluginProxy; + +#define WM_NPP_NEWSTREAM WM_USER +#define WM_NPP_ASFILE WM_USER + 1 +#define WM_NPP_DESTROYSTREAM WM_USER + 2 +#define WM_NPP_URLNOTIFY WM_USER + 3 +#define WM_NPP_WRITEREADY WM_USER + 4 +#define WM_NPP_WRITE WM_USER + 5 + +#define WM_TEAR_DOWN WM_USER + 10 + +// StreamOperation class used to provide a subset of the NPAPI GetURL* API. +// Class makes use of urlmon's IBindStatusCallback to receive notifications +// from urlmon as data is transferred. Refer to the MSDN documentation +// for information on the usage model of IBindStatusCallback. +class ATL_NO_VTABLE StreamOperation : + public CComObjectRootEx<CComMultiThreadModel>, + public CWindowImpl<StreamOperation, CWindow, CNullTraits>, + public CComCoClass<StreamOperation, &CLSID_NULL>, + public IBindStatusCallback { + public: + typedef CWindowImpl<StreamOperation, CWindow, CNullTraits> CWindowImplBase; + + StreamOperation(); + ~StreamOperation(); + + // Assign/Retrieve the url from which to stream the data. + void SetURL(const wchar_t* url) { + url_ = url; + } + + const ATL::CStringW& GetURL() const { + return url_; + } + + void SetFullURL(const wchar_t* url) { + full_url_ = url; + } + + const ATL::CStringW& GetFullURL() const { + return full_url_; + } + + // Returns the MIME-type of the data stream. + const ATL::CStringW& GetContentType() const { + return content_type_; + } + + NPStream* GetNPStream() { + return &np_stream_; + } + + HANDLE GetThreadHandle() const { + return thread_handle_; + } + + // Assign the owning plugin pointer that spawned this operation. + void SetOwner(NPPluginProxy* plugin) { + owner_ = plugin; + } + + // Assign/Retrieve the opaque NPAPI-provided callback data for the + // stream-operation. + void SetNotifyData(void *notify_data) { + notify_data_ = notify_data; + } + + void* GetNotifyData() { + return notify_data_; + } + + // Call to request that the streaming operation terminate early. After this + // has been called, no further data notifications will take place. The next, + // and last notification will be through + // IBindStatusCallback::OnStopBinding(...). + HRESULT RequestCancellation(); + +BEGIN_COM_MAP(StreamOperation) + COM_INTERFACE_ENTRY(IBindStatusCallback) +END_COM_MAP() + + // To allow interaction with non-thread-safe NPAPI plug-in modules, the + // streaming code uses Windows message pumps to serialize the interactions + // calling back into the plug-in on the thread in which the plug-in resides. + // When information about the state of the streaming request is provided + // through a IBindStatusCallback routine, the thread will post a message + // to the window created by the StreamOperation instance. Because this + // window will reside in the same thread as the calling plug-in, we are + // guaranteed serialization and mutual exclusion of the handling of the + // routines below. +BEGIN_MSG_MAP(StreamOperation) + MESSAGE_HANDLER(WM_NPP_NEWSTREAM, OnNPPNewStream) + MESSAGE_HANDLER(WM_NPP_ASFILE, OnNPPAsFile) + MESSAGE_HANDLER(WM_NPP_DESTROYSTREAM, OnNPPDestroyStream) + MESSAGE_HANDLER(WM_NPP_URLNOTIFY, OnNPPUrlNotify) + MESSAGE_HANDLER(WM_NPP_WRITEREADY, OnNPPWriteReady) + MESSAGE_HANDLER(WM_NPP_WRITE, OnNPPWrite) + MESSAGE_HANDLER(WM_TEAR_DOWN, OnTearDown); +END_MSG_MAP() + + // Helper function called in response to WM_TEAR_DOWN to destroy class + // resources on the appropriate thread. + LRESULT OnTearDown(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled); + + // The following OnNPP... routines forward the respective notification to + // the plugin that spawned the data transmission. + LRESULT OnNPPNewStream(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled); + + LRESULT OnNPPDestroyStream(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled); + + LRESULT OnNPPAsFile(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled); + + LRESULT OnNPPUrlNotify(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled); + + LRESULT OnNPPWriteReady(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled); + + LRESULT OnNPPWrite(UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL& bHandled); + + // Methods implementing the IBindStatusCallback interface. Refer to + // the MSDN documentation for the expected behaviour of these routines. + virtual HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD dwReserved, + IBinding *pib); + + virtual HRESULT STDMETHODCALLTYPE GetPriority(LONG *pnPriority); + + virtual HRESULT STDMETHODCALLTYPE OnLowResource(DWORD reserved); + + virtual HRESULT STDMETHODCALLTYPE OnProgress(ULONG ulProgress, + ULONG ulProgressMax, + ULONG ulStatusCode, + LPCWSTR szStatusText); + + virtual HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT hresult, + LPCWSTR szError); + + virtual HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD *grfBINDF, + BINDINFO *pbindinfo); + + virtual HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD grfBSCF, + DWORD dwSize, + FORMATETC *pformatetc, + STGMEDIUM *pstgmed); + + virtual HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID riid, + IUnknown *punk); + + static HRESULT OpenURL(NPPluginProxy *owning_plugin, const wchar_t* url, + void *notify_data); + + virtual void OnFinalMessage(HWND hWnd); + + DECLARE_PROTECT_FINAL_CONSTRUCT(); + private: + // Callback object for interacting with the urlmon streaming manager. + ATL::CComPtr<IBinding> binding_; + + // The url from which the data is fetched, and the associated MIME-type. + ATL::CStringW url_; + ATL::CStringW full_url_; + ATL::CStringW content_type_; + + // Back-pointer to the plug-in instance requesting the data transfer. + NPPluginProxy *owner_; + + // Opaque data specified at request initiation that is passed back to the + // plug-in during call-back invocation. + void *notify_data_; + + NPStream np_stream_; + + int stream_size_; + int stream_received_; + + // Cache of the type of stream requested by the plug-in. May be one of: + // NP_NORMAL, NP_ASFILE, NP_ASFILEONLY. + int stream_type_; + + // Pointer to file handle used to save incoming data if the stream type is + // NP_ASFILE or NP_ASFILEONLY. + FILE* temp_file_; + + // Temporary file name. + CStringW temp_file_name_; + + // Handle to the worker-thread where the streaming notifications are received. + HANDLE thread_handle_; + + // Value used to indicate the streaming operation should stop processing + // input data. + bool cancel_requested_; + + static unsigned int __stdcall WorkerProc(void *worker_arguments); + + DISALLOW_COPY_AND_ASSIGN(StreamOperation); +}; + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_STREAM_OPERATION_H_ diff --git a/o3d/plugin/npapi_host_control/win/variant_utils.cc b/o3d/plugin/npapi_host_control/win/variant_utils.cc new file mode 100644 index 0000000..fd0e441 --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/variant_utils.cc @@ -0,0 +1,207 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "plugin/npapi_host_control/win/variant_utils.h" +#include "base/scoped_ptr.h" +#include "plugin/npapi_host_control/win/dispatch_proxy.h" + +void VariantToNPVariant(NPBrowserProxy* browser_proxy, + const VARIANT* source, + NPVariant* destination) { + ATLASSERT(!(source->vt & VT_ARRAY)); + + switch (source->vt) { + case VT_EMPTY: + VOID_TO_NPVARIANT(*destination); + break; + case VT_NULL: + NULL_TO_NPVARIANT(*destination); + break; + case VT_I2: + INT32_TO_NPVARIANT(source->iVal, *destination); + break; + case VT_I4: + INT32_TO_NPVARIANT(source->intVal, *destination); + break; + case VT_R4: + DOUBLE_TO_NPVARIANT(source->fltVal, *destination); + break; + case VT_R8: + DOUBLE_TO_NPVARIANT(source->dblVal, *destination); + break; + case VT_CY: + case VT_DATE: + ATLASSERT(false); + break; + case VT_BSTR: { + // BSTR objects may be NULL to indicate an empty string. + if (source->bstrVal) { + int required_size = WideCharToMultiByte(CP_UTF8, 0, source->bstrVal, + -1, NULL, 0, NULL, NULL); + ATLASSERT(required_size != 0); + + char* string_contents = static_cast<char*>( + browser_proxy->GetBrowserFunctions()->memalloc(required_size)); + WideCharToMultiByte(CP_UTF8, 0, source->bstrVal, -1, string_contents, + required_size, NULL, NULL); + STRINGN_TO_NPVARIANT(string_contents, required_size - 1, + *destination); + } else { + char* string_contents = static_cast<char*>( + browser_proxy->GetBrowserFunctions()->memalloc(1)); + string_contents[0] = 0; + STRINGN_TO_NPVARIANT(string_contents, 0, *destination); + } + break; + } + case VT_DISPATCH: + OBJECT_TO_NPVARIANT(browser_proxy->GetNPObject(source->pdispVal), + *destination); + break; + case VT_ERROR: + ATLASSERT(false); + break; + case VT_BOOL: + BOOLEAN_TO_NPVARIANT(source->boolVal, *destination); + break; + case VT_VARIANT: + case VT_UNKNOWN: + case VT_DECIMAL: + ATLASSERT(false); + break; + case VT_I1: + INT32_TO_NPVARIANT(source->iVal, *destination); + break; + case VT_UI1: + INT32_TO_NPVARIANT(source->iVal, *destination); + break; + case VT_UI2: + INT32_TO_NPVARIANT(source->iVal, *destination); + break; + case VT_UI4: + INT32_TO_NPVARIANT(source->iVal, *destination); + break; + case VT_I8: + case VT_UI8: + ATLASSERT(false); + break; + case VT_INT: + INT32_TO_NPVARIANT(source->iVal, *destination); + break; + case VT_UINT: + INT32_TO_NPVARIANT(source->iVal, *destination); + break; + case VT_VOID: + VOID_TO_NPVARIANT(*destination); + break; + case VT_HRESULT: + case VT_PTR: + case VT_SAFEARRAY: + case VT_CARRAY: + case VT_USERDEFINED: + case VT_LPSTR: + case VT_LPWSTR: + case VT_RECORD: + case VT_INT_PTR: + case VT_UINT_PTR: + case VT_FILETIME: + case VT_BLOB: + case VT_STREAM: + case VT_STORAGE: + case VT_STREAMED_OBJECT: + case VT_STORED_OBJECT: + case VT_BLOB_OBJECT: + case VT_CF: + case VT_CLSID: + case VT_VERSIONED_STREAM: + case VT_BSTR_BLOB: + case VT_VECTOR: + case VT_ARRAY: + case VT_BYREF: + case VT_RESERVED: + case VT_ILLEGAL: + ATLASSERT(false); + break; + default: + break; + } +} + +void NPVariantToVariant(NPBrowserProxy* browser_proxy, + const NPVariant* source, + CComVariant* destination) { + if (!destination) { + return; + } + + switch (source->type) { + case NPVariantType_Void: + destination->ChangeType(VT_VOID, NULL); + break; + case NPVariantType_Null: + destination->ChangeType(VT_NULL, NULL); + break; + case NPVariantType_Bool: + *destination = source->value.boolValue; + break; + case NPVariantType_Int32: + *destination = source->value.intValue; + break; + case NPVariantType_Double: + *destination = source->value.doubleValue; + break; + case NPVariantType_String: { + int required_size = 0; + required_size = MultiByteToWideChar( + CP_UTF8, 0, + source->value.stringValue.utf8characters, + source->value.stringValue.utf8length, NULL, 0); + + scoped_array<wchar_t> wide_value(new wchar_t[required_size + 1]); + MultiByteToWideChar( + CP_UTF8, 0, + source->value.stringValue.utf8characters, + source->value.stringValue.utf8length, wide_value.get(), + required_size + 1); + wide_value[required_size] = 0; + + *destination = wide_value.get(); + break; + } + case NPVariantType_Object: + *destination = browser_proxy->GetDispatchObject( + source->value.objectValue); + break; + default: + ATLASSERT(false); + } +} diff --git a/o3d/plugin/npapi_host_control/win/variant_utils.h b/o3d/plugin/npapi_host_control/win/variant_utils.h new file mode 100644 index 0000000..88bf18a --- /dev/null +++ b/o3d/plugin/npapi_host_control/win/variant_utils.h @@ -0,0 +1,65 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// File declaring helper functions for conversion between ActiveX and +// NPAPI variant types. + +#ifndef O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_VARIANT_UTILS_H_ +#define O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_VARIANT_UTILS_H_ + +#include "plugin/npapi_host_control/win/np_browser_proxy.h" +#include "plugin/npapi_host_control/win/np_object_proxy.h" + +// Converts an ActiveX variant to an NPAPI variant. +// Parameters: +// browser_proxy: The emulated NPAPI browser environment, required for +// managing NPAPI string resource construction, etc. +// source: The source COM VARIANT. +// destination: The NPAPI variant to receive the value stored in the source. +// On failure, the destination will be empty. +void VariantToNPVariant(NPBrowserProxy* browser_proxy, + const VARIANT* source, + NPVariant* destination); + + +// Converts a NPAPI variant to an ActiveX variant. +// Parameters: +// browser_proxy: The emulated NPAPI browser environment, required for +// managing NPAPI string resource construction, etc. +// source: The source NPAPI variant. +// destination: The COM VARIANT to receive the value stored in the source. +// On failure, the destination will be empty. +void NPVariantToVariant(NPBrowserProxy* browser_proxy, + const NPVariant* source, + CComVariant* destination); + +#endif // O3D_PLUGIN_NPAPI_HOST_CONTROL_WIN_VARIANT_UTILS_H_ |