diff options
Diffstat (limited to 'o3d/plugin')
134 files changed, 28397 insertions, 0 deletions
diff --git a/o3d/plugin/build.scons b/o3d/plugin/build.scons new file mode 100644 index 0000000..3ff6a97 --- /dev/null +++ b/o3d/plugin/build.scons @@ -0,0 +1,409 @@ +# 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 os.path +import sys +Import('env') +env.SConscript('$SCONSTRUCT_DIR/plugin/idl_list.scons') +Import('O3D_IDL_SOURCES') + +env.Tool('replace_strings') +env.Append(O3D_PLUGIN_DESCRIPTION= + '$O3D_PLUGIN_NAME version:$O3D_PLUGIN_VERSION', + O3D_PLUGIN_MIME_TYPE='application/vnd.o3d.auto') + +plugin_replace_strings = [ + ('@@@PluginName@@@', env.subst('$O3D_PLUGIN_NAME')), + ('@@@ProductVersionCommas@@@', + env.subst('$O3D_PLUGIN_VERSION_COMMAS')), + ('@@@ProductVersion@@@', env.subst('$O3D_PLUGIN_VERSION')), + ('@@@PluginDescription@@@', env.subst('$O3D_PLUGIN_DESCRIPTION')), + ('@@@PluginMimeType@@@', env.subst('$O3D_PLUGIN_MIME_TYPE')), +] + +# TODO: collapse these if possible. +env.Append(GLUE_DIR = env.Dir('glue'), + NIXYSA_STATIC_GLUE = '$NIXYSA_DIR/static_glue/npapi') + +if env.Bit('windows'): + testing_inputs = [ + 'win/plugin_metrics-win32.cc', + 'win/plugin_logging-win32.cc', + ] +elif env.Bit('mac'): + testing_inputs = [ + 'mac/plugin_metrics-mac.cc', + 'mac/plugin_logging-mac.mm', + ] + +# Add Skia include paths +env.Append(CPPPATH=['$SKIA_DIR/include/core', + '$SKIA_DIR/include/effects']) + +# Build a library for testing. +# Currently windows and mac. +if env.Bit('windows') or env.Bit('mac'): + logging_lib = env.ComponentLibrary('o3dPlugin_logging', testing_inputs) + +# vista SDK provides an incompatible version of npapi.h, so make sure we +# prepend our blessed version. +env.Prepend(CPPPATH = ['$NPAPI_DIR/include']) +env.Append( + CPPPATH = [ + 'cross', + '$GLUE_DIR', + '$NIXYSA_STATIC_GLUE', + '$SCONSTRUCT_DIR/plugin/win', # for RES resource.h + '$WTL_71_DIR/include', + ], + LIBPATH = [ + '$NACL_LIB_DIR', + ], + LIBS = [ +# 'o3dBreakpad', + 'o3dArchive', + 'o3dCore', + 'o3dArchive', + 'o3dCorePlatform', + 'o3dUtils', + 'google_nacl_imc', + 'o3d_base', + 'v8', + 'skia', + ], +) + + +if env.Bit('windows'): + env.Append( + LIBS = [ + 'advapi32', + 'o3dBreakpad', + 'o3dStatsreport_Common', + 'o3dStatsreport', + 'shell32', + 'shlwapi', + ], + CPPDEFINES = ['XP_WIN'] + ) + + + +if env.Bit('mac'): + if env['DEBUG']: + env['MAC_BREAKPAD_CONFIG'] = 'Debug' + else: + env['MAC_BREAKPAD_CONFIG'] = 'Release' + env.Append( + MAC_BREAKPAD_SRC_DIR = '$BREAKPAD_DIR/client/mac', + # TODO: is there a way to tell xcodebuild to output the build in + # scons-out instead of the source tree ? + MAC_BREAKPAD_DIR = '$MAC_BREAKPAD_SRC_DIR/build/$MAC_BREAKPAD_CONFIG', + LIBS = [ + 'o3dStatsreport_Common', + 'o3dStatsreport', + logging_lib, + ], + FRAMEWORKS = [ + 'Carbon', + 'OpenGL', + 'Cg', + 'AGL', + 'Foundation', + 'Breakpad', + 'Cocoa', + 'IOKit', + ], + CCFLAGS = ['-F$MAC_BREAKPAD_DIR', + '-F$CG_DIR'], + CPPPATH = ['mac'], + CPPDEFINES = ['XP_MACOSX'] + ) + +if env.Bit('linux'): + env.Append(CPPDEFINES = ['XP_UNIX', 'MOZ_X11']); + +# Add libraries based on the requested renderer +env.Append(CPPPATH = env['RENDERER_INCLUDE_PATH'], + LIBPATH = env['RENDERER_LIB_PATH'], + LIBS = env['RENDERER_LIBS'] + env['ICU_LIBS']) + + +def NixysaEmitter(target, source, env): + bases = [os.path.splitext(s.name)[0] for s in source] + ['globals'] + targets = ['glue/%s_glue.cc' % b for b in bases] + targets += ['glue/%s_glue.h' % b for b in bases] + return targets, source + +AUTOGEN_ARGS = ['$NIXYSA_DIR/codegen.py', + '--binding-module=o3d:plugin/o3d_binding.py', + '--generate=npapi', + '--output-dir=$GLUE_DIR', + '$SOURCES'] + +env['PYTHONPATH'] = ['$NIXYSA_DIR', + '$GFLAGS_DIR/python', + '$PLY_DIR'] +env['BUILDERS']['Nixysa'] = Builder(action=env.Python(AUTOGEN_ARGS), + emitter=NixysaEmitter) +AUTOGEN_OUTPUT = env.Nixysa(O3D_IDL_SOURCES) +env.SideEffect('glue/hash', AUTOGEN_OUTPUT) +AUTOGEN_CC_FILES = [f for f in AUTOGEN_OUTPUT if f.suffix == '.cc'] + +inputs = AUTOGEN_CC_FILES + [ + 'cross/async_loading.cc', + 'cross/archive_request_glue.cc', + 'cross/blacklist.cc', + 'cross/o3d_glue.cc', + 'cross/np_v8_bridge.cc', + 'cross/out_of_memory.cc', + 'cross/stream_manager.cc', + 'cross/config_common.cc', +] + +env_version = env.Clone() +env_version.Append( + CPPDEFINES = [ + ('O3D_PLUGIN_NAME', '\\"$O3D_PLUGIN_NAME\\"'), + ('O3D_PLUGIN_DESCRIPTION', '\\"$O3D_PLUGIN_DESCRIPTION\\"'), + ('O3D_PLUGIN_MIME_TYPE', '\\"$O3D_PLUGIN_MIME_TYPE\\"') + ]) + +idlglue_static_sources = [ + 'common', + 'static_object', + 'npn_api', +] +env_idlglue = env.Clone() + +# TODO: figure out resources on the mac. +if env.Bit('windows'): + env.ReplaceStrings( + 'win/o3dPlugin.rc', 'win/o3dPlugin.rc_template', + REPLACE_STRINGS = plugin_replace_strings + ) + if env['DEBUG']: + # release v8 brings libcmt that conflicts with libcmtd + env.Append(LINKFLAGS=['/NODEFAULTLIB:LIBCMT']) + env_idlglue.Append(CPPDEFINES=['OS_WINDOWS']) + inputs += [env_idlglue.ComponentObject(s, '$NIXYSA_STATIC_GLUE/%s.cc' % s) + for s in idlglue_static_sources] + inputs += env_version.ComponentObject('cross/main', 'cross/main.cc') + inputs += [ + logging_lib, + 'win/main_win.cc', + 'win/config.cc', + 'win/o3dPlugin.def', + 'win/update_lock.cc', + env.RES('win/o3dPlugin.rc'), + ] + + +if env.Bit('linux'): + env_idlglue.Append(CPPDEFINES=['OS_LINUX']) + inputs += [env_idlglue.SharedObject(s, '$NIXYSA_STATIC_GLUE/%s.cc' % s) + for s in idlglue_static_sources] + inputs += env_version.SharedObject('cross/main', 'cross/main.cc') + inputs += [ + 'linux/main_linux.cc', + 'linux/config.cc', + ] + + +# SCons doesn't really know about MacOSX bundles, so we need to override a +# lot of its behavior to make one, ie -bundle flag, no lib prefix, no .dylib suffix. +if env.Bit('mac'): + breakpad_framework = env.Command( + env.Dir('$MAC_BREAKPAD_DIR/Breakpad.framework'), + env.Dir('$MAC_BREAKPAD_SRC_DIR/Breakpad.xcodeproj'), + ' '.join(['cd $MAC_BREAKPAD_SRC_DIR &&', + 'xcodebuild', + '-project Breakpad.xcodeproj', + '-target Breakpad', + '-configuration $MAC_BREAKPAD_CONFIG'])) + plugin_mac_object = env.SharedObject('mac/plugin_mac', 'mac/plugin_mac.mm') + env.Requires(plugin_mac_object, breakpad_framework) + + env_idlglue.Append(CPPDEFINES=['OS_MACOSX']) + inputs += [env_idlglue.SharedObject(s, '$NIXYSA_STATIC_GLUE/%s.cc' % s) + for s in idlglue_static_sources] + inputs += env_version.SharedObject('cross/main', 'cross/main.cc') + inputs += [ + 'mac/main_mac.mm', + 'mac/config_mac.mm', + plugin_mac_object + ] + + env['SHLINKFLAGS'] = ['-bundle', + '-F$MAC_BREAKPAD_DIR', + '-F$CG_DIR', + ] + env['SHLIBPREFIX'] = [''] + env['SHLIBSUFFIX'] = [''] + plugin_dll = env.SharedLibrary('o3d', inputs) + plugin_install = env.Replicate('$ARTIFACTS_DIR/O3D.plugin/Contents/MacOS/', plugin_dll) + + # insert version number into Info.plist + env.ReplaceStrings( + 'mac/processed/Info.plist', '$SCONSTRUCT_DIR/plugin/mac/Info.plist', + REPLACE_STRINGS = plugin_replace_strings + ) + + # copy mac resource data + env.Replicate('$ARTIFACTS_DIR/O3D.plugin/Contents/', + '$SCONSTRUCT_DIR/plugin/mac/Resources') + env.Replicate('$ARTIFACTS_DIR/O3D.plugin/Contents', + 'mac/processed/Info.plist') + + # Make a string substituted version of o3d_plugin.r in the artifacts + # directory. + env.ReplaceStrings( + '$ARTIFACTS_DIR/o3d_plugin.r', 'mac/o3d_plugin.r', + REPLACE_STRINGS = plugin_replace_strings + ) + # Compile the string substituted o3d_plugin.r to make o3d.rsrc + env.Command('$ARTIFACTS_DIR/O3D.plugin/Contents/Resources/o3d.rsrc', + ['$ARTIFACTS_DIR/o3d_plugin.r'], + [ + 'Rez -useDF "$ARTIFACTS_DIR/o3d_plugin.r" -o "$ARTIFACTS_DIR/O3D.plugin/Contents/Resources/o3d.rsrc"' + ]) + + if env['DEBUG']: + stripCmd = 'echo debug build, no strip' + else: + stripCmd = 'strip -S "$ARTIFACTS_DIR/O3D.plugin/Contents/MacOS/o3d"' + + # Cleanup end result and build the installer + created_installer = env.Command('$ARTIFACTS_DIR/plugin_done', + [env.Dir('$ARTIFACTS_DIR/O3D.plugin')], + [ + 'xcodebuild -project installer/mac/O3D_Stats/O3D_Stats.xcodeproj -configuration Release', + 'ditto "$SCONSTRUCT_DIR/installer/mac/O3D_Stats/build/Release/O3D_Stats.bundle" "$ARTIFACTS_DIR/O3D_Stats.bundle"', + # Because the frameworks are inside a plugin bundle (not application bundle) the plugin executable needs to be + # tweaked to reference their paths via @loader_path instead of @executable_path. + '$SCONSTRUCT_DIR/plugin/mac/Tools/fix_install_names.sh $ARTIFACTS_DIR/O3D.plugin/Contents/MacOS/o3d', + + # make a copy of the executable, before we strip all the symbols + 'rm -f "$ARTIFACTS_DIR/o3d"', + 'cp -f "$ARTIFACTS_DIR/O3D.plugin/Contents/MacOS/o3d" "$ARTIFACTS_DIR/o3d"', + stripCmd, + # Delete frameworks so we start fresh, and ditto can't get confused + Delete("$ARTIFACTS_DIR/O3D.plugin/Contents/Frameworks/"), + # Use ditto, not Replicate() as Replicate mangles the symlinks. + # The use of ditto also lets us strip the frameworks down to just i386, which saves a lot of space. + 'ditto --arch i386 "$MAC_BREAKPAD_DIR/Breakpad.framework" "$ARTIFACTS_DIR/O3D.plugin/Contents/Frameworks/Breakpad.framework"', + '/usr/bin/install_name_tool -change ' + '@executable_path/../Frameworks/Breakpad.framework/Resources/breakpadUtilities.dylib ' + '@loader_path/Resources/breakpadUtilities.dylib ' + '"$ARTIFACTS_DIR/O3D.plugin/Contents/Frameworks/Breakpad.framework/Breakpad"', + 'ditto --arch i386 "$CG_DIR/Cg.framework" "$ARTIFACTS_DIR/O3D.plugin/Contents/Frameworks/Cg.framework"', + # Delete private frameworks headers. + 'find "$ARTIFACTS_DIR/O3D.plugin/Contents/Frameworks" -iname "*.h" -delete', + 'find "$ARTIFACTS_DIR/O3D.plugin/Contents/Frameworks" -iname "Headers" -type l -delete', + 'find "$ARTIFACTS_DIR/O3D.plugin/Contents/Frameworks" -iname "Headers" -type d -prune -delete', + Touch('$ARTIFACTS_DIR/plugin_done') + ]) + + + env['MAC_INSTALLER_PROJECT'] = 'open_source_o3d_mac_npapi_metapackage.packproj' + make_installer = int(ARGUMENTS.get('MAKE_INSTALLER', 0)) + + + + if make_installer: + if int(ARGUMENTS.get('MAC_KILLSWITCH', 0)): + kill_command = 'rm "$ARTIFACTS_DIR/O3D.plugin/Contents/MacOS/o3d"' + else: + kill_command = 'echo normal binary' + # Cleanup end result and build the installer + env.Command(env.Dir('$ARTIFACTS_DIR/O3D.mpkg'), + [ + "$ARTIFACTS_DIR/plugin_done", + ], + [ + # Delete first as Copy can fail to overwrite if the packproj is locked + Delete("$ARTIFACTS_DIR/$MAC_INSTALLER_PROJECT"), + # Copy installer project file into artifacts so it can operate on files local to own directory + # and so work on debug or release builds without change. + Copy("$ARTIFACTS_DIR/$MAC_INSTALLER_PROJECT", + '$SCONSTRUCT_DIR/plugin/mac/o3d_mac_npapi_metapackage/$MAC_INSTALLER_PROJECT'), + # Make the installer. + kill_command, + 'freeze "$ARTIFACTS_DIR/$MAC_INSTALLER_PROJECT"', + # Delete temp files in artifacts now we are done. + Delete("$ARTIFACTS_DIR/$MAC_INSTALLER_PROJECT") + ]) + + env.Command('$ARTIFACTS_DIR/o3d.dmg', + [ + env.Dir('$ARTIFACTS_DIR/O3D.mpkg'), + ], + [ + Delete("$ARTIFACTS_DIR/DMG_SRC"), + 'mkdir "$ARTIFACTS_DIR/DMG_SRC"', + 'ditto "$ARTIFACTS_DIR/O3D.mpkg" "$ARTIFACTS_DIR/DMG_SRC/O3D.mpkg"', + 'hdiutil create -srcfolder "$ARTIFACTS_DIR/DMG_SRC" -size 30mb -ov -fs HFS+ -imagekey zlib-level=9 -volname "O3D $O3D_PLUGIN_VERSION" "$ARTIFACTS_DIR/o3d.dmg"', + Delete("$ARTIFACTS_DIR/DMG_SRC"), + ]) + + +# else build the shared library in a platform independent way +else: + plugin_dll = env.SharedLibrary('npo3dautoplugin', inputs) +# copy to artifacts + plugin_install = env.Replicate('$ARTIFACTS_DIR', plugin_dll) + +if env.Bit('linux'): + env.Requires(plugin_install, env.Replicate( + '$ARTIFACTS_DIR', [ + '$CG_DIR/lib/libCg.so', + '$CG_DIR/lib/libCgGL.so', + '$GLEW_DIR/lib/libGLEW.so.1.5', + ] + )) + + +# alias 'plugin' to build the plug-in in artifacts +env.Alias('plugin', plugin_install) + +# 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['BUILD_TYPE'] == ARGUMENTS.get('MODE') or + (ARGUMENTS.get('MODE', 'default') == 'default' and + env['BUILD_TYPE'] == 'dbg-d3d')): + i = env.Replicate('$FIREFOX_PLUGIN_DIR', plugin_dll[0]) + env.Alias('install', i) + +if env.Bit('windows'): + # Make the logging program + exe = env.ComponentProgram('statsLogger', + logging_lib + ['win/logger_main.cc']) + # Copy the resulting executable to the Artifacts directory. + env.Replicate('$ARTIFACTS_DIR', [exe]) diff --git a/o3d/plugin/cross/archive_request_glue.cc b/o3d/plugin/cross/archive_request_glue.cc new file mode 100644 index 0000000..704aaa7 --- /dev/null +++ b/o3d/plugin/cross/archive_request_glue.cc @@ -0,0 +1,214 @@ +/* + * 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/cross/archive_request_glue.h" + +#include "base/basictypes.h" +#include "plugin/cross/stream_manager.h" +#include "plugin/cross/o3d_glue.h" +#include "import/cross/archive_request.h" + +namespace glue { +namespace namespace_o3d { +namespace class_ArchiveRequest { + +using _o3d::PluginObject; +using o3d::Pack; +using o3d::ArchiveRequest; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Callbacks +// +// TODO : get rid of these horrible callback objects which end up just +// dispatching to the ArchiveRequest object. +// Need to change the StreamManager class to implement an interface: +// WriteReadyCallback +// WriteCallback +// FinishedCallback +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class ArchiveNewStreamCallback : public StreamManager::NewStreamCallback { + public: + explicit ArchiveNewStreamCallback(ArchiveRequest *request) + : request_(request) { } + + virtual void Run(DownloadStream *stream) { + request_->NewStreamCallback(stream); + } + + private: + ArchiveRequest::Ref request_; + DISALLOW_COPY_AND_ASSIGN(ArchiveNewStreamCallback); +}; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class ArchiveWriteReadyCallback : public StreamManager::WriteReadyCallback { + public: + explicit ArchiveWriteReadyCallback(ArchiveRequest *request) + : request_(request) { } + + virtual int32 Run(DownloadStream *stream) { + return request_->WriteReadyCallback(stream); + } + + private: + ArchiveRequest::Ref request_; + DISALLOW_COPY_AND_ASSIGN(ArchiveWriteReadyCallback); +}; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class ArchiveWriteCallback : public StreamManager::WriteCallback { + public: + explicit ArchiveWriteCallback(ArchiveRequest *request) : request_(request) { + } + + virtual int32 Run(DownloadStream *stream, + int32 offset, + int32 length, + void *data) { + return request_->WriteCallback(stream, offset, length, data); + } + + private: + ArchiveRequest::Ref request_; + DISALLOW_COPY_AND_ASSIGN(ArchiveWriteCallback); +}; + +class ArchiveFinishedCallback : public StreamManager::FinishedCallback { + public: + explicit ArchiveFinishedCallback(ArchiveRequest *request) + : request_(request) { + } + + virtual ~ArchiveFinishedCallback() { + // If the file request was interrupted (for example we moved to a new page + // before the file transfer was completed) then we tell the FileRequest + // object that the request failed. It's important to call this here since + // set_success() will release the pack reference that the FileRequest holds + // which will allow the pack to be garbage collected. + if (!request_->done()) { + request_->set_success(false); + } + } + + // Loads the Archive file, calls the JS callback to notify success. + virtual void Run(DownloadStream *stream, + bool success, + const std::string &filename, + const std::string &mime_type) { + request_->FinishedCallback(stream, success, filename, mime_type); + } + + private: + ArchiveRequest::Ref request_; + DISALLOW_COPY_AND_ASSIGN(ArchiveFinishedCallback); +}; + +// Sets up the parameters required for all ArchiveRequests. +void userglue_method_open(void *plugin_data, + ArchiveRequest *request, + const String &method, + const String &uri) { + if (request->done()) { + request->set_success(false); + request->set_ready_state(ArchiveRequest::STATE_INIT); // not ready + return; // We don't yet support reusing ArchiveRequests. + } + + String method_lower(method); + std::transform(method.begin(), method.end(), method_lower.begin(), ::tolower); + if (method_lower != "get") { + request->set_success(false); + return; // We don't yet support fetching files via POST. + } + request->set_uri(uri); + request->set_ready_state(ArchiveRequest::STATE_OPEN); +} + +// Starts progressively downloading the requested file +// The ArchiveRequest object will get callbacks as bytes stream in +void userglue_method_send(void *plugin_data, + ArchiveRequest *request) { + PluginObject *plugin_object = static_cast<PluginObject *>(plugin_data); + StreamManager *stream_manager = plugin_object->stream_manager(); + bool result = false; + + if (request->done()) { + request->set_success(false); + return; // ArchiveRequests can't be reused. + } + if (request->ready_state() != 1) { // Forgot to call open, or other error. + request->set_success(false); + return; + } + CHECK(request->pack()); + + ArchiveNewStreamCallback *new_stream_callback = + new ArchiveNewStreamCallback(request); + + ArchiveWriteReadyCallback *writeready_callback = + new ArchiveWriteReadyCallback(request); + + ArchiveWriteCallback *write_callback = + new ArchiveWriteCallback(request); + + ArchiveFinishedCallback *finished_callback = + new ArchiveFinishedCallback(request); + + if (finished_callback) { + DownloadStream *stream = stream_manager->LoadURL(request->uri(), + new_stream_callback, + writeready_callback, + write_callback, + finished_callback, + NP_NORMAL); + if (!stream) { + // We don't call O3D_ERROR here because the URI may be user set + // so we don't want to cause an error callback when the developer + // may not be able to know the URI is correct. + request->set_error("could not create download stream"); + + // We need to call the callback to report failure. Because it's async, the + // code making the request can't know that once it has called send() that + // the request still exists since send() may have called the callback and + // the callback may have deleted the request. + request->FinishedCallback(NULL, false, request->uri(), std::string("")); + } + + // If stream is not NULL request may not exist as LoadURL may already have + // completed and therefore called the callback which may have freed the + // request so we can't set anything on the request here. + } +} + +} // namespace class_ArchiveRequest +} // namespace namespace_o3d +} // namespace glue diff --git a/o3d/plugin/cross/archive_request_glue.h b/o3d/plugin/cross/archive_request_glue.h new file mode 100644 index 0000000..f9f1384 --- /dev/null +++ b/o3d/plugin/cross/archive_request_glue.h @@ -0,0 +1,66 @@ +/* + * 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 declares the glue for FileRequest actions. + +#ifndef O3D_PLUGIN_ARCHIVE_REQUEST_GLUE_H_ +#define O3D_PLUGIN_ARCHIVE_REQUEST_GLUE_H_ + +#include "core/cross/callback.h" +#include "core/cross/types.h" + +namespace o3d { +class ArchiveRequest; +} // namespace o3d + +namespace glue { +namespace namespace_o3d { +namespace class_ArchiveRequest { + +using o3d::ArchiveRequest; +using o3d::String; + +// Sets up the parameters required for all FileRequests. +void userglue_method_open(void *plugin_data, + ArchiveRequest *request, + const String &method, + const String &uri); + +// Starts downloading or reading the requested file, passing in a callback that +// will parse and incorporate the file upon success. +void userglue_method_send(void *plugin_data, + ArchiveRequest *request); +} // namespace class_FileRequest +} // namespace namespace_o3d +} // namespace glue + +#endif // O3D_PLUGIN_ARCHIVE_REQUEST_GLUE_H_ diff --git a/o3d/plugin/cross/async_loading.cc b/o3d/plugin/cross/async_loading.cc new file mode 100644 index 0000000..6c75d82 --- /dev/null +++ b/o3d/plugin/cross/async_loading.cc @@ -0,0 +1,223 @@ +/* + * 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 implements the asynchronous file-loading glue. + +#include <algorithm> + +#include "plugin/cross/async_loading.h" +#include "plugin/cross/o3d_glue.h" +#include "plugin/cross/stream_manager.h" +#include "core/cross/bitmap.h" +#include "core/cross/error_status.h" +#include "core/cross/file_request.h" +#include "core/cross/pack.h" +#include "core/cross/texture.h" + +namespace glue { +namespace namespace_o3d { +namespace class_FileRequest { + +using _o3d::PluginObject; +using o3d::Bitmap; +using o3d::Pack; +using o3d::Texture; + +// StreamManager::FinishedCallback +// implementation that imports the file as a texture once downloaded. +// When the download completes, LoadTextureURLCallback::Run() will be called, +// which will parse and load the downloaded file. After that load is complete, +// onreadystatechange will be run to notify the user. +class LoadTextureURLCallback : public StreamManager::FinishedCallback { + public: + // Creates a new LoadTextureURLCallback. + static LoadTextureURLCallback *Create(FileRequest *request) { + return new LoadTextureURLCallback(request); + } + + virtual ~LoadTextureURLCallback() { + // If the file request was interrupted (for example we moved to a new page + // before the file transfer was completed) then we tell the FileRequest + // object that the request failed. It's important to call this here since + // set_success() will release the pack reference that the FileRequest holds + // which will allow the pack to be garbage collected. + if (!request_->done()) { + request_->set_success(false); + } + } + + // Loads the texture file, calls the JS callback to pass back the texture + // object. + virtual void Run(DownloadStream*, + bool success, + const std::string &filename, + const std::string &mime_type) { + Texture::Ref texture; + if (success) { + o3d::ErrorCollector error_collector(request_->service_locator()); + request_->set_ready_state(FileRequest::STATE_LOADED); + // Try to get the image file type from the returned MIME type. + // Unfortunately, TGA and DDS don't have standard MIME type, so we may + // have to rely on the filename, or let the image loader figure it out by + // itself (by trying every possible type). + Bitmap::ImageFileType image_type = + Bitmap::GetFileTypeFromMimeType(mime_type.c_str()); + texture = Texture::Ref( + request_->pack()->CreateTextureFromFile( + request_->uri(), + filename.c_str(), + image_type, + request_->generate_mipmaps())); + if (texture) { + texture->set_name(request_->uri()); + request_->set_texture(texture); + } else { + success = false; + } + request_->set_error(error_collector.errors()); + } else { + // No error is passed in from the stream but we MUST have an error + // for the request to work on the javascript side. + request_->set_error("Could not download texture. It could be a " + "permission-related issue."); + } + request_->set_success(success); + // Since the standard codes only go far enough to tell us that the download + // succeeded, we set the success [and implicitly the done] flags to give the + // rest of the story. + if (request_->onreadystatechange()) + request_->onreadystatechange()->Run(); + } + + private: + FileRequest::Ref request_; + + explicit LoadTextureURLCallback(FileRequest *request) + : request_(request) { + } +}; + +// Sets up the parameters required for all FileRequests. +void userglue_method_open(void *plugin_data, + FileRequest *request, + const String &method, + const String &uri, + bool async) { + if (!async) { + request->set_success(false); + O3D_ERROR(request->service_locator()) + << ("synchronous request not supported"); + return; // We don't yet support synchronous requests. + } + if (request->done()) { + request->set_success(false); + request->set_ready_state(FileRequest::STATE_INIT); // Show we're unready. + O3D_ERROR(request->service_locator()) + << "request can not be reused"; + return; // We don't yet support reusing FileRequests. + } + + String method_lower(method); + std::transform(method.begin(), method.end(), method_lower.begin(), ::tolower); + if (method_lower != "get") { + request->set_success(false); + O3D_ERROR(request->service_locator()) + << "request does not support POST yet"; + return; // We don't yet support fetching files via POST. + } + request->set_uri(uri); + request->set_ready_state(FileRequest::STATE_OPEN); +} + +// Starts downloading or reading the requested file, passing in a callback that +// will parse and incorporate the file upon success. +void userglue_method_send(void *plugin_data, + FileRequest *request) { + PluginObject *plugin_object = static_cast<PluginObject *>(plugin_data); + StreamManager *stream_manager = plugin_object->stream_manager(); + StreamManager::FinishedCallback *callback = NULL; + bool result = false; + + if (request->done()) { + request->set_success(false); + O3D_ERROR(request->service_locator()) + << "request can not be reused"; + return; // FileRequests can't be reused. + } + if (request->ready_state() != 1) { // Forgot to call open, or other error. + request->set_success(false); + O3D_ERROR(request->service_locator()) + << "open must be called before send"; + return; + } + CHECK(request->pack()); + + switch (request->type()) { + case FileRequest::TYPE_TEXTURE: + callback = LoadTextureURLCallback::Create(request); + break; + default: + CHECK(false); + } + if (callback) { + DownloadStream *stream = + stream_manager->LoadURL(request->uri(), + NULL, // new stream callback + NULL, // write ready callback + NULL, // write callback + callback, // finished callback + NP_ASFILEONLY); + + if (!stream) { + request->set_success(false); + + // We don't call O3D_ERROR here because the URI may be user set + // so we don't want to cause an error callback when the devloper + // may not be able to know the URI is correct. + request->set_error("could not create download stream"); + + // We need to call the callback to report failure. Because it's async, the + // code making the request can't know that once it has called send() that + // the request still exists since send() may have called the callback and + // the callback may have deleted the request. + request->onreadystatechange()->Run(); + } + + // If stream is not NULL request may not exist as LoadURL may already have + // completed and therefore called the callback which may have freed the + // request so we can't set anything on the request here. + } +} + +} // namespace class_FileRequest +} // namespace namespace_o3d +} // namespace glue diff --git a/o3d/plugin/cross/async_loading.h b/o3d/plugin/cross/async_loading.h new file mode 100644 index 0000000..d0df9df --- /dev/null +++ b/o3d/plugin/cross/async_loading.h @@ -0,0 +1,67 @@ +/* + * 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 declares the glue for FileRequest actions. + +#ifndef EXPERIMENTAL_O3D_O3DPLUGIN_AUTOGEN_O3D_GLUE_ASYNC_LOADING_H_ +#define EXPERIMENTAL_O3D_O3DPLUGIN_AUTOGEN_O3D_GLUE_ASYNC_LOADING_H_ + +#include "core/cross/callback.h" +#include "core/cross/types.h" + +namespace o3d { +class FileRequest; +} // namespace o3d + +namespace glue { +namespace namespace_o3d { +namespace class_FileRequest { + +using o3d::FileRequest; +using o3d::String; + +// Sets up the parameters required for all FileRequests. +void userglue_method_open(void *plugin_data, + FileRequest *request, + const String &method, + const String &uri, + bool async); + +// Starts downloading or reading the requested file, passing in a callback that +// will parse and incorporate the file upon success. +void userglue_method_send(void *plugin_data, + FileRequest *request); +} // namespace class_FileRequest +} // namespace namespace_o3d +} // namespace glue + +#endif // EXPERIMENTAL_O3D_O3DPLUGIN_AUTOGEN_O3D_GLUE_ASYNC_LOADING_H_ diff --git a/o3d/plugin/cross/blacklist.cc b/o3d/plugin/cross/blacklist.cc new file mode 100644 index 0000000..270c4df --- /dev/null +++ b/o3d/plugin/cross/blacklist.cc @@ -0,0 +1,70 @@ +/* + * 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 <iostream> +#include <fstream> + +#include "plugin/cross/config.h" +#include "base/logging.h" + +// Checks the driver GUID against the blacklist file. Returns true if there's a +// match [this driver is blacklisted] or if an IO error occurred. Check the +// state of input_file to determine which it was. +// +// Note that this function always returns false if the guid is 0, since it will +// be zero if we had a failure in reading it, and the user will already have +// been warned. +bool IsDriverBlacklisted(std::ifstream *input_file, unsigned int guid) { + if (!guid) { + return false; + } + *input_file >> std::ws; + while (input_file->good()) { + if (input_file->peek() == '#') { + char comment[256]; + input_file->getline(comment, 256); + // If the line was too long for this to work, it'll set the failbit. + } else { + unsigned int id; + *input_file >> std::hex >> id; + if (id == guid) { + return true; + } + } + *input_file >> std::ws; // Skip whitespace here, to catch EOF cleanly. + } + if (input_file->fail()) { + LOG(ERROR) << "Failed to read the blacklisted driver file completely."; + return true; + } + CHECK(input_file->eof()); + return false; +} diff --git a/o3d/plugin/cross/config.h b/o3d/plugin/cross/config.h new file mode 100644 index 0000000..58ecc00 --- /dev/null +++ b/o3d/plugin/cross/config.h @@ -0,0 +1,115 @@ +/* + * 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 defines a few functions to check the user's hardware and software +// configuration. + +#ifndef O3D_PLUGIN_CROSS_CONFIG_H_ +#define O3D_PLUGIN_CROSS_CONFIG_H_ + +#include <fstream> +#include <string> +#include "plugin/cross/o3d_glue.h" + +// Returns the user agent string. +// Arguments: +// npp: plugin instance. +// Returns: +// The user agent string. +std::string GetUserAgent(NPP npp); + +struct GPUDevice { + unsigned int vendor_id; + unsigned int device_id; + std::string driver; + std::string description; + std::string name; + unsigned int guid; +}; + +// Asks the user to ok a continuation +// Arguments: +// npp: plugin instance. +// error: the error to signal. +// Returns: +// true if the error was overridden by the user, false otherwise. +bool AskUser(NPP npp, const std::string &error); + +// Gets the device information. +// Arguments: +// device: a GPUDevice structure that will contain the vendor and device IDs. +// name: a pointer to a string that will contain the device string. +// Returns: +// true if successful. If not, device and name are not modified. +bool GetGPUDevice(NPP npp, GPUDevice *device); + +// Checks that the current hardware and software configuration is supported, +// prompting the user to give him and option to run anyway. +// Arguments: +// npp: plugin instance. +// Returns: +// Whether to allow the plug-in to run. +bool CheckConfig(NPP npp); + +// The following functions are platform-dependent, and are implemented in the +// respective {platform}/config.cc files + +// Checks the operating system version. +bool CheckOSVersion(NPP npp); + +// Checks the user agent string. +bool CheckUserAgent(NPP npp, const std::string &user_agent); + +// Used to get the text file that lists blacklisted driver GUIDs. +// Returns true on success. +bool OpenDriverBlacklistFile(std::ifstream *input_file); + +// Checks the driver GUID against the blacklist file. Returns true if there's a +// match [this driver is blacklisted] or if an IO error occurred. Check the +// state of input_file to determine which it was. +// Note that this function always returns false if the guid is 0, since it will +// be 0 if we had a failure in reading it, and the user will already have been +// warned. +bool IsDriverBlacklisted(std::ifstream *input_file, unsigned int guid); + +// Checks the current hardware, software configurations and puts the values +// into metrics. +bool GetUserConfigMetrics(); + +// Checks the browser version. +// Arguments: +// npp: plugin instance. +bool GetUserAgentMetrics(NPP npp); + +bool GetOpenGLMetrics(); + +#endif // O3D_PLUGIN_CROSS_CONFIG_H_ diff --git a/o3d/plugin/cross/config_common.cc b/o3d/plugin/cross/config_common.cc new file mode 100644 index 0000000..de375c0 --- /dev/null +++ b/o3d/plugin/cross/config_common.cc @@ -0,0 +1,215 @@ +/* + * 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 common code to check the hardware and software +// configuration of the client machine: +// - User agent (browser) +// - OS version +// - GPU vendor + +#ifdef RENDERER_D3D9 +#include <d3d9.h> +#endif + +#include <iostream> +#include <fstream> + +#include "base/logging.h" +#include "core/cross/install_check.h" +#include "plugin/cross/config.h" +#include "plugin/cross/o3d_glue.h" +#include "core/cross/error.h" +#include "third_party/nixysa/files/static_glue/npapi/common.h" + +using glue::_o3d::GetServiceLocator; + +// Gets the value of "navigator.userAgent" in the JavaScript context, which +// contains the user agent string. +std::string GetUserAgent(NPP npp) { + GLUE_PROFILE_START(npp, "NPN_UserAgent"); + const char* user_agent = NPN_UserAgent(npp); + GLUE_PROFILE_STOP(npp, "NPN_UserAgent"); + std::string uagent_string; + if (user_agent) { + uagent_string = std::string(user_agent); + } + return uagent_string; +} + +// Pops up a dialog box using JavaScript showing the error and gives the user a +// chance to continue anyway. +bool AskUser(NPP npp, const std::string &error) { + NPObject *global_object; + GLUE_PROFILE_START(npp, "NPN_GetValue"); + NPN_GetValue(npp, NPNVWindowNPObject, &global_object); + GLUE_PROFILE_STOP(npp, "NPN_GetValue"); + GLUE_PROFILE_START(npp, "NPN_GetStringIdentifier"); + NPIdentifier alert_id = NPN_GetStringIdentifier("confirm"); + GLUE_PROFILE_STOP(npp, "NPN_GetStringIdentifier"); + std::string message = error; + // TODO: internationalize message. + // TODO: Should this change to call some hardcoded javascript function + // like "o3djs.util.confirmContinuation" or even a global name like + // o3djs_confirmContinuation. This would move localization outside + // C++ and give the developer a chance to handle it his own way. + message += "\nPress OK to continue anyway."; + + NPVariant args[1]; + NPVariant result; + STRINGN_TO_NPVARIANT(message.c_str(), message.length(), args[0]); + GLUE_PROFILE_START(npp, "NPN_Invoke"); + bool temp = NPN_Invoke(npp, global_object, alert_id, args, 1, &result); + GLUE_PROFILE_STOP(npp, "NPN_Invoke"); + if (temp) { + bool retval = NPVARIANT_IS_BOOLEAN(result) && NPVARIANT_TO_BOOLEAN(result); + GLUE_PROFILE_START(npp, "NPN_ReleaseVariantValue"); + NPN_ReleaseVariantValue(&result); + GLUE_PROFILE_STOP(npp, "NPN_ReleaseVariantValue"); + return retval; + } else { + return false; + } +} + +// Gets the GPU device IDs and name, pops up a dialog box to confirm with the +// user in case we can't get the information. +bool GetGPUDevice(NPP npp, GPUDevice *device) { +#if defined(RENDERER_D3D9) + // Check GPU vendor using D3D. + IDirect3D9 *d3d = Direct3DCreate9(D3D_SDK_VERSION); + if (!d3d) { + O3D_ERROR(GetServiceLocator(npp)) << "Direct3D9 is unavailable"; + return false; + } + D3DADAPTER_IDENTIFIER9 identifier; + HRESULT hr = d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); + d3d->Release(); + if (hr != D3D_OK) { + O3D_ERROR(GetServiceLocator(npp)) << "Unable to get device ID"; + device->vendor_id = 0; + device->device_id = 0; + device->name = "Unknown"; + device->driver = "Unknown"; + device->description = "Unknown"; + device->guid = 0; + return false; + } + device->vendor_id = identifier.VendorId; + device->device_id = identifier.DeviceId; + device->name = identifier.DeviceName; + device->driver = identifier.Driver; + device->description = identifier.Description; + device->guid = identifier.DeviceIdentifier.Data1; + return true; +#else + // TODO: check GL version, blacklisted vendors ? + device->vendor_id = 0; + device->device_id = 0; + device->name = "Unknown"; + device->driver = "Unknown"; + device->description = "Unknown"; + device->guid = 0; + return true; +#endif +} + +// List of "black-listed" GPUs. +// +// A Vendor ID of 0 means end of the list. A device ID of 0 means the entire +// line of devices from this vendor is black-listed. +// +// NOTE: Black-listed GPUs are only for GPUs that have security or stability +// issues. GPUs that are missing required features are handled by the renderer. +static const GPUDevice g_blacklisted_gpus[] = { + {0, 0, }, // End Marker +}; + +// Checks various configuration elements: +// - Windows version +// - GPU vendor +// - User agent (browser) +bool CheckConfig(NPP npp) { + if (!CheckOSVersion(npp)) return false; + + GPUDevice device; + if (!GetGPUDevice(npp, &device)) return false; + for (unsigned int i = 0; + g_blacklisted_gpus[i].vendor_id != 0; ++i) { + if (device.vendor_id == g_blacklisted_gpus[i].vendor_id && + (device.device_id == g_blacklisted_gpus[i].device_id || + g_blacklisted_gpus[i].device_id == 0)) { + O3D_ERROR(GetServiceLocator(npp)) + << "Unsupported GPU device: " + device.name; + return false; + } + } + + { + std::ifstream blacklist_file; + std::string error; + if (!OpenDriverBlacklistFile(&blacklist_file)) { + // Allow missing blacklist file for now, or else pulse and developer + // builds [which don't install the file] will fail. + // TODO: Look into this again for the public release. + // error = "Failed to open driver blacklist file.\n" + // "Can't verify that it's safe to run O3D."; + } else if (IsDriverBlacklisted(&blacklist_file, device.guid)) { + if (blacklist_file.fail()) { + error = "Error reading driver blacklist file.\n" + "Can't verify that it's safe to run O3D."; + } else { + error = "Your driver cannot run O3D safely."; + } + } + if (error.length() && !AskUser(npp, error)) { + return false; + } + } + + { + std::string error; + if (!o3d::RendererInstallCheck(&error)) { + if (error.length()) { + O3D_ERROR(GetServiceLocator(npp)) << error; + } else { + O3D_ERROR(GetServiceLocator(npp)) + << "Could not initialize the graphics driver."; + } + return false; + } + } + + // Check User agent. Only Firefox, Chrome and IE are supported. + std::string user_agent = GetUserAgent(npp); + if (!CheckUserAgent(npp, user_agent)) return false; + return true; +} diff --git a/o3d/plugin/cross/download_stream.h b/o3d/plugin/cross/download_stream.h new file mode 100644 index 0000000..8222386 --- /dev/null +++ b/o3d/plugin/cross/download_stream.h @@ -0,0 +1,66 @@ +/* + * 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_CROSS_DOWNLOAD_STREAM_H_ +#define O3D_PLUGIN_CROSS_DOWNLOAD_STREAM_H_ + +#include <string> + +namespace glue { + +// Abstract interface representing a download +class DownloadStream { + public: + enum State { + STREAM_REQUESTED, + STREAM_STARTED, // in progress + STREAM_FINISHED // completed successfully or terminated + }; + + virtual ~DownloadStream() {} + + virtual std::string GetURL() = 0; + + // Returns empty string if no cache file + virtual std::string GetCachedFile() = 0; + + virtual State GetState() = 0; + virtual int GetReceivedByteCount() = 0; + virtual size_t GetStreamLength() = 0; + + // Stops downloading + virtual void Cancel() = 0; +}; + +} // namespace glue + +#endif // O3D_PLUGIN_CROSS_DOWNLOAD_STREAM_H_ diff --git a/o3d/plugin/cross/main.cc b/o3d/plugin/cross/main.cc new file mode 100644 index 0000000..6bf5aab --- /dev/null +++ b/o3d/plugin/cross/main.cc @@ -0,0 +1,160 @@ +/* + * 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/cross/main.h" + +using glue::_o3d::PluginObject; +using glue::StreamManager; + +int BreakpadEnabler::scope_count_ = 0; + +// Used for breakpad crash handling +ExceptionManager *g_exception_manager = NULL; + +extern "C" { + char *NP_GetMIMEDescription(void) { + return O3D_PLUGIN_MIME_TYPE "::O3D MIME"; + } + + NPError NP_GetValue(void *instance, NPPVariable variable, void *value) { + switch (variable) { + case NPPVpluginNameString: + *static_cast<char **>(value) = O3D_PLUGIN_NAME; + break; + case NPPVpluginDescriptionString: + *static_cast<char **>(value) = O3D_PLUGIN_DESCRIPTION; + break; + default: + return NPERR_INVALID_PARAM; + break; + } + return NPERR_NO_ERROR; + } + + NPError OSCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs) { + HANDLE_CRASHES; + pluginFuncs->version = 11; + pluginFuncs->size = sizeof(pluginFuncs); + pluginFuncs->newp = NPP_New; + pluginFuncs->destroy = NPP_Destroy; + pluginFuncs->setwindow = NPP_SetWindow; + pluginFuncs->newstream = NPP_NewStream; + pluginFuncs->destroystream = NPP_DestroyStream; + pluginFuncs->asfile = NPP_StreamAsFile; + pluginFuncs->writeready = NPP_WriteReady; + pluginFuncs->write = NPP_Write; + pluginFuncs->print = NPP_Print; + pluginFuncs->event = NPP_HandleEvent; + pluginFuncs->urlnotify = NPP_URLNotify; + pluginFuncs->getvalue = NPP_GetValue; + pluginFuncs->setvalue = NPP_SetValue; + + return NPERR_NO_ERROR; + } + + NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, + NPBool seekable, uint16 *stype) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + StreamManager *stream_manager = obj->stream_manager(); + if (stream_manager->NewStream(stream, stype)) { + return NPERR_NO_ERROR; + } else { + // TODO: find out which error we should return + return NPERR_INVALID_PARAM; + } + } + + NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + StreamManager *stream_manager = obj->stream_manager(); + if (stream_manager->DestroyStream(stream, reason)) { + return NPERR_NO_ERROR; + } else { + // TODO: find out which error we should return + return NPERR_INVALID_PARAM; + } + } + + int32 NPP_WriteReady(NPP instance, NPStream *stream) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + StreamManager *stream_manager = obj->stream_manager(); + return stream_manager->WriteReady(stream); + } + + int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, + void *buffer) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + StreamManager *stream_manager = obj->stream_manager(); + return stream_manager->Write(stream, offset, len, buffer); + } + + void NPP_Print(NPP instance, NPPrint *platformPrint) { + HANDLE_CRASHES; + } + + void NPP_URLNotify(NPP instance, const char *url, NPReason reason, + void *notifyData) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + StreamManager *stream_manager = obj->stream_manager(); + stream_manager->URLNotify(url, reason, notifyData); + } + + NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) { + HANDLE_CRASHES; + switch (variable) { + case NPPVpluginScriptableNPObject: { + void **v = static_cast<void **>(value); + PluginObject *obj = static_cast<PluginObject *>(instance->pdata); + // Return value is expected to be retained + GLUE_PROFILE_START(instance, "retainobject"); + NPN_RetainObject(obj); + GLUE_PROFILE_STOP(instance, "retainobject"); + *v = obj; + break; + } + default: + return NP_GetValue(instance, variable, value); + break; + } + return NPERR_NO_ERROR; + } + + NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) { + HANDLE_CRASHES; + return NPERR_GENERIC_ERROR; + } +} // extern "C" diff --git a/o3d/plugin/cross/main.h b/o3d/plugin/cross/main.h new file mode 100644 index 0000000..79a657a --- /dev/null +++ b/o3d/plugin/cross/main.h @@ -0,0 +1,143 @@ +/* + * 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 header is used by the platform-specific portions of the plugin +// main implementation to define the cross-platform parts of the +// interface and global variables. + +#ifndef O3D_PLUGIN_CROSS_MAIN_H_ +#define O3D_PLUGIN_CROSS_MAIN_H_ + +#include <npupp.h> +#include <stdio.h> + +#include <fstream> +#include <iostream> + +#include "breakpad/win/exception_handler_win32.h" +#include "core/cross/renderer.h" +#include "core/cross/renderer_platform.h" +#include "plugin/cross/o3d_glue.h" +#include "plugin/cross/config.h" +#include "plugin/cross/stream_manager.h" +#include "third_party/nixysa/files/static_glue/npapi/common.h" +#include "third_party/nixysa/files/static_glue/npapi/npn_api.h" + +extern ExceptionManager *g_exception_manager; + +class RenderOnDemandCallbackHandler + : public o3d::Client::RenderOnDemandCallback { + public: + explicit RenderOnDemandCallbackHandler(glue::_o3d::PluginObject* obj) + : obj_(obj) { + } + + // This function is implemented for each platform. + virtual void Run(); + private: + glue::_o3d::PluginObject* obj_; +}; + +// BreakpadEnabler is a simple class to keep track of whether or not +// we're executing code that we want to handle crashes for +// (when the o3d plugin is running in Firefox, we don't want to handle +// crashes for the Flash plugin or Firefox, just the o3d code) +// Create a stack-based instance at the start of each function +// where crash handling is desired. + +#define HANDLE_CRASHES BreakpadEnabler enabler + +class BreakpadEnabler { + public: + BreakpadEnabler() { + ++scope_count_; + } + + virtual ~BreakpadEnabler() { + --scope_count_; + } + + static bool IsEnabled() { return scope_count_ > 0; } + + private: + static int scope_count_; +}; + + +namespace o3d { +void WriteLogString(const char* text, int length); +} // end namespace o3d + +// NPAPI declarations. Some of these are only implemented in the +// platform-specific versions of "main.cc". + +extern "C" { + NPError OSCALL NP_Shutdown(void); + NPError OSCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs); + NPError NPP_Destroy(NPP instance, NPSavedData **save); + NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason); + NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value); + + NPError NPP_New(NPMIMEType pluginType, + NPP instance, + uint16 mode, + int16 argc, + char *argn[], + char *argv[], + NPSavedData *saved); + + NPError NPP_NewStream(NPP instance, + NPMIMEType type, + NPStream *stream, + NPBool seekable, + uint16 *stype); + + NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value); + NPError NPP_SetWindow(NPP instance, NPWindow *window); + + int32 NPP_Write(NPP instance, + NPStream *stream, + int32 offset, + int32 len, + void *buffer); + + int32 NPP_WriteReady(NPP instance, NPStream *stream); + void NPP_Print(NPP instance, NPPrint *platformPrint); + void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname); + + void NPP_URLNotify(NPP instance, + const char *url, + NPReason reason, + void *notifyData); +}; // end extern "C" + +#endif // O3D_PLUGIN_CROSS_MAIN_H_ diff --git a/o3d/plugin/cross/marshaling_utils.h b/o3d/plugin/cross/marshaling_utils.h new file mode 100644 index 0000000..1a8b4d0 --- /dev/null +++ b/o3d/plugin/cross/marshaling_utils.h @@ -0,0 +1,129 @@ +/* + * 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 utility functions for marshaling between +// C++ types and dynamic types. + +#ifndef O3D_PLUGIN_CROSS_MARSHALING_UTILS_H_ +#define O3D_PLUGIN_CROSS_MARSHALING_UTILS_H_ + +#include <vector> + +namespace o3d { + +// Converts a std::vector<float>, representing a JavaScript array +// of numbers, to a FloatN, VectorN, or PointN. This template function +// supports conversion to any type which accesses float elements using +// operator[]. +template <typename VectorType, int dimension> +VectorType VectorToType(void* plugin_data, + const std::vector<float>& dynamic_value) { + if (dynamic_value.size() != dimension) { + o3d::ServiceLocator* service_locator = + static_cast<glue::_o3d::PluginObject*>( + plugin_data)->service_locator(); + O3D_ERROR(service_locator) + << "Vector type expected array of " << dimension + << " number values, got " << dynamic_value.size(); + return VectorType(); + } + VectorType vector_value; + for (int i = 0; i < dimension; ++i) { + vector_value[i] = dynamic_value[i]; + } + return vector_value; +} + +// Converts a FloatN, VectorN or PointN to an std::vector<float>. +// This template function supports conversion from any type which +// accesses float elements using operator[]. +template <typename VectorType, int dimension> +std::vector<float> VectorFromType(const VectorType& vector_value) { + std::vector<float> dynamic_value(dimension); + for (int i = 0; i < dimension; ++i) { + dynamic_value[i] = vector_value[i]; + } + return dynamic_value; +} + +// Converts an std::vector<std::vector<float> > to a MatrixN. +template <typename MatrixType, int rows, int columns> +MatrixType VectorOfVectorToType( + void* plugin_data, + const std::vector<std::vector<float> >& dynamic_value) { + if (dynamic_value.size() != rows) { + o3d::ServiceLocator* service_locator = + static_cast<glue::_o3d::PluginObject*>( + plugin_data)->service_locator(); + O3D_ERROR(service_locator) + << "Matrix type expected array of " << rows + << " arrays of " << columns << " number values, got " + << dynamic_value.size() << " rows"; + return MatrixType(); + } + MatrixType matrix_value; + for (int i = 0; i != rows; ++i) { + if (dynamic_value[i].size() != columns) { + o3d::ServiceLocator* service_locator = + static_cast<glue::_o3d::PluginObject*>( + plugin_data)->service_locator(); + O3D_ERROR(service_locator) + << "Matrix type expected array of " << rows + << " arrays of " << columns << " number values, got " + << dynamic_value[i].size() << " columns in row " + << i; + return MatrixType(); + } + for (int j = 0; j < columns; ++j) { + matrix_value.setElem(i, j, dynamic_value[i][j]); + } + } + return matrix_value; +} + +// Converts a MatrixN to a std::vector<std::vector<float> >. +template <typename MatrixType, int rows, int columns> +std::vector<std::vector<float> > VectorOfVectorFromType( + const MatrixType& matrix_value) { + std::vector<std::vector<float> > dynamic_value(rows); + for (int i = 0; i < rows; ++i) { + dynamic_value[i].resize(columns); + for (int j = 0; j < columns; ++j) { + dynamic_value[i][j] = matrix_value.getElem(i, j); + } + } + return dynamic_value; +} + +} // namespace o3d + +#endif // O3D_PLUGIN_CROSS_MARSHALING_UTILS_H_ diff --git a/o3d/plugin/cross/np_v8_bridge.cc b/o3d/plugin/cross/np_v8_bridge.cc new file mode 100644 index 0000000..0cfcd59 --- /dev/null +++ b/o3d/plugin/cross/np_v8_bridge.cc @@ -0,0 +1,1511 @@ +/* + * 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. + */ + + +// Code relating to interoperation of V8 JavaScript engine with NPAPI. +// Tests are in o3d/tests/selenium/tests/v8.html. They can be run +// by opening the web page in a browser or as part of the selenium tests. + +#include <npapi.h> +#include <sstream> +#include <vector> +#include "plugin/cross/np_v8_bridge.h" + +using v8::AccessorInfo; +using v8::Arguments; +using v8::Array; +using v8::Context; +using v8::DontDelete; +using v8::DontEnum; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Int32; +using v8::Integer; +using v8::Local; +using v8::Message; +using v8::Null; +using v8::Number; +using v8::Object; +using v8::ObjectTemplate; +using v8::Persistent; +using v8::PropertyAttribute; +using v8::ReadOnly; +using v8::Script; +using v8::TryCatch; +using v8::Undefined; +using v8::Value; + +namespace o3d { + +// Only used during debugging. Type "o3d::DebugV8String(a.val_)" in the +// watch window to get the string representation of a V8 object. +const char* DebugV8String(Value* value) { + static char buffer[4096]; + if (value == NULL) { + ::base::snprintf(buffer, sizeof(buffer), "<null>"); + } else { + value->ToString()->WriteUtf8(buffer); + } + return buffer; +} + +namespace { + +// The indices of the internal fields of a V8 proxy for an NPObject. +enum { + // Pointer to the bridge that created the proxy. + V8_NP_OBJECT_BRIDGE, + // Pointer to the wrapped NPObject. + V8_NP_OBJECT_WRAPPED, + V8_NP_OBJECT_NUM_INTERNAL_FIELDS +}; + +// The name of the "hidden" property in a V8 non-proxy object that contains +// an External that points to the NPObject proxy for it. The property does +// not exist if there is no associated NPObject proxy. +const char* const kInternalProperty = "internal_property_"; + +// Convert an NPIdentifier (NULL, string or integer) to a V8 value. +Local<Value> NPToV8Identifier(NPIdentifier np_identifier) { + if (np_identifier == NULL) { + return Local<Value>(); + } else if (NPN_IdentifierIsString(np_identifier)) { + NPUTF8* utf8_name = NPN_UTF8FromIdentifier(np_identifier); + Local<v8::String> v8_identifier = v8::String::New(utf8_name); + NPN_MemFree(utf8_name); + return v8_identifier; + } else { + return Integer::New(NPN_IntFromIdentifier(np_identifier)); + } +} + +// Convert a V8 value (empty, string or integer) into an NPIdentifier. +NPIdentifier V8ToNPIdentifier(v8::Handle<Value> v8_identifier) { + if (v8_identifier.IsEmpty()) { + return NULL; + } else if (v8_identifier->IsNumber()) { + return NPN_GetIntIdentifier(v8_identifier->Int32Value()); + } else if (v8_identifier->IsString()) { + return NPN_GetStringIdentifier( + *v8::String::Utf8Value(v8_identifier->ToString())); + } else { + return NULL; + } +} +} // namespace anonymous + +// The class of NPObject proxies that wrap V8 objects. These field the NPAPI +// functions and translate them into V8 calls. +class NPV8Object : public NPObject { + public: + static NPObjectPtr<NPV8Object> Create(NPV8Bridge* bridge, + Local<Object> v8_object) { + NPObjectPtr<NPV8Object> np_object = + NPObjectPtr<NPV8Object>::AttachToReturned( + static_cast<NPV8Object*>(NPN_CreateObject(bridge->npp(), + &np_class_))); + np_object->v8_object_ = Persistent<Object>::New(v8_object); + np_object->bridge_ = bridge; + return np_object; + } + + v8::Handle<Object> v8_object() const { + return v8_object_; + } + + // Drop references between NPObject and V8 object. Must be called before the + // NPObject is destroyed so V8 can garbage collect the associated V8 object. + void UnlinkFromV8() { + HandleScope handleScope; + if (!v8_object_.IsEmpty()) { + v8_object_->DeleteHiddenValue(v8::String::NewSymbol(kInternalProperty)); + v8_object_.Dispose(); + v8_object_.Clear(); + } + } + + static NPClass np_class_; + + private: + NPV8Object() : bridge_(NULL) { + } + + static NPObject* Allocate(NPP npp, NPClass* np_class) { + NPV8Object* np_v8_object = new NPV8Object(); + np_v8_object->bridge_ = NULL; + return np_v8_object; + } + + static void Deallocate(NPObject* np_object) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + // Uncomment this line to see objects with a non-zero reference + // count being deallocated. For example, Firefox does this when unloading + // the plugin. + // DCHECK_EQ(0, np_v8_object_map->referenceCount); + np_v8_object->UnlinkFromV8(); + delete np_v8_object; + } + + static void Invalidate(NPObject* np_object) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + np_v8_object->bridge_ = NULL; + np_v8_object->UnlinkFromV8(); + } + + static bool HasMethod(NPObject* np_object, NPIdentifier np_name) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + TryCatch tryCatch; + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + Local<Value> v8_name = NPToV8Identifier(np_name); + Local<Value> value = v8_object->Get(v8_name); + if (tryCatch.HasCaught()) { + bridge->ReportV8Exception(tryCatch); + return false; + } + + // Returns true iff the object has a property with the given name and + // the object assigned to the property is a function. This works for V8 + // functions and assigned browser JavaScript functions (because their + // proxies are created from FunctionTemplates so V8 considers them to be + // functions). + return !value.IsEmpty() && value->IsFunction(); + } + + // Called when a method is invoked through "obj.m(...)". + static bool Invoke(NPObject* np_object, NPIdentifier np_name, + const NPVariant* np_args, uint32_t numArgs, + NPVariant* result) { + // This works around a bug in Chrome: + // http://code.google.com/p/chromium/issues/detail?id=5110 + // NPN_InvokeDefault is transformed into a call to Invoke on the plugin with + // a null method name identifier. + if (np_name == NULL) { + return InvokeDefault(np_object, np_args, numArgs, result); + } + + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + TryCatch tryCatch; + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + Local<Value> v8_name = NPToV8Identifier(np_name); + Local<Value> value = v8_object->Get(v8_name); + if (value.IsEmpty() || !value->IsFunction()) + return false; + Local<Function> function = Local<Function>::Cast(value); + std::vector<v8::Handle<Value> > v8_args(numArgs); + for (uint32_t i = 0; i != numArgs; ++i) { + v8_args[i] = bridge->NPToV8Variant(np_args[i]); + } + + *result = bridge->V8ToNPVariant( + function->Call(v8_object, numArgs, + numArgs == 0 ? NULL : &v8_args.front())); + if (tryCatch.HasCaught()) { + bridge->ReportV8Exception(tryCatch); + return false; + } + return true; + } + + // Called when an object is called as a function "f(...)". + static bool InvokeDefault(NPObject* np_object, const NPVariant* np_args, + uint32_t numArgs, NPVariant* result) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + TryCatch tryCatch; + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + if (!v8_object->IsFunction()) + return false; + v8::Handle<Function> function = v8::Handle<Function>::Cast(v8_object); + + std::vector<v8::Handle<Value> > v8_args(numArgs); + for (uint32_t i = 0; i != numArgs; ++i) { + v8_args[i] = bridge->NPToV8Variant(np_args[i]); + } + + *result = bridge->V8ToNPVariant( + function->Call(v8_object, numArgs, + numArgs == 0 ? NULL : &v8_args.front())); + if (tryCatch.HasCaught()) { + bridge->ReportV8Exception(tryCatch); + return false; + } + return true; + } + + // Called when an object is called as a constructor "new C(...)". + static bool Construct(NPObject* np_object, const NPVariant* np_args, + uint32_t numArgs, NPVariant* result) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + TryCatch tryCatch; + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + if (!v8_object->IsFunction()) + return false; + v8::Handle<Function> function = v8::Handle<Function>::Cast(v8_object); + + std::vector<v8::Handle<Value> > v8_args(numArgs); + for (uint32_t i = 0; i != numArgs; ++i) { + v8_args[i] = bridge->NPToV8Variant(np_args[i]); + } + + Local<Object> v8_result = function->NewInstance( + numArgs, numArgs == 0 ? NULL : &v8_args.front()); + if (v8_result.IsEmpty()) + return false; + + *result = bridge->V8ToNPVariant(v8_result); + if (tryCatch.HasCaught()) { + bridge->ReportV8Exception(tryCatch); + return false; + } + return true; + } + + static bool HasProperty(NPObject* np_object, NPIdentifier np_name) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + // This is a better approach than the one below. It allows functions + // to be retreived as first class objects. Unfortunately we can't + // support this yet because of a Chrome bug: + // http://code.google.com/p/chromium/issues/detail?id=5742 + // if (NPN_IdentifierIsString(np_name)) { + // Local<v8::String> v8_name = Local<v8::String>::Cast( + // NPToV8Identifier(np_name)); + // return v8_object->Has(v8_name); + // } else { + // return v8_object->Has(NPN_IntFromIdentifier(np_name)); + // } + + // Instead hide properties with function type. This ensures that Chrome + // will invoke them with Invoke rather than InvokeDefault. The problem + // with InvokeDefault is it doesn't tell us what "this" should be + // bound to, whereas Invoke does. + Local<Value> v8_name = NPToV8Identifier(np_name); + if (NPN_IdentifierIsString(np_name)) { + if (!v8_object->Has(v8_name->ToString())) { + return false; + } + } else { + if (!v8_object->Has(NPN_IntFromIdentifier(np_name))) { + return false; + } + } + Local<Value> v8_property_value = v8_object->Get(v8_name); + if (v8_property_value->IsFunction()) { + return false; + } + + return true; + } + + static bool GetProperty(NPObject* np_object, NPIdentifier np_name, + NPVariant* result) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + TryCatch tryCatch; + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + Local<Value> v8_name = NPToV8Identifier(np_name); + Local<Value> v8_property_value = v8_object->Get(v8_name); + if (tryCatch.HasCaught()) { + bridge->ReportV8Exception(tryCatch); + return false; + } + + // See comment in HasProperty. Do not return properties that are + // functions. It will prevent Chrome from invoking them as methods. + if (v8_property_value.IsEmpty() || v8_property_value->IsFunction()) + return false; + + *result = bridge->V8ToNPVariant(v8_property_value); + return true; + } + + static bool SetProperty(NPObject* np_object, NPIdentifier np_name, + const NPVariant* np_value) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + TryCatch tryCatch; + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + Local<Value> v8_name = NPToV8Identifier(np_name); + bool success = v8_object->Set(v8_name, bridge->NPToV8Variant(*np_value)); + + if (tryCatch.HasCaught()) { + bridge->ReportV8Exception(tryCatch); + return false; + } + + return success; + } + + static bool RemoveProperty(NPObject* np_object, NPIdentifier np_name) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + TryCatch tryCatch; + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + bool success; + if (NPN_IdentifierIsString(np_name)) { + NPUTF8* utf8_name = NPN_UTF8FromIdentifier(np_name); + Local<v8::String> v8_name = v8::String::New(utf8_name); + NPN_MemFree(utf8_name); + success = v8_object->Delete(v8_name); + } else { + success = v8_object->Delete(NPN_IntFromIdentifier(np_name)); + } + + if (tryCatch.HasCaught()) { + bridge->ReportV8Exception(tryCatch); + return false; + } + + return success; + } + + static bool Enumerate(NPObject* np_object, NPIdentifier** np_names, + uint32_t* numNames) { + NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); + NPV8Bridge* bridge = np_v8_object->bridge_; + if (bridge == NULL) + return false; + + HandleScope handleScope; + Context::Scope scope(bridge->script_context()); + + v8::Handle<Object> v8_object = np_v8_object->v8_object_; + if (v8_object.IsEmpty()) + return false; + + Local<Array> v8_names = v8_object->GetPropertyNames(); + + // Due to a bug in Chrome, need to filter out any properties that + // are functions. See comment in HasProperty. + int num_non_function_properties = 0; + for (int i = 0; i != v8_names->Length(); ++i) { + Local<Value> v8_property_value = + v8_object->Get(v8_names->Get(Int32::New(i))); + if (!v8_property_value->IsFunction()) { + ++num_non_function_properties; + } + } + *numNames = num_non_function_properties; + *np_names = static_cast<NPIdentifier*> ( + NPN_MemAlloc(num_non_function_properties * sizeof(NPIdentifier))); + int j = 0; + for (uint32_t i = 0; i != v8_names->Length(); ++i) { + Local<Value> v8_name = v8_names->Get(Int32::New(i)); + Local<Value> v8_property_value = v8_object->Get(v8_name); + if (!v8_property_value->IsFunction()) { + (*np_names)[j++] = V8ToNPIdentifier(v8_name); + } + } + + return true; + } + + NPV8Bridge* bridge_; + AutoV8Persistent<Object> v8_object_; + DISALLOW_COPY_AND_ASSIGN(NPV8Object); +}; + +NPClass NPV8Object::np_class_ = { + NP_CLASS_STRUCT_VERSION, + Allocate, + Deallocate, + Invalidate, + HasMethod, + Invoke, + InvokeDefault, + HasProperty, + GetProperty, + SetProperty, + RemoveProperty, + Enumerate, + Construct +}; + +NPV8Bridge::NPV8Bridge(ServiceLocator* service_locator, NPP npp) + : service_locator_(service_locator), + error_status_(service_locator), + npp_(npp) { + np_name_identifier_ = NPN_GetStringIdentifier("name"); + np_call_identifier_ = NPN_GetStringIdentifier("call"); + np_length_identifier_ = NPN_GetStringIdentifier("length"); + np_proxy_identifier_ = NPN_GetStringIdentifier("npv8_proxy_"); +} + +NPV8Bridge::~NPV8Bridge() { + // Do not call weak reference callback after the bridge is destroyed + // because the callbacks assume it exists. The only purpose of the callback + // is to remove the corresponding object entry from the NP-V8 object map + // and its about to get cleared anyway. + for (NPV8ObjectMap::iterator it = np_v8_object_map_.begin(); + it != np_v8_object_map_.end(); ++it) { + it->second.ClearWeak(); + } +} + +NPObjectPtr<NPObject> NPV8Bridge::NPEvaluateObject(const char* script) { + NPString np_script = { script, strlen(script) }; + NPVariant np_variant; + NPObjectPtr<NPObject> np_result; + if (NPN_Evaluate(npp_, global_np_object_.Get(), &np_script, &np_variant)) { + if (NPVARIANT_IS_OBJECT(np_variant)) { + np_result = NPObjectPtr<NPObject>(NPVARIANT_TO_OBJECT(np_variant)); + } + NPN_ReleaseVariantValue(&np_variant); + } + return np_result; +} + +namespace { +// Create code that looks like this: +// (function(func, protoArray) { +// return function() { +// switch (arguments.length) { +// case 0: +// return func.call(this); +// case 1: +// return func.call(this, +// arguments[0]); +// case 2: +// return func.call(this, +// arguments[0], +// arguments[1]); +// ... +// default: +// var args = protoArray.slice(); +// for (var i = 0; i < arguments.length; ++i) { +// args[i] = arguments[i]; +// } +// return func.apply(this, args); +// } +// }; +// }) +String MakeWrapFunctionScript() { + std::ostringstream code; + code << "(function(func, protoArray) {"; + code << " return function() {"; + code << " switch (arguments.length) {"; + for (int i = 0; i <= 10; ++i) { + code << " case " << i << ": return func.call(this"; + for (int j = 0; j < i; ++j) { + code << ", arguments[" << j << "]"; + } + code << ");"; + } + code << " default:"; + code << " var args = protoArray.slice();"; + code << " for (var i = 0; i < arguments.length; ++i) {"; + code << " args.push(arguments[i]);"; + code << " }"; + code << " return func.apply(this, args);"; + code << " }"; + code << " };"; + code << "})"; + return code.str(); +} +} // namespace anonymous + +void NPV8Bridge::Initialize(const NPObjectPtr<NPObject>& global_np_object) { + HandleScope handleScope; + + global_np_object_ = global_np_object; + + // This template is used for V8 proxies of NPObjects. + v8_np_constructor_template_ = Persistent<FunctionTemplate>::New( + FunctionTemplate::New()); + InitializeV8ObjectTemplate(v8_np_constructor_template_->InstanceTemplate()); + + // This template is used for the global V8 object. + Local<FunctionTemplate> v8_global_template = FunctionTemplate::New(); + InitializeV8ObjectTemplate(v8_global_template->PrototypeTemplate()); + + script_context_ = Context::New(NULL, v8_global_template->InstanceTemplate()); + Context::Scope scope(script_context_); + + // Give the global object a prototype that allows V8 to access global + // variables in another JavaScript environemnt over NPAPI. + Local<Object> v8_global_prototype = + Local<Object>::Cast(script_context_->Global()->GetPrototype()); + Local<Object> v8_global_prototype2 = + Local<Object>::Cast(v8_global_prototype->GetPrototype()); + global_prototype_ = Persistent<Object>::New(v8_global_prototype2); + NPToV8Object(v8_global_prototype2, global_np_object); + + function_map_ = Persistent<Object>::New(Object::New()); + + // Create a browser JavaScript function that can later be called to get the + // type of an object (as the browser sees it). This is useful for determining + // whether an object received over NPAPI is a function (which means its + // proxy must be created from a FunctionTemplate rather than an + // ObjectTemplate). + static const char kIsFunctionScript[] = + "(function(obj) { return obj instanceof Function; })"; + np_is_function_function_ = NPEvaluateObject(kIsFunctionScript); + + // Create a browser JavaScript function that can later be used to enumerate + // the properties of an object. This is used as a fallback if NPN_Evaluate + // is not implemented by the browser (like Firefox 2) and the enumerate + // callback is not implemented by the NPObject. + static const char kEnumerateScript[] = + "(function(object) {" + " var properties = [];" + " for (var property in object) {" + " if (object.hasOwnProperty(property)) {" + " properties[properties.length++] = property;" + " }" + " }" + " return properties;" + "})"; + np_enumerate_function_ = NPEvaluateObject(kEnumerateScript); + + // Create a browser JavaScript function that can later be used to create + // a wrapper around an V8 function proxy, making it appear to be a real + // browser function. + np_wrap_function_function_ = NPEvaluateObject( + MakeWrapFunctionScript().c_str()); + + // Create an NPObject proxy for a V8 array. This is for the browser to use as + // a prototype for creating new V8 arrays with slice(). + np_empty_array_ = V8ToNPObject(v8::Array::New(0)); +} + +void NPV8Bridge::ReleaseNPObjects() { + np_v8_object_map_.clear(); + np_construct_functions_.clear(); + + global_np_object_.Clear(); + np_is_function_function_.Clear(); + np_enumerate_function_.Clear(); + np_wrap_function_function_.Clear(); + np_empty_array_.Clear(); +} + +v8::Handle<Context> NPV8Bridge::script_context() { + return script_context_; +} + +bool NPV8Bridge::Evaluate(const NPVariant* np_args, int numArgs, + NPVariant* np_result) { + HandleScope handleScope; + Context::Scope scope(script_context_); + + Local<Value> v8_code; + if (numArgs == 1) { + v8_code = NPToV8Variant(np_args[0]); + } else { + return false; + } + + if (v8_code.IsEmpty() || !v8_code->IsString()) + return false; + + TryCatch tryCatch; + + Local<Script> v8_script = v8::Script::Compile(v8_code->ToString()); + if (tryCatch.HasCaught()) { + ReportV8Exception(tryCatch); + return false; + } + if (v8_script.IsEmpty()) + return false; + + Local<Value> v8_result = v8_script->Run(); + if (tryCatch.HasCaught()) { + ReportV8Exception(tryCatch); + return false; + } + if (v8_result.IsEmpty()) + return false; + + *np_result = V8ToNPVariant(v8_result); + return true; +} + +void NPV8Bridge::SetGlobalProperty(const String& name, + NPObjectPtr<NPObject>& np_object) { + HandleScope handleScope; + Context::Scope scope(script_context_); + script_context_->Global()->Set(v8::String::New(name.c_str()), + NPToV8Object(np_object)); +} + +NPVariant NPV8Bridge::V8ToNPVariant(Local<Value> value) { + HandleScope handleScope; + NPVariant np_variant; + if (value.IsEmpty() || value->IsUndefined()) { + VOID_TO_NPVARIANT(np_variant); + } else if (value->IsNull()) { + NULL_TO_NPVARIANT(np_variant); + } else if (value->IsBoolean()) { + BOOLEAN_TO_NPVARIANT(value->BooleanValue(), np_variant); + } else if (value->IsInt32()) { + INT32_TO_NPVARIANT(value->Int32Value(), np_variant); + } else if (value->IsNumber()) { + DOUBLE_TO_NPVARIANT(value->NumberValue(), np_variant); + } else if (value->IsString()) { + Local<v8::String> v8_string = value->ToString(); + int utf8_length = v8_string->Length(); + NPUTF8* utf8_chars = static_cast<NPUTF8*>(NPN_MemAlloc(utf8_length + 1)); + v8_string->WriteUtf8(utf8_chars); + STRINGN_TO_NPVARIANT(utf8_chars, utf8_length, np_variant); + } else if (value->IsObject()) { + Local<Object> v8_object = value->ToObject(); + NPObjectPtr<NPObject> np_object = V8ToNPObject(v8_object); + OBJECT_TO_NPVARIANT(np_object.Disown(), np_variant); + } + return np_variant; +} + +Local<Value> NPV8Bridge::NPToV8Variant(const NPVariant& np_variant) { + Local<Value> v8_result; + switch (np_variant.type) { + case NPVariantType_Void: + v8_result = Local<Value>::New(Undefined()); + break; + case NPVariantType_Null: + v8_result = Local<Value>::New(Null()); + break; + case NPVariantType_Bool: + v8_result = Local<Value>::New( + v8::Boolean::New(NPVARIANT_TO_BOOLEAN(np_variant))); + break; + case NPVariantType_Int32: + v8_result = Local<Value>::New( + Int32::New(NPVARIANT_TO_INT32(np_variant))); + break; + case NPVariantType_Double: + v8_result = Local<Value>::New( + Number::New(NPVARIANT_TO_DOUBLE(np_variant))); + break; + case NPVariantType_String: + { + NPString np_string = NPVARIANT_TO_STRING(np_variant); + v8_result = Local<Value>::New( + v8::String::New(np_string.utf8characters, np_string.utf8length)); + break; + } + case NPVariantType_Object: + v8_result = NPToV8Object( + NPObjectPtr<NPObject>(NPVARIANT_TO_OBJECT(np_variant))); + break; + default: + v8_result = Local<Value>(); + break; + } + return v8_result; +} + +NPObjectPtr<NPObject> NPV8Bridge::V8ToNPObject(Local<Value> v8_value) { + HandleScope handleScope; + NPObjectPtr<NPObject> np_object; + if (!v8_value.IsEmpty() && v8_value->IsObject()) { + Local<Object> v8_object = Local<Object>::Cast(v8_value); + if (v8_object->InternalFieldCount() == 0) { + // It is must be a V8 created JavaScript object (or function), a V8 + // function proxy for an NP function or a V8 function proxy for a named + // native method. If it is already associated with an NP object then that + // will be stored in the "internal property". Return that if it's there, + // otherwise create a new NP proxy. + Local<v8::String> internal_name = v8::String::NewSymbol( + kInternalProperty); + Local<Value> v8_internal = v8_object->GetHiddenValue(internal_name); + + if (v8_internal.IsEmpty() || v8_internal->IsUndefined()) { + // No existing NP object so create a proxy and store it in the "internal + // property". + np_object = NPV8Object::Create(this, v8_object); + v8_internal = External::New(np_object.Get()); + v8_object->SetHiddenValue(internal_name, v8_internal); + } else { + np_object = NPObjectPtr<NPObject>( + static_cast<NPObject*>( + Local<External>::Cast(v8_internal)->Value())); + } + + // If it is a V8 function then wrap it in a browser function so that its + // typeof will be reported as 'function' in the browser and it can be + // used in cases where a real function is required (rather than an + // object that just happens to be invocable. + if (v8_value->IsFunction() && + np_object->_class == &NPV8Object::np_class_) { + np_object = WrapV8Function(np_object); + } + } else { + // This is a V8 object proxy. The NP object is referenced from an internal + // field. + Local<Value> internal = v8_object->GetInternalField( + V8_NP_OBJECT_WRAPPED); + np_object = NPObjectPtr<NPObject>( + static_cast<NPObject*>(Local<External>::Cast(internal)->Value())); + } + } + return np_object; +} + +// Wrap NPV8Object proxying a V8 function in a browser function so that its +// typeof will be reported as 'function' in the browser and it can be +// used in cases where a real function is required (rather than an +// object that just happens to be invocable. +// A new wrapper function is created whenever a V8 function crosses into the +// browser. So === won't do the right thing in the browser. +NPObjectPtr<NPObject> NPV8Bridge::WrapV8Function( + const NPObjectPtr<NPObject>& np_object) { + + NPObjectPtr<NPObject> np_result = np_object; + NPVariant np_args[2]; + OBJECT_TO_NPVARIANT(np_object.Get(), np_args[0]); + OBJECT_TO_NPVARIANT(np_empty_array_.Get(), np_args[1]); + NPVariant np_variant; + if (NPN_InvokeDefault(npp_, np_wrap_function_function_.Get(), + np_args, 2, &np_variant)) { + if (NPVARIANT_IS_OBJECT(np_variant)) { + NPObjectPtr<NPObject> np_wrapper(NPVARIANT_TO_OBJECT(np_variant)); + + // Add a reference back to the NPV8Object so we can find it again. + if (NPN_SetProperty(npp_, np_wrapper.Get(), np_proxy_identifier_, + &np_args[0])) { + np_result = np_wrapper; + } + } + NPN_ReleaseVariantValue(&np_variant); + } + return np_result; +} + +Local<Value> NPV8Bridge::NPToV8Object(const NPObjectPtr<NPObject>& np_object) { + if (np_object.IsNull()) + return Local<Value>::New(Null()); + + // This might be a wrapper for a function. Find the actual proxy in that + // case. + NPObjectPtr<NPObject> np_real_object = np_object; + { + // NPN_GetProperty might cause an O3D NPObject to set an error if the + // property does not exist. Prevent that. It would be better to simply + // test whether the property exists by calling NPN_HasProperty but that + // is not supported in Mac Safari. + ErrorSuppressor error_suppressor(service_locator_); + NPVariant np_variant; + if (NPN_GetProperty(npp_, np_real_object.Get(), np_proxy_identifier_, + &np_variant)) { + if (NPVARIANT_IS_OBJECT(np_variant)) { + np_real_object = NPVARIANT_TO_OBJECT(np_variant); + } + NPN_ReleaseVariantValue(&np_variant); + } + } + + if (np_real_object->_class == &NPV8Object::np_class_) { + NPV8Object* np_v8_object = static_cast<NPV8Object*>(np_real_object.Get()); + return Local<Object>::New(np_v8_object->v8_object()); + } else { + NPV8ObjectMap::const_iterator it = np_v8_object_map_.find(np_real_object); + if (it != np_v8_object_map_.end()) + return Local<Object>::New(it->second); + + if (IsNPFunction(np_real_object)) { + return NPToV8Function(np_real_object); + } else { + Local<Function> v8_function = v8_np_constructor_template_->GetFunction(); + Local<Object> v8_object = v8_function->NewInstance(); + if (!v8_object.IsEmpty()) { + // NewInstance sets a JavaScript exception if it fails. Eventually + // it'll be caught when control flow hits a TryCatch. Just make sure + // not to dereference it before then. + NPToV8Object(v8_object, np_real_object); + } + return v8_object; + } + } +} + +void NPV8Bridge::NPToV8Object(v8::Local<Object> v8_target, + const NPObjectPtr<NPObject>& np_object) { + v8_target->SetInternalField(V8_NP_OBJECT_BRIDGE, External::New(this)); + v8_target->SetInternalField(V8_NP_OBJECT_WRAPPED, + External::New(np_object.Get())); + RegisterV8Object(v8_target, np_object); +} + +bool NPV8Bridge::IsNPFunction(const NPObjectPtr<NPObject>& np_object) { + // Before invoking the potentially expensive instanceof function (it has to + // go through the browser) check whether the object has a call + // property. If it doesn't have one then it isn't a JavaScript + // function. + if (!NPN_HasProperty(npp_, np_object.Get(), np_call_identifier_)) { + return false; + } + + // If it looks like it might be a function then call the instanceof function + // in the browser to confirm. + bool is_function = false; + NPVariant np_object_variant; + OBJECT_TO_NPVARIANT(np_object.Get(), np_object_variant); + NPVariant np_is_function; + if (NPN_InvokeDefault(npp_, np_is_function_function_.Get(), + &np_object_variant, 1, &np_is_function)) { + if (NPVARIANT_IS_BOOLEAN(np_is_function)) { + is_function = NPVARIANT_TO_BOOLEAN(np_is_function); + } + NPN_ReleaseVariantValue(&np_is_function); + } + return is_function; +} + +v8::Local<v8::Function> NPV8Bridge::NPToV8Function( + const NPObjectPtr<NPObject>& np_function) { + Local<FunctionTemplate> v8_function_template = FunctionTemplate::New( + V8CallFunction, External::New(this)); + + Local<Function> v8_function = v8_function_template->GetFunction(); + + Local<v8::String> internal_name = v8::String::NewSymbol( + kInternalProperty); + v8_function->SetHiddenValue(internal_name, External::New(np_function.Get())); + + // Copy function name from NP function. + NPVariant np_name; + if (NPN_GetProperty(npp_, np_function.Get(), np_name_identifier_, &np_name)) { + Local<Value> v8_name_value = NPToV8Variant(np_name); + NPN_ReleaseVariantValue(&np_name); + if (!v8_name_value.IsEmpty() && v8_name_value->IsString()) { + Local<v8::String> v8_name = Local<v8::String>::Cast(v8_name_value); + v8_function->SetName(v8_name); + } + } + + RegisterV8Object(v8_function, np_function); + return v8_function; +} + +void NPV8Bridge::RegisterV8Object(v8::Local<v8::Object> v8_object, + const NPObjectPtr<NPObject>& np_object) { + np_v8_object_map_[np_object] = Persistent<Object>::New(v8_object); + np_v8_object_map_[np_object].MakeWeak(this, NPV8WeakReferenceCallback); +} + +bool NPV8Bridge::IsNPObjectReferenced(NPObjectPtr<NPObject> np_object) { + return np_v8_object_map_.find(np_object) != np_v8_object_map_.end(); +} + +void NPV8Bridge::InitializeV8ObjectTemplate( + Local<ObjectTemplate> v8_object_template) { + v8_object_template->SetInternalFieldCount( + V8_NP_OBJECT_NUM_INTERNAL_FIELDS); + v8_object_template->SetNamedPropertyHandler(V8NamedPropertyGetter, + V8NamedPropertySetter, + V8NamedPropertyQuery, + V8NamedPropertyDeleter, + V8NamedPropertyEnumerator); + v8_object_template->SetIndexedPropertyHandler(V8IndexedPropertyGetter, + V8IndexedPropertySetter, + V8IndexedPropertyQuery, + V8IndexedPropertyDeleter, + V8IndexedPropertyEnumerator); + v8_object_template->SetCallAsFunctionHandler(V8CallAsFunction); +} + +void NPV8Bridge::NPV8WeakReferenceCallback(Persistent<Value> v8_value, + void* parameter) { + HandleScope handleScope; + NPV8Bridge* bridge = static_cast<NPV8Bridge*>(parameter); + NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject( + Local<Value>::New(v8_value)); + bridge->np_v8_object_map_.erase(np_object); +} + +void NPV8Bridge::ReportV8Exception(const TryCatch& v8_try_catch) { + if (v8_try_catch.HasCaught()) { + Local<Message> v8_message = v8_try_catch.Message(); + if (v8_message.IsEmpty()) { + Local<Value> v8_exception = v8_try_catch.Exception(); + if (v8_exception.IsEmpty()) { + error_status_->SetLastError( + "An unknown exception ocurred while executing V8 JavaScript code"); + } else { + v8::String::Utf8Value as_utf8(v8_exception); + if (*as_utf8) { + error_status_->SetLastError(*as_utf8); + } else { + error_status_->SetLastError( + "An exception was thrown but its toString method failed"); + } + } + } else { + String source_line(*v8::String::Utf8Value(v8_message->GetSourceLine())); + String text(*v8::String::Utf8Value(v8_message->Get())); + String message = text + " in " + source_line; + error_status_->SetLastError(message); + } + } +} + +v8::Local<v8::Array> NPV8Bridge::NPToV8IdentifierArray( + const NPVariant& np_array, bool named) { + Local<Array> v8_array; + if (!NPVARIANT_IS_OBJECT(np_array)) + return v8_array; + + NPObject* np_array_object = NPVARIANT_TO_OBJECT(np_array); + NPVariant np_length; + if (NPN_GetProperty(npp_, np_array_object, np_length_identifier_, + &np_length)) { + Local<Value> v8_length = NPToV8Variant(np_length); + NPN_ReleaseVariantValue(&np_length); + + if (v8_length.IsEmpty() || !v8_length->IsNumber()) + return v8_array; + + v8_array = Array::New(); + int length = v8_length->Int32Value(); + for (int i = 0; i < length; ++i) { + NPVariant np_element; + if (!NPN_GetProperty(npp_, np_array_object, NPN_GetIntIdentifier(i), + &np_element)) + return Local<Array>(); + Local<Value> v8_element = NPToV8Variant(np_element); + NPN_ReleaseVariantValue(&np_element); + if (v8_element->IsString() == named) { + v8_array->Set(Int32::New(v8_array->Length()), v8_element); + } + } + } + + return v8_array; +} + +Local<Array> NPV8Bridge::NPToV8IdentifierArray(const NPIdentifier* ids, + uint32_t id_count, bool named) { + Local<Array> v8_array = Array::New(); + for (uint32_t i = 0; i < id_count; ++i) { + if (NPN_IdentifierIsString(ids[i]) == named) { + v8_array->Set(Int32::New(v8_array->Length()), NPToV8Identifier(ids[i])); + } + } + return v8_array; +} + +Local<Array> NPV8Bridge::Enumerate(const NPObjectPtr<NPObject> np_object, + bool named) { + Local<Array> v8_array; + HandleScope handleScope; + + // First try calling NPN_Enumerate. This will return false if the browser + // does not support NPN_Enumerate. + NPIdentifier* ids; + uint32_t id_count; + if (NPN_Enumerate(npp_, np_object.Get(), &ids, &id_count)) { + v8_array = NPToV8IdentifierArray(ids, id_count, named); + NPN_MemFree(ids); + } else if (np_object->_class->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM && + np_object->_class->enumerate != NULL && + np_object->_class->enumerate(np_object.Get(), &ids, &id_count)) { + // Next see if the object has an enumerate callback and invoke it + // directly. This is the path used when V8 enumerates the + // properties of a native object if the browser does not support + // NPN_Enumerate. + v8_array = NPToV8IdentifierArray(ids, id_count, named); + NPN_MemFree(ids); + } else { + // The final fallback is to invoke a JavaScript function that + // enumerates all the properties into an array and returns it to + // the plugin. + NPVariant np_result; + NPVariant np_arg; + OBJECT_TO_NPVARIANT(np_object.Get(), np_arg); + if (NPN_InvokeDefault(npp_, np_enumerate_function_.Get(), &np_arg, 1, + &np_result)) { + v8_array = NPToV8IdentifierArray(np_result, named); + NPN_ReleaseVariantValue(&np_result); + } + } + + return v8_array; +} + +v8::Handle<Value> NPV8Bridge::V8PropertyGetter(Local<Value> v8_name, + const AccessorInfo& info) { + Local<Value> v8_result; + HandleScope handleScope; + + Local<Object> holder = info.Holder(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + if (holder.IsEmpty()) + return v8_result; + + NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); + if (np_object.IsNull()) + return v8_result; + + NPIdentifier np_name = V8ToNPIdentifier(v8_name); + if (np_name == NULL) + return v8_result; + + NPVariant np_result; + if (NPN_HasProperty(bridge->npp_, np_object.Get(), np_name) && + NPN_GetProperty(bridge->npp_, np_object.Get(), np_name, &np_result)) { + v8_result = bridge->NPToV8Variant(np_result); + NPN_ReleaseVariantValue(&np_result); + } else if (np_object->_class->hasMethod != NULL && + np_object->_class->hasMethod(np_object.Get(), np_name)) { + // It's not calling NPN_HasMethod here because of a bug in Firefox + // (Mozilla bug ID 467945), where NPN_HasMethod forwards to the object's + // hasProperty function instead. The workaround is to sidestep npruntime. + v8_result = bridge->function_map_->Get(v8_name); + if (v8_result.IsEmpty() || v8_result->IsUndefined()) { + Local<FunctionTemplate> function_template = + FunctionTemplate::New(V8CallNamedMethod, v8_name); + v8_result = function_template->GetFunction(); + bridge->function_map_->Set(v8_name, v8_result); + } + } + + return v8_result; +} + +v8::Handle<Value> NPV8Bridge::V8PropertySetter( + Local<Value> v8_name, + Local<Value> v8_value, + const AccessorInfo& info) { + Local<Value> v8_result; + HandleScope handleScope; + + Local<Object> holder = info.Holder(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); + if (np_object.IsNull()) + return v8_result; + + NPIdentifier np_name = V8ToNPIdentifier(v8_name); + if (np_name == NULL) + return v8_result; + + NPVariant np_value = bridge->V8ToNPVariant(v8_value); + NPN_SetProperty(bridge->npp_, np_object.Get(), np_name, &np_value); + NPN_ReleaseVariantValue(&np_value); + + return v8_result; +} + +v8::Handle<v8::Boolean> NPV8Bridge::V8PropertyQuery(Local<Value> v8_name, + const AccessorInfo& info) { + HandleScope handleScope; + + Local<Object> holder = info.Holder(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); + if (np_object.IsNull()) + return v8::Handle<v8::Boolean>(); + + NPIdentifier np_name = V8ToNPIdentifier(v8_name); + if (np_name == NULL) + return v8::Handle<v8::Boolean>(); + + bool has = NPN_HasProperty(bridge->npp_, np_object.Get(), np_name) || + NPN_HasMethod(bridge->npp_, np_object.Get(), np_name); + return v8::Boolean::New(has); +} + +v8::Handle<v8::Boolean> NPV8Bridge::V8PropertyDeleter( + Local<Value> v8_name, + const AccessorInfo& info) { + HandleScope handleScope; + + Local<Object> holder = info.Holder(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); + if (np_object.IsNull()) + return v8::Handle<v8::Boolean>(); + + NPIdentifier np_name = V8ToNPIdentifier(v8_name); + if (np_name == NULL) + return v8::Handle<v8::Boolean>(); + + // Workaround for a bug in Chrome. Chrome does not check whether the + // removeProperty callback is implemented before calling it, causing + // NPN_RemoveProperty to crash if it is not. So do the check before calling + // it. + bool deleted = np_object->_class->removeProperty != NULL && + NPN_RemoveProperty(bridge->npp_, np_object.Get(), np_name); + return v8::Boolean::New(deleted); +} + +v8::Handle<Value> NPV8Bridge::V8NamedPropertyGetter(Local<v8::String> v8_name, + const AccessorInfo& info) { + return V8PropertyGetter(v8_name, info); +} + +v8::Handle<Value> NPV8Bridge::V8NamedPropertySetter(Local<v8::String> v8_name, + Local<Value> v8_value, + const AccessorInfo& info) { + return V8PropertySetter(v8_name, v8_value, info); +} + +v8::Handle<v8::Boolean> NPV8Bridge::V8NamedPropertyQuery( + Local<v8::String> v8_name, + const AccessorInfo& info) { + return V8PropertyQuery(v8_name, info); +} + +v8::Handle<v8::Boolean> NPV8Bridge::V8NamedPropertyDeleter( + Local<v8::String> v8_name, + const AccessorInfo& info) { + return V8PropertyDeleter(v8_name, info); +} + +v8::Handle<Array> NPV8Bridge::V8NamedPropertyEnumerator( + const AccessorInfo& info) { + HandleScope handleScope; + + Local<Object> holder = info.Holder(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); + if (np_object.IsNull()) + return v8::Handle<Array>(); + + return bridge->Enumerate(np_object, true); +} + +v8::Handle<Value> NPV8Bridge::V8IndexedPropertyGetter( + uint32_t index, + const AccessorInfo& info) { + return V8PropertyGetter(Integer::New(index), info); +} + +v8::Handle<Value> NPV8Bridge::V8IndexedPropertySetter( + uint32_t index, + Local<Value> v8_value, + const AccessorInfo& info) { + return V8PropertySetter(Integer::New(index), v8_value, info); +} + +v8::Handle<v8::Boolean> NPV8Bridge::V8IndexedPropertyQuery( + uint32_t index, + const AccessorInfo& info) { + return V8PropertyQuery(Integer::New(index), info); +} + +v8::Handle<v8::Boolean> NPV8Bridge::V8IndexedPropertyDeleter( + uint32_t index, + const AccessorInfo& info) { + return V8PropertyDeleter(Integer::New(index), info); +} + +v8::Handle<Array> NPV8Bridge::V8IndexedPropertyEnumerator( + const AccessorInfo& info) { + HandleScope handleScope; + + Local<Object> holder = info.Holder(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); + if (np_object.IsNull()) + return v8::Handle<Array>(); + + return bridge->Enumerate(np_object, false); +} + +v8::Handle<Value> NPV8Bridge::V8CallNamedMethod(const Arguments& args) { + Local<Value> v8_result; + HandleScope handleScope; + + if (args.IsConstructCall()) + return v8_result; + + Local<Object> v8_holder = args.Holder(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + v8_holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + NPObjectPtr<NPObject> np_this = bridge->V8ToNPObject(v8_holder); + if (np_this.IsNull()) + return v8_result; + + v8::Handle<Value> v8_name = args.Data(); + NPIdentifier np_name = V8ToNPIdentifier(v8_name); + if (np_name == NULL) + return v8_result; + + std::vector<NPVariant> np_args(args.Length()); + for (int i = 0; i != args.Length(); ++i) { + np_args[i] = bridge->V8ToNPVariant(args[i]); + } + + NPVariant np_result; + if (NPN_Invoke(bridge->npp_, + np_this.Get(), + np_name, + args.Length() == 0 ? NULL : &np_args.front(), + args.Length(), + &np_result)) { + v8_result = bridge->NPToV8Variant(np_result); + NPN_ReleaseVariantValue(&np_result); + } + + for (int i = 0; i != args.Length(); ++i) { + NPN_ReleaseVariantValue(&np_args[i]); + } + + return v8_result; +} + +v8::Handle<Value> NPV8Bridge::V8CallFunction(const Arguments& args) { + Local<Value> v8_result; + HandleScope handleScope; + + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast(args.Data())->Value()); + Context::Scope scope(bridge->script_context()); + + Local<Function> v8_callee = args.Callee(); + Local<Object> v8_this = args.This(); + + // Allocate an extra argument element for the "this" pointer. This is only + // used if we end up invoking a method through function.call(this, arg0, ..., + // argn). + std::vector<NPVariant> np_args(args.Length() + 1); + VOID_TO_NPVARIANT(np_args[0]); + for (int i = 0; i != args.Length(); ++i) { + np_args[i + 1] = bridge->V8ToNPVariant(args[i]); + } + + // Need to determine whether the object was called as a standalone function, + // a method or a constructor. The constructor case is easy: args has a flag + // for it. If the function was called standalone then "this" will reference + // the global object. Otherwise assume it is a method invocation. + NPVariant np_result; + if (args.IsConstructCall()) { + // NPN_Construct was giving me trouble on some browsers (like Chrome). It + // might have better support in the future. For the time being, I'm using + // this alternative. + NPObjectPtr<NPObject> np_construct_function = + bridge->GetNPConstructFunction(args.Length()); + np_args[0] = bridge->V8ToNPVariant(v8_callee); + if (NPN_InvokeDefault(bridge->npp_, np_construct_function.Get(), + &np_args[0], args.Length() + 1, &np_result)) { + v8_result = bridge->NPToV8Variant(np_result); + NPN_ReleaseVariantValue(&np_result); + } + } else if (v8_this == bridge->script_context_->Global()) { + // Treat standalone case specially. We use NPN_InvokeDefault rather than + // NPN_Invoke with the "call" method because we want to have "this" refer + // to the browser's global environment rather than the V8 global + // environment. + NPObjectPtr<NPObject> np_callee = bridge->V8ToNPObject(v8_callee); + if (NPN_InvokeDefault(bridge->npp_, np_callee.Get(), 1 + &np_args[0], + args.Length(), &np_result)) { + v8_result = bridge->NPToV8Variant(np_result); + NPN_ReleaseVariantValue(&np_result); + } + } else { + // Invoke a function as a method by invoking its "call" call method. This + // is not the usual way of invoking a method in runtime. The usual way would + // to be to call NPN_Invoke on the target object (the one to be bound to + // "this") with a method name. But we don't know the method name. We don't + // even know if the function is assigned to one of the properties of the + // target object. To avoid that trouble, we invoke the function's "call" + // method with "this" as an explicit argument. + NPObjectPtr<NPObject> np_callee = bridge->V8ToNPObject(v8_callee); + np_args[0] = bridge->V8ToNPVariant(v8_this); + if (NPN_Invoke(bridge->npp_, np_callee.Get(), bridge->np_call_identifier_, + &np_args[0], args.Length() + 1, &np_result)) { + v8_result = bridge->NPToV8Variant(np_result); + NPN_ReleaseVariantValue(&np_result); + } + } + + for (int i = 0; i != args.Length() + 1; ++i) { + NPN_ReleaseVariantValue(&np_args[i]); + } + + return v8_result; +} + +v8::Handle<Value> NPV8Bridge::V8CallAsFunction(const Arguments& args) { + Local<Value> v8_result; + HandleScope handleScope; + + Local<Object> v8_callee = args.This(); + NPV8Bridge* bridge = static_cast<NPV8Bridge*>( + Local<External>::Cast( + v8_callee->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); + Context::Scope scope(bridge->script_context()); + + std::vector<NPVariant> np_args(args.Length()); + for (int i = 0; i != args.Length(); ++i) { + np_args[i] = bridge->V8ToNPVariant(args[i]); + } + + NPVariant np_result; + NPObjectPtr<NPObject> np_callee = bridge->V8ToNPObject(v8_callee); + if (NPN_InvokeDefault(bridge->npp_, np_callee.Get(), + args.Length() == 0 ? NULL : &np_args[0], args.Length(), + &np_result)) { + v8_result = bridge->NPToV8Variant(np_result); + NPN_ReleaseVariantValue(&np_result); + } + + for (int i = 0; i != args.Length(); ++i) { + NPN_ReleaseVariantValue(&np_args[i]); + } + + return v8_result; +} + +// Evaluates and returns an NP function that will construct an object. The +// function takes the constructor and constructor arguments as arguments. +// I'm doing this because not all browsers seem to support calling NPN_Construct +// on JavaScript constructor functions. +NPObjectPtr<NPObject> NPV8Bridge::GetNPConstructFunction(int arity) { + NPConstructFunctionMap::const_iterator it = np_construct_functions_.find( + arity); + if (it != np_construct_functions_.end()) + return it->second; + + // Build a function that looks like: + // (function (c,p0,p1) { return new c(p0,p1); }) + std::ostringstream code; + code << "(function(c"; + for (int i = 0; i != arity; ++i) { + code << ",p" << i; + } + code << ") { return new c("; + String separator = ""; + for (int i = 0; i != arity; ++i) { + code << separator << String("p") << i; + separator = ","; + } + code << "); })"; + + return NPEvaluateObject(code.str().c_str()); +} +} // namespace o3d diff --git a/o3d/plugin/cross/np_v8_bridge.h b/o3d/plugin/cross/np_v8_bridge.h new file mode 100644 index 0000000..779a5a3 --- /dev/null +++ b/o3d/plugin/cross/np_v8_bridge.h @@ -0,0 +1,400 @@ +/* + * 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. + */ + + +// Code relating to interoperation of V8 JavaScript engine with NPAPI. + +#ifndef O3D_PLUGIN_CROSS_NP_V8_BRIDGE_H_ +#define O3D_PLUGIN_CROSS_NP_V8_BRIDGE_H_ + +#include <npapi.h> +#include <npruntime.h> +#include <map> + +#include "base/cross/std_hash.h" +#include "core/cross/error_status.h" +#include "core/cross/service_dependency.h" +#include "core/cross/types.h" + +// The XLib header files define these preprocessor macros which v8 uses as +// identifiers. Need to undefine them before including v8. +#ifdef None +#undef None +#endif + +#ifdef True +#undef True +#endif + +#ifdef False +#undef False +#endif + +#ifdef Value +#undef Value +#endif + +#include "third_party/v8/include/v8.h" + +namespace o3d { + +// Smart pointer for NPObjects that automatically retains and releases the +// reference count. +template <typename T> +class NPObjectPtr { + public: + NPObjectPtr() + : owned(true), + object_(NULL) { + } + + template <typename U> + explicit NPObjectPtr(U* object) + : object_(object) { + Retain(); + } + + NPObjectPtr(const NPObjectPtr& rhs) + : object_(rhs.object_) { + Retain(); + } + + template <typename U> + explicit NPObjectPtr(const NPObjectPtr<U>& rhs) + : object_(rhs.Get()) { + Retain(); + } + + ~NPObjectPtr() { + Release(); + } + + template <typename U> + NPObjectPtr& operator=(U* rhs) { + Release(); + object_ = rhs; + Retain(); + return *this; + } + + NPObjectPtr& operator=(const NPObjectPtr& rhs) { + Release(); + object_ = rhs.object_; + Retain(); + return *this; + } + + template <typename U> + NPObjectPtr& operator=(const NPObjectPtr<U>& rhs) { + Release(); + object_ = rhs.Get(); + Retain(); + return *this; + } + + bool operator<(const NPObjectPtr& rhs) const { + return object_ < rhs.object_; + } + + bool operator==(const NPObjectPtr& rhs) const { + return object_ == rhs.object_; + } + + T* operator->() const { + return object_; + } + + T* Get() const { + return object_; + } + + bool IsNull() const { + return object_ == NULL; + } + + void Clear() { + Release(); + object_ = NULL; + } + + // Does not increment the reference count. When a function returns a pointer + // to an NPObject, the rule is that its reference count has already been + // incremented on behalf of the caller. + static NPObjectPtr AttachToReturned(T* object) { + NPObjectPtr result(object); + result.Release(); + return result; + } + + // Calling this prevents the NPObject's reference count from being decremented + // by this smart pointer when it is destroyed or a new reference is assigned. + T* Disown() const { + owned = false; + return object_; + } + + private: + void Retain() { + owned = true; + if (object_ != NULL) { + NPN_RetainObject(object_); + } + } + + void Release() { + if (owned && object_ != NULL) { + NPN_ReleaseObject(object_); + } + } + + mutable bool owned; + T* object_; +}; + +// Hashes an NPObject so it can be used in a hash_map. +template <typename T> +class NPObjectPtrHash { + public: + size_t operator() (const NPObjectPtr<T>& ptr) const { + return o3d::base::hash<size_t>()(reinterpret_cast<size_t>(ptr.Get())); + } +}; + +// A V8 handle that automatically disposes itself when it is destroyed. There +// must be only one of these for each persistent handle, otherwise they might +// be disposed more than once. +template <typename T> +class AutoV8Persistent : public v8::Persistent<T> { + public: + AutoV8Persistent() {} + + template <typename U> + explicit AutoV8Persistent(const v8::Persistent<U>& rhs) + : v8::Persistent<T>(rhs) { + } + + template <typename U> + AutoV8Persistent& operator=(const v8::Persistent<U>& rhs) { + *(v8::Persistent<T>*)this = rhs; + return *this; + } + + ~AutoV8Persistent() { + this->Dispose(); + this->Clear(); + } +}; + +// The bridge provides a way of evaluating JavaScript in the V8 engine and +// marshalling between V8 and NPAPI representations of objects and values. +class NPV8Bridge { + friend class NPV8Object; + public: + NPV8Bridge(ServiceLocator* service_locator, NPP npp); + ~NPV8Bridge(); + + NPP npp() { return npp_; } + + // Initializes the V8 environment. The global NPObject is wrapped with a V8 + // proxy and used as the global environment's prototype. This means that if + // a variable cannot be resolved in the V8 environment then it will attempt + // to resolve it in the NPObject. This allows V8 to read global variables in + // the browser environment. Note that assignments will never go to the + // global environment's prototype, changes will only be visible locally. + void Initialize(const NPObjectPtr<NPObject>& global_np_object); + + // This function tells the bridge to forget and release all of the NPObjects + // that it knows about. + void ReleaseNPObjects(); + + v8::Handle<v8::Context> script_context(); + + // Evaluates some JavaScript code in V8. It currently expects only one + // argument in the argument array, which must be a string containing the + // JavaScript code to evaluate. It returns the result of the evaluation + // as an NPAPI variant, which must be freed using NPN_ReleaseVariantValue. + bool Evaluate(const NPVariant* np_args, int numArgs, NPVariant* np_result); + + // Adds an object property to the V8 global environment. + void SetGlobalProperty(const String& name, + NPObjectPtr<NPObject>& np_object); + + // Converts a V8 value into an NPVariant. The NPVariant must be freed with + // NPN_ReleaseVariantValue. Caller must enter the script context. + NPVariant V8ToNPVariant(v8::Local<v8::Value> value); + + // Converts an NPVariant to a V8 value. Caller must enter the script context. + v8::Local<v8::Value> NPToV8Variant(const NPVariant& np_variant); + + // Converts a V8 object to an NPObject, either by wrapping the V8 object + // with an NPV8Object proxy or if the V8 object is a proxy, returning the + // NPObject it wraps. Caller must enter the script context. + NPObjectPtr<NPObject> V8ToNPObject(v8::Local<v8::Value> v8_object); + + // Converts an NPObject to a V8 object, either by wrapping the NPObject with + // a V8 proxy or if the NPObject is a proxy, returning the V8 object it wraps. + // Caller must enter the script context. + v8::Local<v8::Value> NPToV8Object(const NPObjectPtr<NPObject>& np_object); + + // Determines whether the given NPObject is currently referenced by V8 through + // a proxy. + bool IsNPObjectReferenced(NPObjectPtr<NPObject> np_object); + + private: + + NPObjectPtr<NPObject> NPEvaluateObject(const char* script); + + void NPToV8Object(v8::Local<v8::Object> v8_target, + const NPObjectPtr<NPObject>& np_object); + + bool IsNPFunction(const NPObjectPtr<NPObject>& np_object); + + v8::Local<v8::Function> NPToV8Function( + const NPObjectPtr<NPObject>& np_function); + + void ReleaseUnreferencedWrapperFunctions(); + + NPObjectPtr<NPObject> WrapV8Function(const NPObjectPtr<NPObject>& np_object); + + void RegisterV8Object(v8::Local<v8::Object> v8_object, + const NPObjectPtr<NPObject>& np_object); + + void InitializeV8ObjectTemplate( + v8::Local<v8::ObjectTemplate> v8_object_template); + + static void NPV8WeakReferenceCallback(v8::Persistent<v8::Value> value, + void* parameter); + + void ReportV8Exception(const v8::TryCatch& tryCatch); + + v8::Local<v8::Array> NPToV8IdentifierArray( + const NPVariant& np_array, bool named); + + v8::Local<v8::Array> NPToV8IdentifierArray( + const NPIdentifier* ids, uint32_t id_count, bool named); + + // Implements enumeration of NPObject properties using NPN_Evaluate where + // supported by the browser or otherwise falling back on emulation. Returns + // either named or indexed properties depending on named parameter. + v8::Local<v8::Array> Enumerate(const NPObjectPtr<NPObject> np_object, + bool named); + + static v8::Handle<v8::Value> V8PropertyGetter(v8::Local<v8::Value> v8_name, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Value> V8PropertySetter(v8::Local<v8::Value> v8_name, + v8::Local<v8::Value> v8_value, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Boolean> V8PropertyQuery(v8::Local<v8::Value> v8_name, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Boolean> V8PropertyDeleter( + v8::Local<v8::Value> v8_name, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Value> V8NamedPropertyGetter( + v8::Local<v8::String> v8_name, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Value> V8NamedPropertySetter( + v8::Local<v8::String> v8_name, + v8::Local<v8::Value> v8_value, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Boolean> V8NamedPropertyQuery( + v8::Local<v8::String> v8_name, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Boolean> V8NamedPropertyDeleter( + v8::Local<v8::String> v8_name, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Array> V8NamedPropertyEnumerator( + const v8::AccessorInfo& info); + + static v8::Handle<v8::Value> V8IndexedPropertyGetter( + uint32_t index, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Value> V8IndexedPropertySetter( + uint32_t index, + v8::Local<v8::Value> v8_value, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Boolean> V8IndexedPropertyQuery( + uint32_t index, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Boolean> V8IndexedPropertyDeleter( + uint32_t index, + const v8::AccessorInfo& info); + + static v8::Handle<v8::Array> V8IndexedPropertyEnumerator( + const v8::AccessorInfo& info); + + static v8::Handle<v8::Value> V8CallNamedMethod(const v8::Arguments& args); + + static v8::Handle<v8::Value> V8CallFunction(const v8::Arguments& args); + + static v8::Handle<v8::Value> V8CallAsFunction(const v8::Arguments& args); + + NPObjectPtr<NPObject> GetNPConstructFunction(int arity); + + typedef o3d::base::hash_map<NPObjectPtr<NPObject>, + AutoV8Persistent<v8::Object>, + NPObjectPtrHash<NPObject> > NPV8ObjectMap; + + typedef std::map<int, NPObjectPtr<NPObject> > NPConstructFunctionMap; + + ServiceLocator* service_locator_; + ServiceDependency<IErrorStatus> error_status_; + NPP npp_; + NPObjectPtr<NPObject> global_np_object_; + AutoV8Persistent<v8::Context> script_context_; + AutoV8Persistent<v8::FunctionTemplate> v8_np_constructor_template_; + AutoV8Persistent<v8::Object> function_map_; + AutoV8Persistent<v8::Object> global_prototype_; + NPV8ObjectMap np_v8_object_map_; + NPObjectPtr<NPObject> np_enumerate_function_; + NPObjectPtr<NPObject> np_is_function_function_; + NPObjectPtr<NPObject> np_wrap_function_function_; + NPConstructFunctionMap np_construct_functions_; + NPIdentifier np_name_identifier_; + NPIdentifier np_call_identifier_; + NPIdentifier np_length_identifier_; + NPIdentifier np_proxy_identifier_; + NPObjectPtr<NPObject> np_empty_array_; + DISALLOW_COPY_AND_ASSIGN(NPV8Bridge); +}; +} // namespace o3d + +#endif // O3D_PLUGIN_CROSS_NP_V8_BRIDGE_H_ diff --git a/o3d/plugin/cross/o3d_glue.cc b/o3d/plugin/cross/o3d_glue.cc new file mode 100644 index 0000000..0a7ddd4 --- /dev/null +++ b/o3d/plugin/cross/o3d_glue.cc @@ -0,0 +1,856 @@ +/* + * 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 <build/build_config.h> +#ifdef OS_WIN +#include <windows.h> +#include <shellapi.h> +#endif // OS_WIN + +#include <string> +#include <algorithm> +#include "core/cross/renderer.h" +#include "plugin/cross/o3d_glue.h" +#include "plugin/cross/config.h" +#include "plugin/cross/stream_manager.h" +#include "client_glue.h" +#include "globals_glue.h" +#include "third_party/nixysa/files/static_glue/npapi/common.h" + +#ifdef OS_MACOSX +#include "plugin_mac.h" +#endif + +namespace glue { +namespace _o3d { + +void RegisterType(NPP npp, const ObjectBase::Class *clientclass, + NPClass *npclass) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + plugin_object->RegisterType(clientclass, npclass); +} + +bool CheckObject(NPP npp, NPObject *npobject, + const ObjectBase::Class *clientclass) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + return plugin_object->CheckObject(npobject, clientclass); +} + +NPAPIObject *GetNPObject(NPP npp, ObjectBase *object) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + return plugin_object->GetNPObject(object); +} + +NPObject *Allocate(NPP npp, NPClass *npclass) { + return new NPAPIObject(npp); +} +void Deallocate(NPObject *object) { + NPAPIObject *npobject = static_cast<NPAPIObject *>(object); + if (npobject->mapped()) { + NPP npp = npobject->npp(); + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + plugin_object->UnmapObject(npobject); + } + delete npobject; +} + +ServiceLocator *GetServiceLocator(NPP npp) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + return plugin_object->service_locator(); +} + +Client *GetClient(NPP npp) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + return plugin_object->client(); +} + +PluginObject::PluginObject(NPP npp) + : npp_(npp), + evaluation_counter_(&service_locator_), + class_manager_(&service_locator_), + object_manager_(&service_locator_), + profiler_(&service_locator_), + fullscreen_(false), + renderer_(NULL), + features_(NULL), + fullscreen_region_valid_(false), + renderer_init_status_(Renderer::UNINITIALIZED), +#ifdef OS_WIN + hWnd_(NULL), + fullscreen_hWnd_(NULL), + parent_hWnd_(NULL), + plugin_hWnd_(NULL), + default_plugin_window_proc_(NULL), + got_dblclick_(false), + painted_once_(false), +#endif +#ifdef OS_MACOSX + renderer_is_software_(false), + scroll_is_in_progress_(false), + drawing_model_(NPDrawingModelQuickDraw), + event_model_(NPEventModelCarbon), + mac_window_(0), + mac_fullscreen_window_(0), + mac_fullscreen_overlay_window_(0), + mac_window_selected_tab_(0), + mac_cocoa_window_(0), + mac_surface_hidden_(0), + mac_2d_context_(0), + mac_agl_context_(0), + mac_cgl_context_(0), + last_mac_event_time_(0), + wants_redraw_(false), +#endif +#ifdef OS_LINUX + display_(NULL), + window_(0), + xt_widget_(NULL), + xt_app_context_(NULL), + xt_interval_(0), + draw_(true), + in_plugin_(false), + last_click_time_(0), +#endif + np_v8_bridge_(&service_locator_, npp), + stream_manager_(new StreamManager(npp)), + cursor_type_(o3d::Cursor::DEFAULT), + prev_width_(0), + prev_height_(0) { +#ifdef OS_WIN + memset(cursors_, 0, sizeof(cursors_)); +#endif + +#ifdef OS_MACOSX + memset(last_buffer_rect_, 0, sizeof(last_buffer_rect_)); +#endif + + // create an O3D object + client_ = new Client(&service_locator_); + + globals_npobject_ = glue::CreateStaticNPObject(npp); + client_npobject_ = + glue::namespace_o3d::class_Client::GetNPObject(npp, client_); + user_agent_ = GetUserAgent(npp); +} + +PluginObject::~PluginObject() { +} + +void PluginObject::Init(int argc, char* argn[], char* argv[]) { + DCHECK(argc == 0 || (argn != NULL && argv != NULL)); + features_ = new Features(&service_locator_); + + std::string o3d_name("o3d_features"); + + for (int ii = 0; ii < argc; ++ii) { + DCHECK(argn[ii]); + const char* name = argn[ii]; + if (o3d_name.compare(name) == 0) { + DCHECK(argv[ii]); + features_->Init(argv[ii]); + break; + } + } + + NPObject* np_window; + NPN_GetValue(npp_, NPNVWindowNPObject, &np_window); + o3d::NPObjectPtr<NPObject> np_window_ptr = + o3d::NPObjectPtr<NPObject>::AttachToReturned(np_window); + np_v8_bridge_.Initialize(np_window_ptr); + + o3d::NPObjectPtr<NPObject> np_plugin_ptr(this); + np_v8_bridge_.SetGlobalProperty(o3d::String("plugin"), + np_plugin_ptr); +} + +void PluginObject::TearDown() { +#ifdef OS_WIN + ClearPluginProperty(hWnd_); +#endif // OS_WIN +#ifdef OS_MACOSX + ReleaseSafariBrowserWindow(mac_cocoa_window_); +#endif + UnmapAll(); + + // Delete the StreamManager to cleanup any streams that are in midflight. + // This needs to happen here, before the client is deleted as the streams + // could be holding references to FileRequest objects. + stream_manager_.reset(NULL); + + delete client_; + // Release the graphics context before deletion. + DeleteRenderer(); + + delete features_; + + // There is a reference cycle between the V8 bridge and the plugin. + // Explicitly remove all V8 references during tear-down, so that the cycle is + // broken, and the reference counting system will successfully delete the + // plugin. + np_v8_bridge_.ReleaseNPObjects(); +} + +void PluginObject::CreateRenderer(const o3d::DisplayWindow& display_window) { + renderer_ = o3d::Renderer::CreateDefaultRenderer(&service_locator_); + DCHECK(renderer_); + + if (!CheckConfig(npp_)) { + renderer_init_status_ = o3d::Renderer::GPU_NOT_UP_TO_SPEC; + } else { + // Attempt to initialize renderer. + renderer_init_status_ = renderer_->Init(display_window, false); + if (renderer_init_status_ != o3d::Renderer::SUCCESS) { + DeleteRenderer(); + } + } +} + +void PluginObject::DeleteRenderer() { + if (renderer_) { + delete renderer_; + renderer_ = NULL; + } +} + + +#ifdef OS_MACOSX + +// These following 3 methods are needed for a Safari workaround - basically it +// does not notify us when our tab is hidden by tab switching, and just stops +// sending us events. The workaround is to keep track of when the browser last +// sent us an event, and what the selected tab was at that time. If we find that +// we are no longer getting events and the selected tab value has changed, +// DetectTabHiding() starts returning true when the timer calls it and code +// elsewhere can take action. SafariBrowserWindowForWindowRef() and +// SelectedTabForSafariBrowserWindow() are both written in such a way that +// non-Safari browsers should yield NULL, so DetectTabHiding would always +// return false and the workaround would not be triggered. + + +// Function gets notified every time we receive a Mac event. +// It records the time of the event and tries to read the selected tab value +// from Safari (on other browsers this tab value should always be NULL). +// Written so that last_mac_event_time_ is always valid or NULL. +void PluginObject::MacEventReceived() { + CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); + CFDateRef previousTime = last_mac_event_time_; + last_mac_event_time_ = now; + if (previousTime != NULL) { + CFRelease(previousTime); + } + if (!mac_cocoa_window_) { + mac_cocoa_window_ = SafariBrowserWindowForWindowRef(mac_window_); + } + mac_window_selected_tab_ = + SelectedTabForSafariBrowserWindow(mac_cocoa_window_); +} + +// Returns the time elapsed since the MacEventReceived function was last called. +CFTimeInterval PluginObject::TimeSinceLastMacEvent() { + CFTimeInterval elapsed = 0.0; + if (last_mac_event_time_ != NULL) { + CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); + elapsed = CFDateGetTimeIntervalSinceDate(now, last_mac_event_time_); + CFRelease(now); + } + return elapsed; +} + +// Detects if Safari has hidden our tab. +// The heuristic is that we have not received any Mac events for a certain time +// and also the value for selected tab is different from the value it had the +// last time we did get a Mac event. +bool PluginObject::DetectTabHiding() { + const CFTimeInterval kMacTimeOut = 0.2; // a fifth of a second + if (TimeSinceLastMacEvent() < kMacTimeOut) + return false; + + if (!mac_cocoa_window_) { + mac_cocoa_window_ = SafariBrowserWindowForWindowRef(mac_window_); + } + + return SelectedTabForSafariBrowserWindow(mac_cocoa_window_) != + mac_window_selected_tab_; +} + + +// Pick a constant way out of Apple's 0-22 range for our "no theme cursor" +// constant. +const ThemeCursor kNoThemeCursorForThat = 1000; + +// Map o3d cursors to Mac theme cursors if possible, otherwise return +// kNoThemeCursorForThat. +ThemeCursor O3DToMacThemeCursor(o3d::Cursor::CursorType cursor_type) { + switch (cursor_type) { + case o3d::Cursor::DEFAULT: + return kThemeArrowCursor; + case o3d::Cursor::NONE: // There is no standard blank cursor. + return kNoThemeCursorForThat; + case o3d::Cursor::CROSSHAIR: + return kThemeCrossCursor; + case o3d::Cursor::POINTER: + return kThemePointingHandCursor; + case o3d::Cursor::E_RESIZE: + return kThemeResizeRightCursor; + case o3d::Cursor::NE_RESIZE: // No diagonal resize directions on Mac. + return kNoThemeCursorForThat; + case o3d::Cursor::NW_RESIZE: // No diagonal resize directions on Mac. + return kNoThemeCursorForThat; + case o3d::Cursor::N_RESIZE: + return kThemeResizeUpCursor; + case o3d::Cursor::SE_RESIZE: // No diagonal resize directions on Mac. + return kNoThemeCursorForThat; + case o3d::Cursor::SW_RESIZE: // No diagonal resize directions on Mac. + return kNoThemeCursorForThat; + case o3d::Cursor::S_RESIZE: + return kThemeResizeDownCursor; + case o3d::Cursor::W_RESIZE: + return kThemeResizeLeftCursor; + case o3d::Cursor::MOVE: + return kThemeOpenHandCursor; + case o3d::Cursor::TEXT: + return kThemeIBeamCursor; + case o3d::Cursor::WAIT: + return kThemeWatchCursor; + case o3d::Cursor::PROGRESS: + return kThemeSpinningCursor; + case o3d::Cursor::HELP: // No standard Help cursor. + return kNoThemeCursorForThat; + } + + return kNoThemeCursorForThat; +} + + +// Set cursor to the one specified in cursor_type_. +// TODO add support for cursors that don't have equivalent Mac +// theme cursors. +void PluginObject::PlatformSpecificSetCursor() { + if (cursor_type_ == o3d::Cursor::NONE) { + // hide cursor if visible + if (CGCursorIsVisible()) { + CGDisplayHideCursor(kCGDirectMainDisplay); + } + } else { + ThemeCursor the_id = O3DToMacThemeCursor(cursor_type_); + + if (the_id != kNoThemeCursorForThat) { + SetThemeCursor(the_id); + } else { + // could add code here to set other cursors by other means + SetThemeCursor(kThemeArrowCursor); + } + // show cursor if hidden + if (!CGCursorIsVisible()) + CGDisplayShowCursor(kCGDirectMainDisplay); + } +} + +bool PluginObject::WantsRedraw() { + if (client()->render_mode() == o3d::Client::RENDERMODE_CONTINUOUS) + return true; + + // If we're rendering on-demand, then a call to client->render() should + // only force a redraw one time + return wants_redraw_; +} + +#endif // OS_MACOSX + + +void PluginObject::RegisterType(const ObjectBase::Class *clientclass, + NPClass *npclass) { + client_to_np_class_map_[clientclass] = npclass; + np_to_client_class_map_[npclass] = clientclass; +} + +bool PluginObject::CheckObject(NPObject *npobject, + const ObjectBase::Class *clientclass) const { + if (!npobject) return true; + NPClass *npclass = npobject->_class; + NPToClientClassMap::const_iterator it = np_to_client_class_map_.find(npclass); + if (it == np_to_client_class_map_.end()) return false; + if (static_cast<NPAPIObject *>(npobject)->npp()->pdata != this) { + // The object was created by another plug-in instance. Don't allow direct + // references to these objects, that would cause havok. + return false; + } + return ObjectBase::ClassIsA(it->second, clientclass); +} + +NPAPIObject *PluginObject::GetNPObject(ObjectBase *object) { + if (!object) return NULL; + NPAPIObject *npobject = object_map_[object->id()]; + if (!npobject) { + NPClass *npclass = GetNPClass(object->GetClass()); + GLUE_PROFILE_START(npp_, "createobject"); + npobject = static_cast<NPAPIObject *>(NPN_CreateObject(npp_, npclass)); + GLUE_PROFILE_STOP(npp_, "createobject"); + npobject->Initialize(object); + object_map_[object->id()] = npobject; + npobject->set_mapped(true); + } else { + GLUE_PROFILE_START(npp_, "retainobject"); + NPN_RetainObject(npobject); + GLUE_PROFILE_STOP(npp_, "retainobject"); + } + return npobject; +} + +void PluginObject::UnmapObject(NPAPIObject *npobject) { + npobject->set_mapped(false); + object_map_.erase(npobject->id()); +} + +void PluginObject::UnmapAll() { + for (ClientObjectMap::iterator it = object_map_.begin(); + it != object_map_.end(); ++it) { + it->second->set_mapped(false); + } + object_map_.clear(); +} + +NPClass *PluginObject::GetNPClass(const ObjectBase::Class *clientclass) { + const ObjectBase::Class *cursor = clientclass; + while (cursor) { + ClientToNPClassMap::const_iterator it = + client_to_np_class_map_.find(cursor); + if (it != client_to_np_class_map_.end()) { + NPClass *npclass = it->second; + if (cursor != clientclass) client_to_np_class_map_[clientclass] = npclass; + return npclass; + } + cursor = cursor->parent(); + } + return NULL; +} +// Static function to handle log asserts in the FATAL ERROR case +void PluginObject::LogAssertHandlerFunction(const std::string& str) { + DLOG(ERROR) << "FATAL LOG ERROR: " << str; +} + +enum { + PROP_CLIENT, + PROP_GPU_CONFIG, + NUM_PROPERTY_IDS +}; + +static NPIdentifier property_ids[NUM_PROPERTY_IDS]; +static const NPUTF8 *property_names[NUM_PROPERTY_IDS] = { + "client", + "gpuConfig", +}; + +enum { + METHOD_EVAL, + NUM_METHOD_IDS, +}; + +static NPIdentifier method_ids[NUM_METHOD_IDS]; +static const NPUTF8 *method_names[NUM_METHOD_IDS] = { + "eval", +}; + +static NPObject *PluginAllocate(NPP npp, NPClass *npclass) { + return new PluginObject(npp); +} + +static void PluginDeallocate(NPObject *object) { + delete static_cast<PluginObject *>(object); +} + +static bool PluginHasMethod(NPObject *header, NPIdentifier name) { + DebugScopedId id(name); + PluginObject *plugin_object = static_cast<PluginObject *>(header); + if (name == method_ids[METHOD_EVAL]) { + return true; + } else { + NPObject *globals = plugin_object->globals_npobject(); + return globals->_class->hasMethod(globals, name); + } +} + +static bool PluginInvoke(NPObject *header, NPIdentifier name, + const NPVariant *args, uint32_t argCount, + NPVariant *np_result) { + DebugScopedId id(name); + PluginObject *plugin_object = static_cast<PluginObject *>(header); + if (name == method_ids[METHOD_EVAL]) { + return plugin_object->np_v8_bridge()->Evaluate(args, argCount, np_result); + } else { + NPObject *globals = plugin_object->globals_npobject(); + return globals->_class->invoke(globals, name, args, argCount, np_result); + } +} + +static bool PluginInvokeDefault(NPObject *header, const NPVariant *args, + uint32_t argCount, NPVariant *result) { + PluginObject *plugin_object = static_cast<PluginObject *>(header); + NPP npp = plugin_object->npp(); + NPObject *globals = plugin_object->globals_npobject(); + return globals->_class->invokeDefault(globals, args, argCount, result); +} + +static bool PluginHasProperty(NPObject *header, NPIdentifier name) { + DebugScopedId id(name); + PluginObject *plugin_object = static_cast<PluginObject *>(header); + NPP npp = plugin_object->npp(); + for (unsigned int i = 0; i < NUM_PROPERTY_IDS; ++i) { + if (name == property_ids[i]) return true; + } + NPObject *globals = plugin_object->globals_npobject(); + return globals->_class->hasProperty(globals, name); +} + +static bool PluginGetProperty(NPObject *header, NPIdentifier name, + NPVariant *variant) { + DebugScopedId id(name); + PluginObject *plugin_object = static_cast<PluginObject *>(header); + NPP npp = plugin_object->npp(); + if (name == property_ids[PROP_GPU_CONFIG]) { + // Gets the GPU config (VendorID, DeviceID, name) as a string. + // NOTE: this should probably be removed before we ship. + GPUDevice device; + bool result = GetGPUDevice(npp, &device); + if (!result) return false; + std::string return_value = std::string("VendorID = 0x"); + char id_text[9]; + base::snprintf(id_text, sizeof(id_text), "%04x", device.vendor_id); + return_value += id_text; + return_value += ", DeviceID = 0x"; + base::snprintf(id_text, sizeof(id_text), "%04x", device.device_id); + return_value += id_text; + return_value += ", DeviceName = '"; + return_value += device.name + "'"; + return_value += ", Driver = '"; + return_value += device.driver + "'"; + return_value += ", Description = '"; + return_value += device.description + "'"; + return_value += ", GUID = 0x"; + base::snprintf(id_text, sizeof(id_text), "%08x", device.guid); + return_value += id_text; + GLUE_PROFILE_START(npp, "StringToNPVariant"); + bool temp = StringToNPVariant(return_value, variant); + GLUE_PROFILE_STOP(npp, "StringToNPVariant"); + return temp; + } + + if (name == property_ids[PROP_CLIENT]) { + NPObject *npobject = plugin_object->client_npobject(); + GLUE_PROFILE_START(npp, "retainobject"); + NPN_RetainObject(npobject); + GLUE_PROFILE_STOP(npp, "retainobject"); + OBJECT_TO_NPVARIANT(npobject, *variant); + return true; + } + NPObject *globals = plugin_object->globals_npobject(); + return globals->_class->getProperty(globals, name, variant); +} + +static bool PluginSetProperty(NPObject *header, NPIdentifier name, + const NPVariant *variant) { + DebugScopedId id(name); + PluginObject *plugin_object = static_cast<PluginObject *>(header); + NPP npp = plugin_object->npp(); + if (name == property_ids[PROP_CLIENT]) { + return false; + } + NPObject *globals = plugin_object->globals_npobject(); + return globals->_class->setProperty(globals, name, variant); +} + +static bool PluginEnumerate(NPObject *header, NPIdentifier **value, + uint32_t *count) { + *count = NUM_PROPERTY_IDS + NUM_METHOD_IDS + glue::GetStaticPropertyCount(); + PluginObject *plugin_object = static_cast<PluginObject *>(header); + NPP npp = plugin_object->npp(); + GLUE_PROFILE_START(npp, "memalloc"); + *value = static_cast<NPIdentifier *>( + NPN_MemAlloc(*count * sizeof(NPIdentifier))); + GLUE_PROFILE_STOP(npp, "memalloc"); + memcpy(*value, property_ids, NUM_PROPERTY_IDS * sizeof(NPIdentifier)); + memcpy(*value + NUM_PROPERTY_IDS, method_ids, + NUM_METHOD_IDS * sizeof(NPIdentifier)); + glue::StaticEnumeratePropertyHelper( + *value + NUM_PROPERTY_IDS + NUM_METHOD_IDS); + return true; +} + +static NPClass plugin_npclass = { + NP_CLASS_STRUCT_VERSION, + PluginAllocate, + PluginDeallocate, + 0, + PluginHasMethod, + PluginInvoke, + 0, + PluginHasProperty, + PluginGetProperty, + PluginSetProperty, + 0, + PluginEnumerate, +}; + +PluginObject *PluginObject::Create(NPP npp) { + GLUE_PROFILE_START(npp, "createobject"); + PluginObject *plugin_object = + static_cast<PluginObject *>(NPN_CreateObject(npp, &plugin_npclass)); + GLUE_PROFILE_STOP(npp, "createobject"); + return plugin_object; +} + +void InitializeGlue(NPP npp) { + GLUE_PROFILE_START(npp, "getstringidentifiers"); + NPN_GetStringIdentifiers(property_names, NUM_PROPERTY_IDS, property_ids); + NPN_GetStringIdentifiers(method_names, NUM_METHOD_IDS, method_ids); + GLUE_PROFILE_STOP(npp, "getstringidentifiers"); + glue::InitializeGlue(npp); +} + +// Plugin has been resized. +void PluginObject::Resize(int width, int height) { + // check that the window size has actually changed + if (prev_width_ != width || prev_height_ != height) { + prev_width_ = width; + prev_height_ = height; + if (renderer_ && !fullscreen_) { + // Tell the renderer and client that our window has been resized. + // If we're in fullscreen mode when this happens, we don't want to pass + // the information through; the renderer will pick it up when we switch + // back to plugin mode. + renderer_->Resize(width, height); + // This is just so that the client can send an event to the user. + client()->SendResizeEvent(width, height, fullscreen_); + } + } +} + +int PluginObject::width() const { + if (renderer_) { + return renderer_->width(); + } + return 0; +} + +int PluginObject::height() const { + if (renderer_) { + return renderer_->height(); + } + return 0; +} + +void PluginObject::GetDisplayModes(std::vector<o3d::DisplayMode> *modes) { + if (renderer()) { + renderer()->GetDisplayModes(modes); + } else { + modes->clear(); + } +} + +void PluginObject::RedirectToFile(const char *url) { + char cmd[] = "window.location = 'file:///%s';"; + scoped_array<char> full_cmd(new char[strlen(url) + sizeof(cmd)]); + sprintf(full_cmd.get(), cmd, url); + + NPObject *global_object; + NPN_GetValue(npp(), NPNVWindowNPObject, &global_object); + NPString string; + string.utf8characters = full_cmd.get(); + string.utf8length = strlen(string.utf8characters); + NPVariant result; + bool temp = NPN_Evaluate(npp(), global_object, &string, &result); + if (temp) { + NPN_ReleaseVariantValue(&result); + } +} + +o3d::Cursor::CursorType PluginObject::cursor() const { + return cursor_type_; +} + +void PluginObject::set_cursor(o3d::Cursor::CursorType cursor_type) { + cursor_type_ = cursor_type; + PlatformSpecificSetCursor(); +} + +#ifdef OS_WIN +bool PluginObject::fullscreen_class_registered_ = false; +WNDCLASSEX PluginObject::fullscreen_class_; + +static const wchar_t* kWindowPropertyName = L"o3d"; + +void PluginObject::StorePluginProperty(HWND hWnd, PluginObject *obj) { + if (obj->GetHWnd()) { // Clear out the record from the old window first. + ClearPluginProperty(obj->GetHWnd()); + } + StorePluginPropertyUnsafe(hWnd, obj); +} + +void PluginObject::StorePluginPropertyUnsafe(HWND hWnd, PluginObject *obj) { + obj->SetHWnd(hWnd); + if (hWnd) { + SetProp(hWnd, kWindowPropertyName, static_cast<HANDLE>(obj)); + ::DragAcceptFiles(hWnd, true); + } +} + +PluginObject *PluginObject::GetPluginProperty(HWND hWnd) { + return static_cast<PluginObject*>(GetProp(hWnd, kWindowPropertyName)); +} + +void PluginObject::ClearPluginProperty(HWND hWnd) { + if (hWnd) { + RemoveProp(hWnd, kWindowPropertyName); + ::DragAcceptFiles(hWnd, false); + } +} + +WNDCLASSEX *PluginObject::GetFullscreenWindowClass(HINSTANCE hInstance, + WNDPROC window_proc) { + if (!fullscreen_class_registered_) { + ZeroMemory(&fullscreen_class_, sizeof(WNDCLASSEX)); + fullscreen_class_.cbSize = sizeof(WNDCLASSEX); + fullscreen_class_.hInstance = hInstance; + fullscreen_class_.lpfnWndProc = window_proc; + fullscreen_class_.lpszClassName = L"O3DFullScreenWindowClass"; + fullscreen_class_.style = CS_DBLCLKS; + + RegisterClassEx(&fullscreen_class_); + fullscreen_class_registered_ = true; + } + return &fullscreen_class_; +} + +static LPCTSTR O3DToWindowsCursor(o3d::Cursor::CursorType cursor_type) { + switch (cursor_type) { + case o3d::Cursor::DEFAULT: + return IDC_ARROW; + case o3d::Cursor::NONE: + return NULL; + case o3d::Cursor::CROSSHAIR: + return IDC_CROSS; + case o3d::Cursor::POINTER: + return IDC_HAND; + case o3d::Cursor::E_RESIZE: + return IDC_SIZEWE; + case o3d::Cursor::NE_RESIZE: + return IDC_SIZENESW; + case o3d::Cursor::NW_RESIZE: + return IDC_SIZENWSE; + case o3d::Cursor::N_RESIZE: + return IDC_SIZENS; + case o3d::Cursor::SE_RESIZE: + return IDC_SIZENWSE; + case o3d::Cursor::SW_RESIZE: + return IDC_SIZENESW; + case o3d::Cursor::S_RESIZE: + return IDC_SIZENS; + case o3d::Cursor::W_RESIZE: + return IDC_SIZEWE; + case o3d::Cursor::MOVE: + return IDC_SIZEALL; + case o3d::Cursor::TEXT: + return IDC_IBEAM; + case o3d::Cursor::WAIT: + return IDC_WAIT; + case o3d::Cursor::PROGRESS: + return IDC_APPSTARTING; + case o3d::Cursor::HELP: + return IDC_HELP; + } + return IDC_ARROW; +} + +void PluginObject::PlatformSpecificSetCursor() { + if (!cursors_[cursor_type_]) { + cursors_[cursor_type_] = ::LoadCursor(NULL, + O3DToWindowsCursor(cursor_type_)); + } + ::SetCursor(cursors_[cursor_type_]); +} + +#endif // OS_WIN + +#ifdef OS_LINUX + +void PluginObject::PlatformSpecificSetCursor() { + // TODO: fill this in. +} + +#endif // OS_LINUX + +} // namespace _o3d + +namespace globals { + +using _o3d::PluginObject; +// This implements the definition in common.h of a function to receive +// all glue error reports. +void SetLastError(NPP npp, const char *error) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + O3D_ERROR(plugin_object->service_locator()) << error; +} + +// These implement the definitions in common.h of functions to receive +// all profiling calls. +void ProfileStart(NPP npp, const std::string& key) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + if (plugin_object) { // May not be initialized yet. + plugin_object->client()->ProfileStart(key); + } +} + +void ProfileStop(NPP npp, const std::string& key) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + if (plugin_object) { // May not be initialized yet. + plugin_object->client()->ProfileStop(key); + } +} + +void ProfileReset(NPP npp) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + if (plugin_object) { // May not be initialized yet. + plugin_object->client()->ProfileReset(); + } +} + +std::string ProfileToString(NPP npp) { + PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata); + if (plugin_object) { // May not be initialized yet. + return plugin_object->client()->ProfileToString(); + } else { + return ""; + } +} + +} // namespace globals +} // namespace glue diff --git a/o3d/plugin/cross/o3d_glue.h b/o3d/plugin/cross/o3d_glue.h new file mode 100644 index 0000000..019488a --- /dev/null +++ b/o3d/plugin/cross/o3d_glue.h @@ -0,0 +1,430 @@ +/* + * 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_CROSS_O3D_GLUE_H_ +#define O3D_PLUGIN_CROSS_O3D_GLUE_H_ + +#include <build/build_config.h> + +#ifdef OS_MACOSX +#include <OpenGL/OpenGL.h> +#include <AGL/agl.h> +#endif + +#ifdef OS_LINUX +#include <GL/glx.h> +#include <X11/Intrinsic.h> +#endif + + +#include <map> +#include <set> +#include <string> +#include <vector> +#include "base/scoped_ptr.h" +#include "base/cross/std_hash.h" +#include "core/cross/display_mode.h" +#include "core/cross/display_window.h" +#include "core/cross/object_base.h" +#include "core/cross/service_locator.h" +#include "core/cross/evaluation_counter.h" +#include "core/cross/class_manager.h" +#include "core/cross/features.h" +#include "core/cross/object_manager.h" +#include "core/cross/error.h" +#include "core/cross/profiler.h" +#include "plugin/cross/np_v8_bridge.h" +#include "client_glue.h" +#include "third_party/nixysa/files/static_glue/npapi/common.h" + +namespace o3d { +class Client; +class Renderer; +} + +namespace glue { +class StreamManager; + +namespace _o3d { +using o3d::Id; +using o3d::ObjectBase; +using o3d::Client; +using o3d::ClassManager; +using o3d::EvaluationCounter; +using o3d::Features; +using o3d::EvaluationCounter; +using o3d::ObjectManager; +using o3d::Profiler; +using o3d::Renderer; +using o3d::ServiceLocator; + +class NPAPIObject: public NPObject { + NPP npp_; + Id id_; + bool mapped_; + public: + explicit NPAPIObject(NPP npp): npp_(npp), id_(0), mapped_(false) {} + void Initialize(const ObjectBase *object) { id_ = object->id(); } + NPP npp() const { return npp_; } + Id id() const { return id_; } + bool mapped() const { return mapped_; } + void set_mapped(bool mapped) { mapped_ = mapped; } +}; +void RegisterType(NPP npp, const ObjectBase::Class *clientclass, + NPClass *npclass); +bool CheckObject(NPP npp, NPObject *npobject, + const ObjectBase::Class *clientclass); +NPAPIObject *GetNPObject(NPP npp, ObjectBase *object); +NPObject *Allocate(NPP npp, NPClass *npclass); +void Deallocate(NPObject *object); +ServiceLocator *GetServiceLocator(NPP npp); +Client *GetClient(NPP npp); +void InitializeGlue(NPP npp); + +typedef glue::namespace_o3d::class_Client::NPAPIObject ClientNPObject; + +class PluginObject: public NPObject { + typedef o3d::base::hash_map<Id, NPAPIObject *> ClientObjectMap; + typedef o3d::base::hash_map<const ObjectBase::Class *, NPClass *> + ClientToNPClassMap; + typedef o3d::base::hash_map<NPClass *, const ObjectBase::Class *> + NPToClientClassMap; + + NPP npp_; + ServiceLocator service_locator_; + EvaluationCounter evaluation_counter_; + ClassManager class_manager_; + ObjectManager object_manager_; + Profiler profiler_; + bool fullscreen_; // Are we rendered fullscreen or in the plugin region? + Renderer *renderer_; + Client *client_; + Features* features_; + ClientObjectMap object_map_; + ClientToNPClassMap client_to_np_class_map_; + NPToClientClassMap np_to_client_class_map_; + NPObject *globals_npobject_; + ClientNPObject *client_npobject_; + std::string user_agent_; + Renderer::InitStatus renderer_init_status_; + // The current cursor type. + o3d::Cursor::CursorType cursor_type_; + + // Last known dimensions of plugin + int prev_width_; + int prev_height_; + + o3d::NPV8Bridge np_v8_bridge_; + scoped_ptr<StreamManager> stream_manager_; + + public: +#ifdef OS_WIN + void SetDefaultPluginWindowProc(WNDPROC proc) { + default_plugin_window_proc_ = proc; + } + WNDPROC GetDefaultPluginWindowProc() { + return default_plugin_window_proc_; + } + void SetHWnd(HWND hWnd) { + hWnd_ = hWnd; + } + HWND GetHWnd() { + return hWnd_; + } + void SetFullscreenHWnd(HWND hWnd) { + fullscreen_hWnd_ = hWnd; + } + HWND GetFullscreenHWnd() { + return fullscreen_hWnd_; + } + void SetParentHWnd(HWND hWnd) { + parent_hWnd_ = hWnd; + } + HWND GetParentHWnd() { + return parent_hWnd_; + } + void SetPluginHWnd(HWND hWnd) { + plugin_hWnd_ = hWnd; + } + HWND GetPluginHWnd() { + return plugin_hWnd_; + } + bool RecordPaint() { + bool painted = painted_once_; + painted_once_ = true; + return painted; + } + bool got_dblclick() const { return got_dblclick_; } + void set_got_dblclick(bool got_dblclick) { got_dblclick_ = got_dblclick; } +#endif +#ifdef OS_MACOSX + + void SetFullscreenOverlayMacWindow(WindowRef window) { + mac_fullscreen_overlay_window_ = window; + } + + WindowRef GetFullscreenOverlayMacWindow() { + return mac_fullscreen_overlay_window_; + } + + void SetFullscreenMacWindow(WindowRef window) { + mac_fullscreen_window_ = window; + } + + WindowRef GetFullscreenMacWindow() { + return mac_fullscreen_window_; + } + + // Always returns |true| if RENDERMODE_CONTINUOUS, otherwise + // only if client->render() has been called and we haven't yet + // handled it + bool WantsRedraw(); + void SetWantsRedraw(bool wants) { wants_redraw_ = wants; } + + bool ScrollIsInProgress() { return scroll_is_in_progress_; } + void SetScrollIsInProgress(bool state) { scroll_is_in_progress_ = state; } + bool scroll_is_in_progress_; + + bool RendererIsSoftware() {return renderer_is_software_;} + bool SetRendererIsSoftware(bool state) {renderer_is_software_ = state;} + bool renderer_is_software_; + + NPDrawingModel drawing_model_; + NPEventModel event_model_; + WindowRef mac_window_; // may be NULL in the Chrome case + WindowRef mac_fullscreen_window_; // NULL if not in fullscreen modee + WindowRef mac_fullscreen_overlay_window_; // NULL if not in fullscreen mode + // these vars needed for the Safari tab switch detection hack + CFDateRef last_mac_event_time_; + bool wants_redraw_; + void * mac_cocoa_window_; + void* mac_window_selected_tab_; + bool mac_surface_hidden_; + // end of Safari tab detection vars + GLint last_buffer_rect_[4]; + Point last_plugin_loc_; + // can be a CGrafPtr, a CGContextRef or NULL depending on drawing_model + void* mac_2d_context_; + // either can be NULL depending on drawing_model + AGLContext mac_agl_context_; + CGLContextObj mac_cgl_context_; +#endif +#ifdef OS_LINUX + Display *display_; + Window window_; + Widget xt_widget_; + XtAppContext xt_app_context_; + XtIntervalId xt_interval_; + bool draw_; + bool in_plugin_; + Time last_click_time_; +#endif + explicit PluginObject(NPP npp); + ~PluginObject(); + void Init(int argc, char* argn[], char* argv[]); + void TearDown(); + ServiceLocator* service_locator() { return &service_locator_; } + Client *client() { return client_; } + Renderer *renderer() { return renderer_; } + NPP npp() { return npp_; } + void RegisterType(const ObjectBase::Class *clientclass, NPClass *npclass); + bool CheckObject(NPObject *npobject, + const ObjectBase::Class *clientclass) const; + NPAPIObject *GetNPObject(ObjectBase *object); + void UnmapObject(NPAPIObject *npobject); + void UnmapAll(); + NPClass *GetNPClass(const ObjectBase::Class *clientclass); + + NPObject *globals_npobject() { return globals_npobject_; } + ClientNPObject *client_npobject() { return client_npobject_; } + + static const char* kOnRenderEventName; + + static PluginObject *Create(NPP npp); + + StreamManager *stream_manager() const { return stream_manager_.get(); } + + static void LogAssertHandlerFunction(const std::string& str); + + Renderer::InitStatus renderer_init_status() const { + return renderer_init_status_; + } + + // Plugin has been resized. + void Resize(int width, int height); + + int width() const; + int height() const; + + // Fullscreen stuff + bool fullscreen() const { + return fullscreen_; + } + + // Get a vector of the available fullscreen display modes. + // Clears *modes on error. + void GetDisplayModes(std::vector<o3d::DisplayMode> *modes); + + // This isn't exposed directly to JavaScript for security reasons; it gets + // called by the platform-specific event-handling code if the region set by + // SetFullscreenClickRegion is clicked. It requests the mode previously set + // by SetFullscreenClickRegion(), and fails if there wasn't one. + bool RequestFullscreenDisplay(); + + // Make a region of the plugin area that will invoke fullscreen mode if + // clicked. The app developer is responsible for communicating this to the + // user, as this region has no visible marker. The user is also responsible + // for updating this region if the plugin gets resized, as we don't know + // whether or how to scale it. + void SetFullscreenClickRegion(int x, int y, int width, int height, + int mode_id) { + fullscreen_region_valid_ = true; + fullscreen_region_x_ = x; + fullscreen_region_y_ = y; + fullscreen_region_width_ = width; + fullscreen_region_height_ = height; + fullscreen_region_mode_id_ = mode_id; + } + void ClearFullscreenClickRegion() { + fullscreen_region_valid_ = false; + } + + // Tests a mouse click location for a hit on the fullscreen click region. + bool HitFullscreenClickRegion(int x, int y) { + if (!fullscreen_region_valid_ || fullscreen_) { + return false; + } + return (x >= fullscreen_region_x_) && + (x - fullscreen_region_x_ < fullscreen_region_width_) && + (y >= fullscreen_region_y_) && + (y - fullscreen_region_y_ < fullscreen_region_height_); + } + + // Deactivate fullscreen mode. + void CancelFullscreenDisplay(); + + // Redirect by setting window.location to file:///<url>. + void RedirectToFile(const char *url); + + // Get the cursor's shape. + o3d::Cursor::CursorType cursor() const; + + // Set the cursor's shape. + void set_cursor(o3d::Cursor::CursorType cursor_type); + + // Sets the cursor to whatever the current cursor is. + void PlatformSpecificSetCursor(); + + const std::string& user_agent() const { return user_agent_; } + bool IsFirefox() const { + return user_agent_.find("Firefox") != user_agent_.npos; + } + bool IsChrome() const { + return user_agent_.find("Chrome") != user_agent_.npos; + } + bool IsMSIE() const { + return user_agent_.find("MSIE") != user_agent_.npos; + } + + v8::Handle<v8::Context> script_context() { + return np_v8_bridge_.script_context(); + } + + o3d::NPV8Bridge* np_v8_bridge() { return &np_v8_bridge_; } + + // Creates the renderer. + void CreateRenderer(const o3d::DisplayWindow& display_window); + + // Deletes the renderer. + void DeleteRenderer(); + +#ifdef OS_MACOSX + // Methods needed for the Safari tab-switching hack. + void MacEventReceived(); + + CFTimeInterval TimeSinceLastMacEvent(); + + bool DetectTabHiding(); +#endif + +#ifdef OS_WIN + // Stashes a pointer to the plugin on the HWND, and keeps a corresponding + // pointer to the HWND on the plugin. If the plugin's already registered with + // an HWND, this will clear out the plugin pointer on the old HWND before + // setting up the new one. + static void StorePluginProperty(HWND hWnd, PluginObject *obj); + // Similar to StorePluginProperty, but doesn't clear out any old pointers. + static void StorePluginPropertyUnsafe(HWND hWnd, PluginObject *obj); + // Looks up the plugin stored on this HWND. + static PluginObject *GetPluginProperty(HWND hWnd); + // Clears out the plugin stored on this HWND. + static void ClearPluginProperty(HWND hWnd); + // WNDCLASSEX used for constructing fullscreen windows. + static WNDCLASSEX *GetFullscreenWindowClass( + HINSTANCE instance, WNDPROC window_proc); + // One bit of state that helps us turn the sequence of events that windows + // produces on a double click into what JavaScript is expecting. + bool got_dblclick_; +#endif // OS_WIN +#ifdef OS_LINUX + bool in_plugin() const { return in_plugin_; } + void set_in_plugin(bool value) { in_plugin_ = value; } + Time last_click_time() const { return last_click_time_; } + void set_last_click_time(Time value) { last_click_time_ = value; } +#endif + + private: + bool fullscreen_region_valid_; + int fullscreen_region_x_; + int fullscreen_region_y_; + int fullscreen_region_width_; + int fullscreen_region_height_; + int fullscreen_region_mode_id_; +#ifdef OS_WIN + HWND hWnd_; // The window we are currenlty drawing to (use this) + HWND fullscreen_hWnd_; // The fullscreen window if we are fullscreen + HWND parent_hWnd_; + HWND plugin_hWnd_; // The window we were given inside the browser. + HCURSOR cursors_[o3d::Cursor::NUM_CURSORS]; // loaded windows cursors. + HCURSOR hCursor_; + WNDPROC default_plugin_window_proc_; + static WNDCLASSEX fullscreen_class_; + static bool fullscreen_class_registered_; + bool painted_once_; +#endif // OS_WIN +}; + +} // namespace o3d +} // namespace glue + + +#endif // O3D_PLUGIN_CROSS_O3D_GLUE_H_ diff --git a/o3d/plugin/cross/out_of_memory.cc b/o3d/plugin/cross/out_of_memory.cc new file mode 100644 index 0000000..47937f1 --- /dev/null +++ b/o3d/plugin/cross/out_of_memory.cc @@ -0,0 +1,231 @@ +/* + * 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 implements a failure handler for the new +// operator and malloc. + +// TODO: This does not currently work on linux. The replacement +// operator new, malloc, etc do not take priority over those declared in +// the standard libraries. + +#include <stdio.h> +#include <stdlib.h> +#include <wchar.h> +#include <build/build_config.h> + +#ifdef _MSC_VER +#include <new.h> +#endif + +#ifdef OS_WIN +#include <windows.h> +#endif + +#if defined(OS_MACOSX) || defined(OS_LINUX) +#include <dlfcn.h> +#endif + +#include "plugin/cross/out_of_memory.h" +#include "plugin/cross/plugin_metrics.h" + +#ifdef _MSC_VER +extern "C" { + void* _getptd(); +} // _MSC_VER +#endif + +namespace o3d { +namespace { +// The reserve is allocated just after the plugin starts. +// In the event that an allocation fails, the reserve is +// freed, hopefully freeing enough memory to allow any code +// run after abort() to do its work. +const size_t kReserveSize = 1024 * 256; +void* g_reserve; +} // namespace anonymous + +// This is called when a memory allocation fails in the plugin. Note +// that it will not be called if a memory allocation fails in another +// shared library, such as in the c runtime library on platforms where +// we use a shared c runtime library. In those cases, we have to hope +// that the allocation failed because new was called (in which case an +// exception will be thrown but the reserve will not be freed) or that +// the calling library correctly checks for a NULL return and does +// something appropriate. +int HandleOutOfMemory(size_t size) { + if (g_reserve != NULL) { + // First time round, free the reserve and exit with abort. + // This should allow some crash reporting before the process + // exits.signal + free(g_reserve); + g_reserve = NULL; + + // Do this on MacOSX and Linux when they support metrics. + // Also, at the time of writing, the matrics logging is not + // hooked up to breakpad, so this metric will not get logged + // anywhere! Remove this comment when that is done and tested. +#ifdef OS_WIN + ++metric_out_of_memory_total; +#endif // OS_MACOSX + + fprintf(stderr, "Aborting: out of memory allocating %lu bytes\n", + static_cast<unsigned long>(size)); + +#ifdef OS_WIN + // This is different on Windows so that it is comnpatible with + // the way that breakpad works. On Windows, it intercepts + // exceptions. On unixy platforms, it handles signals. Also, + // on Windows, this is friendlier to the browser's own crash + // logging (for browsers that log crashes). + RaiseException(ERROR_OUTOFMEMORY, + EXCEPTION_NONCONTINUABLE_EXCEPTION, + 0, NULL); +#else // OS_WIN + abort(); +#endif // OS_WIN + } else { + // If the handler is reentered, try to exit without raising + // SIGABRT (or executing exit handlers). + _exit(EXIT_FAILURE); + } + return 0; +} + +bool SetupOutOfMemoryHandler() { +#ifdef _MSC_VER + // Workaround for MSVC. Sometimes the runtime calls this when + // there is not enough memory to allocate the "per-thread + // data structure". Calling this forces the "per-thread data + // structure" to be allocated. + _getptd(); + + // This causes malloc failures to call the new handler as well. + // It is not necessary to replace the implementations of malloc, + // etc under MSVC. + _set_new_mode(1); + _set_new_handler(HandleOutOfMemory); +#endif // _MSC_VER + + g_reserve = malloc(kReserveSize); + + return true; +} +} // namespace o3d + +#if defined(OS_MACOSX) || defined(OS_LINUX) +namespace { +void* dlsym_helper(const char* symbol_name) { + void* ptr = dlsym(RTLD_NEXT, symbol_name); + if (ptr == NULL) { + fprintf(stderr, "Error: could not locate symbol \"%s\"\n", symbol_name); + abort(); + } + return ptr; +} +} // namespace anonymous + +void* operator new(size_t size) { + return malloc(size); +} + +void operator delete(void* ptr) { + free(ptr); +} + +extern "C" { +void *malloc(size_t size) { + typedef void* (*Func)(size_t); + static Func func = (Func) dlsym_helper("malloc"); + void* ptr = func(size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +void *realloc(void* old_ptr, size_t new_size) { + typedef void* (*Func)(void*, size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("realloc")); + void* ptr = func(old_ptr, new_size); + + // realloc() returns NULL when you ask for zero bytes. This differs + // from malloc() which returns a pointer to a zero sized allocated block. + if (new_size != 0 && ptr == NULL) + o3d::HandleOutOfMemory(new_size); + return ptr; +} + +void *calloc(size_t num_items, size_t size) { + typedef void* (*Func)(size_t, size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("calloc")); + void* ptr = func(num_items, size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +void *valloc(size_t size) { + typedef void* (*Func)(size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("valloc")); + void* ptr = func(size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +void* memalign(size_t alignment, size_t size) { + typedef void* (*Func)(size_t, size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("memalign")); + void* ptr = func(alignment, size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +char* strdup(const char* ptr) { + typedef char* (*Func)(const char*); + static Func func = reinterpret_cast<Func>(dlsym_helper("strdup")); + char* result = func(ptr); + if (ptr != NULL && result == NULL) + o3d::HandleOutOfMemory((strlen(ptr) + 1) * sizeof(char)); + return result; +} + +wchar_t* wcsdup(const wchar_t* ptr) { + typedef wchar_t* (*Func)(const wchar_t*); + static Func func = reinterpret_cast<Func>(dlsym_helper("wcsdup")); + wchar_t* result = func(ptr); + if (ptr != NULL && result == NULL) + o3d::HandleOutOfMemory((wcslen(ptr) + 1) * sizeof(wchar_t)); + return result; +} +} +#endif // defined(OS_MACOSX) || defined(OS_LINUX) diff --git a/o3d/plugin/cross/out_of_memory.h b/o3d/plugin/cross/out_of_memory.h new file mode 100644 index 0000000..aef1b1e --- /dev/null +++ b/o3d/plugin/cross/out_of_memory.h @@ -0,0 +1,44 @@ +/* + * 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 declares a failure handler for the new +// operator and malloc and a function to set it up. + +#ifndef O3D_PLUGIN_CROSS_OUT_OF_MEMORY_H_ +#define O3D_PLUGIN_CROSS_OUT_OF_MEMORY_H_ + +namespace o3d { +void HandleOutOfMemory(); +bool SetupOutOfMemoryHandler(); +} + +#endif // O3D_PLUGIN_CROSS_OUT_OF_MEMORY_H_ diff --git a/o3d/plugin/cross/plugin_logging.h b/o3d/plugin/cross/plugin_logging.h new file mode 100644 index 0000000..f69c0a7 --- /dev/null +++ b/o3d/plugin/cross/plugin_logging.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. + */ + + +// This file defines the logging object which performs the metric aggregation +// and uploading. This class takes care of the initialization of the logging +// object and determining if the user has opted in or out to having logs sent +// back. Furthermore, there are some helper functions to make testing easier. + +#ifndef O3D_PLUGIN_CROSS_PLUGIN_LOGGING_H_ +#define O3D_PLUGIN_CROSS_PLUGIN_LOGGING_H_ + +#include "statsreport/common/highres_timer.h" +#include "base/scoped_ptr.h" +#include "base/basictypes.h" + +namespace o3d { + +class PluginLogging { + public: + PluginLogging(); + virtual ~PluginLogging() {} + + // Check to see if sufficient time has passed to process metrics. If such + // time has passed, we reset the timer and proceed with processing metrics. + // + // Returns true if the metrics were processed properly. + bool UpdateLogging(); + + // Record how much time the plugin has spent running. + virtual void RecordProcessTimes(); + + // Takes care of gathering current statistics and uploading them to the server + // if necessary. + // + // Parameters: + // exiting - whether the program is exiting + // + // Returns true if metrics were uploaded and/or aggregated successfully. + virtual bool ProcessMetrics(const bool exiting, + const bool force_report); + + // A helper function to call AggregateMetrics used for testing. Calls + // AggregateMetrics which gathers up the current metrics, puts them in + // the registry and then resets them. + virtual void DoAggregateMetrics(); + + // A helper function for testing. This function calls + // stats_report::AggregateAndReportMetrics which will aggregate the metrics + // and upload to the server if sufficient time has passed. + // + // Parameters: + // extra_url_argument - extra url to be added after source id (O3D) + // and version number + // user_agent - eventually the client_id, currently not used + // + // Returns true if metrics were uploaded successfully. Note: false does + // not necessarily mean an error; just that no metrics were uploaded. + virtual bool DoAggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + const bool force_report); + + // PluginLogging assumes ownership of the timer. + void SetTimer(HighresTimer* timer); + + // Factory method for creating the logger and taking care of initialization + // and checks for opt-in/out. + // + // Returns the results CreateUsageStatsLogger which will be a pointer to new + // PluginLogging object if the user opted in or NULL if they opted out. + // + // The existence of a PluginLogging object is used to check if logging is + // turned on in other parts of the code. + static PluginLogging* InitializeUsageStatsLogging(); + + // Access the key determing opt-in. Separated out for testing. + // Returns true if the user opted in. +#ifdef OS_MACOSX + static bool GetOptInKeyValue(void); +#else + static bool GetOptInKeyValue(const wchar_t* clientstate_registry_key, + const wchar_t* opt_in_registry_key); +#endif + + // Method for actually creating the logger. Separated out for testing. + // + // Returns the results CreateUsageStatsLogger which will be a pointer to new + // PluginLogging object if the user opted in or NULL if they opted out. + // + // The existence of a PluginLogging object is used to check if logging is + // turned on in other parts of the code. + template <class LoggingType> + static LoggingType* CreateUsageStatsLogger(const bool opt_in) { + if (opt_in == true) { + // They opted in! + LoggingType* logger = new LoggingType(); + stats_report::g_global_metrics.Initialize(); + + // Do an initial grab of the metrics. Don't pass true for force_report. + // This will force an upload of the metrics the first time o3d is run + // since the lastTransmission metric will not exist. + logger->ProcessMetrics(false, false); + return logger; + } + // Otherwise, they opted out so we make sure the registry is clear + ClearLogs(); + return NULL; + } + + // Method for cleaning out the logs. Used if the user opts-out to make sure + // we don't retain any information from them. + static void ClearLogs(); + + private: + // Timer for determining the next time aggregation should occur. + scoped_ptr<HighresTimer> timer_; + uint64 running_time_; + uint64 prev_uptime_seconds_; + uint64 prev_cputime_seconds_; + DISALLOW_COPY_AND_ASSIGN(PluginLogging); +}; + +} // namespace o3d + +#endif // O3D_PLUGIN_CROSS_PLUGIN_LOGGING_H_ diff --git a/o3d/plugin/cross/plugin_logging_test.cc b/o3d/plugin/cross/plugin_logging_test.cc new file mode 100644 index 0000000..a480604 --- /dev/null +++ b/o3d/plugin/cross/plugin_logging_test.cc @@ -0,0 +1,228 @@ +/* + * 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/cross/plugin_logging.h" +#include <shlwapi.h> +#ifdef OS_WIN +#include <atlbase.h> +#endif +#include "statsreport/metrics.h" +#include "statsreport/uploader.h" +#ifdef OS_WIN +#include "tests/common/win/testing_common.h" +#endif + +// This mock class mimics the timer so that we don't actually have to wait for +// the timer to timeout in the tests since we're not testing the timer here. +class MockTimer : public HighresTimer { + public: + using HighresTimer::Start; + MockTimer() : elapsed_ms_(0) {} + virtual ULONGLONG GetElapsedMs() const; + virtual void SetElapsedMs(const ULONGLONG ms); + + private: + ULONGLONG elapsed_ms_; +}; + +ULONGLONG MockTimer::GetElapsedMs() const { + return elapsed_ms_; +} +void MockTimer::SetElapsedMs(const ULONGLONG ms) { + elapsed_ms_ = ms; +} + +// This mock class mocks the stats uploader so we don't a) log false stats +// and b) send hits to the server every time we run the tests. +class MockStatsUploader : public stats_report::StatsUploader { + public: + MockStatsUploader() {} + ~MockStatsUploader() {} + virtual bool UploadMetrics(const char* extra_url_data, + const char* user_agent, + const char *content); +}; + +bool MockStatsUploader::UploadMetrics(const char* extra_url_data, + const char* user_agent, + const char *content) { + // Return true to indicate that they were uploaded successfully. + return true; +} + +// This subclass is here just to make the protected methods public, so +// the testing functions can call them. +class MockPluginLogging : public o3d::PluginLogging { + public: + using o3d::PluginLogging::UpdateLogging; + using o3d::PluginLogging::SetTimer; + MockPluginLogging() : o3d::PluginLogging(), + aggregate_metrics_success_(0), + upload_metrics_success_(false) {} + virtual bool ProcessMetrics(const bool exiting, const bool force_report); + virtual void DoAggregateMetrics(); + virtual bool DoAggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + const bool force_report); + void SetAggregateMetricsSuccess(const int value); + int AggregateMetricsSuccess(); + int UploadMetricsSuccess(); + private: + int aggregate_metrics_success_; + bool upload_metrics_success_; +}; + +bool MockPluginLogging::ProcessMetrics(const bool exiting, + const bool force_report) { + return true; +} + +void MockPluginLogging::DoAggregateMetrics() { + aggregate_metrics_success_ = 1; +} + +bool MockPluginLogging::DoAggregateAndReportMetrics( + const char* extra_url_arguments, + const char* user_agent, + const bool force_report) { + aggregate_metrics_success_ = 2; + MockStatsUploader stats_uploader; + upload_metrics_success_ = TestableAggregateAndReportMetrics( + extra_url_arguments, + user_agent, + force_report, + &stats_uploader); + return upload_metrics_success_; +} + +void MockPluginLogging::SetAggregateMetricsSuccess(const int value) { + aggregate_metrics_success_ = value; +} + +int MockPluginLogging::AggregateMetricsSuccess() { + return aggregate_metrics_success_; +} + +int MockPluginLogging::UploadMetricsSuccess() { + return upload_metrics_success_; +} + +// Test fixture for the PluginLogging tests. +class PluginLoggingTests : public testing::Test { + protected: + virtual void SetUp(); + virtual void TearDown(); + + MockPluginLogging* plugin_logging() { return plugin_logging_; } + void SetPluginLogging(MockPluginLogging* plugin_logging_in) { + plugin_logging_ = plugin_logging_in; + } + MockTimer* mock_timer() { return mock_timer_; } + + private: + MockPluginLogging* plugin_logging_; + MockTimer* mock_timer_; +}; + +void PluginLoggingTests::SetUp() { + HRESULT hr = CoInitialize(NULL); + mock_timer_ = new MockTimer(); + plugin_logging_ = new MockPluginLogging(); + plugin_logging_->SetTimer(mock_timer_); + stats_report::g_global_metrics.Initialize(); +} + +void PluginLoggingTests::TearDown() { + // Only unitialize if a plugin_logging_ exists. + // If it does not exist, it means we also never initialized. + if (plugin_logging_) { + delete plugin_logging_; + stats_report::g_global_metrics.Uninitialize(); + } +} + +// Test if the metric collection is properly initialized. +TEST_F(PluginLoggingTests, InitializeMetricCollection) { + EXPECT_TRUE(stats_report::g_global_metrics.initialized()); +} + +// Tests the PluginLogging's metric processing functions. +TEST_F(PluginLoggingTests, ProcessMetricsTests) { + EXPECT_FALSE(plugin_logging()->UpdateLogging()); + // NOTE: This time should be greater than the desired time. + mock_timer()->SetElapsedMs(5 * 60 * 1000); + EXPECT_TRUE(plugin_logging()->UpdateLogging()); + + // This time should be less than the interval. + mock_timer()->SetElapsedMs(1000); + EXPECT_FALSE(plugin_logging()->UpdateLogging()); +} + +// Tests that the proper method to aggregate metrics was called. +TEST_F(PluginLoggingTests, AggregateMetricsTests) { + // Start by initializing the variable which tells us success or not. + plugin_logging()->SetAggregateMetricsSuccess(0); + // Pass false for "exiting" parameter to call AggregateMetrics. + // Value does not matter for "force_report" for this testt so pass false. + EXPECT_TRUE(plugin_logging()->PluginLogging::ProcessMetrics(true, false)); + EXPECT_EQ(plugin_logging()->AggregateMetricsSuccess(), 1); + + plugin_logging()->SetAggregateMetricsSuccess(0); + // Pass true for "exiting" parameter to call AggregateAndReportMetrics. + // Value does not matter for "force_report" for this testt so pass false. + EXPECT_TRUE(plugin_logging()->PluginLogging::ProcessMetrics(false, false)); + EXPECT_EQ(plugin_logging()->AggregateMetricsSuccess(), 2); +} + +// Check that the force_report boolean forces reporting of the metrics. +TEST_F(PluginLoggingTests, CheckForceReport) { + // Using this call rather than just calling TestableAggregateAndReportMetrics + // directly because this is the stand alone call that we want to test. + // Pass false for "exiting" since reporting does not happen otherwise. + EXPECT_TRUE(plugin_logging()->PluginLogging::ProcessMetrics(false, true)); +} + +// Tests that when opt_in is turned on we create a logger and process metrics. +TEST_F(PluginLoggingTests, CheckOptIn) { + TearDown(); + SetPluginLogging( + o3d::PluginLogging::CreateUsageStatsLogger<MockPluginLogging>(true)); + EXPECT_TRUE(plugin_logging()); +} + +// Tests that when opt_in is turned OFF we do not create a logger. +TEST_F(PluginLoggingTests, CheckOptOut) { + TearDown(); + SetPluginLogging( + o3d::PluginLogging::CreateUsageStatsLogger<MockPluginLogging>(false)); + EXPECT_FALSE(plugin_logging()); +} diff --git a/o3d/plugin/cross/plugin_main.h b/o3d/plugin/cross/plugin_main.h new file mode 100644 index 0000000..e0af989 --- /dev/null +++ b/o3d/plugin/cross/plugin_main.h @@ -0,0 +1,38 @@ +/* + * 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 TOOLS_IDLGLUE_NG_O3D_GLUE_PLUGIN_MAIN_H__ +#define TOOLS_IDLGLUE_NG_O3D_GLUE_PLUGIN_MAIN_H__ + +#include "plugin/cross/o3d_glue.h" + +#endif // TOOLS_IDLGLUE_NG_O3D_GLUE_PLUGIN_MAIN_H__ diff --git a/o3d/plugin/cross/plugin_metrics.h b/o3d/plugin/cross/plugin_metrics.h new file mode 100644 index 0000000..97e9807 --- /dev/null +++ b/o3d/plugin/cross/plugin_metrics.h @@ -0,0 +1,152 @@ +/* + * 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. + */ + + +// Declares the system metrics used by the plugin. + +#ifndef O3D_PLUGIN_CROSS_PLUGIN_METRICS_H_ +#define O3D_PLUGIN_CROSS_PLUGIN_METRICS_H_ + +#include "statsreport/metrics.h" + +namespace o3d { +// NOTE: DO NOT REORDER THESE ENUMS! +// This enum will be used by stats analysis code as well and they must stay in +// sync. When this stat is reported, it only reports an integer that we compare +// against this enum so the order is very important. +// We start at 1, as 0 is reserved for "no value", ie we didn't check. +enum BrowserTypeName { + BROWSER_NAME_UNKNOWN = 1, + BROWSER_NAME_CHROME, + BROWSER_NAME_SAFARI, + BROWSER_NAME_FIREFOX, + BROWSER_NAME_MSIE, + BROWSER_NAME_OPERA, + BROWSER_NAME_CAMINO, + BROWSER_NAME_OMNIWEB +}; + +// This enum is simply for easy viewing of data +enum SystemType { + SYSTEM_NAME_WIN = 1, + SYSTEM_NAME_MAC, + SYSTEM_NAME_LINUX +}; + +// User operating system +DECLARE_METRIC_integer(system_type); + +DECLARE_METRIC_integer(windows_major_version); +DECLARE_METRIC_integer(windows_minor_version); +DECLARE_METRIC_integer(windows_sp_major_version); +DECLARE_METRIC_integer(windows_sp_minor_version); + +DECLARE_METRIC_integer(mac_major_version); +DECLARE_METRIC_integer(mac_minor_version); +DECLARE_METRIC_integer(mac_bugfix_version); + +DECLARE_METRIC_integer(gl_major_version); +DECLARE_METRIC_integer(gl_minor_version); +DECLARE_METRIC_integer(gl_hlsl_major_version); +DECLARE_METRIC_integer(gl_hlsl_minor_version); + + +DECLARE_METRIC_bool(POW2_texture_caps); +DECLARE_METRIC_bool(NONPOW2CONDITIONAL_texture_caps); + +// User GPU +DECLARE_METRIC_integer(gpu_vendor_id); +DECLARE_METRIC_integer(gpu_device_id); +DECLARE_METRIC_integer(gpu_driver_major_version); +DECLARE_METRIC_integer(gpu_driver_minor_version); +DECLARE_METRIC_integer(gpu_driver_bugfix_version); +DECLARE_METRIC_integer(gpu_vram_size); +DECLARE_METRIC_bool(direct3d_available); + + +// Shader versions +DECLARE_METRIC_integer(pixel_shader_main_version); +DECLARE_METRIC_integer(pixel_shader_sub_version); +DECLARE_METRIC_integer(vertex_shader_main_version); +DECLARE_METRIC_integer(vertex_shader_sub_version); + +// Browser +DECLARE_METRIC_integer(browser_type); +DECLARE_METRIC_integer(browser_major_version); +DECLARE_METRIC_integer(browser_minor_version); +DECLARE_METRIC_integer(browser_bugfix_version); + +// Running time for instance of plugin +DECLARE_METRIC_count(uptime_seconds); +DECLARE_METRIC_count(cpu_time_seconds); +DECLARE_METRIC_timing(running_time_seconds); + +// Crashes +// How many times the plugin has crashed. Not all crashes may be reported. +DECLARE_METRIC_count(crashes_total); +DECLARE_METRIC_count(crashes_uploaded); +DECLARE_METRIC_count(out_of_memory_total); + +// Bluescreens +// How many times has the plugin caused a bluescreen of death +DECLARE_METRIC_count(bluescreens_total); + +// D3D Caps +DECLARE_METRIC_integer(d3d_devcaps); +DECLARE_METRIC_integer(d3d_misccaps); +DECLARE_METRIC_integer(d3d_rastercaps); +DECLARE_METRIC_integer(d3d_zcmpcaps); +DECLARE_METRIC_integer(d3d_srcblendcaps); +DECLARE_METRIC_integer(d3d_dstblendcaps); +DECLARE_METRIC_integer(d3d_alphacaps); +DECLARE_METRIC_integer(d3d_texcaps); +DECLARE_METRIC_integer(d3d_texfiltercaps); +DECLARE_METRIC_integer(d3d_cubetexfiltercaps); +DECLARE_METRIC_integer(d3d_texaddrcaps); +DECLARE_METRIC_integer(d3d_linecaps); +DECLARE_METRIC_integer(d3d_stencilcaps); +DECLARE_METRIC_integer(d3d_texopcaps); +DECLARE_METRIC_integer(d3d_vs20caps); +DECLARE_METRIC_integer(d3d_vs20_dynflowctrldepth); +DECLARE_METRIC_integer(d3d_vs20_numtemps); +DECLARE_METRIC_integer(d3d_vs20_staticflowctrldepth); +DECLARE_METRIC_integer(d3d_ps20caps); +DECLARE_METRIC_integer(d3d_ps20_dynflowctrldepth); +DECLARE_METRIC_integer(d3d_ps20_numtemps); +DECLARE_METRIC_integer(d3d_ps20_staticflowctrldepth); +DECLARE_METRIC_integer(d3d_ps20_numinstrslots); + +// Installs and uninstalls +// TODO: Will likely need to get from Google Update + +} // namespace o3d + +#endif // O3D_PLUGIN_CROSS_PLUGIN_METRICS_H_ diff --git a/o3d/plugin/cross/stream_manager.cc b/o3d/plugin/cross/stream_manager.cc new file mode 100644 index 0000000..07f25a2 --- /dev/null +++ b/o3d/plugin/cross/stream_manager.cc @@ -0,0 +1,356 @@ +/* + * 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/cross/stream_manager.h" + +#include <algorithm> +#include <map> + +#include "base/logging.h" +#include "plugin/cross/o3d_glue.h" +#include "third_party/nixysa/files/static_glue/npapi/common.h" + +namespace glue { + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +StreamManager::StreamManager(NPP plugin_instance) + : plugin_instance_(plugin_instance) { +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +StreamManager::~StreamManager() { + // If the destructor gets called while streams are in mid-flight then delete + // them here to make sure we can garbage collect cleanly. + std::vector<NPDownloadStream *>::iterator it; + for (it = entries_.begin(); it < entries_.end(); ++it) { + delete (*it); + } + entries_.clear(); +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DownloadStream *StreamManager::LoadURL(const std::string &url, + NewStreamCallback *new_stream_callback, + WriteReadyCallback *write_ready_callback, + WriteCallback *write_callback, + FinishedCallback *finished_callback, + uint16 stream_type) { + DCHECK(finished_callback != NULL); + + NPDownloadStream *entry = new NPDownloadStream(url, + "", + stream_type, + plugin_instance_, + new_stream_callback, + write_ready_callback, + write_callback, + finished_callback); + + GLUE_PROFILE_START(plugin_instance_, "geturlnotify"); + // NPN_GetURLNotify may call-back into the plug-in before returning, so + // add the download stream entry before making the call. + entries_.push_back(entry); + NPError ret = NPN_GetURLNotify(plugin_instance_, url.c_str(), NULL, entry); + GLUE_PROFILE_STOP(plugin_instance_, "geturlnotify"); + if (ret != NPERR_NO_ERROR) { + // If the operation failed, it's possible that the browser hosting + // environment did not call the appropriate notify routines which + // clean up the entries_ stack. We check if the entry is still + // at the top, and delete it here. + if (!entries_.empty() && entries_.back() == entry) { + entries_.pop_back(); + // NOTE: Should we run the finished_callback here ? + delete entry; + entry = NULL; + } + } + return entry; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::CheckEntry(NPDownloadStream *entry) { + std::vector<NPDownloadStream *>::iterator it = + std::find(entries_.begin(), entries_.end(), entry); + return it != entries_.end(); +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::NewStream(NPStream *stream, uint16 *stype) { + NPDownloadStream *entry = static_cast<NPDownloadStream*>(stream->notifyData); + if (!CheckEntry(entry)) { + // We got a new stream, but we don't know about it. + return false; + } + return entry->NewStream(stream, stype); +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::DestroyStream(NPStream *stream, NPReason reason) { + NPDownloadStream *entry = static_cast<NPDownloadStream*>(stream->notifyData); + if (!CheckEntry(entry)) { + // We don't know about this stream. + return false; + } + DCHECK_EQ(stream, entry->GetStream()); + + return entry->DestroyStream(reason); +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::SetStreamFile(NPStream *stream, const char *filename) { + NPDownloadStream *entry = static_cast<NPDownloadStream*>(stream->notifyData); + if (!CheckEntry(entry)) { + // We don't know about this stream. + return false; + } + DCHECK_EQ(stream, entry->GetStream()); + + return entry->SetStreamFile(filename); +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::URLNotify(const char *url, + NPReason reason, + void *notifyData) { + NPDownloadStream *entry = static_cast<NPDownloadStream *>(notifyData); + if (!CheckEntry(entry)) { + // We don't know about this stream. + return false; + } + + std::vector<NPDownloadStream *>::iterator it = + std::find(entries_.begin(), entries_.end(), entry); + DCHECK(it != entries_.end()); + entries_.erase(it); + + bool result = entry->URLNotify(reason); + delete entry; + return result; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int32 StreamManager::WriteReady(NPStream *stream) { + NPDownloadStream *entry = static_cast<NPDownloadStream*>(stream->notifyData); + if (!CheckEntry(entry)) { + // We don't know about this stream. + return 0; + } + + return entry->WriteReady(); +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int32 StreamManager::Write(NPStream *stream, + int32 offset, + int32 len, + void *buffer) { + NPDownloadStream *entry = static_cast<NPDownloadStream*>(stream->notifyData); + if (!CheckEntry(entry)) { + // We don't know about this stream. + return 0; + } + + return entry->Write(offset, len, buffer); +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// DownloadStream implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +StreamManager::NPDownloadStream::~NPDownloadStream() { +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +std::string StreamManager::NPDownloadStream::GetURL() { + return url_; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +std::string StreamManager::NPDownloadStream::GetCachedFile() { + return file_; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DownloadStream::State StreamManager::NPDownloadStream::GetState() { + return state_; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int StreamManager::NPDownloadStream::GetReceivedByteCount() { + return bytes_received_; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +size_t StreamManager::NPDownloadStream::GetStreamLength() { + return stream_ ? stream_->end : 0; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void StreamManager::NPDownloadStream::Cancel() { + NPN_DestroyStream(plugin_instance_, stream_, NPRES_USER_BREAK); + state_ = STREAM_FINISHED; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// NPDownloadStream implementation +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +typedef std::map<std::string, std::string> StringMap; + +// Extracts headers from the browser-returned header string, as a name->value +// map. +static StringMap ExtractHeaders(const char *header_string) { + using std::string; + StringMap map; + if (!header_string) return map; + string headers = header_string; + // Doc says headers as returned by the browser are LF-terminated, including + // the last one. + // It's unclear if they are rewritten by the browser to be in a "canonical" + // form (i.e. single-line, no extra space etc.). We currently assume that + // they are. + // TODO: verify this, and/or implement correct parsing to handle RFC + // 1945/2616 parsing. + string::size_type index = 0; + while (index < headers.size()) { + string::size_type eol = std::min(headers.size(), headers.find("\n", index)); + string line = headers.substr(index, eol-index); + string::size_type separator = line.find(":"); + if (separator != string::npos) { + string key = line.substr(0, separator); + string value; + if (separator + 1 < line.size()) { + string::size_type value_index = + line.find_first_not_of(" \t", separator + 1); + if (value_index != string::npos) + value = line.substr(value_index); + } + map[key] = value; + } + index = eol + 1; + } + + return map; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::NPDownloadStream::NewStream(NPStream *new_stream, + uint16 *stype) { + stream_ = new_stream; + state_ = DownloadStream::STREAM_STARTED; + *stype = stream_type_; + + // callback if provided + if (new_stream_callback_.get()) { + new_stream_callback_->Run(this); + } + + return true; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::NPDownloadStream::DestroyStream(NPReason reason) { + stream_ = NULL; + state_ = DownloadStream::STREAM_FINISHED; + return true; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::NPDownloadStream::SetStreamFile(const char *filename) { + if (finished_callback_.get()) { + StringMap header_map = ExtractHeaders(stream_->headers); + std::string mime_type = header_map["Content-Type"]; + file_ = filename; + // On success, run the finished_callback. + if (file_.size() != 0) { + // finished_callback should only be called once. + finished_callback_->Run(this, true, file_, mime_type); + finished_callback_.reset(NULL); + } + } + + return true; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool StreamManager::NPDownloadStream::URLNotify(NPReason reason) { + if (finished_callback_.get()) { + // On failure, run the finished_callback. + // Note that the streaming case (NP_NORMAL) does not get a file + // so we can't check its size + if ((reason != NPRES_DONE) || + (stream_type_ != NP_NORMAL && file_.size() == 0)) { + // finished_callback should only be called once. + finished_callback_->Run(this, false, "", ""); + finished_callback_.reset(NULL); + } + + // Finished callback for streaming case + // where SetStreamFile() is not called + if (reason == NPRES_DONE && stream_type_ == NP_NORMAL) { + finished_callback_->Run(this, true, "", ""); + finished_callback_.reset(NULL); + } + } + + new_stream_callback_.reset(NULL); + write_ready_callback_.reset(NULL); + write_callback_.reset(NULL); + + return true; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int32 StreamManager::NPDownloadStream::WriteReady() { + if (write_ready_callback_.get()) { + return write_ready_callback_->Run(this); + } + + return 4096; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int32 StreamManager::NPDownloadStream::Write(int32 offset, + int32 len, + void *buffer) { + if (write_callback_.get()) { + int32 n = write_callback_->Run(this, offset, len, buffer); + bytes_received_ += n; + return n; + } + + return len; +} + +} // namespace glue diff --git a/o3d/plugin/cross/stream_manager.h b/o3d/plugin/cross/stream_manager.h new file mode 100644 index 0000000..f7c6723 --- /dev/null +++ b/o3d/plugin/cross/stream_manager.h @@ -0,0 +1,169 @@ +/* + * 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 EXPERIMENTAL_O3D_O3DPLUGIN_AUTOGEN_O3D_GLUE_STREAM_MANAGER_H_ +#define EXPERIMENTAL_O3D_O3DPLUGIN_AUTOGEN_O3D_GLUE_STREAM_MANAGER_H_ + +#include <npapi.h> +#include <string> +#include <vector> +#include "base/scoped_ptr.h" +#include "core/cross/callback.h" +#include "plugin/cross/download_stream.h" + +namespace glue { + +// Stream manager class, to help managing asynchronous loading of URLs into +// files. +class StreamManager { + public: + // TODO : get rid of these horrible callback objects + // and use an interface instead + typedef o3d::Callback1<DownloadStream*> NewStreamCallback; + + typedef o3d::Callback4<DownloadStream*, + bool, + const std::string &, + const std::string &> FinishedCallback; + + // The signature for these callbacks corresponds to the NPP_WriteReady() + // and NPP_Write() calls + typedef o3d::ResultCallback1<int32, DownloadStream*> WriteReadyCallback; + + typedef o3d::ResultCallback4<int32, + DownloadStream*, + int32, + int32, + void*> WriteCallback; + + explicit StreamManager(NPP plugin_instance); + ~StreamManager(); + + // Loads URL into a file, calls finished_callback->Run(success, filename) + // when done. + // returns a DownloadStream object (or NULL) if error + // filename is the name of the file where the contents of the URL are + // stored. + // LoadURL() takes ownership of new_stream_callback, write_ready_callback, + // write_callback, and callback: They will be deleted once the stream has + // completed. + DownloadStream *LoadURL(const std::string &url, + NewStreamCallback *new_stream_callback, + WriteReadyCallback *write_ready_callback, + WriteCallback *write_callback, + FinishedCallback *callback, + uint16 stream_type); + + // Manages the NPP_NewStream callback. + bool NewStream(NPStream *stream, uint16 *stype); + // Manages the NPP_DestroyStream callback. + bool DestroyStream(NPStream *stream, NPReason reason); + // Manages the NPP_StreamAsFile callback. + bool SetStreamFile(NPStream *stream, const char *filename); + // Manages the NPP_URLNotify callback. + bool URLNotify(const char *url, NPReason reason, void *notifyData); + + // Continuous streaming support + int32 WriteReady(NPStream *stream); + int32 Write(NPStream *stream, int32 offset, int32 len, void *buffer); + + private: + class NPDownloadStream : public DownloadStream { + public: + NPDownloadStream(const std::string &url, + const std::string &file, + uint16 stream_type, + NPP plugin_instance, + NewStreamCallback *new_stream_callback, + WriteReadyCallback *write_ready_callback, + WriteCallback *write_callback, + FinishedCallback *callback) + : url_(url), + file_(file), + stream_type_(stream_type), + plugin_instance_(plugin_instance), + stream_(NULL), + new_stream_callback_(new_stream_callback), + write_ready_callback_(write_ready_callback), + write_callback_(write_callback), + finished_callback_(callback), + bytes_received_(0), + state_(STREAM_REQUESTED) {} + + virtual ~NPDownloadStream(); + + // DownloadStream interface + virtual std::string GetURL(); + virtual std::string GetCachedFile(); + virtual State GetState(); + virtual int GetReceivedByteCount(); + virtual size_t GetStreamLength(); + virtual void Cancel(); + + // NPAPI stream stuff + NPStream *GetStream() const { return stream_; } + bool NewStream(NPStream *new_stream, uint16 *stype); + bool DestroyStream(NPReason reason); + bool SetStreamFile(const char *filename); + bool URLNotify(NPReason reason); + int32 WriteReady(); + int32 Write(int32 offset, int32 len, void *buffer); + + + private: + std::string url_; + std::string file_; + + // stream type (as file or continuous stream) + uint16 stream_type_; + + NPP plugin_instance_; + NPStream *stream_; + + // callbacks + scoped_ptr<NewStreamCallback> new_stream_callback_; + scoped_ptr<WriteReadyCallback> write_ready_callback_; + scoped_ptr<WriteCallback> write_callback_; + scoped_ptr<FinishedCallback> finished_callback_; + + int bytes_received_; + State state_; + }; + bool CheckEntry(NPDownloadStream *entry); + + NPP plugin_instance_; + std::vector<NPDownloadStream *> entries_; +}; + +} // namespace glue + +#endif // EXPERIMENTAL_O3D_O3DPLUGIN_AUTOGEN_O3D_GLUE_STREAM_MANAGER_H_ diff --git a/o3d/plugin/idl/archive_request.idl b/o3d/plugin/idl/archive_request.idl new file mode 100644 index 0000000..1633a46 --- /dev/null +++ b/o3d/plugin/idl/archive_request.idl @@ -0,0 +1,170 @@ +/* + * 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. + */ + +namespace o3d { + +[include="import/cross/archive_request.h"] +callback void ArchiveRequestCallback(); + +%[ + An ArchiveRequest object is used to carry out an asynchronous request for a + compressed archive (containing multiple files). + + Note: The archive must have as its first file a file named 'aaaaaaaa.o3d' + who's contents is 'o3d'. This is to prevent O3D being used to open + archive files that were not meant for it. + + \code + var request = pack.createArchiveRequest(); + request.open("GET", url); + + request.onfileavailable = myFileAvailableCallback; + request.onreadystatechange = myReadyStateChangeCallback; + request.send(); + + function myFileAvailableCallback() { + dump("uri: " + request.data.uri + "\n"); + dump("content: " + request.data.stringValue + "\n"); + + // You can pass a RawData to various creation functions. Note: request.data + // is only valid during an onfileavailable callback. + // Examples: + if (request.data.uri == 'mytexture.jpg') + pack.createTexture2d(request.data, makeMips); + if (request.data.uri == 'myvertices.bin') + vertexBuffer.set(request.data); + if (request.data.uri == 'myAudio.mp3') + audioSystem.createSound(request.data); + } + + function myReadyStateChangeCallback() { + if (request.done) { + if (request.success) { + // there were no errors trying to read the archive + } else { + dump(request.error); + } + } + } + + // When you are done with the RawDatas loaded by the request, remove + // the request from the pack to free them. + pack.removeObject(request); + \endcode +%] + +[nocpp, include="import/cross/archive_request.h"] class ArchiveRequest + : ObjectBase { + %[ + A callback that gets called each time readyState changes. + %] + [setter] + ArchiveRequestCallback? onreadystatechange; + + %[ + A callback that gets called each time a file fully downloads and becomes + available. + %] + [setter] + ArchiveRequestCallback? onfileavailable; + + %[ + The uri of the archive being downloaded. + %] + [getter] String uri; + + %[ + A RawData object representing the file that is currently available. + Note: This value is only valid inside the onfileavailable callback. + %] + [getter] RawData? data; + + %[ + The length of the entire archive in bytes. + + Use this value along with bytesReceived to figure out the download progress. + %] + [getter] int streamLength; + + %[ + The number of bytes downloaded so far. + + You can use this value along with streamLength to figure out the download + progress. + %] + [getter] int bytesReceived; + + %[ + Holds the same values as in XMLHttpRequest: + \li 0 = uninitialized + \li 1 = opened + \li 2 = sent + \li 3 = receiving + \li 4 = loaded (the file has been downloaded, but may or may not have been + parsed yet) + %] + [getter] int readyState; + + %[ + Indicates whether processing for this FileRequest has finished. + %] + [getter] bool done; + + %[ + This field is only valid if done is true. It indicates whether or not the + request succeeded. If false see error for an error message. + %] + [getter] bool success; + + %[ + An error message. + If done is true and success is false this will be an error message + describing what went wrong. + %] + [getter] String error; + + %[ + Sets up several of the request fields. + \param method "GET" is the only supported method at this time + \param uri the location of the file to fetch + %] + [nocpp, userglue, plugin_data] void open( + String method, String uri); + + %[ + Send the request. + Unlike XMLHttpRequest the onreadystatechange callback will be called no + matter what, with success or failure. + %] + [nocpp, userglue, plugin_data] void send(); +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/bounding_box.idl b/o3d/plugin/idl/bounding_box.idl new file mode 100644 index 0000000..b8ac932 --- /dev/null +++ b/o3d/plugin/idl/bounding_box.idl @@ -0,0 +1,210 @@ +/* + * 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. + */ + +// TODO: some how make this file conditionally compile different for C++ +// vs Javascript like with #ifdef CPLUSPLUS etc.. +namespace o3d { + +%[ + A BoundingBox represents an Axis Aligned Bounding Box. +%] +[binding_model=by_value, marshaled, nocpp, include="core/cross/bounding_box.h"] +class BoundingBox { + %[ + Creates BoundingBox from min_extent and max_extent + \param min_extent minimum extent of the box. + \param max_extent maximum extent of the box. + %] + BoundingBox(Vectormath::Aos::Point3 min_extent, + Vectormath::Aos::Point3 max_extent); + + %[ + Assigns a JavaScript array of arrays of numbers into a BoundingBox. + This property is implicitly invoked whenever such an array is assigned to + a BoundingBox property or such an array is passed as a BoundingBox method + parameter. + %] + [plugin_data, userglue, userglue_setter, setter, nodocs] + float[][] marshaled; + + %[ + True if this boundingbox has been initialized. + %] + [getter] bool valid_; + + %[ + The min extent of the box. + %] + [getter] Vectormath::Aos::Point3 min_extent; + + %[ + The max extent of the box. + %] + [getter] Vectormath::Aos::Point3 max_extent; + + %[ + Multiplies the bounding box by the given matrix returning a new bounding + box. + \param matrix The matrix to multiply by. + \returns The new bounding box. + %] + [const, userglue] BoundingBox Mul(Vectormath::Aos::Matrix4 matrix); + + %[ + Adds a bounding box to this bounding box returning a bounding box that + encompases both. + \return The new bounding box. + %] + [const, userglue] BoundingBox Add(BoundingBox box); + + %[ + Checks if a ray defined in same coordinate system as this box intersects + this bounding box. + \param start position of start of ray in local space. + \param end position of end of ray in local space. + \return RayIntersectionInfo. If result.value is false then something was + wrong like using this function with an uninitialized bounding box. If + result.intersected is true then the ray intersected the box + and result.position is the exact point of intersection. + %] + [const, userglue] + o3d::RayIntersectionInfo IntersectRay(Vectormath::Aos::Point3 start, + Vectormath::Aos::Point3 end); + + %[ + Checks if a ray defined in same coordinate system as this box intersects + this bounding box. + \param startX The x coordinate of start of ray in local space. + \param startY The y coordinate of start of ray in local space. + \param startZ The z coordinate of start of ray in local space. + \param endX The x coordinate of end of ray in local space. + \param endY The y coordinate of end of ray in local space. + \param endZ The z coordinate of end of ray in local space. + \return RayIntersectionInfo. If result.value is false then something was + wrong like using this function with an uninitialized bounding box. If + result.intersected is true then the ray intersected the box + and result.position is the exact point of intersection. + %] + [const, userglue] + o3d::RayIntersectionInfo IntersectRay(float startX, + float startY, + float startZ, + float endX, + float endY, + float endZ); + + %[ + Returns true if the bounding box is inside the frustum. + \param matrix Matrix to transform the box from its local space to view + frustum space. + \return True if the box is in the frustum. + %] + [const] bool InFrustum(Vectormath::Aos::Matrix4 matrix); + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + o3d::BoundingBox* _this, + const std::vector<std::vector<float> >& values) { + if (values.size() == 0) { + *_this = o3d::BoundingBox(); + } else if (values.size() != 2) { + o3d::ServiceLocator* service_locator = + static_cast<glue::_o3d::PluginObject*>( + plugin_data)->service_locator(); + O3D_ERROR(service_locator) + << "BoundingBox: expected 2 values, got " << values.size(); + } else { + for (int i = 0; i < values.size(); ++i) { + if (values[i].size() != 3) { + o3d::ServiceLocator* service_locator = + static_cast<glue::_o3d::PluginObject*>( + plugin_data)->service_locator(); + O3D_ERROR(service_locator) + << "BoundingBox: expected 3 values, got " << values.size(); + return; + } + } + *_this = o3d::BoundingBox( + Vectormath::Aos::Point3(values[0][0], values[0][1], values[0][2]), + Vectormath::Aos::Point3(values[1][0], values[1][1], values[1][2])); + } + } + o3d::BoundingBox userglue_method_Mul( + o3d::BoundingBox* self, + const Vectormath::Aos::Matrix4 &matrix) { + o3d::BoundingBox bounding_box; + self->Mul(matrix, &bounding_box); + return bounding_box; + } + o3d::BoundingBox userglue_method_Add( + o3d::BoundingBox* self, + const o3d::BoundingBox& box) { + o3d::BoundingBox result; + self->Add(box, &result); + return result; + } + o3d::RayIntersectionInfo userglue_method_IntersectRay( + o3d::BoundingBox* self, + const Vectormath::Aos::Point3& start, + const Vectormath::Aos::Point3& end) { + o3d::RayIntersectionInfo ray_intersection_info; + self->IntersectRay(start, end, &ray_intersection_info); + return ray_intersection_info; + } + o3d::RayIntersectionInfo userglue_method_IntersectRay( + o3d::BoundingBox* self, + float startX, + float startY, + float startZ, + float endX, + float endY, + float endZ) { + o3d::RayIntersectionInfo ray_intersection_info; + self->IntersectRay(Vectormath::Aos::Point3(startX, startY, startZ), + Vectormath::Aos::Point3(endX, endY, endZ), + &ray_intersection_info); + return ray_intersection_info; + } + %} +}; // BoundingBox + +%[ + A Param which stores a BoundingBox. +%] +[nocpp, include="core/cross/param.h"] class ParamBoundingBox : Param { + %[ + The BoundingBox stored by the Param. + %] + [getter, setter] BoundingBox value_; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/buffer.idl b/o3d/plugin/idl/buffer.idl new file mode 100644 index 0000000..0124c7b --- /dev/null +++ b/o3d/plugin/idl/buffer.idl @@ -0,0 +1,482 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + The Buffer object is a low level container for a flat list of + floating point or integer values. These are currently used to define + geometry. +%] +[nocpp, include="core/cross/buffer.h"] +class Buffer : NamedObject { + %[ + Allocates memory for the data to be stored in the buffer based on + the types of fields set on the buffer. + + \param num_elements Number of elements to allocate.. + \return True if operation was successful. + %] + [userglue] bool AllocateElements(unsigned int num_elements); + + %[ + Defines a field on this buffer. + + Note: Creating a field after having allocated the buffer is an expensive + operation as the data currently in the buffer has to be shuffled around + to make room for the new field. + + \param field_type type of data in the field. Valid types are "FloatField", + "UInt32Field", "UByteNField". + \param num_components number of components in the field. + %] + [userglue] + Field? CreateField(String field_type, unsigned int num_components); + + %[ + Removes a field from this buffer. + + Note: Removing a field after having allocated the buffer is an expensive + operation as the data currently in the buffer has to be shuffled around + to remove the old field. + + \param field field to remove. + %] + void RemoveField(Field field); + + %[ + Sets the values in the buffer given a RawData object. + + \param raw_data contains data to assign to the Buffer data itself. + %] + bool Set(RawData raw_data); + + %[ + Sets the values in the buffer given a RawData object. + + \param raw_data contains data to assign to the Buffer data itself. + \param offset is a byte offset from the start of raw_data + \param length is the byte length of the data to set + \return True if operation was successful. + %] + bool Set(RawData raw_data, + size_t offset, + size_t length); + + %[ + Number of elements in the buffer. + %] + [getter] unsigned int num_elements; + + %[ + The total components in all fields in this buffer. + %] + [getter] unsigned int total_components; + + %[ + The fields currently set on the buffer. + %] + [userglue_getter, getter] FieldArray fields; + + [verbatim=cpp_glue] %{ + o3d::Field* userglue_method_CreateField( + o3d::Buffer* buffer, + const o3d::String& field_type, + unsigned int num_components) { + return buffer->CreateFieldByClassName(field_type, num_components); + } + o3d::FieldArray userglue_getter_fields(o3d::Buffer* buffer) { + const o3d::FieldRefArray buffer_fields = buffer->fields(); + o3d::FieldArray fields(buffer_fields.size()); + for (size_t ii = 0; ii < buffer_fields.size(); ++ii) { + fields[ii] = buffer_fields[ii].Get(); + } + return fields; + } + bool userglue_method_AllocateElements(o3d::Buffer* buffer, + unsigned int num_elements) { + bool result = buffer->AllocateElements(num_elements); + if (result) { + // Clear the buffer so at least from Javascript we can't get garbage. + o3d::BufferLockHelper locker(buffer); + void* data = locker.GetData(o3d::Buffer::WRITE_ONLY); + if (!data) { + O3D_ERROR(buffer->service_locator()) + << "could not lock buffer"; + } else { + memset(data, 0, buffer->GetSizeInBytes()); + } + } + return result; + } + %} +}; + +%[ + VertexBufferBase is a the base class for both VertexBuffer and SourceBuffer + + \sa VertexBuffer + \sa SourceBuffer +%] +[nocpp, include="core/cross/buffer.h"] +class VertexBufferBase : Buffer { + %[ + Gets a copy of the values of the data stored in the buffer. + Modifying this copy has no effect on the buffer. + + \return An array of values. + %] + [nocpp, userglue] float[] Get(); + + %[ + Gets a copy of a sub range of the values in the data stored in the buffer. + Modifying this copy has no effect on the buffer. + + \param start_index index of the element value to get. + \param num_elements the number of elements to get. + \return An array of values. + %] + [nocpp, userglue] + float[] GetAt(unsigned int start_index, unsigned int num_elements); + + %[ + Sets the values of the data stored in the buffer. + The number of values passed in must be a multiple of the number of + components needed for the fields defined on this buffer. + + \param values Values to be stored in the buffer. + \return True if operation was successful. + %] + [nocpp, userglue] bool Set(float[] values); + + %[ + Sets the values of the data stored in the buffer. The buffer must have + already been created either through buffer.set or buffer.allocateElements + + The number of values passed in must be a multiple of the number of + components needed for the fields defined on this buffer. + + \param start_index index of first value to set. + \param values Values to be stored in the buffer starting at index. + \return True if operation was successful. + %] + [nocpp, userglue] void SetAt(unsigned int start_index, float[] values); + + [verbatim=cpp_glue] %{ + std::vector<float> userglue_method_Get(o3d::VertexBufferBase *buffer) { + std::vector<float> retval; + o3d::BufferLockHelper helper(buffer); + float* buffer_data = helper.GetDataAs<float>( + o3d::Buffer::READ_ONLY); + if (!buffer_data) { + O3D_ERROR(buffer->service_locator()) + << "could not lock buffer"; + } else { + retval.resize(buffer->total_components() * buffer->num_elements()); + unsigned element_offset = 0; + // for each field, copy the stuff into the array. + const o3d::FieldRefArray& fields = buffer->fields(); + for (unsigned ff = 0; ff < fields.size(); ++ff) { + o3d::Field* field = fields[ff]; + field->GetAsFloats(0, + &retval[0] + element_offset, + field->num_components(), + buffer->num_elements()); + element_offset += field->num_components(); + } + } + return retval; + } + std::vector<float> userglue_method_GetAt(o3d::VertexBufferBase *buffer, + unsigned int start_index, + unsigned int length) { + std::vector<float> retval; + if (start_index + length > buffer->num_elements() || + start_index + length < start_index) { + O3D_ERROR(buffer->service_locator()) + << "number of requested values would run past end of buffer"; + } else { + o3d::BufferLockHelper helper(buffer); + float* buffer_data = helper.GetDataAs<float>( + o3d::Buffer::READ_ONLY); + if (!buffer_data) { + O3D_ERROR(buffer->service_locator()) + << "could not lock buffer"; + } else { + retval.resize(length * buffer->total_components()); + unsigned element_offset = 0; + // for each field, copy the stuff into the array. + const o3d::FieldRefArray& fields = buffer->fields(); + for (unsigned ff = 0; ff < fields.size(); ++ff) { + o3d::Field* field = fields[ff]; + field->GetAsFloats(start_index, + &retval[0] + element_offset, + field->num_components(), + length); + element_offset += field->num_components(); + } + } + } + return retval; + } + bool userglue_method_Set(o3d::VertexBufferBase *buffer, + const std::vector<float> &values) { + unsigned int total_components = buffer->total_components(); + size_t size = values.size(); + if (total_components == 0) { + O3D_ERROR(buffer->service_locator()) + << "no fields are defined on the buffer"; + return false; + } + if (size % total_components != 0) { + O3D_ERROR(buffer->service_locator()) + << "the number of values passed in is not a multiple of the number" + << " of components in the fields on the buffer."; + return false; + } + + unsigned num_elements = size / total_components; + + if (!buffer->AllocateElements(num_elements)) { + return false; + } + + o3d::BufferLockHelper helper(buffer); + void* buffer_data = helper.GetData(o3d::Buffer::WRITE_ONLY); + if (!buffer_data) { + O3D_ERROR(buffer->service_locator()) + << "could not lock buffer"; + return false; + } + + unsigned element_offset = 0; + // for each field, copy the stuff out of the array. + const o3d::FieldRefArray& fields = buffer->fields(); + for (unsigned ff = 0; ff < fields.size(); ++ff) { + o3d::Field* field = fields[ff]; + field->SetFromFloats(&values[element_offset], + total_components, + 0, + num_elements); + element_offset += field->num_components(); + } + return true; + } + void userglue_method_SetAt(o3d::VertexBufferBase *buffer, + unsigned int start_index, + const std::vector<float> &values) { + unsigned int total_components = buffer->total_components(); + size_t size = values.size(); + if (total_components == 0) { + O3D_ERROR(buffer->service_locator()) + << "no fields are defined on the buffer"; + return; + } + if (size % total_components != 0) { + O3D_ERROR(buffer->service_locator()) + << "the number of values passed in is not a multiple of the number" + << " of components in the fields on the buffer."; + return; + } + + unsigned num_elements_to_set = size / total_components; + unsigned last_element = start_index + num_elements_to_set; + if (last_element > buffer->num_elements() || + last_element < start_index) { + O3D_ERROR(buffer->service_locator()) + << "Attempt to set elements outside of Buffer"; + return; + } + + o3d::BufferLockHelper helper(buffer); + void* buffer_data = helper.GetData(o3d::Buffer::WRITE_ONLY); + if (!buffer_data) { + O3D_ERROR(buffer->service_locator()) + << "could not lock buffer"; + return; + } + + unsigned element_offset = 0; + // for each field, copy the stuff out of the array. + const o3d::FieldRefArray& fields = buffer->fields(); + for (unsigned ff = 0; ff < fields.size(); ++ff) { + o3d::Field* field = fields[ff]; + field->SetFromFloats(&values[element_offset], + total_components, + start_index, + num_elements_to_set); + element_offset += field->num_components(); + } + } + %} +}; + +%[ + VertexBuffer is a Buffer object used for storing vertex data for geometry. + (e.g. vertex positions, normals, colors, etc). + A VertexBuffer can be rendered directly by the GPU. + + \sa SourceBuffer +%] +[nocpp, include="core/cross/buffer.h"] +class VertexBuffer : VertexBufferBase { +}; + +%[ + SourceBuffer is a Buffer object used for storing vertex data for + geometry. (e.g. vertex positions, normals, colors, etc). + + A SourceBuffer is the source for operations like skinning and morph + targets. It can not be directly rendered by the GPU. + + \sa VertexBuffer +%] +[nocpp, include="core/cross/buffer.h"] +class SourceBuffer : VertexBufferBase { +}; + +%[ + IndexBuffer is a buffer object used for storing geometry index data (e.g. + triangle indices). +%] +[nocpp, include="core/cross/buffer.h"] class IndexBuffer : Buffer { + %[ + Sets the values of the data stored in the buffer. + + \param values Values to be stored in the buffer. + \return True if operation was successful. + %] + [nocpp, userglue] bool Set(unsigned int[] values); + + %[ + Sets the values of the data stored in the buffer. The buffer must have + already been created either through buffer.set or buffer.allocateElements. + + \param start_index index of first value to set. + \param values Values to be stored in the buffer starting at index. + \return True if operation was successful. + %] + [nocpp, userglue] void SetAt(unsigned int start_index, unsigned int[] values); + + [verbatim=cpp_glue] %{ + bool userglue_method_Set(o3d::IndexBuffer *buffer, + const std::vector<unsigned int> &values) { + unsigned int total_components = buffer->total_components(); + size_t size = values.size(); + if (total_components == 0) { + O3D_ERROR(buffer->service_locator()) + << "no fields are defined on the buffer"; + return false; + } + if (size % total_components != 0) { + O3D_ERROR(buffer->service_locator()) + << "the number of values passed in is not a multiple of the number" + << " of components in the fields on the buffer."; + return false; + } + + unsigned num_elements = size / total_components; + + if (!buffer->AllocateElements(num_elements)) { + return false; + } + + o3d::BufferLockHelper helper(buffer); + void* buffer_data = helper.GetData(o3d::Buffer::WRITE_ONLY); + if (!buffer_data) { + O3D_ERROR(buffer->service_locator()) + << "could not lock buffer"; + return false; + } + + unsigned element_offset = 0; + // for each field, copy the stuff out of the array. + const o3d::FieldRefArray& fields = buffer->fields(); + for (unsigned ff = 0; ff < fields.size(); ++ff) { + o3d::Field* field = fields[ff]; + field->SetFromUInt32s(&values[element_offset], + total_components, + 0, + num_elements); + element_offset += field->num_components(); + } + return true; + } + void userglue_method_SetAt(o3d::IndexBuffer *buffer, + unsigned int start_index, + const std::vector<unsigned int> &values) { + unsigned int total_components = buffer->total_components(); + size_t size = values.size(); + if (total_components == 0) { + O3D_ERROR(buffer->service_locator()) + << "no fields are defined on the buffer"; + return; + } + if (size % total_components != 0) { + O3D_ERROR(buffer->service_locator()) + << "the number of values passed in is not a multiple of the number" + << " of components in the fields on the buffer."; + return; + } + + unsigned num_elements_to_set = size / total_components; + unsigned last_element = start_index + num_elements_to_set; + if (last_element > buffer->num_elements() || + last_element < start_index) { + O3D_ERROR(buffer->service_locator()) + << "Attempt to set elements outside of Buffer"; + return; + } + + o3d::BufferLockHelper helper(buffer); + void* buffer_data = helper.GetData(o3d::Buffer::WRITE_ONLY); + if (!buffer_data) { + O3D_ERROR(buffer->service_locator()) + << "could not lock buffer"; + return; + } + + unsigned element_offset = 0; + // for each field, copy the stuff out of the array. + const o3d::FieldRefArray& fields = buffer->fields(); + for (unsigned ff = 0; ff < fields.size(); ++ff) { + o3d::Field* field = fields[ff]; + field->SetFromUInt32s(&values[element_offset], + total_components, + start_index, + num_elements_to_set); + element_offset += field->num_components(); + } + } + %} +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/canvas.idl b/o3d/plugin/idl/canvas.idl new file mode 100644 index 0000000..013349a --- /dev/null +++ b/o3d/plugin/idl/canvas.idl @@ -0,0 +1,181 @@ +/* + * 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. + */ + + +namespace o3d { + +%[ +Canvas provides an interface for drawing text and 2D primitives on a 2D surface. +The contents of the canvas surface can be transfered to a compatible Texture2D +object via the copyToTexture() method. Each Canvas object maintains +a stack of 2D transformation matrices which allow fine control over +the placement of drawable elements. Both geometry and drawing coordinates +provided to every draw call are transformed by the concatenation of +all matrices in the stack. +%] + +[nocpp, include="core/cross/canvas.h"] +class Canvas : ParamObject { + %[ + Sets the size of the bitmap area that the Canvas uses. + \param width The width of the bitmap. + \param height The height of the bitmap. + \returns true if the Canvas surface was allocated successfully. + %] + bool SetSize(int width, int height); + + %[ + Clears the bitmap's pixels with the specified color. + \param color The color to clear the bitmap with, provided as an array of + four values [red, green, blue, alpha]. All values should be between 0.0 + and 1.0. For alpha values 0.0 is transparent and 1.0 opaque. + %] + [userglue] void Clear(Float4 color); + + %[ + Draws the specified rectangle using the specified paint. The rectangle will + be filled based on the color and shader in the paint. + \param left The left side of the rectangle to be drawn + \param top The top side of the rectangle to be drawn + \param right The right side of the rectangle to be drawn + \param bottom The bottom side of the rectangle to be drawn + \param paint The paint used to draw the rectangle + %] + void DrawRect(float left, + float top, + float right, + float bottom, + CanvasPaint paint); + + %[ + Draws the text, with origin at (x,y), using the specified paint. The origin + is interpreted based on the textAlign property in the paint. + \param text String of text to be drawn + \param x The x coordinate for the text origin + \param y The y coordinate for the text origin + \param paint The CanvasPaint object that specifies the text style, size, etc + %] + void DrawText(String text, float x, float y, CanvasPaint paint); + + %[ + Draws the text with its baseline along the + specified path. The paint's textAlign property determines where along the + path to start the text. The path must contain at least two positions. + \param text String of text to be drawn + \param positions An array of x,y positions making up the path. + \param horizontalOffset The distance along the path to add to the text + starting position. + \param verticalOffset The distance above(-) or below(+) the path to position + the text. + \param paint The CanvasPaint object that specifies the text style, size, + etc. + %] + void DrawTextOnPath(String text, + Float2[] positions, + float horizontalOffset, + float verticalOffset, + CanvasPaint paint); + + %[ + Draws the contents of the specified texture onto the canvas surface. + The bottom left corner of the bitmap will be at (x, y) and transformed by + the current matrix. Only ARGB8 and XRGB8 textures are supported. For XRG8 + textures, Alpha is assumed to be 1 (opaque). + \param texture The Texture2D object where the bitmap is extracted from. + \param left The position of the left side of the bitmap. + \param bottom The position of the bottom side of the bitmap. + %] + void DrawBitmap(Texture2D texture, float left, float bottom); + + %[ + This call saves the current matrix and pushes a + copy onto a private stack. Subsequent calls to translate, scale, + rotate all operate on this copy. + When the balancing call to restoreMatrix() is made, this copy is deleted and + the previous matrix is restored. + %] + void SaveMatrix(); + + %[ + Balances a call to saveMatrix(), and removes modifications to matrix + since the last save call. It is an error to call this more than + previous calls to saveMatrix(). + %] + void RestoreMatrix(); + + %[ + Adds a rotation to the current canvas matrix. + \param degrees The amount to rotate, in degrees + %] + void Rotate(float degrees); + + + %[ + Adds a scale to the current canvas matrix. + \param sx The amount to scale in x + \param sy The amount to scale in y + %] + void Scale(float sx, float sy); + + %[ + Adds a translation to the current canvas matrix. + \param dx The amount to translate in x + \param dy The amount to translate in y + %] + void Translate(float dx, float dy); + + %[ + Copies the contents of the Canvas to a 2D texture object. The size of the + texture must match exactly the size of the Canvas set by the setSize() method. + The format of the texture must be set to either ARGB8 or XRGB8. + %] + void CopyToTexture(Texture2D texture); + + %[ + The width of the bitmap used by the Canvas (read only). + %] + [getter] int width_; + + %[ + The height of the bitmap used by the Canvas (read only). + %] + [getter] int height_; + + [verbatim=cpp_glue] %{ + void userglue_method_Clear(o3d::Canvas* canvas, + const o3d::Float4& color) { + canvas->Clear(color[0], color[1], color[2], color[3]); + } + %} + +}; // Canvas + +} // namespace o3d diff --git a/o3d/plugin/idl/canvas_paint.idl b/o3d/plugin/idl/canvas_paint.idl new file mode 100644 index 0000000..94c3812 --- /dev/null +++ b/o3d/plugin/idl/canvas_paint.idl @@ -0,0 +1,186 @@ +/* + * 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. + */ + + +namespace o3d { + +%[ + CanvasFontMetrics is used to return values, measured in pixels, describing + the properties of a font used by the CanvasPaint objects. All the properties + of CanvasFontMetrics are read-only. +%] + +[binding_model=by_value, nocpp, include="core/cross/canvas_paint.h"] +class CanvasFontMetrics { + %[ + The greatest distance above the baseline for any glyph (will be <= 0) + %] + [getter] float top_; + + %[ + The recommended distance above the baseline (will be <= 0) + %] + [getter] float ascent_; + + %[ + The recommended distance below the baseline (will be >= 0) + %] + [getter] float descent_; + + %[ + The greatest distance below the baseline for any glyph (will be >= 0) + %] + [getter] float bottom_; + + %[ + The recommended distance to add between lines of text (will be >= 0) + %] + [getter] float leading_; +}; // CanvasFontMetrics + + +%[ + The CanvasPaint class is used for specifying how to draw objects and text to + a canvas. +%] + +[nocpp, include="core/cross/canvas_paint.h"] +class CanvasPaint : ParamObject { + %[ + \var Style + \li NORMAL, + \li BOLD, + \li ITALIC, + \li BOLD_ITALIC + Text styles + %] + enum Style { + NORMAL, + BOLD, + ITALIC, + BOLD_ITALIC + }; + + %[ + \var TextAlign + \li LEFT, + \li CENTER, + \li RIGHT, + Text alignment options + %] + enum TextAlign { + LEFT, + CENTER, + RIGHT + }; + + %[ + Sets the color and radius of an outline around the text. Setting the + radius to 0 cancels the outline effect. The outline and shadow effects are + mutually exclusive. + \param radius Distance outward from object to draw the background + \param color Color of the outline + %] + void SetOutline(float radius, Float4 color); + + %[ + Create a blur shadow effect on this paint. Setting the radius to 0 cancels + the shadow effect. + \param radius radius to blur the paint + \param offset_y offset of the blur in X + \param offset_x offset of the blur in Y + \param color color for the blur + %] + void SetShadow(float radius, + float offset_x, + float offset_y, + Float4 color); + + %[ + Returns metrics describing the font currently set on this paint object. + %] + CanvasFontMetrics GetFontMetrics(); + + %[ + Returns the bounds of the given text string when rendered with this paint. + The bounds are returned as an array containing [left, top, right, bottom] + values relative to (0, 0). + \param text The string of text to be measured. + %] + Float4 MeasureText(String text); + + %[ + The color used for all the draw operations using this paint. + %] + [getter, setter] Float4 color; + + %[ + The size of the font used for drawing text. + %] + [getter, setter] float text_size; + + %[ + The font typeface used for drawing text. Passing an empty string will + revert to the default font. + %] + [getter, setter] String text_typeface; + + %[ + The style applied to the text (e.g. italic, bold, etc) + %] + [getter, setter] Style text_style; + + %[ + The alignment mode used for drawing text. + %] + [getter, setter] TextAlign text_align; + + %[ + The 2D shader used by this paint. Set to null to stop using a shader. + %] + [getter, setter] CanvasShader? shader; + + %[ + Metrics of the current font used by the paint object. + %] + [userglue_getter, getter] CanvasFontMetrics font_metrics; + + [verbatim=cpp_glue] %{ + o3d::CanvasFontMetrics userglue_getter_font_metrics( + o3d::CanvasPaint* self) { + return self->GetFontMetrics(); + } + %} + +}; // CanvasPaint + +} // namespace o3d + diff --git a/o3d/plugin/idl/canvas_shader.idl b/o3d/plugin/idl/canvas_shader.idl new file mode 100644 index 0000000..252836b --- /dev/null +++ b/o3d/plugin/idl/canvas_shader.idl @@ -0,0 +1,94 @@ +/* + * 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. + */ + + +namespace o3d { + +%[ +CanvasShader is the base class of 2D gradient shaders that can be applied to a +CanvasPaint. When a shader is assigned to a CanvasPaint object, all subsequent +drawing (text and objects) will use the shader pattern as a fill material. +%] + +[nocpp, include="core/cross/canvas_shader.h"] +class CanvasShader : ParamObject { + %[ + \var TileMode + \li CLAMP copy the edge color if the shader draws outside of its bounds + \li REPEAT repeat horizontally and vertically outside its bounds + \li MIRROR same as above, alternating mirror images + %] + enum TileMode { + CLAMP, /* copy the edge color if the shader draws outside of its bounds */ + REPEAT, /* repeat horizontally and vertically outside its bounds */ + MIRROR /* same, alternating mirror images */ + }; + +}; // class CanvasShader + +%[ +A shader that generates a linear gradient between two specified points. Two or +more colors need to be specified for the gradient. +%] +[nocpp, include="core/cross/canvas_shader.h"] +class CanvasLinearGradient : CanvasShader { + %[ + The start point of this gradient. + %] + [getter, setter] Float2 start_point; + + %[ + The end point of this gradient. + %] + [getter, setter] Float2 end_point; + + %[ + The array of colors to be distributed between the two points in RBGA format + stored as an array of [r, g, b, a] colors. + %] + [getter, setter] Float4[] colors; + + %[ + The relative position of each corresponding color in the color array. + Values must begin with 0 and end with 1.0 and there should be exactly as + many numbers as there are colors. If positions is set to an empty array then + the colors are distributed evenly between between the start and end point. + %] + [getter, setter] float[] positions; + + %[ + The TileMode of this gradient which controls how the gradient pattern + repeats. + %] + [getter, setter] TileMode tile_mode; + +}; // class CanvasLinearGradient +} // namespace o3d diff --git a/o3d/plugin/idl/clear_buffer.idl b/o3d/plugin/idl/clear_buffer.idl new file mode 100644 index 0000000..8a10453 --- /dev/null +++ b/o3d/plugin/idl/clear_buffer.idl @@ -0,0 +1,76 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A ClearBuffer is a render node that clears the color buffer, zbuffer and/or + stencil buffer of the current render target. +%] + +[nocpp, include="core/cross/clear_buffer.h"] class ClearBuffer + : RenderNode { + %[ + The color to clear the buffer in RGBA Float4 format. + %] + [getter, setter] Float4 clear_color; + + %[ + true clears the current render target's color buffer to the clear color. + false does not clear the color buffer. + %] + [getter, setter] bool clear_color_flag_; + + %[ + The value to clear the depth buffer (0.0 to 1.0) + %] + [getter, setter] float clear_depth_; + + %[ + true clears the current render target's depth buffer to the clear depth + value. false does not clear the depth buffer. + %] + [getter, setter] bool clear_depth_flag_; + + %[ + The value to clear the stencil buffer to (0 - 255). + %] + [getter, setter] int clear_stencil_; + + %[ + true clears the current render target's stencil buffer to the clear stencil + value. false does not clear the stencil buffer + %] + [getter, setter] bool clear_stencil_flag_; + +}; // ClearBuffer + +} // namespace o3d diff --git a/o3d/plugin/idl/client.idl b/o3d/plugin/idl/client.idl new file mode 100644 index 0000000..0ac3b91 --- /dev/null +++ b/o3d/plugin/idl/client.idl @@ -0,0 +1,543 @@ +/* + * 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 public interface specification for the client. + +namespace o3d { + +%[ + IdArray is a typdef for an array of Ids. +%] +typedef Id[] IdArray; + +%[ + PackArray is a typdef for an array of Packs. +%] +typedef Pack[] PackArray; + +%[ + ObjectBaseArray is a typdef for an array of ObjectBase objects. +%] +typedef ObjectBase[] ObjectBaseArray; + +callback void LostResourcesCallback(); + +callback void EventCallback(Event event_descriptor); + +[binding_model=by_pointer, include="core/cross/renderer.h", nocpp, glue_iface] +class Renderer { + %[ + The initialization status of the renderer. + \li UNINITIALIZED + \li SUCCESS The renderer is initialized. + \li GPU_NOT_UP_TO_SPEC The renderer determined the user's machine can not + run O3D + \li OUT_OF_RESOURCES The user's machine does not have enough graphic + resources available to start another instance of the O3D renderer. + \li INITIALIZATION_ERROR Some unknown error like possibly drivers not being + installed correctly. + %] + enum InitStatus { + UNINITIALIZED, + SUCCESS, + GPU_NOT_UP_TO_SPEC, + OUT_OF_RESOURCES, + INITIALIZATION_ERROR + }; +}; + +%[ + The Client class is the main point of entry to O3D. It defines methods + for creating and deleting packs. Each new object created by the Client is + assigned a unique ID. + + The Client has a root transform for the transform graph and a root render + node for the render graph. +%] +[binding_model=by_pointer, include="core/cross/client.h", + nocpp, glue_iface] +class Client { + + callback void RenderCallback(RenderEvent render_event); + callback void TickCallback(TickEvent tick_event); + callback void ErrorCallback(String error_msg); + + %[ + The transform graph root Transform + %] + [getter] Transform root_; + + %[ + Call this function from window.onunload to ensure the browser does not + continue to call callbacks (like the render callback) after the page is + unloaded. It is possible that during unload the browser unloads all the + javascript code, but then, after that, still asks the plugin to render. The + browser then calls javascript functions that no longer exist which causes an + error. To prevent that situation you need to clear all your callbacks on + unload. cleanup handles that for you so you don't have to dispose each and + every callback by hand. + %] + void Cleanup(); + + %[ + Creates a pack object. + \return A pack object. + %] + Pack CreatePack(); + + %[ + Searches the Client for an object matching the given id. + + \param id The id of the object to look for. + \return The object or null if a object with the given id is not found. + %] + [const] ObjectBase? GetObjectById(Id id); + + %[ + Searches the Client for objects of a particular name and type. + \param name name of object to look for. + \param class_name name of class to look for. + \return Array of objects found. + %] + [const] ObjectArray GetObjects(String name, String class_name); + + %[ + Searches the Client for objects of a particular type. + \param class_name name of class to look for. + \return Array of objects found. + %] + [const] ObjectArray GetObjectsByClassName(String class_name); + + %[ + \var Property, + \li RENDERMODE_CONTINUOUS, Draw as often as possible up to refresh rate. + \li RENDERMODE_ON_DEMAND, Draw once then only when the OS requests it + (like uncovering part of a window.) + %] + enum RenderMode { + RENDERMODE_CONTINUOUS, // Draw as often as possible up to refresh rate. + RENDERMODE_ON_DEMAND // Draw once then when the OS request it + // (like uncovering part of a window.) + }; + + %[ + The current render mode. The default mode is RENDERMODE_CONTINUOUS.\n + Valid values are: + \li RENDERMODE_CONTINUOUS, Draw as often as possible up to refresh rate. + \li RENDERMODE_ON_DEMAND, Draw when the OS requests it (like uncovering + part of a window.) + %] + [getter, setter] RenderMode render_mode_; + + %[ + Forces a render of the current scene if the current render mode is + RENDERMODE_ON_DEMAND. + %] + void Render(); + + %[ + Renders a render graph. + + Normally the client calls this function automatically for you effectively + doing a client.renderTree(client.renderGraphRoot) but there are cases + where it is beneficial to be able to call this yourself and pass it + different roots when you need to manipulate something between calls. + + This function can only be called from inside a render callback. If you call + it the client will not do its default call as mentioned above. + %] + void RenderTree(RenderNode render_node); + + %[ + Returns an array of DisplayModes which are available for use in fullscreen + mode. + %] + [userglue, plugin_data] DisplayMode[] GetDisplayModes(); + + %[ + Makes a region of the plugin area that will invoke fullscreen mode if + clicked. The developer is responsible for communicating this to the user, + as this region has no visible marker. The developer is also responsible for + updating this region if the plugin gets resized, as we don't know whether or + how to scale it. + %] + [userglue, plugin_data] + void SetFullscreenClickRegion(int x, int y, int width, int height, int + mode_id); + %[ + Cancels fullscreen display, reverting to displaying content only in the + plugin region. If the plugin is already not in fullscreen mode, this has + no effect. + %] + [userglue, plugin_data] void CancelFullscreenDisplay(); + + %[ + Whether content is displayed in fullscreen mode or in a plugin window. The + default is false [not fullscreen]. + %] + [userglue_getter, getter, plugin_data] + bool fullscreen; + + %[ + Returns the width of the current drawing area [plugin or fullscreen] in + pixels. + %] + [userglue_getter, getter, plugin_data] + int width; + + %[ + Returns the height of the current drawing area [plugin or fullscreen] in + pixels. + %] + [userglue_getter, getter, plugin_data] + int height; + + %[ + The root of the render graph. + %] + [getter] RenderNode render_graph_root_; + + %[ + Sets the per frame render callback. + + Note: The callback will not be called recursively. When your callback is + called if you somehow manage to cause the client to render more frames + before you've returned from the callback you will not be called for those + frames. + + \code + g_client.setRenderCallback(onrender); + + function onrender(render_event) { + var elapsedTime = render_event.elapsedTime; + + // elapsedTime is the time elasped since the last callback. + // You can use this value to make your application frame rate independent. + // For example: + // position = position + velocity_in_units_per_second * elapsedTime; + } + \endcode + + \param render_callback The callback to call each frame. + %] + void SetRenderCallback(RenderCallback? render_callback); + + %[ + Clears the per frame render callback. + %] + void ClearRenderCallback(); + + %[ + Sets a render callback to be called at the end of the + rendering cycle of each frame. + + Note: The callback will not be called recursively. When your callback is + called if you somehow manage to cause the client to render more frames + before you've returned from the callback you will not be called for those + frames. + + \code + g_client.setPostRenderCallback(onpostrender); + + function onpostrender(render_event) { + var elapsedTime = render_event.elapsedTime; + + // elapsedTime is the time elasped since the last callback. + // You can use this value to make your application frame rate independent. + // For example: + // position = position + velocity_in_units_per_second * elapsedTime; + } + \endcode + + \param post_render_callback The callback to call each frame. + %] + void SetPostRenderCallback(RenderCallback? post_render_callback); + + %[ + Clears the post render callback. + %] + void ClearPostRenderCallback(); + + %[ + Sets the lost resources callback. + + The contents of certain resources, RenderSurfaces, can get discarded by the + system under certain circumstances. If you application needs that contents + to be in a certain state then you can set a callback giving your program the + opportunity to restore that state if and when it is lost. + + \param lost_resources_callback The callback when resources are lost. + %] + void SetLostResourcesCallback(LostResourcesCallback? lost_resources_callback); + + %[ + Clears the lost resources callback. + %] + void ClearLostResourcesCallback(); + + %[ + Sets a callback for a given event type. See @Event for a list of event + types. However, only mousedown, mousemove, mouseup, and dblclick will + actually do anything at present. + + There can be only one callback for a given event type at a time; setting a + new one deletes the old one. + %] + void SetEventCallback(String type, EventCallback? handler); + + %[ + Removes the previously-registered callback for an event of the given type. + %] + void ClearEventCallback(String type); + + %[ + Sets the texture to use when a Texture or Sampler is missing while + rendering. The default is a red texture with a yellow no symbol. + <span style="color:yellow; background-color: red;">Ø</span>. + If you set it to null you'll get an error if you try to render something + that is missing a needed Texture, Sampler or ParamSampler. + + For example if you don't care about missing textures, setting it to a black + texture would be one option. Another example is if you want to write all + your shaders to expect a texture then set this to a white texture. If you + want to make sure you are not missing any textures set it null and see if + you get any errors using Client.setErrorCallback or Client.lastError. + + \code + // Set the error texture to black. + var t = g_pack.createTexture2D('', 1, 1, g_o3d.Texture.XRGB8, 1); + t.set(0, [0, 0, 0]); + g_client.setErrorTexture(t); + \endcode + + \param texture texture to use for missing textures or null. + %] + void SetErrorTexture(Texture? texture); + + %[ + Sets a callback for when the client ticks. The client processes some things + like animation timers at up to 100hz. This callback will get called before + each of those process ticks. + + NOTE: The client takes ownership of the TickCallback you + pass in. It will be deleted if you call SetTickCallback a + second time or if you call ClearTickCallback. + + Note: The callback will not be called recursively. + + \param tick_callback TickCallback to call when the Client ticks. + %] + void SetTickCallback(TickCallback? tick_callback); + + %[ + Clears the tick callback + + NOTE: The client takes ownership of the TickCallback you + pass in. It will be deleted if you call SetTickCallback a second + time or if you call ClearTickCallback + %] + void ClearTickCallback(); + + %[ + Sets a callback for when the client gets an error. For example when a shader + is compiled and there is an error or if you attempt to bind a param to a + param of an incompatible type. + + NOTE: The client takes ownership of the ErrorCallback you + pass in. It will be deleted if you call SetErrorCallback a + second time or if you call ClearErrorCallback. + + NOTE: The callback will not be called recursively. If you are in a + callback, and do something that causes another error before you have + returned from the callback, your callback will not be called a second time. + + NOTE: If you put up an alert in response to an error it is best if you + clear the error callback before you put up the alert. Otherwise you'll get + an alert everytime the client tries to render which is every time you close + the current alert which means you'll be in an infinite loop of alerts. + + \param error_callback ErrorCallback to call when the Client gets an error. + %] + void SetErrorCallback(ErrorCallback? error_callback); + + %[ + Clears the Error callback + + NOTE: The client takes ownership of the ErrorCallback you + pass in. It will be deleted if you call SetErrorCallback a second + time or if you call ClearErrorCallback. + %] + void ClearErrorCallback(); + + %[ + Makes all parameters get re-evaluated. + %] + void InvalidateAllParameters(); + + %[ + This function is only available in the test version of the plugin. + %] + bool SaveScreen(String file_name); + + %[ + Returns the status of initializing the renderer so we can display the + appropriate message. We require a certain minimum set of graphics + capabilities. If the user's computer does not have his minimum + set this will be GPU_NOT_UP_TO_SPEC. If the user is out of graphics + resources this will be OUT_OF_RESOURCES. If some other error happened this + will be INITIALIZATION_ERROR. Otherwise it will be SUCCESS. + %] + [userglue_getter, getter, plugin_data] + Renderer::InitStatus renderer_init_status; + + %[ + Gets / Sets the cursor's shape. + %] + [userglue_getter, userglue_setter, getter, setter, plugin_data] + Cursor::CursorType cursor; + + %[ + Returns the socket address of the IMC message queue associated with the + Client. + %] + [const] String GetMessageQueueAddress(); + + %[ + The last error reported by the plugin. + %] + [userglue_getter, getter] String last_error_; + + %[ + All the objects managed by this client. + + Each access to this field gets the entire list so it is best to get it + just once. For example: + \code + var objects = client.objects; + for (var i = 0; i < objects.length; i++) { + var object = objects[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying Client, while modifications to the array's members + <strong>will</strong> affect them. + %] + [userglue_getter, getter] ObjectBaseArray objects_; + + %[ + Clears the error returned by GetLastError. + %] + void ClearLastError(); + + %[ + Resets the profiling information. + %] + void ProfileReset(); + + %[ + Returns the profiling information as a string. + %] + String ProfileToString(); + + %[ + A unique id for this client. + %] + [getter=id] Id client_id; + + [verbatim=cpp_glue] %{ + o3d::String userglue_getter_last_error_( + o3d::Client* self) { + return self->GetLastError(); + } + o3d::ObjectBaseArray userglue_getter_objects_( + o3d::Client* self) { + return self->GetByClass<o3d::ObjectBase>(); + } + std::vector<o3d::DisplayMode> userglue_method_GetDisplayModes( + void *plugin_data, o3d::Client *self) { + std::vector<o3d::DisplayMode> modes; + static_cast<glue::_o3d::PluginObject*>(plugin_data)->GetDisplayModes( + &modes); + return modes; + } + void userglue_method_SetFullscreenClickRegion( + void *plugin_data, o3d::Client *self, int x, int y, int width, + int height, int mode_id) { + static_cast<glue::_o3d::PluginObject*>(plugin_data)-> + SetFullscreenClickRegion(x, y, width, height, mode_id); + } + void userglue_method_CancelFullscreenDisplay( + void *plugin_data, o3d::Client *self) { + static_cast<glue::_o3d::PluginObject*>(plugin_data)-> + CancelFullscreenDisplay(); + } + bool userglue_getter_fullscreen( + void *plugin_data, + o3d::Client* self) { + return static_cast<glue::_o3d::PluginObject*>( + plugin_data)->fullscreen(); + } + int userglue_getter_width( + void *plugin_data, + o3d::Client* self) { + return static_cast<glue::_o3d::PluginObject*>( + plugin_data)->width(); + } + int userglue_getter_height( + void *plugin_data, + o3d::Client* self) { + return static_cast<glue::_o3d::PluginObject*>( + plugin_data)->height(); + } + void userglue_setter_cursor(void* plugin_data, + o3d::Client* self, + o3d::Cursor::CursorType cursor_type) { + static_cast<glue::_o3d::PluginObject*>(plugin_data)->set_cursor( + cursor_type); + } + o3d::Cursor::CursorType userglue_getter_cursor( + void* plugin_data, + o3d::Client* self) { + return static_cast<glue::_o3d::PluginObject*>( + plugin_data)->cursor(); + } + o3d::Renderer::InitStatus userglue_getter_renderer_init_status( + void* plugin_data, o3d::Client*) { + return static_cast<glue::_o3d::PluginObject*>( + plugin_data)->renderer_init_status(); + } + %} +}; + + +} // namespace o3d diff --git a/o3d/plugin/idl/counter.idl b/o3d/plugin/idl/counter.idl new file mode 100644 index 0000000..981df37 --- /dev/null +++ b/o3d/plugin/idl/counter.idl @@ -0,0 +1,238 @@ +/* + * 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. + */ + +namespace o3d { + +typedef float[] NumberArray; +typedef NumberArray[] NumberArrayArray; + +%[ + A Counter counts seconds, ticks or render frames depending on the type of + counter. You can set where it starts counting from and where it stops counting + at, whether or not it is running or paused and how it loops or does not loop. + You can also give it callbacks to call at specific count values. +%] +[nocpp, include="core/cross/counter.h"] class Counter + : ParamObject { + + callback void CounterCallback(); + + %[ + \var CountMode + \li CONTINUOUS, Keep running the counter forever. + \li ONCE, Stop at start or end depending on the direction. + \li CYCLE, When at end, jump back to start or visa versa. + \li OSCILLATE, Go from start to end back to start. + }; + %] + enum CountMode { + CONTINUOUS, + ONCE, + CYCLE, + OSCILLATE + }; + + %[ + Whether or not this counter is running. Default = true. + %] + [getter, setter] bool running_; + + %[ + Which direction this counter is counting. Default = true. + %] + [getter, setter] bool forward_; + + %[ + The start count for this counter. Default = 0. + %] + [getter, setter] float start_; + + %[ + The end count for this counter. Default = 0. + %] + [getter, setter] float end_; + + %[ + The current count value for this counter. Default = 0. + %] + [getter] float count_; + + %[ + The current count mode for this counter. Default = CONTINUOUS. + %] + [getter, setter] CountMode count_mode_; + + %[ + Sets the current count value for this counter as well as the resetting + the state of the callbacks. + + In other words. Assume start = 1, end = 5, count = 1, and you have a + callback at 3. + + <code> + myCounter.start = 1; + myCounter.end = 5; + myCounter.addCallback(3, myCallback); + myCounter.reset(); + + myCounter.advance(2); // count is now 3, myCallback is called. + myCounter.advance(2); // count is now 5 + </code> + + vs. + + <code> + myCounter.start = 1; + myCounter.end = 5; + myCounter.addCallback(3, myCallback); + myCounter.reset(); + + myCounter.advance(2); // count is now 3, myCallback is called. + myCounter.setCount(3); // count is now 3, callback state has been reset. + myCounter.advance(2); // count is now 5, myCallback is called. + </code> + + In the second case myCallback was called twice. + + \param count Value to set the count to. + %] + void SetCount(float count); + + %[ + A multiplier used when advancing the count. The default value is 1.0. + For example you could set this to 0.5 to run the counter at half speed + or 2.0 to run it at double speed. Default = 1. + %] + [getter, setter] float multiplier_; + + %[ + Resets the counter back to the start or end time depending on the forward + setting and also resets the Callback state. + Note: Reset does not change the running state of the counter. + %] + void Reset(); + + %[ + Advances the counter the given amount. The actual amount advanced depends + on the forward and multiplier settings. The formula is + + <code> + new_count = count + advance_amount * multiplier * (forward ? 1.0 : -1.0); + </code> + + Any callbacks that fall in the range between the counter's current count and + the amount advanced will be called. + + This function is normally called automatically by the client if the counter + is set to running = true. but you can call it manually. + + \param advance_amount Amount to advance count. + %] + [userglue] void Advance(float advance_amount); + + %[ + Adds a callback for a given count value. Only one callback can be + added to a specific count value. If another callback is added with the + same count value the previous callback for that value will be replaced. + + Note: A callback at start will only get called when counting backward, a + callback at end will only get called counting forward. + + \param count Count at which to call callback. + \param counter_callback Callback to call at given count. + %] + void AddCallback(float count, CounterCallback? counter_callback); + + %[ + Removes a callback for a given count value. + + \param count Count to remove callback for, + \return true if there was a callback for that count, false if there was not + a callback for that count. + %] + bool RemoveCallback(float count); + + %[ + Removes all the callbacks on this counter. + %] + void RemoveAllCallbacks(); + + %[ + Returns all the counts for which all callback has been added. + \return Array of counts. + %] + [userglue, const] NumberArray GetCallbackCounts(); + + [verbatim=cpp_glue] %{ + // This is userglue because we need to force the client to call any + // callbacks that need to be called as a result of the advance. + void userglue_method_Advance(o3d::Counter* self, + float advance_amount) { + o3d::Counter::CounterCallbackQueue queue; + self->Advance(advance_amount, &queue); + queue.CallCounterCallbacks(); + } + std::vector<float> userglue_method_GetCallbackCounts( + o3d::Counter* self) { + const o3d::Counter::CounterCallbackInfoArray& callbacks = + self->GetCallbacks(); + std::vector<float> float_array; + float_array.reserve(callbacks.size()); + for (unsigned ii = 0; ii < callbacks.size(); ++ii) { + float_array.push_back(callbacks[ii].count()); + } + return float_array; + } + %} +}; // Counter + +%[ + A Counter that counts seconds. +%] +[nocpp, include="core/cross/counter.h"] class SecondCounter + : Counter { +}; // SecondCounter + +%[ + A Counter that counts render frames. +%] +[nocpp, include="core/cross/counter.h"] class RenderFrameCounter + : Counter { +}; // RenderFrameCounter + +%[ + A Counter that counts ticks. +%] +[nocpp, include="core/cross/counter.h"] class TickCounter + : Counter { +}; // TickCounter + +} // namespace o3d diff --git a/o3d/plugin/idl/cursor.idl b/o3d/plugin/idl/cursor.idl new file mode 100644 index 0000000..9899bba --- /dev/null +++ b/o3d/plugin/idl/cursor.idl @@ -0,0 +1,82 @@ +/* + * 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 IDL declaration of the CursorType enumeration. + +namespace o3d { + +[include="core/cross/cursor.h"] +namespace Cursor { + +%[ + \var CursorType + \li DEFAULT + \li NONE + \li CROSSHAIR + \li POINTER + \li E_RESIZE + \li NE_RESIZE + \li NW_RESIZE + \li N_RESIZE + \li SE_RESIZE + \li SW_RESIZE + \li S_RESIZE + \li W_RESIZE + \li MOVE + \li TEXT + \li WAIT + \li PROGRESS + \li HELP +%] +enum CursorType { + DEFAULT, + NONE, + CROSSHAIR, + POINTER, + E_RESIZE, + NE_RESIZE, + NW_RESIZE, + N_RESIZE, + SE_RESIZE, + SW_RESIZE, + S_RESIZE, + W_RESIZE, + MOVE, + TEXT, + WAIT, + PROGRESS, + HELP +}; + +} // namespace Cursor + +} // namespace o3d diff --git a/o3d/plugin/idl/curve.idl b/o3d/plugin/idl/curve.idl new file mode 100644 index 0000000..0f876f2 --- /dev/null +++ b/o3d/plugin/idl/curve.idl @@ -0,0 +1,326 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A CurveKey prepresents a key on an Curve. +%] +[nocpp, include="core/cross/curve.h"] +class CurveKey : ObjectBase { + + %[ + The input of this key. + %] + [getter, setter, userglue_setter] float input; + + %[ + The output of this key. + %] + [getter, setter, userglue_setter] float output; + + %[ + Destroys this key, removing it from its owner. + %] + void Destroy(); + + [verbatim=cpp_glue] %{ + void userglue_setter_input(o3d::CurveKey* self, float value) { + self->SetInput(value); + } + void userglue_setter_output(o3d::CurveKey* self, float value) { + self->SetOutput(value); + } + %} +}; // CurveKey + +typedef CurveKey[] CurveKeyArray; + +%[ + An CurveKey that holds its output (is not interpolated between this key + and the next.) +%] +[nocpp, include="core/cross/curve.h"] +class StepCurveKey : CurveKey { +}; + +%[ + An CurveKey that linearly interpolates between this key and the next key. +%] +[nocpp, include="core/cross/curve.h"] +class LinearCurveKey : CurveKey { +}; + +%[ + An CurveKey that uses a bezier curve for interpolation between this key + and the next. +%] +[nocpp, include="core/cross/curve.h"] +class BezierCurveKey : CurveKey { + %[ + The in tangent for this key. + %] + [setter, getter, userglue_setter] Float2 in_tangent; + + %[ + The out tangent for this key. + %] + [setter, getter, userglue_setter] Float2 out_tangent; + + [verbatim=cpp_glue] %{ + void userglue_setter_in_tangent(o3d::BezierCurveKey* self, + const o3d::Float2& tangent) { + self->SetInTangent(tangent); + } + void userglue_setter_out_tangent(o3d::BezierCurveKey* self, + const o3d::Float2& tangent) { + self->SetOutTangent(tangent); + } + %} +}; + +%[ + A Curve stores a bunch of CurveKeys and given a value + representing an input point on a curve returns the output of the curve for + that input. Curve is data only. It is used by 1 or more + FunctionEval objects or by direct use from javascript. +%] +[nocpp, include="core/cross/curve.h"] +class Curve : Function { + %[ + enum Infinity { + \li CONSTANT, Uses the output value of the first or last animation key. + \li LINEAR, Takes the distance between the closest animation key + input value and the evaluation time. Multiplies this + distance against the instant slope at the closest + animation key and offsets the result with the closest + animation key output value. + \li CYCLE, Cycles over the first and last keys using + input = (input - first) % (last - first) + first; + Note that in CYCLE mode you can never get the end output + because a cycle goes from start to end exclusive of end. + \li CYCLE_RELATIVE, Same as cycle except the offset of the entire + cycle is added to each consecutive cycle. + \li OSCILLATE, Ping Pongs between the first and last keys. + }; + %] + enum Infinity { + CONSTANT, + LINEAR, + CYCLE, + CYCLE_RELATIVE, + OSCILLATE + }; + + %[ + The behavior of the curve before the first key. + %] + [getter, setter] Infinity pre_infinity; + + %[ + The behavior of the curve after the last key. + %] + [getter, setter] Infinity post_infinity; + + %[ + Whether or not a cache is used to speed up evaluation of this Curve + \sa SetSampleRate + %] + [getter, setter] bool use_cache; + + %[ + Gets the sample rate for the cache. By default Animation data is + cached so that using the animation is fast. To do this the keys that + represent the animation are sampled. The higher the frequency of the + samples the closer the cache will match the actual keys. + The default is 1/30 (30hz). You can set it anywhere from 1/240th (240hz) to + any larger value. Note: Setting the sample rate has the side effect of + invalidating the cache thereby causing it to get rebuilt. + Must be 1/240 or greater. Default = 1/30 + %] + [getter, setter, userglue_setter] float sample_rate; + + %[ + Returns whether or not the curve is discontinuous. A discontinuous curve + takes more time to evaluate. + %] + [const] bool IsDiscontinuous(); + + %[ + The keys for this curve. + %] + [const, getter, userglue_getter] CurveKeyArray keys; + + %[ + Adds 1 or more StepKeys to this Curve. + + Example: + \code + // Creates 2 keys. + // 1 key at 0 with an output of 10 + // 1 key at 20 with an output of 30 + curve.addStepKeys([0,10,20,30]); + \endcode. + + \param keys Array of input, output pairs. + %] + [userglue] void AddStepKeys(NumberArray keys); + + %[ + Adds 1 or more LinearKeys to this Curve. + + Example: + \code + // Creates 2 keys. + // 1 key at 0 with an output of 10 + // 1 key at 20 with an output of 30 + curve.addLinearKeys([0,10,20,30]); + \endcode. + + \param keys Array of input, output pairs. + %] + [userglue] void AddLinearKeys(NumberArray keys); + + %[ + Adds 1 or more BezierKeys to this Curve. + + Example: + \code + // Creates 2 keys. + // 1 key at 0 with an output of 10, in tangent of 1,9, out tangent 9,0.5 + // 1 key at 20 with an output of 30, in tangent of 30, 3, out tangent 4, 28 + curve.addBezierKeys([0,10,1,9,9,0.5,2,30,3,4,28]); + \endcode. + + \param keys Array of input, output pairs. + %] + [userglue] void AddBezierKeys(NumberArray keys); + + %[ + Creates a new key for this curve. + \param key_type name of key class to create. Valid type names are: + \li 'o3d.StepCurveKey', + \li 'o3d.LinearCurveKey', + \li 'o3d.BezierCurveKey', + \return The created key. + %] + [userglue] CurveKey CreateKey(String key_type); + + %[ + Deserializes from the curve data given a RawData object. + + \param raw_data contains curve data + \param offset is a byte offset from the start of raw_data + \param length is the byte length of the data to set + \return True if operation was successful. + %] + bool Set(o3d::RawData raw_data, + size_t offset, + size_t length); + + %[ + Deserializes from the curve data given a RawData object. + \param raw_data entire contents contains curve data + \return True if operation was successful. + %] + bool Set(o3d::RawData raw_data); + + [verbatim=cpp_glue] %{ + void userglue_setter_sample_rate(o3d::Curve* self, float rate) { + self->SetSampleRate(rate); + } + o3d::CurveKey* userglue_method_CreateKey(o3d::Curve* self, + o3d::String& key_type) { + return self->CreateKeyByClassName(key_type); + } + void userglue_method_AddStepKeys(o3d::Curve* self, + const std::vector<float>& values) { + const int kNumStepKeyValues = 2; + if (values.size() % kNumStepKeyValues != 0) { + O3D_ERROR(self->service_locator()) + << "expected multiple of 2 values got " << values.size(); + } else { + for (unsigned ii = 0; ii < values.size(); ii += kNumStepKeyValues) { + o3d::StepCurveKey* key = self->Create<o3d::StepCurveKey>(); + key->SetInput(values[ii]); + key->SetOutput(values[ii + 1]); + } + } + } + void userglue_method_AddLinearKeys(o3d::Curve* self, + const std::vector<float>& values) { + const int kNumLinearKeyValues = 2; + if (values.size() % kNumLinearKeyValues != 0) { + O3D_ERROR(self->service_locator()) + << "expected multiple of 2 values got " << values.size(); + } else { + for (unsigned ii = 0; ii < values.size(); ii += kNumLinearKeyValues) { + o3d::LinearCurveKey* key = + self->Create<o3d::LinearCurveKey>(); + key->SetInput(values[ii]); + key->SetOutput(values[ii + 1]); + } + } + } + void userglue_method_AddBezierKeys(o3d::Curve* self, + const std::vector<float>& values) { + const int kNumBezierKeyValues = 6; + if (values.size() % kNumBezierKeyValues != 0) { + O3D_ERROR(self->service_locator()) + << "expected multiple of 6 values got " << values.size(); + } else { + for (unsigned ii = 0; ii < values.size(); ii += kNumBezierKeyValues) { + o3d::BezierCurveKey* key = + self->Create<o3d::BezierCurveKey>(); + key->SetInput(values[ii]); + key->SetOutput(values[ii + 1]); + key->SetInTangent(o3d::Float2(values[ii + 2], + values[ii + 3])); + key->SetOutTangent(o3d::Float2(values[ii + 4], + values[ii + 5])); + } + } + } + o3d::CurveKeyArray userglue_getter_keys(o3d::Curve* self) { + const o3d::CurveKeyRefArray& source_keys = self->keys(); + o3d::CurveKeyArray keys; + keys.reserve(source_keys.size()); + for (unsigned ii = 0; ii < source_keys.size(); ++ii) { + keys.push_back(source_keys[ii].Get()); + } + return keys; + } + %} +}; // Curve + +} // namespace o3d + + diff --git a/o3d/plugin/idl/display_mode.idl b/o3d/plugin/idl/display_mode.idl new file mode 100644 index 0000000..9f5efd7 --- /dev/null +++ b/o3d/plugin/idl/display_mode.idl @@ -0,0 +1,64 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A DisplayMode describes the size and refresh rate of a display mode; it's + usually used in [or when transitioning into] fullscreen mode.%] + +[binding_model=by_value, nocpp, include="core/cross/display_mode.h"] +class DisplayMode { + + %[ + The width in pixels of the screen when in this mode. + %] + [getter] int width; + + %[ + The height in pixels of the screen when in this mode. + %] + [getter] int height; + + %[ + The refresh rate in Hz. + %] + [getter] int refresh_rate; + + %[ + An opaque identifier used to identify the mode when requesting a fullscreen + transition. + %] + [getter] int id; + +}; // DisplayMode + +} // namespace o3d diff --git a/o3d/plugin/idl/draw_context.idl b/o3d/plugin/idl/draw_context.idl new file mode 100644 index 0000000..cf1aae4 --- /dev/null +++ b/o3d/plugin/idl/draw_context.idl @@ -0,0 +1,57 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + The DrawContext defines the parameters used for a particular drawing pass. + It contains two 4-by-4 matrix params, view and + projection. These correspond to the viewing and projection transformation + matrices. +%] + +[nocpp, include="core/cross/draw_context.h"] class DrawContext + : ParamObject { + %[ + The view matrix represents the viewing transformation, used to take vertices + from world space to view space. + %] + [getter, setter] Vectormath::Aos::Matrix4 view; + + %[ + The projection matrix represents the projection transformation, used to take + vertices from view space to screen space. This matrix is usually an + orthographic or perspective transformation. + %] + [getter, setter] Vectormath::Aos::Matrix4 projection; +}; // DrawContext + +} // namespace o3d diff --git a/o3d/plugin/idl/draw_element.idl b/o3d/plugin/idl/draw_element.idl new file mode 100644 index 0000000..d07800c --- /dev/null +++ b/o3d/plugin/idl/draw_element.idl @@ -0,0 +1,74 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A DrawElement causes an Element to be Drawn with a particular material. + You can override other Effect parameters by adding corresponding params to + the DrawElement. + + \sa Element + \sa Material + \sa Effect +%] +[nocpp, include="core/cross/material.h"] class DrawElement + : ParamObject { + + %[ + The Material for this Draw Element. If it is null the material of owner will + be used. + %] + [getter, setter] Material? material_; + + %[ + The current owner of this Draw Element. Set to null to stop being owned. + + Note: DrawElements are referenced by the Pack they are created in and their + owner. If the DrawElement is removed from its Pack then setting the owner + to null will free the DrawElement. Or, visa versa, if you set the + DrawElement's owner to null then removing it from its Pack will free the + DrawElement. + %] + [getter, setter, userglue_setter] Element? owner_; + + [verbatim=cpp_glue] %{ + void userglue_setter_owner_( + o3d::DrawElement* _this, + o3d::Element* owner) { + _this->SetOwner(owner); + } + %} +}; // DrawElement + +typedef DrawElement[] DrawElementArray; + +} // namespace o3d diff --git a/o3d/plugin/idl/draw_list.idl b/o3d/plugin/idl/draw_list.idl new file mode 100644 index 0000000..7a46e0b --- /dev/null +++ b/o3d/plugin/idl/draw_list.idl @@ -0,0 +1,58 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A DrawList gets used during rendering to collect DrawElements to + render. Each Material references a DrawList. Depending on the material, as + DrawElements get collected they will be put on different DrawLists. +%] +[nocpp, include="core/cross/draw_list.h"] class DrawList + : NamedObject { + %[ + \var SortMethod, + \li BY_PERFORMANCE + \li BY_Z_ORDER + \li BY_PRIORITY + + Method to sort DrawList by. + %] + enum SortMethod { + BY_PERFORMANCE = 0, + BY_Z_ORDER = 1, + BY_PRIORITY = 2 + }; + + [getter] unsigned int global_index_; +}; // DrawList + +} // namespace o3d diff --git a/o3d/plugin/idl/draw_pass.idl b/o3d/plugin/idl/draw_pass.idl new file mode 100644 index 0000000..6fd6eab --- /dev/null +++ b/o3d/plugin/idl/draw_pass.idl @@ -0,0 +1,50 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A DrawPass renders a DrawList. +%] +[nocpp, include="core/cross/draw_pass.h"] class DrawPass + : RenderNode { + %[ + The DrawList for this DrawPass. + %] + [getter, setter] DrawList? draw_list_; + + %[ + The sort method for this DrawPass to draw the DrawList by. + %] + [getter, setter] DrawList::SortMethod sort_method_; +}; // Material + +} // namespace o3d diff --git a/o3d/plugin/idl/effect.idl b/o3d/plugin/idl/effect.idl new file mode 100644 index 0000000..b6d2243 --- /dev/null +++ b/o3d/plugin/idl/effect.idl @@ -0,0 +1,230 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + EffectParameterInfo holds information about the Parameters an Effect needs. + \sa o3d.Effect.getParameterInfo +%] +[binding_model=by_value, nocpp, include="core/cross/effect.h"] +class EffectParameterInfo { + %[ + The name of the parameter. + %] + [getter] String name; + + %[ + The type of the parameter. + %] + [userglue_getter, getter] String class_name; + + %[ + The number of elements. Non-zero for array types, zero for non-array types. + %] + [getter] int num_elements; + + %[ + The semantic of the parameter. This is always in UPPERCASE. + %] + [getter] String semantic; + + %[ + If this is a standard parameter (SAS) this will be the name of the type + of Param needed. Otherwise it will be the empty string. + + Standard Parameters are generally handled automatically by o3d but you + can supply your own if you have a unique situation. + %] + [userglue_getter, getter] String sas_class_name; + + [verbatim=cpp_glue] %{ + o3d::String userglue_getter_class_name( + const o3d::EffectParameterInfo& self) { + return self.class_type()->name(); + } + o3d::String userglue_getter_sas_class_name( + const o3d::EffectParameterInfo& self) { + return self.sas_class_type() ? self.sas_class_type()->name() : ""; + } + %} +}; + +typedef EffectParameterInfo[] EffectParameterInfoArray; + +%[ + EffectStreamInfo holds information about the Streams an Effect needs. + \sa o3d.Effect.getStreamInfo +%] +[binding_model=by_value, nocpp, include="core/cross/effect.h"] +class EffectStreamInfo { + %[ + The semantic of the stream. + %] + [getter] Stream::Semantic semantic; + + %[ + The semantic index of the stream. + %] + [getter] int semantic_index; +}; + +typedef EffectStreamInfo[] EffectStreamInfoArray; + +%[ + An Effect contains a vertex and pixel shader. +%] + +[nocpp, include="core/cross/effect.h"] class Effect : ParamObject { + %[ + Loads the vertex and pixel shader programs from an string containing + an O3D FX description. + + The string is subset of CG and HLSL. No techinques are allowed. + + To define the entry points add 2 lines in the following format. + \code + "// #o3d VertexShaderEntryPoint myVertexShader\n" + "// #o3d PixelShaderEntryPoint myPixelShader\n" + \endcode + + where "myVertexShader" and "myPixelShader" are the names of your + vertex and pixel shaders. At this time the format of those 2 lines + is extremely strict. You must have 1 and exactly 1 space between // and + #o3d, between #o3d and + VertexShaderEntryPoint/PixelShaderEntryPoint and between those and your + entry points. + + You must also specify a matrix load order like this. + + \code + // #o3d MatrixLoadOrder RowMajor + \endcode + + Valid orders are RowMajor and ColumnMajor + + Note: Currently it is possible to create effects strings that work on only + one platform (GL or D3D). You should test your shaders on both platforms. + + By version 1.0 this function will enforce shaders + that only work on both platforms. That format is mostly CG. + + \param effect the code of the effect. + %] + [virtual, pure] bool LoadFromFXString(String effect); + + %[ + For each of the effect's uniform parameters, creates corresponding + parameters on the given ParamObject. Skips SAS Parameters. + + If a Param with the same name but the wrong type already exists on the + given ParamObject CreateUniformParameters will attempt to replace it with + one of the correct type. + + Note: The most common thing to pass to this function is a Material but + depending on your application it may be more appropriate to pass in a + Transform, Effect, Element or DrawElement. + + \param param_object The param object on which the new paramters will be + created. + \sa o3d.Effect.createSASParameters + %] + void CreateUniformParameters(ParamObject param_object); + + %[ + For each of the effect's uniform parameters, if it is a SAS parameter + creates corresponding StandardParamMatrix4 parameters on the given + ParamObject. Note that SAS parameters are handled automatically by the + rendering system. so except in some rare cases there is no reason to call + this function. Also be aware that the StandardParamMatrix4 Paramters like + WorldViewProjectionParamMatrix4, etc.. are only valid during rendering. + At all other times they will not return valid values. + + If a Param with the same name but the wrong type already exists on the + given ParamObject CreateSASParameters will attempt to replace it with + one of the correct type. + + \param param_object The param object on which the new paramters will be + created. + \sa o3d.Effect.createUniformParameters + %] + void CreateSASParameters(ParamObject param_object); + + %[ + Gets info about the parameters this effect needs. + \return an array of EffectParameterInfos. + %] + [userglue] EffectParameterInfoArray GetParameterInfo(); + + %[ + Gets info about the streams this effect needs. + \return an array of EffectParameterInfos. + %] + [userglue] EffectStreamInfoArray GetStreamInfo(); + + %[ + \var Property, + \li ROW_MAJOR, Matrix parameters are loaded in row-major order (DX-style). + \li COLUMN_MAJOR, Matrix parameters are loaded in column-major order + (OpenGL-style). + %] + enum MatrixLoadOrder { + ROW_MAJOR, + COLUMN_MAJOR + }; + + %[ + The order in which matrix data is loaded to the GPU. + %] + [getter] MatrixLoadOrder matrix_load_order_; + + %[ + The source for the shaders on this Effect. + %] + [getter] String source_; + + [verbatim=cpp_glue] %{ + std::vector<o3d::EffectParameterInfo> userglue_method_GetParameterInfo( + o3d::Effect* self) { + std::vector<o3d::EffectParameterInfo> info_array; + self->GetParameterInfo(&info_array); + return info_array; + } + std::vector<o3d::EffectStreamInfo> userglue_method_GetStreamInfo( + o3d::Effect* self) { + std::vector<o3d::EffectStreamInfo> info_array; + self->GetStreamInfo(&info_array); + return info_array; + } + %} +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/element.idl b/o3d/plugin/idl/element.idl new file mode 100644 index 0000000..e18ae2e --- /dev/null +++ b/o3d/plugin/idl/element.idl @@ -0,0 +1,172 @@ +/* + * 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. + */ + +namespace o3d { + +typedef DrawElement[] DrawElementArray; + +%[ + An Element manages DrawElements for classes inherited from Element. +%] +[nocpp, include="core/cross/element.h"] class Element : ParamObject { + + %[ + The Material for this element. + %] + [getter, setter] Material? material_; + + %[ + The BoundingBox for this element. If culling is on this bounding box will be + tested against the view frustum of any draw context used to render this + Element. + %] + [getter, setter] BoundingBox bounding_box_; + + %[ + The z sort point for this element. If this Element is drawn by a DrawPass + that is set to sort by z order this value will be multiplied by the + worldViewProjection matrix to compute a z value to sort by. + %] + [getter, setter] Float3 z_sort_point; + + %[ + The priority for this element. Used to sort if this Element is drawn by a + DrawPass that is set to sort by priority. + %] + [getter, setter] float priority_; + + %[ + The cull settings for this element. If true this Element will be culled + by the bounding box above compared to the view frustum it is being rendered + with. + %] + [getter, setter] bool cull_; + + %[ + The current owner of this Draw Element. Pass in null to stop being owned. + + Note: Elements are referenced by the Pack they are created in and their + owner. If the Element is removed from its Pack then setting the owner + to null will free the Element. Or, visa versa, if you set the + Element's owner to null then removing it from its Pack will free the + Element. + %] + [getter, setter, userglue_setter] Shape? owner_; + + %[ + Gets all the DrawElements under this Element. + + Each access to this field gets the entire list so it is best to get it + just once. For example: + \code + var drawElements = element.drawElements; + for (var i = 0; i < drawElements.length; i++) { + var drawElement = drawElements[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying Element, while modifications to the members of the array. + <strong>will</strong> affect them. + %] + [userglue_getter, getter] DrawElementArray draw_elements_; + + %[ + Creates a DrawElement for this Element. Note that unlike + Shape.createDrawElements and Transform.createDrawElements this one will + create more than one element for the same material. + + \param pack: pack used to manage created DrawElement. + \param material: material to use for DrawElement. If you pass null + it will use the material on this Element. This allows you to easily + setup the default (just draw as is) by passing null or setup a shadow + pass by passing in a shadow material. + %] + DrawElement CreateDrawElement(Pack pack, + Material? material); + + %[ + Computes the intersection of a ray in the same coordinate system as + the specified POSITION stream. + \param position_stream_index Index of POSITION stream. + \param cull which side of the triangles to ignore. + \param start position of start of ray in local space. + \param end position of end of ray. in local space. + \return RayIntersectionInfo class. If valid() is false then something + was wrong, Check client::GetLastError(). If intersected() is true then + the ray intersected a something. position() is the exact point of + intersection. + %] + [const, userglue] RayIntersectionInfo IntersectRay( + int position_stream_index, + State::Cull cull, + Vectormath::Aos::Point3 start, + Vectormath::Aos::Point3 end); + + %[ + Computes the bounding box in same coordinate system as the specified + POSITION stream. + \param position_stream_index Index of POSITION stream. + \return The boundingbox for this element in local space. + %] + [const, userglue] BoundingBox GetBoundingBox(int position_stream_index); + + [verbatim=cpp_glue] %{ + void userglue_setter_owner_( + o3d::Element* _this, + o3d::Shape* owner) { + _this->SetOwner(owner); + } + o3d::RayIntersectionInfo userglue_method_IntersectRay( + o3d::Element* self, + int semantic_index, + o3d::State::Cull cull, + const Vectormath::Aos::Point3& start, + const Vectormath::Aos::Point3& end) { + o3d::RayIntersectionInfo result; + self->IntersectRay(semantic_index, cull, start, end, &result); + return result; + } + o3d::BoundingBox userglue_method_GetBoundingBox( + o3d::Element* self, + int semantic_index) { + o3d::BoundingBox result; + self->GetBoundingBox(semantic_index, &result); + return result; + } + o3d::DrawElementArray userglue_getter_draw_elements_( + o3d::Element* self) { + return self->GetDrawElements(); + } + %} +}; // Element + +} // namespace o3d diff --git a/o3d/plugin/idl/event.idl b/o3d/plugin/idl/event.idl new file mode 100644 index 0000000..ba6f315 --- /dev/null +++ b/o3d/plugin/idl/event.idl @@ -0,0 +1,186 @@ +/* + * 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 public interface specification for the event object +// passed to event handlers by the client. + +namespace o3d { + +%[ + An Event object contains information describing a JavaScript event; it's used + as an argument to event handlers triggered by the plugin.%] + +[binding_model=by_value, nocpp, include="core/cross/event.h"] +class Event { + + %[ + String identifiers for JavaScript events. + \var type + \li invalid + \li click + \li dblclick + \li mousedown + \li mousemove + \li mouseup + \li wheel + \li keydown + \li keypress + \li keyup + \li resize + %] + enum Type { + TYPE_INVALID, + TYPE_CLICK, + TYPE_DBLCLICK, + TYPE_MOUSEDOWN, + TYPE_MOUSEMOVE, + TYPE_MOUSEUP, + TYPE_WHEEL, + TYPE_KEYDOWN, + TYPE_KEYPRESS, + TYPE_KEYUP, + TYPE_RESIZE + }; + %[ + The type of event this object represents. + \var type + \li invalid + \li click + \li dblclick + \li keydown + \li keypress + \li keyup + \li mousedown + \li mousemove + \li mouseup + \li wheel + \li resize + %] + [userglue_getter, getter] String type; + %[ + \var Button, + \li BUTTON_LEFT + \li BUTTON_RIGHT + \li BUTTON_MIDDLE + \li BUTTON_4 + \li BUTTON_5 + Constants used to identify mouse buttons. + %] + enum Button { + BUTTON_LEFT = 0, + BUTTON_MIDDLE = 1, + BUTTON_RIGHT = 2, + BUTTON_4 = 3, + BUTTON_5 = 4 + }; + %[ + Which mouse button caused the event, in the case of mousedown, mouseup, + click, and dblclick events. This uses the values in enum Button. + %] + [getter] int button; + %[ + Whether the ctrl key was pressed at the time of the event. + %] + [getter] bool ctrl_key; + %[ + Whether the alt [option, on OSX] key was pressed at the time of the event. + %] + [getter] bool alt_key; + %[ + Whether the shift key was pressed at the time of the event. + %] + [getter] bool shift_key; + %[ + Whether the meta [command, on OSX] key was pressed at the time of the event. + %] + [getter] bool meta_key; + %[ + The key code of the key pressed or released. + %] + [getter] int key_code; + %[ + The character created by a keypress event. + %] + [getter, property=r] int char_code; + %[ + The x-coordinate in pixels from the left side of the plugin or fullscreen + display region. + %] + [getter] int x; + %[ + The y-coordinate in pixels from the top of the plugin or fullscreen + display region. + %] + [getter] int y; + %[ + The x-coordinate in pixels from the left side of the screen. + %] + [getter] int screen_x; + %[ + The y-coordinate in pixels from the top of the screen. + %] + [getter] int screen_y; + %[ + The horizontal scroll offset for wheel events, in arbitrary units. + Positive values mean right; negative mean left. + %] + [getter] int delta_x; + %[ + The vertical scroll offset for wheel events, in arbitrary units. + Positive values mean up or away from the user; negative mean down or toward + the user. + %] + [getter] int delta_y; + %[ + The width in pixels of the plugin or fullscreen display region as a result + of this event. + %] + [getter] int width; + %[ + The height in pixels of the plugin or fullscreen display region as a result + of this event. + %] + [getter] int height; + %[ + Whether we're currently displaying in fullscreen mode. + %] + [getter] bool fullscreen; + + [verbatim=cpp_glue] %{ + o3d::String userglue_getter_type( + const o3d::Event& self) { + return self.type_string(); + } + %} +}; // Event + +} // namespace o3d diff --git a/o3d/plugin/idl/field.idl b/o3d/plugin/idl/field.idl new file mode 100644 index 0000000..df3ca41 --- /dev/null +++ b/o3d/plugin/idl/field.idl @@ -0,0 +1,242 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A Field is a base class that manages a set of components in a + Buffer of a specific type. Fields are managed by Buffers and can not be + directly created. When a Buffer is destroyed or if a Field is removed from a + Buffer the Field's buffer property will be set to null. +%] +[nocpp, include="core/cross/buffer.h"] +class Field : NamedObject { + %[ + The number of components in this field. + %] + [getter] unsigned int num_components; + + %[ + The Buffer the field belongs to. + %] + [getter] Buffer? buffer; + + %[ + The offset of this field in the Buffer in bytes. + %] + [getter] unsigned int offset; + + %[ + The size of one element of this field. + %] + [getter] unsigned int size; +}; + +%[ + A field that contains floating point numbers. +%] +[nocpp, include="core/cross/buffer.h"] +class FloatField : Field { + %[ + Sets the values of the data stored in the field. + + The buffer for the field must have already been created either through + buffer.set or through buffer.allocateElements. + + The number of values passed in must be a multiple of the number of + components needed for the field. + + \param start_index index of first value to set. + \param values Values to be stored in the buffer starting at index. + %] + [userglue] void SetAt(unsigned int start_index, float[] values); + + %[ + Gets the values stored in the field. + + \param start_index index of the first value to get. + \param num_elements number of elements to read from field. + %] + [userglue] float[] GetAt(unsigned int start_index, unsigned int num_elements); + + [verbatim=cpp_glue] %{ + void userglue_method_SetAt(o3d::FloatField* field, + unsigned int start_index, + const std::vector<float>& values) { + if (values.size() % field->num_components() != 0) { + O3D_ERROR(field->service_locator()) + << "the number of values passed in is not a multiple of the number" + << " of components in the field."; + return; + } + if (!values.empty()) { + field->SetFromFloats(&values[0], field->num_components(), start_index, + values.size() / field->num_components()); + } + } + std::vector<float> userglue_method_GetAt( + o3d::FloatField* field, + unsigned int start_index, + unsigned int num_elements) { + std::vector<float> retval; + if (field->RangeValid(start_index, num_elements)) { + retval.resize(num_elements * field->num_components()); + field->GetAsFloats(start_index, &retval[0], field->num_components(), + num_elements); + } + return retval; + } + %} +}; + +%[ + A field that contains unsigned integers. +%] +[nocpp, include="core/cross/buffer.h"] +class UInt32Field : Field { + %[ + Sets the values of the data stored in the field. + + The buffer for the field must have already been created either through + buffer.set or through buffer.allocateElements. + + The number of values passed in must be a multiple of the number of + components needed for the field. + + \param start_index index of first value to set. + \param values Values to be stored in the buffer starting at index. + %] + [userglue] void SetAt(unsigned int start_index, unsigned int[] values); + + %[ + Gets the values stored in the field. + + \param start_index index of the first value to get. + \param num_elements number of elements to read from field. + %] + [userglue] unsigned int[] GetAt(unsigned int start_index, + unsigned int num_elements); + + [verbatim=cpp_glue] %{ + void userglue_method_SetAt(o3d::UInt32Field* field, + unsigned int start_index, + const std::vector<unsigned int>& values) { + if (values.size() % field->num_components() != 0) { + O3D_ERROR(field->service_locator()) + << "the number of values passed in is not a multiple of the number" + << " of components in the field."; + return; + } + if (!values.empty()) { + field->SetFromUInt32s(&values[0], field->num_components(), start_index, + values.size() / field->num_components()); + } + } + std::vector<uint32> userglue_method_GetAt( + o3d::UInt32Field* field, + unsigned int start_index, + unsigned int num_elements) { + std::vector<uint32> retval; + if (field->RangeValid(start_index, num_elements)) { + retval.resize(num_elements * field->num_components()); + field->GetAsUInt32s(start_index, &retval[0], field->num_components(), + num_elements); + } + return retval; + } + %} +}; + +%[ + A field that contains unsigned bytes that each represent a 0 to 1 value. + A typical use is for vertex colors since 4 bytes per vertex color are much + smaller than 4 floats per vertex color. +%] +[nocpp, include="core/cross/buffer.h"] +class UByteNField : Field { + %[ + Sets the values of the data stored in the field. + + The buffer for the field must have already been created either through + buffer.set or through buffer.allocateElements. + + The number of values passed in must be a multiple of the number of + components needed for the field. + + \param start_index index of first value to set. + \param values Values to be stored in the buffer starting at index. + %] + [userglue] void SetAt(unsigned int start_index, float[] values); + + %[ + Gets the values stored in the field. + + \param start_index index of the first value to get. + \param num_elements number of elements to read from field. + %] + [userglue] float[] GetAt(unsigned int start_index, unsigned int num_elements); + + [verbatim=cpp_glue] %{ + void userglue_method_SetAt(o3d::UByteNField* field, + unsigned int start_index, + const std::vector<float>& values) { + if (values.size() % field->num_components() != 0) { + O3D_ERROR(field->service_locator()) + << "the number of values passed in is not a multiple of the number" + << " of components in the field."; + return; + } + if (!values.empty()) { + field->SetFromFloats(&values[0], field->num_components(), start_index, + values.size() / field->num_components()); + } + } + std::vector<float> userglue_method_GetAt( + o3d::UByteNField* field, + unsigned int start_index, + unsigned int num_elements) { + std::vector<float> retval; + if (field->RangeValid(start_index, num_elements)) { + retval.resize(num_elements * field->num_components()); + field->GetAsFloats(start_index, &retval[0], field->num_components(), + num_elements); + } + return retval; + } + %} +}; + +%[ + An array of fields. +%] +typedef Field[] FieldArray; + +} // namespace o3d diff --git a/o3d/plugin/idl/file_request.idl b/o3d/plugin/idl/file_request.idl new file mode 100644 index 0000000..c0d6f77 --- /dev/null +++ b/o3d/plugin/idl/file_request.idl @@ -0,0 +1,124 @@ +/* + * 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. + */ + +namespace o3d { + +[include="core/cross/file_request.h"] callback void FileRequestCallback(); + +%[ + A FileRequest object is used to carry out an asynchronous request for a file + to be loaded. Its use parallels that of XMLHttpRequest; you create one, call + open, set the onreadystatechange callback, and call send. + Note that unlike XMLHttpRequests, FileRequests cannot be reused. + + For texture loads, on success the texture will be stored in a field of the + same name on the FileRequest itself. + + \code + var request = pack.createFileRequest("TEXTURE"); + var texture; + request.open("GET", url, true); + request.onreadystatechange = function() { + if (request.done) { + if (request.success) { + texture = request.texture; + } else { + dump('Load of texture file returned failure.'); + } + } + }; + request.send(); + \endcode +%] + +[nocpp, include="core/cross/file_request.h"] class FileRequest + : ObjectBase { + [setter=set_onreadystatechange] + FileRequestCallback onreadystatechange; + [getter=uri] String uri; + %[ + On completion of successful texture file loads, this holds the loaded + texture. + %] + [getter=texture] Texture? texture; + %[ + Whether or not to generate mip-maps on textures that are loaded (default: + true). Mip-maps are not generated for DXTC textures. DDS files can contain + pre-computed mip-maps for DXTC textures though. + %] + [getter, setter] bool generate_mipmaps; + + %[ + This holds the same values as in XMLHttpRequest: + \li 0 = uninitialized + \li 1 = opened + \li 2 = sent + \li 3 = receiving + \li 4 = loaded (the file has been downloaded, but may or may not have been + parsed yet) + %] + [getter=ready_state] int readyState; + %[ + This indicates whether any further processing will be done on this + FileRequest. + %] + [getter=done] bool done; + %[ + This field is only valid if done is true. It indicates whether or not the + request succeeded. If it failed error holds an error message. + %] + [getter=success] bool success; + + %[ + An error message. + If done is true and success is false this will be an error message + describing what went wrong. + %] + [getter] String error; + + %[ + Set up several of the request fields. + \param method "GET" is the only supported method at this time + \param uri the location of the file to fetch + \param async true is the only legal value at this time + %] + [nocpp, userglue, plugin_data] void open( + String method, String uri, bool async); + + %[ + Send the request. + Unlike XMLHttpRequest the onreadystatechange callback will be called no + matter what, with success or failure. + %] + [nocpp, userglue, plugin_data] void send(); +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/function.idl b/o3d/plugin/idl/function.idl new file mode 100644 index 0000000..a13ea38 --- /dev/null +++ b/o3d/plugin/idl/function.idl @@ -0,0 +1,87 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A Function is a class that has an Evaluate method. Evaluate takes 1 input and + returns 1 output. +%] +[nocpp, include="core/cross/function.h"] +class Function : NamedObject { + %[ + Gets an output for this function for the given input. + \param input Input to get output at. + \return The output for the given input. + %] + [const, userglue] float Evaluate(float input); + + [verbatim=cpp_glue] %{ + float userglue_method_Evaluate(o3d::Function* self, float value) { + return self->Evaluate(value, NULL); + } + %} +}; // Function + +%[ + A Param which stores a Function. +%] +[nocpp, include="core/cross/function.h"] class ParamFunction : Param { + %[ + The Function stored by the Param. + %] + [getter, setter] Function? value_; +}; + +%[ + A FunctionEval evaluates a Function through parameters. +%] +[nocpp, include="core/cross/function.h"] +class FunctionEval : ParamObject { + %[ + The input to the function + %] + [getter, setter] float input; + + %[ + The output of the function for the given input. + %] + [getter] float output; + + %[ + The function to evaluate. + %] + [getter, setter] Function? function_object; +}; // FunctionEval + +} // namespace o3d + + diff --git a/o3d/plugin/idl/material.idl b/o3d/plugin/idl/material.idl new file mode 100644 index 0000000..7c4374d --- /dev/null +++ b/o3d/plugin/idl/material.idl @@ -0,0 +1,59 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A Material holds the various uniform parameters an Effect needs to render. + For example a Lambert effect might need "diffuse", "ambient", "emissive". + The parameters needed on a Material will vary depending its Effect. + Note that a material MUST have its drawList set in order for objects using it + to render. +%] +[nocpp, include="core/cross/material.h"] class Material : ParamObject { + + %[ + The Effect for this material. + %] + [getter, setter] Effect? effect_; + + %[ + The State for this material. + %] + [getter, setter] State? state_; + + %[ + The DrawList this material will render on. + %] + [getter, setter] DrawList? draw_list_; +}; // Material + +} // namespace o3d diff --git a/o3d/plugin/idl/matrix4_axis_rotation.idl b/o3d/plugin/idl/matrix4_axis_rotation.idl new file mode 100644 index 0000000..de364c4 --- /dev/null +++ b/o3d/plugin/idl/matrix4_axis_rotation.idl @@ -0,0 +1,63 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + This param operation applies a rotation to its input matrix. The + rotation is specified in terms of an angle and an axis of rotation. + It can be used, for example, to supply the local matrix of a transform. +%] +[nocpp, include="core/cross/matrix4_axis_rotation.h"] +class Matrix4AxisRotation : ParamObject { + %[ + The local rotation axis. This must be a unit vector. + %] + [getter, setter] Float3 axis; + + %[ + The angle of rotation in radians. + %] + [getter, setter] float angle; + + %[ + The input matrix. Identity by default. + %] + [getter, setter] Vectormath::Aos::Matrix4 input_matrix; + + %[ + The output is equal to the rotation of the input matrix. + %] + [getter] Vectormath::Aos::Matrix4 output_matrix; +}; // Matrix4AxisRotation + +} // namespace o3d + diff --git a/o3d/plugin/idl/matrix4_composition.idl b/o3d/plugin/idl/matrix4_composition.idl new file mode 100644 index 0000000..87bbfc6 --- /dev/null +++ b/o3d/plugin/idl/matrix4_composition.idl @@ -0,0 +1,59 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + This param operation composes (multiplies) a local matrix with an input + matrix. It can be used, for example, to supply the local matrix of a + transform. +%] +[nocpp, include="core/cross/matrix4_composition.h"] +class Matrix4Composition : ParamObject { + %[ + The local matrix. + %] + [getter, setter] Vectormath::Aos::Matrix4 local_matrix; + + %[ + The input matrix. Identity by default. + %] + [getter, setter] Vectormath::Aos::Matrix4 input_matrix; + + %[ + The output is equal to the composition (multiplication) of the inpur + matrix with the local matrix. + %] + [getter] Vectormath::Aos::Matrix4 output_matrix; +}; // Matrix4Composition + +} // namespace o3d + diff --git a/o3d/plugin/idl/matrix4_scale.idl b/o3d/plugin/idl/matrix4_scale.idl new file mode 100644 index 0000000..ec567ee --- /dev/null +++ b/o3d/plugin/idl/matrix4_scale.idl @@ -0,0 +1,57 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + This param operation applies a scaling to its input matrix. + It can be used, for example, to supply the local matrix of a transform. +%] +[nocpp, include="core/cross/matrix4_scale.h"] +class Matrix4Scale : ParamObject { + %[ + The local scaling. + %] + [getter, setter] Float3 scale; + + %[ + The parent matrix. Identity by default. + %] + [getter, setter] Vectormath::Aos::Matrix4 input_matrix; + + %[ + The output is equal to the scale of the input matrix. + %] + [getter] Vectormath::Aos::Matrix4 output_matrix; +}; // Matrix4Scale + +} // namespace o3d + diff --git a/o3d/plugin/idl/matrix4_translation.idl b/o3d/plugin/idl/matrix4_translation.idl new file mode 100644 index 0000000..29afdc3 --- /dev/null +++ b/o3d/plugin/idl/matrix4_translation.idl @@ -0,0 +1,57 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + This param operation applies a translation to its input matrix. + It can be used, for example, to supply the local matrix of a transform. +%] +[nocpp, include="core/cross/matrix4_translation.h"] +class Matrix4Translation : ParamObject { + %[ + The local translation. + %] + [getter, setter] Float3 translation; + + %[ + The input matrix. Identity by default. + %] + [getter, setter] Vectormath::Aos::Matrix4 input_matrix; + + %[ + The output is equal to the translation of the input matrix. + %] + [getter] Vectormath::Aos::Matrix4 output_matrix; +}; // Matrix4Translation + +} // namespace o3d + diff --git a/o3d/plugin/idl/named.idl b/o3d/plugin/idl/named.idl new file mode 100644 index 0000000..f9fc4c4 --- /dev/null +++ b/o3d/plugin/idl/named.idl @@ -0,0 +1,118 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + The base class of most O3D run-time objects. +%] + +[binding_model=o3d,include="core/cross/object_base.h"] +class ObjectBase { + %[ + Unique id of the object. + + This id will be unique, even across multiple O3D clients in the same + page. + %] + [getter, userglue_getter] Id client_id; + + [verbatim=cpp_header] %{ + static Class *GetApparentClass() { return NULL; } + %} + + %[ + The concrete class name for an object derived from ObjectBase. + + If you want to know if an object is of a certain type you should use + objectBase.isAClassName + + \code + var t = pack.createObject('o3d.Transform'); + t.className == 'o3d.Transform'; // true + \endcode + %] + [userglue_getter, getter] String class_name_; + + %[ + Takes the name of a class as an argument, and returns true if this object is + either an instance of that class or derives from that class. + \code + var t = pack.createObject('o3d.Transform'); + t.isAClassName('o3d.Transform'); // true + t.isAClassName('o3d.ParamObject'); // true + t.isAClassName('o3d.Shape'); // false + \endcode + \returns true if this object is a or is derived from the given class name. + %] + bool IsAClassName(String class_name); + + [verbatim=cpp_glue] %{ + o3d::Id userglue_getter_client_id(o3d::ObjectBase* self) { + return self->id(); + } + o3d::String userglue_getter_class_name_(o3d::ObjectBase* self) { + return self->GetClassName(); + } + %} +}; + +%[ + Base class for all objects that are identifiable by a name. +%] +[binding_model=o3d, include="core/cross/named_object.h"] +class NamedObjectBase: ObjectBase { + %[ + The object's name. + %] + [getter] String name; + +}; + +%[ + Base class for all objects that can have their name set. +%] +[binding_model=o3d, include="core/cross/named_object.h"] +class NamedObject: NamedObjectBase { + %[ + The object's name. + + Setting this has no meaning to O3D, but is useful for debugging and for + the functions Client.getObjects, Pack.getObject, + RenderNode.getRenderNodesByNameInTree and + RenderNode.getTransformsByNameInTree + which search for objects by name. + %] + // NOTE: I tried putting just setter here but nixysa can't handle that. + [getter, setter] String name; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/pack.idl b/o3d/plugin/idl/pack.idl new file mode 100644 index 0000000..460d2c6 --- /dev/null +++ b/o3d/plugin/idl/pack.idl @@ -0,0 +1,308 @@ +/* + * 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. + */ + +namespace o3d { + +typedef ObjectBase[] ObjectArray; + +%[ + A Pack object functions as a container for O3D objects. The Pack + is used to control the lifetime scope of a collection of objects in bulk. The + Pack object achieves this by simply storing a set of references to its + contained objects, which ensures that the ref-counts for those objects never + reach zero while the pack is alive. + \sa o3d.Pack.removeObject + \sa o3d.Pack.destroy +%] +[nocpp, include="core/cross/pack.h"] class Pack : NamedObject { + + %[ + Removes all internal references to the Pack from the client. + The pack, and all objects contained in it are permitted to be destroyed + after the pack's destruction. Objects will only be destroyed after all + references to them have been removed. + + NOTE: Calling pack.destroy does NOT free your resources. It justs releases + the pack's reference to those resources. An example should hopefully make + it clearer. + + pack.destroy() is effectively almost the same as this. + + \code + var objectsInPack = pack.getObjectsByClassName('o3d.ObjectBase'); + for (var ii = 0; ii < objectsInPack.length; ++ii) { + pack.removeObject(objectsInPack[ii]); + } + \endcode + + The only difference is that after all the objects are removed the pack + itself will be released from the client. See documenation on + pack.removeObject for why this is important. + + It's important to note that many objects are only referenced by the pack. + Textures, Effects, Materials, for example. That means the moment you call + pack.destroy() those objects will be freed. If the client then tries to + render and some objects are missing you'll immediately get an error. + + \return True if the pack was successfully deleted. + %] + void Destroy(); + + %[ + Removes a pack's reference to an object. Any object created from + pack.create___ function can be removed. This releases the pack's reference + to that object so if nothing else is referencing that object it will be + deleted. + + NOTE: Calling pack.removeObject does NOT automatically free your resource. + It just releases the pack's reference to that resource. An example should + hopefully make it clearer. + + Suppose you make a transform like this: + + \code + var myTransform = pack.createObject('Transform'); + myTransform.parent = g_client.root; + pack.removeObject(myTransform); + \endcode + + In the code above, myTransform is referenced twice. Once by the pack, and + again by g_client.root So in this case, calling pack.removeObject() + only releases the pack's reference leaving the reference by g_client.root. + + \code + myTransform.parent = null; + \endcode + + Now the last reference has been removed and myTransform will be freed. + + \param object Object to remove. + \return True if the object was successfully removed. False if the object + is not part of this pack. + + \sa o3d.Pack.destroy + %] + bool RemoveObject(ObjectBase object); + + %[ + Creates an Object by Class name. + + Note: You may omit the 'o3d.'. + + \param type_name name of Class to create. Valid type names are: + \li o3d.Canvas + \li o3d.CanvasLinearGradient + \li o3d.CanvasPaint + \li o3d.ClearBuffer + \li o3d.Counter + \li o3d.Curve + \li o3d.DrawContext + \li o3d.DrawElement + \li o3d.DrawList + \li o3d.DrawPass + \li o3d.Effect + \li o3d.FunctionEval + \li o3d.IndexBuffer + \li o3d.Material + \li o3d.ParamArray + \li o3d.ParamObject + \li o3d.Primitive + \li o3d.RenderFrameCounter + \li o3d.RenderNode + \li o3d.RenderSurfaceSet + \li o3d.Sampler + \li o3d.SecondCounter + \li o3d.Shape + \li o3d.Skin + \li o3d.SkinEval + \li o3d.SourceBuffer + \li o3d.State + \li o3d.StateSet + \li o3d.StreamBank + \li o3d.Texture2D + \li o3d.TextureCUBE + \li o3d.TickCounter + \li o3d.Transform + \li o3d.TreeTraversal + \li o3d.VertexBuffer + \li o3d.Viewport + \li o3d.Matrix4AxisRotation + \li o3d.Matrix4Composition + \li o3d.Matrix4Scale + \li o3d.Matrix4Translation + \li o3d.ParamOp2FloatsToFloat2 + \li o3d.ParamOp3FloatsToFloat3 + \li o3d.ParamOp4FloatsToFloat4 + \li o3d.ParamOp16FloatsToMatrix4 + \li o3d.TRSToMatrix4 + \return The created object. + %] + ObjectBase? CreateObject(String type_name); + + %[ + Creates a new Texture2D object of the specified size and format and + reserves the necessary resources for it. + + Note: If enable_render_surfaces is true, then the dimensions must be a + power of two. + + \param width The width of the texture area in texels (max = 2048) + \param height The height of the texture area in texels (max = 2048) + \param format The memory format of each texel + \param levels The number of mipmap levels. Use zero to create the compelete + mipmap chain. + \param enable_render_surfaces: If true, the texture object will expose + RenderSurface objects through GetRenderSurface(...). + \return The Texture2D object. + + %] + Texture2D? CreateTexture2D(int width, + int height, + Texture::Format format, + int levels, + bool enable_render_surfaces); + + %[ + Creates a new TextureCUBE object of the specified size and format and + reserves the necessary resources for it. + Note: If enable_render_surfaces is true, then the dimensions must be a + power of two. + + \param edge_length The edge of the texture area in texels (max = 2048) + \param format The memory format of each texel + \param levels The number of mipmap levels. Use zero to create the compelete + mipmap chain. + \param enable_render_surfaces If true, the texture object will expose + RenderSurface objects through GetRenderSurface(...). + \return The TextureCUBE object. + %] + TextureCUBE? CreateTextureCUBE(int edge_length, + Texture::Format format, + int levels, + bool enable_render_surfaces); + + %[ + Creates a new RenderDepthStencilSurface object of a format suitable for use + as a depth-stencil render target. + Note: The dimensions of the RenderDepthStencilSurface must be a power of + two. + + \param width The width of the RenderSurface in pixels + \param height The height of the RenderSurface in pixels + \return The RenderSurface object. + %] + RenderDepthStencilSurface? CreateDepthStencilSurface(int width, int height); + + %[ + Search the pack for all objects of a certain class with a certain name. + + Note that modifications to this array [e.g. push()] will not affect + the underlying Pack, while modifications to the array's members + <strong>will</strong> affect them. + + \param name Name to look for + \param class_type_name the Class of the object. It is okay to pass base + types for example "o3d.RenderNode" will return ClearBuffers, + DrawPasses, etc... + \returns Array of Objects. + %] + [const] ObjectArray GetObjects(String name, String class_type_name); + + %[ + Search the pack for all objects of a certain class + + Note that modifications to this array [e.g. push()] will not affect + the underlying Pack, while modifications to the array's members + <strong>will</strong> affect them. + + \param class_type_name the Class of the object. It is okay to pass base + types for example "o3d.RenderNode" will return ClearBuffers, + DrawPasses, etc... + \returns Array of Objects. + %] + [const] ObjectArray GetObjectsByClassName(String class_type_name); + + %[ + All the objects managed by this pack. + + Each access to this field gets the entire list so it is best to get it + just once. For example: + \code + var objects = pack.objects; + for (var i = 0; i < objects.length; i++) { + var object = objects[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying Pack, while modifications to the array's members + <strong>will</strong> affect them. + %] + [userglue_getter, getter] ObjectBaseArray objects_; + + %[ + Creates a FileRequest to be used to asynchronously load a Texture. + \param type Must be "TEXTURE" + \returns a FileRequest + %] + [noccp, userglue] FileRequest? CreateFileRequest(String type); + [verbatim=cpp_glue, include="core/cross/file_request.h"] %{ + o3d::FileRequest *userglue_method_CreateFileRequest( + o3d::Pack *pack, const o3d::String &type) { + return pack->CreateFileRequest(type); + } + o3d::ObjectBaseArray userglue_getter_objects_( + o3d::Pack* self) { + return self->GetByClass<o3d::ObjectBase>(); + } + %} + + %[ + Creates an ArchiveRequest so we can stream in assets from an archive + \returns an ArchiveRequest + %] + [noccp, userglue] ArchiveRequest CreateArchiveRequest(); + [verbatim=cpp_glue, include="import/cross/archive_request.h"] %{ + o3d::ArchiveRequest *userglue_method_CreateArchiveRequest( + o3d::Pack *pack) { + return pack->CreateArchiveRequest(); + } + %} + + %[ + Creates a Texture given a RawData object + \returns the Texture + %] + Texture? CreateTextureFromRawData(RawData raw_data, + bool generate_mips); +}; // Pack + +} // namespace o3d diff --git a/o3d/plugin/idl/param.idl b/o3d/plugin/idl/param.idl new file mode 100644 index 0000000..f0a4705 --- /dev/null +++ b/o3d/plugin/idl/param.idl @@ -0,0 +1,326 @@ +/* + * 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. + */ + +namespace o3d { + +typedef Param[] ParamVector; + +%[ + Params store data defined name/value pairs on ParamObjects. + Each Param has a name, a type and a value that can be set and queried. + One of their uses is to hold "uniform constants" used to parameterize shaders. + Params can be connected in a source/destination fashion such that a target + Param gets its value from the source param. +%] + +[nocpp, include="core/cross/param.h"] class Param : NamedObjectBase { + + [verbatim=cpp_header] %{ + static int data_length_[NUM_PARAM_TYPES]; // Data size requirements per type + + void *data_; // Storage for Param value + const void *handle_; // Handle to an implementation specific object holding + // corresponding to the Param + %} + + %[ + If true, this param will make sure its input param is up to date when + using it as a source. Default = true. + + This is for helping with Param cycles. + + If paramA gets its value from paramB and paramB gets its value from + paramA:\n + If you go paramA.value, paramB will evaluate then copy to paramA.\n + If you go paramB.value, paramA will evaluate then copy to paramB.\n + If you set paramB.updateInput = false, then:\n + If you go paramA.value, paramB will evaluate then copy to paramA.\n + If you go paramB.value, paramB just copy paramA. paramA will NOT evaluate + when paramB asks for its value. + %] + [getter, setter, property=rw] bool update_input; + + %[ + Directly binds two Param elements such that this + parameter gets its value from the source parameter. The + source must be compatible with this parameter. + + \param source_param The parameter that the value originates from. Passing in + null will unbind any parameter currently bound. + \return True if the bind succeeded. + %] + bool Bind(Param? source_param); + + %[ + Breaks any input connection coming into the Param. + %] + void UnbindInput(); + + %[ + Breaks a specific param-bind output connection on this param. + + \param destination_param param to unbind. + \return True if the param was a destination param and was unbound. + %] + void UnbindOutput(Param destination_param); + + %[ + Breaks all param-bind output connections on this param. + %] + void UnbindOutputs(); + + %[ + If true the param is read only. Its value can not be set nor can it be used + as the destination in a ParamBind + %] + [getter, property=r] bool read_only_; + + %[ + The input connection for this param. + %] + [getter, property_r] Param? input_connection; + + %[ + The output connections for this param. + %] + [getter, property=r] ParamVector output_connections; +}; + +%[ + A Param class which stores a single float. +%] +[nocpp, include="core/cross/param.h"] class ParamFloat : Param { + %[ + The float stored by the Param. + %] + [getter, setter, property=rw] float value_; +}; + +%[ + A Param class which stores a Float2. +%] +[nocpp, include="core/cross/param.h"] class ParamFloat2 : Param { + %[ + Sets the Float2 stored by the Param by an array of 2 numbers. + %] + [setter, getter, property=rw, nocpp] Float2 value; + + %[ + Sets the value of ParamFloat2 by 2 numbers. + \param v0 first value. + \param v1 second value. + %] + [userglue] void set(float v0, float v1); + + [verbatim=cpp_glue] %{ + void userglue_method_set(o3d::ParamFloat2* self, + float v0, + float v1) { + self->set_value(o3d::Float2(v0, v1)); + } + %} +}; + +%[ + A Param class which stores a Float3. +%] +[nocpp, include="core/cross/param.h"] class ParamFloat3 : Param { + %[ + Sets the Float3 stored by the Param by an array of 3 numbers. + %] + [setter, getter, property=rw, nocpp] Float3 value; + + %[ + Sets the entries of the value of ParamFloat3 to the 3 given numbers. + \param v0 first value. + \param v1 second value. + \param v2 third value. + %] + [userglue] void set(float v0, float v1, float v2); + + [verbatim=cpp_glue] %{ + void userglue_method_set(o3d::ParamFloat3* self, + float v0, + float v1, + float v2) { + self->set_value(o3d::Float3(v0, v1, v2)); + } + %} +}; + +%[ + A Param class which stores a Float4. +%] +[nocpp, include="core/cross/param.h"] class ParamFloat4 : Param { + %[ + Sets the Float4 stored by the Param by an array of 4 numbers. + %] + [setter, getter, property=rw, nocpp] Float4 value; + + %[ + Sets the value of ParamFloat4 by 4 numbers. + \param v0 first value. + \param v1 second value. + \param v2 third value. + \param v3 fourth value. + %] + [userglue] void set(float v0, float v1, float v2, float v3); + + [verbatim=cpp_glue] %{ + void userglue_method_set(o3d::ParamFloat4* self, + float v0, + float v1, + float v2, + float v3) { + self->set_value(o3d::Float4(v0, v1, v2, v3)); + } + %} +}; + +%[ + A Param class which stores a 4-by-4 matrix. +%] +[nocpp, include="core/cross/param.h"] class ParamMatrix4 : Param { + %[ + Sets the 4-by-4 matrix stored by the Param by a length-4 array of arrays of + 4 numbers. + %] + [getter, setter, property=rw, nocpp] Vectormath::Aos::Matrix4 value; +}; + +%[ + A Param class which stores an integer. +%] +[nocpp, include="core/cross/param.h"] class ParamInteger : Param { + %[ + The integer stored by the Param. + %] + [getter, setter, property=rw] int value_; +}; + +%[ + A Param class which stores a boolean. +%] +[nocpp, include="core/cross/param.h"] class ParamBoolean : Param { + %[ + The boolean stored by the Param. + %] + [getter, setter, property=rw] bool value_; +}; + +%[ + A Param which stores a string. +%] +[nocpp, include="core/cross/param.h"] class ParamString : Param { + %[ + The string stored by the Param. + %] + [getter, setter, property=rw] String value_; +}; + +%[ + A Param which stores a Texture. +%] +[nocpp, include="core/cross/sampler.h"] class ParamSampler : Param { + %[ + The Sampler stored by the Param. + %] + [getter, setter, property=rw] Sampler? value_; +}; + +[nocpp, include="core/cross/param.h"] class ParamTexture : Param { + %[ + The Texture stored by the Param. + %] + [getter, setter, property=rw] Texture? value_; +}; + +%[ + A Param which stores a Material. +%] +[nocpp, include="core/cross/param.h"] class ParamMaterial : Param { + %[ + The Material stored by the Param. + %] + [getter, setter, property=rw] Material? value_; +}; + +%[ + A Param which stores a State. +%] +[nocpp, include="core/cross/param.h"] class ParamState : Param { + %[ + The State stored by the Param. + %] + [getter, setter, property=rw] State? value_; +}; + +%[ + A Param which stores a Effect. +%] +[nocpp, include="core/cross/param.h"] class ParamEffect : Param { + %[ + The Effect stored by the Param. + %] + [getter, setter, property=rw] Effect? value_; +}; + +%[ + A Param which stores a Transform. +%] +[nocpp, include="core/cross/param.h"] class ParamTransform : Param { + %[ + The Transform stored by the Param. + %] + [getter, setter, property=rw] Transform? value_; +}; + +%[ + A Param which stores a DrawList. +%] +[nocpp, include="core/cross/param.h"] class ParamDrawList : Param { + %[ + The DrawList stored by the Param. + %] + [getter, setter, property=rw] DrawList? value_; +}; + +%[ + A Param which stores a DrawContext. +%] +[nocpp, include="core/cross/param.h"] class ParamDrawContext : Param { + %[ + The DrawContext stored by the Param. + %] + [getter, setter, property=rw] DrawContext? value_; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/param_array.idl b/o3d/plugin/idl/param_array.idl new file mode 100644 index 0000000..63156d4 --- /dev/null +++ b/o3d/plugin/idl/param_array.idl @@ -0,0 +1,149 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A ParamArray is an object that holds an array of Params. +%] + +[nocpp, include="core/cross/param_array.h"] class ParamArray + : NamedObject { + + %[ + Creates a Param of the given type at the index requested. If a Param + already exists at that index the new param will be replace it. If the + index is past the end of the current array params of the requested type + will be created to fill out the array to the requested index. + + \param index index at which to create new param. + \param param_type_name The type of Param to create. For a list of valid + types see ParamObject.createParam + \return Param created at index or null if failure. + + %] + [userglue] Param? CreateParam(unsigned int index, String param_type_name); + + %[ + Gets a Param by index. + + \param index Index of Param to get. + \return The Param at index, or null if out of range. + %] + [userglue, const] Param? GetParam(unsigned int index); + + %[ + Removes a range of params. + + \param start_index Index of first param to remove. + \param num_to_remove The number of params to remove starting at start_index. + %] + void RemoveParams(unsigned int start_index, unsigned int num_to_remove); + + %[ + Resizes the array. + + \param num_params The number of params to make the array. + \param param_type_name The type of Param to create if resizing + requires params to be created. For a list of valid types see + ParamObject.createParam + %] + [userglue] void Resize(unsigned int num_params, String param_type_name); + + %[ + Gets all the param on this param object. + + Each access to this field gets the entire list, so it is best to get it + just once. For example: + \code + var params = ParamArray.params; + for (var i = 0; i < params.length; i++) { + var param = params[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying ParamObject, while modifications to the array's members + <strong>will</strong> affect them. + %] + [userglue_getter, getter] ParamVector params_; + + %[ + Returns the number of parameters in this ParamArray. + %] + [userglue_getter, getter] unsigned int length; + + [verbatim=cpp_glue] %{ + o3d::ParamVector userglue_getter_params_( + o3d::ParamArray *self) { + const o3d::ParamArray::ParamRefVector& src_params = self->params(); + o3d::ParamVector params(src_params.size()); + for (unsigned ii = 0; ii < src_params.size(); ++ii) { + params[ii] = src_params[ii]; + } + return params; + } + unsigned int userglue_getter_length( + o3d::ParamArray *self) { + return self->size(); + } + o3d::Param* userglue_method_CreateParam( + o3d:: ParamArray *self, + unsigned int index, + const o3d::String& param_type_name) { + return self->CreateParamByClassName(index, param_type_name); + } + void userglue_method_Resize( + o3d:: ParamArray *self, + unsigned int num_params, + const o3d::String& param_type_name) { + self->ResizeByClassName(num_params, param_type_name); + } + o3d::Param* userglue_method_GetParam( + o3d:: ParamArray *self, + unsigned int index) { + return self->GetUntypedParam(index); + } + %} + +}; // ParamArray + +%[ + A Param which stores a ParamArray. +%] +[nocpp, include="core/cross/param.h"] class ParamParamArray : Param { + %[ + The ParamArray stored by the Param. + %] + [getter, setter] ParamArray value_; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/param_object.idl b/o3d/plugin/idl/param_object.idl new file mode 100644 index 0000000..cac6efd --- /dev/null +++ b/o3d/plugin/idl/param_object.idl @@ -0,0 +1,161 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A ParamObject is the base class for all objects that can have Params. + Defines methods to add and remove params, search for params, etc. +%] +[nocpp, include="core/cross/param_object.h"] +class ParamObject : NamedObject { + %[ + Creates a Param with the given name and type on the ParamObject. + Will fail if a param with the same name already exists. + + \param param_name The name of the Param to be created. + \param param_type_name The type of Param to create. Valid types are + \li 'o3d.ParamBoolean', + \li 'o3d.ParamBoundingBox', + \li 'o3d.ParamDrawContext', + \li 'o3d.ParamDrawList', + \li 'o3d.ParamEffect', + \li 'o3d.ParamFloat', + \li 'o3d.ParamFloat2', + \li 'o3d.ParamFloat3', + \li 'o3d.ParamFloat4', + \li 'o3d.ParamInteger', + \li 'o3d.ParamMaterial', + \li 'o3d.ParamMatrix4', + \li 'o3d.ParamParamArray', + \li 'o3d.ParamRenderSurface', + \li 'o3d.ParamRenderDepthStencilSurface', + \li 'o3d.ParamSampler', + \li 'o3d.ParamSkin', + \li 'o3d.ParamSteamBank', + \li 'o3d.ParamState', + \li 'o3d.ParamString', + \li 'o3d.ParamTexture', + \li 'o3d.ParamTransform', + \li 'o3d.ProjectionParamMatrix4', + \li 'o3d.ProjectionInverseParamMatrix4', + \li 'o3d.ProjectionTransposeParamMatrix4', + \li 'o3d.ProjectionInverseTransposeParamMatrix4', + \li 'o3d.ViewParamMatrix4', + \li 'o3d.ViewInverseParamMatrix4', + \li 'o3d.ViewTransposeParamMatrix4', + \li 'o3d.ViewInverseTransposeParamMatrix4', + \li 'o3d.ViewProjectionParamMatrix4', + \li 'o3d.ViewProjectionInverseParamMatrix4', + \li 'o3d.ViewProjectionTransposeParamMatrix4', + \li 'o3d.ViewProjectionInverseTransposeParamMatrix4', + \li 'o3d.WorldParamMatrix4', + \li 'o3d.WorldInverseParamMatrix4', + \li 'o3d.WorldTransposeParamMatrix4', + \li 'o3d.WorldInverseTransposeParamMatrix4', + \li 'o3d.WorldViewParamMatrix4', + \li 'o3d.WorldViewInverseParamMatrix4', + \li 'o3d.WorldViewTransposeParamMatrix4', + \li 'o3d.WorldViewInverseTransposeParamMatrix4', + \li 'o3d.WorldViewProjectionParamMatrix4', + \li 'o3d.WorldViewProjectionInverseParamMatrix4', + \li 'o3d.WorldViewProjectionTransposeParamMatrix4', + \li 'o3d.WorldViewProjectionInverseTransposeParamMatrix4' + \return The newly created Param or null on failure. + %] + [userglue] Param? CreateParam(String param_name, String param_type_name); + + %[ + Searches by name for a Param defined in the object. + + \param param_name Name to search for. + \return The Param with the given name, or null otherwise. + %] + [userglue, const] Param? GetParam(String param_name); + + %[ + Removes a Param from a ParamObject. + + This function will fail if the param does not exist on this ParamObject + or if the param is unremovable. + + \param param param to remove. + \return True if the param was removed. + %] + bool RemoveParam(Param param); + + %[ + Gets all the param on this param object. + + Each access to this field gets the entire list, so it is best to get it + just once. For example: + \code + var params = paramObject.params; + for (var i = 0; i < params.length; i++) { + var param = params[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying ParamObject, while modifications to the array's members + <strong>will</strong> affect them. + %] + [userglue_getter, getter, property=r] ParamVector params_; + + %[ + Copies all the params from a the given source_param_object to this param + object. Does not replace any currently existing params with the same name. + + \param source_param_object param object to copy params from. + %] + void CopyParams(ParamObject source_param_object); + + [verbatim=cpp_glue] %{ + o3d::ParamVector userglue_getter_params_( + o3d::ParamObject *self) { + return self->GetParams(); + } + o3d::Param* userglue_method_CreateParam( + o3d:: ParamObject *self, + const o3d::String& param_name, + const o3d::String& param_type_name) { + return self->CreateParamByClassName(param_name, param_type_name); + } + + o3d::Param* userglue_method_GetParam( + o3d:: ParamObject *self, + const o3d::String& param_name) { + return self->GetUntypedParam(param_name); + } + %} +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/param_operation.idl b/o3d/plugin/idl/param_operation.idl new file mode 100644 index 0000000..9935f93 --- /dev/null +++ b/o3d/plugin/idl/param_operation.idl @@ -0,0 +1,265 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A Param operation that takes 2 floats to produce a Float2. +%] +[nocpp, include="core/cross/param_operation.h"] +class ParamOp2FloatsToFloat2 : ParamObject { + %[ + The first value for the Float2. + %] + [getter, setter] float input_0; + + %[ + The second value for the Float2. + %] + [getter, setter] float input_1; + + %[ + The Float2 that results from the inputs. + %] + [getter] Float2 output; +}; + +%[ + A Param operation that takes 3 floats to produce a Float3. +%] +[nocpp, include="core/cross/param_operation.h"] +class ParamOp3FloatsToFloat3 : ParamObject { + %[ + The first value for the Float3. + %] + [getter, setter] float input_0; + + %[ + The second value for the Float3. + %] + [getter, setter] float input_1; + + %[ + The third value for the Float3. + %] + [getter, setter] float input_2; + + %[ + The Float3 that results from the inputs. + %] + [getter] Float3 output; +}; + +%[ + A Param operation that takes 4 floats to produce a Float4. +%] +[nocpp, include="core/cross/param_operation.h"] +class ParamOp4FloatsToFloat4 : ParamObject { + %[ + The first value for the Float4. + %] + [getter, setter] float input_0; + + %[ + The second value for the Float4. + %] + [getter, setter] float input_1; + + %[ + The third value for the Float4. + %] + [getter, setter] float input_2; + + %[ + The fourth value for the Float4. + %] + [getter, setter] float input_3; + + %[ + The Float4 that results from the inputs. + %] + [getter] Float4 output; +}; + +%[ + A Param operation that takes 16 floats to produce a Matrix4. +%] +[nocpp, include="core/cross/param_operation.h"] +class ParamOp16FloatsToMatrix4 : ParamObject { + %[ + The first value for the Float4. + %] + [getter, setter] float input_0; + + %[ + The second value for the Float4. + %] + [getter, setter] float input_1; + + %[ + The third value for the Float4. + %] + [getter, setter] float input_2; + + %[ + The fourth value for the Float4. + %] + [getter, setter] float input_3; + + %[ + The fifth value for the Float4. + %] + [getter, setter] float input_4; + + %[ + The sixth value for the Float4. + %] + [getter, setter] float input_5; + + %[ + The seventh value for the Float4. + %] + [getter, setter] float input_6; + + %[ + The eighth value for the Float4. + %] + [getter, setter] float input_7; + + %[ + The ninth value for the Float4. + %] + [getter, setter] float input_8; + + %[ + The tenth value for the Float4. + %] + [getter, setter] float input_9; + + %[ + The eleventh value for the Float4. + %] + [getter, setter] float input_10; + + %[ + The twelfth value for the Float4. + %] + [getter, setter] float input_11; + + %[ + The thirteenth value for the Float4. + %] + [getter, setter] float input_12; + + %[ + The fourteenth value for the Float4. + %] + [getter, setter] float input_13; + + %[ + The fifteenth value for the Float4. + %] + [getter, setter] float input_14; + + %[ + The sixteenth value for the Float4. + %] + [getter, setter] float input_15; + + %[ + The Matrix4 that results from the inputs. + %] + [getter] Vectormath::Aos::Matrix4 output; +}; + +%[ + A Param operation that takes 9 floats to produce a 4-by-4 matrix. + The 9 floats encode a translation vector, angles of rotation around the x, y, + and z axes, and three scaling factors. The resulting transformation scales + first, then then rotates around the z-axis, then the y-axis, then the x-axis, + then translates. +%] +[nocpp, include="core/cross/param_operation.h"] +class TRSToMatrix4 : ParamObject { + %[ + The x translation for the Matrix4. + %] + [getter, setter] float translate_x; + + %[ + The y translation for the Matrix4. + %] + [getter, setter] float translate_y; + + %[ + The z translation for the Matrix4. + %] + [getter, setter] float translate_z; + + %[ + The x rotation for the Matrix4. + %] + [getter, setter] float rotate_x; + + %[ + The y rotation for the Matrix4. + %] + [getter, setter] float rotate_y; + + %[ + The z rotation for the Matrix4. + %] + [getter, setter] float rotate_z; + + %[ + The x scale for the Matrix4. + %] + [getter, setter] float scale_x; + + %[ + The y scale for the Matrix4. + %] + [getter, setter] float scale_y; + + %[ + The z scale for the Matrix4. + %] + [getter, setter] float scale_z; + + %[ + The Matrix4 that results from the inputs. + %] + [getter] Vectormath::Aos::Matrix4 output; +}; + +} // namespace o3d + + diff --git a/o3d/plugin/idl/plugin.idl b/o3d/plugin/idl/plugin.idl new file mode 100644 index 0000000..26a00b9 --- /dev/null +++ b/o3d/plugin/idl/plugin.idl @@ -0,0 +1,59 @@ +/* + * 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 public interface specification for the plugin. + +[verbatim=js_header] %{ + /*! + * \mainpage API Reference + * + * O3D is a plug-in for your browser. In order to make o3d produce + * the graphics you see in the demos, you'll need to access the plug-in and its + * components from within JavaScript. You can do so through a member variable + * on the plug-in called \a client, representing the instance of o3d within + * the plug-in. + * + * Here is how to access the client itself: + * \code + * g_client = document.o3d.client; + * \endcode + * + * where <code>document.o3d</code> + * represents the plug-in (rather than the Client instance within the plug-in). + * Not much else can be done with the plug-in, so you will want to access + * the \a client as shown above. + * + * Now that you know how to access the instance of o3d from your + * JavaScript, check out the rest of the API for more information on how to + * make o3d do what you want. + */ +%} diff --git a/o3d/plugin/idl/primitive.idl b/o3d/plugin/idl/primitive.idl new file mode 100644 index 0000000..4e0547c --- /dev/null +++ b/o3d/plugin/idl/primitive.idl @@ -0,0 +1,93 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A Primitive is a type of Element that is made from a list of points, + lines or triangles that use a single material. +%] +[nocpp, include="core/cross/primitive.h"] +class Primitive : Element { + %[ + \var PrimitiveType, + \li POINTLIST, + \li LINELIST, + \li LINESTRIP, + \li TRIANGLELIST, + \li TRIANGLESTRIP, + \li TRIANGLEFAN + + Type of geometric primitives used by the Primitive. + %] + enum PrimitiveType { + POINTLIST = 1, + LINELIST = 2, + LINESTRIP = 3, + TRIANGLELIST = 4, + TRIANGLESTRIP = 5, + TRIANGLEFAN = 6 + }; + + %[ + The type of primitive the primitive is (i.e., Primitive::POINTLIST, + Primitive::LINELIST, Primitive::TRIANGLELIST, etc.) + %] + [getter, setter] Primitive::PrimitiveType primitive_type; + + %[ + The number of vertices the primitive has. + %] + [getter, setter] unsigned int number_vertices; + + %[ + The number of rendering primitives (i.e., triangles, points, lines) the + primitive has. + %] + [getter, setter] unsigned int number_primitives; + + %[ + The index of the first vertex to render. + %] + [getter, setter] unsigned int start_index; + + %[ + The stream bank this primitive uses for vertices. + %] + [getter, setter] StreamBank? stream_bank; + + %[ + The index buffer for the primitive. If null the primitive is non-indexed. + %] + [getter, setter] IndexBuffer? index_buffer; +}; // Primitive + +} // namespace o3d diff --git a/o3d/plugin/idl/raw_data.idl b/o3d/plugin/idl/raw_data.idl new file mode 100644 index 0000000..8089d71 --- /dev/null +++ b/o3d/plugin/idl/raw_data.idl @@ -0,0 +1,88 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A RawData object contains raw binary data which could contain + image, audio, text, or other information. + + \code + var request = g_pack.createArchiveRequest(); + + request.onfileavailable = function() { + var texture = g_pack.createTextureFromRawData(request.data, true); + ... + }; + + request.send(); + + \endcode +%] + +[nocpp, include="import/cross/raw_data.h"] class RawData : ParamObject { + %[ + Returns the raw data as a string. The data must be a valid utf-8 string + and the uri must end in .json, .txt, .xml, .ini or .csv + %] + [getter, userglue_getter, StringValue] String string_value; + + %[ + The uri of the RawData. + %] + [getter] String uri; + + %[ + The length in bytes of the RawData. + %] + [getter=GetLength] size_t length; + + %[ + Discards all the resources associated with this data object. + %] + void Discard(); + + %[ + Flushes the memory resources associated with this data object, + but keeps a cache in case the data object is used later. + %] + void Flush(); + + [verbatim=cpp_glue, include="import/cross/raw_data.h"] %{ + o3d::String userglue_getter_string_value( + o3d::RawData* self) { + return self->StringValue(); + } + %} + +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/ray_intersection_info.idl b/o3d/plugin/idl/ray_intersection_info.idl new file mode 100644 index 0000000..176a46e --- /dev/null +++ b/o3d/plugin/idl/ray_intersection_info.idl @@ -0,0 +1,64 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A RayIntersectionInfo is used to return the results of ray intersection tests. +%] + +[binding_model=by_value, nocpp, include="core/cross/ray_intersection_info.h"] +class RayIntersectionInfo { + %[ + True if this ray intersection info is valid. For example if you call + element.intersectRay on an element that has no vertex buffers the result + will be invalid. + %] + [getter] bool valid_; + + %[ + True if this ray intersection intersected something. + %] + [getter] bool intersected_; + + %[ + The position the ray intersected something. + %] + [getter] Vectormath::Aos::Point3 position; + + %[ + The index of the primitive that was intersected. + %] + [getter] int primitive_index_; + +}; // RayIntersectionInfo + +} // namespace o3d diff --git a/o3d/plugin/idl/render_event.idl b/o3d/plugin/idl/render_event.idl new file mode 100644 index 0000000..4719596 --- /dev/null +++ b/o3d/plugin/idl/render_event.idl @@ -0,0 +1,106 @@ +/* + * 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 defines the public interface for the RenderEvent class. + +namespace o3d { + +%[ + This class is used to pass infomation to a registered onrender callback. +%] +[binding_model=by_value, include="core/cross/render_event.h", + nocpp,glue_iface] class RenderEvent { + %[ + The elapsed time in seconds since the last frame was rendered. + %] + [getter] float elapsed_time_; + + %[ + The time it took to render. + %] + [getter] float render_time_; + + %[ + The time it took to render and process tick callbacks, render callbacks, + counter callbacks. + %] + [getter] float active_time_; + + %[ + The number of transforms processed last frame. + + This is the number of transforms the renderer considered for traversing. + A Transform may not be considered for traversing either because it is not in + one of the subtrees the TreeTraversals are setup to traverse or because it + one of its parents was culled or set to visible = false. + %] + [getter] int transforms_processed_; + + %[ + The number of transforms that were culled last frame directly. + + Of the transforms processed, how many were culled. + %] + [getter] int transforms_culled_; + + %[ + The number of draw elements processed last frame. + + This is the number of draw elements the renderer considered for rendering. + If a transform is not traversed either because it is not in one of the + subtrees the TreeTraversals are setup to traverse or because it is marked + as visible = false then any draw elements in that part of the transform + graph are not processed. + %] + [getter] int draw_elements_processed_; + + %[ + The number of draw elements that were culled last frame directly. + (vs culled hierarchically) + %] + [getter] int draw_elements_culled_; + + %[ + The number of draw elements rendered last frame. Note: a draw element can + be rendered more than once per frame based on how many transforms it is + under and how many DrawPasses use the DrawLists it is put on. + %] + [getter] int draw_elements_rendered_; + + %[ + The number of low-level primitives rendered last frame. This is the sum of + the number primitives (triangles, lines) submitted to the renderer. + %] + [getter] int primitives_rendered_; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/render_node.idl b/o3d/plugin/idl/render_node.idl new file mode 100644 index 0000000..65bf48e --- /dev/null +++ b/o3d/plugin/idl/render_node.idl @@ -0,0 +1,135 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + RenderNode is the base of all RenderNodes in the render graph. All + RenderNodes have o3d.priority, o3d.active parameters. + RenderNodes are rendered in order of priority. +%] +[nocpp, include="core/cross/render_node.h"] + class RenderNode : ParamObject { + + %[ + Sets the priority of this render node. lower priorities are rendered first. + %] + [getter, setter] float priority_; + + %[ + Setting false skips this render node. Setting true processes this render + node. (ie, renders + whatever it's supposed to render) + %] + [getter, setter] bool active_; + + %[ + Sets the parent of the node by re-parenting the node under parent_node. + Setting parent_node to null removes the node and the entire subtree below + it from the render graph. + %] + [setter, userglue_setter] RenderNode? parent_; + + %[ + The immediate children of this RenderNode. + + Each access to this field gets the entire list so it is best to get it + just once. For example: + \code + var children = renderNode.children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying RenderNode, while modifications to the array's members + <strong>will</strong> affect them. + %] + [userglue_getter, getter] RenderNodeArray children_; + + %[ + Returns this render node and all its descendants. Note that this render node + might not be in the render graph. + + Note that modifications to this array [e.g. push()] will not affect + the underlying RenderNode, while modifications to the array's members + <strong>will</strong> affect them. + + \return An array containing all render nodes of the subtree. + %] + [nocpp] RenderNodeArray GetRenderNodesInTree(); + + %[ + Searches for render nodes that match the given name in the hierarchy under + and including this render node. Since there can be several render nodes with + a given name the results are returned in an array. + + Note that modifications to this array [e.g. push()] will not affect + the underlying RenderNode, while modifications to the array's members + <strong>will</strong> affect them. + + \param name Rendernode name to look for. + \return An array containing all nodes among this node and its decendants + that have the given name. + %] + [nocpp] RenderNodeArray GetRenderNodesByNameInTree(String name); + + %[ + Searches for render nodes that match the given class name in the hierarchy + under and including this render node. + + Note that modifications to this array [e.g. push()] will not affect + the underlying RenderNode, while modifications to the array's members + <strong>will</strong> affect them. + + \param class_name class name to look for. + \return An array containing all nodes among this node and its decendants + whose type is class_name. + %] + RenderNodeArray GetRenderNodesByClassNameInTree(String class_name); + + [verbatim=cpp_glue] %{ + void userglue_setter_parent_( + o3d::RenderNode* _this, + o3d::RenderNode* parent) { + _this->SetParent(parent); + } + o3d::RenderNodeArray userglue_getter_children_( + o3d::RenderNode *self) { + return self->GetChildren(); + } + %} +}; // RenderNode + +typedef RenderNode[] RenderNodeArray; + +} // namespace o3d diff --git a/o3d/plugin/idl/render_surface.idl b/o3d/plugin/idl/render_surface.idl new file mode 100644 index 0000000..70a3573 --- /dev/null +++ b/o3d/plugin/idl/render_surface.idl @@ -0,0 +1,76 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A RenderSurfaceBase is the base for RenderSurface and + RenderDepthStencilSurface. +%] +[nocpp, include="core/cross/render_surface.h"] class RenderSurfaceBase + : ParamObject { + %[ + The width of the surface, in pixels. + %] + [field_access=private, getter] int width; + + %[ + The height of the surface, in pixels. + %] + [field_access=private, getter] int height; + +}; // RenderSurfaceBaes + + +%[ + A RenderSurface encapsulates the notion of a renderable surface. + When used in conjunction with a RenderSurfaceSet node in the render graph, + the API allows for rendering of primitives to the given surface. + RenderSurface objects are not constructable through the Pack API, they may + only be accessed through the texture getRenderSurface(...) interfaces. +%] +[nocpp, include="core/cross/render_surface.h"] class RenderSurface + : RenderSurfaceBase { + %[ + The texture in which this surface is contained. + %] + [field_access=private, getter] Texture? texture; +}; // RenderSurface + +%[ + A RenderDepthStencilSurface represents a depth stencil render surface. +%] +[nocpp, include="core/cross/render_surface.h"] +class RenderDepthStencilSurface + : RenderSurfaceBase { +}; // RenderDepthStencilSurface + +} // namespace o3d diff --git a/o3d/plugin/idl/render_surface_set.idl b/o3d/plugin/idl/render_surface_set.idl new file mode 100644 index 0000000..e7e6585 --- /dev/null +++ b/o3d/plugin/idl/render_surface_set.idl @@ -0,0 +1,63 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A RenderSurfaceSet node will bind depth and color RenderSurface nodes + to the current rendering context. All RenderNodes descending + from the given RenderSurfaceSet node will operate on the contents of + the bound depth and color buffers. + Note the following usage constraints of this node: + 1) If both a color and depth surface is bound, then they must be of matching + dimensions. + 2) At least one of render_surface and render_depth_surface must non-null. +%] +[nocpp, include="core/cross/render_surface_set.h"] +class RenderSurfaceSet : RenderNode { + + %[ + The render surface to which the color contents of all RenderNode children + should be drawn. + %] + [getter, setter] + RenderSurface? render_surface_; + + %[ + The render depth stencil surface to which the depth contents of all + RenderNode children should be drawn. + %] + [getter, setter] + RenderDepthStencilSurface? render_depth_stencil_surface_; + +}; // RenderSurfaceSet + +} // namespace o3d diff --git a/o3d/plugin/idl/sampler.idl b/o3d/plugin/idl/sampler.idl new file mode 100644 index 0000000..b8d9c5e --- /dev/null +++ b/o3d/plugin/idl/sampler.idl @@ -0,0 +1,132 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + Sampler is the base of all texture samplers. Texture samplers encapsulate + a texture reference with a set of states that define how the texture + gets applied to a surface. Sampler states are set either via Params defined + on the Sampler object or directly via one the convenience methods defined + on the Sampler. The following states are supported (default values are in + parenthesis): + \li 'addressModeU' (WRAP) + \li 'addressModeV' (WRAP) + \li 'addressModeW' (WRAP) + \li 'magFilter' (LINEAR) + \li 'minFilter' (LINEAR) + \li 'mipFilter' (POINT) + \li 'borderColor' ([0,0,0,0]) + \li 'maxAnisotropy' (1) +%] + +[nocpp, include="core/cross/sampler.h"] +class Sampler : ParamObject { + %[ + \var AddressMode, + Controls what happens with texture coordinates outside the [0..1] range. + \li WRAP + \li MIRROR + \li CLAMP + \li BORDER + %] + enum AddressMode { + WRAP, + MIRROR, + CLAMP, + BORDER + }; + + %[ + \var FilterType, + Texture filtering types. + \li NONE + \li POINT + \li LINEAR + \li ANISOTROPIC + %] + enum FilterType { + NONE, + POINT, + LINEAR, + ANISOTROPIC + }; + + %[ + The texture address mode for the u coordinate. + %] + [getter, setter] AddressMode address_mode_u_; + + %[ + The texture address mode for the v coordinate. + %] + [getter, setter] AddressMode address_mode_v_; + + %[ + The texture address mode for the w coordinate. + %] + [getter, setter] AddressMode address_mode_w_; + + %[ + The magnification filter. + %] + [getter, setter] FilterType mag_filter_; + + %[ + The minification filter. + %] + [getter, setter] FilterType min_filter_; + + %[ + The mipmap filter used during minification. + %] + [getter, setter] FilterType mip_filter_; + + %[ + Color returned for texture coordinates outside the [0,1] range when the + address mode is set to BORDER. + %] + [getter, setter] Float4 border_color; + + %[ + Degree of anisotropy used when the ANISOTROPIC filter type is used. + %] + [getter, setter] int max_anisotropy_; + + %[ + The Texture object used by this Sampler. + %] + [getter, setter] Texture? texture_; +}; // Sampler + + +} // namespace o3d + diff --git a/o3d/plugin/idl/shape.idl b/o3d/plugin/idl/shape.idl new file mode 100644 index 0000000..1423e94 --- /dev/null +++ b/o3d/plugin/idl/shape.idl @@ -0,0 +1,89 @@ +/* + * 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. + */ + +namespace o3d { + +typedef Element[] ElementArray; + +%[ + The Shape represents a collection of Elements. The typical example is a + cube with 6 faces where each face uses a different material would be + represented as 1 Shape with 6 Elements, one for each material. +%] + +[nocpp, include="core/cross/shape.h"] class Shape : ParamObject { + %[ + The elements owned by this shape. + + Each access to this field gets the entire list so it is best to get it + just once. For example: + \code + var elements = renderNode.elements; + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying Shape, while modifications to the array's members + <strong>will</strong> affect them. + %] + [userglue_getter, getter, userglue_setter, setter] + ElementArray elements_; + + %[ + Creates a DrawElement for each Element owned by this Shape. + If an Element already has a DrawElement that uses \a material a new + DrawElement will not be created. + \param pack: pack used to manage created DrawElements. + \param material material to use for each DrawElement. If you pass null it + will use the material on the corresponding Element. + This allows you to easily setup the default (just draw as is) by passing + null or setup a shadow pass by passing in a shadow material. + %] + void CreateDrawElements(Pack pack, + Material? material); + + [verbatim=cpp_glue] %{ + + o3d::ElementArray userglue_getter_elements_( + o3d::Shape *self) { + return self->GetElements(); + } + void userglue_setter_elements_( + o3d::Shape *_this, + const o3d::ElementArray& elements) { + _this->SetElements(elements); + } + %} +}; // Shape + +} // namespace o3d diff --git a/o3d/plugin/idl/skin.idl b/o3d/plugin/idl/skin.idl new file mode 100644 index 0000000..762358d --- /dev/null +++ b/o3d/plugin/idl/skin.idl @@ -0,0 +1,274 @@ +/* + * 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 idlglue declaration of Skin and SkinEval. + +namespace o3d { + +%[ + A Skin holds an array of matrix indices and influences for vertices in a skin. + A Skin is data only and can be used by one or more SkinEvals to implement + skinning. +%] +[nocpp, include="core/cross/skin.h"] class Skin + : NamedObject { + + %[ + Sets the influences for an individual vertex. + + \param vertex_index The index of the vertex to set influences for + \param influences An array of pairs of numbers where the first number + is the index of the matrix to influence the vertex and the second + number is the amount of influence where 0 = no influence and 1 = 100% + influence. + %] + [userglue] void SetVertexInfluences(unsigned int vertex_index, + NumberArray influences); + + %[ + Gets the influences for an individual vertex. + \param vertex_index the index of the vertex to get influences from. + \return Returns an Array of number pairs where the first number of each + pair is the index of a matrix that influences this vertex and the + second number is the amount of influence where 0 = no influence + and 1 = 100% influcence. + %] + [const, userglue] NumberArray GetVertexInfluences(unsigned int vertex_index); + + %[ + An array with one element per vertex, where each element is an array of + influences consisting of a number pair, where the first number is a + matrix index and the second is the amount of influence. + %] + [getter,userglue_getter,setter,userglue_setter] + NumberArray[] influences_; + + %[ + Sets the inverse bind pose matrix for a particular joint/bone/transform. + \param index index of bone/joint/transform. + \param matrix Inverse bind pose matrix for that joint. + %] + void SetInverseBindPoseMatrix(unsigned int index, + Vectormath::Aos::Matrix4 matrix); + + %[ + The array of inverse bone matrices + %] + [getter,setter] Vectormath::Aos::Matrix4[] + inverse_bind_pose_matrices_; + + // TODO: Add setter that takes an array of matrices and sets all + // of the matrix arrays. + // TODO: Add SetNumberInverseBindPoseMatrices + // TODO: Add SetNumberVertexInflucences + + %[ + Deserializes from the skin data given a RawData object. + + \param raw_data contains skin data + \param offset is a byte offset from the start of raw_data + \param length is the byte length of the data to set + \return True if operation was successful. + %] + bool Set(o3d::RawData raw_data, + size_t offset, + size_t length); + + %[ + Deserializes from the skin data given a RawData object. + \param raw_data entire contents contains skin data + \return True if operation was successful. + %] + bool Set(o3d::RawData raw_data); + + [verbatim=cpp_glue] %{ + void userglue_method_SetVertexInfluences(o3d::Skin* self, + unsigned int vertex_index, + const std::vector<float>& values) { + if (values.size() % 2 != 0) { + O3D_ERROR(self->service_locator()) + << "odd number of values passed into SetVertexInfluence. " + << "Even number required as they are pairs."; + } else { + o3d::Skin::Influences influences; + for (unsigned ii = 0; ii < values.size(); ii += 2) { + influences.push_back(o3d::Skin::Influence( + static_cast<unsigned int>(values[ii]), + values[ii + 1])); + } + self->SetVertexInfluences(vertex_index, influences); + } + } + std::vector<std::vector<float> > userglue_getter_influences_( + o3d::Skin* _this) { + const o3d::Skin::InfluencesArray& influences = _this->influences(); + std::vector<std::vector<float> > result(influences.size()); + for (int i = 0; i != influences.size(); ++i) { + const o3d::Skin::Influences& vertex_influences = influences[i]; + for (int j = 0; j != vertex_influences.size(); ++j) { + const o3d::Skin::Influence& influence = vertex_influences[j]; + result[i].push_back(static_cast<float>(influence.matrix_index)); + result[i].push_back(influence.weight); + } + } + return result; + } + void userglue_setter_influences_( + o3d::Skin* _this, + const std::vector<std::vector<float> >& input) { + for (int i = 0; i != input.size(); ++i) { + const std::vector<float>& vertex_input = input[i]; + o3d::Skin::Influences influences(vertex_input.size() / 2); + for (int j = 0; j != vertex_input.size() / 2; ++j) { + influences[j] = o3d::Skin::Influence( + static_cast<int>(vertex_input[j * 2]), + vertex_input[j * 2 + 1]); + } + _this->SetVertexInfluences(i, influences); + } + } + std::vector<float> userglue_method_GetVertexInfluences( + o3d::Skin* self, + unsigned int vertex_index) { + std::vector<float> values; + const o3d::Skin::Influences* influences = + self->GetVertexInfluences(vertex_index); + if (influences) { + values.resize(influences->size() * 2); + for (unsigned ii = 0; ii < influences->size(); ++ii) { + values[ii * 2 + 0] = + static_cast<float>((*influences)[ii].matrix_index); + values[ii * 2 + 1] = (*influences)[ii].weight; + } + } + return values; + } + %} + +}; // Skin + +%[ + A SkinEval is a VertexSource that takes its Streams, a ParamArray of 4-by-4 + matrices and a Skin and skins the vertices in its streams storing the results + in bound output streams. +%] +[nocpp, include="core/cross/skin.h"] class SkinEval + : VertexSource { + + %[ + The Skin to use for skinning. + %] + [getter, setter] Skin? skin; + + %[ + The array of matrices to use for skinning. + %] + [getter, setter] ParamArray matrices; + + %[ + The base matrix to subtract from the matrices before skinning. + %] + [getter, setter] Vectormath::Aos::Matrix4 base; + + %[ + Binds a SourceBuffer field to the SkinEval and defines how the data in the + field should be interpreted. The field's buffer must be of a + compatible type otherwise the binding fails and the function returns false. + \param semantic The particular use of this stream. + \param semantic_index Which index of a particular semantic to use. + \param field The field containing information for this stream. + \param start_index The first element to use. + \return True if successful. + %] + [virtual] bool SetVertexStream(Stream::Semantic semantic, + int semantic_index, + Field field, + unsigned int start_index); + + + %[ + Removes a vertex stream from this SkinEval. + \param semantic The particular use of this stream. + \param semantic_index Which index of a particular semantic to use. + \return true if the specified stream existed. + %] + bool RemoveVertexStream(Stream::Semantic semantic, int semantic_index); + + %[ + Searches the vertex streams bound to the SkinEval for one with the given + stream semantic. + \param semantic The particular use of this stream. + \param semantic_index Which index of a particular semantic to use. + \return The found stream or NULL if it does not exist. + %] + [userglue] + Stream GetVertexStream(Stream::Semantic semantic, int semantic_index); + + %[ + A vector of the vertex streams on this SkinEval. + %] + [userglue_getter, getter] StreamVector vertex_streams; + + [verbatim=cpp_glue] %{ + std::vector<o3d::Stream*> userglue_getter_vertex_streams( + o3d::SkinEval* self) { + std::vector<o3d::Stream*> streams; + const o3d::StreamParamVector& stream_params = + self->vertex_stream_params(); + streams.reserve(stream_params.size()); + for (unsigned ii = 0; ii < stream_params.size(); ++ii) { + streams.push_back( + const_cast<o3d::Stream*>(&stream_params[ii]->stream())); + } + return streams; + } + o3d::Stream* userglue_method_GetVertexStream( + o3d::SkinEval* self, + o3d::Stream::Semantic semantic, + int semantic_index) { + return const_cast<o3d::Stream*>( + self->GetVertexStream(semantic, semantic_index)); + } + %} +}; // SkinEval + +%[ + A Param which stores a Skin. +%] +[nocpp, include="core/cross/skin.h"] class ParamSkin : Param { + %[ + The Skin stored by the Param. + %] + [getter, setter] Skin? value_; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/standard_param.idl b/o3d/plugin/idl/standard_param.idl new file mode 100644 index 0000000..b893bbc7 --- /dev/null +++ b/o3d/plugin/idl/standard_param.idl @@ -0,0 +1,200 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A type of ParamMatrix4 that supplies the current world matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse world matrix at + render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldInverseParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current world transpose matrix at + render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse world transpose + matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldInverseTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current view matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse view matrix at render + time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewInverseParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current view transpose matrix at + render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse view transpose matrix + at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewInverseTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current projecton matrix at render + time. +%] +[nocpp, include="core/cross/standard_param.h"] class ProjectionParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse projection matrix at + render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ProjectionInverseParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current projection transpose matrix + at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ProjectionTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse projection transpose + matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ProjectionInverseTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current world view matrix at + render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse world view matrix at + render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewInverseParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current transpose world view matrix + at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse world view + transpose matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewInverseTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current view projection matrix at + render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewProjectionParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse view projection + matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewProjectionInverseParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current view projection transpose + matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewProjectionTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse view projection + transpose matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class ViewProjectionInverseTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current world view projection matrix + at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewProjectionParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse world view projection + matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewProjectionInverseParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current world view projection + transpose matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewProjectionTransposeParamMatrix4 : ParamMatrix4 { +}; + +%[ + A type of ParamMatrix4 that supplies the current inverse world view projection + transpose matrix at render time. +%] +[nocpp, include="core/cross/standard_param.h"] class WorldViewProjectionInverseTransposeParamMatrix4 : ParamMatrix4 { +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/state.idl b/o3d/plugin/idl/state.idl new file mode 100644 index 0000000..9386208 --- /dev/null +++ b/o3d/plugin/idl/state.idl @@ -0,0 +1,277 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A State object sets the RenderStates for a particular material or StateSet. +%] +[nocpp, include="core/cross/state.h"] class State : ParamObject { + %[ + \var Comparison + \li CMP_NEVER (Never) + \li CMP_LESS (Less Than) + \li CMP_EQUAL (Equal To) + \li CMP_LEQUAL (Less Than or Equal To) + \li CMP_GREATER (Greater Than) + \li CMP_NOTEQUAL (Not Equal To) + \li CMP_GEQUAL (Greater Than or Equal To) + \li CMP_ALWAYS (Always) + %] + enum Comparison { + CMP_NEVER, /* Never */ + CMP_LESS, /* Less Than */ + CMP_EQUAL, /* Equal To */ + CMP_LEQUAL, /* Less Than or Equal To */ + CMP_GREATER, /* Greater Than */ + CMP_NOTEQUAL, /* Not Equal To */ + CMP_GEQUAL, /* Greater Than or Equal To */ + CMP_ALWAYS /* Always */ + }; + + %[ + \var Cull + \li CULL_NONE Don't Cull by face + \li CULL_CW Cull clock-wise faces + \li CULL_CCW Cull counter clock-wise faces + %] + enum Cull { + CULL_NONE, /* Don't Cull by face */ + CULL_CW, /* Cull clock-wise faces*/ + CULL_CCW /* Cull counter clock-wise faces */ + }; + + %[ + \var Fill + \li POINT + \li WIREFRAME + \li SOLID + %] + enum Fill { + POINT, /* Points */ + WIREFRAME, /* Wireframe */ + SOLID /* Solid */ + }; + + %[ + \var BlendingFunction + \li BLENDFUNC_ZERO + \li BLENDFUNC_ONE + \li BLENDFUNC_SOURCE_COLOR + \li BLENDFUNC_INVERSE_SOURCE_COLOR + \li BLENDFUNC_SOURCE_ALPHA + \li BLENDFUNC_INVERSE_SOURCE_ALPHA + \li BLENDFUNC_DESTINATION_ALPHA + \li BLENDFUNC_INVERSE_DESTINATION_ALPHA + \li BLENDFUNC_DESTINATION_COLOR + \li BLENDFUNC_INVERSE_DESTINATION_COLOR + \li BLENDFUNC_SOURCE_ALPHA_SATUTRATE + %] + enum BlendingFunction { + BLENDFUNC_ZERO, + BLENDFUNC_ONE, + BLENDFUNC_SOURCE_COLOR, + BLENDFUNC_INVERSE_SOURCE_COLOR, + BLENDFUNC_SOURCE_ALPHA, + BLENDFUNC_INVERSE_SOURCE_ALPHA, + BLENDFUNC_DESTINATION_ALPHA, + BLENDFUNC_INVERSE_DESTINATION_ALPHA, + BLENDFUNC_DESTINATION_COLOR, + BLENDFUNC_INVERSE_DESTINATION_COLOR, + BLENDFUNC_SOURCE_ALPHA_SATUTRATE + }; + + %[ + \var BlendingEquation + \li BLEND_ADD + \li BLEND_SUBTRACT + \li BLEND_REVERSE_SUBTRACT + \li BLEND_MIN + \li BLEND_MAX + %] + enum BlendingEquation { + BLEND_ADD, + BLEND_SUBTRACT, + BLEND_REVERSE_SUBTRACT, + BLEND_MIN, + BLEND_MAX + }; + + %[ + \var StencilOperation + \li STENCIL_KEEP + \li STENCIL_ZERO + \li STENCIL_REPLACE + \li STENCIL_INCREMENT_SATURATE + \li STENCIL_DECREMENT_SATURATE + \li STENCIL_INVERT + \li STENCIL_INCREMENT + \li STENCIL_DECREMENT + %] + enum StencilOperation { + STENCIL_KEEP, + STENCIL_ZERO, + STENCIL_REPLACE, + STENCIL_INCREMENT_SATURATE, + STENCIL_DECREMENT_SATURATE, + STENCIL_INVERT, + STENCIL_INCREMENT, + STENCIL_DECREMENT + }; + + %[ + Returns a Param for a given state. If the param does not already exist it + will be created. If the state_name is invalid it will return null. + \param state_name name of the state + \return param or null if no matching state. + + Example: + \code + // Gets the client. + g_o3d = document.o3d.o3d; + ... + // Creates a state object. + var state = my_pack.createState("my_state"); + + // Sets some states. + state.getStateParam('o3d.StencilEnable').value = true; + state.getStateParam('o3d.StencilReference').value = 25; + state.getStateParam('o3d.StencilPassOperation').value = + g_o3d.State.STENCIL_REPLACE; + state.getStateParam('o3d.StencilComparisonFunction').value = + g_o3d.State.CMP_ALWAYS; + state.getStateParam('o3d.ZEnable').value = false; + state.getStateParam('o3d.ZWriteEnable').value = false; + state.getStateParam('o3d.ColorWriteEnable').value = 0; + \endcode + Valid states: + <table> + <tr><td>State Name</td><td>Type</td><td>Default Value</td></tr> + <tr><td>o3d.AlphaTestEnable</td><td>ParamBoolean</td> + <td>default = false</td></tr> + <tr><td>o3d.AlphaReference</td><td>ParamFloat 0-1</td> + <td>default = 0</td></tr> + <tr><td>o3d.AlphaComparisonFunction</td><td>ParamInteger, + State.Comparison</td><td>default = State.CMP_ALWAYS</td></tr> + <tr><td>o3d.CullMode</td><td>ParamInteger, State.Cull</td> + <td>default = State.CULL_CW</td></tr> + <tr><td>o3d.DitherEnable</td><td>ParamBoolean</td> + <td>default = false</td></tr> + <tr><td>o3d.LineSmoothEnable</td><td>ParamBoolean</td> + <td>default = false</td></tr> + <tr><td>o3d.PointSpriteEnable</td><td>ParamBoolean</td> + <td>default = false</td></tr> + <tr><td>o3d.PointSize</td><td>ParamFloat</td><td>TBD</td></tr> + <tr><td>o3d.PolygonOffset1</td><td>ParamFloat<td>TBD</td></tr> + <tr><td>o3d.PolygonOffset2</td><td>ParamFloat<td>TBD</td></tr> + <tr><td>o3d.FillMode</td><td>ParamInteger, State.Fill</td> + <td>default = State.SOLID</td></tr> + <tr><td>o3d.ZEnable</td><td>ParamBoolean</td> + <td>default = true</td></tr> + <tr><td>o3d.ZWriteEnable</td><td>ParamBoolean</td> + <td>default = true</td></tr> + <tr><td>o3d.ZComparisonFunction</td> + <td>ParamInteger, State.Comparison</td> + <td>default = State.CMP_ALWAYS</td></tr> + <tr><td>o3d.AlphaBlendEnable</td><td>ParamBoolean</td> + <td>default = false</td></tr> + <tr><td>o3d.SourceBlendFunction</td> + <td>ParamInteger, State.BlendingFunction</td> + <td>default = State.BLENDFUNC_ONE</td></tr> + <tr><td>o3d.DestinationBlendFunction</td> + <td>ParamInteger, State.BlendingFunction</td> + <td>default = State.BLENDFUNC_ZERO</td></tr> + <tr><td>o3d.StencilEnable</td><td>ParamBoolean</td> + <td>default = false</td></tr> + <tr><td>o3d.StencilFailOperation</td> + <td>ParamInteger, State.StencilOperation</td> + <td>default = State.STENCIL_KEEP</td></tr> + <tr><td>o3d.StencilZFailOperation</td> + <td>ParamInteger, State.StencilOperation</td> + <td>default = State.STENCIL_KEEP</td></tr> + <tr><td>o3d.StencilPassOperation</td> + <td>ParamInteger, State.StencilOperation</td> + <td>default = State.STENCIL_KEEP</td></tr> + <tr><td>o3d.StencilComparisonFunction</td> + <td>ParamInteger, State.Comparison</td> + <td>default = State.CMP_ALWAYS</td></tr> + <tr><td>o3d.StencilReference</td><td>ParamInteger 0-255</td> + <td>default = 0</td></tr> + <tr><td>o3d.StencilMask</td><td>ParamInteger 0-255</td> + <td>default = 255</td></tr> + <tr><td>o3d.StencilWriteMask</td><td>ParamInteger 0-255</td> + <td>default = 255</td></tr> + <tr><td>o3d.ColorWriteEnable</td> + <td>ParamInteger 0-15 bit 0 = red, bit 1 = green, + bit 2 = blue, bit 3 = alpha</td><td>default = 15</td></tr> + <tr><td>o3d.BlendEquation</td> + <td>ParamInteger, State.BlendingEquation</td> + <td>default = State.BLEND_ADD</td></tr> + <tr><td>o3d.TwoSidedStencilEnable</td><td>ParamBoolean</td> + <td>default = false</td></tr> + <tr><td>o3d.CCWStencilFailOperation</td> + <td>ParamInteger, State.StencilOperation</td> + <td>default = State.STENCIL_KEEP</td></tr> + <tr><td>o3d.CCWStencilZFailOperation</td> + <td>ParamInteger, State.StencilOperation</td> + <td>default = State.STENCIL_KEEP</td></tr> + <tr><td>o3d.CCWStencilPassOperation</td> + <td>ParamInteger, State.StencilOperation</td> + <td>default = State.STENCIL_KEEP</td></tr> + <tr><td>o3d.CCWStencilComparisonFunction</td> + <td>ParamInteger, State.Comparison</td> + <td>default = State.CMP_ALWAYS</td></tr> + <tr><td>o3d.SeparateAlphaBlendEnable</td><td>ParamBoolean</td> + <td>default = false;</td></tr> + <tr><td>o3d.SourceBlendAlphaFunction</td> + <td>ParamInteger, State.BlendingFunction</td> + <td>default = State.BLENDFUNC_ONE</td></tr> + <tr><td>o3d.DestinationBlendAlphaFunction</td> + <td>ParamInteger, State.BlendingFunction</td> + <td>default = State.BLENDFUNC_ZERO</td></tr> + <tr><td>o3d.BlendAlphaEquation</td> + <td>ParamInteger, State.BlendingEquation</td> + <td>default = State.BLEND_ADD</td></tr> + </table> + + %] + [nocpp, userglue] Param? GetStateParam(String state_name); + + [verbatim=cpp_glue] %{ + o3d::Param* userglue_method_GetStateParam( + o3d::State *self, + const o3d::String& state_name) { + return self->GetUntypedStateParam(state_name); + } + %} +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/state_set.idl b/o3d/plugin/idl/state_set.idl new file mode 100644 index 0000000..050c6b8 --- /dev/null +++ b/o3d/plugin/idl/state_set.idl @@ -0,0 +1,49 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A StateSet is a render node that sets render states of all of its + children. You can make this a parent of a part of the render graph to set + render states in a more global way. +%] +[nocpp, include="core/cross/state_set.h"] class StateSet + : RenderNode { + + %[ + The State for this StateSet. + %] + [getter, setter] State? state_; + +}; // StateSet + +} // namespace o3d diff --git a/o3d/plugin/idl/stream.idl b/o3d/plugin/idl/stream.idl new file mode 100644 index 0000000..991ade3 --- /dev/null +++ b/o3d/plugin/idl/stream.idl @@ -0,0 +1,118 @@ +/* + * 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. + */ + +%[ + Namespace o3d +%] +namespace o3d { + +%[ + A stream object defines how Buffer data is interpreted by an Effect when + rendering a Primitive.. + It contains a pointer to a Field, a semantic and semantic index + that determine how the data is accessed. +%] + +[nocpp, include="core/cross/stream.h"] +class Stream : ObjectBase { + %[ + \var Semantic, + \li UNKNOWN_SEMANTIC = 0, + \li POSITION, + \li NORMAL, + \li TANGENT, + \li BINORMAL, + \li COLOR, + \li TEXCOORD + + Semantics used when binding buffers to the streambank. They determine how + the Stream links up to the shader inputs. + %] + enum Semantic { + UNKNOWN_SEMANTIC = 0, + POSITION, + NORMAL, + TANGENT, + BINORMAL, + COLOR, + TEXCOORD + }; + + %[ + The associated Field. + %] + [userglue_getter, getter] Field? field; + + %[ + The semantic specified for the Stream. + %] + [getter] Semantic semantic; + + %[ + The semantic index specified for the Stream + (eg., TEXCOORD1 = 1, BINORMAL7 = 7, etc). + %] + [getter] int semantic_index; + + %[ + The start index for the Stream. + %] + [getter] unsigned int start_index; + + [verbatim=cpp_glue] %{ + o3d::Field* userglue_getter_field(o3d::Stream* self) { + return const_cast<o3d::Field*>(&self->field()); + } + %} +}; + +%[ + An array of Streams. +%] +typedef Stream[] StreamVector; + +%[ + A Param which stores a Stream. +%] +[nocpp, include="core/cross/stream.h"] class ParamVertexBufferStream : Param { + %[ + The Stream stored by the Param. + %] + [userglue_getter, getter] Stream? stream; + [verbatim=cpp_glue] %{ + o3d::Stream* userglue_getter_stream( + o3d::ParamVertexBufferStream* self) { + return const_cast<o3d::Stream*>(&self->stream()); + } + %} +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/stream_bank.idl b/o3d/plugin/idl/stream_bank.idl new file mode 100644 index 0000000..a5f08d9 --- /dev/null +++ b/o3d/plugin/idl/stream_bank.idl @@ -0,0 +1,148 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + The StreamBank a collection of streams that hold vertices. +%] +[nocpp, include="core/cross/primitive.h"] +class StreamBank : NamedObject { + + %[ + Binds a VertexBuffer field to the StreamBank and defines how the data in + the buffer should be interpreted. The field's buffer must be of a + compatible type otherwise the binding fails and the function returns false. + \param semantic The particular use of this stream. + \param semantic_index Which index of a particular semantic to use. + \param field The field containing information for this stream. + \param start_index The first element to use. + \return True if successful. + %] + bool SetVertexStream(Stream::Semantic semantic, + int semantic_index, + Field field, + unsigned int start_index); + + %[ + Searches the vertex streams bound to the StreamBank for one with the given + stream semantic. If a stream is not found then it returns null. + \param semantic The particular use of this stream. + \param semantic_index Which index of a particular semantic to use. + \return The found stream or null if it does not exist. + %] + [userglue] + Stream? GetVertexStream(Stream::Semantic semantic, int semantic_index); + + %[ + Removes a vertex stream from this StreamBank. + \param semantic The particular use of this stream. + \param semantic_index Which index of a particular semantic to use. + \return true if the specified stream existed. + %] + bool RemoveVertexStream(Stream::Semantic semantic, int semantic_index); + + %[ + Binds the source stream to the corresponding stream in this VertexSource. + + \param source Source to get vertices from. + \param semantic The semantic of the vertices to get + \param semantic_index The semantic index of the vertices to get. + \return True if success. False if failure. If the requested semantic or + semantic index do not exist on the source or this source the bind will + fail. + %] + bool BindStream(VertexSource source, + Stream::Semantic semantic, + int semantic_index); + + %[ + Unbinds the requested stream. + \param semantic The semantic of the vertices to unbind + \param semantic_index The semantic index of the vertices to unbind. + \return True if unbound. False those vertices do not exist or were not + bound. + %] + bool UnbindStream(Stream::Semantic semantic, int semantic_index); + + %[ + An array of the vertex streams on this StreamBank. + + Each access to this field gets the entire list so it is best to get it + just once. For example: + \code + var streams = streamBank.vertexStreams; + for (var i = 0; i < streams.length; i++) { + var stream = streams[i]; + } + \endcode + + Note that modifications to this array [e.g. push()] will not affect + the underlying StreamBank, while modifications to the members of the array. + <strong>will</strong> affect them. + %] + [userglue_getter, getter] StreamVector vertex_streams; + + [verbatim=cpp_glue] %{ + std::vector<o3d::Stream*> userglue_getter_vertex_streams( + o3d::StreamBank* self) { + std::vector<o3d::Stream*> streams; + const o3d::StreamParamVector& stream_params = + self->vertex_stream_params(); + streams.reserve(stream_params.size()); + for (unsigned ii = 0; ii < stream_params.size(); ++ii) { + streams.push_back( + const_cast<o3d::Stream*>(&stream_params[ii]->stream())); + } + return streams; + } + o3d::Stream* userglue_method_GetVertexStream( + o3d::StreamBank* self, + o3d::Stream::Semantic semantic, + int semantic_index) { + return const_cast<o3d::Stream*>( + self->GetVertexStream(semantic, semantic_index)); + } + %} +}; // StreamBank + +%[ + A Param which stores a StreamBank. +%] +[nocpp, include="core/cross/stream_bank.h"] +class ParamStreamBank : Param { + %[ + The StreamBank stored by the Param. + %] + [getter, setter] StreamBank? value_; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/texture.idl b/o3d/plugin/idl/texture.idl new file mode 100644 index 0000000..03dc49b --- /dev/null +++ b/o3d/plugin/idl/texture.idl @@ -0,0 +1,312 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + The Texture class is a base class for image data used in texture mapping. +%] +[include="core/cross/texture_base.h"] class Texture : ParamObject { + %[ + \var Format, + \li UNKNOWN_FORMAT + \li XRGB8 + \li ARGB8 + \li ABGR16F + \li R32F + \li ABGR32F + \li DXT1 + \li DXT3 + \li DXT5 + + The in-memory format of the texture bitmap. + + NOTE: The R32F format is different on GL vs D3D. If you use it in a shader + you must only use the red channel. The green, blue and alpha channels are + undefined. + + For example: + \code + ... + + // The texture sampler is used to access the texture bitmap in the fragment + // shader. + sampler texSampler0; + + ... + + // input parameters for our vertex shader + struct PixelShaderInput { + float4 position : POSITION; + float2 texcoord : TEXCOORD0; // Texture coordinates + }; + + float4 pixelShaderFunction(PixelShaderInput input): COLOR { + // ** Use only valid channels. ** ---------+ + // | + // V + return tex2D(texSampler0, input.texcoord).rrrr; + } + \endcode + %] + enum Format { + UNKNOWN_FORMAT, + XRGB8, + ARGB8, + ABGR16F, + R32F, + ABGR32F, + DXT1, + DXT3, + DXT5 + }; + + %[ + The memory format used for storing the bitmap associated with the texture + object. + %] + [getter] Format format; + + %[ + The number of mipmap levels used by the texture. + %] + [getter] int levels; + + %[ + True of all the alpha values in the texture are 1.0 + %] + [getter, getter, setter] bool alpha_is_one; +}; // Texture + +%[ + A class for 2D textures that defines the interface for getting + the dimensions of the texture, its memory format and number of mipmap levels. +%] +[include="core/cross/texture.h"] class Texture2D : Texture { + %[ + The width of the texture, in texels. + %] + [getter] int width; + + %[ + The height of the texture, in texels. + %] + [getter] int height; + + %[ + Returns a RenderSurface object associated with a mip_level of a texture. + + \param mip_level: The mip-level of the surface to be returned. + \param pack: The pack in which the surface will reside. + \return The RenderSurface object. + %] + RenderSurface? GetRenderSurface(int mip_level, Pack pack); + + // TODO: Add Get, GetRect, SetRect and/or expose Bitmap. + %[ + Sets the values of the data stored in the texture. + + It is not recommend that you call this for large textures but it is useful + for making simple ramps or noise textures for shaders. + + NOTE: the number of values must equal the size of the texture * the number + of elements. In other words, for a XRGB8 texture there must be + width * height * 3 values. For an ARGB8, ABGR16F or ABGR32F texture there + must be width * height * 4 values. For an R32F texture there must be + width * height values. + + NOTE: the order of channels is R G B for XRGB8 textures and R G B A + for ARGB8, ABGR16F and ABGR32F textures so for example for XRGB8 textures\n + \n + [1, 0, 0] = a red pixel\n + [0, 0, 1] = a blue pixel\n + \n + For ARGB8, ABGR16F, ABGR32F textures\n + \n + [1, 0, 0, 0] = a red pixel with zero alpha\n + [1, 0, 0, 1] = a red pixel with one alpha\n + [0, 0, 1, 1] = a blue pixel with one alpha\n + + \param level the mip level to update. + \param values Values to be stored in the buffer. + %] + [nocpp, userglue, include="core/cross/math_utilities.h"] + void Set(int level, float[] values); + + [verbatim=cpp_glue] %{ + void userglue_method_Set(o3d::Texture2D* self, + int level, + const std::vector<float>& values) { + if (level < 0 || level >= self->levels()) { + O3D_ERROR(self->service_locator()) + << "level (" << level << " out of range"; + return; + } + unsigned width = std::max(self->width() >> level, 1); + unsigned height = std::max(self->height() >> level, 1); + unsigned num_elements; + unsigned swizzle[4] = {2, 1, 0, 3}; + switch (self->format()) { + case o3d::Texture::XRGB8: + num_elements = 3; + break; + case o3d::Texture::R32F: + swizzle[0] = 0; + num_elements = 1; + break; + case o3d::Texture::ARGB8: + case o3d::Texture::ABGR16F: + num_elements = 4; + break; + case o3d::Texture::ABGR32F: { + num_elements = 4; + const o3d::Texture::RGBASwizzleIndices& indices = + self->GetABGR32FSwizzleIndices(); + for (int ii = 0; ii < 4; ++ii) { + swizzle[ii] = indices[ii]; + } + break; + } + default: + O3D_ERROR(self->service_locator()) + << "Texture::Set not supported for this type of texture"; + return; + } + unsigned needed = num_elements * width * height; + if (values.size() != needed) { + O3D_ERROR(self->service_locator()) + << "needed " << needed << " values but " << values.size() + << " passed in."; + return; + } + void* data; + if (!self->Lock(level, &data)) { + O3D_ERROR(self->service_locator()) << "could not lock texture"; + return; + } + switch (self->format()) { + case o3d::Texture::ABGR16F: { + unsigned short* destination = static_cast<unsigned short*>(data); + unsigned short* end = destination + width * height * num_elements; + const float* source = &values[0]; + while (destination < end) { + for (int element = 0; element < num_elements; ++element) { + destination[element] = Vectormath::Aos::FloatToHalf( + source[swizzle[element]]); + } + destination += num_elements; + source += num_elements; + } + break; + } + case o3d::Texture::R32F: + case o3d::Texture::ABGR32F: { + float* destination = static_cast<float*>(data); + float* end = destination + width * height * num_elements; + const float* source = &values[0]; + while (destination < end) { + for (int element = 0; element < num_elements; ++element) { + destination[element] = source[swizzle[element]]; + } + destination += num_elements; + source += num_elements; + } + break; + } + default: { + unsigned char* destination = static_cast<unsigned char*>(data); + unsigned char* end = destination + width * height * 4; + const float* source = &values[0]; + while (destination < end) { + destination[0] = static_cast<unsigned char>( + source[swizzle[0]] * 255.0f); + destination[1] = static_cast<unsigned char>( + source[swizzle[1]] * 255.0f); + destination[2] = static_cast<unsigned char>( + source[swizzle[2]] * 255.0f); + destination[3] = (num_elements == 4) ? + static_cast<unsigned char>(source[swizzle[3]] * 255.0f) : 255; + destination += 4; + source += num_elements; + } + break; + } + } + if (!self->Unlock(level)) { + O3D_ERROR(self->service_locator()) << "could not unlock texture"; + } + } + %} +}; // Texture2D + + +%[ +TextureCUBE is a class for textures used for cube mapping. A cube texture +stores bitmaps for the 6 faces of a cube and is addressed via three texture +coordinates. +%] +[include="core/cross/texture.h"] class TextureCUBE : Texture { + %[ + \var CubeFace, + \li FACE_POSITIVE_X + \li FACE_NEGATIVE_X + \li FACE_POSITIVE_Y + \li FACE_NEGATIVE_Y + \li FACE_POSITIVE_Z + \li FACE_NEGATIVE_Z + + The names of each of the six faces of a cube map texture. + %] + enum CubeFace { FACE_POSITIVE_X, + FACE_NEGATIVE_X, + FACE_POSITIVE_Y, + FACE_NEGATIVE_Y, + FACE_POSITIVE_Z, + FACE_NEGATIVE_Z }; + + %[ + The length of each edge of the cube, in texels. + %] + [field_access=private, getter] int edge_length; + + %[ + Returns a RenderSurface object associated with a given cube face and + mip_level of a texture. + + \param face The cube face from which to extract the surface. + \param mip_level The mip-level of the surface to be returned. + \param pack: The pack in which the surface will reside. + \return The RenderSurface object. + %] + RenderSurface? GetRenderSurface(CubeFace face, int mip_level, Pack pack); +}; // TextureCUBE + +} // namespace o3d diff --git a/o3d/plugin/idl/tick_event.idl b/o3d/plugin/idl/tick_event.idl new file mode 100644 index 0000000..840b775 --- /dev/null +++ b/o3d/plugin/idl/tick_event.idl @@ -0,0 +1,49 @@ +/* + * 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 defines the public interface for the TickEvent class. + +namespace o3d { + +%[ + This class is used to pass infomation to a registered ontick callback. +%] +[binding_model=by_value, include="core/cross/tick_event.h", + nocpp,glue_iface] class TickEvent { + %[ + Use this property to get the elapsed time since the last tick event + seconds. + %] + [getter] float elapsed_time_; +}; + +} // namespace o3d diff --git a/o3d/plugin/idl/transform.idl b/o3d/plugin/idl/transform.idl new file mode 100644 index 0000000..da0887d --- /dev/null +++ b/o3d/plugin/idl/transform.idl @@ -0,0 +1,372 @@ +/* + * 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. + */ + +namespace o3d { + +typedef Shape[] ShapeArray; + +%[ + The Transform defines parent child relationship and a localMatrix.. + A Transform can have one or no parents and + an arbitrary number of children. +%] +[nocpp, include="core/cross/transform.h"] class Transform + : ParamObject { + %[ + The Visibility for this transform. + %] + [getter, setter] bool visible_; + + %[ + Sets the parent of the transform by re-parenting the transform under + parent. Setting parent to null removes the transform and the + entire subtree below it from the transform graph. + If the operation would create a cycle it fails. + %] + [setter, userglue_setter] Transform? parent_; + + %[ + The immediate children of this Transform. + + Each access to this field gets the entire list, so it is best to get it + just once. For example: + \code + var children = transform.children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + } + \endcode + + Note that modifications to this array [e.g. additions to it] will not affect + the underlying Transform, while modifications to the members of the array + <strong>will</strong> affect them. + %] + [userglue_getter, getter] TransformArray children_; + + %[ + Returns all the transforms under this transform including this one. + + Note that modifications to this array [e.g. additions to it] will not affect + the underlying Transform, while modifications to the members of the array + <strong>will</strong> affect them. + + \return An array containing the transforms of the subtree. + %] + TransformArray GetTransformsInTree(); + + %[ + Searches for transforms that match the given name in the hierarchy under and + including this transform. Since there can be more than one transform with a + given name, results are returned in an array. + + Note that modifications to this array [e.g. additions to it] will not affect + the underlying Transform, while modifications to the members of the array + <strong>will</strong> affect them. + + \param name Transform name to look for. + \return An array containing the transforms of the under and including this + transform matching the given name. + %] + TransformArray GetTransformsByNameInTree(String name); + + %[ + Evaluates and returns the current world matrix. + + \return The updated world matrix. + %] + Vectormath::Aos::Matrix4 GetUpdatedWorldMatrix(); + + %[ + Adds a shape do this transform. + \param shape Shape to add. + %] + void AddShape(Shape shape); + + %[ + Removes a shape from this transform. + \param shape Shape to remove. + \return true if successful, false if shape was not in this transform. + %] + bool RemoveShape(Shape shape); + + %[ + Gets the shapes owned by this transform. + + Each access to this field gets the entire list so it is best to get it + just once. For example: + \code + var shapes = transform.shapes; + for (var i = 0; i < shapes.length; i++) { + var shape = shapes[i]; + } + \endcode + + Note that modifications to this array [e.g. additions to it] will not affect + the underlying Transform, while modifications to the members of the array + <strong>will</strong> affect them. + %] + [userglue_getter, getter, userglue_setter, setter] + ShapeArray shapes_; + + %[ + Walks the tree of transforms starting with this transform and creates + draw elements. If an Element already has a DrawElement that uses material a + new DrawElement will not be created. + \param pack Pack used to manage created elements. + \param material Material to use for each element. If you pass null, it will + use the material on the element to which a draw element is being added. + In other words, a DrawElement will use the material of the + corresponding Element if material is null. This allows you to easily + setup the default (just draw as is) by passing null or setup a shadow + pass by passing in a shadow material. + %] + void CreateDrawElements(Pack pack, + Material? material); + + %[ + World (model) matrix as it was last computed. + %] + [getter] Vectormath::Aos::Matrix4 world_matrix; + + %[ + Local transformation matrix. + %] + [getter, setter, nocpp] Vectormath::Aos::Matrix4 local_matrix; + + %[ + The cull setting for this transform. If true this Transform will be culled + by having its bounding box compared to the view frustum of any draw context + it is used with. + %] + [getter, setter] bool cull_; + + %[ + The BoundingBox for this Transform. If culling is on this bounding box will + be tested against the view frustum of any draw context used to with this + Transform. + %] + [getter, setter] BoundingBox bounding_box_; + + %[ + Sets the local matrix of this transform to the identity matrix. + %] + [userglue] void identity(); + + %[ + Pre-composes the local matrix of this Transform with a translation. For + example, if the local matrix is a rotation then new local matrix will + translate by the given vector and then rotated. + + \param translation vector of 3 entries by which to translate. + %] + [userglue] void translate(Vectormath::Aos::Vector3 translation); + + %[ + Pre-composes the local matrix of this Transform with a translation. For + example, if the local matrix is a rotation then the new local matrix will + translate by the given amounts and then rotate. + + \param x amount to translate in x. + \param y amount to translate in y. + \param z amount to translate in z. + %] + [userglue] void translate(float x, float y, float z); + + %[ + Pre-composes the local matrix of this Transform with a rotation about the + x-axis. For example, if the local matrix is a tranlsation, the new local + matrix will rotate around the x-axis and then translate. + + \param radians The number of radians to rotate around x-axis. + %] + [userglue] void rotateX(float radians); + + %[ + Pre-composes the local matrix of this Transform with a rotation about the + y-axis. For example, if the local matrix is a translation, the new local + matrix will rotate around the y-axis and then translate. + + \param radians The number of radians to rotate around y-axis. + %] + [userglue] void rotateY(float radians); + + %[ + Pre-composes the local matrix of this Transform with a rotation about the + z-axis. For example, if the local matrix is a translation, the new local + matrix will rotate around the z-axis and then translate. + + \param radians The number of radians to rotate around z-axis. + %] + [userglue] void rotateZ(float radians); + + %[ + Pre-composes the local matrix of this Transform with a rotation. + Interprets the three entries of the given vector as angles by which to + rotate around the x, y and z axes. Rotates around the x-axis first, + then the y-axis, then the z-axis. + + \param radiansXYZ A vector of angles (in radians) by which to rotate + around the x, y and z axes. + %] + [userglue] void rotateZYX(Vectormath::Aos::Vector3 radiansXYZ); + + %[ + Pre-composes the local matrix of this Transform with a rotation around the + given axis. For example, if the local matrix is a translation, the new + local matrix will rotate around the given axis and then translate. + + \param radians The number of radians to rotate. + \param axis a non-zero vector representing the axis around which to rotate. + %] + [userglue] void axisRotate(Vectormath::Aos::Vector3 axis, float radians); + + %[ + Pre-composes the local matrix of this Transform with a rotation defined by + the given quaternion. + + \param unit_quat A non-zero quaternion to be interpreted as a rotation. + %] + [userglue] void quaternionRotate(Vectormath::Aos::Quat unit_quat); + + %[ + Pre-composes the local matrix of this transform by a scaling transformation. + For example, if the local matrix is a rotation, the new local matrix will + scale and then rotate. + + \param scale amount to scale. + %] + [userglue] void scale(Vectormath::Aos::Vector3 scale); + + %[ + Pre-composes the local matrix of this transform by a scaling transformation. + For example, if the local matrix is a rotation, the new local matrix will + scale and then rotate. + + \param x amount to scale in the x dimension. + \param y amount to scale in the y dimension. + \param z amount to scale in the z dimension. + %] + [userglue] void scale(float x, float y, float z); + + [verbatim=cpp_glue] %{ + void userglue_setter_parent_( + o3d::Transform* _this, + o3d::Transform* parent) { + _this->SetParent(parent); + } + o3d::TransformArray userglue_getter_children_( + o3d::Transform *self) { + return self->GetChildren(); + } + o3d::ShapeArray userglue_getter_shapes_( + o3d::Transform *self) { + return self->GetShapes(); + } + void userglue_setter_shapes_( + o3d::Transform *self, + const o3d::ShapeArray& shapes) { + self->SetShapes(shapes); + } + void userglue_method_identity(o3d::Transform* self) { + self->set_local_matrix(Vectormath::Aos::Matrix4::identity()); + } + void userglue_method_translate( + o3d::Transform* self, + const Vectormath::Aos::Vector3& translation) { + self->set_local_matrix( + self->local_matrix() * + Vectormath::Aos::Matrix4::translation(translation)); + } + void userglue_method_translate(o3d::Transform* self, + float x, + float y, + float z) { + self->set_local_matrix(self->local_matrix() * + Vectormath::Aos::Matrix4::translation( + Vectormath::Aos::Vector3(x, y, z))); + } + void userglue_method_rotateX(o3d::Transform* self, + float radians) { + self->set_local_matrix(self->local_matrix() * + Vectormath::Aos::Matrix4::rotationX(radians)); + } + void userglue_method_rotateY(o3d::Transform* self, + float radians) { + self->set_local_matrix(self->local_matrix() * + Vectormath::Aos::Matrix4::rotationY(radians)); + } + void userglue_method_rotateZ(o3d::Transform* self, + float radians) { + self->set_local_matrix(self->local_matrix() * + Vectormath::Aos::Matrix4::rotationZ(radians)); + } + void userglue_method_rotateZYX(o3d::Transform* self, + const Vectormath::Aos::Vector3& radiansXYZ) { + self->set_local_matrix(self->local_matrix() * + Vectormath::Aos::Matrix4::rotationZYX(radiansXYZ)); + } + void userglue_method_axisRotate(o3d::Transform* self, + const Vectormath::Aos::Vector3& axis, + float radians) { + self->set_local_matrix( + self->local_matrix() * + Vectormath::Aos::Matrix4::rotation(radians, + Vectormath::Aos::normalize(axis))); + } + void userglue_method_quaternionRotate(o3d::Transform* self, + const Vectormath::Aos::Quat& quat) { + self->set_local_matrix(self->local_matrix() * + Vectormath::Aos::Matrix4::rotation( + Vectormath::Aos::normalize(quat))); + } + void userglue_method_scale( + o3d::Transform* self, + const Vectormath::Aos::Vector3& scale) { + self->set_local_matrix(self->local_matrix() * + Vectormath::Aos::Matrix4::scale(scale)); + } + void userglue_method_scale(o3d::Transform* self, + float x, + float y, + float z) { + self->set_local_matrix( + self->local_matrix() * + Vectormath::Aos::Matrix4::scale(Vectormath::Aos::Vector3(x, y, z))); + } + %} +}; // Transform + +%[ + TransformArray is a typdef for an array of Transforms. +%] +typedef Transform[] TransformArray; + +} // namespace o3d diff --git a/o3d/plugin/idl/tree_traversal.idl b/o3d/plugin/idl/tree_traversal.idl new file mode 100644 index 0000000..a536f21 --- /dev/null +++ b/o3d/plugin/idl/tree_traversal.idl @@ -0,0 +1,71 @@ +/* + * 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. + */ + +namespace o3d { + +%[ + A TreeTraversal has multiple DrawLists registered with it. Each DrawList has + a DrawContext registered with it. At render time the TreeTraversal walks the + transform graph from the transform it's pointing at and for each DrawElement + it finds who's matertial matches one of its registered DrawLists it adds that + DrawElement to that DrawList. +%] +[nocpp, include="core/cross/tree_traversal.h"] class TreeTraversal + : RenderNode { + + %[ + The root Transform this TreeTraversal will start traversing from. + %] + [getter, setter] Transform? transform_; + + %[ + Registers a DrawList with this TreeTraversal so that when this + TreeTraversal traverses its tree, DrawElements using materials that use + this DrawList will be added though possibly culled by the view frustum of + the DrawContext. Note: passing in the same DrawList more than once will + override the previous settings for that DrawList. + \param draw_list DrawList to register. + \param draw_context DrawContext to use with the DrawList. + \param reset true if you want the DrawList reset before traversing. + %] + void RegisterDrawList(DrawList draw_list, + DrawContext draw_context, + bool reset); + + %[ + Unregisters a DrawList with this TreeTraversal. + \param draw_list DrawList to unregister. + \return true if unregistered. false if this draw_list was not registered. + %] + bool UnregisterDrawList(DrawList draw_list); +}; // TreeTraversal + +} // namespace o3d diff --git a/o3d/plugin/idl/types.idl b/o3d/plugin/idl/types.idl new file mode 100644 index 0000000..5756d9e --- /dev/null +++ b/o3d/plugin/idl/types.idl @@ -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. + */ + +// TODO: some how make this file conditionally compile different for C++ +// vs JavaScript like with #ifdef CPLUSPLUS etc.. +[verbatim=cpp_glue,include="plugin/cross/marshaling_utils.h"] +namespace o3d { + +%[ + A data type consisting of 2 numbers. A Float2 is represented in JavaScript by + an array containing 2 numbers: [x, y]. +%] +[binding_model=by_value, marshaled, nocpp, include="core/cross/types.h"] +class Float2 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + o3d::Float2* self, + const std::vector<float>& dynamicValue) { + *self = o3d::VectorToType<o3d::Float2, 2>( + plugin_data, dynamicValue); + } + + std::vector<float> userglue_getter_marshaled( + void* plugin_data, + const o3d::Float2& self) { + return o3d::VectorFromType<o3d::Float2, 2>(self); + } + %} +}; + +%[ + A data type consisting of 3 numbers. A Float3 is represented in JavaScript by + an array containing 3 numbers: [x, y, z]. +%] +[binding_model=by_value, marshaled, nocpp, include="core/cross/types.h"] +class Float3 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + o3d::Float3* self, + const std::vector<float>& dynamicValue) { + *self = o3d::VectorToType<o3d::Float3, 3>( + plugin_data, dynamicValue); + } + + std::vector<float> userglue_getter_marshaled( + void* plugin_data, + const o3d::Float3& self) { + return o3d::VectorFromType<o3d::Float3, 3>(self); + } + %} +}; + +%[ + A data type consisting of 4 numbers. A Float4 is represented in JavaScript by + an array containing 4 numbers: [x, y, z, w]. +%] +[binding_model=by_value, marshaled, nocpp, include="core/cross/types.h"] +class Float4 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + o3d::Float4* self, + const std::vector<float>& dynamicValue) { + *self = o3d::VectorToType<o3d::Float4, 4>( + plugin_data, dynamicValue); + } + + std::vector<float> userglue_getter_marshaled( + void* plugin_data, + const o3d::Float4& self) { + return o3d::VectorFromType<o3d::Float4, 4>(self); + } + %} +}; + +%[ + Id used to uniquely identify objects. +%] +[include="core/cross/types.h"] typedef int Id; + +%[ + Data type for passing data around in the scenegraph. +%] +[include="core/cross/types.h"] typedef float Float; + +%[ + Data type for passing data around in the scenegraph. +%] +[include="core/cross/types.h"] typedef std::string String; + +} // namespace o3d diff --git a/o3d/plugin/idl/vector.idl b/o3d/plugin/idl/vector.idl new file mode 100644 index 0000000..84dd4f1 --- /dev/null +++ b/o3d/plugin/idl/vector.idl @@ -0,0 +1,181 @@ +/* + * 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/cross/marshaling_utils.h"] %{ +%} + +namespace Vectormath { +namespace Aos { + +[binding_model=by_value, marshaled, nocpp, include= + "third_party/vectormath/files/vectormathlibrary/include/vectormath/scalar/cpp/vectormath_aos.h"] +class Vector3 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + Vectormath::Aos::Vector3* self, + const std::vector<float>& dynamicValue) { + *self = o3d::VectorToType<Vectormath::Aos::Vector3, 3>( + plugin_data, dynamicValue); + } + + std::vector<float> userglue_getter_marshaled( + void* plugin_data, + const Vectormath::Aos::Vector3& self) { + return + o3d::VectorFromType<Vectormath::Aos::Vector3, 3>(self); + } + %} +}; + +[binding_model=by_value, marshaled, nocpp, include= + "third_party/vectormath/files/vectormathlibrary/include/vectormath/scalar/cpp/vectormath_aos.h"] +class Point3 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + Vectormath::Aos::Point3* self, + const std::vector<float>& dynamicValue) { + *self = o3d::VectorToType<Vectormath::Aos::Point3, 3>( + plugin_data, dynamicValue); + } + + std::vector<float> userglue_getter_marshaled( + void* plugin_data, + const Vectormath::Aos::Point3& self) { + return o3d::VectorFromType<Vectormath::Aos::Point3, 3>(self); + } + %} +}; + +[binding_model=by_value, marshaled, nocpp, include= + "third_party/vectormath/files/vectormathlibrary/include/vectormath/scalar/cpp/vectormath_aos.h"] +class Vector4 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + Vectormath::Aos::Vector4* self, + const std::vector<float>& dynamicValue) { + *self = o3d::VectorToType<Vectormath::Aos::Vector4, 4>( + plugin_data, dynamicValue); + } + + std::vector<float> userglue_getter_marshaled( + void* plugin_data, + const Vectormath::Aos::Vector4& self) { + return + o3d::VectorFromType<Vectormath::Aos::Vector4, 4>(self); + } + %} +}; + +[binding_model=by_value, marshaled, nocpp, include= + "third_party/vectormath/files/vectormathlibrary/include/vectormath/scalar/cpp/vectormath_aos.h"] +class Matrix3 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[][] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + Vectormath::Aos::Matrix3* self, + const std::vector<std::vector<float> >& dynamicValue) { + *self = o3d::VectorOfVectorToType<Vectormath::Aos::Matrix3, 3, 3>( + plugin_data, dynamicValue); + } + + std::vector<std::vector<float> > userglue_getter_marshaled( + void* plugin_data, + const Vectormath::Aos::Matrix3& self) { + return o3d::VectorOfVectorFromType<Vectormath::Aos::Matrix3, 3, 3>( + self); + } + %} +}; + +[binding_model=by_value, marshaled, nocpp, include= + "third_party/vectormath/files/vectormathlibrary/include/vectormath/scalar/cpp/vectormath_aos.h"] +class Matrix4 { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[][] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + Vectormath::Aos::Matrix4* self, + const std::vector<std::vector<float> >& dynamicValue) { + *self = o3d::VectorOfVectorToType<Vectormath::Aos::Matrix4, 4, 4>( + plugin_data, dynamicValue); + } + + std::vector<std::vector<float> > userglue_getter_marshaled( + void* plugin_data, + const Vectormath::Aos::Matrix4& self) { + return o3d::VectorOfVectorFromType<Vectormath::Aos::Matrix4, 4, 4>( + self); + } + %} +}; + +[binding_model=by_value, marshaled, nocpp, include= + "third_party/vectormath/files/vectormathlibrary/include/vectormath/scalar/cpp/vectormath_aos.h"] +class Quat { + [plugin_data, userglue_getter, userglue_setter, getter, setter, nodocs] + float[] marshaled; + + [verbatim=cpp_glue] %{ + void userglue_setter_marshaled( + void* plugin_data, + Vectormath::Aos::Quat* self, + const std::vector<float>& dynamicValue) { + *self = o3d::VectorToType<Vectormath::Aos::Quat, 4>( + plugin_data, dynamicValue); + } + + std::vector<float> userglue_getter_marshaled( + void* plugin_data, + const Vectormath::Aos::Quat& self) { + return o3d::VectorFromType<Vectormath::Aos::Quat, 4>(self); + } + %} +}; + +} +} diff --git a/o3d/plugin/idl/vertex_source.idl b/o3d/plugin/idl/vertex_source.idl new file mode 100644 index 0000000..92cfe58 --- /dev/null +++ b/o3d/plugin/idl/vertex_source.idl @@ -0,0 +1,70 @@ +/* + * 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 idlglue declaration of VertexSource. + +namespace o3d { + +%[ + A VertexSource is an object that allows binding Streams such that the + VertexSource updates the Buffers of the Streams that have been bound to it. + An example of a VertexSource object is a SkinEval +%] +[nocpp, include="core/cross/vertex_source.h"] class VertexSource + : ParamObject { + + %[ + Bind the source stream to the corresponding stream in this VertexSource. + + \param source Source to get vertices from. + \param semantic The semantic of the vertices to get + \param semantic_index The semantic index of the vertices to get. + \return True if success. False if failure. If the requested semantic or + semantic index do not exist on the source or this source the bind will + fail. + %] + bool BindStream(VertexSource source, + Stream::Semantic semantic, + int semantic_index); + + %[ + Unbinds the requested stream. + \param semantic The semantic of the vertices to unbind + \param semantic_index The semantic index of the vertices to unbind. + \return True if unbound. False those vertices do not exist or were not + bound. + %] + bool UnbindStream(Stream::Semantic semantic, int semantic_index); +}; // VertexSource + +} // namespace o3d + diff --git a/o3d/plugin/idl/viewport.idl b/o3d/plugin/idl/viewport.idl new file mode 100644 index 0000000..6e28be6 --- /dev/null +++ b/o3d/plugin/idl/viewport.idl @@ -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. + */ + +namespace o3d { + +%[ + A Viewport is a render node that sets the render viewport and depth range for + its children. It uses an array in the format [left, top, width, height] where + left, top, width and height are in a 0.0 to 1.0 range that represent positions + and dimensions relative to the size of the client's rendering area. The depth + range is represented by an array in the format [min Z, max Z]. The depth range + provides the mapping of the clip space coordinates into normalized z buffer + coordinates. +%] +[nocpp, include="core/cross/viewport.h"] class Viewport + : RenderNode { + %[ + The position and size to set the viewport in + [left, top, width, height] format. The default values + are (0.0, 0.0, 1.0, 1.0). In other words, the full area. + + Note: These values must describe a rectangle that is 100% inside the client + area. In other words, (0.5, 0.0, 1.0, 1.0) would describe an area that is + 1/2 off right side of the screen. That is an invalid value and will be + clipped to (0.5, 0.0, 0.5, 1.0). + + \sa Viewport + %] + [getter, setter] Float4 viewport; + + %[ + The min Z and max Z depth range in [min Z, max Z] format. + The default values are [0.0, 1.0]. + + \sa Viewport + %] + [getter, setter] Float2 depth_range; +}; // Viewport + +} // namespace o3d diff --git a/o3d/plugin/idl_list.scons b/o3d/plugin/idl_list.scons new file mode 100644 index 0000000..45eb9d3 --- /dev/null +++ b/o3d/plugin/idl_list.scons @@ -0,0 +1,91 @@ +# 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. + + +O3D_IDL_SOURCES = [ + 'idl/bounding_box.idl', + 'idl/buffer.idl', + 'idl/canvas.idl', + 'idl/canvas_paint.idl', + 'idl/canvas_shader.idl', + 'idl/clear_buffer.idl', + 'idl/client.idl', + 'idl/counter.idl', + 'idl/cursor.idl', + 'idl/curve.idl', + 'idl/display_mode.idl', + 'idl/draw_context.idl', + 'idl/draw_element.idl', + 'idl/draw_list.idl', + 'idl/draw_pass.idl', + 'idl/effect.idl', + 'idl/element.idl', + 'idl/event.idl', + 'idl/field.idl', + 'idl/file_request.idl', + 'idl/function.idl', + 'idl/material.idl', + 'idl/matrix4_axis_rotation.idl', + 'idl/matrix4_composition.idl', + 'idl/matrix4_scale.idl', + 'idl/matrix4_translation.idl', + 'idl/named.idl', + 'idl/pack.idl', + 'idl/param_array.idl', + 'idl/param.idl', + 'idl/param_object.idl', + 'idl/param_operation.idl', + 'idl/plugin.idl', + 'idl/primitive.idl', + 'idl/ray_intersection_info.idl', + 'idl/render_event.idl', + 'idl/render_node.idl', + 'idl/render_surface.idl', + 'idl/render_surface_set.idl', + 'idl/sampler.idl', + 'idl/shape.idl', + 'idl/skin.idl', + 'idl/standard_param.idl', + 'idl/state.idl', + 'idl/state_set.idl', + 'idl/stream.idl', + 'idl/stream_bank.idl', + 'idl/texture.idl', + 'idl/tick_event.idl', + 'idl/transform.idl', + 'idl/tree_traversal.idl', + 'idl/types.idl', + 'idl/vector.idl', + 'idl/vertex_source.idl', + 'idl/viewport.idl', + 'idl/archive_request.idl', + 'idl/raw_data.idl', +] + +Export('O3D_IDL_SOURCES') diff --git a/o3d/plugin/linux/config.cc b/o3d/plugin/linux/config.cc new file mode 100644 index 0000000..ce1dca5 --- /dev/null +++ b/o3d/plugin/linux/config.cc @@ -0,0 +1,62 @@ +/* + * 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 code to check the hardware and software configuration of +// the client machine: +// - User agent (browser) +// - OS version +// - GPU vendor + +#include "plugin/cross/config.h" + +#include <iostream> +#include <fstream> + +// Checks OS version +bool CheckOSVersion(NPP npp) { + return true; +} + +bool CheckUserAgent(NPP npp, const std::string &user_agent) { + // TODO: check user agent. + return true; +} + +bool OpenDriverBlacklistFile(std::ifstream *input_file) { + // TODO + return false; +} + +bool GetUserConfigMetrics() { + // TODO: Finish this function! + return true; +} diff --git a/o3d/plugin/linux/main_linux.cc b/o3d/plugin/linux/main_linux.cc new file mode 100644 index 0000000..7168333 --- /dev/null +++ b/o3d/plugin/linux/main_linux.cc @@ -0,0 +1,522 @@ +/* + * 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 implements the platform specific parts of the plugin for +// the Linux platform. + +#include <X11/keysym.h> +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "plugin/cross/main.h" +#include "plugin/cross/out_of_memory.h" + +using glue::_o3d::PluginObject; +using glue::StreamManager; +using o3d::DisplayWindowLinux; +using o3d::Event; + +namespace { +// We would normally make this a stack variable in main(), but in a +// plugin, that's not possible, so we allocate it dynamically and +// destroy it explicitly. +scoped_ptr<base::AtExitManager> g_at_exit_manager; +} // end anonymous namespace + +static void DrawPlugin(PluginObject *obj) { + // TODO: this draws no matter what instead of just + // invalidating the region, which means it will execute even if the plug-in + // window is invisible. Figure out a way to prevent that, possibly going + // through the XEmbed extension. + // Limit drawing to no more than once every timer tick. + if (!obj->draw_) return; + obj->client()->RenderClient(); + obj->draw_ = false; +} + +void RenderOnDemandCallbackHandler::Run() { + DrawPlugin(obj_); +} + +void LinuxTimer(XtPointer data, XtIntervalId* id) { + PluginObject *obj = static_cast<PluginObject *>(data); + DCHECK(obj->xt_interval_ == *id); + obj->client()->Tick(); + obj->draw_ = true; + if (obj->client()->render_mode() == o3d::Client::RENDERMODE_CONTINUOUS) { + DrawPlugin(obj); + } + obj->xt_interval_ = + XtAppAddTimeOut(obj->xt_app_context_, 10, LinuxTimer, obj); +} + +void LinuxExposeHandler(Widget w, + XtPointer user_data, + XEvent *event, + Boolean *cont) { + PluginObject *obj = static_cast<PluginObject *>(user_data); + if (event->type != Expose) return; + XExposeEvent *expose_event = &event->xexpose; + DrawPlugin(obj); +} + +static int KeySymToDOMKeyCode(KeySym key_sym) { + // See https://developer.mozilla.org/en/DOM/Event/UIEvent/KeyEvent for the + // DOM values. + // X keycodes are not useful, because they describe the geometry, not the + // associated symbol, so a 'Q' on a QWERTY (US) keyboard has the same keycode + // as a 'A' on an AZERTY (french) one. + // Key symbols are closer to what the DOM expects, but they depend on the + // shift/control/alt combination - the same key has several symbols ('a' vs + // 'A', '1' vs '!', etc.), so we do extra work so that 'a' and 'A' both + // generate the same DOM keycode. + if ((key_sym >= XK_0 && key_sym <= XK_Z)) { + // DOM keycode matches ASCII value, as does the keysym. + return key_sym; + } else if ((key_sym >= XK_a && key_sym <= XK_z)) { + return key_sym - XK_a + XK_A; + } else if ((key_sym >= XK_KP_0 && key_sym <= XK_KP_9)) { + return 0x60 + key_sym - XK_KP_0; + } else if ((key_sym >= XK_F1 && key_sym <= XK_F24)) { + return 0x70 + key_sym - XK_F1; + } + switch (key_sym) { + case XK_Cancel: + return 0x03; + case XK_Help: + return 0x06; + case XK_BackSpace: + return 0x08; + case XK_Tab: + return 0x09; + case XK_Clear: + return 0x0C; + case XK_Return: + return 0x0D; + case XK_KP_Enter: + return 0x0E; + case XK_Shift_L: + case XK_Shift_R: + return 0x10; + case XK_Control_L: + case XK_Control_R: + return 0x11; + case XK_Alt_L: + case XK_Alt_R: + return 0x12; + case XK_Pause: + return 0x13; + case XK_Caps_Lock: + return 0x14; + case XK_Escape: + return 0x1B; + case XK_space: + return 0x20; + case XK_Page_Up: + case XK_KP_Page_Up: + return 0x21; + case XK_Page_Down: + case XK_KP_Page_Down: + return 0x22; + case XK_End: + case XK_KP_End: + return 0x23; + case XK_Home: + case XK_KP_Home: + return 0x24; + case XK_Left: + case XK_KP_Left: + return 0x25; + case XK_Up: + case XK_KP_Up: + return 0x26; + case XK_Right: + case XK_KP_Right: + return 0x27; + case XK_Down: + case XK_KP_Down: + return 0x28; + case XK_Print: + return 0x2C; + case XK_Insert: + case XK_KP_Insert: + return 0x2D; + case XK_Delete: + case XK_KP_Delete: + return 0x2E; + case XK_Menu: + return 0x5D; + case XK_asterisk: + case XK_KP_Multiply: + return 0x6A; + case XK_plus: + case XK_KP_Add: + return 0x6B; + case XK_underscore: + return 0x6C; + case XK_minus: + case XK_KP_Subtract: + return 0x6E; + case XK_KP_Decimal: + return 0x6E; + case XK_KP_Divide: + return 0x6F; + case XK_Num_Lock: + return 0x90; + case XK_Scroll_Lock: + return 0x91; + case XK_comma: + return 0xBC; + case XK_period: + return 0xBE; + case XK_slash: + return 0xBF; + case XK_grave: + return 0xC0; + case XK_bracketleft: + return 0xDB; + case XK_backslash: + return 0xDC; + case XK_bracketright: + return 0xDD; + case XK_apostrophe: + return 0xDE; + case XK_Meta_L: + case XK_Meta_R: + return 0xE0; + default: + return 0; + } +} + +static int GetModifierState(int x_state) { + int modifier_state = 0; + if (x_state & ControlMask) { + modifier_state |= Event::MODIFIER_CTRL; + } + if (x_state & ShiftMask) { + modifier_state |= Event::MODIFIER_SHIFT; + } + if (x_state & Mod1Mask) { + modifier_state |= Event::MODIFIER_ALT; + } + if (x_state & Mod2Mask) { + modifier_state |= Event::MODIFIER_META; + } + return modifier_state; +} + +void LinuxKeyHandler(Widget w, + XtPointer user_data, + XEvent *xevent, + Boolean *cont) { + PluginObject *obj = static_cast<PluginObject *>(user_data); + XKeyEvent *key_event = &xevent->xkey; + Event::Type type; + switch (xevent->type) { + case KeyPress: + type = Event::TYPE_KEYDOWN; + break; + case KeyRelease: + type = Event::TYPE_KEYUP; + break; + default: + return; + } + Event event(type); + char char_code = 0; + KeySym key_sym; + int result = XLookupString(key_event, &char_code, sizeof(char_code), + &key_sym, NULL); + event.set_key_code(KeySymToDOMKeyCode(key_sym)); + int modifier_state = GetModifierState(key_event->state); + event.set_modifier_state(modifier_state); + obj->client()->AddEventToQueue(event); + if (xevent->type == KeyPress && result > 0) { + event.clear_key_code(); + event.set_char_code(char_code); + event.set_type(Event::TYPE_KEYPRESS); + obj->client()->AddEventToQueue(event); + } +} + +// TODO: Any way to query the system for the correct value ? +const unsigned int kDoubleClickTime = 300; // in ms + +void LinuxMouseButtonHandler(Widget w, + XtPointer user_data, + XEvent *xevent, + Boolean *cont) { + PluginObject *obj = static_cast<PluginObject *>(user_data); + XButtonEvent *button_event = &xevent->xbutton; + Event::Type type; + switch (xevent->type) { + case ButtonPress: + type = Event::TYPE_MOUSEDOWN; + break; + case ButtonRelease: + type = Event::TYPE_MOUSEUP; + break; + default: + return; + } + Event event(type); + switch (button_event->button) { + case 1: + event.set_button(Event::BUTTON_LEFT); + break; + case 2: + event.set_button(Event::BUTTON_MIDDLE); + break; + case 3: + event.set_button(Event::BUTTON_RIGHT); + break; + case 4: + case 5: + // Mouse wheel. 4 is up, 5 is down. Reported by X as Press/Release. + // Ignore the Press, report the Release as the wheel event. + if (xevent->type == ButtonPress) + return; + event.set_type(Event::TYPE_WHEEL); + event.set_delta(0, (button_event->button == 4) ? 1 : -1); + break; + default: + return; + } + int modifier_state = GetModifierState(button_event->state); + event.set_modifier_state(modifier_state); + event.set_position(button_event->x, button_event->y, + button_event->x_root, button_event->y_root, + obj->in_plugin()); + obj->client()->AddEventToQueue(event); + if (event.type() == Event::TYPE_MOUSEUP && obj->in_plugin()) { + event.set_type(Event::TYPE_CLICK); + obj->client()->AddEventToQueue(event); + if (button_event->time < obj->last_click_time() + kDoubleClickTime) { + obj->set_last_click_time(0); + event.set_type(Event::TYPE_DBLCLICK); + } else { + obj->set_last_click_time(button_event->time); + } + } +} + +void LinuxMouseMoveHandler(Widget w, + XtPointer user_data, + XEvent *xevent, + Boolean *cont) { + PluginObject *obj = static_cast<PluginObject *>(user_data); + if (xevent->type != MotionNotify) + return; + XMotionEvent *motion_event = &xevent->xmotion; + Event event(Event::TYPE_MOUSEMOVE); + int modifier_state = GetModifierState(motion_event->state); + event.set_modifier_state(modifier_state); + event.set_position(motion_event->x, motion_event->y, + motion_event->x_root, motion_event->y_root, + obj->in_plugin()); + obj->client()->AddEventToQueue(event); +} + +void LinuxEnterLeaveHandler(Widget w, + XtPointer user_data, + XEvent *xevent, + Boolean *cont) { + PluginObject *obj = static_cast<PluginObject *>(user_data); + switch (xevent->type) { + case EnterNotify: + obj->set_in_plugin(true); + break; + case LeaveNotify: + obj->set_in_plugin(false); + break; + default: + return; + } +} + +// TODO: Where should this really live? It's platform-specific, but in +// PluginObject, which mainly lives in cross/o3d_glue.h+cc. +bool PluginObject::RequestFullscreenDisplay() { +#ifndef NDEBUG // TODO: Remove after user-prompt feature goes in. +#endif + return false; +} + +void PluginObject::CancelFullscreenDisplay() { +#ifndef NDEBUG // TODO: Remove after user-prompt feature goes in. +#endif +} + +extern "C" { + NPError InitializePlugin() { + if (!o3d::SetupOutOfMemoryHandler()) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + // Initialize the AtExitManager so that base singletons can be + // destroyed properly. + g_at_exit_manager.reset(new base::AtExitManager()); + + CommandLine::Init(0, NULL); + InitLogging("debug.log", + logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG, + logging::DONT_LOCK_LOG_FILE, + logging::APPEND_TO_OLD_LOG_FILE); + + DLOG(INFO) << "NP_Initialize"; + + return NPERR_NO_ERROR; + } + + NPError OSCALL NP_Initialize(NPNetscapeFuncs *browserFuncs, + NPPluginFuncs *pluginFuncs) { + NPError retval = InitializeNPNApi(browserFuncs); + if (retval != NPERR_NO_ERROR) return retval; + NP_GetEntryPoints(pluginFuncs); + return InitializePlugin(); + } + + NPError OSCALL NP_Shutdown(void) { + HANDLE_CRASHES; + DLOG(INFO) << "NP_Shutdown"; + + CommandLine::Terminate(); + + // Force all base singletons to be destroyed. + g_at_exit_manager.reset(NULL); + + return NPERR_NO_ERROR; + } + + NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, + char *argn[], char *argv[], NPSavedData *saved) { + HANDLE_CRASHES; + + PluginObject* pluginObject = glue::_o3d::PluginObject::Create( + instance); + instance->pdata = pluginObject; + glue::_o3d::InitializeGlue(instance); + pluginObject->Init(argc, argn, argv); + + // Get the metrics for the system setup + GetUserConfigMetrics(); + return NPERR_NO_ERROR; + } + + NPError NPP_Destroy(NPP instance, NPSavedData **save) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + if (obj) { + if (obj->xt_widget_) { + // NOTE: This crashes. Not sure why, possibly the widget has + // already been destroyed, but we haven't received a SetWindow(NULL). + // XtRemoveEventHandler(obj->xt_widget_, ExposureMask, False, + // LinuxExposeHandler, obj); + obj->xt_widget_ = NULL; + } + if (obj->xt_interval_) { + XtRemoveTimeOut(obj->xt_interval_); + obj->xt_interval_ = 0; + } + obj->window_ = 0; + obj->display_ = NULL; + + obj->TearDown(); + NPN_ReleaseObject(obj); + instance->pdata = NULL; + } + + return NPERR_NO_ERROR; + } + + + NPError NPP_SetWindow(NPP instance, NPWindow *window) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + + NPSetWindowCallbackStruct *cb_struct = + static_cast<NPSetWindowCallbackStruct *>(window->ws_info); + Window xwindow = reinterpret_cast<Window>(window->window); + if (xwindow != obj->window_) { + Display *display = cb_struct->display; + Widget widget = XtWindowToWidget(display, xwindow); + if (!widget) { + DLOG(ERROR) << "window is not a Widget"; + return NPERR_MODULE_LOAD_FAILED_ERROR; + } + + // Create and assign the graphics context. + o3d::DisplayWindowLinux default_display; + default_display.set_display(display); + default_display.set_window(xwindow); + + obj->CreateRenderer(default_display); + obj->client()->Init(); + obj->client()->SetRenderOnDemandCallback( + new RenderOnDemandCallbackHandler(obj)); + obj->display_ = display; + obj->window_ = xwindow; + obj->xt_widget_ = widget; + XtAddEventHandler(widget, ExposureMask, 0, LinuxExposeHandler, obj); + XtAddEventHandler(widget, KeyPressMask|KeyReleaseMask, 0, + LinuxKeyHandler, obj); + XtAddEventHandler(widget, ButtonPressMask|ButtonReleaseMask, 0, + LinuxMouseButtonHandler, obj); + XtAddEventHandler(widget, PointerMotionMask, 0, + LinuxMouseMoveHandler, obj); + XtAddEventHandler(widget, EnterWindowMask|LeaveWindowMask, 0, + LinuxEnterLeaveHandler, obj); + obj->xt_app_context_ = XtWidgetToApplicationContext(widget); + obj->xt_interval_ = + XtAppAddTimeOut(obj->xt_app_context_, 10, LinuxTimer, obj); + } + obj->Resize(window->width, window->height); + + return NPERR_NO_ERROR; + } + + // Called when the browser has finished attempting to stream data to + // a file as requested. If fname == NULL the attempt was not successful. + void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + StreamManager *stream_manager = obj->stream_manager(); + + stream_manager->SetStreamFile(stream, fname); + } + + int16 NPP_HandleEvent(NPP instance, void *event) { + HANDLE_CRASHES; + return 0; + } +}; // end extern "C" diff --git a/o3d/plugin/mac/Info.plist b/o3d/plugin/mac/Info.plist new file mode 100644 index 0000000..78ae8e6 --- /dev/null +++ b/o3d/plugin/mac/Info.plist @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleName</key> + <string>O3D Plugin</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>o3d</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.google.o3d</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>BRPL</string> + <key>CFBundleSignature</key> + <string>TEST</string> + <key>CFBundleVersion</key> + <string>@@@ProductVersion@@@</string> + <key>CFBundleShortVersionString</key> + <string>@@@ProductVersion@@@</string> + <key>CCFBundleGetInfoString</key> + <string>"Version @@@ProductVersion@@@, Copyright 2009, Google Inc."</string> + <key>CSResourcesFileMapped</key> + <true/> + <key>WebPluginDescription</key> + <string>@@@PluginDescription@@@</string> + <key>WebPluginMIMETypes</key> + <dict> + <key>@@@PluginMimeType@@@</key> + <dict> + <key>WebPluginTypeDescription</key> + <string>@@@PluginName@@@ Type</string> + </dict> + </dict> + <key>WebPluginName</key> + <string>@@@PluginName@@@</string> + + <key>BreakpadProduct</key> + <string>O3D</string> + <key>BreakpadProductDisplay</key> + <string>O3D browser plugin</string> + <key>BreakpadVendor</key> + <string>Google</string> + <key>BreakpadVersion</key> + <string>@@@ProductVersion@@@ (mac)</string> + <key>BreakpadSendAndExit</key> + <string>1</string> + <key>BreakpadReportInterval</key> + <string>3600</string> + <key>BreakpadURL</key> + <string>https://clients2.google.com/cr</string> + +</dict> +</plist> diff --git a/o3d/plugin/mac/Resources/English.lproj/InfoPlist.strings b/o3d/plugin/mac/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..5cfd33d --- /dev/null +++ b/o3d/plugin/mac/Resources/English.lproj/InfoPlist.strings @@ -0,0 +1,10 @@ +/* + File: InfoPlist.strings + + Description:Finder Bundle Information + + Localized versions of Info.plist keys */ + +CFBundleName = "O3D Plugin"; + +NSHumanReadableCopyright = "Copyright 2009, Google, Inc."; diff --git a/o3d/plugin/mac/Tools/fix_install_names.sh b/o3d/plugin/mac/Tools/fix_install_names.sh new file mode 100644 index 0000000..aae5c56 --- /dev/null +++ b/o3d/plugin/mac/Tools/fix_install_names.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# Since the web-plugin is not the main executable, we need to change +# the @executable_path reference to @loader_path for our embedded frameworks +# + +# First take care of Breakpad.framework +/usr/bin/install_name_tool -change \ + @executable_path/../Frameworks/Breakpad.framework/Versions/A/Breakpad \ + @loader_path/../Frameworks/Breakpad.framework/Versions/A/Breakpad \ + "$1" + +# Cg.framework +/usr/bin/install_name_tool -change \ + @executable_path/../Library/Frameworks/Cg.framework/Cg \ + @loader_path/../Frameworks/Cg.framework/Cg \ + "$1" diff --git a/o3d/plugin/mac/config_mac.mm b/o3d/plugin/mac/config_mac.mm new file mode 100644 index 0000000..e5673b8 --- /dev/null +++ b/o3d/plugin/mac/config_mac.mm @@ -0,0 +1,330 @@ +/* + * 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 code to check the hardware and software configuration of +// the client machine: +// - User agent (browser) +// - OS version +// - GPU vendor + +#include <IOKit/IOKitLib.h> + +#include <iostream> +#include <fstream> + +#include "plugin/cross/config.h" +#include "plugin/cross/plugin_metrics.h" +#include "plugin_mac.h" + + +// Trivial little functions to check for the OS version boundaries we care about +// and keep the result cached so they are cheap to call repeatedly. + + +// Returns whether OS is 10.4 (Tiger) or higher. +static bool IsMacOSTenFourOrHigher() { + static bool isCached = false, result = false; + if (!isCached) { + SInt32 major = 0, minor = 0; + // These selectors don't exist pre 10.4 but as we check the error + // the function will correctly return NO which is the right answer. + result = ((Gestalt(gestaltSystemVersionMajor, &major) == noErr) && + (Gestalt(gestaltSystemVersionMinor, &minor) == noErr) && + ((major > 10) || (major == 10 && minor >= 4))); + isCached = true; + } + return result; +} + + +// Checks OS version +bool CheckOSVersion(NPP npp) { + // TODO: turn this back on when ready. + if (!IsMacOSTenFourOrHigher()) { + std::string error = + std::string("Unsupported Mac OS X version. 10.4 is required."); + return AskUser(npp, error); + } + return true; +} + +bool CheckUserAgent(NPP npp, const std::string &user_agent) { + return true; +} + +bool OpenDriverBlacklistFile(std::ifstream *input_file) { + // TODO: + return false; +} + + +static CFTypeRef SearchPortForProperty(io_registry_entry_t dspPort, + CFStringRef propertyName) { + return IORegistryEntrySearchCFProperty(dspPort, + kIOServicePlane, + propertyName, + kCFAllocatorDefault, + kIORegistryIterateRecursively | + kIORegistryIterateParents); +} + + +static void CFReleaseIf(CFTypeRef d) { + if (d) + CFRelease(d); +} + + +static UInt32 IntValueOfCFData(CFDataRef d) { + UInt32 value = 0; + + if (d) { + const UInt32 *vp = reinterpret_cast<const UInt32*>(CFDataGetBytePtr(d)); + if (vp != NULL) + value = *vp; + } + + return value; +} + + +static int IntValueOfCFNumber(CFNumberRef n) { + int value = 0; + + if ((n != NULL) && (CFGetTypeID(n) == CFNumberGetTypeID())) + CFNumberGetValue(n, kCFNumberSInt32Type, &value); + + return value; +} + + +static void GetVideoCardInfo(CGDirectDisplayID displayID, + int *vendorID, + int *deviceID, + int *vramSize) { + io_registry_entry_t dspPort = CGDisplayIOServicePort(displayID); + + CFTypeRef vendorIDRef = SearchPortForProperty(dspPort, CFSTR("vendor-id")); + if (vendorID) *vendorID = IntValueOfCFData((CFDataRef)vendorIDRef); + + CFTypeRef deviceIDRef = SearchPortForProperty(dspPort, CFSTR("device-id")); + if (deviceID) *deviceID = IntValueOfCFData((CFDataRef)deviceIDRef); + + CFTypeRef typeCodeRef = SearchPortForProperty(dspPort, + CFSTR(kIOFBMemorySizeKey)); + if (vramSize) *vramSize = IntValueOfCFNumber((CFNumberRef)typeCodeRef); + + CFReleaseIf(vendorIDRef); + CFReleaseIf(deviceIDRef); + CFReleaseIf(typeCodeRef); +} + +struct GPUInfo { + int vendorID; + int deviceID; +}; + +// A list of GPUs which we know will not work well in o3d +// We want to fallback to using the software render for these +GPUInfo softwareRenderList[] = { + {0x8086, 0x2a02}, // Intel GMA X3100 Macbook + {0x8086, 0x27a2} // Intel GMA 950 Mac Mini +}; + +bool UseSoftwareRenderer() { + static bool use_software_renderer = false; + static bool is_initialized = false; + + if (!is_initialized) { + int vendorID; + int deviceID; + GetVideoCardInfo(kCGDirectMainDisplay, + &vendorID, + &deviceID, + NULL); + + use_software_renderer = false; + for (int i = 0; i < arraysize(softwareRenderList); ++i) { + GPUInfo &softwareRenderInfo = softwareRenderList[i]; + if (vendorID == softwareRenderInfo.vendorID + && deviceID == softwareRenderInfo.deviceID) { + use_software_renderer = true; + break; + } + } + is_initialized = true; + } + + return use_software_renderer; +} + +static bool GetVideoCardMetrics(CGDirectDisplayID displayID) { + int vendorID; + int deviceID; + int vramSize; + GetVideoCardInfo(displayID, + &vendorID, + &deviceID, + &vramSize); + + o3d::metric_gpu_vendor_id = vendorID; + o3d::metric_gpu_device_id = deviceID; + o3d::metric_gpu_vram_size = vramSize; + + return true; +} + + +// Return a pointer to the last character with value c in string s. +// Returns NULL if c is not found. +static char* FindLastChar(char *s, char c) { + char *s_found = NULL; + + while (*s != '\0') { + if (*s == c) + s_found = s; + s++; + } + + return s_found; +} + + +bool GetOpenGLMetrics() { + char *gl_version_string = (char*)glGetString(GL_VERSION); + char *gl_extensions_string = (char*)glGetString(GL_EXTENSIONS); + + if ((gl_version_string == NULL) || (gl_extensions_string == NULL)) + return false; + + // Get the OpenGL version from the start of the string. + int gl_major = 0, gl_minor = 0; + sscanf(gl_version_string, "%u.%u", &gl_major, &gl_minor); + o3d::metric_gl_major_version = gl_major; + o3d::metric_gl_minor_version = gl_minor; + + // Get the OpenGL driver version. + // This bit is Mac specific - Mac OpenGL drivers have the driver version + // at the end of the gl version string preceded by a dash. + char *s = FindLastChar(gl_version_string, '-'); + if (s) { + char *driver_string = s + 1; // skip '-' + int driver_major = 0, driver_minor = 0, driver_bugfix = 0; + sscanf(driver_string, "%u.%u.%u", + &driver_major, &driver_minor, &driver_bugfix); + o3d::metric_gpu_driver_major_version = driver_major; + o3d::metric_gpu_driver_minor_version = driver_minor; + o3d::metric_gpu_driver_bugfix_version = driver_bugfix; + } + + // Get the HLSL version. + // On OpenGL 1.x it's 1.0 if the GL_ARB_shading_language_100 extension is + // present. + // On OpenGL 2.x it's a matter of getting the GL_SHADING_LANGUAGE_VERSION + // string. + int gl_hlsl_major = 0, gl_hlsl_minor = 0; + if ((gl_major == 1) && + strstr(gl_extensions_string, "GL_ARB_shading_language_100")) { + gl_hlsl_major = 1; + gl_hlsl_minor = 0; + } else if (gl_major >= 2) { + char* glsl_version_string = (char*)glGetString(GL_SHADING_LANGUAGE_VERSION); + if (glsl_version_string) { + sscanf(glsl_version_string, "%u.%u", &gl_hlsl_major, &gl_hlsl_minor); + } + } + o3d::metric_gl_hlsl_major_version = gl_hlsl_major; + o3d::metric_gl_hlsl_minor_version = gl_hlsl_minor; + + return true; +} + + +bool GetUserConfigMetrics() { + // Check Mac version. + o3d::metric_system_type = o3d::SYSTEM_NAME_MAC; + + SInt32 major = 0, minor = 0, bugfix = 0; + + Gestalt(gestaltSystemVersionMajor, &major); + Gestalt(gestaltSystemVersionMinor, &minor); + Gestalt(gestaltSystemVersionBugFix, &bugfix); + + o3d::metric_mac_major_version = major; // eg 10 + o3d::metric_mac_minor_version = minor; // eg 5 + o3d::metric_mac_bugfix_version = bugfix; // eg 6 + + o3d::metric_direct3d_available.Set(false); + + GetVideoCardMetrics(kCGDirectMainDisplay); + + return true; +} + + +bool GetUserAgentMetrics(NPP npp) { + // Check User agent so we can get the browser + // TODO: This is the best we could come up with for this in order to + // go from browser to string. + GLUE_PROFILE_START(npp, "uagent"); + std::string user_agent = NPN_UserAgent(npp); + GLUE_PROFILE_STOP(npp, "uagent"); + // The Chrome user_agent string also contains Safari. Search for Chrome first. + if (std::string::npos != user_agent.find("Chrome")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_CHROME; + // The OmniWeb user_agent also contains Safari. Search for OminWeb first. + } else if (std::string::npos != user_agent.find("OmniWeb")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_OMNIWEB; + // now we can safely look for Safari + } else if (std::string::npos != user_agent.find("Safari")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_SAFARI; + } else if (std::string::npos != user_agent.find("Opera")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_OPERA; + } else if (std::string::npos != user_agent.find("Firefox")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_FIREFOX; + } else if (std::string::npos != user_agent.find("MSIE")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_MSIE; + } else if (std::string::npos != user_agent.find("Camino")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_CAMINO; + } else { + o3d::metric_browser_type = o3d::BROWSER_NAME_UNKNOWN; + } + + int browser_major = 0, browser_minor = 0, browser_bugfix = 0; + if (GetBrowserVersionInfo(&browser_major, &browser_minor, &browser_bugfix)) { + o3d::metric_browser_major_version = browser_major; + o3d::metric_browser_minor_version = browser_minor; + o3d::metric_browser_bugfix_version = browser_bugfix; + } + + return true; +} diff --git a/o3d/plugin/mac/main_mac.mm b/o3d/plugin/mac/main_mac.mm new file mode 100644 index 0000000..4e94929 --- /dev/null +++ b/o3d/plugin/mac/main_mac.mm @@ -0,0 +1,1114 @@ +/* + * 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 implements the platform specific parts of the plugin for +// the Macintosh platform. + +#include "plugin/cross/main.h" + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" + +#import <Cocoa/Cocoa.h> +#include <Carbon/Carbon.h> +#include <OpenGL/OpenGL.h> +#include <AGL/agl.h> +#include <AGL/aglRenderers.h> + +#include "core/cross/event.h" +#include "statsreport/metrics.h" +#include "plugin/cross/plugin_logging.h" +#include "plugin/cross/plugin_metrics.h" +#include "plugin/cross/out_of_memory.h" +#include "plugin/mac/plugin_mac.h" + +o3d::PluginLogging* g_logger = NULL; +bool g_logging_initialized = false; + +using glue::_o3d::PluginObject; +using glue::StreamManager; +using o3d::DisplayWindowMac; +using o3d::Event; + +namespace { +// We would normally make this a stack variable in main(), but in a +// plugin, that's not possible, so we allocate it dynamically and +// destroy it explicitly. +scoped_ptr<base::AtExitManager> g_at_exit_manager; +} // end anonymous namespace + +// if defined, in AGL mode we do double buffered drawing +// #define USE_AGL_DOUBLE_BUFFER + +#define CFTIMER +// #define DEFERRED_DRAW_ON_NULLEVENTS + +// currently drawing with the timer doesn't play well with USE_AGL_DOUBLE_BUFFER +#ifdef CFTIMER +#undef USE_AGL_DOUBLE_BUFFER +#endif + +static void DrawPlugin(PluginObject* obj) { + obj->client()->RenderClient(); +#ifdef USE_AGL_DOUBLE_BUFFER + // In AGL mode, we have to call aglSwapBuffers to guarantee that our + // pixels make it to the screen. + if (obj->mac_agl_context_ != NULL) + aglSwapBuffers(obj->mac_agl_context_); +#endif +} + +void RenderOnDemandCallbackHandler::Run() { + obj_->SetWantsRedraw(true); +} + +// Cocoa key events for things like arrow key presses can have strange unicode +// values in the 0xF700-0xF747 range, eg NSUpArrowFunctionKey is 0xF700. +// These values are defined in NSEvent.h. +// Map the ones we care about to the more commonly understood values in +// the Mac system header Events.h, eg kUpArrowCharCode is 30. +static int TranslateMacUnicodeControlChar(int theChar) { + switch(theChar) { + case NSUpArrowFunctionKey: + return kUpArrowCharCode; + case NSDownArrowFunctionKey: + return kDownArrowCharCode; + case NSLeftArrowFunctionKey: + return kLeftArrowCharCode; + case NSRightArrowFunctionKey: + return kRightArrowCharCode; + } + return theChar; +} + + +// The Mac standard char codes for arrow keys are different from the +// web standard. +// Also in the browser world the enter key gets mapped to be the same as the +// return key. +static int TranslateMacControlCharToWebChar(int theChar) { + switch(theChar) { + case kUpArrowCharCode: + return 38; + case kDownArrowCharCode: + return 40; + case kLeftArrowCharCode: + return 37; + case kRightArrowCharCode: + return 39; + case kEnterCharCode: + return kReturnCharCode; + } + return theChar; +} + + +// Given an instance, and some event data, calls Javascript methods +// placed on the object tag so that the keystrokes can be handled in +// Javascript. +static void DispatchKeyboardEvent(PluginObject* obj, + EventKind kind, + int theChar, + int theKeyCode, + EventModifiers mods) { + theChar = TranslateMacUnicodeControlChar(theChar); + theChar = TranslateMacControlCharToWebChar(theChar); + int upperChar = (theChar >= 'a' && theChar <='z') ? theChar - 32 : theChar; + + Event::Type type; + switch (kind) { + case keyDown: + // We'll also have to send a keypress below. + type = Event::TYPE_KEYDOWN; + break; + case autoKey: + type = Event::TYPE_KEYPRESS; + break; + case keyUp: + type = Event::TYPE_KEYUP; + break; + } + Event event(type); + + switch (kind) { + case keyDown: + case keyUp: + event.set_key_code(upperChar); + break; + case autoKey: + event.set_char_code(theChar); + break; + default: + LOG(FATAL) << "Unknown keyboard event: " << kind; + } + + int modifier_state = 0; + if (mods & controlKey) { + modifier_state |= Event::MODIFIER_CTRL; + } + if (mods & shiftKey) { + modifier_state |= Event::MODIFIER_SHIFT; + } + if (mods & optionKey) { + modifier_state |= Event::MODIFIER_ALT; + } + if (mods & cmdKey) { + modifier_state |= Event::MODIFIER_META; + } + event.set_modifier_state(modifier_state); + obj->client()->AddEventToQueue(event); + if (type == Event::TYPE_KEYDOWN) { + event.clear_key_code(); + event.set_char_code(theChar); + event.set_type(Event::TYPE_KEYPRESS); + obj->client()->AddEventToQueue(event); + } +} + +// Given an instance, and a MacOS keyboard event, calls Javascript methods +// placed on the object tag so that the keystrokes can be handled in +// Javascript. +static void DispatchMacKeyboardEvent(PluginObject* obj, + EventRecord* the_event) { + unsigned char theKeyCode = (the_event->message & keyCodeMask) >> 8; + + DispatchKeyboardEvent(obj, + the_event->what, + the_event->message & charCodeMask, + theKeyCode, + the_event->modifiers); +} + + + +static void HandleMouseEvent(PluginObject* obj, + EventRecord* the_event) { + DCHECK(obj); + DCHECK(obj->client()); + int screen_x = the_event->where.h; + int screen_y = the_event->where.v; + static Point last_mouse_loc = {0,0}; + + o3d::Event::Type type; + switch (the_event->what) { + case mouseDown: + type = o3d::Event::TYPE_MOUSEDOWN; + break; + case mouseUp: + type = o3d::Event::TYPE_MOUSEUP; + break; + case nullEvent: + case osEvt: // can be various things but in this context it's mouse move + if (last_mouse_loc.h == screen_x && last_mouse_loc.v == screen_y) + return; + type = o3d::Event::TYPE_MOUSEMOVE; + break; + default: + LOG(FATAL) << "Unknown mouse event: " << the_event->what; + return; + } + + last_mouse_loc = the_event->where; + bool in_plugin = false; // Did the event happen within our drawing region? + + int x, y; + // now make x and y plugin relative coords + if (obj->GetFullscreenMacWindow()) { + Rect wBounds; + GetWindowBounds(obj->GetFullscreenMacWindow(), kWindowGlobalPortRgn, + &wBounds); + x = screen_x - wBounds.left; + y = screen_y - wBounds.top; + in_plugin = true; + } else { + Rect wBounds; + GetWindowBounds(obj->mac_window_, kWindowGlobalPortRgn, &wBounds); + x = screen_x - wBounds.left - obj->last_plugin_loc_.h; + y = screen_y - wBounds.top - obj->last_plugin_loc_.v; + in_plugin = x >=0 && y >= 0 && x < obj->width() && y < obj->height(); + } + + o3d::Event event(type); + int modifier_state = 0; + if (the_event->modifiers & controlKey) { + modifier_state |= o3d::Event::MODIFIER_CTRL; + } + if (the_event->modifiers & shiftKey) { + modifier_state |= o3d::Event::MODIFIER_SHIFT; + } + if (the_event->modifiers & optionKey) { + modifier_state |= o3d::Event::MODIFIER_ALT; + } + if (the_event->modifiers & cmdKey) { + modifier_state |= o3d::Event::MODIFIER_META; + } + + event.set_modifier_state(modifier_state); + + // TODO: Figure out how to detect and set buttons properly. + if (the_event->modifiers & btnState) { + event.set_button(o3d::Event::BUTTON_LEFT); + } + + event.set_position(x, y, screen_x, screen_y, in_plugin); + obj->client()->AddEventToQueue(event); + + if (in_plugin && type == Event::TYPE_MOUSEDOWN && + obj->HitFullscreenClickRegion(x, y)) { + obj->RequestFullscreenDisplay(); + } +} + +// Handle a mouse-related NPCocoaEvent. +// These events come from the new Cocoa revision of the NPAPI spec, +// currently implemented only in Safari. +// See https://wiki.mozilla.org/Mac:NPAPI_Event_Models +static void HandleCocoaMouseEvent(PluginObject* obj, + NPCocoaEvent* the_event) { + DCHECK(obj); + DCHECK(obj->client()); + int screen_x = the_event->data.mouse.pluginX; + int screen_y = the_event->data.mouse.pluginY; + + o3d::Event::Type type; + switch (the_event->type) { + case NPCocoaEventMouseDown: + type = o3d::Event::TYPE_MOUSEDOWN; + break; + case NPCocoaEventMouseUp: + type = o3d::Event::TYPE_MOUSEUP; + break; + // The Mac makes a distinction between moved and dragged (ie moved with + // the button down), but the O3D event model does not. + case NPCocoaEventMouseMoved: + case NPCocoaEventMouseDragged: + type = o3d::Event::TYPE_MOUSEMOVE; + break; + case NPCocoaEventScrollWheel: + type = o3d::Event::TYPE_WHEEL; + break; + // Don't care about these currently. + case NPCocoaEventMouseEntered: + case NPCocoaEventMouseExited: + default: + return; + } + + int x = the_event->data.mouse.pluginX; + int y = the_event->data.mouse.pluginY; + + // Did the event happen within our drawing region? + bool in_plugin = x >= 0 && y >= 0 && x < obj->width() && y < obj->height(); + + o3d::Event event(type); + int modifier_state = 0; + if (the_event->data.mouse.modifierFlags & NSControlKeyMask) { + modifier_state |= o3d::Event::MODIFIER_CTRL; + } + if (the_event->data.mouse.modifierFlags & + (NSAlphaShiftKeyMask | NSShiftKeyMask)) { + modifier_state |= o3d::Event::MODIFIER_SHIFT; + } + if (the_event->data.mouse.modifierFlags & NSAlternateKeyMask) { + modifier_state |= o3d::Event::MODIFIER_ALT; + } + if (the_event->data.mouse.modifierFlags & NSCommandKeyMask) { + modifier_state |= o3d::Event::MODIFIER_META; + } + + event.set_modifier_state(modifier_state); + + if (the_event->type == NPCocoaEventScrollWheel) { + if (the_event->data.mouse.deltaX && the_event->data.mouse.deltaY) { + if (abs(the_event->data.mouse.deltaX) > + abs(the_event->data.mouse.deltaY)) { + the_event->data.mouse.deltaY = 0; + } else { + the_event->data.mouse.deltaX = 0; + } + } + event.set_delta(the_event->data.mouse.deltaX * 10.0, + the_event->data.mouse.deltaY * 10.0); + } + + if ((the_event->type == NPCocoaEventMouseDown) || + (the_event->type == NPCocoaEventMouseUp)) { + event.set_button( + MacOSMouseButtonNumberToO3DButton( + the_event->data.mouse.buttonNumber)); + } + + event.set_position(x, y, screen_x, screen_y, in_plugin); + obj->client()->AddEventToQueue(event); + + if (in_plugin && type == Event::TYPE_MOUSEDOWN && + obj->HitFullscreenClickRegion(x, y)) { + obj->RequestFullscreenDisplay(); + } +} + + + + +// Convert an NSEvent style modifiers field to an EventRecord style one. +// Not all bits have a representation in the target type, eg NSFunctionKeyMask +// but we just need to convert the basic modifiers that need to be passed on +// to Javascript. +static EventModifiers CocoaToEventRecordModifiers(NSUInteger inMods) { + EventModifiers outMods = 0; + + // NSEvent distinuishes between the shift key being down and the capslock key, + // but the W3C event spec does not make this distinction. + if (inMods & (NSAlphaShiftKeyMask | NSShiftKeyMask)) + outMods |= shiftKey; + if (inMods & NSControlKeyMask) + outMods |= controlKey; + if (inMods & NSAlternateKeyMask) + outMods |= optionKey; + if (inMods & NSCommandKeyMask) + outMods |= cmdKey; + + return outMods; +} + +// Handle an NPCocoaEvent style event. The Cocoa event interface is +// a recent addition to the NAPI spec. +// See https://wiki.mozilla.org/Mac:NPAPI_Event_Models for further details. +// The principle advantages are that we can get scrollwheel messages, +// mouse-moved messages, and can tell which mouse button was pressed. +// This API will also be required for a carbon-free 64 bit version for 10.6. +bool HandleCocoaEvent(NPP instance, NPCocoaEvent* the_event) { + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + WindowRef fullscreen_window = obj->GetFullscreenMacWindow(); + bool handled = false; + + if (g_logger) g_logger->UpdateLogging(); + + obj->MacEventReceived(); + switch (the_event->type) { + case NPCocoaEventDrawRect: + DrawPlugin(obj); + handled = true; + break; + case NPCocoaEventMouseDown: + case NPCocoaEventMouseUp: + case NPCocoaEventMouseMoved: + case NPCocoaEventMouseDragged: + case NPCocoaEventMouseEntered: + case NPCocoaEventMouseExited: + case NPCocoaEventScrollWheel: + HandleCocoaMouseEvent(obj, the_event); + break; + case NPCocoaEventKeyDown: + // Hard-coded trapdoor to get out of fullscreen mode if user hits escape. + if (fullscreen_window) { + NSString *chars = + (NSString*) the_event->data.key.charactersIgnoringModifiers; + + if ([chars characterAtIndex:0] == '\e') { + obj->CancelFullscreenDisplay(); + break; + } + } // otherwise fall through + case NPCocoaEventKeyUp: { + EventKind eventKind = (the_event->type == NPCocoaEventKeyUp) ? keyUp : + (the_event->data.key.isARepeat) ? autoKey : keyDown; + + EventModifiers modifiers = + CocoaToEventRecordModifiers(the_event->data.key.modifierFlags); + + NSString *chars = + (NSString*) the_event->data.key.charactersIgnoringModifiers; + + DispatchKeyboardEvent(obj, + eventKind, + [chars characterAtIndex:0], + the_event->data.key.keyCode, + modifiers); + break; + } + case NPCocoaEventFlagsChanged: + case NPCocoaEventFocusChanged: + case NPCocoaEventWindowFocusChanged: + // Safari tab switching recovery code. + if (obj->mac_surface_hidden_) { + obj->mac_surface_hidden_ = false; + NPN_ForceRedraw(instance); // invalidate to cause a redraw + } + + // Auto-recovery for any situation where another window comes in front + // of the fullscreen window and we need to exit fullscreen mode. + // This can happen because another browser window has come forward, or + // because another app has been called to the front. + // TODO: We'll have problems with this when dealing with e.g. + // Japanese text input IME windows. + if (fullscreen_window && fullscreen_window != ActiveNonFloatingWindow()) { + obj->CancelFullscreenDisplay(); + } + + break; + } + + return handled; +} + +// List of message types from mozilla's nsplugindefs.h, which is more +// complete than the list in NPAPI.h. +enum nsPluginEventType { + nsPluginEventType_GetFocusEvent = (osEvt + 16), + nsPluginEventType_LoseFocusEvent, + nsPluginEventType_AdjustCursorEvent, + nsPluginEventType_MenuCommandEvent, + nsPluginEventType_ClippingChangedEvent, + nsPluginEventType_ScrollingBeginsEvent, + nsPluginEventType_ScrollingEndsEvent, + nsPluginEventType_Idle = 0 +}; + + +bool HandleMacEvent(EventRecord* the_event, NPP instance) { + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + bool handled = false; + WindowRef fullscreen_window = obj->GetFullscreenMacWindow(); + + if (g_logger) g_logger->UpdateLogging(); + + // Help the plugin keep track of when we last saw an event so the CFTimer can + // notice if we get cut off, eg by our tab being hidden by Safari, which there + // is no other way for us to detect. + obj->MacEventReceived(); + + switch (the_event->what) { + case nullEvent: +#ifdef DEFERRED_DRAW_ON_NULLEVENTS + GLUE_PROFILE_START(instance, "forceredraw"); + NPN_ForceRedraw(instance); // invalidate to cause a redraw + GLUE_PROFILE_STOP(instance, "forceredraw"); +#elif defined(CFTIMER) +#else + DrawPlugin(obj); +#endif + // Safari tab switching recovery code. + if (obj->mac_surface_hidden_) { + obj->mac_surface_hidden_ = false; + NPN_ForceRedraw(instance); // invalidate to cause a redraw + } + + // Auto-recovery for any situation where another window comes in front + // of the fullscreen window and we need to exit fullscreen mode. + // This can happen because another browser window has come forward, or + // because another app has been called to the front. + if (fullscreen_window && fullscreen_window != ActiveNonFloatingWindow()) { + obj->CancelFullscreenDisplay(); + } + + // Send nullEvents to HandleMouseEvent so they can be used to simulate + // mouse moved events. Not needed in fullscreen mode, where we really do + // get mouse moved events. See the osEvt case below. + if (!fullscreen_window) + HandleMouseEvent(obj, the_event); + + handled = true; + break; + case updateEvt: + DrawPlugin(obj); + handled = true; + break; + case osEvt: + // These are mouse moved messages when so tagged in the high byte. + if ((the_event->message >> 24) == mouseMovedMessage) { + HandleMouseEvent(obj, the_event); + handled = true; + } + break; + case mouseDown: + HandleMouseEvent(obj, the_event); + break; + case mouseUp: + HandleMouseEvent(obj, the_event); + handled = true; + break; + case keyDown: + // Hard-coded trapdoor to get out of fullscreen mode if user hits escape. + unsigned char theChar = the_event->message & charCodeMask; + if (theChar == '\e' && fullscreen_window) { + obj->CancelFullscreenDisplay(); + break; + } // otherwise fall through + case autoKey: + case keyUp: { + DispatchMacKeyboardEvent(obj, the_event); + handled = true; + break; + } + case nsPluginEventType_ScrollingBeginsEvent: + obj->SetScrollIsInProgress(true); + break; + case nsPluginEventType_ScrollingEndsEvent: + obj->SetScrollIsInProgress(false); + break; + default: + break; + } + return handled; +} + +extern "C" { + NPError InitializePlugin() { + if (!o3d::SetupOutOfMemoryHandler()) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + InitializeBreakpad(); + +#ifdef CFTIMER + gRenderTimer.Start(); +#endif // CFTIMER + + // Initialize the AtExitManager so that base singletons can be + // destroyed properly. + g_at_exit_manager.reset(new base::AtExitManager()); + + // Turn on the logging. + CommandLine::Init(0, NULL); + InitLogging("debug.log", + logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG, + logging::DONT_LOCK_LOG_FILE, + logging::APPEND_TO_OLD_LOG_FILE); + + DLOG(INFO) << "NP_Initialize"; + + o3d::SetupOutOfMemoryHandler(); + + return NPERR_NO_ERROR; + } + + NPError OSCALL NP_Initialize(NPNetscapeFuncs* browserFuncs) { + HANDLE_CRASHES; + NPError retval = InitializeNPNApi(browserFuncs); + if (retval != NPERR_NO_ERROR) return retval; + return InitializePlugin(); + } + + // Wrapper that discards the return value to match the expected type of + // NPP_ShutdownUPP. + void NPP_ShutdownWrapper() { + NP_Shutdown(); + } + + // This code is needed to support browsers based on a slightly dated version + // of the NPAPI such as Firefox 2, and Camino 1.6. These browsers expect there + // to be a main() to call to do basic setup. + int main(NPNetscapeFuncs* browserFuncs, + NPPluginFuncs* pluginFuncs, + NPP_ShutdownUPP* shutdownProc) { + HANDLE_CRASHES; + NPError error = NP_Initialize(browserFuncs); + if (error == NPERR_NO_ERROR) + error = NP_GetEntryPoints(pluginFuncs); + *shutdownProc = NPP_ShutdownWrapper; + + return error; + } + + NPError OSCALL NP_Shutdown(void) { + HANDLE_CRASHES; + DLOG(INFO) << "NP_Shutdown"; + + if (g_logger) { + // Do a last sweep to aggregate metrics before we shut down + g_logger->ProcessMetrics(true, false); + delete g_logger; + g_logger = NULL; + g_logging_initialized = false; + stats_report::g_global_metrics.Uninitialize(); + } + + CommandLine::Terminate(); + +#ifdef CFTIMER + gRenderTimer.Stop(); +#endif + + // Force all base singletons to be destroyed. + g_at_exit_manager.reset(NULL); + + ShutdownBreakpad(); + + return NPERR_NO_ERROR; + } + + // Negotiates the best plugin event model, sets the browser to use that, + // and updates the PluginObject so we can remember which one we chose. + // We favor the newer Cocoa-based model, but can cope with browsers that + // only support the original event model, or indeed can't even understand + // what we are asking for. + // Cannot actually fail - + static void Mac_SetBestEventModel(NPP instance, PluginObject* obj) { + NPError err = NPERR_NO_ERROR; + NPEventModel event_model = NPEventModelCarbon; + NPBool supportsCocoaEventModel = FALSE; + NPBool supportsCarbonEventModel = FALSE; + + // See if browser supports Cocoa event model. + err = NPN_GetValue(instance, + NPNVsupportsCocoaBool, + &supportsCocoaEventModel); + if (err != NPERR_NO_ERROR) { + supportsCocoaEventModel = FALSE; + err = NPERR_NO_ERROR; // browser doesn't support switchable event models + } + + // See if browser supports Carbon event model. + err = NPN_GetValue(instance, + NPNVsupportsCarbonBool, + &supportsCarbonEventModel); + if (err != NPERR_NO_ERROR) { + supportsCarbonEventModel = FALSE; + err = NPERR_NO_ERROR; + } + + // Now we've collected our data, the decision phase begins. + + // If we didn't successfully get TRUE for either question, the browser + // just does not know about the new switchable event models, so must only + // support the old Carbon event model. + if (!(supportsCocoaEventModel || supportsCocoaEventModel)) { + supportsCarbonEventModel = TRUE; + obj->event_model_ = NPEventModelCarbon; + } + + if (supportsCocoaEventModel) { + NPN_SetValue(instance, NPPVpluginEventModel, + reinterpret_cast<void*>(NPEventModelCocoa)); + obj->event_model_ = NPEventModelCocoa; + } else { + NPN_SetValue(instance, NPPVpluginEventModel, + reinterpret_cast<void*>(NPEventModelCocoa)); + obj->event_model_ = NPEventModelCarbon; + } + } + + + // Negotiates the best plugin drawing model, sets the browser to use that, + // and updates the PluginObject so we can remember which one we chose. + // Returns NPERR_NO_ERROR (0) if successful, otherwise an NPError code. + static NPError Mac_SetBestDrawingModel(NPP instance, PluginObject* obj) { + NPError err = NPERR_NO_ERROR; + NPBool supportsCoreGraphics = FALSE; + NPBool supportsOpenGL = FALSE; + NPBool supportsQuickDraw = FALSE; + NPDrawingModel drawing_model = NPDrawingModelQuickDraw; + + // test for direct OpenGL support + err = NPN_GetValue(instance, + NPNVsupportsOpenGLBool, + &supportsOpenGL); + if (err != NPERR_NO_ERROR) + supportsOpenGL = FALSE; + + // test for QuickDraw support + err = NPN_GetValue(instance, + NPNVsupportsQuickDrawBool, + &supportsQuickDraw); + if (err != NPERR_NO_ERROR) + supportsQuickDraw = FALSE; + + // Test for Core Graphics support + err = NPN_GetValue(instance, + NPNVsupportsCoreGraphicsBool, + &supportsCoreGraphics); + if (err != NPERR_NO_ERROR) + supportsCoreGraphics = FALSE; + + + // In order of preference. Preference is now determined by compatibility, + // not by modernity, and so is the opposite of the order I first used. + if (supportsQuickDraw && !(obj->event_model_ == NPEventModelCocoa)) { + drawing_model = NPDrawingModelQuickDraw; + } else if (supportsCoreGraphics) { + drawing_model = NPDrawingModelCoreGraphics; + } else if (supportsOpenGL) { + drawing_model = NPDrawingModelOpenGL; + } else { + // This case is for browsers that didn't even understand the question + // eg FF2, so drawing models are not supported, just assume QuickDraw. + obj->drawing_model_ = NPDrawingModelQuickDraw; + return NPERR_NO_ERROR; + } + + err = NPN_SetValue(instance, NPPVpluginDrawingModel, + reinterpret_cast<void*>(drawing_model)); + if (err != NPERR_NO_ERROR) + return err; + + obj->drawing_model_ = drawing_model; + + return NPERR_NO_ERROR; + } + + + NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, + char* argn[], char* argv[], NPSavedData* saved) { + HANDLE_CRASHES; + + NPError err = NPERR_NO_ERROR; + + if (!g_logging_initialized) { + GetUserAgentMetrics(instance); + GetUserConfigMetrics(); + // Create usage stats logs object + g_logger = o3d::PluginLogging::InitializeUsageStatsLogging(); + g_logging_initialized = true; + } + + PluginObject* pluginObject = glue::_o3d::PluginObject::Create( + instance); + instance->pdata = pluginObject; + glue::_o3d::InitializeGlue(instance); + pluginObject->Init(argc, argn, argv); + + Mac_SetBestEventModel(instance, + static_cast<PluginObject*>(instance->pdata)); + + err = Mac_SetBestDrawingModel( + instance, static_cast<PluginObject*>(instance->pdata)); + if (err != NPERR_NO_ERROR) + return err; + return NPERR_NO_ERROR; + } + + NPError NPP_Destroy(NPP instance, NPSavedData** save) { + HANDLE_CRASHES; + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + if (obj) { +#if defined(CFTIMER) + gRenderTimer.RemoveInstance(instance); +#endif + + obj->TearDown(); + NPN_ReleaseObject(obj); + instance->pdata = NULL; + } + + return NPERR_NO_ERROR; + } + + static bool CheckForAGLError() { + return aglGetError() != AGL_NO_ERROR; + } + + + NPError NPP_SetWindow(NPP instance, NPWindow* window) { + HANDLE_CRASHES; + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + WindowRef new_window = NULL; + + assert(window != NULL); + + obj->last_plugin_loc_.h = window->x; + obj->last_plugin_loc_.v = window->y; + + switch (obj->drawing_model_) { + case NPDrawingModelOpenGL: { + NP_GLContext* np_gl = reinterpret_cast<NP_GLContext*>(window->window); + if (obj->event_model_ == NPEventModelCocoa) { + NSWindow * ns_window = reinterpret_cast<NSWindow*>(np_gl->window); + new_window = reinterpret_cast<WindowRef>([ns_window windowRef]); + } else { + new_window = np_gl->window; + } + obj->mac_2d_context_ = NULL; + obj->mac_cgl_context_ = np_gl->context; + break; + } + case NPDrawingModelCoreGraphics: { + NP_CGContext* np_cg = reinterpret_cast<NP_CGContext*>(window->window); + if (obj->event_model_ == NPEventModelCocoa) { + NSWindow * ns_window = reinterpret_cast<NSWindow*>(np_cg->window); + new_window = reinterpret_cast<WindowRef>([ns_window windowRef]); + } else { + new_window = np_cg->window; + } + obj->mac_2d_context_ = np_cg->context; + break; + } + case NPDrawingModelQuickDraw: { + NP_Port* np_qd = reinterpret_cast<NP_Port*>(window->window); + obj->mac_2d_context_ = np_qd->port; + if (np_qd->port) + new_window = GetWindowFromPort(np_qd->port); + break; + } + default: + return NPERR_INCOMPATIBLE_VERSION_ERROR; + break; + } + + // Whether the target window has changed. + bool window_changed = new_window != obj->mac_window_; + + // Whether we already had a window before this call. + bool had_a_window = obj->mac_window_ != NULL; + + obj->mac_window_ = new_window; + + if (obj->drawing_model_ == NPDrawingModelOpenGL) { + CGLSetCurrentContext(obj->mac_cgl_context_); + } else if (obj->mac_agl_context_ == NULL) { // setup AGL context + AGLPixelFormat myAGLPixelFormat = NULL; + + // We need to spec out a few similar but different sets of renderer + // specifications - define some macros to lessen the duplication. +#define O3D_DEPTH_SETTINGS AGL_RGBA, AGL_DEPTH_SIZE, 24, +#define O3D_STENCIL_SETTINGS AGL_STENCIL_SIZE, 8, +#define O3D_HARDWARE_RENDERER AGL_ACCELERATED, AGL_NO_RECOVERY, +#define O3D_SOFTWARE_RENDERER AGL_RENDERER_ID, AGL_RENDERER_GENERIC_FLOAT_ID, +#define O3D_MULTISAMPLE AGL_MULTISAMPLE, \ + AGL_SAMPLE_BUFFERS_ARB, 1, AGL_SAMPLES_ARB, 4, AGL_MULTISAMPLE, +#define O3D_END AGL_NONE + +#ifdef USE_AGL_DOUBLE_BUFFER +#define O3D_DOUBLE_BUFFER_SETTINGS AGL_DOUBLEBUFFER, +#else +#define O3D_DOUBLE_BUFFER_SETTINGS +#endif + + if (!UseSoftwareRenderer()) { + // Try to create a hardware context with the following + // specification + GLint attributes[] = { + O3D_DEPTH_SETTINGS + O3D_STENCIL_SETTINGS + O3D_DOUBLE_BUFFER_SETTINGS + O3D_HARDWARE_RENDERER + O3D_MULTISAMPLE + O3D_END + }; + myAGLPixelFormat = aglChoosePixelFormat(NULL, + 0, + attributes); + + // If this fails, then don't try to be as ambitious + // (so don't ask for multi-sampling), but still require hardware + if (myAGLPixelFormat == NULL) { + GLint low_end_attributes[] = { + O3D_DEPTH_SETTINGS + O3D_STENCIL_SETTINGS + O3D_DOUBLE_BUFFER_SETTINGS + O3D_HARDWARE_RENDERER + O3D_END + }; + myAGLPixelFormat = aglChoosePixelFormat(NULL, + 0, + low_end_attributes); + } + } + + if (myAGLPixelFormat == NULL) { + // Fallback to software renderer either if the vendorID/gpuID + // is known to be problematic, or if the hardware context failed + // to get created + // + // Note that we don't request multisampling since it's too slow. + GLint software_renderer_attributes[] = { + O3D_DEPTH_SETTINGS + O3D_STENCIL_SETTINGS + O3D_DOUBLE_BUFFER_SETTINGS + O3D_SOFTWARE_RENDERER + O3D_END + }; + myAGLPixelFormat = aglChoosePixelFormat(NULL, + 0, + software_renderer_attributes); + + obj->SetRendererIsSoftware(true); + } + + if (myAGLPixelFormat == NULL || CheckForAGLError()) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + obj->mac_agl_context_ = aglCreateContext(myAGLPixelFormat, NULL); + + if (CheckForAGLError()) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + aglDestroyPixelFormat(myAGLPixelFormat); + + if (!SetWindowForAGLContext(obj->mac_agl_context_, obj->mac_window_)) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + aglSetCurrentContext(obj->mac_agl_context_); + + GetOpenGLMetrics(); + +#ifdef USE_AGL_DOUBLE_BUFFER + GLint swapInterval = 1; // request synchronization + aglSetInteger(obj->mac_agl_context_, AGL_SWAP_INTERVAL, &swapInterval); +#endif + } + + int clipHeight = window->clipRect.bottom - window->clipRect.top; + int clipWidth = window->clipRect.right - window->clipRect.left; + + int x_offset = window->clipRect.left - window->x; + int y_offset = window->clipRect.top - window->y; + int gl_x_origin = x_offset; + int gl_y_origin = window->clipRect.bottom - (window->y + window->height); + + // Firefox calls us with an empty cliprect all the time, toggling our + // cliprect back and forth between empty and the normal value, particularly + // during scrolling. + // As we need to make our AGL surface track the clip rect, honoring all of + // these calls would result in spectacular flashing. + // The goal here is to try to spot the calls we can safely ignore. + // The bogus empty cliprects always have left and top of the real clip. + // A null cliprect should always be honored ({0,0,0,0} means a hidden tab), + // as should the first ever call to this function, + // or an attempt to change the window. + // The call at the end of a scroll step is also honored. + // Otherwise, skip this change and do not hide our context. + bool is_empty_cliprect = (clipHeight <= 0 || clipWidth <= 0); + bool is_null_cliprect = (window->clipRect.bottom == 0 && + window->clipRect.top == 0 && + window->clipRect.left == 0 && + window->clipRect.right == 0); + + if (is_empty_cliprect && (!is_null_cliprect) && + had_a_window && (!window_changed) && !obj->ScrollIsInProgress()) { + return NPERR_NO_ERROR; + } + + // Workaround - the Apple software renderer crashes if you set the drawing + // area to 0x0, so use 1x1. + if (is_empty_cliprect && obj->RendererIsSoftware()) + clipWidth = clipHeight = 1; + + // update size and location of the agl context + if (obj->mac_agl_context_) { + + // We already had a window, and now we've got a different window - + // this can happen when the user drags out a tab in Safari into its own + // window. + if (had_a_window && window_changed) + SetWindowForAGLContext(obj->mac_agl_context_, obj->mac_window_); + + aglSetCurrentContext(obj->mac_agl_context_); + + Rect windowRect = {0, 0, 0, 0}; + if (obj->drawing_model_ == NPDrawingModelQuickDraw) + GetWindowBounds(obj->mac_window_, kWindowContentRgn, &windowRect); + else + GetWindowBounds(obj->mac_window_, kWindowStructureRgn, &windowRect); + + int windowHeight = windowRect.bottom - windowRect.top; + + // These are in window coords, the weird bit being that agl wants the + // location of the bottom of the GL context in y flipped coords, + // eg 100 would mean the bottom of the GL context was 100 up from the + // bottom of the window. + obj->last_buffer_rect_[0] = window->x + x_offset; + obj->last_buffer_rect_[1] = (windowHeight - (window->y + clipHeight)) + - y_offset; // this param is y flipped + obj->last_buffer_rect_[2] = clipWidth; + obj->last_buffer_rect_[3] = clipHeight; + obj->mac_surface_hidden_ = false; + + aglSetInteger(obj->mac_agl_context_, AGL_BUFFER_RECT, + obj->last_buffer_rect_); + aglEnable(obj->mac_agl_context_, AGL_BUFFER_RECT); + } + + // Renderer is already initialized from a previous call to this function, + // just update size and position and return. + if (had_a_window) { + obj->renderer()->SetClientOriginOffset(gl_x_origin, gl_y_origin); + obj->Resize(window->width, window->height); + return NPERR_NO_ERROR; + } + + // Create and assign the graphics context. + o3d::DisplayWindowMac default_display; + default_display.set_agl_context(obj->mac_agl_context_); + default_display.set_cgl_context(obj->mac_cgl_context_); + + obj->CreateRenderer(default_display); + obj->client()->Init(); + obj->client()->SetRenderOnDemandCallback( + new RenderOnDemandCallbackHandler(obj)); + + obj->renderer()->SetClientOriginOffset(gl_x_origin, gl_y_origin); + obj->Resize(window->width, window->height); + + +#ifdef CFTIMER + // now that the grahics context is setup, add this instance to the timer + // list so it gets drawn repeatedly + gRenderTimer.AddInstance(instance); +#endif // CFTIMER + + return NPERR_NO_ERROR; + } + + // Called when the browser has finished attempting to stream data to + // a file as requested. If fname == NULL the attempt was not successful. + void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) { + HANDLE_CRASHES; + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + StreamManager* stream_manager = obj->stream_manager(); + + // Some browsers give us an absolute HFS path in fname, some give us an + // absolute Posix path, so convert to Posix if needed. + if ((!fname) || (fname[0] == '/') || !fname[0]) { + stream_manager->SetStreamFile(stream, fname); + } else { + const char* converted_fname = CreatePosixFilePathFromHFSFilePath(fname); + if (converted_fname == NULL) + return; // TODO should also log error if we ever get here + stream_manager->SetStreamFile(stream, converted_fname); + delete converted_fname; + } + } + + int16 NPP_HandleEvent(NPP instance, void* event) { + HANDLE_CRASHES; + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + if (obj->event_model_ == NPEventModelCarbon) { + EventRecord* theEvent = static_cast<EventRecord*>(event); + return HandleMacEvent(theEvent, instance) ? 1 : 0; + } else if (obj->event_model_ == NPEventModelCocoa){ + return HandleCocoaEvent(instance, (NPCocoaEvent*)event) ? 1 : 0; + } + return 0; + } +}; // end extern "C" diff --git a/o3d/plugin/mac/o3d_mac_npapi_metapackage/open_source_o3d_mac_npapi_metapackage.packproj b/o3d/plugin/mac/o3d_mac_npapi_metapackage/open_source_o3d_mac_npapi_metapackage.packproj new file mode 100644 index 0000000..650936ae --- /dev/null +++ b/o3d/plugin/mac/o3d_mac_npapi_metapackage/open_source_o3d_mac_npapi_metapackage.packproj @@ -0,0 +1,776 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Hierarchy</key> + <dict> + <key>Attributes</key> + <dict> + <key>Components</key> + <array> + <dict> + <key>Attributes</key> + <dict> + <key>Documents</key> + <dict> + <key>Background Image</key> + <dict> + <key>IFPkgFlagBackgroundAlignment</key> + <integer>4</integer> + <key>IFPkgFlagBackgroundScaling</key> + <integer>1</integer> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + <key>License</key> + <dict> + <key>International</key> + <dict> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + </dict> + <key>ReadMe</key> + <dict> + <key>International</key> + <dict> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + </dict> + <key>Welcome</key> + <dict> + <key>International</key> + <dict> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + </dict> + </dict> + <key>Files</key> + <dict> + <key>Compress</key> + <true/> + <key>Hierarchy</key> + <dict> + <key>Children</key> + <array> + <dict> + <key>Children</key> + <array> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Utilities</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Applications</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Application Support</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Documentation</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Filesystems</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Frameworks</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>O3D.plugin</string> + <key>Path Type</key> + <integer>2</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>3</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Internet Plug-Ins</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>PreferencePanes</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Printers</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>QuickTime</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Screen Savers</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Scripts</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>509</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>Library</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>1021</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>Children</key> + <array> + <dict> + <key>Children</key> + <array> + <dict> + <key>Children</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>Path</key> + <string>Extensions</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>493</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>Path</key> + <string>Library</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>493</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>Path</key> + <string>System</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>493</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>Path</key> + <string>/</string> + <key>Path Type</key> + <integer>1</integer> + <key>Privileges</key> + <integer>1021</integer> + <key>Type</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <key>IFPkgFlagDefaultLocation</key> + <string>/</string> + <key>Imported Package</key> + <false/> + <key>Package Path</key> + <string></string> + <key>Split Forks</key> + <true/> + </dict> + <key>Plugins</key> + <dict> + <key>PluginsList</key> + <array> + <dict> + <key>Path</key> + <string>Introduction</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>ReadMe</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>License</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>Target</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>PackageSelection</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>Install</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>FinishUp</string> + <key>Type</key> + <integer>0</integer> + </dict> + </array> + </dict> + <key>Scripts</key> + <dict> + <key>Additional Resources</key> + <dict> + <key>International</key> + <array/> + </dict> + <key>Installation Scripts</key> + <dict> + <key>IFInstallationScriptsPostflight</key> + <dict> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>2</integer> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPostinstall</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPostupgrade</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPreflight</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPreinstall</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPreupgrade</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + </dict> + <key>Requirements</key> + <array/> + </dict> + <key>Settings</key> + <dict> + <key>Description</key> + <dict> + <key>International</key> + <dict> + <key>IFPkgDescriptionDeleteWarning</key> + <string></string> + <key>IFPkgDescriptionDescription</key> + <string></string> + <key>IFPkgDescriptionTitle</key> + <string>O3D</string> + <key>IFPkgDescriptionVersion</key> + <string>1.0</string> + </dict> + </dict> + <key>Display Information</key> + <dict> + <key>CFBundleGetInfoString</key> + <string></string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIconFile Path Type</key> + <integer>1</integer> + <key>CFBundleIdentifier</key> + <string>com.google.o3d.pkg</string> + <key>CFBundleName</key> + <string>O3D</string> + <key>CFBundleShortVersionString</key> + <string>1.0a1</string> + </dict> + <key>Options</key> + <dict> + <key>IFPkgFlagAllowBackRev</key> + <false/> + <key>IFPkgFlagAuthorizationAction</key> + <integer>0</integer> + <key>IFPkgFlagFollowLinks</key> + <false/> + <key>IFPkgFlagIsRequired</key> + <false/> + <key>IFPkgFlagOverwritePermissions</key> + <false/> + <key>IFPkgFlagRelocatable</key> + <false/> + <key>IFPkgFlagRestartAction</key> + <integer>0</integer> + <key>IFPkgFlagRootVolumeOnly</key> + <true/> + <key>IFPkgFlagUpdateInstalledLanguages</key> + <false/> + </dict> + <key>Version</key> + <dict> + <key>IFMajorVersion</key> + <integer>1</integer> + <key>IFMinorVersion</key> + <integer>0</integer> + </dict> + </dict> + </dict> + <key>IFPkgFlagPackageSelection</key> + <integer>1</integer> + <key>Name</key> + <string>O3D</string> + <key>Status</key> + <integer>1</integer> + <key>Type</key> + <integer>1</integer> + </dict> + </array> + <key>Documents</key> + <dict> + <key>Background Image</key> + <dict> + <key>IFPkgFlagBackgroundAlignment</key> + <integer>4</integer> + <key>IFPkgFlagBackgroundScaling</key> + <integer>1</integer> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + <key>License</key> + <dict> + <key>International</key> + <dict> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + </dict> + <key>ReadMe</key> + <dict> + <key>International</key> + <dict> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + </dict> + <key>Welcome</key> + <dict> + <key>International</key> + <dict> + <key>Mode</key> + <integer>0</integer> + <key>Path</key> + <string></string> + <key>Path Type</key> + <integer>1</integer> + </dict> + </dict> + </dict> + <key>Plugins</key> + <dict> + <key>PluginsList</key> + <array> + <dict> + <key>Path</key> + <string>Introduction</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>ReadMe</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>License</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>Target</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>PackageSelection</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>Install</string> + <key>Type</key> + <integer>0</integer> + </dict> + <dict> + <key>Path</key> + <string>FinishUp</string> + <key>Type</key> + <integer>0</integer> + </dict> + </array> + </dict> + <key>Scripts</key> + <dict> + <key>Additional Resources</key> + <dict> + <key>International</key> + <array/> + </dict> + <key>Installation Scripts</key> + <dict> + <key>IFInstallationScriptsPostflight</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPostinstall</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPostupgrade</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPreflight</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPreinstall</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + <key>IFInstallationScriptsPreupgrade</key> + <dict> + <key>Path</key> + <string></string> + <key>Status</key> + <false/> + </dict> + </dict> + <key>Requirements</key> + <array/> + </dict> + <key>Settings</key> + <dict> + <key>Description</key> + <dict> + <key>International</key> + <dict> + <key>IFPkgDescriptionDeleteWarning</key> + <string></string> + <key>IFPkgDescriptionDescription</key> + <string></string> + <key>IFPkgDescriptionTitle</key> + <string>O3D</string> + <key>IFPkgDescriptionVersion</key> + <string>1.0</string> + </dict> + </dict> + <key>Display Information</key> + <dict> + <key>CFBundleGetInfoString</key> + <string>O3D 1.0 Copyright © 2008 Google Inc.</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIconFile Path Type</key> + <integer>1</integer> + <key>CFBundleIdentifier</key> + <string>com.google.pkg.o3d_mac_npapi_metapackage</string> + <key>CFBundleName</key> + <string>O3D</string> + <key>CFBundleShortVersionString</key> + <string>1.0a1</string> + </dict> + <key>Version</key> + <dict> + <key>IFMajorVersion</key> + <integer>1</integer> + <key>IFMinorVersion</key> + <integer>0</integer> + </dict> + </dict> + </dict> + <key>IFPkgFlagComponentDirectory</key> + <string>Contents/Resources</string> + <key>IFPkgFlagPackageSelection</key> + <integer>0</integer> + <key>Name</key> + <string>O3D</string> + <key>Status</key> + <integer>1</integer> + <key>Type</key> + <integer>0</integer> + </dict> + <key>Name</key> + <string>Project</string> + <key>Settings</key> + <dict> + <key>10.1 Compatibility</key> + <true/> + <key>Build Path</key> + <string>.</string> + <key>Build Path Type</key> + <integer>2</integer> + <key>Comment</key> + <string></string> + <key>Remove .DS_Store</key> + <true/> + <key>Remove .pbdevelopment</key> + <true/> + <key>Remove CVS</key> + <true/> + </dict> +</dict> +</plist> diff --git a/o3d/plugin/mac/o3d_plugin.r b/o3d/plugin/mac/o3d_plugin.r new file mode 100644 index 0000000..8032bc7 --- /dev/null +++ b/o3d/plugin/mac/o3d_plugin.r @@ -0,0 +1,15 @@ +#include <CoreServices/CoreServices.r> + +resource 'STR#' (126) { { + "@@@PluginDescription@@@", + "@@@PluginName@@@" +} }; + +resource 'STR#' (127) { { + "@@@PluginName@@@" +} }; + +resource 'STR#' (128) { { + "@@@PluginMimeType@@@", + "" +} }; diff --git a/o3d/plugin/mac/plugin_logging-mac.mm b/o3d/plugin/mac/plugin_logging-mac.mm new file mode 100644 index 0000000..8466260 --- /dev/null +++ b/o3d/plugin/mac/plugin_logging-mac.mm @@ -0,0 +1,236 @@ +/* + * 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 code to perform the necessary logging operations +// including: +// - initializing logging object +// - aggregating metrics +// - uploading metrics to the stats server + +#include <build/build_config.h> +#include "statsreport/const-mac.h" +#include "core/cross/types.h" +#include "statsreport/metrics.h" +#include "plugin/cross/plugin_logging.h" +#include "plugin/cross/plugin_metrics.h" +#include "statsreport/common/const_product.h" +#include "statsreport/uploader.h" + +namespace o3d { + + +#define kPrefString @"O3D_STATS" +#define kUserKey @"Google_O3D_User" + + +PluginLogging::PluginLogging() : timer_(new HighresTimer()), + running_time_(0), + prev_uptime_seconds_(0), + prev_cputime_seconds_(0) { + DLOG(INFO) << "Creating logger."; + timer_->Start(); +} + +bool PluginLogging::UpdateLogging() { + // Check that sufficient time has passed since last aggregation + // Otherwise we can just return + if (timer_->GetElapsedMs() < kStatsAggregationIntervalMSec) return false; + + // Sufficient time has passed, let's process + // Reset timer + timer_->Start(); + // We are not exiting just yet so pass false for that argument + // And we don't have to force it, so pass false for forcing + return ProcessMetrics(false, false); +} + + + +void PluginLogging::RecordProcessTimes() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + ProcessSerialNumber myProcess = {0, kCurrentProcess}; + + NSDictionary* processDict = (NSDictionary*)ProcessInformationCopyDictionary( + &myProcess, + kProcessDictionaryIncludeAllInformationMask); + + ProcessInfoExtendedRec info; + bzero(&info, sizeof(info)); + GetProcessInformation(&myProcess, (ProcessInfoRec*)&info); + + NSDate* launchDate = [processDict objectForKey:@"LSLaunchTime"]; + NSDate* nowDate = [NSDate dateWithTimeIntervalSinceNow:0]; + NSTimeInterval uptime = [nowDate timeIntervalSinceDate:launchDate]; + + uint64 additional_uptime = uptime - prev_uptime_seconds_; + metric_uptime_seconds += additional_uptime; + running_time_ += additional_uptime; + prev_uptime_seconds_ = uptime; + + uint64 cputime = ((double)info.processActiveTime) / 60.0; + metric_cpu_time_seconds += (cputime - prev_cputime_seconds_); + prev_cputime_seconds_ = cputime; + + [pool release]; +} + +static NSString* generateGUID() { + CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); + NSString* returned = (NSString*)CFUUIDCreateString(kCFAllocatorDefault, + uuidRef); + CFRelease(uuidRef); + return returned; +} + + +// Read a pref string for current user, but global to all apps. +static NSString* ReadGlobalPreferenceString(NSString *key) { + return (NSString *)CFPreferencesCopyValue((CFStringRef)key, + kCFPreferencesAnyApplication, + kCFPreferencesCurrentUser, + kCFPreferencesCurrentHost); +} + + +// Write a pref string for the current user, but global to all apps. +static void WriteGlobalPreferenceString(NSString *key, + NSString *value) { + CFPreferencesSetValue((CFStringRef)key, + (CFPropertyListRef)value, + kCFPreferencesAnyApplication, + kCFPreferencesCurrentUser, + kCFPreferencesCurrentHost); + + CFPreferencesSynchronize(kCFPreferencesAnyApplication, + kCFPreferencesCurrentUser, + kCFPreferencesCurrentHost); +} + + +static const char *GetUserID(void) { + static NSString* id_str = NULL; + + if (id_str == NULL) { + // if not in cache, try to read it + id_str = ReadGlobalPreferenceString(kUserKey); + + // if it was not stored, then create and store it + if (!id_str) { + id_str = generateGUID(); + WriteGlobalPreferenceString(kUserKey, id_str); + } + } + return [id_str UTF8String]; +} + + +bool PluginLogging::ProcessMetrics(const bool exiting, + const bool force_report) { + DLOG(INFO) << "ProcessMetrics()"; + // Grab incremental process times, has to be done each time + // around the loop, because - well - time passes between + // iterations :) + RecordProcessTimes(); + + // This mutex protects the writing to the registry. This way, + // if we have multiple instances attempting to aggregate at + // once, they won't overwrite one another. + + // 1. return if we can't get mutex + + + if (exiting) { + // If we're exiting, we aggregate to make sure that we record + // the tail activity for posterity. We don't report, because + // that might delay the process exit arbitrarily, and we don't + // want that. + // We also make sure to add a sample to the running time + metric_running_time_seconds.AddSample(running_time_); + DoAggregateMetrics(); + } else { + // TODO: placeholder - where is this supposed to come from? + std::string user_id(GetUserID()); + std::string ui_param("ui="); + std::string client_id_argument = ui_param + user_id; + std::string user_agent8 = std::string(kUserAgent) + + PRODUCT_VERSION_STRING; + DoAggregateAndReportMetrics(client_id_argument.c_str(), + user_agent8.c_str(), force_report); + } + + + // 2. release mutex + + return true; +} + +void PluginLogging::DoAggregateMetrics() { + DLOG(INFO) << "DoAggregateMetrics()"; + stats_report::AggregateMetrics(); +} + +bool PluginLogging::DoAggregateAndReportMetrics( + const char* extra_url_arguments, + const char* user_agent, + const bool force_report) { + DLOG(INFO) << "DoAggregateAndReportMetrics()"; + // AggregateAndReportMetrics returns true if metrics were uploaded + return stats_report::AggregateAndReportMetrics(extra_url_arguments, + user_agent, + force_report); +} + +void PluginLogging::SetTimer(HighresTimer* timer) { + timer_.reset(timer); +} + + +PluginLogging* PluginLogging::InitializeUsageStatsLogging() { + bool opt_in = GetOptInKeyValue(); + + return CreateUsageStatsLogger<PluginLogging>(opt_in); +} + +bool PluginLogging::GetOptInKeyValue(void) { +#ifdef NDEBUG + NSString *value = ReadGlobalPreferenceString(kPrefString); + return ![value isEqualToString:@"NO"]; +#else + return true; +#endif +} + +void PluginLogging::ClearLogs() { + stats_report::ResetPersistentMetrics(); +} + +} // namespace o3d diff --git a/o3d/plugin/mac/plugin_mac.h b/o3d/plugin/mac/plugin_mac.h new file mode 100644 index 0000000..8563228 --- /dev/null +++ b/o3d/plugin/mac/plugin_mac.h @@ -0,0 +1,106 @@ +/* + * 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_MAC_PLUGIN_MAC_H_ +#define O3D_PLUGIN_MAC_PLUGIN_MAC_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include <npupp.h> +#include <AGL/agl.h> +#include <vector> + +// Just for o3d::Event::Button at the moment. +#include "core/cross/event.h" + + +// RenderTimer maintains an animation timer (nominally running at 60fps) +// +// Keeps track of the current NPP instances running in the browser and then +// renders each one during each timer callback. +class RenderTimer { + public: + RenderTimer() {} + + void Start(); + void Stop(); + + void AddInstance(NPP instance); + void RemoveInstance(NPP instance); + + typedef std::vector<NPP>::iterator InstanceIterator; + + private: + RenderTimer(const RenderTimer&); + + static void TimerCallback(CFRunLoopTimerRef timer, void* info); + CFRunLoopTimerRef timerRef_; + static std::vector<NPP> instances_; +}; + +extern RenderTimer gRenderTimer; + +void InitializeBreakpad(); +void ShutdownBreakpad(); + + +void* SafariBrowserWindowForWindowRef(WindowRef theWindow); + +void* SelectedTabForSafariBrowserWindow(void* cocoaWindow); + +void ReleaseSafariBrowserWindow(void* browserWindow); + + +// Some miscellaneous helper functions... + +bool SetWindowForAGLContext(AGLContext context, WindowRef window); + +void CFReleaseIfNotNull(CFTypeRef cf); + +bool IsMacOSTenFiveOrHigher(void); + +// Converts an old style Mac HFS path eg "HD:Users:xxx:file.zip" into a standard +// Posix path eg "/Users/xxx/file.zip" Assumes UTF8 in and out, returns a block +// of memory allocated with new, so you'll want to delete this at some point. +// Returns NULL in the event of an error. +char* CreatePosixFilePathFromHFSFilePath(const char* hfsPath); + +bool HandleMacEvent(EventRecord* the_event, NPP instance); + +o3d::Event::Button MacOSMouseButtonNumberToO3DButton(int inButton); + +bool GetBrowserVersionInfo(int *returned_major, + int *returned_minor, + int *returned_bugfix); + +bool UseSoftwareRenderer(); + +#endif // O3D_PLUGIN_MAC_PLUGIN_MAC_H_ diff --git a/o3d/plugin/mac/plugin_mac.mm b/o3d/plugin/mac/plugin_mac.mm new file mode 100644 index 0000000..f245f3e --- /dev/null +++ b/o3d/plugin/mac/plugin_mac.mm @@ -0,0 +1,822 @@ +/* + * 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_mac.h" +#include <Breakpad/Breakpad.h> +#include <Cocoa/Cocoa.h> +#include "plugin/cross/o3d_glue.h" +#include "plugin/cross/main.h" +#include "core/mac/display_window_mac.h" + +BreakpadRef gBreakpadRef = NULL; + +using glue::_o3d::PluginObject; +using o3d::DisplayWindowMac; + +// Returns the version number of the running Mac browser, as parsed from +// the short version string in the plist of the app's bundle. +bool GetBrowserVersionInfo(int *returned_major, + int *returned_minor, + int *returned_bugfix) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int major = 0; + int minor = 0; + int bugfix = 0; + NSBundle *app_bundle = [NSBundle mainBundle]; + NSString *versionString = + [app_bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + + if ([versionString length]) { + NSScanner *versionScanner = [NSScanner scannerWithString:versionString]; + NSCharacterSet *dotSet = + [NSCharacterSet characterSetWithCharactersInString:@"."]; + NSCharacterSet *numSet = + [NSCharacterSet characterSetWithCharactersInString:@"0123456789"]; + NSCharacterSet *notNumSet = [numSet invertedSet]; + + // Skip anything at start that is not a number. + [versionScanner scanCharactersFromSet:notNumSet intoString:NULL]; + + // Keep reading values until we get all 3 numbers, or something isn't found. + if ([versionScanner scanInt:&major]) + if ([versionScanner scanCharactersFromSet:dotSet intoString:NULL]) + if ([versionScanner scanInt:&minor]) + if ([versionScanner scanCharactersFromSet:dotSet intoString:NULL]) + [versionScanner scanInt:&bugfix]; + } + + *returned_major = major; + *returned_minor = minor; + *returned_bugfix = bugfix; + + [pool release]; + + // If we read any version numbers, then we succeeded. + return (major || minor || bugfix); +} + + +// Code for peeking at the selected tab object for a WindowRef in Safari. This +// is purely so we can detect changes in active tab in Safari, since it does not +// notify us when our tab is hidden or revealed. +// +// If the browser is not Safari these functions should harmlessly be NULL since +// the walk from windowref to cocoa window (of type "BrowserWindow") to cocoa +// windowcontroller to selectedTab should harmlessly break down somewhere along +// the way. +// +// This is useful merely for peeking at Safari's internal state for a tab change +// event, as Safari does not notify us when this happens. It coerces the value +// to a void*, as the plain .cc files don't like the type id, and we actually +// only care whether the value changes or not. + + +@interface NSWindowController (plugin_hack) +- (id)selectedTab; +@end + +void ReleaseSafariBrowserWindow(void* browserWindow) { + NSWindow* cocoaWindow = (NSWindow*) browserWindow; + [cocoaWindow release]; +} + +void* SafariBrowserWindowForWindowRef(WindowRef theWindow) { + if (theWindow != NULL) { + NSWindow* cocoaWindow = [[NSWindow alloc] initWithWindowRef:theWindow]; + if (cocoaWindow) { + if (strcmp(object_getClassName(cocoaWindow), "BrowserWindow") == 0) { + return cocoaWindow; + } else { + [cocoaWindow release]; + } + } + } + return NULL; +} + +void* SelectedTabForSafariBrowserWindow(void* browserWindow) { + id selectedTab = nil; + NSWindow* cocoaWindow = (NSWindow*) browserWindow; + if (cocoaWindow) { + @try { + selectedTab = [[cocoaWindow windowController] selectedTab]; + } @catch(NSException* exc) { + } + } + return (void*) selectedTab; +} + +// Detects when Safari has hidden or revealed our tab. +// If a hide event is detected, it sets the mac_surface_hidden_ flag and hides +// the surface. +// Later, if the mac_surface_hidden_ flag is set but we are no longer hidden, +// ie DetectTabHiding is now returning false, it restores the surface to the +// previous state. +void ManageSafariTabSwitching(PluginObject* obj) { + if (obj->DetectTabHiding()) { + if (!obj->mac_surface_hidden_) { + obj->mac_surface_hidden_ = true; + GLint rect[4] = {0,0,0,0}; + aglSetInteger(obj->mac_agl_context_, AGL_BUFFER_RECT, rect); + aglEnable(obj->mac_agl_context_, AGL_BUFFER_RECT); + } + } else if (obj->mac_surface_hidden_) { + obj->mac_surface_hidden_ = false; + aglSetInteger(obj->mac_agl_context_, AGL_BUFFER_RECT, + obj->last_buffer_rect_); + aglEnable(obj->mac_agl_context_, AGL_BUFFER_RECT); + } +} + + +#pragma mark ____RenderTimer + +RenderTimer gRenderTimer; +std::vector<NPP> RenderTimer::instances_; + +void RenderTimer::Start() { + CFRunLoopTimerContext timerContext; + memset(&timerContext, 0, sizeof(timerContext)); + timerContext.info = (void*)NULL; + + if (!timerRef_) { + timerRef_ = CFRunLoopTimerCreate(NULL, + 1.0, + 1.0 / 60.0, + 0, + 0, + TimerCallback, + &timerContext); + + CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef_, kCFRunLoopCommonModes); + } +} + +void RenderTimer::Stop() { + if (timerRef_) { + CFRunLoopTimerInvalidate(timerRef_); + timerRef_ = NULL; + } + instances_.clear(); // this should already be empty, but make sure +} + +void RenderTimer::AddInstance(NPP instance) { + // avoid adding the same instance twice! + InstanceIterator i = find(instances_.begin(), instances_.end(), instance); + if (i == instances_.end()) { + instances_.push_back(instance); + } +} + +void RenderTimer::RemoveInstance(NPP instance) { + InstanceIterator i = find(instances_.begin(), instances_.end(), instance); + if (i != instances_.end()) { + instances_.erase(i); + } +} + +void RenderTimer::TimerCallback(CFRunLoopTimerRef timer, void* info) { + HANDLE_CRASHES; + for (int i = 0; i < instances_.size(); ++i) { + NPP instance = instances_[i]; + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + + ManageSafariTabSwitching(obj); + obj->client()->Tick(); + + // We're visible if (a) we are in fullscreen mode or (b) our cliprect + // height and width are both a sensible size, ie > 1 pixel. + // We don't check for 0 as we have to size to 1 x 1 on occasion rather than + // 0 x 0 to avoid crashing the Apple software renderer, but do not want to + // actually draw to a 1 x 1 pixel area. + bool plugin_visible = obj->GetFullscreenMacWindow() || + (obj->last_buffer_rect_[2] > 1 && obj->last_buffer_rect_[3] > 1); + + if (plugin_visible && obj->WantsRedraw()) { + obj->SetWantsRedraw(false); // for on-demand drawing + + // Force a sync to the VBL (once per timer callback) + // to avoid tearing + GLint sync = (i == 0); + if (obj->mac_cgl_context_) { + CGLSetParameter(obj->mac_cgl_context_, kCGLCPSwapInterval, &sync); + } else if (obj->mac_agl_context_) { + aglSetInteger(obj->mac_agl_context_, AGL_SWAP_INTERVAL, &sync); + } + + obj->client()->RenderClient(); + } + } +} + +#pragma mark ____BREAKPAD + +bool ExceptionCallback(int exception_type, + int exception_code, + mach_port_t crashing_thread) { + return BreakpadEnabler::IsEnabled(); +} + +void InitializeBreakpad() { + if (!gBreakpadRef) { + NSBundle* bundle = [NSBundle bundleWithIdentifier:@"com.google.o3d"]; + NSDictionary* info = [bundle infoDictionary]; + + gBreakpadRef = BreakpadCreate(info); + BreakpadSetFilterCallback(gBreakpadRef, ExceptionCallback); + } +} + +void ShutdownBreakpad() { + BreakpadRelease(gBreakpadRef); + gBreakpadRef = NULL; +} + +#pragma mark ____MISCELLANEOUS_HELPER + +void CFReleaseIfNotNull(CFTypeRef cf) { + if (cf != NULL) + CFRelease(cf); +} + + +// Given a WindowRef and an AGLContext, make the context draw in that window. +// Return Value: true if the window is successfully set, false otherwise. +bool SetWindowForAGLContext(AGLContext context, WindowRef window) { + return (IsMacOSTenFiveOrHigher()) ? + aglSetWindowRef(context, window) : + aglSetDrawable(context, GetWindowPort(window)); +} + + +// Converts an old style Mac HFS path eg "HD:Users:xxx:file.zip" into +// a standard Posix path eg "/Users/xxx/file.zip" +// Assumes UTF8 in and out, returns a block of memory allocated with new, +// so you'll want to delete this at some point. +// Returns NULL in the event of an error. +char* CreatePosixFilePathFromHFSFilePath(const char* hfsPath) { + CFStringRef cfHFSPath = NULL; + CFStringRef cfPosixPath = NULL; + CFURLRef cfHFSURL = NULL; + char* posix_path = NULL; + + if (hfsPath && hfsPath[0]) { + cfHFSPath = CFStringCreateWithCString(kCFAllocatorDefault, + hfsPath, + kCFStringEncodingUTF8); + if (cfHFSPath) { + cfHFSURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, + cfHFSPath, + kCFURLHFSPathStyle, + false); + if (cfHFSURL) { + cfPosixPath = CFURLCopyFileSystemPath(cfHFSURL, kCFURLPOSIXPathStyle); + if (cfPosixPath) { + // returned value includes space for 0 terminator byte + int maxSize = + CFStringGetMaximumSizeOfFileSystemRepresentation(cfPosixPath); + posix_path = new char [maxSize]; + CFStringGetFileSystemRepresentation(cfPosixPath, + posix_path, + maxSize); + } + } + } + } + CFReleaseIfNotNull(cfHFSPath); + CFReleaseIfNotNull(cfPosixPath); + CFReleaseIfNotNull(cfHFSURL); + return posix_path; +} + +// Returns whether OS is 10.5 (Leopard) or higher. +bool IsMacOSTenFiveOrHigher() { + static bool isCached = false, result = false; + + if (!isCached) { + SInt32 major = 0; + SInt32 minor = 0; + // These selectors don't exist pre 10.4 but as we check the error + // the function will correctly return NO which is the right answer. + result = ((::Gestalt(gestaltSystemVersionMajor, &major) == noErr) && + (::Gestalt(gestaltSystemVersionMinor, &minor) == noErr) && + ((major > 10) || (major == 10 && minor >= 5))); + isCached = true; + } + return result; +} + + +static Rect CGRect2Rect(const CGRect &inRect) { + Rect outRect; + outRect.left = inRect.origin.x; + outRect.top = inRect.origin.y; + outRect.right = inRect.origin.x + inRect.size.width; + outRect.bottom = inRect.origin.y + inRect.size.height; + return outRect; +} + +static CGRect Rect2CGRect(const Rect &inRect) { + CGRect outRect; + outRect.origin.x = inRect.left; + outRect.origin.y = inRect.top; + outRect.size.width = inRect.right - inRect.left; + outRect.size.height = inRect.bottom - inRect.top; + return outRect; +} + + +// A little wrapper for ATSUSetAttributes to make calling it with one attribute +// less annoying. +static void MySetAttribute(ATSUStyle style, + ATSUAttributeTag tag, + ByteCount size, + ATSUAttributeValuePtr value) { + + ATSUAttributeTag tags[2] = {tag, 0}; + ByteCount sizes[2] = {size, 0}; + ATSUAttributeValuePtr values[2] = {value, 0}; + + ATSUSetAttributes(style, 1, tags, sizes, values); +} + +// A little wrapper for ATSUSetLayoutControls to make calling it with one +// attribute less annoying. +static void MySetLayoutControl(ATSUTextLayout layout, + ATSUAttributeTag tag, + ByteCount size, + ATSUAttributeValuePtr value) { + + ATSUAttributeTag tags[2] = {tag, 0}; + ByteCount sizes[2] = {size, 0}; + ATSUAttributeValuePtr values[2] = {value, 0}; + + ATSUSetLayoutControls(layout, 1, tags, sizes, values); +} + +static OSStatus HandleOverlayWindow(EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, + void *inUserData) { + return noErr; +} + +static void PaintRoundedCGRect(CGContextRef context, + CGRect rect, + float radius, + bool fill) { + CGFloat lx = CGRectGetMinX(rect); + CGFloat cx = CGRectGetMidX(rect); + CGFloat rx = CGRectGetMaxX(rect); + CGFloat by = CGRectGetMinY(rect); + CGFloat cy = CGRectGetMidY(rect); + CGFloat ty = CGRectGetMaxY(rect); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, lx, cy); + CGContextAddArcToPoint(context, lx, by, cx, by, radius); + CGContextAddArcToPoint(context, rx, by, rx, cy, radius); + CGContextAddArcToPoint(context, rx, ty, cx, ty, radius); + CGContextAddArcToPoint(context, lx, ty, lx, cy, radius); + CGContextClosePath(context); + + if (fill) + CGContextFillPath(context); + else + CGContextStrokePath(context); +} + +// Convenience function for fetching SInt32 parameters from Carbon EventRefs. +static SInt32 GetIntEventParam(EventRef inEvent, EventParamName inName) { + SInt32 value = 0; + return (GetEventParameter(inEvent, inName, typeSInt32, NULL, sizeof(value), + NULL, &value) == noErr) ? value : 0; +} + + +#pragma mark ____OVERLAY_WINDOW + + +static void DrawToOverlayWindow(WindowRef overlayWindow) { +#define kTextWindowHeight 40 + + CGContextRef overlayContext = NULL; + OSStatus result = noErr; + // TODO this will need to be a localized string + char * display_text = "Press Esc to exit full screen mode."; + + UniChar* display_text_16 = NULL; + CFStringRef display_text_cfstr = NULL; + int textLength = 0; + + QDBeginCGContext(GetWindowPort(overlayWindow), &overlayContext); + + display_text_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, + display_text, + kCFStringEncodingUTF8); + textLength = CFStringGetLength(display_text_cfstr); + display_text_16 = (UniChar*)calloc(textLength + 1, sizeof(*display_text_16)); + CFStringGetCharacters(display_text_cfstr, + CFRangeMake(0, textLength), + display_text_16); + +#define kOverlayWindowFontName "Helvetica" + + CGFloat kWhiteOpaque[] = {1.0, 1.0, 1.0, 1.0}; + CGFloat kBlackOpaque[] = {0.0, 0.0, 0.0, 1.0}; + CGFloat kGreyNotOpaque[] = {0.5, 0.5, 0.5, 0.5}; + + Rect bounds = {0,0,100,100}; + GetWindowBounds(overlayWindow, kWindowContentRgn, &bounds); + + CGRect cgTotalRect = Rect2CGRect(bounds); + + CGContextSetShouldSmoothFonts(overlayContext, true); + CGContextClearRect(overlayContext, cgTotalRect); + + CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorRef textShadow = CGColorCreate(myColorSpace, kBlackOpaque); + CGColorRef roundRectBackColor = CGColorCreate(myColorSpace, kGreyNotOpaque); + CGSize shadowOffset = {0.0,0.0}; + + CGContextSetFillColor(overlayContext, kWhiteOpaque); + CGContextSetStrokeColor(overlayContext, kWhiteOpaque); + + if (strlen(display_text)) { + ATSUStyle style; + ATSUTextLayout layout; + ATSUFontID font; + Fixed pointSize = Long2Fix(36); + + ATSUCreateStyle(&style); + ATSUFindFontFromName(kOverlayWindowFontName, strlen(kOverlayWindowFontName), + kFontFullName, kFontNoPlatformCode, kFontNoScriptCode, + kFontNoLanguageCode, &font); + + MySetAttribute(style, kATSUFontTag, sizeof(font), &font); + MySetAttribute(style, kATSUSizeTag, sizeof(pointSize), &pointSize); + + ATSUCreateTextLayout(&layout); + ATSUSetTextPointerLocation(layout, display_text_16, + kATSUFromTextBeginning, kATSUToTextEnd, + textLength); + ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd); + + MySetLayoutControl(layout, kATSUCGContextTag, + sizeof(CGContextRef), &overlayContext); + + // Need to enable this for languages like Japanese to draw as something + // other than a series of squares. + ATSUSetTransientFontMatching(layout, true); + + CGContextSetFillColorWithColor(overlayContext, roundRectBackColor); + CGRect mine = CGRectMake((bounds.right/2.0) - 400.0, + (bounds.bottom/2.0)-30.0, 800.0, 80.0); + PaintRoundedCGRect(overlayContext, mine, 16.0, true); + + CGContextSetFillColor(overlayContext, kWhiteOpaque); + + CGContextSaveGState(overlayContext); + CGContextSetShadowWithColor(overlayContext, shadowOffset, 4.0, textShadow); + ATSUDrawText(layout, kATSUFromTextBeginning, kATSUToTextEnd, + X2Fix((bounds.right/2.0) - 300.0), X2Fix(bounds.bottom/2.0)); + CGContextRestoreGState(overlayContext); + + ATSUDisposeStyle(style); + ATSUDisposeTextLayout(layout); + } + + + CGColorRelease(roundRectBackColor); + CGColorRelease (textShadow); + CGColorSpaceRelease (myColorSpace); + CFReleaseIfNotNull(display_text_cfstr); + + QDEndCGContext(GetWindowPort(overlayWindow), &overlayContext); +} + + +static WindowRef CreateOverlayWindow(void) { + Rect bounds = CGRect2Rect(CGDisplayBounds(CGMainDisplayID())); + WindowClass wClass = kOverlayWindowClass; + WindowRef window = NULL; + OSStatus err = noErr; + WindowAttributes overlayAttributes = kWindowNoShadowAttribute | + kWindowIgnoreClicksAttribute | + kWindowNoActivatesAttribute | + kWindowStandardHandlerAttribute; + EventTypeSpec eventTypes[] = { + kEventClassWindow, kEventWindowDrawContent, + kEventClassWindow, kEventWindowShown + }; + + + err = CreateNewWindow(wClass, + overlayAttributes, + &bounds, + &window); + if (err) + return NULL; + + ShowWindow(window); + InstallEventHandler(GetWindowEventTarget(window), HandleOverlayWindow, + sizeof(eventTypes)/sizeof(eventTypes[0]), eventTypes, + NULL, NULL); + return window; +} + + +// Maps the MacOS button numbers to the constants used by our +// event mechanism. Not quite as obvious as you might think, as the Mac +// thinks the numbering should go left, right, middle and our W3C-influenced +// system goes left, middle, right. +// Defaults to left-button if passed a strange value. Pass Cocoa mouse button +// codes as-is (they start at 0), pass Carbon button codes - 1. +o3d::Event::Button MacOSMouseButtonNumberToO3DButton(int inButton) { + + switch(inButton) { + case 0: + return o3d::Event::BUTTON_LEFT; + case 1: + return o3d::Event::BUTTON_RIGHT; + case 2: + return o3d::Event::BUTTON_MIDDLE; + case 3: + return o3d::Event::BUTTON_4; + case 4: + return o3d::Event::BUTTON_5; + } + + return o3d::Event::BUTTON_LEFT; +} + + +#pragma mark ____FULLSCREEN_WINDOW + + +// Handles the CarbonEvents that we get sent for the fullscreen mode window. +// Most of these can be converted to EventRecord events and handled by the +// HandleMacEvent() function in main_mac.mm, but some have no equivalent in +// that space, scroll-wheel events for example, and so must be handled here. +static OSStatus HandleFullscreenWindow(EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, + void *inUserData) { + OSStatus err = noErr; + OSType event_class = GetEventClass(inEvent); + OSType event_kind = GetEventKind(inEvent); + NPP instance = (NPP)inUserData; + PluginObject* obj = (PluginObject*)(instance->pdata); + HIPoint mouse_loc = { 0.0, 0.0 }; + bool is_scroll_event = event_class == kEventClassMouse && + (event_kind == kEventMouseScroll || + event_kind == kEventMouseWheelMoved); + + // If it's any kind of mouse event, get the global mouse loc. + if (event_class == kEventClassMouse) { + GetEventParameter(inEvent, kEventParamMouseLocation, + typeHIPoint, NULL, + sizeof(mouse_loc), NULL, + &mouse_loc); + } + + // Handle the two kinds of scroll message we understand. + if (is_scroll_event) { + SInt32 x_scroll = 0; + SInt32 y_scroll = 0; + EventMouseWheelAxis axis = kEventMouseWheelAxisY; + + switch (event_kind) { + // The newer kind of scroll event, as sent when two-finger + // dragging on a touchpad. + case kEventMouseScroll: + x_scroll = GetIntEventParam(inEvent, + kEventParamMouseWheelSmoothHorizontalDelta); + y_scroll = GetIntEventParam(inEvent, + kEventParamMouseWheelSmoothVerticalDelta); + + // only pass x or y value - pass whichever is larger + if (x_scroll && y_scroll) { + if (abs(x_scroll) > abs(y_scroll)) + y_scroll = 0; + else + x_scroll = 0; + } + break; + // The older kind of scroll event, as sent when using the wheel on + // a third-party mouse. + case kEventMouseWheelMoved: + GetEventParameter(inEvent, kEventParamMouseWheelAxis, + typeMouseWheelAxis, NULL, + sizeof(axis), NULL, + &axis); + + if (axis == kEventMouseWheelAxisY) { + y_scroll = GetIntEventParam(inEvent, + kEventParamMouseWheelDelta); + } else { + x_scroll = GetIntEventParam(inEvent, + kEventParamMouseWheelDelta); + } + break; + } + + // Dispatch the event now that we have all the data. + if (x_scroll || y_scroll) { + o3d::Event event(o3d::Event::TYPE_WHEEL); + event.set_delta(x_scroll, y_scroll); + // Global and local locs are the same, in this case, + // as we have a fullscreen window at 0,0. + event.set_position(mouse_loc.x, mouse_loc.y, + mouse_loc.x, mouse_loc.y, true); + obj->client()->AddEventToQueue(event); + } + return noErr; + } else if (event_class == kEventClassMouse && + (event_kind == kEventMouseDown || event_kind == kEventMouseUp)) { + + o3d::Event::Type type = (event_kind == kEventMouseDown) ? + o3d::Event::TYPE_MOUSEDOWN : + o3d::Event::TYPE_MOUSEUP; + o3d::Event event(type); + event.set_position(mouse_loc.x, mouse_loc.y, + mouse_loc.x, mouse_loc.y, true); + + EventMouseButton button = 0; + GetEventParameter(inEvent, kEventParamMouseButton, + typeMouseButton, NULL, + sizeof(button), NULL, + &button); + // Carbon mouse button numbers start at 1, so subtract 1 here - + // Cocoa mouse buttons, by contrast, start at 0). + event.set_button(MacOSMouseButtonNumberToO3DButton(button - 1)); + + // add the modifiers to the event, if any + UInt32 carbonMods = GetIntEventParam(inEvent, + kEventParamKeyModifiers); + if (carbonMods) { + int modifier_state = 0; + if (carbonMods & controlKey) { + modifier_state |= o3d::Event::MODIFIER_CTRL; + } + if (carbonMods & shiftKey) { + modifier_state |= o3d::Event::MODIFIER_SHIFT; + } + if (carbonMods & optionKey) { + modifier_state |= o3d::Event::MODIFIER_ALT; + } + if (carbonMods & cmdKey) { + modifier_state |= o3d::Event::MODIFIER_META; + } + + event.set_modifier_state(modifier_state); + } + + obj->client()->AddEventToQueue(event); + } else { // not a scroll event or a click + + // All other events are currently handled by being converted to an + // old-style EventRecord as passed by the classic NPAPI interface + // and dispatched through our common routine. + EventRecord eventRecord; + + if (ConvertEventRefToEventRecord(inEvent, &eventRecord)) { + HandleMacEvent(&eventRecord, (NPP)inUserData); + return noErr; + } else { + return eventNotHandledErr; + } + } +} + +static WindowRef CreateFullscreenWindow(PluginObject *obj, + int mode_id) { + Rect bounds = CGRect2Rect(CGDisplayBounds(CGMainDisplayID())); + WindowRef window = NULL; + OSStatus err = noErr; + EventTypeSpec eventTypes[] = { + kEventClassKeyboard, kEventRawKeyDown, + kEventClassKeyboard, kEventRawKeyRepeat, + kEventClassKeyboard, kEventRawKeyUp, + kEventClassMouse, kEventMouseDown, + kEventClassMouse, kEventMouseUp, + kEventClassMouse, kEventMouseMoved, + kEventClassMouse, kEventMouseDragged, + kEventClassMouse, kEventMouseScroll, + kEventClassMouse, kEventMouseWheelMoved + }; + + err = CreateNewWindow(kSimpleWindowClass, + kWindowStandardHandlerAttribute, + &bounds, + &window); + if (err) + return NULL; + + InstallEventHandler(GetWindowEventTarget(window), HandleFullscreenWindow, + sizeof(eventTypes)/sizeof(eventTypes[0]), eventTypes, + obj->npp(), NULL); + ShowWindow(window); + return window; +} + +void CleanupFullscreenWindow(PluginObject *obj) { + if (obj->GetFullscreenMacWindow()) { + DisposeWindow(obj->GetFullscreenMacWindow()); + obj->SetFullscreenMacWindow(NULL); + } + + if (obj->GetFullscreenOverlayMacWindow()) { + DisposeWindow(obj->GetFullscreenOverlayMacWindow()); + obj->SetFullscreenOverlayMacWindow(NULL); + } +} + + +bool PluginObject::RequestFullscreenDisplay() { +#ifndef NDEBUG // TODO: Remove after all security is in. + // If already in fullscreen mode, do nothing. + if (GetFullscreenMacWindow()) + return false; + + SetSystemUIMode(kUIModeAllSuppressed, kUIOptionAutoShowMenuBar); + SetFullscreenMacWindow( + CreateFullscreenWindow(this, fullscreen_region_mode_id_)); + + Rect bounds = {0,0,0,0}; + GetWindowBounds(GetFullscreenMacWindow(), kWindowContentRgn, &bounds); + + SetWindowForAGLContext(mac_agl_context_, GetFullscreenMacWindow()); + aglDisable(mac_agl_context_, AGL_BUFFER_RECT); + renderer()->SetClientOriginOffset(0, 0); + renderer_->Resize(bounds.right - bounds.left, bounds.bottom - bounds.top); + + const double kFadeOutTime = 3.0; + SetFullscreenOverlayMacWindow(CreateOverlayWindow()); + DrawToOverlayWindow(GetFullscreenOverlayMacWindow()); + TransitionWindowOptions options = {0, kFadeOutTime, NULL, NULL}; + TransitionWindowWithOptions(GetFullscreenOverlayMacWindow(), + kWindowFadeTransitionEffect, + kWindowHideTransitionAction, + NULL, true, &options); + return true; +#else + return false; +#endif +} + +void PluginObject::CancelFullscreenDisplay() { +#ifndef NDEBUG // TODO: Remove after user-prompt feature goes in. + + // if not in fullscreen mode, do nothing + if (!GetFullscreenMacWindow()) + return; + + //fullscreen_ = false; + SetWindowForAGLContext(mac_agl_context_, mac_window_); + aglSetInteger(mac_agl_context_, AGL_BUFFER_RECT, last_buffer_rect_); + aglEnable(mac_agl_context_, AGL_BUFFER_RECT); + renderer_->Resize(prev_width_, prev_height_); + CleanupFullscreenWindow(this); + + // Somehow the browser window does not automatically activate again + // when we close the fullscreen window, so explicitly reactivate it. + if (mac_cocoa_window_) { + NSWindow* browser_window = (NSWindow*) mac_cocoa_window_; + [browser_window makeKeyAndOrderFront:browser_window]; + } else { + SelectWindow(mac_window_); + } + + SetSystemUIMode(kUIModeNormal, 0); +#endif +} + diff --git a/o3d/plugin/mac/plugin_metrics-mac.cc b/o3d/plugin/mac/plugin_metrics-mac.cc new file mode 100644 index 0000000..56c3a14 --- /dev/null +++ b/o3d/plugin/mac/plugin_metrics-mac.cc @@ -0,0 +1,85 @@ +/* + * 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 "statsreport/metrics.h" +#include "plugin/cross/plugin_metrics.h" + +namespace o3d { +DEFINE_METRIC_integer(system_type); + +DEFINE_METRIC_integer(mac_major_version); +DEFINE_METRIC_integer(mac_minor_version); +DEFINE_METRIC_integer(mac_bugfix_version); + +// User GPU +DEFINE_METRIC_integer(gpu_vendor_id); +DEFINE_METRIC_integer(gpu_device_id); +DEFINE_METRIC_integer(gpu_driver_major_version); +DEFINE_METRIC_integer(gpu_driver_minor_version); +DEFINE_METRIC_integer(gpu_driver_bugfix_version); +DEFINE_METRIC_integer(gpu_vram_size); +DEFINE_METRIC_bool(direct3d_available); + + +// Shader versions +DEFINE_METRIC_integer(pixel_shader_main_version); +DEFINE_METRIC_integer(pixel_shader_sub_version); +DEFINE_METRIC_integer(vertex_shader_main_version); +DEFINE_METRIC_integer(vertex_shader_sub_version); + +DEFINE_METRIC_bool(POW2_texture_caps); +DEFINE_METRIC_bool(NONPOW2CONDITIONAL_texture_caps); + +DEFINE_METRIC_integer(browser_type); +DEFINE_METRIC_integer(browser_major_version); +DEFINE_METRIC_integer(browser_minor_version); +DEFINE_METRIC_integer(browser_bugfix_version); + +DEFINE_METRIC_timing(running_time); + +DEFINE_METRIC_count(uptime_seconds); +DEFINE_METRIC_count(cpu_time_seconds); +DEFINE_METRIC_timing(running_time_seconds); + +DEFINE_METRIC_count(crashes_total); +DEFINE_METRIC_count(crashes_uploaded); +DEFINE_METRIC_count(out_of_memory_total); + +DEFINE_METRIC_count(bluescreens_total); + +// OpenGL Caps - insert more here +DEFINE_METRIC_integer(gl_major_version); +DEFINE_METRIC_integer(gl_minor_version); +DEFINE_METRIC_integer(gl_hlsl_major_version); +DEFINE_METRIC_integer(gl_hlsl_minor_version); + +} // namespace o3d 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_ diff --git a/o3d/plugin/o3d_binding.py b/o3d/plugin/o3d_binding.py new file mode 100644 index 0000000..7d7490c --- /dev/null +++ b/o3d/plugin/o3d_binding.py @@ -0,0 +1,522 @@ +#!/usr/bin/python2.4 +# 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. + + +"""o3d binding model module. + +This module implements the glue functions for the o3d binding model, binding +O3D objects. + +In C++, objects using this binding model are passed and returned by pointer. +For example +void SetValue(Class *value); +Class *GetValue(); + +For JS bindings, the browser object holds an id, representing the C++ object +through that can be accessed through the Client object. +""" + + +import string + +import cpp_utils +import java_utils + + +class CallingConstructor(Exception): + """Raised when trying to call a constructor on an O3D object.""" + pass + + +def JavaMemberString(scope, type_defn): + """Gets the representation of a member name in Java. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + + Returns: + a string representing the type + """ + return java_utils.GetScopedName(scope, type_defn) + + +def CppTypedefString(scope, type_defn): + """Gets the representation of a type when used in a C++ typedef. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + + Returns: + a (string, boolean) pair, the first element being the representation of + the type, the second element indicating whether or not the definition of + the type is needed for the expression to be valid. + """ + return cpp_utils.GetScopedName(scope, type_defn), False + + +def CppMemberString(scope, type_defn): + """Gets the representation of a type when used as a C++ class member. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + + Returns: + a (string, boolean) pair, the first element being the representation of + the type, the second element indicating whether or not the definition of + the type is needed for the expression to be valid. + """ + return '%s*' % cpp_utils.GetScopedName(scope, type_defn), False + + +def CppReturnValueString(scope, type_defn): + """Gets the representation of a type when used as a C++ function return value. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + + Returns: + a (string, boolean) pair, the first element being the representation of + the type, the second element indicating whether or not the definition of + the type is needed for the expression to be valid. + """ + return '%s*' % cpp_utils.GetScopedName(scope, type_defn), False + + +def CppParameterString(scope, type_defn): + """Gets the representation of a type when used for a function parameter. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + + Returns: + a (string, boolean) pair, the first element being the representation of + the type, the second element indicating whether or not the definition of + the type is needed for the expression to be valid. + """ + return '%s*' % cpp_utils.GetScopedName(scope, type_defn), False + + +def CppMutableParameterString(scope, type_defn): + """Gets the representation of a type for a mutable function parameter. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + + Returns: + a (string, boolean) pair, the first element being the string representing + the type, the second element indicating whether or not the definition of + the type is needed for the expression to be valid. + """ + return '%s*' % cpp_utils.GetScopedName(scope, type_defn), False + + +def CppMutableToNonMutable(scope, type_defn, expr): + """Gets the string converting a mutable expression to a non-mutable one. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + expr: a string for the mutable expression. + + Returns: + a string, which is the non-mutable expression. + """ + (scope, type_defn) = (scope, type_defn) # silence gpylint. + return expr + + +def CppBaseClassString(scope, type_defn): + """Gets the representation of a type for a base class. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition for the type. + + Returns: + a (string, boolean) pair, the first element being the string representing + the type, the second element indicating whether or not the definition of + the type is needed for the expression to be valid. + """ + return cpp_utils.GetScopedName(scope, type_defn) + + +def CppCallMethod(scope, type_defn, object_expr, mutable, method, param_exprs): + """Gets the representation of a member function call. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition, representing the type of the object being called. + object_expr: a string, which is the expression for the object being called. + mutable: a boolean, whether or not the 'object_expr' expression is mutable + or not + method: a Function, representing the function to call. + param_exprs: a list of strings, each being the expression for the value of + each parameter. + + Returns: + a string, which is the expression for the function call. + """ + (scope, type_defn, mutable) = (scope, type_defn, mutable) # silence gpylint. + return '%s->%s(%s)' % (object_expr, method.name, ', '.join(param_exprs)) + + +def CppCallStaticMethod(scope, type_defn, method, param_exprs): + """Gets the representation of a static function call. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition, representing the type of the object being called. + method: a Function, representing the function to call. + param_exprs: a list of strings, each being the expression for the value of + each parameter. + + Returns: + a string, which is the expression for the function call. + """ + return '%s::%s(%s)' % (cpp_utils.GetScopedName(scope, type_defn), + method.name, ', '.join(param_exprs)) + + +def CppCallConstructor(scope, type_defn, method, param_exprs): + """Gets the representation of a constructor call. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition, representing the type of the object being called. + method: a Function, representing the constructor to call. + param_exprs: a list of strings, each being the expression for the value of + each parameter. + + Returns: + a string, which is the expression for the constructor call. + + Raises: + CallingConstructor: always. O3D objects can't be constructed directly. + """ + raise CallingConstructor + + +def CppSetField(scope, type_defn, object_expr, field, param_expr): + """Gets the representation of an expression setting a field in an object. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition, representing the type of the object containing the + field being set. + object_expr: a string, which is the expression for the object containing + the field being set. + field: a string, the name of the field to be set. + param_expr: a strings, being the expression for the value to be set. + + Returns: + a string, which is the expression for setting the field. + """ + (scope, type_defn) = (scope, type_defn) # silence gpylint. + return '%s->%s(%s)' % (object_expr, cpp_utils.GetSetterName(field), + param_expr) + + +def CppGetField(scope, type_defn, object_expr, field): + """Gets the representation of an expression getting a field in an object. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition, representing the type of the object containing the + field being retrieved. + object_expr: a string, which is the expression for the object containing + the field being retrieved. + field: a string, the name of the field to be retrieved. + + Returns: + a string, which is the expression for getting the field. + """ + (scope, type_defn) = (scope, type_defn) # silence gpylint. + return '%s->%s()' % (object_expr, cpp_utils.GetGetterName(field)) + + +def CppSetStatic(scope, type_defn, field, param_expr): + """Gets the representation of an expression setting a static field. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition, representing the type of the object containing the + field being set. + field: a string, the name of the field to be set. + param_expr: a strings, being the expression for the value to be set. + + Returns: + a string, which is the expression for setting the field. + """ + return '%s::%s(%s)' % (cpp_utils.GetScopedName(scope, type_defn), + cpp_utils.GetSetterName(field), param_expr) + + +def CppGetStatic(scope, type_defn, field): + """Gets the representation of an expression getting a static field. + + Args: + scope: a Definition for the scope in which the expression will be written. + type_defn: a Definition, representing the type of the object containing the + field being retrieved. + field: a string, the name of the field to be retrieved. + + Returns: + a string, which is the expression for getting the field. + """ + return '%s::%s()' % (cpp_utils.GetScopedName(scope, type_defn), + cpp_utils.GetGetterName(field)) + + +_binding_glue_header_template = string.Template('') + + +def NpapiBindingGlueHeader(scope, type_defn): + """Gets the NPAPI glue header for a given type. + + Args: + scope: a Definition for the scope in which the glue will be written. + type_defn: a Definition, representing the type. + + Returns: + a string, the glue header. + """ + class_name = cpp_utils.GetScopedName(scope, type_defn) + return _binding_glue_header_template.substitute(Class=class_name) + + +_binding_glue_cpp_template = string.Template(""" +void InitializeGlue(NPP npp) { + InitializeIds(npp); + glue::_o3d::RegisterType(npp, ${Class}::GetApparentClass(), &npclass); +} + +glue::_o3d::NPAPIObject *GetNPObject(NPP npp, ${Class}* object) { + return glue::_o3d::GetNPObject(npp, object); +} + +static NPObject *Allocate(NPP npp, NPClass *theClass) { + return glue::_o3d::Allocate(npp, theClass); +} + +static void Deallocate(NPObject *header) { + return glue::_o3d::Deallocate(header); +} +""") + + +def NpapiBindingGlueCpp(scope, type_defn): + """Gets the NPAPI glue implementation for a given type. + + Args: + scope: a Definition for the scope in which the glue will be written. + type_defn: a Definition, representing the type. + + Returns: + a string, the glue implementation. + """ + class_name = cpp_utils.GetScopedName(scope, type_defn) + return _binding_glue_cpp_template.substitute(Class=class_name) + + +dispatch_function_header_template = string.Template(""" +glue::_o3d::NPAPIObject *${variable_npobject} = + static_cast<glue::_o3d::NPAPIObject *>(header); +NPP ${npp} = ${variable_npobject}->npp(); +${Class} *${variable} = glue::_o3d::GetClient( + ${npp})->GetById<${Class}>(${variable_npobject}->id()); +${result} = (${variable} != NULL); +if (!${result}) { + O3D_ERROR(glue::_o3d::GetServiceLocator(${npp})) << + "Invalid object; perhaps it's been destroyed already?"; +} +""") + + +def NpapiDispatchFunctionHeader(scope, type_defn, variable, npp, success): + """Gets a header for NPAPI glue dispatch functions. + + This function creates a string containing a C++ code snippet that should be + included at the beginning of NPAPI glue dispatch functions like Invoke or + GetProperty. This code snippet will declare and initialize certain variables + that will be used in the dispatch functions, like the NPObject representing + the object, or a pointer to the NPP instance. + + Args: + scope: a Definition for the scope in which the glue will be written. + type_defn: a Definition, representing the type. + variable: a string, representing a name of a variable that can be used to + store a reference to the object. + npp: a string, representing the name of the variable that holds the pointer + to the NPP instance. Will be declared by the code snippet. + success: the name of a bool variable containing the current success status. + (is not declared by the code snippet). + + Returns: + a (string, string) pair, the first string being the code snippet, and the + second string being an expression to access the object. + """ + class_name = cpp_utils.GetScopedName(scope, type_defn) + variable_npobject = '%s_npobject' % variable + text = dispatch_function_header_template.substitute( + variable_npobject=variable_npobject, npp=npp, variable=variable, + Class=class_name, result=success) + return text, variable + + +from_npvariant_template = string.Template(""" +${Class} *${variable} = NULL; +if (NPVARIANT_IS_OBJECT(${input})) { + NPObject *npobject = NPVARIANT_TO_OBJECT(${input}); + if (glue::_o3d::CheckObject(npp, npobject, + ${Class}::GetApparentClass())) { + glue::_o3d::NPAPIObject *client_object = + static_cast<glue::_o3d::NPAPIObject *>(npobject); + ${variable} = + glue::_o3d::GetClient(${npp})->GetById<${Class}>( + client_object->id()); + if (${variable} == NULL) { + ${result} = false; + *error_handle = "Error in " ${context} + ": input wasn't a valid object from this plugin instance."; + } else { + ${result} = true; + } + } else { + *error_handle = "Error in " ${context} + ": invalid type."; + ${result} = false; + } +} else { + *error_handle = "Error in " ${context} + ": was expecting an object."; + ${result} = false; +} +""") + + +def NpapiFromNPVariant(scope, type_defn, input_expr, variable, success, + exception_context, npp): + """Gets the string to get a value from a NPVariant. + + This function creates a string containing a C++ code snippet that is used to + retrieve a value from a NPVariant. If an error occurs, like if the NPVariant + is not of the correct type, the snippet will set the success status variable + to false and set *error_handle with an appropriate error message. + + Args: + scope: a Definition for the scope in which the glue will be written. + type_defn: a Definition, representing the type of the value. + input_expr: an expression representing the NPVariant to get the value from. + variable: a string, representing a name of a variable that can be used to + store a reference to the value. + success: the name of a bool variable containing the current success status. + exception_context: the name of a string containing context information, for + use in exception reporting. + npp: a string, representing the name of the variable that holds the pointer + to the NPP instance. + + Returns: + a (string, string) pair, the first string being the code snippet and the + second one being the expression to access that value. + """ + class_name = cpp_utils.GetScopedName(scope, type_defn) + text = from_npvariant_template.substitute(Class=class_name, + variable=variable, + input=input_expr, + npp=npp, + result=success, + context=exception_context) + return (text, variable) + + +expr_to_npobject_template = string.Template(""" +glue::_o3d::NPAPIObject *${variable} = + glue::_o3d::GetNPObject(${npp}, ${expr}); +if (!${variable}) { + *error_handle = "Error : type cannot be null."; + ${result} = false; +} +""") + +npobject_to_npvariant_template = string.Template(""" +OBJECT_TO_NPVARIANT(${variable}, *${output}); +""") + + +def NpapiExprToNPVariant(scope, type_defn, variable, expression, output, + success, npp): + """Gets the string to store a value into a NPVariant. + + This function creates a string containing a C++ code snippet that is used to + store a value into a NPVariant. That operation takes two phases, one that + allocates necessary NPAPI resources, and that can fail, and one that actually + sets the NPVariant (that can't fail). If an error occurs, the snippet will + set the success status variable to false. + + Args: + scope: a Definition for the scope in which the glue will be written. + type_defn: a Definition, representing the type of the value. + variable: a string, representing a name of a variable that can be used to + store a reference to the value. + expression: a string representing the expression that yields the value to + be stored. + output: an expression representing a pointer to the NPVariant to store the + value into. + success: the name of a bool variable containing the current success status. + npp: a string, representing the name of the variable that holds the pointer + to the NPP instance. + + Returns: + a (string, string) pair, the first string being the code snippet for the + first phase, and the second one being the code snippet for the second phase. + """ + (scope, type_defn) = (scope, type_defn) # silence gpylint. + phase_1_text = expr_to_npobject_template.substitute(variable=variable, + npp=npp, + expr=expression, + result=success) + phase_2_text = npobject_to_npvariant_template.substitute( + variable=variable, + output=output, + result=success) + return phase_1_text, phase_2_text + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/o3d/plugin/o3d_iface_generator.py b/o3d/plugin/o3d_iface_generator.py new file mode 100644 index 0000000..82092bf --- /dev/null +++ b/o3d/plugin/o3d_iface_generator.py @@ -0,0 +1,618 @@ +#!/usr/bin/python2.4 +# 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. + + +"""O3D interface class generator. + +This module generates the O3D interface classes. +""" + + +import os +import syntax_tree +import cpp_utils +import naming + +class CircularDefinition(Exception): + """Raised when a circular type definition is found.""" + + def __init__(self, type_defn): + Exception.__init__(self) + self.type = type_defn + + +class BadForwardDeclaration(Exception): + """Raised when an impossible forward declaration is required.""" + + +def ForwardDecl(section, type_defn): + """Emits the forward declaration of a type, if possible. + + Inner types (declared inside a class) cannot be forward-declared. + Only classes can be forward-declared. + + Args: + section: the section to emit to. + type_defn: the Definition for the type to forward-declare. + + Raises: + BadForwardDeclaration: an inner type or a non-class was passed as an + argument. + """ + # inner types cannot be forward-declared + if type_defn.parent.defn_type != 'Namespace': + raise BadForwardDeclaration + stack = type_defn.GetParentScopeStack() + if type_defn.defn_type == 'Class': + for scope in stack: + if scope.name: + section.PushNamespace(scope.name) + section.EmitCode('class %s;' % type_defn.name) + for scope in stack[::-1]: + if scope.name: + section.PopNamespace() + else: + raise BadForwardDeclaration + + +class O3DInterfaceGenerator(object): + """Header generator class. + + This class takes care of the details of generating a C++ header file + containing all the definitions from a syntax tree. + + It contains a list of functions named after each of the Definition classes in + syntax_tree, with a common signature. The appropriate function will be called + for each definition, to generate the code for that definition. + """ + + class GenerationContext(object): + def __init__(self, header_scope, cpp_scope, header_section, cpp_section): + self.header_scope = header_scope + self.header_section = header_section + self.cpp_scope = cpp_scope + self.cpp_section = cpp_section + self.needed_decl = set() + self.needed_defn = set() + self.emitted_defn = set() + + def Fork(self, header_scope, cpp_scope, header_section, cpp_section): + new_context = type(self)(header_scope, cpp_scope, header_section, + cpp_section) + new_context.needed_decl = self.needed_decl + new_context.needed_defn = self.needed_defn + new_context.emitted_defn = self.emitted_defn + return new_context + + def CheckType(self, need_defn, type_defn): + """Checks for the definition or declaration of a type. + + This function helps keeping track of which types are needed to be defined + or declared in the C++ file before other definitions can happen. If the + definition is needed (and is not in this C++ header file), the proper + include will be generated. If the type only needs to be forward-declared, + the forward declaration will be output (if the type is not otherwise + defined). + + Args: + need_defn: a boolean, True if the C++ definition of the type is needed, + False if only the declaration is needed. + type_defn: the Definition of the type to check. + """ + while type_defn.defn_type == 'Array': + # arrays are implicitly defined with their data type + type_defn = type_defn.data_type + if need_defn: + if type_defn not in self.emitted_defn: + self.needed_defn.add(type_defn) + else: + if type_defn in self.emitted_defn: + return + if type_defn.parent and type_defn.parent.defn_type != 'Namespace': + # inner type: need the definition of the parent. + self.CheckType(True, type_defn.parent) + else: + # only forward-declare classes. + # typedefs could be forward-declared by emitting the definition again, + # but this necessitates the source type to be forward-declared before. + # TODO: see if it is possible to find a proper ordering that let + # us forward-declare typedefs instead of needing to include the + # definition. + if type_defn.defn_type == 'Class': + self.needed_decl.add(type_defn) + else: + self.needed_defn.add(type_defn) + + def __init__(self, output_dir, namespace): + self._output_dir = output_dir + self._void_type = namespace.LookupTypeRecursive('void') + + def GetHeaderFile(self, idl_file): + return idl_file.source.split('.')[0] + '.h' + + def GetCppFile(self, idl_file): + return idl_file.source.split('.')[0] + '.cc' + + def GetInterfaceInclude(self, type_defn): + if self.NeedsGlue(type_defn): + return self.GetHeaderFile(type_defn.source.file) + else: + return type_defn.GetDefinitionInclude() + + def GetImplementationInclude(self, type_defn): + return type_defn.GetDefinitionInclude() + + def IsVoid(self, type_defn): + return type_defn.GetFinalType() == self._void_type + + def NeedsGlue(self, obj): + return obj.LookupBindingModel() == 'o3d' or 'glue_iface' in obj.attributes + + def GetSectionFromAttributes(self, parent_section, defn): + """Gets the code section appropriate for a given definition. + + Classes have 3 definition sections: private, protected and public. This + function will pick one of the sections, based on the attributes of the + definition, if its parent is a class. For other scopes (namespaces) it will + return the parent scope main section. + + Args: + parent_section: the main section for the parent scope. + defn: the definition. + + Returns: + the appropriate section. + """ + if defn.parent and defn.parent.defn_type == 'Class': + if 'private' in defn.attributes: + return parent_section.GetSection('private:') or parent_section + elif 'protected' in defn.attributes: + return parent_section.GetSection('protected:') or parent_section + else: + return parent_section.GetSection('public:') or parent_section + else: + return parent_section + + def Verbatim(self, context, obj): + """Generates the code for a Verbatim definition. + + Verbatim definitions being written for a particular type of output file, + this function will check the 'verbatim' attribute, and only emit the + verbatim code if it is 'cpp_header'. + + Args: + parent_section: the main section of the parent scope. + obj: the Verbatim definition. + """ + try: + verbatim_attr = obj.attributes['verbatim'] + except KeyError: + source = obj.source + print ('%s:%d ignoring verbatim with no verbatim attribute' % + (source.file.source, source.line)) + return + if verbatim_attr == 'o3d_iface_header': + section = self.GetSectionFromAttributes(context.header_section, obj) + section.EmitCode(obj.text) + elif verbatim_attr == 'o3d_iface_cpp': + context.cpp_section.EmitCode(obj.text) + + def Typedef(self, context, obj): + """Generates the code for a Typedef definition. + + Args: + parent_section: the main section of the parent scope. + obj: the Typedef definition. + + Returns: + a list of (boolean, Definition) pairs, of all the types that need + to be declared (boolean is False) or defined (boolean is True) before + this definition. + """ + section = self.GetSectionFromAttributes(context.header_section, obj) + bm = obj.type.binding_model + type_string, unused_need_defn = bm.CppTypedefString(context.header_scope, + obj.type) + context.CheckType(True, obj.type) + section.EmitCode('typedef %s %s;' % (type_string, obj.name)) + + def Variable(self, context, obj): + """Generates the code for a Variable definition. + + This function will generate the member/global variable declaration, as well + as the setter/getter functions if specified in the attributes. + + Args: + parent_section: the main section of the parent scope. + obj: the Variable definition. + """ + bm = obj.type.binding_model + type_string, need_defn = bm.CppMemberString(context.header_scope, obj.type) + context.CheckType(need_defn, obj.type) + need_glue = self.NeedsGlue(obj) or (obj.parent.is_type and + self.NeedsGlue(obj.parent)); + + getter_attributes = {} + if 'static' in obj.attributes: + getter_attributes['static'] = obj.attributes['static'] + static = 'static ' + else: + static = '' + for attr in ['public', 'protected', 'private']: + if attr in obj.attributes: + getter_attributes[attr] = obj.attributes[attr] + + if not need_glue: + if obj.parent.defn_type == 'Class': + if 'field_access' in obj.attributes: + member_section = context.header_section.GetSection( + obj.attributes['field_access'] + ':') + else: + member_section = context.header_section.GetSection('private:') + else: + member_section = context.header_section + field_name = naming.Normalize(obj.name, naming.LowerTrailing) + member_section.EmitCode('%s%s %s;' % (static, type_string, field_name)) + + if 'getter' in obj.attributes: + func = obj.MakeGetter(getter_attributes, cpp_utils.GetGetterName(obj)) + if need_glue: + self.FunctionGlue(context, func) + impl = None + else: + impl = ' { return %s; }' % field_name + self.FunctionDecl(context, func, impl) + if 'setter' in obj.attributes: + func = obj.MakeSetter(getter_attributes, cpp_utils.GetSetterName(obj)) + if need_glue: + self.FunctionGlue(context, func) + impl = None + else: + impl = ' { %s = %s; }' % (field_name, obj.name) + self.FunctionDecl(context, func, impl) + + def GetParamsDecls(self, scope, obj, context=None): + param_strings = [] + for p in obj.params: + bm = p.type.binding_model + if p.mutable: + text, need_defn = bm.CppMutableParameterString(scope, p.type) + else: + text, need_defn = bm.CppParameterString(scope, p.type) + if context: + context.CheckType(need_defn, p.type) + param_strings += ['%s %s' % (text, p.name)] + return ', '.join(param_strings) + + def FunctionDecl(self, context, obj, impl_string=None): + section = self.GetSectionFromAttributes(context.header_section, obj) + if not impl_string: + impl_string = ';' + params_string = self.GetParamsDecls(context.header_scope, obj, context) + prefix_strings = [] + suffix_strings = [] + for attrib in ['static', 'virtual', 'inline']: + if attrib in obj.attributes: + prefix_strings.append(attrib) + if prefix_strings: + prefix_strings.append('') + if 'const' in obj.attributes: + suffix_strings.append('const') + if 'pure' in obj.attributes: + suffix_strings.append('= 0') + if suffix_strings: + suffix_strings.insert(0, '') + prefix = ' '.join(prefix_strings) + suffix = ' '.join(suffix_strings) + if obj.type: + bm = obj.type.binding_model + return_type, need_defn = bm.CppReturnValueString(context.header_scope, + obj.type) + context.CheckType(need_defn, obj.type) + section.EmitCode('%s%s %s(%s)%s%s' % (prefix, return_type, obj.name, + params_string, suffix, impl_string)) + else: + section.EmitCode('%s%s(%s)%s%s' % (prefix, obj.name, params_string, + suffix, impl_string)) + + def FunctionGlue(self, context, obj): + if not obj.type: + # TODO autogen a factory + return + if 'pure' in obj.attributes: + return + + if obj.parent.is_type: + func_name = '%s::%s' % (obj.parent.name, obj.name) + if 'static' in obj.attributes: + call_prefix = 'impl::' + func_name + else: + # this_call + if self.NeedsGlue(obj.parent): + call_prefix = 'GetImpl()->' + else: + call_prefix = '' + else: + call_prefix = '' + func_name = obj.name + + params_string = self.GetParamsDecls(context.cpp_scope, obj) + param_exprs = [] + for p in obj.params: + if self.NeedsGlue(p.type): + param_exprs.append('%s->GetImpl()' % p.name) + else: + param_exprs.append(p.name) + + if not self.IsVoid(obj.type): + return_prefix = 'return ' + if self.NeedsGlue(obj.type): + return_suffix = '->GetIface()' + else: + return_suffix = '' + else: + return_prefix = '' + return_suffix = '' + + bm = obj.type.binding_model + return_type, unused = bm.CppReturnValueString(context.header_scope, + obj.type) + if 'const' in obj.attributes: + func_suffix = ' const' + else: + func_suffix = '' + + section = context.cpp_section + section.EmitCode('%s %s(%s)%s {' % (return_type, func_name, params_string, + func_suffix)) + section.EmitCode('%s%s%s(%s)%s;' % (return_prefix, call_prefix, obj.name, + ', '.join(param_exprs), return_suffix)) + section.EmitCode('}') + + def Function(self, context, obj): + """Generates the code for a Function definition. + + Args: + parent_section: the main section of the parent scope. + obj: the Function definition. + """ + self.FunctionDecl(context, obj) + if self.NeedsGlue(obj) or (obj.parent.is_type and + self.NeedsGlue(obj.parent)): + self.FunctionGlue(context, obj) + + def Class(self, context, obj): + """Generates the code for a Class definition. + + This function will recursively generate the code for all the definitions + inside the class. These definitions will be output into one of 3 sections + (private, protected, public), depending on their attributes. These + individual sections will only be output if they are not empty. + + Args: + parent_section: the main section of the parent scope. + obj: the Class definition. + """ + h_section = self.GetSectionFromAttributes(context.header_section, + obj).CreateSection(obj.name) + c_section = context.cpp_section + + need_glue = self.NeedsGlue(obj) + if need_glue: + h_section.PushNamespace('impl') + h_section.EmitCode('class %s;' % obj.name) + h_section.PopNamespace() + h_section.EmitCode('') + if obj.base_type: + bm = obj.base_type.binding_model + h_section.EmitCode('class %s : public %s {' % + (obj.name, bm.CppBaseClassString(context.header_scope, + obj.base_type))) + context.CheckType(True, obj.base_type) + else: + h_section.EmitCode('class %s {' % obj.name) + public_section = h_section.CreateSection('public:') + protected_section = h_section.CreateSection('protected:') + private_section = h_section.CreateSection('private:') + + new_context = context.Fork(obj, context.cpp_scope, h_section, c_section) + self.DefinitionList(new_context, obj.defn_list) + + if need_glue: + public_section.EmitCode('impl::%s *GetImpl();' % obj.name) + c_section.EmitCode('impl::%s *%s::GetImpl() {' % (obj.name, obj.name)) + c_section.EmitCode('return static_cast<impl::%s *>(impl_);' % obj.name) + c_section.EmitCode('}') + + if not public_section.IsEmpty(): + public_section.AddPrefix('public:') + if not protected_section.IsEmpty(): + protected_section.AddPrefix('protected:') + if not private_section.IsEmpty(): + private_section.AddPrefix('private:') + h_section.EmitCode('};') + + def Namespace(self, context, obj): + """Generates the code for a Namespace definition. + + This function will recursively generate the code for all the definitions + inside the namespace. + + Args: + parent_section: the main section of the parent scope. + obj: the Namespace definition. + """ + context.header_section.PushNamespace(obj.name) + context.cpp_section.PushNamespace(obj.name) + new_context = context.Fork(obj, obj, context.header_section, + context.cpp_section) + self.DefinitionList(new_context, obj.defn_list) + context.header_section.PopNamespace() + context.cpp_section.PopNamespace() + + def Typename(self, context, obj): + """Generates the code for a Typename definition. + + Since typenames (undefined types) cannot be expressed in C++, this function + will not output code. The definition may be output with a verbatim section. + + Args: + parent_section: the main section of the parent scope. + scope: the parent scope. + obj: the Typename definition. + """ + + def Enum(self, context, obj): + """Generates the code for an Enum definition. + + Args: + parent_section: the main section of the parent scope. + scope: the parent scope. + obj: the Enum definition. + """ + section = self.GetSectionFromAttributes(context.header_section, obj) + section.EmitCode('enum %s {' % obj.name) + for value in obj.values: + if value.value is None: + section.EmitCode('%s,' % value.name) + else: + section.EmitCode('%s = %s,' % (value.name, value.value)) + section.EmitCode('};') + + def DefinitionList(self, context, defn_list): + """Generates the code for all the definitions in a list. + + Args: + parent_section: the main section of the parent scope. + scope: the parent scope. + defn_list: the list of definitions. + """ + for obj in defn_list: + context.emitted_defn.add(obj) + # array types are implicitly defined + for k in obj.array_defns: + context.emitted_defn.add(obj.array_defns[k]) + getattr(self, obj.defn_type)(context, obj) + + def Generate(self, idl_file, namespace, defn_list): + """Generates the header file. + + Args: + idl_file: the source IDL file containing the definitions, as a + idl_parser.File instance. + namespace: a Definition for the global namespace. + defn_list: the list of top-level definitions. + + Returns: + a cpp_utils.CppFileWriter that contains the C++ header file code. + + Raises: + CircularDefinition: circular definitions were found in the file. + """ + all_defn = syntax_tree.GetObjectsRecursive(defn_list) + need_glue = False + for defn in all_defn: + if self.NeedsGlue(defn): + need_glue = True + break + if not need_glue: + return [] + + header_writer = cpp_utils.CppFileWriter( + '%s/%s' % (self._output_dir, self.GetHeaderFile(idl_file)), True) + + cpp_writer = cpp_utils.CppFileWriter( + '%s/%s' % (self._output_dir, self.GetCppFile(idl_file)), True) + + h_decl_section = header_writer.CreateSection('decls') + h_code_section = header_writer.CreateSection('defns') + c_code_section = cpp_writer.CreateSection('glue') + + context = self.GenerationContext(namespace, namespace, h_code_section, + c_code_section) + + self.DefinitionList(context, defn_list) + + context.needed_decl -= context.needed_defn + if context.needed_decl: + for type_defn in context.needed_decl: + # TODO: sort by namespace so that we don't open and close them + # more than necessary. + ForwardDecl(h_decl_section, type_defn) + h_decl_section.EmitCode('') + + for type_defn in context.needed_defn: + if type_defn.source.file == idl_file: + raise CircularDefinition(type_defn) + + h_includes = set(self.GetInterfaceInclude(type_defn) + for type_defn in context.needed_defn) + c_includes = set(self.GetImplementationInclude(type_defn) + for type_defn in context.emitted_defn + if self.NeedsGlue(type_defn)) + c_includes.add(self.GetHeaderFile(idl_file)) + + for include_file in h_includes: + if include_file is not None: + header_writer.AddInclude(include_file) + for include_file in c_includes: + if include_file is not None: + cpp_writer.AddInclude(include_file) + return [header_writer, cpp_writer] + + +def ProcessFiles(output_dir, pairs, namespace): + """Generates the headers for all input files. + + Args: + output_dir: the output directory. + pairs: a list of (idl_parser.File, syntax_tree.Definition list) describing + the list of top-level definitions in each source file. + namespace: a syntax_tree.Namespace for the global namespace. + + Returns: + a list of cpp_utils.CppFileWriter, one for each output file. + """ + output_dir = output_dir + '/iface' + if not os.access(output_dir + '/', os.F_OK): + os.makedirs(output_dir) + + generator = O3DInterfaceGenerator(output_dir, namespace) + writer_list = [] + for (f, defn) in pairs: + writer_list += generator.Generate(f, namespace, defn) + return writer_list + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/o3d/plugin/win/config.cc b/o3d/plugin/win/config.cc new file mode 100644 index 0000000..ef1fb39 --- /dev/null +++ b/o3d/plugin/win/config.cc @@ -0,0 +1,233 @@ +/* + * 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 code to check the hardware and software configuration of +// the client machine: +// - User agent (browser) +// - Windows version +// - GPU vendor + +// TODO: Waiting on posix updates to be able to include this in order +// to parse the useragent string for browser version. +// #include <regex.h> +#include <shlobj.h> +#include <shlwapi.h> +#include <tchar.h> +#include <windows.h> +#ifdef RENDERER_D3D9 +#include <d3d9.h> +#endif + +#include <string> +#include <iostream> +#include <fstream> + +#include "base/logging.h" +#include "plugin/cross/config.h" +#include "plugin/cross/plugin_metrics.h" +#include "core/cross/install_check.h" +#include "third_party/nixysa/files/static_glue/npapi/common.h" + +// Check Windows version. +bool CheckOSVersion(NPP npp) { + OSVERSIONINFOEX version = {sizeof(OSVERSIONINFOEX)}; // NOLINT + GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&version)); + if (version.dwMajorVersion == 5 && version.dwMinorVersion == 1) { + // NT 5.1 = Windows XP + if (version.wServicePackMajor < 2) { + // TODO: internationalize messages. + std::string error = std::string("Windows XP Service Pack 2 is required."); + if (!AskUser(npp, error)) return false; + } + } else if (version.dwMajorVersion == 6 && version.dwMinorVersion == 0) { + // 6.0 is Vista or Server 2008; it's now worth a try. + } else { + std::string error = std::string("Unsupported Windows version."); + if (!AskUser(npp, error)) return false; + } + return true; +} + +// Checks user agent string. We only allow Firefox, Chrome, and IE. +bool CheckUserAgent(NPP npp, const std::string &user_agent) { + if (user_agent.find("Firefox") == user_agent.npos && + user_agent.find("Chrome") == user_agent.npos && + user_agent.find("MSIE") == user_agent.npos) { + std::string error = std::string("Unsupported user agent: ") + user_agent; + return AskUser(npp, error); + } + return true; +} + +bool OpenDriverBlacklistFile(std::ifstream *input_file) { + CHECK(input_file); + CHECK(!input_file->is_open()); + + // Determine the full path. + // It will look something like: + // "c:\Documents and Settings\username\Application Data\Google\O3D\ + // driver_blacklist.txt" + + TCHAR app_data_path[MAX_PATH]; + HRESULT result = SHGetFolderPath( + NULL, + CSIDL_APPDATA, + NULL, + 0, + app_data_path); + + if (result != 0) { + return false; + } + + PathAppend(app_data_path, _T("Google\\O3D\\driver_blacklist.txt")); + if (!PathFileExists(app_data_path)) { + return false; + } + input_file->open(app_data_path, std::ifstream::in); + return input_file->good(); +} + +bool GetUserConfigMetrics() { + // Check Windows version. + o3d::metric_system_type = o3d::SYSTEM_NAME_WIN; + + OSVERSIONINFOEX version = {sizeof(OSVERSIONINFOEX)}; // NOLINT + GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&version)); + o3d::metric_windows_major_version = version.dwMajorVersion; + o3d::metric_windows_minor_version = version.dwMinorVersion; + o3d::metric_windows_sp_major_version = version.wServicePackMajor; + o3d::metric_windows_sp_minor_version = version.wServicePackMinor; + + // Check the device capabilities. +#ifdef RENDERER_D3D9 + // Check GPU vendor using D3D. + IDirect3D9 *d3d = Direct3DCreate9(D3D_SDK_VERSION); + if (!d3d) { + o3d::metric_direct3d_available.Set(false); + DLOG(ERROR) << "Direct3D9 is unavailable"; + return false; + } + o3d::metric_direct3d_available.Set(true); + D3DADAPTER_IDENTIFIER9 identifier; + HRESULT hr = d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); + D3DCAPS9 d3d_caps; + HRESULT caps_result = d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + &d3d_caps); + // Get GPU device information + if (hr != D3D_OK) { + DLOG(ERROR) << "Unable to get device ID"; + return false; + } + o3d::metric_gpu_vendor_id = identifier.VendorId; + o3d::metric_gpu_device_id = identifier.DeviceId; + o3d::metric_gpu_driver_major_version = identifier.DriverVersion.LowPart; + o3d::metric_gpu_driver_minor_version = identifier.DriverVersion.HighPart; + + // Need to release after we get the vram size + d3d->Release(); + + // Get shader versions + DWORD pixel_shader = d3d_caps.PixelShaderVersion; + o3d::metric_pixel_shader_main_version = + D3DSHADER_VERSION_MAJOR(pixel_shader); + o3d::metric_pixel_shader_sub_version = + D3DSHADER_VERSION_MINOR(pixel_shader); + DWORD vertex_shader = d3d_caps.VertexShaderVersion; + o3d::metric_vertex_shader_main_version = + D3DSHADER_VERSION_MAJOR(vertex_shader); + o3d::metric_vertex_shader_sub_version = + D3DSHADER_VERSION_MINOR(vertex_shader); + + // Detemine if device can handle NPoT textures + o3d::metric_POW2_texture_caps.Set( + (d3d_caps.TextureCaps & D3DPTEXTURECAPS_POW2) != 0); + o3d::metric_NONPOW2CONDITIONAL_texture_caps.Set( + (d3d_caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL) != 0); + + o3d::metric_d3d_devcaps = d3d_caps.DevCaps; + o3d::metric_d3d_misccaps = d3d_caps.PrimitiveMiscCaps; + o3d::metric_d3d_rastercaps = d3d_caps.RasterCaps; + o3d::metric_d3d_zcmpcaps = d3d_caps.ZCmpCaps; + o3d::metric_d3d_srcblendcaps = d3d_caps.SrcBlendCaps; + o3d::metric_d3d_dstblendcaps = d3d_caps.DestBlendCaps; + o3d::metric_d3d_alphacaps = d3d_caps.AlphaCmpCaps; + o3d::metric_d3d_texcaps = d3d_caps.TextureCaps; + o3d::metric_d3d_texfiltercaps = d3d_caps.TextureFilterCaps; + o3d::metric_d3d_cubetexfiltercaps = d3d_caps.CubeTextureFilterCaps; + o3d::metric_d3d_texaddrcaps = d3d_caps.TextureAddressCaps; + o3d::metric_d3d_linecaps = d3d_caps.LineCaps; + o3d::metric_d3d_stencilcaps = d3d_caps.StencilCaps; + o3d::metric_d3d_texopcaps = d3d_caps.TextureOpCaps; + o3d::metric_d3d_vs20caps = d3d_caps.VS20Caps.Caps; + o3d::metric_d3d_vs20_dynflowctrldepth = + d3d_caps.VS20Caps.DynamicFlowControlDepth; + o3d::metric_d3d_vs20_numtemps = d3d_caps.VS20Caps.NumTemps; + o3d::metric_d3d_vs20_staticflowctrldepth = + d3d_caps.VS20Caps.StaticFlowControlDepth; + o3d::metric_d3d_ps20caps = d3d_caps.PS20Caps.Caps; + o3d::metric_d3d_ps20_dynflowctrldepth = + d3d_caps.PS20Caps.DynamicFlowControlDepth; + o3d::metric_d3d_ps20_numtemps = d3d_caps.PS20Caps.NumTemps; + o3d::metric_d3d_ps20_staticflowctrldepth = + d3d_caps.PS20Caps.StaticFlowControlDepth; + o3d::metric_d3d_ps20_numinstrslots = d3d_caps.PS20Caps.NumInstructionSlots; +#else + o3d::metric_direct3d_available.Set(false); +#endif + return true; +} + +bool GetUserAgentMetrics(NPP npp) { + // Check User agent so we can get the browser + // TODO: This is the best we could come up with for this in order to + // go from browser to string. + GLUE_PROFILE_START(npp, "uagent"); + std::string user_agent = NPN_UserAgent(npp); + GLUE_PROFILE_STOP(npp, "uagent"); + // The Chrome user_agent string also contains Safari. Search for Chrome first. + if (std::string::npos != user_agent.find("Chrome")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_CHROME; + } else if (std::string::npos != user_agent.find("Safari")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_SAFARI; + } else if (std::string::npos != user_agent.find("Opera")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_OPERA; + } else if (std::string::npos != user_agent.find("Firefox")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_FIREFOX; + } else if (std::string::npos != user_agent.find("MSIE")) { + o3d::metric_browser_type = o3d::BROWSER_NAME_MSIE; + } else { + o3d::metric_browser_type = o3d::BROWSER_NAME_UNKNOWN; + } + return true; +} diff --git a/o3d/plugin/win/logger_main.cc b/o3d/plugin/win/logger_main.cc new file mode 100644 index 0000000..4ff94a9 --- /dev/null +++ b/o3d/plugin/win/logger_main.cc @@ -0,0 +1,58 @@ +/* + * 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 main function for a small program which calls +// the logging code and initiate an upload of logs. + +#include <windows.h> +#include <stdlib.h> +#include <shlwapi.h> +#include "plugin/cross/plugin_logging.h" +#include "statsreport/metrics.h" + +//----------------------------------------------------------------------------- +// Name: main() +// Desc: The application's entry point +//----------------------------------------------------------------------------- +int main(int argc, wchar_t **argv) { + printf("Starting stats logging.\n"); + HRESULT hr = CoInitialize(NULL); + o3d::PluginLogging g_logger; + stats_report::g_global_metrics.Initialize(); + if (!g_logger.ProcessMetrics(false, true)) { + printf("Error with stats logging.'n"); + exit(1); + } + printf("Stats logging successful.\n"); + stats_report::g_global_metrics.Uninitialize(); + exit(0); +} diff --git a/o3d/plugin/win/main_win.cc b/o3d/plugin/win/main_win.cc new file mode 100644 index 0000000..01f8704 --- /dev/null +++ b/o3d/plugin/win/main_win.cc @@ -0,0 +1,959 @@ +/* + * 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 implements the platform specific parts of the plugin for +// the Windows platform. + +#include "plugin/cross/main.h" + +#include <windows.h> +#include <windowsx.h> +#include <shellapi.h> + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "core/cross/display_mode.h" +#include "core/cross/event.h" +#include "plugin/cross/plugin_logging.h" +#include "plugin/cross/out_of_memory.h" +#include "statsreport/metrics.h" +#include "breakpad/win/bluescreen_detector.h" + +using glue::_o3d::PluginObject; +using glue::StreamManager; +using o3d::DisplayWindowWindows; +using o3d::Event; + +o3d::PluginLogging* g_logger = NULL; +bool g_logging_initialized = false; +o3d::BluescreenDetector *g_bluescreen_detector = NULL; + +namespace { +// We would normally make this a stack variable in main(), but in a +// plugin, that's not possible, so we allocate it dynamically and +// destroy it explicitly. +scoped_ptr<base::AtExitManager> g_at_exit_manager; +} // end anonymous namespace + +void RenderOnDemandCallbackHandler::Run() { + ::InvalidateRect(obj_->GetHWnd(), NULL, TRUE); +} + +static int HandleKeyboardEvent(PluginObject *obj, + HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam) { + DCHECK(obj); + DCHECK(obj->client()); + Event::Type type; + // First figure out which kind of event to create, and do any event-specific + // processing that can be done prior to creating it. + switch (Msg) { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + type = Event::TYPE_KEYDOWN; + if (wParam == VK_ESCAPE) { + obj->CancelFullscreenDisplay(); + } + break; + case WM_KEYUP: + case WM_SYSKEYUP: + type = Event::TYPE_KEYUP; + break; + case WM_CHAR: + case WM_SYSCHAR: + type = Event::TYPE_KEYPRESS; + break; + default: + LOG(FATAL) << "Unknown keyboard event: " << Msg; + } + Event event(type); + switch (Msg) { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + event.set_key_code(wParam); + break; + case WM_CHAR: + case WM_SYSCHAR: + event.set_char_code(wParam); + break; + default: + LOG(FATAL) << "Unknown keyboard event: " << Msg; + } + // TODO: Try out TranslateAccelerator to see if that causes + // accelerators to start working. They would then evade JavaScript handlers, + // though, so perhaps we'd have to check to see if we want to handle them + // first? That would require going around the event queue, at least + // partially. OTOH I see hints in blog posts that only Firefox allows + // handlers to suppress syskeys anyway, so maybe it's not that big a + // deal...unless apps want to use those keys...and depending on what happens + // in other browsers. + + unsigned char keyboard_state[256]; + if (!::GetKeyboardState(static_cast<PBYTE>(keyboard_state))) { + LOG(ERROR) << "GetKeyboardState failed."; + return 1; + } + + int modifier_state = 0; + if (keyboard_state[VK_CONTROL] < 0) { + modifier_state |= Event::MODIFIER_CTRL; + } + if (keyboard_state[VK_SHIFT] < 0) { + modifier_state |= Event::MODIFIER_SHIFT; + } + if (keyboard_state[VK_MENU] < 0) { + modifier_state |= Event::MODIFIER_ALT; + } + event.set_modifier_state(modifier_state); + obj->client()->AddEventToQueue(event); + return 0; +} + +static void HandleMouseEvent(PluginObject *obj, + HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam) { + DCHECK(obj); + DCHECK(obj->client()); + bool fake_dblclick = false; + Event::Type type; + int x = GET_X_LPARAM(lParam); + int y = GET_Y_LPARAM(lParam); + int screen_x, screen_y; + bool in_plugin = false; + { + RECT rect; + if (!::GetWindowRect(hWnd, &rect)) { + DCHECK(false); + return; + } + if (Msg == WM_MOUSEWHEEL || Msg == WM_MOUSEHWHEEL || + Msg == WM_CONTEXTMENU) { + // These messages return screen-relative coordinates, not + // window-relative coordinates. + screen_x = x; + screen_y = y; + x -= rect.left; + y -= rect.top; + } else { + screen_x = x + rect.left; + screen_y = y + rect.top; + } + if (x >= 0 && x < rect.right - rect.left && + y >= 0 && y < rect.bottom - rect.top) { + // x, y are 0-based from the top-left corner of the plugin. Rect is in + // screen coordinates, with bottom > top, right > left. + in_plugin = true; + } + } + // First figure out which kind of event to create, and do any event-specific + // processing that can be done prior to creating it. + switch (Msg) { + case WM_MOUSEMOVE: + type = Event::TYPE_MOUSEMOVE; + break; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: +#if (_WIN32_WINNT >= 0x0500) + case WM_XBUTTONDOWN: +#endif + type = Event::TYPE_MOUSEDOWN; + obj->set_got_dblclick(false); + SetCapture(hWnd); // Capture mouse to make sure we get the mouseup. + break; + + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: +#if (_WIN32_WINNT >= 0x0500) + case WM_XBUTTONUP: +#endif + type = Event::TYPE_MOUSEUP; + if (obj->got_dblclick()) { + fake_dblclick = in_plugin; + obj->set_got_dblclick(false); + } + ReleaseCapture(); + break; + + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: +#if (_WIN32_WINNT >= 0x0500) + case WM_XBUTTONDBLCLK: +#endif + // On a double-click, windows produces: down, up, move, dblclick, up. + // JavaScript should receive: down, up, [optional move, ] click, down, + // up, click, dblclick. + // The EventManager turns (down, up) into click, since we need that on all + // platforms. Windows then has to turn (dblclick, up) into (down, up, + // click, dblclick) IFF both events took place in the plugin. If only the + // dblclick did, it just turns into a down. If only the up did, it's just + // an up, and we shouldn't be passing along the down/dblclick anyway. So + // we turn the doubleclick into a mousedown, store the fact that it was a + // doubleclick, and wait for the corresponding mouseup to finish off the + // sequence. If we get anything that indicates that we missed the mouseup + // [because it went to a different window or element], we forget about the + // dblclick. + DCHECK(in_plugin); + obj->set_got_dblclick(true); + type = Event::TYPE_MOUSEDOWN; + SetCapture(hWnd); // Capture mouse to make sure we get the mouseup. + break; + + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + type = Event::TYPE_WHEEL; + break; + + case WM_CONTEXTMENU: + type = Event::TYPE_CONTEXTMENU; + break; + + default: + LOG(FATAL) << "Unknown mouse event: " << Msg; + } + Event event(type); + // Now do any event-specific code that requires an Event object. + switch (Msg) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + event.set_button(Event::BUTTON_LEFT); + break; + + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + event.set_button(Event::BUTTON_RIGHT); + break; + + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: + event.set_button(Event::BUTTON_MIDDLE); + break; + +#if (_WIN32_WINNT >= 0x0500) + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: + if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) { + event.set_button(Event::BUTTON_4); + } else { + event.set_button(Event::BUTTON_5); + } + break; +#endif + case WM_MOUSEWHEEL: + event.set_delta(0, GET_WHEEL_DELTA_WPARAM(wParam)); + break; + case WM_MOUSEHWHEEL: + event.set_delta(GET_WHEEL_DELTA_WPARAM(wParam), 0); + break; + + default: + break; + } + + if (event.type() != WM_CONTEXTMENU) { + // Only the context menu event doesn't get this information. + int modifier_state = 0; + if (wParam & MK_CONTROL) { + modifier_state |= Event::MODIFIER_CTRL; + } + if (wParam & MK_SHIFT) { + modifier_state |= Event::MODIFIER_SHIFT; + } + if (::GetKeyState(VK_MENU) < 0) { + modifier_state |= Event::MODIFIER_ALT; + } + event.set_modifier_state(modifier_state); + } + + event.set_position(x, y, screen_x, screen_y, in_plugin); + obj->client()->AddEventToQueue(event); + if (fake_dblclick) { + event.set_type(Event::TYPE_DBLCLICK); + obj->client()->AddEventToQueue(event); + } + if (in_plugin && type == Event::TYPE_MOUSEDOWN && + obj->HitFullscreenClickRegion(x, y)) { + obj->RequestFullscreenDisplay(); + } +} + +// This returns 0 on success, 1 on failure, to match WindowProc. +static LRESULT ForwardEvent(PluginObject *obj, + HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam, + bool translateCoords) { + DCHECK(obj); + DCHECK(obj->GetPluginHWnd()); + HWND dest_hwnd = obj->GetParentHWnd(); + DCHECK(hWnd); + DCHECK(dest_hwnd); + bool fullscreen = hWnd == obj->GetFullscreenHWnd(); + if (fullscreen) { + dest_hwnd = obj->GetPluginHWnd(); + } else if (obj->IsChrome()) { + // When trying to find the parent window of the Chrome plugin, new Chrome is + // different than old Chrome; it's got an extra wrapper window around the + // plugin that didn't used to be there. The wrapper won't listen to events, + // so if we see it, we have to go one window up the tree from there in order + // to find someone who'll listen to us. The new behavior is seen in nightly + // builds of Chromium as of 2.0.163.0 (9877) [but went in some time before + // that]; the old behavior is still exhibited by Chrome as of 1.0.154.48. + wchar_t chrome_class_name[] = L"WrapperNativeWindowClass"; + wchar_t buffer[sizeof(chrome_class_name) / sizeof(chrome_class_name[0])]; + if (!GetClassName(dest_hwnd, buffer, sizeof(buffer) / sizeof(buffer[0]))) { + return 1; + } + if (!wcscmp(chrome_class_name, buffer)) { + dest_hwnd = ::GetParent(dest_hwnd); + } + } + if (translateCoords) { + int x = GET_X_LPARAM(lParam); + int y = GET_Y_LPARAM(lParam); + + RECT rect0, rect1; + if (!::GetWindowRect(hWnd, &rect0)) { + DCHECK(false); + return 1; + } + if (!::GetWindowRect(dest_hwnd, &rect1)) { + DCHECK(false); + return 1; + } + int width = rect0.right - rect0.left; + int width_1 = rect1.right - rect1.left; + + int x_1; + int y_1; + + if (!fullscreen) { // Translate from plugin to browser offset coords. + x_1 = x - rect1.left + rect0.left; + } else { // Translate from screen to plugin offset coords. + // The plugin and the fullscreen window each fill their respective entire + // window, so there aren't any offsets to add or subtract. + x_1 = x * width_1 / width; + } + int height = rect0.bottom - rect0.top; + int height_1 = rect1.bottom - rect1.top; + if (!fullscreen) { // Translate from plugin to browser offset coords. + y_1 = y - rect1.top + rect0.top; + } else { // Translate from screen to plugin offset coords. + // The plugin and the fullscreen window each fill their respective entire + // window, so there aren't any offsets to add or subtract. + y_1 = y * height_1 / height; + } + + lParam = MAKELPARAM(x_1, y_1); + } + return !::PostMessage(dest_hwnd, Msg, wParam, lParam); +} + +static LRESULT HandleDragAndDrop(PluginObject *obj, WPARAM wParam) { + HDROP hDrop = reinterpret_cast<HDROP>(wParam); + UINT num_files = ::DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); + if (!num_files) { + ::DragFinish(hDrop); + return 0; + } + UINT path_len = ::DragQueryFile(hDrop, 0, NULL, 0); + // Let's limit that length, just in case. + if (!path_len || path_len > 4096) { + ::DragFinish(hDrop); + return 0; + } + scoped_array<TCHAR> path(new TCHAR[path_len + 1]); // Add 1 for the NUL. + UINT num_chars = ::DragQueryFile(hDrop, 0, path.get(), path_len + 1); + DCHECK(num_chars == path_len); + ::DragFinish(hDrop); + + char *path_to_use = NULL; +#ifdef UNICODE // TCHAR is WCHAR + // TODO: We definitely need to move this to a string utility class. + int bytes_needed = ::WideCharToMultiByte(CP_UTF8, + 0, + path.get(), + num_chars + 1, + NULL, + 0, + NULL, + NULL); + // Let's limit that length, just in case. + if (!bytes_needed || bytes_needed > 4096) { + return 0; + } + scoped_array<char> utf8_path(new char[bytes_needed]); + int bytes_conv = ::WideCharToMultiByte(CP_UTF8, + 0, + path.get(), + num_chars + 1, + utf8_path.get(), + bytes_needed, + NULL, + NULL); + DCHECK(bytes_conv == bytes_needed); + path_to_use = utf8_path.get(); + num_chars = bytes_conv; +#else + path_to_use = path.get(); +#endif + + for (int i = 0; i < num_chars; ++i) { + if (path_to_use[i] == '\\') { + path_to_use[i] = '/'; + } + } + obj->RedirectToFile(path_to_use); + + return 1; +} + +static LRESULT CALLBACK +WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + PluginObject *obj = PluginObject::GetPluginProperty(hWnd); + if (obj == NULL) { // It's not my window + return 1; // 0 often means we handled it. + } + + // Limit the ways in which we can be reentrant. Note that this WindowProc + // may be called by different threads. For example, IE will register plugin + // instances on separate threads. + o3d::Client::ScopedIncrement reentrance_count(obj->client()); + + switch (Msg) { + case WM_PAINT: { + if (reentrance_count.get() > 1) { + break; // Ignore this message; we're reentrant. + } + PAINTSTRUCT paint_struct; + HDC hdc = ::BeginPaint(hWnd, &paint_struct); + if (paint_struct.rcPaint.right - paint_struct.rcPaint.left != 0 || + paint_struct.rcPaint.bottom - paint_struct.rcPaint.top != 0) { + if (obj->renderer()) { + // It appears to be necessary to use GDI to paint something at least + // once before D3D rendering will work in Vista with Aero. + if (!obj->RecordPaint()) { + ::SetPixelV(hdc, 0, 0, RGB(0, 0, 0)); + } + + obj->client()->RenderClient(); + } else { + // If there Client has no Renderer associated with it, paint the draw + // area gray. + ::SelectObject(paint_struct.hdc, GetStockObject(DKGRAY_BRUSH)); + ::Rectangle(paint_struct.hdc, + paint_struct.rcPaint.left, + paint_struct.rcPaint.top, + paint_struct.rcPaint.right, + paint_struct.rcPaint.bottom); + } + } + ::EndPaint(hWnd, &paint_struct); + break; + } + case WM_SETCURSOR: { + obj->set_cursor(obj->cursor()); + return 1; + } + case WM_ERASEBKGND: { + return 1; // tell windows we don't need the background cleared + } + case WM_SIZE: { + // Resize event called + if (reentrance_count.get() > 1) { + break; // Ignore this message; we're reentrant. + } + + // get new dimensions of window + int window_width = LOWORD(lParam); + int window_height = HIWORD(lParam); + + // Tell the plugin that it has been resized + obj->Resize(window_width, window_height); + break; + } + case WM_TIMER: { + if (reentrance_count.get() > 1) { + break; // Ignore this message; we're reentrant. + } + // DoOnFrameCallback(obj); + + // TODO: Only logging for windows until we figure out the proper + // mac way + if (g_logger) g_logger->UpdateLogging(); + + obj->client()->Tick(); + if (obj->client()->render_mode() == + o3d::Client::RENDERMODE_CONTINUOUS) { + // Must invalidate GetHWnd()'s drawing area, no matter which window is + // receiving this event. It turns out that we have to set the timer on + // the window we're using for drawing anyway, whichever that is, but + // it's possible that an extra event will slip through. + ::InvalidateRect(obj->GetHWnd(), NULL, TRUE); + } + // Calling UpdateWindow to force a WM_PAINT here causes problems in + // Firefox 2 if rendering takes too long. WM_PAINT will be sent anyway + // when there are no other messages to process. + break; + } + case WM_NCDESTROY: { + // Win32 docs say we must remove all our properties before destruction. + // However, this message doesn't appear to come early enough to be useful + // when running in Chrome, at least. + PluginObject::ClearPluginProperty(hWnd); + break; + } + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: +#if (_WIN32_WINNT >= 0x0500) + case WM_XBUTTONDOWN: + case WM_XBUTTONDBLCLK: +#endif + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + // Without this SetFocus, if you alt+tab away from Firefox, then click + // back in the plugin, Firefox will get keyboard focus but we won't. + // However, if we do it on mouseup as well, then the click that triggers + // fullscreen, and is followed by a mouseup in the plugin, which will grab + // the focus back from the fullscreen window. + ::SetFocus(hWnd); + // FALL THROUGH + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: +#if (_WIN32_WINNT >= 0x0500) + case WM_XBUTTONUP: +#endif + case WM_MOUSEMOVE: + case WM_CONTEXTMENU: + HandleMouseEvent(obj, hWnd, Msg, wParam, lParam); + break; + + case WM_DEADCHAR: + case WM_SYSDEADCHAR: + case WM_UNICHAR: + // I believe these are redundant; TODO: Test this on a non-US + // keyboard. + break; + + case WM_CHAR: + case WM_SYSCHAR: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + return HandleKeyboardEvent(obj, hWnd, Msg, wParam, lParam); + +#if(_WIN32_WINNT >= 0x0500) + case WM_APPCOMMAND: +#endif /* _WIN32_WINNT >= 0x0500 */ + return ForwardEvent(obj, hWnd, Msg, wParam, lParam, false); + + case WM_DROPFILES: + return HandleDragAndDrop(obj, wParam); + + case WM_KILLFOCUS: + // If we lose focus [which also happens on alt+f4 killing the fullscreen + // window] fall back to plugin mode to avoid lost-device awkwardness. + // TODO: We'll have problems with this when dealing with e.g. + // Japanese text input IME windows. + if (hWnd == obj->GetFullscreenHWnd()) { + obj->CancelFullscreenDisplay(); + return 0; + } + // FALL THROUGH + case WM_SETFOCUS: + default: + // Decrement reentrance_count here. It's OK if this call + // boomerangs back to us, given that we're not in the middle of doing + // anything caused by this message. Since we decrement reentrance_count + // manually, its destructor will know not to. + reentrance_count.decrement(); + + if (hWnd == obj->GetFullscreenHWnd()) { + return ::CallWindowProc(::DefWindowProc, + hWnd, + Msg, + wParam, + lParam); + } else { + return ::CallWindowProc(obj->GetDefaultPluginWindowProc(), + hWnd, + Msg, + wParam, + lParam); + } + } + return 0; +} + +extern "C" { + NPError InitializePlugin() { + if (!o3d::SetupOutOfMemoryHandler()) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + // Setup crash handler + if (!g_exception_manager) { + g_exception_manager = new ExceptionManager(false); + g_exception_manager->StartMonitoring(); + } + + // Initialize the AtExitManager so that base singletons can be + // destroyed properly. + g_at_exit_manager.reset(new base::AtExitManager()); + + // Turn on the logging. + CommandLine::Init(0, NULL); + InitLogging(L"debug.log", + logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG, + logging::DONT_LOCK_LOG_FILE, + logging::APPEND_TO_OLD_LOG_FILE); + + DLOG(INFO) << "NP_Initialize"; + + // Limit the current thread to one processor (the current one). This ensures + // that timing code runs on only one processor, and will not suffer any ill + // effects from power management. See "Game Timing and Multicore Processors" + // in the DirectX docs for more details + { + HANDLE current_process_handle = GetCurrentProcess(); + + // Get the processor affinity mask for this process + DWORD_PTR process_affinity_mask = 0; + DWORD_PTR system_affinity_mask = 0; + + if (GetProcessAffinityMask(current_process_handle, + &process_affinity_mask, + &system_affinity_mask) != 0 && + process_affinity_mask) { + // Find the lowest processor that our process is allows to run against + DWORD_PTR affinity_mask = (process_affinity_mask & + ((~process_affinity_mask) + 1)); + + // Set this as the processor that our thread must always run against + // This must be a subset of the process affinity mask + HANDLE hCurrentThread = GetCurrentThread(); + if (INVALID_HANDLE_VALUE != hCurrentThread) { + SetThreadAffinityMask(hCurrentThread, affinity_mask); + CloseHandle(hCurrentThread); + } + } + + CloseHandle(current_process_handle); + } + + return NPERR_NO_ERROR; + } + + NPError OSCALL NP_Initialize(NPNetscapeFuncs *browserFuncs) { + HANDLE_CRASHES; + NPError retval = InitializeNPNApi(browserFuncs); + if (retval != NPERR_NO_ERROR) return retval; + return InitializePlugin(); + } + + NPError OSCALL NP_Shutdown(void) { + HANDLE_CRASHES; + DLOG(INFO) << "NP_Shutdown"; + if (g_logger) { + // Do a last sweep to aggregate metrics before we shut down + g_logger->ProcessMetrics(true, false); + delete g_logger; + g_logger = NULL; + g_logging_initialized = false; + stats_report::g_global_metrics.Uninitialize(); + } + + CommandLine::Terminate(); + + // Force all base singletons to be destroyed. + g_at_exit_manager.reset(NULL); + + // TODO : This is commented out until we can determine if + // it's safe to shutdown breakpad at this stage (Gears, for + // example, never deletes...) + // Shutdown breakpad + // delete g_exception_manager; + + // Strictly speaking, on windows, it's not really necessary to call + // Stop(), but we do so for completeness + if (g_bluescreen_detector) { + g_bluescreen_detector->Stop(); + delete g_bluescreen_detector; + g_bluescreen_detector = NULL; + } + + return NPERR_NO_ERROR; + } + + NPError NPP_New(NPMIMEType pluginType, + NPP instance, + uint16 mode, + int16 argc, + char *argn[], + char *argv[], + NPSavedData *saved) { + HANDLE_CRASHES; + + if (!g_logging_initialized) { + // Get user config metrics. These won't be stored though unless the user + // opts-in for usagestats logging + GetUserAgentMetrics(instance); + GetUserConfigMetrics(); + // Create usage stats logs object + g_logger = o3d::PluginLogging::InitializeUsageStatsLogging(); + if (g_logger) { + // Setup blue-screen detection + g_bluescreen_detector = new o3d::BluescreenDetector(); + g_bluescreen_detector->Start(); + } + g_logging_initialized = true; + } + PluginObject* pluginObject = glue::_o3d::PluginObject::Create( + instance); + instance->pdata = pluginObject; + glue::_o3d::InitializeGlue(instance); + pluginObject->Init(argc, argn, argv); + return NPERR_NO_ERROR; + } + + void CleanupFullscreenWindow(PluginObject *obj) { + DCHECK(obj->GetFullscreenHWnd()); + obj->StorePluginProperty(obj->GetPluginHWnd(), obj); + ::DestroyWindow(obj->GetFullscreenHWnd()); + obj->SetFullscreenHWnd(NULL); + } + + void CleanupAllWindows(PluginObject *obj) { + DCHECK(obj->GetHWnd()); + DCHECK(obj->GetPluginHWnd()); + ::KillTimer(obj->GetHWnd(), 0); + if (obj->GetFullscreenHWnd()) { + CleanupFullscreenWindow(obj); + } + PluginObject::ClearPluginProperty(obj->GetHWnd()); + ::SetWindowLongPtr(obj->GetPluginHWnd(), + GWL_WNDPROC, + reinterpret_cast<LONG_PTR>( + obj->GetDefaultPluginWindowProc())); + obj->SetPluginHWnd(NULL); + obj->SetHWnd(NULL); + } + + NPError NPP_Destroy(NPP instance, NPSavedData **save) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + if (obj) { + if (obj->GetHWnd()) { + CleanupAllWindows(obj); + } + + obj->TearDown(); + NPN_ReleaseObject(obj); + instance->pdata = NULL; + } + + return NPERR_NO_ERROR; + } + + HWND CreateFullscreenWindow(PluginObject *obj, + int mode_id) { + o3d::DisplayMode mode; + if (!obj->renderer()->GetDisplayMode(mode_id, &mode)) { + return NULL; + } + CHECK(mode.width() > 0 && mode.height() > 0); + + HINSTANCE instance = + reinterpret_cast<HINSTANCE>( + ::GetWindowLongPtr(obj->GetPluginHWnd(), GWLP_HINSTANCE)); + WNDCLASSEX *wcx = obj->GetFullscreenWindowClass(instance, WindowProc); + HWND hWnd = CreateWindowEx(NULL, + wcx->lpszClassName, + L"O3D Test Fullscreen Window", + WS_POPUP, + 0, 0, + mode.width(), + mode.height(), + NULL, + NULL, + instance, + NULL); + + ShowWindow(hWnd, SW_SHOW); + return hWnd; + } + + // TODO: Where should this really live? It's platform-specific, but in + // PluginObject, which mainly lives in cross/o3d_glue.h+cc. + bool PluginObject::RequestFullscreenDisplay() { + bool success = false; +#ifndef NDEBUG // TODO: Remove after user-prompt feature goes in. + DCHECK(GetPluginHWnd()); + if (!fullscreen_ && renderer_ && fullscreen_region_valid_) { + DCHECK(renderer_->fullscreen() == fullscreen_); + DCHECK(!GetFullscreenHWnd()); + HWND drawing_hwnd = + CreateFullscreenWindow(this, fullscreen_region_mode_id_); + if (drawing_hwnd) { + ::KillTimer(GetHWnd(), 0); + SetFullscreenHWnd(drawing_hwnd); + StorePluginPropertyUnsafe(drawing_hwnd, this); + + DisplayWindowWindows display; + display.set_hwnd(GetHWnd()); + if (renderer_->SetFullscreen(true, display, + fullscreen_region_mode_id_)) { + fullscreen_ = true; + client()->SendResizeEvent(renderer_->width(), renderer_->height(), + true); + success = true; + } else { + CleanupFullscreenWindow(this); + } + prev_width_ = renderer_->width(); + prev_height_ = renderer_->height(); + ::SetTimer(GetHWnd(), 0, 10, NULL); + } else { + LOG(ERROR) << "Failed to create fullscreen window."; + } + } +#endif + return success; + } + + void PluginObject::CancelFullscreenDisplay() { +#ifndef NDEBUG // TODO: Remove after user-prompt feature goes in. + DCHECK(GetPluginHWnd()); + if (fullscreen_) { + DCHECK(renderer()); + DCHECK(renderer()->fullscreen()); + ::KillTimer(GetHWnd(), 0); + DisplayWindowWindows display; + display.set_hwnd(GetPluginHWnd()); + if (!renderer_->SetFullscreen(false, display, 0)) { + LOG(FATAL) << "Failed to get the renderer out of fullscreen mode!"; + } + CleanupFullscreenWindow(this); + prev_width_ = renderer_->width(); + prev_height_ = renderer_->height(); + client()->SendResizeEvent(prev_width_, prev_height_, false); + ::SetTimer(GetHWnd(), 0, 10, NULL); + fullscreen_ = false; + } +#endif + } + + NPError NPP_SetWindow(NPP instance, NPWindow *window) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + + HWND hWnd = static_cast<HWND>(window->window); + if (!hWnd) { + // Chrome calls us this way before NPP_Destroy. + if (obj->GetHWnd()) { + CleanupAllWindows(obj); + } + return NPERR_NO_ERROR; + } + if (obj->GetHWnd() == hWnd) { + return NPERR_NO_ERROR; + } + if (obj->fullscreen()) { + // We can get here if the user alt+tabs away from the fullscreen plugin + // window or JavaScript resizes the plugin window. + DCHECK(obj->GetPluginHWnd()); + DCHECK(obj->GetFullscreenHWnd()); + DCHECK(obj->GetPluginHWnd() == hWnd); + return NPERR_NO_ERROR; + } + DCHECK(!obj->GetPluginHWnd()); + obj->SetPluginHWnd(hWnd); + obj->SetParentHWnd(::GetParent(hWnd)); + PluginObject::StorePluginProperty(hWnd, obj); + obj->SetDefaultPluginWindowProc( + reinterpret_cast<WNDPROC>( + ::SetWindowLongPtr(hWnd, + GWL_WNDPROC, + reinterpret_cast<LONG_PTR>(WindowProc)))); + + // create and assign the graphics context + DisplayWindowWindows default_display; + default_display.set_hwnd(obj->GetHWnd()); + + obj->CreateRenderer(default_display); + obj->client()->Init(); + obj->client()->SetRenderOnDemandCallback( + new RenderOnDemandCallbackHandler(obj)); + + // we set the timer to 10ms or 100fps. At the time of this comment + // the renderer does a vsync the max fps it will run will be the refresh + // rate of the monitor or 100fps, which ever is lower. + ::SetTimer(obj->GetHWnd(), 0, 10, NULL); + + return NPERR_NO_ERROR; + } + + // Called when the browser has finished attempting to stream data to + // a file as requested. If fname == NULL the attempt was not successful. + void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname) { + HANDLE_CRASHES; + PluginObject *obj = static_cast<PluginObject*>(instance->pdata); + StreamManager *stream_manager = obj->stream_manager(); + + stream_manager->SetStreamFile(stream, fname); + } + + int16 NPP_HandleEvent(NPP instance, void *event) { + HANDLE_CRASHES; + return 0; + } +} // end extern "C" diff --git a/o3d/plugin/win/o3dPlugin.def b/o3d/plugin/win/o3dPlugin.def new file mode 100644 index 0000000..9685d16 --- /dev/null +++ b/o3d/plugin/win/o3dPlugin.def @@ -0,0 +1,6 @@ +LIBRARY npo3dautoplugin + +EXPORTS + NP_GetEntryPoints @1 + NP_Initialize @2 + NP_Shutdown @3 diff --git a/o3d/plugin/win/o3dPlugin.rc_template b/o3d/plugin/win/o3dPlugin.rc_template new file mode 100644 index 0000000..8085cf9 --- /dev/null +++ b/o3d/plugin/win/o3dPlugin.rc_template @@ -0,0 +1,106 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.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 ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @@@ProductVersionCommas@@@ + PRODUCTVERSION @@@ProductVersionCommas@@@ + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + // This value must not change format as it is used by javascript + // to find the plugin. See o3d/main.scons + VALUE "FileDescription", "@@@PluginDescription@@@" + VALUE "FileVersion", "@@@ProductVersionCommas@@@" + VALUE "InternalName", "npo3dautoplugin" + VALUE "LegalCopyright", "Copyright (C) 2007" + VALUE "MIMEType", "@@@PluginMimeType@@@" + VALUE "OriginalFilename", "npo3dautoplugin.dll" + // This value must not change as it is used by javascript + // to find the plugin. See o3d/main.scons + VALUE "ProductName", "@@@PluginName@@@" + VALUE "ProductVersion", "@@@ProductVersionCommas@@@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/o3d/plugin/win/o3dPlugin.sln b/o3d/plugin/win/o3dPlugin.sln new file mode 100644 index 0000000..d4ab270 --- /dev/null +++ b/o3d/plugin/win/o3dPlugin.sln @@ -0,0 +1,92 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "o3dPlugin", "o3dPlugin.vcproj", "{F431F75C-5EA2-4E96-BA23-B44A951A9276}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B} = {B57C4010-AE66-47A2-8B1F-F839B6E6A67B} + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA} = {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA} + {6028E8C8-F1DC-4248-9961-B0CADFEA244C} = {6028E8C8-F1DC-4248-9961-B0CADFEA244C} + {E1978291-230F-4F95-A227-84A13F22833C} = {E1978291-230F-4F95-A227-84A13F22833C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "o3dImport", "..\..\..\..\o3d\import\win\import.vcproj", "{6028E8C8-F1DC-4248-9961-B0CADFEA244C}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "o3dCore", "..\..\..\..\o3d\core\win\core_win.vcproj", "{B57C4010-AE66-47A2-8B1F-F839B6E6A67B}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "o3d_base", "..\..\..\..\o3d\base\win\o3d_base.vcproj", "{E1978291-230F-4F95-A227-84A13F22833C}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "google_nacl_imc", "..\..\..\..\native_client\intermodule_comm\google_nacl_imc\google_nacl_imc.vcproj", "{7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + DebugD3D9|Win32 = DebugD3D9|Win32 + DebugGL|Win32 = DebugGL|Win32 + ReleaseD3D9|Win32 = ReleaseD3D9|Win32 + ReleaseGL|Win32 = ReleaseGL|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.DebugD3D9|Win32.ActiveCfg = DebugD3D9|Win32 + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.DebugD3D9|Win32.Build.0 = DebugD3D9|Win32 + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.DebugGL|Win32.ActiveCfg = DebugGL|Win32 + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.DebugGL|Win32.Build.0 = DebugGL|Win32 + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.ReleaseD3D9|Win32.ActiveCfg = ReleaseD3D9|Win32 + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.ReleaseD3D9|Win32.Build.0 = ReleaseD3D9|Win32 + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.ReleaseGL|Win32.ActiveCfg = ReleaseGL|Win32 + {F431F75C-5EA2-4E96-BA23-B44A951A9276}.ReleaseGL|Win32.Build.0 = ReleaseGL|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.DebugD3D9|Win32.ActiveCfg = DebugD3D9|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.DebugD3D9|Win32.Build.0 = DebugD3D9|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.DebugGL|Win32.ActiveCfg = DebugGL|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.DebugGL|Win32.Build.0 = DebugGL|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.ReleaseD3D9|Win32.ActiveCfg = ReleaseD3D9|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.ReleaseD3D9|Win32.Build.0 = ReleaseD3D9|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.ReleaseGL|Win32.ActiveCfg = ReleaseGL|Win32 + {6028E8C8-F1DC-4248-9961-B0CADFEA244C}.ReleaseGL|Win32.Build.0 = ReleaseGL|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.DebugD3D9|Win32.ActiveCfg = DebugD3D9|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.DebugD3D9|Win32.Build.0 = DebugD3D9|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.DebugGL|Win32.ActiveCfg = DebugGL|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.DebugGL|Win32.Build.0 = DebugGL|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.ReleaseD3D9|Win32.ActiveCfg = ReleaseD3D9|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.ReleaseD3D9|Win32.Build.0 = ReleaseD3D9|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.ReleaseGL|Win32.ActiveCfg = ReleaseGL|Win32 + {B57C4010-AE66-47A2-8B1F-F839B6E6A67B}.ReleaseGL|Win32.Build.0 = ReleaseGL|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.DebugD3D9|Win32.ActiveCfg = Debug|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.DebugD3D9|Win32.Build.0 = Debug|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.DebugGL|Win32.ActiveCfg = Debug|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.DebugGL|Win32.Build.0 = Debug|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.ReleaseD3D9|Win32.ActiveCfg = Release|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.ReleaseD3D9|Win32.Build.0 = Release|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.ReleaseGL|Win32.ActiveCfg = Release|Win32 + {E1978291-230F-4F95-A227-84A13F22833C}.ReleaseGL|Win32.Build.0 = Release|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.DebugD3D9|Win32.ActiveCfg = Debug|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.DebugD3D9|Win32.Build.0 = Debug|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.DebugGL|Win32.ActiveCfg = Debug|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.DebugGL|Win32.Build.0 = Debug|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.ReleaseD3D9|Win32.ActiveCfg = Release|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.ReleaseD3D9|Win32.Build.0 = Release|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.ReleaseGL|Win32.ActiveCfg = Release|Win32 + {7D7B36BE-B994-4BE2-A60C-753C19E5A9EA}.ReleaseGL|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/o3d/plugin/win/plugin_logging-win32.cc b/o3d/plugin/win/plugin_logging-win32.cc new file mode 100644 index 0000000..c1f389f --- /dev/null +++ b/o3d/plugin/win/plugin_logging-win32.cc @@ -0,0 +1,306 @@ +/* + * 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 code to perform the necessary logging operations +// including initializing logging object, aggregating metrics, and uploading +// metrics to the stats server. + +#include <build/build_config.h> +#ifdef OS_WIN +#include <atlbase.h> +#include "statsreport/const-win32.h" +#endif +#include "core/cross/types.h" +#include "plugin/cross/plugin_logging.h" +#include "plugin/cross/plugin_metrics.h" +#include "statsreport/common/const_product.h" +#include "statsreport/metrics.h" +#include "statsreport/uploader.h" +namespace o3d { + + +HRESULT StringEscape(const CString& str_in, + bool segment_only, + CString* escaped_string); +HRESULT GetRegStringValue(bool is_machine_key, + const CString& relative_key_path, + const CString& value_name, + CString* value); + +PluginLogging::PluginLogging() : timer_(new HighresTimer()), + running_time_(0), + prev_uptime_seconds_(0), + prev_cputime_seconds_(0) { + DLOG(INFO) << "Creating logger."; + timer_->Start(); +} + +bool PluginLogging::UpdateLogging() { + // If sufficient time has not passed since last aggregation, we can just + // return until next time. + if (timer_->GetElapsedMs() < kStatsAggregationIntervalMSec) return false; + + // Reset timer. + timer_->Start(); + // We are not exiting just yet so pass false for that argument. + // We don't have to force stats reporting, so pass false for forcing, too. + return ProcessMetrics(false, false); +} + +static uint64 ToSeconds(FILETIME time) { + ULARGE_INTEGER t; + t.u.HighPart = time.dwHighDateTime; + t.u.LowPart = time.dwLowDateTime; + + return t.QuadPart / 10000000L; +} + +void PluginLogging::RecordProcessTimes() { + FILETIME creation_time, exit_time, kernel_time, user_time; + if (!::GetProcessTimes(::GetCurrentProcess(), &creation_time, &exit_time, + &kernel_time, &user_time)) { + return; + } + FILETIME now; + ::GetSystemTimeAsFileTime(&now); + uint64 uptime = ToSeconds(now) - ToSeconds(creation_time); + uint64 additional_uptime = uptime - prev_uptime_seconds_; + metric_uptime_seconds += additional_uptime; + running_time_ += additional_uptime; + prev_uptime_seconds_ = uptime; + + uint64 cputime = ToSeconds(kernel_time) + ToSeconds(user_time); + metric_cpu_time_seconds += (cputime - prev_cputime_seconds_); + prev_cputime_seconds_ = cputime; +} + +bool PluginLogging::ProcessMetrics(const bool exiting, + const bool force_report) { + DLOG(INFO) << "ProcessMetrics()"; + // Grab incremental process times. This has to be done each time + // around the loop since time passes between iterations. + RecordProcessTimes(); + + // This mutex protects the writing to the registry. This way, + // if we have multiple instances attempting to aggregate at + // once, they won't overwrite one another. + CHandle mutex(::CreateMutexA(NULL, FALSE, kMetricsLockName)); + if (NULL == mutex.m_h) { + DLOG(WARNING) << "Unable to create metrics mutex"; + return false; + } + + // If we can't get the mutex in 3 seconds, let's go around again. + DWORD wait_result = ::WaitForSingleObject(mutex, 3000); + if (WAIT_OBJECT_0 != wait_result) { + DLOG(WARNING) << "Unable to get metrics mutex, error " + << std::hex << wait_result; + return false; + } + if (exiting) { + // If we're exiting, we aggregate to make sure that we record + // the tail activity for posterity. We don't report, because + // that might delay the process exit. + // We also make sure to add a sample to the total running time. + metric_running_time_seconds.AddSample(running_time_); + DoAggregateMetrics(); + } else { + CString user_id; + if (FAILED(GetRegStringValue(true, // is_machine_key + CString(kRelativeGoopdateRegPath), + CString(kRegValueUserId), + &user_id))) { + user_id = CString("unknown user_id"); + } + CString user_id_escaped; + StringEscape(user_id, true, &user_id_escaped); + CStringA client_id_argument = CString("ui=") + + user_id_escaped.GetString(); + DLOG(INFO) << "client id " << client_id_argument; + + std::string user_agent8 = std::string(kUserAgent) + + PRODUCT_VERSION_STRING; + DoAggregateAndReportMetrics(client_id_argument, + user_agent8.c_str(), force_report); + } + + ::ReleaseMutex(mutex); + + return true; +} + +void PluginLogging::DoAggregateMetrics() { + DLOG(INFO) << "DoAggregateMetrics()"; + stats_report::AggregateMetrics(); +} + +bool PluginLogging::DoAggregateAndReportMetrics( + const char* extra_url_arguments, + const char* user_agent, + const bool force_report) { + DLOG(INFO) << "DoAggregateAndReportMetrics()"; + // This eturns true if metrics were uploaded. + return stats_report::AggregateAndReportMetrics(extra_url_arguments, + user_agent, + force_report); +} + +// This method is used for testing. +void PluginLogging::SetTimer(HighresTimer* timer) { + timer_.reset(timer); +} + +// Reads the specified string value from the specified registry key. +// Only supports value types REG_SZ and REG_EXPAND_SZ. +// REG_EXPAND_SZ strings are not expanded. +HRESULT GetRegStringValue(bool is_machine_key, + const CString& relative_key_path, + const CString& value_name, + CString* value) { + if (!value) { + return E_INVALIDARG; + } + + value->Empty(); + HKEY root_key = is_machine_key ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HKEY key = NULL; + LONG res = ::RegOpenKeyEx(root_key, relative_key_path, 0, KEY_READ, &key); + if (res != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(res); + } + + // First get the size of the string buffer. + DWORD type = 0; + DWORD byte_count = 0; + res = ::RegQueryValueEx(key, value_name, NULL, &type, NULL, &byte_count); + if (ERROR_SUCCESS != res) { + return HRESULT_FROM_WIN32(res); + } + if ((type != REG_SZ && type != REG_EXPAND_SZ) || (0 == byte_count)) { + return E_FAIL; + } + + CString local_value; + // GetBuffer throws when not able to allocate the requested buffer. + TCHAR* buffer = local_value.GetBuffer(byte_count / sizeof(TCHAR)); + res = ::RegQueryValueEx(key, + value_name, + NULL, + NULL, + reinterpret_cast<byte*>(buffer), + &byte_count); + if (ERROR_SUCCESS == res) { + local_value.ReleaseBufferSetLength(byte_count / sizeof(TCHAR)); + *value = local_value; + } + + return HRESULT_FROM_WIN32(res); +} + +HRESULT StringEscape(const CString& str_in, + bool segment_only, + CString* escaped_string) { + if (!escaped_string) { + return E_INVALIDARG; + } + + DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1; + HRESULT hr = ::UrlEscape(str_in, + escaped_string->GetBufferSetLength(buf_len), + &buf_len, + segment_only ? + URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY : + URL_ESCAPE_PERCENT); + if (SUCCEEDED(hr)) { + escaped_string->ReleaseBuffer(); + } + return hr; +} + +PluginLogging* PluginLogging::InitializeUsageStatsLogging() { + HKEY hkcu; + ::RegOpenKeyEx(HKEY_CURRENT_USER, + kClientstateRegistryKey, + 0, + KEY_SET_VALUE, + &hkcu); + ::RegSetValueEx(hkcu, L"dr", 0, REG_SZ, (LPBYTE)"1\0", 2); + ::RegCloseKey(hkcu); + + bool opt_in = GetOptInKeyValue(kClientstateRegistryKey, kOptInRegistryKey); + + return CreateUsageStatsLogger<PluginLogging>(opt_in); +} + +bool PluginLogging::GetOptInKeyValue(const wchar_t* clientstate_registry_key, + const wchar_t* opt_in_registry_key) { +#ifdef NDEBUG + HKEY hkcu_opt; + if (::RegOpenKeyEx(HKEY_CURRENT_USER, + clientstate_registry_key, + 0, + KEY_QUERY_VALUE, + &hkcu_opt) != ERROR_SUCCESS) { + return false; + } + DWORD opt_value; + DWORD value_type; + ULONG value_len = sizeof(opt_value); + if (::RegQueryValueEx(hkcu_opt, opt_in_registry_key, 0, + &value_type, reinterpret_cast<BYTE *>(&opt_value), + &value_len) != ERROR_SUCCESS) { + return false; + } + ::RegCloseKey(hkcu_opt); + + return opt_value == 1; +#else + // If we are debugging, always return true. + return true; +#endif + // Default to opt-out for situations we don't handle. + return false; +} + +void PluginLogging::ClearLogs() { + CRegKey key; + CString key_name; + key_name.Format(stats_report::kStatsKeyFormatString, + PRODUCT_NAME_STRING_WIDE); + LONG err = key.Create(HKEY_CURRENT_USER, key_name); + if (ERROR_SUCCESS != err) { + DLOG(WARNING) << "Unable to open metrics key"; + } + stats_report::ResetPersistentMetrics(&key); +} + +} // namespace o3d diff --git a/o3d/plugin/win/plugin_metrics-win32.cc b/o3d/plugin/win/plugin_metrics-win32.cc new file mode 100644 index 0000000..f183595 --- /dev/null +++ b/o3d/plugin/win/plugin_metrics-win32.cc @@ -0,0 +1,103 @@ +/* + * 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 "statsreport/metrics.h" +#include "plugin/cross/plugin_metrics.h" + +namespace o3d { +DEFINE_METRIC_integer(system_type); + +DEFINE_METRIC_integer(windows_major_version); +DEFINE_METRIC_integer(windows_minor_version); +DEFINE_METRIC_integer(windows_sp_major_version); +DEFINE_METRIC_integer(windows_sp_minor_version); + +// User GPU +DEFINE_METRIC_integer(gpu_vendor_id); +DEFINE_METRIC_integer(gpu_device_id); +DEFINE_METRIC_integer(gpu_driver_major_version); +DEFINE_METRIC_integer(gpu_driver_minor_version); +DEFINE_METRIC_integer(gpu_vram_size); +DEFINE_METRIC_bool(direct3d_available); + +// Shader versions +DEFINE_METRIC_integer(pixel_shader_main_version); +DEFINE_METRIC_integer(pixel_shader_sub_version); +DEFINE_METRIC_integer(vertex_shader_main_version); +DEFINE_METRIC_integer(vertex_shader_sub_version); + +DEFINE_METRIC_bool(POW2_texture_caps); +DEFINE_METRIC_bool(NONPOW2CONDITIONAL_texture_caps); + +DEFINE_METRIC_integer(browser_type); +DEFINE_METRIC_integer(browser_major_version); +DEFINE_METRIC_integer(browser_minor_version); +DEFINE_METRIC_integer(browser_bugfix_version); + +DEFINE_METRIC_timing(running_time); + +DEFINE_METRIC_count(uptime_seconds); +DEFINE_METRIC_count(cpu_time_seconds); +DEFINE_METRIC_timing(running_time_seconds); + +DEFINE_METRIC_count(crashes_total); +DEFINE_METRIC_count(crashes_uploaded); +DEFINE_METRIC_count(out_of_memory_total); + +DEFINE_METRIC_count(bluescreens_total); + +// D3D Caps +DEFINE_METRIC_integer(d3d_devcaps); +DEFINE_METRIC_integer(d3d_misccaps); +DEFINE_METRIC_integer(d3d_rastercaps); +DEFINE_METRIC_integer(d3d_zcmpcaps); +DEFINE_METRIC_integer(d3d_srcblendcaps); +DEFINE_METRIC_integer(d3d_dstblendcaps); +DEFINE_METRIC_integer(d3d_alphacaps); +DEFINE_METRIC_integer(d3d_texcaps); +DEFINE_METRIC_integer(d3d_texfiltercaps); +DEFINE_METRIC_integer(d3d_cubetexfiltercaps); +DEFINE_METRIC_integer(d3d_texaddrcaps); +DEFINE_METRIC_integer(d3d_linecaps); +DEFINE_METRIC_integer(d3d_stencilcaps); +DEFINE_METRIC_integer(d3d_texopcaps); +DEFINE_METRIC_integer(d3d_vs20caps); +DEFINE_METRIC_integer(d3d_vs20_dynflowctrldepth); +DEFINE_METRIC_integer(d3d_vs20_numtemps); +DEFINE_METRIC_integer(d3d_vs20_staticflowctrldepth); +DEFINE_METRIC_integer(d3d_ps20caps); +DEFINE_METRIC_integer(d3d_ps20_dynflowctrldepth); +DEFINE_METRIC_integer(d3d_ps20_numtemps); +DEFINE_METRIC_integer(d3d_ps20_staticflowctrldepth); +DEFINE_METRIC_integer(d3d_ps20_numinstrslots); + +} // namespace o3d diff --git a/o3d/plugin/win/resource.h b/o3d/plugin/win/resource.h new file mode 100644 index 0000000..f4b98ca --- /dev/null +++ b/o3d/plugin/win/resource.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by o3d_plugin.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 421 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 421 +#endif +#endif diff --git a/o3d/plugin/win/update_lock.cc b/o3d/plugin/win/update_lock.cc new file mode 100644 index 0000000..3900365 --- /dev/null +++ b/o3d/plugin/win/update_lock.cc @@ -0,0 +1,39 @@ +/* + * 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 "update_lock.h" + +namespace { +// Instantiate a global object to indicate that the application is running. +update_lock::HandleWrapper g_update_lock(update_lock::LockFromUpdates()); + +} // anonymous namespace diff --git a/o3d/plugin/win/update_lock.h b/o3d/plugin/win/update_lock.h new file mode 100644 index 0000000..dcf78ad --- /dev/null +++ b/o3d/plugin/win/update_lock.h @@ -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. + */ + + +#ifndef O3D_PLUGIN_WIN_UPDATE_LOCK_H_ +#define O3D_PLUGIN_WIN_UPDATE_LOCK_H_ + +#include <windows.h> +#include "base/basictypes.h" + +namespace update_lock { + +const TCHAR kRunningEventName[] = + L"Global\\{AA4817F6-5DB2-482f-92E9-6BD2FF9F3B14}"; + +class HandleWrapper { + public: + HandleWrapper(HANDLE handle) { + handle_ = handle; + } + ~HandleWrapper() { + CloseHandle(handle_); + } + HANDLE handle() { + return handle_; + } + private: + HANDLE handle_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(HandleWrapper); +}; // HandleWrapper + +// Returns true if the software is currently not running and can be updated. +bool CanUpdate() { + // Look for Global kernel object created when application is running + HandleWrapper running_event( + OpenEvent(EVENT_ALL_ACCESS, FALSE, kRunningEventName)); + if (NULL != running_event.handle()) { + return false; + } + return true; +} + +// Returns a HANDLE that should be closed when the software is shutting down. +HANDLE LockFromUpdates() { + SECURITY_ATTRIBUTES* security_attributes = NULL; + SECURITY_ATTRIBUTES security_attributes_buffer; + SECURITY_DESCRIPTOR security_descriptor; + + ZeroMemory(&security_attributes_buffer, sizeof(security_attributes_buffer)); + security_attributes_buffer.nLength = sizeof(security_attributes_buffer); + security_attributes_buffer.bInheritHandle = FALSE; + + // initialize the security descriptor and give it a NULL DACL + if (InitializeSecurityDescriptor(&security_descriptor, + SECURITY_DESCRIPTOR_REVISION)) { + if (SetSecurityDescriptorDacl(&security_descriptor, TRUE, + (PACL)NULL, FALSE)) { + security_attributes_buffer.lpSecurityDescriptor = &security_descriptor; + security_attributes = &security_attributes_buffer; + } + } + // An event is used to lock out updates since events are closed by the OS + // if their process dies. So, updates can still happen if an O3D executable + // crashes (as long as all instances of the running event are closed, either + // properly or due to a crash). + return CreateEvent(security_attributes, FALSE, FALSE, kRunningEventName); +} +} // namespace update_lock + +#endif // O3D_PLUGIN_WIN_UPDATE_LOCK_H_ + |