diff options
99 files changed, 2713 insertions, 982 deletions
diff --git a/autobuild.xml b/autobuild.xml index 072dfa678a..2d49fb17c0 100755 --- a/autobuild.xml +++ b/autobuild.xml @@ -1454,9 +1454,9 @@ <key>archive</key> <map> <key>hash</key> - <string>faa1e5b7cf70c143caabe190fa5588ce</string> + <string>fddd634dec5ec03924d62cc774f7f8ea</string> <key>url</key> - <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearance_viewer-update-llappearance-utility/rev/304432/arch/Linux/installer/llappearance_utility-0.0.1-linux-304432.tar.bz2</string> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/p64_viewer-llappearance-utility/rev/317266/arch/Linux/installer/llappearance_utility-0.0.1-linux-317266.tar.bz2</string> </map> <key>name</key> <string>linux</string> @@ -1484,11 +1484,11 @@ <key>archive</key> <map> <key>hash</key> - <string>29a1f64df46094eda0d681821a98d17e</string> + <string>db992d58c46c80df7d4d31f8a4784b98</string> <key>hash_algorithm</key> <string>md5</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/311349/arch/Darwin/installer/llceflib-1.5.3.311349-darwin-311349.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/317959/arch/Darwin/installer/llceflib-1.5.3.317959-darwin-317959.tar.bz2</string> </map> <key>name</key> <string>darwin</string> @@ -1498,18 +1498,18 @@ <key>archive</key> <map> <key>hash</key> - <string>827b7c339a2cd401d9d23f9ee02cb83f</string> + <string>bb3818628131a99cd789febfad9dc2c2</string> <key>hash_algorithm</key> <string>md5</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/311349/arch/CYGWIN/installer/llceflib-1.5.3.311349-windows-311349.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/317959/arch/CYGWIN/installer/llceflib-1.5.3.317959-windows-317959.tar.bz2</string> </map> <key>name</key> <string>windows</string> </map> </map> <key>version</key> - <string>1.5.3.311349</string> + <string>1.5.3.317959</string> </map> <key>llphysicsextensions_source</key> <map> @@ -2135,6 +2135,46 @@ <key>version</key> <string>0.8.0.1</string> </map> + <key>vlc-bin</key> + <map> + <key>copyright</key> + <string>Copyright (C) 1998-2016 VLC authors and VideoLAN</string> + <key>license</key> + <string>GPL2</string> + <key>license_file</key> + <string>LICENSES/vlc.txt</string> + <key>name</key> + <string>vlc-bin</string> + <key>platforms</key> + <map> + <key>linux</key> + <map> + <key>archive</key> + <map> + <key>hash</key> + <string>2f410640df3f9812d1abff02a414cfa8</string> + <key>url</key> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-vlc-bin/rev/315283/arch/Linux/vlc_bin-2.2.3-linux-201606011750-r10.tar.bz2</string> + </map> + <key>name</key> + <string>linux</string> + </map> + <key>windows</key> + <map> + <key>archive</key> + <map> + <key>hash</key> + <string>04cff37070a5f65f3652b4ddcec7183f</string> + <key>url</key> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-vlc-bin/rev/317935/arch/CYGWIN/installer/vlc_bin-2.2.4.317935-windows-317935.tar.bz2</string> + </map> + <key>name</key> + <string>windows</string> + </map> + </map> + <key>version</key> + <string>2.2.4.317935</string> + </map> <key>xmlrpc-epi</key> <map> <key>copyright</key> diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 6dc8e3dfbf..13a31cbce7 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -24,7 +24,6 @@ set(cmake_SOURCE_FILES DirectX.cmake DragDrop.cmake EXPAT.cmake -## ExamplePlugin.cmake FindAPR.cmake FindAutobuild.cmake FindBerkeleyDB.cmake @@ -100,6 +99,7 @@ set(cmake_SOURCE_FILES Variables.cmake ViewerMiscLibs.cmake VisualLeakDetector.cmake + LibVLCPlugin.cmake XmlRpcEpi.cmake ZLIB.cmake ) diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index ba07a80f05..80af7ff2ab 100644 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -18,7 +18,7 @@ else (USESYSTEMLIBS) use_prebuilt_binary(SDL) set (SDL_FOUND TRUE) set (SDL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/i686-linux) - set (SDL_LIBRARY SDL directfb fusion direct) + set (SDL_LIBRARY SDL directfb fusion direct X11) endif (LINUX) endif (USESYSTEMLIBS) diff --git a/indra/cmake/LibVLCPlugin.cmake b/indra/cmake/LibVLCPlugin.cmake new file mode 100644 index 0000000000..4472676fb4 --- /dev/null +++ b/indra/cmake/LibVLCPlugin.cmake @@ -0,0 +1,27 @@ +# -*- cmake -*- +include(Linking) +include(Prebuilt) + +if (USESYSTEMLIBS) + set(LIBVLCPLUGIN OFF CACHE BOOL + "LIBVLCPLUGIN support for the llplugin/llmedia test apps.") +else (USESYSTEMLIBS) + use_prebuilt_binary(vlc-bin) + set(LIBVLCPLUGIN ON CACHE BOOL + "LIBVLCPLUGIN support for the llplugin/llmedia test apps.") + set(VLC_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/vlc) +endif (USESYSTEMLIBS) + +if (WINDOWS) + set(VLC_PLUGIN_LIBRARIES + libvlc.lib + libvlccore.lib + ) +elseif (DARWIN) +elseif (LINUX) + # Specify a full path to make sure we get a static link + set(VLC_PLUGIN_LIBRARIES + ${LIBS_PREBUILT_DIR}/lib/libvlc.a + ${LIBS_PREBUILT_DIR}/lib/libvlccore.a + ) +endif (WINDOWS) diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt index 32aa08a2b7..9f9ca6c39c 100644 --- a/indra/edit-me-to-trigger-new-build.txt +++ b/indra/edit-me-to-trigger-new-build.txt @@ -1,3 +1,4 @@ 2014-02-25 10:34 + diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp index 9d5ec33fed..63e5409876 100644 --- a/indra/linux_crash_logger/linux_crash_logger.cpp +++ b/indra/linux_crash_logger/linux_crash_logger.cpp @@ -51,7 +51,7 @@ int main(int argc, char **argv) return 1; } - app.mainLoop(); + app.frame(); app.cleanup(); LL_INFOS() << "Crash reporter finished normally." << LL_ENDL; return 0; diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp index e2d2e7ff26..4092d43fc5 100644 --- a/indra/linux_crash_logger/llcrashloggerlinux.cpp +++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp @@ -114,7 +114,7 @@ void LLCrashLoggerLinux::gatherPlatformSpecificFiles() { } -bool LLCrashLoggerLinux::mainLoop() +bool LLCrashLoggerLinux::frame() { bool send_logs = true; if(CRASH_BEHAVIOR_ASK == getCrashBehavior()) diff --git a/indra/linux_crash_logger/llcrashloggerlinux.h b/indra/linux_crash_logger/llcrashloggerlinux.h index dae6c46651..789f6f03f5 100644 --- a/indra/linux_crash_logger/llcrashloggerlinux.h +++ b/indra/linux_crash_logger/llcrashloggerlinux.h @@ -36,7 +36,7 @@ class LLCrashLoggerLinux : public LLCrashLogger public: LLCrashLoggerLinux(void); ~LLCrashLoggerLinux(void); - virtual bool mainLoop(); + virtual bool frame(); virtual void updateApplication(const std::string& = LLStringUtil::null); virtual void gatherPlatformSpecificFiles(); virtual bool cleanup(); diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 907dbab8f8..410a5819b3 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -58,6 +58,7 @@ set(llcommon_SOURCE_FILES lleventfilter.cpp llevents.cpp lleventtimer.cpp + llexception.cpp llfasttimer.cpp llfile.cpp llfindlocale.cpp @@ -157,6 +158,7 @@ set(llcommon_HEADER_FILES lleventfilter.h llevents.h lleventemitter.h + llexception.h llfasttimer.h llfile.h llfindlocale.h @@ -315,7 +317,7 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}") - LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}") + LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}") @@ -328,6 +330,11 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}") +## llexception_test.cpp isn't a regression test, and doesn't need to be run +## every build. It's to help a developer make implementation choices about +## throwing and catching exceptions. +##LL_ADD_INTEGRATION_TEST(llexception "" "${test_libs}") + # *TODO - reenable these once tcmalloc libs no longer break the build. #ADD_BUILD_TEST(llallocator llcommon) #ADD_BUILD_TEST(llallocator_heap_profile llcommon) diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index d9933b3d36..ff9a92b45f 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -172,12 +172,12 @@ public: virtual bool cleanup() = 0; // Override to do application cleanup // - // mainLoop() + // frame() // - // Runs the application main loop. It's assumed that when you exit - // this method, the application is in one of the cleanup states, either QUITTING or ERROR + // Pass control to the application for a single frame. Returns 'done' + // flag: if frame() returns false, it expects to be called again. // - virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. + virtual bool frame() = 0; // Override for application body logic // // Crash logging diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index d16bf0160b..8e516d8beb 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -38,6 +38,7 @@ #include "llevents.h" #include "llerror.h" #include "stringize.h" +#include "llexception.h" // do nothing, when we need nothing done void LLCoros::no_cleanup(CoroData*) {} @@ -131,9 +132,9 @@ bool LLCoros::cleanup(const LLSD&) if ((previousCount < 5) || !(previousCount % 50)) { if (previousCount < 5) - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; else - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL; } // The erase() call will invalidate its passed iterator value -- @@ -185,9 +186,9 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const if ((previousCount < 5) || !(previousCount % 50)) { if (previousCount < 5) - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; else - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; } @@ -223,7 +224,7 @@ std::string LLCoros::getName() const void LLCoros::setStackSize(S32 stacksize) { - LL_INFOS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL; + LL_DEBUGS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL; mStackSize = stacksize; } @@ -235,7 +236,23 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla // capture the 'self' param in CoroData data->mSelf = &self; // run the code the caller actually wants in the coroutine - callable(); + try + { + callable(); + } + catch (const LLContinueError&) + { + // Any uncaught exception derived from LLContinueError will be caught + // here and logged. This coroutine will terminate but the rest of the + // viewer will carry on. + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + } + catch (...) + { + // Any OTHER kind of uncaught exception will cause the viewer to + // crash, hopefully informatively. + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + } // This cleanup isn't perfectly symmetrical with the way we initially set // data->mPrev, but this is our last chance to reset mCurrentCoro. sCurrentCoro.reset(data->mPrev); diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp index 0e72c175cb..0d5757effd 100644 --- a/indra/llcommon/lldependencies.cpp +++ b/indra/llcommon/lldependencies.cpp @@ -40,6 +40,7 @@ #include <boost/graph/topological_sort.hpp> #include <boost/graph/exception.hpp> // other Linden headers +#include "llexception.h" LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const { @@ -76,7 +77,7 @@ LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const // Omit independent nodes: display only those that might contribute to // the cycle. describe(out, false); - throw Cycle(out.str()); + LLTHROW(Cycle(out.str())); } // A peculiarity of boost::topological_sort() is that it emits results in // REVERSE topological order: to get the result you want, you must diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h index e0294e271b..125bd6a835 100644 --- a/indra/llcommon/lldependencies.h +++ b/indra/llcommon/lldependencies.h @@ -34,13 +34,13 @@ #include <vector> #include <set> #include <map> -#include <stdexcept> #include <iosfwd> #include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/indirect_iterator.hpp> #include <boost/range/iterator_range.hpp> #include <boost/function.hpp> #include <boost/bind.hpp> +#include "llexception.h" /***************************************************************************** * Utilities @@ -106,9 +106,9 @@ public: /** * Exception thrown by sort() if there's a cycle */ - struct Cycle: public std::runtime_error + struct Cycle: public LLException { - Cycle(const std::string& what): std::runtime_error(what) {} + Cycle(const std::string& what): LLException(what) {} }; /** diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 3beef65723..7cbe4334b3 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -174,7 +174,8 @@ namespace LLError // not really a level // used to indicate that no messages should be logged }; - + // If you change ELevel, please update llvlog() macro below. + /* Macro support The classes CallSite and Log are used by the logging macros below. They are not intended for general use. @@ -305,24 +306,38 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; ///////////////////////////////// // Error Logging Macros -// See top of file for common usage. +// See top of file for common usage. ///////////////////////////////// -// this macro uses a one-shot do statement to avoid parsing errors when writing control flow statements -// without braces: -// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL - -#define lllog(level, once, ...) \ - do { \ - const char* tags[] = {"", ##__VA_ARGS__}; \ - ::size_t tag_count = LL_ARRAY_SIZE(tags) - 1; \ - static LLError::CallSite _site( \ - level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, once, &tags[1], tag_count);\ - if (LL_UNLIKELY(_site.shouldLog())) \ - { \ - std::ostringstream* _out = LLError::Log::out(); \ +// Instead of using LL_DEBUGS(), LL_INFOS() et al., it may be tempting to +// directly code the lllog() macro so you can pass in the LLError::ELevel as a +// variable. DON'T DO IT! The reason is that the first time control passes +// through lllog(), it initializes a local static LLError::CallSite with that +// *first* ELevel value. All subsequent visits will decide whether or not to +// emit output based on the *first* ELevel value bound into that static +// CallSite instance. Use LL_VLOGS() instead. lllog() assumes its ELevel +// argument never varies. + +// this macro uses a one-shot do statement to avoid parsing errors when +// writing control flow statements without braces: +// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL; + +#define lllog(level, once, ...) \ + do { \ + const char* tags[] = {"", ##__VA_ARGS__}; \ + static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \ + lllog_test_() + +#define lllog_test_() \ + if (LL_UNLIKELY(_site.shouldLog())) \ + { \ + std::ostringstream* _out = LLError::Log::out(); \ (*_out) +#define lllog_site_args_(level, once, tags) \ + level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \ + __FUNCTION__, once, &tags[1], LL_ARRAY_SIZE(tags)-1 + //Use this construct if you need to do computation in the middle of a //message: // @@ -363,4 +378,46 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_INFOS_ONCE(...) lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__) #define LL_WARNS_ONCE(...) lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__) +// Use this if you need to pass LLError::ELevel as a variable. +#define LL_VLOGS(level, ...) llvlog(level, false, ##__VA_ARGS__) +#define LL_VLOGS_ONCE(level, ...) llvlog(level, true, ##__VA_ARGS__) + +// The problem with using lllog() with a variable level is that the first time +// through, it initializes a static CallSite instance with whatever level you +// pass. That first level is bound into the CallSite; the level parameter is +// never again examined. One approach to variable level would be to +// dynamically construct a CallSite instance every call -- which could get +// expensive, depending on context. So instead, initialize a static CallSite +// for each level value we support, then dynamically select the CallSite +// instance for the passed level value. +// Compare implementation to lllog() above. +#define llvlog(level, once, ...) \ + do { \ + const char* tags[] = {"", ##__VA_ARGS__}; \ + /* Need a static CallSite instance per expected ELevel value. */ \ + /* Since we intend to index this array with the ELevel, */ \ + /* _sites[0] should be ELevel(0), and so on -- avoid using */ \ + /* ELevel symbolic names when initializing -- except for */ \ + /* the last entry, which handles anything beyond the end. */ \ + /* (Commented ELevel value names are from 2016-09-01.) */ \ + /* Passing an ELevel past the end of this array is itself */ \ + /* a fatal error, so ensure the last is LEVEL_ERROR. */ \ + static LLError::CallSite _sites[] = \ + { \ + /* LEVEL_DEBUG */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(0), once, tags)), \ + /* LEVEL_INFO */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(1), once, tags)), \ + /* LEVEL_WARN */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(2), once, tags)), \ + /* LEVEL_ERROR */ \ + LLError::CallSite(lllog_site_args_(LLError::LEVEL_ERROR, once, tags)) \ + }; \ + /* Clamp the passed 'level' to at most last entry */ \ + std::size_t which((std::size_t(level) >= LL_ARRAY_SIZE(_sites)) ? \ + (LL_ARRAY_SIZE(_sites) - 1) : std::size_t(level)); \ + /* selected CallSite *must* be named _site for LL_ENDL */ \ + LLError::CallSite& _site(_sites[which]); \ + lllog_test_() + #endif // LL_LLERROR_H diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 2d5f964deb..56367b8f54 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -39,6 +39,7 @@ #include "llerror.h" #include "llcoros.h" #include "llmake.h" +#include "llexception.h" #include "lleventfilter.h" @@ -351,7 +352,7 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc) // returning it, deliver it via exception. if (result.second) { - throw LLErrorEvent(desc, result.first); + LLTHROW(LLErrorEvent(desc, result.first)); } // That way, our caller knows a simple return must be from the reply // pump (pump 0). diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 87926c692d..84827aab4a 100644 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -31,10 +31,10 @@ #include <boost/optional.hpp> #include <string> -#include <stdexcept> #include <utility> // std::pair #include "llevents.h" #include "llerror.h" +#include "llexception.h" /** * Like LLListenerOrPumpName, this is a class intended for parameter lists: @@ -234,11 +234,11 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc); * because it's not an error in event processing: rather, this exception * announces an event that bears error information (for some other API). */ -class LL_COMMON_API LLErrorEvent: public std::runtime_error +class LL_COMMON_API LLErrorEvent: public LLException { public: LLErrorEvent(const std::string& what, const LLSD& data): - std::runtime_error(what), + LLException(what), mData(data) {} virtual ~LLErrorEvent() throw() {} diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 645c29d770..19d700a3b0 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -57,6 +57,7 @@ #include "stringize.h" #include "llerror.h" #include "llsdutil.h" +#include "llexception.h" #if LL_MSVC #pragma warning (disable : 4702) #endif @@ -174,7 +175,7 @@ std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string // Unless we're permitted to tweak it, that's Bad. if (! tweak) { - throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'"); + LLTHROW(LLEventPump::DupPumpName("Duplicate LLEventPump name '" + name + "'")); } // The passed name isn't unique, but we're permitted to tweak it. Find the // first decimal-integer suffix not already taken. The insert() attempt @@ -326,8 +327,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL // is only when the existing connection object is still connected. if (found != mConnections.end() && found->second.connected()) { - throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name + - "' on " + typeid(*this).name() + " '" + getName() + "'"); + LLTHROW(DupListenerName("Attempt to register duplicate listener name '" + name + + "' on " + typeid(*this).name() + " '" + getName() + "'")); } // Okay, name is unique, try to reconcile its dependencies. Specify a new // "node" value that we never use for an mSignal placement; we'll fix it @@ -353,8 +354,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL // unsortable. If we leave the new node in mDeps, it will continue // to screw up all future attempts to sort()! Pull it out. mDeps.remove(name); - throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() + - " '" + getName() + "' would cause cycle: " + e.what()); + LLTHROW(Cycle("New listener '" + name + "' on " + typeid(*this).name() + + " '" + getName() + "' would cause cycle: " + e.what())); } // Walk the list to verify that we haven't changed the order. float previous = 0.0, myprev = 0.0; @@ -418,7 +419,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL // NOW remove the offending listener node. mDeps.remove(name); // Having constructed a description of the order change, inform caller. - throw OrderChange(out.str()); + LLTHROW(OrderChange(out.str())); } // This node becomes the previous one. previous = dmi->second; @@ -608,7 +609,7 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const { if (! mListener) { - throw Empty("attempting to call uninitialized"); + LLTHROW(Empty("attempting to call uninitialized")); } return (*mListener)(event); } diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index ba4fcd766e..1526128725 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -37,7 +37,6 @@ #include <set> #include <vector> #include <deque> -#include <stdexcept> #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch @@ -62,6 +61,7 @@ #include "llsingleton.h" #include "lldependencies.h" #include "llstl.h" +#include "llexception.h" /*==========================================================================*| // override this to allow binding free functions with more parameters @@ -95,12 +95,32 @@ struct LLStopWhenHandled result_type operator()(InputIterator first, InputIterator last) const { for (InputIterator si = first; si != last; ++si) - { - if (*si) - { - return true; - } - } + { + try + { + if (*si) + { + return true; + } + } + catch (const LLContinueError&) + { + // We catch LLContinueError here because an LLContinueError- + // based exception means the viewer as a whole should carry on + // to the best of our ability. Therefore subsequent listeners + // on the same LLEventPump should still receive this event. + + // The iterator passed to a boost::signals2 Combiner is very + // clever, but provides no contextual information. We would + // very much like to be able to log the name of the LLEventPump + // plus the name of this particular listener, but alas. + LOG_UNHANDLED_EXCEPTION("LLEventPump"); + } + // We do NOT catch (...) here because we might as well let it + // propagate out to the generic handler. If we were able to log + // context information here, that would be great, but we can't, so + // there's no point. + } return false; } }; @@ -188,10 +208,10 @@ public: bool operator()(const LLSD& event) const; /// exception if you try to call when empty - struct Empty: public std::runtime_error + struct Empty: public LLException { Empty(const std::string& what): - std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {} + LLException(std::string("LLListenerOrPumpName::Empty: ") + what) {} }; private: @@ -371,10 +391,10 @@ public: * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique * variant. */ - struct DupPumpName: public std::runtime_error + struct DupPumpName: public LLException { DupPumpName(const std::string& what): - std::runtime_error(std::string("DupPumpName: ") + what) {} + LLException(std::string("DupPumpName: ") + what) {} }; /** @@ -399,9 +419,9 @@ public: /// group exceptions thrown by listen(). We use exceptions because these /// particular errors are likely to be coding errors, found and fixed by /// the developer even before preliminary checkin. - struct ListenError: public std::runtime_error + struct ListenError: public LLException { - ListenError(const std::string& what): std::runtime_error(what) {} + ListenError(const std::string& what): LLException(what) {} }; /** * exception thrown by listen(). You are attempting to register a diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp new file mode 100644 index 0000000000..b32ec2c9c9 --- /dev/null +++ b/indra/llcommon/llexception.cpp @@ -0,0 +1,55 @@ +/** + * @file llexception.cpp + * @author Nat Goodspeed + * @date 2016-08-12 + * @brief Implementation for llexception. + * + * $LicenseInfo:firstyear=2016&license=viewerlgpl$ + * Copyright (c) 2016, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llexception.h" +// STL headers +// std headers +#include <typeinfo> +// external library headers +#include <boost/exception/diagnostic_information.hpp> +// other Linden headers +#include "llerror.h" +#include "llerrorcontrol.h" + +namespace { +// used by crash_on_unhandled_exception_() and log_unhandled_exception_() +void log_unhandled_exception_(LLError::ELevel level, + const char* file, int line, const char* pretty_function, + const std::string& context) +{ + // log same message but allow caller-specified severity level + LL_VLOGS(level, "LLException") << LLError::abbreviateFile(file) + << "(" << line << "): Unhandled exception caught in " << pretty_function; + if (! context.empty()) + { + LL_CONT << ": " << context; + } + LL_CONT << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; +} +} + +void crash_on_unhandled_exception_(const char* file, int line, const char* pretty_function, + const std::string& context) +{ + // LL_ERRS() terminates and propagates message into crash dump. + log_unhandled_exception_(LLError::LEVEL_ERROR, file, line, pretty_function, context); +} + +void log_unhandled_exception_(const char* file, int line, const char* pretty_function, + const std::string& context) +{ + // Use LL_WARNS() because we seriously do not expect this to happen + // routinely, but we DO expect to return from this function. + log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function, context); +} diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h new file mode 100644 index 0000000000..dfcb7c192f --- /dev/null +++ b/indra/llcommon/llexception.h @@ -0,0 +1,85 @@ +/** + * @file llexception.h + * @author Nat Goodspeed + * @date 2016-06-29 + * @brief Types needed for generic exception handling + * + * $LicenseInfo:firstyear=2016&license=viewerlgpl$ + * Copyright (c) 2016, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLEXCEPTION_H) +#define LL_LLEXCEPTION_H + +#include <stdexcept> +#include <boost/exception/exception.hpp> +#include <boost/throw_exception.hpp> +#include <boost/current_function.hpp> + +// "Found someone who can comfort me +// But there are always exceptions..." +// - Empty Pages, Traffic, from John Barleycorn (1970) +// https://www.youtube.com/watch?v=dRH0CGVK7ic + +/** + * LLException is intended as the common base class from which all + * viewer-specific exceptions are derived. Rationale for why it's derived from + * both std::exception and boost::exception is explained in + * tests/llexception_test.cpp. + * + * boost::current_exception_diagnostic_information() is quite wonderful: if + * all we need to do with an exception is log it, in most places we should + * catch (...) and log boost::current_exception_diagnostic_information(). + * See CRASH_ON_UNHANDLED_EXCEPTION() and LOG_UNHANDLED_EXCEPTION() below. + * + * There may be circumstances in which it would be valuable to distinguish an + * exception explicitly thrown by viewer code from an exception thrown by + * (say) a third-party library. Catching (const LLException&) supports such + * usage. However, most of the value of this base class is in the + * diagnostic_information() available via Boost.Exception. + */ +struct LLException: + public std::runtime_error, + public boost::exception +{ + LLException(const std::string& what): + std::runtime_error(what) + {} +}; + +/** + * The point of LLContinueError is to distinguish exceptions that need not + * terminate the whole viewer session. In general, an uncaught exception will + * be logged and will crash the viewer. However, though an uncaught exception + * derived from LLContinueError will still be logged, the viewer will attempt + * to continue processing. + */ +struct LLContinueError: public LLException +{ + LLContinueError(const std::string& what): + LLException(what) + {} +}; + +/** + * Please use LLTHROW() to throw viewer exceptions whenever possible. This + * enriches the exception's diagnostic_information() with the source file, + * line and containing function of the LLTHROW() macro. + */ +// Currently we implement that using BOOST_THROW_EXCEPTION(). Wrap it in +// LLTHROW() in case we ever want to revisit that implementation decision. +#define LLTHROW(x) BOOST_THROW_EXCEPTION(x) + +/// Call this macro from a catch (...) clause +#define CRASH_ON_UNHANDLED_EXCEPTION(CONTEXT) \ + crash_on_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT) +void crash_on_unhandled_exception_(const char*, int, const char*, const std::string&); + +/// Call this from a catch (const LLContinueError&) clause, or from a catch +/// (...) clause in which you do NOT want the viewer to crash. +#define LOG_UNHANDLED_EXCEPTION(CONTEXT) \ + log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT) +void log_unhandled_exception_(const char*, int, const char*, const std::string&); + +#endif /* ! defined(LL_LLEXCEPTION_H) */ diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 2370253078..f56e5596f5 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -296,7 +296,16 @@ LL_FORCE_INLINE BlockTimer::BlockTimer(BlockTimerStatHandle& timer) { #if LL_FAST_TIMER_ON BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); - if (!cur_timer_data) return; + if (!cur_timer_data) + { + // How likely is it that + // LLThreadLocalSingletonPointer<T>::getInstance() will return NULL? + // Even without researching, what we can say is that if we exit + // without setting mStartTime at all, gcc 4.7 produces (fatal) + // warnings about a possibly-uninitialized data member. + mStartTime = 0; + return; + } TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); accumulator.mActiveCount++; // keep current parent as long as it is active when we are diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 401e4d759a..feb5f41848 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -28,8 +28,11 @@ #define LLHANDLE_H #include "llpointer.h" +#include "llexception.h" +#include <stdexcept> #include <boost/type_traits/is_convertible.hpp> #include <boost/utility/enable_if.hpp> +#include <boost/throw_exception.hpp> /** * Helper object for LLHandle. Don't instantiate these directly, used @@ -213,4 +216,82 @@ private: mutable LLRootHandle<T> mHandle; }; + + +class LLCheckedHandleBase +{ +public: + class Stale : public LLException + { + public: + Stale() : + LLException("Attempt to access stale handle.") + {} + }; + +protected: + LLCheckedHandleBase() { } + +}; + +/** + * This is a simple wrapper for Handles, allowing direct calls to the underlying + * pointer. The checked handle will throw a Stale if an attempt + * is made to access the object referenced by the handle and that object has + * been destroyed. + **/ +template <typename T> +class LLCheckedHandle: public LLCheckedHandleBase +{ +public: + + LLCheckedHandle(LLHandle<T> handle): + mHandle(handle) + { } + + /** + * Test the underlying handle. If it is no longer valid, throw a Stale exception. + */ + void check() const + { + T* ptr = mHandle.get(); + if (!ptr) + BOOST_THROW_EXCEPTION(Stale()); + } + + /** + * Cast back to an appropriate handle + */ + operator LLHandle<T>() const + { + return mHandle; + } + + /** + * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {} + * Does not throw. + */ + /*explicit*/ operator bool() const // explicit conversion operator not available with Linux compiler + { + return (mHandle.get() != NULL); + } + + /** + * Attempt to call a method or access a member in the structure referenced + * by the handle. If the handle no longer points to a valid structure + * throw a Stale. + */ + T* operator ->() const + { + T* ptr = mHandle.get(); + if (!ptr) + BOOST_THROW_EXCEPTION(Stale()); + return ptr; + } + +private: + + LLHandle<T> mHandle; +}; + #endif diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index 84d2a12f65..c87d2a3e58 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -33,6 +33,7 @@ #include "lltimer.h" #include "lluuid.h" #include "llleaplistener.h" +#include "llexception.h" #if LL_MSVC #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally @@ -69,7 +70,7 @@ public: // Rule out empty vector if (plugin.empty()) { - throw Error("no plugin command"); + LLTHROW(Error("no plugin command")); } // Don't leave desc empty either, but in this case, if we weren't @@ -112,7 +113,7 @@ public: // If that didn't work, no point in keeping this LLLeap object. if (! mChild) { - throw Error(STRINGIZE("failed to run " << mDesc)); + LLTHROW(Error(STRINGIZE("failed to run " << mDesc))); } // Okay, launch apparently worked. Change our mDonePump listener. diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h index e33f25e530..8aac8a64c5 100644 --- a/indra/llcommon/llleap.h +++ b/indra/llcommon/llleap.h @@ -13,9 +13,9 @@ #define LL_LLLEAP_H #include "llinstancetracker.h" +#include "llexception.h" #include <string> #include <vector> -#include <stdexcept> /** * LLSD Event API Plugin class. Because instances are managed by @@ -67,9 +67,9 @@ public: * string(s) passed to create() might come from an external source. This * way the caller can catch LLLeap::Error and try to recover. */ - struct Error: public std::runtime_error + struct Error: public LLException { - Error(const std::string& what): std::runtime_error(what) {} + Error(const std::string& what): LLException(what) {} }; virtual ~LLLeap(); diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 0fb257aab1..575edddc43 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -110,11 +110,15 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) #if defined(LL_WINDOWS) return _aligned_malloc(size, align); #else + char* aligned = NULL; void* mem = malloc( size + (align - 1) + sizeof(void*) ); - char* aligned = ((char*)mem) + sizeof(void*); - aligned += align - ((uintptr_t)aligned & (align - 1)); + if (mem) + { + aligned = ((char*)mem) + sizeof(void*); + aligned += align - ((uintptr_t)aligned & (align - 1)); - ((void**)aligned)[-1] = mem; + ((void**)aligned)[-1] = mem; + } return aligned; #endif } diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 44f56daf2d..8c321d06b9 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -34,6 +34,7 @@ #include "llapr.h" #include "apr_signal.h" #include "llevents.h" +#include "llexception.h" #include <boost/foreach.hpp> #include <boost/bind.hpp> @@ -472,9 +473,9 @@ private: *****************************************************************************/ /// Need an exception to avoid constructing an invalid LLProcess object, but /// internal use only -struct LLProcessError: public std::runtime_error +struct LLProcessError: public LLException { - LLProcessError(const std::string& msg): std::runtime_error(msg) {} + LLProcessError(const std::string& msg): LLException(msg) {} }; LLProcessPtr LLProcess::create(const LLSDOrParams& params) @@ -530,8 +531,8 @@ LLProcess::LLProcess(const LLSDOrParams& params): if (! params.validateBlock(true)) { - throw LLProcessError(STRINGIZE("not launched: failed parameter validation\n" - << LLSDNotationStreamer(params))); + LLTHROW(LLProcessError(STRINGIZE("not launched: failed parameter validation\n" + << LLSDNotationStreamer(params)))); } mPostend = params.postend; @@ -596,10 +597,10 @@ LLProcess::LLProcess(const LLSDOrParams& params): } else { - throw LLProcessError(STRINGIZE("For " << params.executable() - << ": unsupported FileParam for " << which - << ": type='" << fparam.type() - << "', name='" << fparam.name() << "'")); + LLTHROW(LLProcessError(STRINGIZE("For " << params.executable() + << ": unsupported FileParam for " << which + << ": type='" << fparam.type() + << "', name='" << fparam.name() << "'"))); } } // By default, pass APR_NO_PIPE for unspecified slots. @@ -678,7 +679,7 @@ LLProcess::LLProcess(const LLSDOrParams& params): if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr, gAPRPoolp))) { - throw LLProcessError(STRINGIZE(params << " failed")); + LLTHROW(LLProcessError(STRINGIZE(params << " failed"))); } // arrange to call status_callback() @@ -1063,7 +1064,7 @@ PIPETYPE& LLProcess::getPipe(FILESLOT slot) PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot); if (! wp) { - throw NoPipe(error); + LLTHROW(NoPipe(error)); } return *wp; } diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h index 43ccadc412..bfac4567a5 100644 --- a/indra/llcommon/llprocess.h +++ b/indra/llcommon/llprocess.h @@ -30,13 +30,13 @@ #include "llinitparam.h" #include "llsdparam.h" #include "llwin32headerslean.h" +#include "llexception.h" #include "apr_thread_proc.h" #include <boost/shared_ptr.hpp> #include <boost/ptr_container/ptr_vector.hpp> #include <boost/optional.hpp> #include <boost/noncopyable.hpp> #include <iosfwd> // std::ostream -#include <stdexcept> #if LL_WINDOWS #include "llwin32headerslean.h" // for HANDLE @@ -479,9 +479,9 @@ public: /// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to /// create a pipe at the corresponding FILESLOT. - struct NoPipe: public std::runtime_error + struct NoPipe: public LLException { - NoPipe(const std::string& what): std::runtime_error(what) {} + NoPipe(const std::string& what): LLException(what) {} }; /** diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp index 185f0d63fb..491f920c0f 100644 --- a/indra/llcommon/llthreadsafequeue.cpp +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -27,6 +27,7 @@ #include <apr_pools.h> #include <apr_queue.h> #include "llthreadsafequeue.h" +#include "llexception.h" @@ -41,13 +42,13 @@ LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * po { if(mOwnsPool) { apr_status_t status = apr_pool_create(&mPool, 0); - if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool"); + if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate pool")); } else { ; // No op. } apr_status_t status = apr_queue_create(&mQueue, capacity, mPool); - if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue"); + if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate queue")); } @@ -68,9 +69,9 @@ void LLThreadSafeQueueImplementation::pushFront(void * element) apr_status_t status = apr_queue_push(mQueue, element); if(status == APR_EINTR) { - throw LLThreadSafeQueueInterrupt(); + LLTHROW(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - throw LLThreadSafeQueueError("push failed"); + LLTHROW(LLThreadSafeQueueError("push failed")); } else { ; // Success. } @@ -88,9 +89,9 @@ void * LLThreadSafeQueueImplementation::popBack(void) apr_status_t status = apr_queue_pop(mQueue, &element); if(status == APR_EINTR) { - throw LLThreadSafeQueueInterrupt(); + LLTHROW(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - throw LLThreadSafeQueueError("pop failed"); + LLTHROW(LLThreadSafeQueueError("pop failed")); } else { return element; } diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index 58cac38769..45289ef0b4 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -27,9 +27,8 @@ #ifndef LL_LLTHREADSAFEQUEUE_H #define LL_LLTHREADSAFEQUEUE_H - +#include "llexception.h" #include <string> -#include <stdexcept> struct apr_pool_t; // From apr_pools.h @@ -40,11 +39,11 @@ class LLThreadSafeQueueImplementation; // See below. // A general queue exception. // class LL_COMMON_API LLThreadSafeQueueError: -public std::runtime_error + public LLException { public: LLThreadSafeQueueError(std::string const & message): - std::runtime_error(message) + LLException(message) { ; // No op. } diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index e3671047b4..d4af2c6b01 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -83,7 +83,7 @@ unsigned int decode( char const * fiveChars ) throw( bad_input_data ) unsigned int ret = 0; for( int ix = 0; ix < 5; ++ix ) { char * s = strchr( encodeTable, fiveChars[ ix ] ); -if( s == 0 ) throw bad_input_data(); +if( s == 0 ) LLTHROW(bad_input_data()); ret = ret * 85 + (s-encodeTable); } return ret; diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index f51279e817..8bace8ac41 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -237,8 +237,21 @@ namespace tut void ErrorTestObject::test<4>() // file abbreviation { - std::string thisFile = __FILE__; - std::string abbreviateFile = LLError::abbreviateFile(thisFile); + std::string prev, abbreviateFile = __FILE__; + do + { + prev = abbreviateFile; + abbreviateFile = LLError::abbreviateFile(abbreviateFile); + // __FILE__ is assumed to end with + // indra/llcommon/tests/llerror_test.cpp. This test used to call + // abbreviateFile() exactly once, then check below whether it + // still contained the string 'indra'. That fails if the FIRST + // part of the pathname also contains indra! Certain developer + // machine images put local directory trees under + // /ngi-persist/indra, which is where we observe the problem. So + // now, keep calling abbreviateFile() until it returns its + // argument unchanged, THEN check. + } while (abbreviateFile != prev); ensure_ends_with("file name abbreviation", abbreviateFile, diff --git a/indra/llcommon/tests/llexception_test.cpp b/indra/llcommon/tests/llexception_test.cpp new file mode 100644 index 0000000000..6bee1943c2 --- /dev/null +++ b/indra/llcommon/tests/llexception_test.cpp @@ -0,0 +1,308 @@ +/** + * @file llexception_test.cpp + * @author Nat Goodspeed + * @date 2016-08-12 + * @brief Tests for throwing exceptions. + * + * This isn't a regression test: it doesn't need to be run every build, which + * is why the corresponding line in llcommon/CMakeLists.txt is commented out. + * Rather it's a head-to-head test of what kind of exception information we + * can collect from various combinations of exception base classes, type of + * throw verb and sequences of catch clauses. + * + * This "test" makes no ensure() calls: its output goes to stdout for human + * examination. + * + * As of 2016-08-12 with Boost 1.57, we come to the following conclusions. + * These should probably be re-examined from time to time as we update Boost. + * + * - It is indisputably beneficial to use BOOST_THROW_EXCEPTION() rather than + * plain throw. The macro annotates the exception object with the filename, + * line number and function name from which the exception was thrown. + * + * - That being the case, deriving only from boost::exception isn't an option. + * Every exception object passed to BOOST_THROW_EXCEPTION() must be derived + * directly or indirectly from std::exception. The only question is whether + * to also derive from boost::exception. We decided to derive LLException + * from both, as it makes message output slightly cleaner, but this is a + * trivial reason: if a strong reason emerges to prefer single inheritance, + * dropping the boost::exception base class shouldn't be a problem. + * + * - (As you will have guessed, ridiculous things like a char* or int or a + * class derived from neither boost::exception nor std::exception can only + * be caught by that specific type or (...), and + * boost::current_exception_diagnostic_information() simply throws up its + * hands and confesses utter ignorance. Stay away from such nonsense.) + * + * - But if you derive from std::exception, to nat's surprise, + * boost::current_exception_diagnostic_information() gives as much + * information about exceptions in a catch (...) clause as you can get from + * a specific catch (const std::exception&) clause, notably the concrete + * exception class and the what() string. So instead of a sequence like + * + * try { ... } + * catch (const boost::exception& e) { ... boost-flavored logging ... } + * catch (const std::exception& e) { ... std::exception logging ... } + * catch (...) { ... generic logging ... } + * + * we should be able to get away with only a catch (...) clause that logs + * boost::current_exception_diagnostic_information(). + * + * - Going further: boost::current_exception_diagnostic_information() provides + * just as much information even within a std::set_terminate() handler. So + * it might not even be strictly necessary to include a catch (...) clause + * since the viewer does use std::set_terminate(). + * + * - (We might consider adding a catch (int) clause because Kakadu internally + * throws ints, and who knows if one of those might leak out. If it does, + * boost::current_exception_diagnostic_information() can do nothing with it. + * A catch (int) clause could at least log the value and rethrow.) + * + * $LicenseInfo:firstyear=2016&license=viewerlgpl$ + * Copyright (c) 2016, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llexception.h" +// STL headers +// std headers +#include <typeinfo> +// external library headers +#include <boost/throw_exception.hpp> +// other Linden headers +#include "../test/lltut.h" + +// helper for display output +// usage: std::cout << center(some string value, fill char, width) << std::endl; +// (assumes it's the only thing on that particular line) +struct center +{ + center(const std::string& label, char fill, std::size_t width): + mLabel(label), + mFill(fill), + mWidth(width) + {} + + // Use friend declaration not because we need to grant access, but because + // it lets us declare a free operator like a member function. + friend std::ostream& operator<<(std::ostream& out, const center& ctr) + { + std::size_t padded = ctr.mLabel.length() + 2; + std::size_t left = (ctr.mWidth - padded) / 2; + std::size_t right = ctr.mWidth - left - padded; + return out << std::string(left, ctr.mFill) << ' ' << ctr.mLabel << ' ' + << std::string(right, ctr.mFill); + } + + std::string mLabel; + char mFill; + std::size_t mWidth; +}; + +/***************************************************************************** +* Four kinds of exceptions: derived from boost::exception, from +* std::exception, from both, from neither +*****************************************************************************/ +// Interestingly, we can't use this variant with BOOST_THROW_EXCEPTION() +// (which we want) -- we reach a failure topped by this comment: +// //All boost exceptions are required to derive from std::exception, +// //to ensure compatibility with BOOST_NO_EXCEPTIONS. +struct FromBoost: public boost::exception +{ + FromBoost(const std::string& what): mWhat(what) {} + ~FromBoost() throw() {} + std::string what() const { return mWhat; } + std::string mWhat; +}; + +struct FromStd: public std::runtime_error +{ + FromStd(const std::string& what): std::runtime_error(what) {} +}; + +struct FromBoth: public boost::exception, public std::runtime_error +{ + FromBoth(const std::string& what): std::runtime_error(what) {} +}; + +// Same deal with FromNeither: can't use with BOOST_THROW_EXCEPTION(). +struct FromNeither +{ + FromNeither(const std::string& what): mWhat(what) {} + std::string what() const { return mWhat; } + std::string mWhat; +}; + +/***************************************************************************** +* Two kinds of throws: plain throw and BOOST_THROW_EXCEPTION() +*****************************************************************************/ +template <typename EXC> +void plain_throw(const std::string& what) +{ + throw EXC(what); +} + +template <typename EXC> +void boost_throw(const std::string& what) +{ + BOOST_THROW_EXCEPTION(EXC(what)); +} + +// Okay, for completeness, functions that throw non-class values. We wouldn't +// even deign to consider these if we hadn't found examples in our own source +// code! (Note that Kakadu's internal exception support is still based on +// throwing ints.) +void throw_char_ptr(const std::string& what) +{ + throw what.c_str(); // umm... +} + +void throw_int(const std::string& what) +{ + throw int(what.length()); +} + +/***************************************************************************** +* Three sequences of catch clauses: +* boost::exception then ..., +* std::exception then ..., +* or just ... +*****************************************************************************/ +void catch_boost_dotdotdot(void (*thrower)(const std::string&), const std::string& what) +{ + try + { + thrower(what); + } + catch (const boost::exception& e) + { + std::cout << "catch (const boost::exception& e)" << std::endl; + std::cout << "e is " << typeid(e).name() << std::endl; + std::cout << "boost::diagnostic_information(e):\n'" + << boost::diagnostic_information(e) << "'" << std::endl; + // no way to report e.what() + } + catch (...) + { + std::cout << "catch (...)" << std::endl; + std::cout << "boost::current_exception_diagnostic_information():\n'" + << boost::current_exception_diagnostic_information() << "'" + << std::endl; + } +} + +void catch_std_dotdotdot(void (*thrower)(const std::string&), const std::string& what) +{ + try + { + thrower(what); + } + catch (const std::exception& e) + { + std::cout << "catch (const std::exception& e)" << std::endl; + std::cout << "e is " << typeid(e).name() << std::endl; + std::cout << "boost::diagnostic_information(e):\n'" + << boost::diagnostic_information(e) << "'" << std::endl; + std::cout << "e.what: '" + << e.what() << "'" << std::endl; + } + catch (...) + { + std::cout << "catch (...)" << std::endl; + std::cout << "boost::current_exception_diagnostic_information():\n'" + << boost::current_exception_diagnostic_information() << "'" + << std::endl; + } +} + +void catch_dotdotdot(void (*thrower)(const std::string&), const std::string& what) +{ + try + { + thrower(what); + } + catch (...) + { + std::cout << "catch (...)" << std::endl; + std::cout << "boost::current_exception_diagnostic_information():\n'" + << boost::current_exception_diagnostic_information() << "'" + << std::endl; + } +} + +/***************************************************************************** +* Try a particular kind of throw against each of three catch sequences +*****************************************************************************/ +void catch_several(void (*thrower)(const std::string&), const std::string& what) +{ + std::cout << std::string(20, '-') << "catch_boost_dotdotdot(" << what << ")" << std::endl; + catch_boost_dotdotdot(thrower, "catch_boost_dotdotdot(" + what + ")"); + + std::cout << std::string(20, '-') << "catch_std_dotdotdot(" << what << ")" << std::endl; + catch_std_dotdotdot(thrower, "catch_std_dotdotdot(" + what + ")"); + + std::cout << std::string(20, '-') << "catch_dotdotdot(" << what << ")" << std::endl; + catch_dotdotdot(thrower, "catch_dotdotdot(" + what + ")"); +} + +/***************************************************************************** +* For a particular kind of exception, try both kinds of throw against all +* three catch sequences +*****************************************************************************/ +template <typename EXC> +void catch_both_several(const std::string& what) +{ + std::cout << std::string(20, '*') << "plain_throw<" << what << ">" << std::endl; + catch_several(plain_throw<EXC>, "plain_throw<" + what + ">"); + + std::cout << std::string(20, '*') << "boost_throw<" << what << ">" << std::endl; + catch_several(boost_throw<EXC>, "boost_throw<" + what + ">"); +} + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llexception_data + { + }; + typedef test_group<llexception_data> llexception_group; + typedef llexception_group::object object; + llexception_group llexceptiongrp("llexception"); + + template<> template<> + void object::test<1>() + { + set_test_name("throwing exceptions"); + + // For each kind of exception, try both kinds of throw against all + // three catch sequences + std::size_t margin = 72; + std::cout << center("FromStd", '=', margin) << std::endl; + catch_both_several<FromStd>("FromStd"); + + std::cout << center("FromBoth", '=', margin) << std::endl; + catch_both_several<FromBoth>("FromBoth"); + + std::cout << center("FromBoost", '=', margin) << std::endl; + // can't throw with BOOST_THROW_EXCEPTION(), just use catch_several() + catch_several(plain_throw<FromBoost>, "plain_throw<FromBoost>"); + + std::cout << center("FromNeither", '=', margin) << std::endl; + // can't throw this with BOOST_THROW_EXCEPTION() either + catch_several(plain_throw<FromNeither>, "plain_throw<FromNeither>"); + + std::cout << center("const char*", '=', margin) << std::endl; + // We don't expect BOOST_THROW_EXCEPTION() to throw anything so daft + // as a const char* or an int, so don't bother with + // catch_both_several() -- just catch_several(). + catch_several(throw_char_ptr, "throw_char_ptr"); + + std::cout << center("int", '=', margin) << std::endl; + catch_several(throw_int, "throw_int"); + } +} // namespace tut diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index 785197ba11..9a4bbbd630 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -35,13 +35,13 @@ #include <tut/tut.hpp> #include "llerrorcontrol.h" +#include "llexception.h" #include "stringize.h" #include <boost/bind.hpp> #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> #include <list> #include <string> -#include <stdexcept> // statically reference the function in test.cpp... it's short, we could // replicate, but better to reuse @@ -67,9 +67,9 @@ struct WrapLLErrs LLError::restoreSettings(mPriorErrorSettings); } - struct FatalException: public std::runtime_error + struct FatalException: public LLException { - FatalException(const std::string& what): std::runtime_error(what) {} + FatalException(const std::string& what): LLException(what) {} }; void operator()(const std::string& message) @@ -78,7 +78,7 @@ struct WrapLLErrs error = message; // Also throw an appropriate exception since calling code is likely to // assume that control won't continue beyond LL_ERRS. - throw FatalException(message); + LLTHROW(FatalException(message)); } std::string error; diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index 8b4afae24a..56e26c23ba 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -57,7 +57,7 @@ public: LLSD constructPostData(); virtual void updateApplication(const std::string& message = LLStringUtil::null); virtual bool init(); - virtual bool mainLoop() = 0; + virtual bool frame() = 0; virtual bool cleanup() = 0; void commonCleanup(); void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; } diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index feb97ec2ab..f71607096c 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -1426,7 +1426,12 @@ void LLImageRaw::copyScaled( LLImageRaw* src ) bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data ) { - llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) ); + S32 components = getComponents(); + if (! ((1 == components) || (3 == components) || (4 == components) )) + { + LL_WARNS() << "Invalid getComponents value (" << components << ")" << LL_ENDL; + return false; + } if (isBufferInvalid()) { @@ -1446,67 +1451,53 @@ bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data ) if (scale_image_data) { - /* - S32 temp_data_size = old_width * new_height * getComponents(); - llassert_always(temp_data_size > 0); - std::vector<U8> temp_buffer(temp_data_size); - - // Vertical - for( S32 col = 0; col < old_width; col++ ) - { - copyLineScaled( getData() + (getComponents() * col), &temp_buffer[0] + (getComponents() * col), old_height, new_height, old_width, old_width ); - } - - deleteData(); + S32 new_data_size = new_width * new_height * components; - U8* new_buffer = allocateDataSize(new_width, new_height, getComponents()); + if (new_data_size > 0) + { + U8 *new_data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), new_data_size); + if(NULL == new_data) + { + return false; + } - // Horizontal - for( S32 row = 0; row < new_height; row++ ) - { - copyLineScaled( &temp_buffer[0] + (getComponents() * old_width * row), new_buffer + (getComponents() * new_width * row), old_width, new_width, 1, 1 ); - } - */ - - S32 new_data_size = new_width * new_height * getComponents(); - llassert_always(new_data_size > 0); - - U8 *new_data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), new_data_size); - if(NULL == new_data) - { - return false; + bilinear_scale(getData(), old_width, old_height, components, old_width*components, new_data, new_width, new_height, components, new_width*components); + setDataAndSize(new_data, new_width, new_height, components); } - - bilinear_scale(getData(), old_width, old_height, getComponents(), old_width*getComponents(), new_data, new_width, new_height, getComponents(), new_width*getComponents()); - setDataAndSize(new_data, new_width, new_height, getComponents()); } else { // copy out existing image data - S32 temp_data_size = old_width * old_height * getComponents(); + S32 temp_data_size = old_width * old_height * components; std::vector<U8> temp_buffer(temp_data_size); memcpy(&temp_buffer[0], getData(), temp_data_size); // allocate new image data, will delete old data - U8* new_buffer = allocateDataSize(new_width, new_height, getComponents()); - - for( S32 row = 0; row < new_height; row++ ) - { - if (row < old_height) - { - memcpy(new_buffer + (new_width * row * getComponents()), &temp_buffer[0] + (old_width * row * getComponents()), getComponents() * llmin(old_width, new_width)); - if (old_width < new_width) - { - // pad out rest of row with black - memset(new_buffer + (getComponents() * ((new_width * row) + old_width)), 0, getComponents() * (new_width - old_width)); - } - } - else - { - // pad remaining rows with black - memset(new_buffer + (new_width * row * getComponents()), 0, new_width * getComponents()); - } - } + U8* new_buffer = allocateDataSize(new_width, new_height, components); + + if (!new_buffer) + { + LL_WARNS() << "Failed to allocate new image data buffer" << LL_ENDL; + return false; + } + + for( S32 row = 0; row < new_height; row++ ) + { + if (row < old_height) + { + memcpy(new_buffer + (new_width * row * components), &temp_buffer[0] + (old_width * row * components), components * llmin(old_width, new_width)); + if (old_width < new_width) + { + // pad out rest of row with black + memset(new_buffer + (components * ((new_width * row) + old_width)), 0, components * (new_width - old_width)); + } + } + else + { + // pad remaining rows with black + memset(new_buffer + (new_width * row * components), 0, new_width * components); + } + } } return true ; diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp index aad139f570..da289ea889 100644 --- a/indra/llimage/llpngwrapper.cpp +++ b/indra/llimage/llpngwrapper.cpp @@ -31,6 +31,16 @@ #include "llimage.h" #include "llpngwrapper.h" +#include "llexception.h" + +namespace { +// Failure to load an image shouldn't crash the whole viewer. +struct PngError: public LLContinueError +{ + PngError(png_const_charp msg): LLContinueError(msg) {} +}; +} // anonymous namespace + // --------------------------------------------------------------------------- // LLPngWrapper // --------------------------------------------------------------------------- @@ -75,11 +85,10 @@ BOOL LLPngWrapper::isValidPng(U8* src) } // Called by the libpng library when a fatal encoding or decoding error -// occurs. We simply throw the error message and let our try/catch -// block clean up. +// occurs. We throw PngError and let our try/catch block clean up. void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg) { - throw msg; + LLTHROW(PngError(msg)); } // Called by the libpng library when reading (decoding) the PNG file. We @@ -129,7 +138,7 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf this, &errorHandler, NULL); if (mReadPngPtr == NULL) { - throw "Problem creating png read structure"; + LLTHROW(PngError("Problem creating png read structure")); } // Allocate/initialize the memory for image information. @@ -187,9 +196,9 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf mFinalSize = dataPtr.mOffset; } - catch (png_const_charp msg) + catch (const PngError& msg) { - mErrorMessage = msg; + mErrorMessage = msg.what(); releaseResources(); return (FALSE); } @@ -288,14 +297,14 @@ BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest) if (mColorType == -1) { - throw "Unsupported image: unexpected number of channels"; + LLTHROW(PngError("Unsupported image: unexpected number of channels")); } mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &errorHandler, NULL); if (!mWritePngPtr) { - throw "Problem creating png write structure"; + LLTHROW(PngError("Problem creating png write structure")); } mWriteInfoPtr = png_create_info_struct(mWritePngPtr); @@ -339,9 +348,9 @@ BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest) png_write_end(mWritePngPtr, mWriteInfoPtr); mFinalSize = dataPtr.mOffset; } - catch (png_const_charp msg) + catch (const PngError& msg) { - mErrorMessage = msg; + mErrorMessage = msg.what(); releaseResources(); return (FALSE); } diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 025c77b85e..aa405362e8 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1,4 +1,4 @@ - /** +/** * @file llimagej2ckdu.cpp * @brief This is an implementation of JPEG2000 encode/decode using Kakadu * @@ -35,16 +35,51 @@ #include "kdu_block_coding.h" -#include <stdexcept> -#include <iostream> +#include "llexception.h" +#include <boost/exception/diagnostic_information.hpp> +#include <sstream> +#include <iomanip> namespace { -// exception used to keep KDU from terminating entire program -- see comments -// in LLKDUMessageError::flush() -struct KDUError: public std::runtime_error +// Failure to load an image shouldn't crash the whole viewer. +struct KDUError: public LLContinueError { - KDUError(const std::string& msg): std::runtime_error(msg) {} + KDUError(const std::string& msg): LLContinueError(msg) {} }; + +// KDU defines int error codes as hex values, so we should log them in hex +// so we can grep KDU headers for the hex. However those hex values +// generally "happen" to encode big-endian multibyte character sequences, +// e.g. KDU_ERROR_EXCEPTION is 0x6b647545: 'kduE' +// But beware because KDU_NULL_EXCEPTION is simply 0 -- which doesn't +// preclude somebody from throwing it. +std::string report_kdu_exception(kdu_exception mb) +{ + std::ostringstream out; + // always report mb in hex + out << "kdu_exception " << std::hex << mb; + + // Also display as many chars as are encoded in the kdu_exception + // value. Make a char array; reserve 1 extra byte for nul terminator. + char bytes[sizeof(kdu_exception) + 1]; + // Back up through 'bytes' + char *bptr = bytes + sizeof(bytes); + *(--bptr) = '\0'; + while (mb) + { + // store low-order byte of mb in next-left char + *(--bptr) = char(mb & 0xFF); + // then shift mb right by one byte + mb >>= 8; + } + // did that produce any characters? + if (*bptr) + { + out << " (" << bptr << ')'; + } + + return out.str(); +} } // anonymous namespace // stream kdu_dims to std::ostream @@ -195,7 +230,7 @@ struct LLKDUMessageError : public LLKDUMessage // shutdown will NOT engage the behavior described above. if (end_of_message) { - throw KDUError("LLKDUMessageError::flush()"); + LLTHROW(KDUError("LLKDUMessageError::flush()")); } } }; @@ -309,10 +344,10 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod { // This method is only called from methods that catch KDUError. // We want to fail the image load, not crash the viewer. - throw KDUError(STRINGIZE("Component " << idx << " dimensions " + LLTHROW(KDUError(STRINGIZE("Component " << idx << " dimensions " << other_dims << " do not match component 0 dimensions " - << dims << "!")); + << dims << "!"))); } } @@ -439,9 +474,19 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco base.setLastError(msg.what()); return false; } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + return false; + } catch (...) { - base.setLastError("Unknown J2C error"); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); return false; } @@ -537,9 +582,21 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco cleanupCodeStream(); return true; // done } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + base.decodeFailed(); + cleanupCodeStream(); + return true; // done + } catch (...) { - base.setLastError( "Unknown J2C error" ); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); base.decodeFailed(); cleanupCodeStream(); return true; // done @@ -728,9 +785,19 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co base.setLastError(msg.what()); return false; } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + return false; + } catch( ... ) { - base.setLastError( "Unknown J2C error" ); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); return false; } @@ -752,9 +819,19 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base) base.setLastError(msg.what()); return false; } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + return false; + } catch (...) { - base.setLastError( "Unknown J2C error" ); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); return false; } } diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 1ca5f58ae2..004db546b7 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -43,6 +43,8 @@ #include "llcoros.h" #include "lleventcoro.h" #include "llcorehttputil.h" +#include "llexception.h" +#include "stringize.h" #include <map> #include <set> @@ -231,13 +233,12 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults); } - catch (std::exception e) - { - LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL; - } catch (...) { - LL_WARNS() << "Caught unknown exception." << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + << "('" << url << "', " << agentIds.size() + << " Agent Ids)")); + throw; } } diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index d4c0788b7d..74cdff2b00 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -27,6 +27,8 @@ #include "linden_common.h" #include "llcoproceduremanager.h" +#include "llexception.h" +#include "stringize.h" #include <boost/assign.hpp> //========================================================================= @@ -388,14 +390,14 @@ void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdap { coproc->mProc(httpAdapter, coproc->mId); } - catch (std::exception &e) - { - LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() << - " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL; - } catch (...) { - LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName + << "', id=" << coproc->mId.asString() + << ") in pool '" << mPoolName << "'")); + // must NOT omit this or we deplete the pool + mActiveCoprocs.erase(itActive); + throw; } LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp index f235965879..04b34a296c 100644 --- a/indra/llmessage/llhttpnode.cpp +++ b/indra/llmessage/llhttpnode.cpp @@ -31,6 +31,7 @@ #include "llstl.h" #include "llhttpconstants.h" +#include "llexception.h" const std::string CONTEXT_HEADERS("headers"); const std::string CONTEXT_PATH("path"); @@ -92,27 +93,28 @@ LLHTTPNode::~LLHTTPNode() namespace { - class NotImplemented + struct NotImplemented: public LLException { + NotImplemented(): LLException("LLHTTPNode::NotImplemented") {} }; } // virtual LLSD LLHTTPNode::simpleGet() const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } // virtual LLSD LLHTTPNode::simplePut(const LLSD& input) const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } // virtual LLSD LLHTTPNode::simplePost(const LLSD& input) const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } @@ -172,7 +174,7 @@ void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) cons // virtual LLSD LLHTTPNode::simpleDel(const LLSD&) const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } // virtual diff --git a/indra/llmessage/tests/commtest.h b/indra/llmessage/tests/commtest.h index 0d149b5258..7c8f27bbd2 100644 --- a/indra/llmessage/tests/commtest.h +++ b/indra/llmessage/tests/commtest.h @@ -33,15 +33,15 @@ #include "llevents.h" #include "llsd.h" #include "llhost.h" +#include "llexception.h" #include "stringize.h" #include <map> #include <string> -#include <stdexcept> #include <boost/lexical_cast.hpp> -struct CommtestError: public std::runtime_error +struct CommtestError: public LLException { - CommtestError(const std::string& what): std::runtime_error(what) {} + CommtestError(const std::string& what): LLException(what) {} }; static bool query_verbose() @@ -68,7 +68,7 @@ static int query_port(const std::string& var) const char* cport = getenv(var.c_str()); if (! cport) { - throw CommtestError(STRINGIZE("missing environment variable" << var)); + LLTHROW(CommtestError(STRINGIZE("missing environment variable" << var))); } // This will throw, too, if the value of PORT isn't numeric. int port(boost::lexical_cast<int>(cport)); diff --git a/indra/llmessage/tests/networkio.h b/indra/llmessage/tests/networkio.h index 2aff90ca1e..5eb739393f 100644 --- a/indra/llmessage/tests/networkio.h +++ b/indra/llmessage/tests/networkio.h @@ -34,6 +34,7 @@ #include "llares.h" #include "llpumpio.h" #include "llhttpclient.h" +#include "llexception.h" /***************************************************************************** * NetworkIO @@ -51,7 +52,7 @@ public: ll_init_apr(); if (! gAPRPoolp) { - throw std::runtime_error("Can't initialize APR"); + LLTHROW(LLException("Can't initialize APR")); } // Create IO Pump to use for HTTP Requests. @@ -59,7 +60,7 @@ public: LLHTTPClient::setPump(*mServicePump); if (ll_init_ares() == NULL || !gAres->isInitialized()) { - throw std::runtime_error("Can't start DNS resolver"); + LLTHROW(LLException("Can't start DNS resolver")); } // You can interrupt pump() without waiting the full timeout duration diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index b30bc1aed6..58c1186a3e 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -349,8 +349,8 @@ void LLGLSLShader::unloadInternal() for (GLsizei i = 0; i < count; i++) { glDetachObjectARB(mProgramObject, obj[i]); - glDeleteObjectARB(obj[i]); - } + glDeleteObjectARB(obj[i]); + } glDeleteObjectARB(mProgramObject); diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index b297223c2e..55f0791174 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -520,7 +520,7 @@ void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns, const std::string& if (!filename.empty()) { LL_CONT << "From " << filename << ":\n"; - } + } LL_CONT << log << LL_ENDL; } } diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 62c3f401bf..fd7406b653 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -812,7 +812,7 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) LLView* LLView::childFromPoint(S32 x, S32 y, bool recur) { if (!getVisible()) - return false; + return NULL; BOOST_FOREACH(LLView* viewp, mChildList) { diff --git a/indra/llwindow/llappdelegate-objc.h b/indra/llwindow/llappdelegate-objc.h index faa5d3abb7..6daf1ac55b 100644 --- a/indra/llwindow/llappdelegate-objc.h +++ b/indra/llwindow/llappdelegate-objc.h @@ -41,7 +41,7 @@ @property (retain) NSString *currentInputLanguage; -- (void) mainLoop; +- (void) oneFrame; - (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent; - (void) languageUpdated; - (bool) romanScript; diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index c22f3382fb..b06cd2c184 100644 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -69,7 +69,7 @@ typedef const NativeKeyEventData * NSKeyEventRef; // These are defined in llappviewermacosx.cpp. bool initViewer(); void handleQuit(); -bool runMainLoop(); +bool pumpMainLoop(); void initMainLoop(); void cleanupViewer(); void handleUrl(const char* url); diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp index 3149fad6e8..ec3616e26a 100644 --- a/indra/mac_crash_logger/llcrashloggermac.cpp +++ b/indra/mac_crash_logger/llcrashloggermac.cpp @@ -64,7 +64,7 @@ void LLCrashLoggerMac::gatherPlatformSpecificFiles() { } -bool LLCrashLoggerMac::mainLoop() +bool LLCrashLoggerMac::frame() { if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND) diff --git a/indra/mac_crash_logger/llcrashloggermac.h b/indra/mac_crash_logger/llcrashloggermac.h index 6d8f63ecac..05ef8c9f53 100644 --- a/indra/mac_crash_logger/llcrashloggermac.h +++ b/indra/mac_crash_logger/llcrashloggermac.h @@ -37,7 +37,7 @@ public: LLCrashLoggerMac(void); ~LLCrashLoggerMac(void); virtual bool init(); - virtual bool mainLoop(); + virtual bool frame(); virtual bool cleanup(); virtual void gatherPlatformSpecificFiles(); }; diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp index 95d4e65207..54e41a1954 100644 --- a/indra/mac_crash_logger/mac_crash_logger.cpp +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -49,7 +49,7 @@ int main(int argc, char **argv) { // return NSApplicationMain(argc, (const char **)argv); } - app.mainLoop(); + app.frame(); app.cleanup(); LL_INFOS() << "Crash reporter finished normally." << LL_ENDL; diff --git a/indra/media_plugins/CMakeLists.txt b/indra/media_plugins/CMakeLists.txt index 24eb3947b4..9055e0111a 100644 --- a/indra/media_plugins/CMakeLists.txt +++ b/indra/media_plugins/CMakeLists.txt @@ -4,15 +4,18 @@ add_subdirectory(base) if (LINUX) add_subdirectory(gstreamer010) + add_subdirectory(libvlc) endif (LINUX) -if (WINDOWS OR DARWIN) +if (DARWIN) add_subdirectory(quicktime) add_subdirectory(cef) -endif (WINDOWS OR DARWIN) +endif (DARWIN) if (WINDOWS) + add_subdirectory(cef) add_subdirectory(winmmshim) + add_subdirectory(libvlc) endif (WINDOWS) ### add_subdirectory(example) diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index 8d9d1dd975..28a8a5886a 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -100,6 +100,12 @@ private: LLCEFLib* mLLCEFLib; VolumeCatcher mVolumeCatcher; + + U8 *mPopupBuffer; + U32 mPopupW; + U32 mPopupH; + U32 mPopupX; + U32 mPopupY; }; //////////////////////////////////////////////////////////////////////////////// @@ -127,12 +133,19 @@ MediaPluginBase(host_send_func, host_user_data) mCookiePath = ""; mPickedFile = ""; mLLCEFLib = new LLCEFLib(); + + mPopupBuffer = NULL; + mPopupW = 0; + mPopupH = 0; + mPopupX = 0; + mPopupY = 0; } //////////////////////////////////////////////////////////////////////////////// // MediaPluginCEF::~MediaPluginCEF() { + delete[] mPopupBuffer; } //////////////////////////////////////////////////////////////////////////////// @@ -155,20 +168,28 @@ void MediaPluginCEF::postDebugMessage(const std::string& msg) // void MediaPluginCEF::onPageChangedCallback(unsigned char* pixels, int x, int y, int width, int height, bool is_popup) { - if (mPixels && pixels) + if( is_popup ) + { + delete mPopupBuffer; + mPopupBuffer = NULL; + mPopupH = 0; + mPopupW = 0; + mPopupX = 0; + mPopupY = 0; + } + + if( mPixels && pixels ) { if (is_popup) { - for (int line = 0; line < height; ++line) + if( width > 0 && height> 0 ) { - int inverted_y = mHeight - y - height; - int src = line * width * mDepth; - int dst = (inverted_y + line) * mWidth * mDepth + x * mDepth; - - if (dst + width * mDepth < mWidth * mHeight * mDepth) - { - memcpy(mPixels + dst, pixels + src, width * mDepth); - } + mPopupBuffer = new U8[ width * height * mDepth ]; + memcpy( mPopupBuffer, pixels, width * height * mDepth ); + mPopupH = height; + mPopupW = width; + mPopupX = x; + mPopupY = mHeight - y - height; } } else @@ -177,6 +198,23 @@ void MediaPluginCEF::onPageChangedCallback(unsigned char* pixels, int x, int y, { memcpy(mPixels, pixels, mWidth * mHeight * mDepth); } + if( mPopupBuffer && mPopupH && mPopupW ) + { + U32 bufferSize = mWidth * mHeight * mDepth; + U32 popupStride = mPopupW * mDepth; + U32 bufferStride = mWidth * mDepth; + int dstY = mPopupY; + + int src = 0; + int dst = dstY * mWidth * mDepth + mPopupX * mDepth; + + for( int line = 0; dst + popupStride < bufferSize && line < mPopupH; ++line ) + { + memcpy( mPixels + dst, mPopupBuffer + src, popupStride ); + src += popupStride; + dst += bufferStride; + } + } } setDirty(0, 0, mWidth, mHeight); @@ -559,6 +597,8 @@ void MediaPluginCEF::receiveMessage(const char* message_string) S32 x = message_in.getValueS32("x"); S32 y = message_in.getValueS32("y"); + y = mHeight - y; + // only even send left mouse button events to LLCEFLib // (partially prompted by crash in OS X CEF when sending right button events) // we catch the right click in viewer and display our own context menu anyway diff --git a/indra/media_plugins/libvlc/CMakeLists.txt b/indra/media_plugins/libvlc/CMakeLists.txt new file mode 100644 index 0000000000..535d29125b --- /dev/null +++ b/indra/media_plugins/libvlc/CMakeLists.txt @@ -0,0 +1,86 @@ +# -*- cmake -*- + +project(media_plugin_libvlc) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(MediaPluginBase) +include(OpenGL) + +include(LibVLCPlugin) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} + ${VLC_INCLUDE_DIR} +) +include_directories(SYSTEM + ${LLCOMMON_SYSTEM_INCLUDE_DIRS} + ) + + +### media_plugin_libvlc + +if(NOT WORD_SIZE EQUAL 32) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif(WINDOWS) +endif(NOT WORD_SIZE EQUAL 32) + +set(media_plugin_libvlc_SOURCE_FILES + media_plugin_libvlc.cpp + ) + +add_library(media_plugin_libvlc + SHARED + ${media_plugin_libvlc_SOURCE_FILES} +) + +target_link_libraries(media_plugin_libvlc + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${VLC_PLUGIN_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +add_dependencies(media_plugin_libvlc + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + +if (WINDOWS) + set_target_properties( + media_plugin_libvlc + PROPERTIES + LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /LTCG /NODEFAULTLIB:LIBCMT" + ) +endif (WINDOWS) + +if (DARWIN) + # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name + set_target_properties( + media_plugin_libvlc + PROPERTIES + PREFIX "" + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + ) + +endif (DARWIN) diff --git a/indra/media_plugins/libvlc/media_plugin_libvlc.cpp b/indra/media_plugins/libvlc/media_plugin_libvlc.cpp new file mode 100644 index 0000000000..0bd323eb58 --- /dev/null +++ b/indra/media_plugins/libvlc/media_plugin_libvlc.cpp @@ -0,0 +1,618 @@ +/** +* @file media_plugin_libvlc.cpp +* @brief LibVLC plugin for LLMedia API plugin system +* +* @cond +* $LicenseInfo:firstyear=2008&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +* @endcond +*/ + +#include "linden_common.h" + +#include "llgl.h" +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#include "vlc/vlc.h" +#include "vlc/libvlc_version.h" + +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginLibVLC : + public MediaPluginBase +{ +public: + MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginLibVLC(); + + /*virtual*/ void receiveMessage(const char* message_string); + +private: + bool init(); + + void initVLC(); + void playMedia(); + void resetVLC(); + void setVolume(const F64 volume); + void updateTitle(const char* title); + + static void* lock(void* data, void** p_pixels); + static void unlock(void* data, void* id, void* const* raw_pixels); + static void display(void* data, void* id); + + /*virtual*/ void setDirty(int left, int top, int right, int bottom) /* override, but that is not supported in gcc 4.6 */; + + static void eventCallbacks(const libvlc_event_t* event, void* ptr); + + libvlc_instance_t* mLibVLC; + libvlc_media_t* mLibVLCMedia; + libvlc_media_player_t* mLibVLCMediaPlayer; + + struct mLibVLCContext + { + unsigned char* texture_pixels; + libvlc_media_player_t* mp; + MediaPluginLibVLC* parent; + }; + struct mLibVLCContext mLibVLCCallbackContext; + + std::string mURL; + F64 mCurVolume; + + bool mIsLooping; + + float mCurTime; + float mDuration; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginLibVLC::MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : +MediaPluginBase(host_send_func, host_user_data) +{ + mTextureWidth = 0; + mTextureHeight = 0; + mWidth = 0; + mHeight = 0; + mDepth = 4; + mPixels = 0; + + mLibVLC = 0; + mLibVLCMedia = 0; + mLibVLCMediaPlayer = 0; + + mCurVolume = 0.0; + + mIsLooping = false; + + mCurTime = 0.0; + mDuration = 0.0; + + mURL = std::string(); + + setStatus(STATUS_NONE); +} + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginLibVLC::~MediaPluginLibVLC() +{ +} + +///////////////////////////////////////////////////////////////////////////////// +// +void* MediaPluginLibVLC::lock(void* data, void** p_pixels) +{ + struct mLibVLCContext* context = (mLibVLCContext*)data; + + *p_pixels = context->texture_pixels; + + return NULL; +} + +///////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::unlock(void* data, void* id, void* const* raw_pixels) +{ + // nothing to do here for the moment + // we *could* modify pixels here to, for example, Y flip, but this is done with + // a VLC video filter transform. +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::display(void* data, void* id) +{ + struct mLibVLCContext* context = (mLibVLCContext*)data; + + context->parent->setDirty(0, 0, context->parent->mWidth, context->parent->mHeight); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::initVLC() +{ + char const* vlc_argv[] = + { + "--no-xlib", + "--video-filter=transform{type=vflip}", // MAINT-6578 Y flip textures in plugin vs client + }; + + int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv); + mLibVLC = libvlc_new(vlc_argc, vlc_argv); + + if (!mLibVLC) + { + // for the moment, if this fails, the plugin will fail and + // the media sub-system will tell the viewer something went wrong. + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::resetVLC() +{ + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + libvlc_release(mLibVLC); +} + +//////////////////////////////////////////////////////////////////////////////// +// *virtual* +void MediaPluginLibVLC::setDirty(int left, int top, int right, int bottom) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); + + message.setValueS32("left", left); + message.setValueS32("top", top); + message.setValueS32("right", right); + message.setValueS32("bottom", bottom); + + message.setValueReal("current_time", mCurTime); + message.setValueReal("duration", mDuration); + message.setValueReal("current_rate", 1.0f); + + sendMessage(message); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) +{ + MediaPluginLibVLC* parent = (MediaPluginLibVLC*)ptr; + if (parent == 0) + { + return; + } + + switch (event->type) + { + case libvlc_MediaPlayerOpening: + parent->setStatus(STATUS_LOADING); + break; + + case libvlc_MediaPlayerPlaying: + parent->mDuration = (float)(libvlc_media_get_duration(parent->mLibVLCMedia)) / 1000.0f; + parent->setStatus(STATUS_PLAYING); + break; + + case libvlc_MediaPlayerPaused: + parent->setStatus(STATUS_PAUSED); + break; + + case libvlc_MediaPlayerStopped: + parent->setStatus(STATUS_DONE); + break; + + case libvlc_MediaPlayerEndReached: + parent->setStatus(STATUS_DONE); + break; + + case libvlc_MediaPlayerEncounteredError: + parent->setStatus(STATUS_ERROR); + break; + + case libvlc_MediaPlayerTimeChanged: + parent->mCurTime = (float)libvlc_media_player_get_time(parent->mLibVLCMediaPlayer) / 1000.0f; + break; + + case libvlc_MediaPlayerPositionChanged: + break; + + case libvlc_MediaPlayerLengthChanged: + parent->mDuration = (float)libvlc_media_get_duration(parent->mLibVLCMedia) / 1000.0f; + break; + + case libvlc_MediaPlayerTitleChanged: + { + char* title = libvlc_media_get_meta(parent->mLibVLCMedia, libvlc_meta_Title); + if (title) + { + parent->updateTitle(title); + } + } + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::playMedia() +{ + if (mURL.length() == 0) + { + return; + } + + if (mLibVLCMediaPlayer) + { + // stop listening to events while we reset things + libvlc_event_manager_t* em = libvlc_media_player_event_manager(mLibVLCMediaPlayer); + if (em) + { + libvlc_event_detach(em, libvlc_MediaPlayerOpening, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerPlaying, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerPaused, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerStopped, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerEndReached, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerEncounteredError, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerTimeChanged, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerPositionChanged, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerLengthChanged, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerTitleChanged, eventCallbacks, NULL); + }; + + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + + mLibVLCMediaPlayer = 0; + } + + if (mLibVLCMedia) + { + libvlc_media_release(mLibVLCMedia); + + mLibVLCMedia = 0; + } + + mLibVLCMedia = libvlc_media_new_location(mLibVLC, mURL.c_str()); + if (!mLibVLCMedia) + { + mLibVLCMediaPlayer = 0; + setStatus(STATUS_ERROR); + return; + } + + mLibVLCMediaPlayer = libvlc_media_player_new_from_media(mLibVLCMedia); + if (!mLibVLCMediaPlayer) + { + setStatus(STATUS_ERROR); + return; + } + + // listen to events + libvlc_event_manager_t* em = libvlc_media_player_event_manager(mLibVLCMediaPlayer); + if (em) + { + libvlc_event_attach(em, libvlc_MediaPlayerOpening, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPlaying, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPaused, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerStopped, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerEndReached, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerEncounteredError, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerTimeChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerLengthChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerTitleChanged, eventCallbacks, this); + } + + mLibVLCCallbackContext.parent = this; + mLibVLCCallbackContext.texture_pixels = mPixels; + mLibVLCCallbackContext.mp = mLibVLCMediaPlayer; + + // Send a "navigate begin" event. + // This is really a browser message but the QuickTime plugin did it and + // the media system relies on this message to update internal state so we must send it too + // Note: see "navigate_complete" message below too + // https://jira.secondlife.com/browse/MAINT-6528 + LLPluginMessage message_begin(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); + message_begin.setValue("uri", mURL); + message_begin.setValueBoolean("history_back_available", false); + message_begin.setValueBoolean("history_forward_available", false); + sendMessage(message_begin); + + // volume level gets set before VLC is initialized (thanks media system) so we have to record + // it in mCurVolume and set it again here so that volume levels are correctly initialized + setVolume(mCurVolume); + + setStatus(STATUS_LOADED); + + libvlc_video_set_callbacks(mLibVLCMediaPlayer, lock, unlock, display, &mLibVLCCallbackContext); + libvlc_video_set_format(mLibVLCMediaPlayer, "RV32", mWidth, mHeight, mWidth * mDepth); + + // note this relies on the "set_loop" message arriving before the "start" (play) one + // but that appears to always be the case + if (mIsLooping) + { + libvlc_media_add_option(mLibVLCMedia, "input-repeat=-1"); + } + + libvlc_media_player_play(mLibVLCMediaPlayer); + + // send a "location_changed" message - this informs the media system + // that a new URL is the 'current' one and is used extensively. + // Again, this is really a browser message but we will use it here. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed"); + message.setValue("uri", mURL); + sendMessage(message); + + // Send a "navigate complete" event. + // This is really a browser message but the QuickTime plugin did it and + // the media system relies on this message to update internal state so we must send it too + // Note: see "navigate_begin" message above too + // https://jira.secondlife.com/browse/MAINT-6528 + LLPluginMessage message_complete(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); + message_complete.setValue("uri", mURL); + message_complete.setValueS32("result_code", 200); + message_complete.setValue("result_string", "OK"); + sendMessage(message_complete); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::updateTitle(const char* title) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", title); + sendMessage(message); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::setVolume(const F64 volume) +{ + mCurVolume = volume; + + if (mLibVLCMediaPlayer) + { + int result = libvlc_audio_set_volume(mLibVLCMediaPlayer, (int)(volume * 100)); + if (result != 0) + { + // volume wasn't set but not much to be done here + } + } + else + { + // volume change was requested but VLC wasn't ready. + // that's okay thought because we saved the value in mCurVolume and + // the next volume change after the VLC system is initilzied will set it + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::receiveMessage(const char* message_string) +{ + LLPluginMessage message_in; + + if (message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if (message_name == "init") + { + initVLC(); + + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; + message.setValueLLSD("versions", versions); + + std::ostringstream s; + s << "LibVLC plugin "; + s << LIBVLC_VERSION_MAJOR; + s << "."; + s << LIBVLC_VERSION_MINOR; + s << "."; + s << LIBVLC_VERSION_REVISION; + + message.setValue("plugin_version", s.str()); + sendMessage(message); + } + else if (message_name == "idle") + { + } + else if (message_name == "cleanup") + { + resetVLC(); + } + else if (message_name == "shm_added") + { + SharedSegmentInfo info; + info.mAddress = message_in.getValuePointer("address"); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if (message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if (iter != mSharedSegments.end()) + { + if (mPixels == iter->second.mAddress) + { + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + mLibVLCMediaPlayer = 0; + + mPixels = NULL; + mTextureSegmentName.clear(); + } + mSharedSegments.erase(iter); + } + else + { + //std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { + //std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if (message_name == "init") + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + message.setValueS32("default_width", 1024); + message.setValueS32("default_height", 1024); + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGB); + message.setValueU32("format", GL_BGRA_EXT); + message.setValueU32("type", GL_UNSIGNED_BYTE); + message.setValueBoolean("coords_opengl", true); + sendMessage(message); + } + else if (message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + if (!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if (iter != mSharedSegments.end()) + { + mPixels = (unsigned char*)iter->second.mAddress; + mWidth = width; + mHeight = height; + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + playMedia(); + }; + }; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + } + else if (message_name == "load_uri") + { + mURL = message_in.getValue("uri"); + playMedia(); + } + } + else + if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + if (message_name == "stop") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_stop(mLibVLCMediaPlayer); + } + } + else if (message_name == "start") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_play(mLibVLCMediaPlayer); + } + } + else if (message_name == "pause") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_pause(mLibVLCMediaPlayer); + } + } + else if (message_name == "seek") + { + if (mDuration > 0) + { + F64 normalized_offset = message_in.getValueReal("time") / mDuration; + libvlc_media_player_set_position(mLibVLCMediaPlayer, normalized_offset); + } + } + else if (message_name == "set_loop") + { + mIsLooping = true; + } + else if (message_name == "set_volume") + { + // volume comes in 0 -> 1.0 + F64 volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool MediaPluginLibVLC::init() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", "LibVLC Plugin"); + sendMessage(message); + + return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, + void* host_user_data, + LLPluginInstance::sendMessageFunction *plugin_send_func, + void **plugin_user_data) +{ + MediaPluginLibVLC* self = new MediaPluginLibVLC(host_send_func, host_user_data); + *plugin_send_func = MediaPluginLibVLC::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/media_plugins/quicktime/CMakeLists.txt index c5615145be..58391007ff 100644..100755 --- a/indra/media_plugins/quicktime/CMakeLists.txt +++ b/indra/media_plugins/quicktime/CMakeLists.txt @@ -14,7 +14,6 @@ include(PluginAPI) include(MediaPluginBase) include(OpenGL) include(QuickTimePlugin) -include(Boost) include_directories( ${LLPLUGIN_INCLUDE_DIRS} @@ -54,12 +53,17 @@ target_link_libraries(media_plugin_quicktime ${PLUGIN_API_WINDOWS_LIBRARIES} ) +add_dependencies(media_plugin_quicktime + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + if (WINDOWS) set_target_properties( media_plugin_quicktime PROPERTIES - LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /NODEFAULTLIB:LIBCMT" - LINK_FLAGS_DEBUG "/MANIFEST:NO /SAFESEH:NO /NODEFAULTLIB:LIBCMTD" + LINK_FLAGS "/MANIFEST:NO" ) endif (WINDOWS) diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index 7ef5a0fe44..b43598e4a5 100644..100755 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -837,9 +837,7 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) else if(message_name == "cleanup") { // TODO: clean up here - LLPluginMessage message("base", "goodbye"); - sendMessage(message); - } + } else if(message_name == "shm_added") { SharedSegmentInfo info; @@ -921,6 +919,9 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) #endif message.setValueS32("depth", mDepth); message.setValueU32("internalformat", GL_RGB); + + // note this apparently only has an effect when media is opened in 2D browser. + // see https://jira.secondlife.com/browse/BUG-18252 - media flipped in 2D so flipping it back. message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. message.setValueBoolean("allow_downsample", true); sendMessage(message); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 195363fb75..16edd39ecc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1755,6 +1755,7 @@ if (WINDOWS) SLPlugin media_plugin_quicktime media_plugin_cef + media_plugin_libvlc winmm_shim windows-crash-logger ) @@ -1970,6 +1971,7 @@ if (LINUX) linux-crash-logger SLPlugin media_plugin_gstreamer010 + media_plugin_libvlc llcommon ) @@ -2077,7 +2079,7 @@ if (DARWIN) ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) - add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_cef mac-crash-logger) + add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_libvlc media_plugin_cef mac-crash-logger) add_dependencies(${VIEWER_BINARY_NAME} mac-crash-logger) if (ENABLE_SIGNING) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 7919852fe1..627a3f43a6 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -4.0.9 +4.1.1 diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp index 92a5413adb..1dddf52961 100644 --- a/indra/newview/llaccountingcostmanager.cpp +++ b/indra/newview/llaccountingcostmanager.cpp @@ -31,6 +31,8 @@ #include "llcoros.h" #include "lleventcoro.h" #include "llcorehttputil.h" +#include "llexception.h" +#include "stringize.h" #include <algorithm> #include <iterator> @@ -154,13 +156,11 @@ void LLAccountingCostManager::accountingCostCoro(std::string url, } while (false); } - catch (std::exception e) - { - LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL; - } catch (...) { - LL_WARNS() << "Caught unknown exception." << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + << "('" << url << "')")); + throw; } mPendingObjectQuota.clear(); diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 49291ea564..c1f898284a 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -30,6 +30,8 @@ #include "llappviewer.h" #include "llviewercontrol.h" +#include "llexception.h" +#include "stringize.h" #include <openssl/x509_vfy.h> #include <openssl/ssl.h> @@ -534,7 +536,7 @@ LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, // somewhat clumsy, as we may run into errors that do not map directly to curl // error codes. Should be refactored with login refactoring, perhaps. result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CACERT); - result.setMessage(cert_exception.getMessage()); + result.setMessage(cert_exception.what()); LLPointer<LLCertificate> cert = cert_exception.getCert(); cert->ref(); // adding an extra ref here result.setErrorData(cert.get()); @@ -544,13 +546,14 @@ LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, catch (LLCertException &cert_exception) { result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_PEER_CERTIFICATE); - result.setMessage(cert_exception.getMessage()); + result.setMessage(cert_exception.what()); LLPointer<LLCertificate> cert = cert_exception.getCert(); cert->ref(); // adding an extra ref here result.setErrorData(cert.get()); } catch (...) { + LOG_UNHANDLED_EXCEPTION(STRINGIZE("('" << url << "')")); // any other odd error, we just handle as a connect error. result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CONNECT_ERROR); } diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index 549df80fa1..be8877328d 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -48,16 +48,19 @@ - (void) applicationDidFinishLaunching:(NSNotification *)notification { frameTimer = nil; - + [self languageUpdated]; - + if (initViewer()) { - frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(mainLoop) userInfo:nil repeats:YES]; + // Set up recurring calls to oneFrame (repeating timer with timeout 0) + // until applicationShouldTerminate. + frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self + selector:@selector(oneFrame) userInfo:nil repeats:YES]; } else { handleQuit(); } - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil]; // [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; @@ -96,22 +99,29 @@ - (NSApplicationDelegateReply) applicationShouldTerminate:(NSApplication *)sender { - if (!runMainLoop()) + // run one frame to assess state + if (!pumpMainLoop()) { + // pumpMainLoop() returns true when done, false if it wants to be + // called again. Since it returned false, do not yet cancel + // frameTimer. handleQuit(); return NSTerminateCancel; } else { + // pumpMainLoop() returned true: it's done. Okay, done with frameTimer. [frameTimer release]; cleanupViewer(); return NSTerminateNow; } } -- (void) mainLoop +- (void) oneFrame { - bool appExiting = runMainLoop(); + bool appExiting = pumpMainLoop(); if (appExiting) { + // Once pumpMainLoop() reports that we're done, cancel frameTimer: + // stop the repetitive calls. [frameTimer release]; [[NSApplication sharedApplication] terminate:self]; } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 721a7cc00b..76d0d46997 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -122,15 +122,20 @@ #include "llleap.h" #include "stringize.h" #include "llcoros.h" +#include "llexception.h" #if !LL_LINUX #include "cef/llceflib.h" -#endif +#if LL_WINDOWS +#include "vlc/libvlc_version.h" +#endif // LL_WINDOWS +#endif // LL_LINUX // Third party library includes #include <boost/bind.hpp> #include <boost/foreach.hpp> #include <boost/algorithm/string.hpp> #include <boost/regex.hpp> +#include <boost/throw_exception.hpp> #if LL_WINDOWS # include <share.h> // For _SH_DENYWR in processMarkerFiles @@ -232,7 +237,6 @@ #include "llcoproceduremanager.h" #include "llviewereventrecorder.h" - // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -770,9 +774,6 @@ bool LLAppViewer::init() // // Start of the application // -#ifdef LL_DARWIN - mMainLoopInitialized = false; -#endif // initialize LLWearableType translation bridge. // Memory will be cleaned up in ::cleanupClass() @@ -926,7 +927,7 @@ bool LLAppViewer::init() // Provide the text fields with callbacks for opening Urls LLUrlAction::setOpenURLCallback(boost::bind(&LLWeb::loadURL, _1, LLStringUtil::null, LLStringUtil::null)); - LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null)); + LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null, false)); LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null)); LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); @@ -1221,6 +1222,23 @@ bool LLAppViewer::init() boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1), boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS)); + /*----------------------------------------------------------------------*/ + // nat 2016-06-29 moved the following here from the former mainLoop(). + mMainloopTimeout = new LLWatchdogTimeout(); + + // Create IO Pump to use for HTTP Requests. + gServicePump = new LLPumpIO(gAPRPoolp); + + // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. + + LLVoiceChannel::initClass(); + LLVoiceClient::getInstance()->init(gServicePump); + LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true); + + joystick = LLViewerJoystick::getInstance(); + joystick->setNeedsReset(true); + /*----------------------------------------------------------------------*/ + return true; } @@ -1295,300 +1313,266 @@ static LLTrace::BlockTimerStatHandle FTM_AGENT_UPDATE("Update"); // externally visible timers LLTrace::BlockTimerStatHandle FTM_FRAME("Frame"); -bool LLAppViewer::mainLoop() +bool LLAppViewer::frame() { -#ifdef LL_DARWIN - if (!mMainLoopInitialized) -#endif - { - LL_INFOS() << "Entering main_loop" << LL_ENDL; - mMainloopTimeout = new LLWatchdogTimeout(); - - //------------------------------------------- - // Run main loop until time to quit - //------------------------------------------- - - // Create IO Pump to use for HTTP Requests. - gServicePump = new LLPumpIO(gAPRPoolp); - - // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. - - LLVoiceChannel::initClass(); - LLVoiceClient::getInstance()->init(gServicePump); - LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true); - - joystick = LLViewerJoystick::getInstance(); - joystick->setNeedsReset(true); - -#ifdef LL_DARWIN - // Ensure that this section of code never gets called again on OS X. - mMainLoopInitialized = true; -#endif - } - // As we do not (yet) send data on the mainloop LLEventPump that varies - // with each frame, no need to instantiate a new LLSD event object each - // time. Obviously, if that changes, just instantiate the LLSD at the - // point of posting. - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - - LLSD newFrame; - + LLSD newFrame; + LLTimer frameTimer,idleTimer; LLTimer debugTime; - + //LLPrivateMemoryPoolTester::getInstance()->run(false) ; //LLPrivateMemoryPoolTester::getInstance()->run(true) ; //LLPrivateMemoryPoolTester::destroy() ; - // Handle messages -#ifdef LL_DARWIN - if (!LLApp::isExiting()) -#else - while (!LLApp::isExiting()) -#endif - { - LL_RECORD_BLOCK_TIME(FTM_FRAME); - LLTrace::BlockTimer::processTimes(); - LLTrace::get_frame_recording().nextPeriod(); - LLTrace::BlockTimer::logStats(); + LL_RECORD_BLOCK_TIME(FTM_FRAME); + LLTrace::BlockTimer::processTimes(); + LLTrace::get_frame_recording().nextPeriod(); + LLTrace::BlockTimer::logStats(); - LLTrace::get_thread_recorder()->pullFromChildren(); + LLTrace::get_thread_recorder()->pullFromChildren(); - //clear call stack records - LL_CLEAR_CALLSTACKS(); + //clear call stack records + LL_CLEAR_CALLSTACKS(); - //check memory availability information - checkMemory() ; - - try + //check memory availability information + checkMemory() ; + + try + { + pingMainloopTimeout("Main:MiscNativeWindowEvents"); + + if (gViewerWindow) { - pingMainloopTimeout("Main:MiscNativeWindowEvents"); + LL_RECORD_BLOCK_TIME(FTM_MESSAGES); + gViewerWindow->getWindow()->processMiscNativeEvents(); + } + + pingMainloopTimeout("Main:GatherInput"); - if (gViewerWindow) + if (gViewerWindow) + { + LL_RECORD_BLOCK_TIME(FTM_MESSAGES); + if (!restoreErrorTrap()) { - LL_RECORD_BLOCK_TIME(FTM_MESSAGES); - gViewerWindow->getWindow()->processMiscNativeEvents(); + LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; } - - pingMainloopTimeout("Main:GatherInput"); - - if (gViewerWindow) - { - LL_RECORD_BLOCK_TIME(FTM_MESSAGES); - if (!restoreErrorTrap()) - { - LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; - } - gViewerWindow->getWindow()->gatherInput(); - } + gViewerWindow->getWindow()->gatherInput(); + } #if 1 && !LL_RELEASE_FOR_DOWNLOAD - // once per second debug info - if (debugTime.getElapsedTimeF32() > 1.f) - { - debugTime.reset(); - } - + // once per second debug info + if (debugTime.getElapsedTimeF32() > 1.f) + { + debugTime.reset(); + } + #endif - //memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); - if(mem_leak_instance) - { - mem_leak_instance->idle() ; - } + //memory leaking simulation + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); + if(mem_leak_instance) + { + mem_leak_instance->idle() ; + } + + // canonical per-frame event + mainloop.post(newFrame); - // canonical per-frame event - mainloop.post(newFrame); + if (!LLApp::isExiting()) + { + pingMainloopTimeout("Main:JoystickKeyboard"); + + // Scan keyboard for movement keys. Command keys and typing + // are handled by windows callbacks. Don't do this until we're + // done initializing. JC + if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible()) + && gViewerWindow->getActive() + && !gViewerWindow->getWindow()->getMinimized() + && LLStartUp::getStartupState() == STATE_STARTED + && (gHeadlessClient || !gViewerWindow->getShowProgress()) + && !gFocusMgr.focusLocked()) + { + joystick->scanJoystick(); + gKeyboard->scanKeyboard(); + } - if (!LLApp::isExiting()) + // Update state based on messages, user input, object idle. { - pingMainloopTimeout("Main:JoystickKeyboard"); + pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! - // Scan keyboard for movement keys. Command keys and typing - // are handled by windows callbacks. Don't do this until we're - // done initializing. JC - if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible()) - && gViewerWindow->getActive() - && !gViewerWindow->getWindow()->getMinimized() - && LLStartUp::getStartupState() == STATE_STARTED - && (gHeadlessClient || !gViewerWindow->getShowProgress()) - && !gFocusMgr.focusLocked()) - { - joystick->scanJoystick(); - gKeyboard->scanKeyboard(); - } + LL_RECORD_BLOCK_TIME(FTM_IDLE); + idle(); - // Update state based on messages, user input, object idle. - { - pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! - - LL_RECORD_BLOCK_TIME(FTM_IDLE); - idle(); + resumeMainloopTimeout(); + } - resumeMainloopTimeout(); - } - - if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) - { - pauseMainloopTimeout(); - saveFinalSnapshot(); - disconnectViewer(); - resumeMainloopTimeout(); - } + if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) + { + pauseMainloopTimeout(); + saveFinalSnapshot(); + disconnectViewer(); + resumeMainloopTimeout(); + } - // Render scene. - // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18 - if (!LLApp::isExiting() && !gHeadlessClient) - { - pingMainloopTimeout("Main:Display"); - gGLActive = TRUE; - display(); - pingMainloopTimeout("Main:Snapshot"); - LLFloaterSnapshot::update(); // take snapshots + // Render scene. + // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18 + if (!LLApp::isExiting() && !gHeadlessClient) + { + pingMainloopTimeout("Main:Display"); + gGLActive = TRUE; + display(); + pingMainloopTimeout("Main:Snapshot"); + LLFloaterSnapshot::update(); // take snapshots LLFloaterOutfitSnapshot::update(); - gGLActive = FALSE; - } + gGLActive = FALSE; } + } - pingMainloopTimeout("Main:Sleep"); + pingMainloopTimeout("Main:Sleep"); + + pauseMainloopTimeout(); + + // Sleep and run background threads + { + LL_RECORD_BLOCK_TIME(FTM_SLEEP); - pauseMainloopTimeout(); + // yield some time to the os based on command line option + if(mYieldTime >= 0) + { + LL_RECORD_BLOCK_TIME(FTM_YIELD); + ms_sleep(mYieldTime); + } - // Sleep and run background threads + // yield cooperatively when not running as foreground window + if ( (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) + || !gFocusMgr.getAppHasFocus()) { - LL_RECORD_BLOCK_TIME(FTM_SLEEP); - - // yield some time to the os based on command line option - if(mYieldTime >= 0) + // Sleep if we're not rendering, or the window is minimized. + S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000); + // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads + // of equal priority on Windows + if (milliseconds_to_sleep > 0) { - LL_RECORD_BLOCK_TIME(FTM_YIELD); - ms_sleep(mYieldTime); + ms_sleep(milliseconds_to_sleep); + // also pause worker threads during this wait period + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); } + } + + if (mRandomizeFramerate) + { + ms_sleep(rand() % 200); + } + + if (mPeriodicSlowFrame + && (gFrameCount % 10 == 0)) + { + LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL; + ms_sleep(500); + } + + const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second + idleTimer.reset(); + S32 total_work_pending = 0; + S32 total_io_pending = 0; + while(1) + { + S32 work_pending = 0; + S32 io_pending = 0; + F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f); + + work_pending += updateTextureThreads(max_time); - // yield cooperatively when not running as foreground window - if ( (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) - || !gFocusMgr.getAppHasFocus()) { - // Sleep if we're not rendering, or the window is minimized. - S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000); - // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads - // of equal priority on Windows - if (milliseconds_to_sleep > 0) - { - ms_sleep(milliseconds_to_sleep); - // also pause worker threads during this wait period - LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); - } + LL_RECORD_BLOCK_TIME(FTM_VFS); + io_pending += LLVFSThread::updateClass(1); } - - if (mRandomizeFramerate) { - ms_sleep(rand() % 200); + LL_RECORD_BLOCK_TIME(FTM_LFS); + io_pending += LLLFSThread::updateClass(1); } - if (mPeriodicSlowFrame - && (gFrameCount % 10 == 0)) + if (io_pending > 1000) { - LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL; - ms_sleep(500); + ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up } - const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second - idleTimer.reset(); - S32 total_work_pending = 0; - S32 total_io_pending = 0; - while(1) - { - S32 work_pending = 0; - S32 io_pending = 0; - F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f); - - work_pending += updateTextureThreads(max_time); - - { - LL_RECORD_BLOCK_TIME(FTM_VFS); - io_pending += LLVFSThread::updateClass(1); - } - { - LL_RECORD_BLOCK_TIME(FTM_LFS); - io_pending += LLLFSThread::updateClass(1); - } - - if (io_pending > 1000) - { - ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up - } - - total_work_pending += work_pending ; - total_io_pending += io_pending ; - - if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time) - { - break; - } - } - gMeshRepo.update() ; + total_work_pending += work_pending ; + total_io_pending += io_pending ; - if(!total_work_pending) //pause texture fetching threads if nothing to process. + if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time) { - LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); - LLAppViewer::getTextureFetch()->pause(); + break; } - if(!total_io_pending) //pause file threads if nothing to process. - { - LLVFSThread::sLocal->pause(); - LLLFSThread::sLocal->pause(); - } + } + gMeshRepo.update() ; + + if(!total_work_pending) //pause texture fetching threads if nothing to process. + { + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); + LLAppViewer::getTextureFetch()->pause(); + } + if(!total_io_pending) //pause file threads if nothing to process. + { + LLVFSThread::sLocal->pause(); + LLLFSThread::sLocal->pause(); + } - //texture fetching debugger - if(LLTextureFetchDebugger::isEnabled()) + //texture fetching debugger + if(LLTextureFetchDebugger::isEnabled()) + { + LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance = + LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger"); + if(tex_fetch_debugger_instance) { - LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance = - LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger"); - if(tex_fetch_debugger_instance) - { - tex_fetch_debugger_instance->idle() ; - } + tex_fetch_debugger_instance->idle() ; } + } - if ((LLStartUp::getStartupState() >= STATE_CLEANUP) && - (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD)) - { - gFrameStalls++; - } - frameTimer.reset(); + if ((LLStartUp::getStartupState() >= STATE_CLEANUP) && + (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD)) + { + gFrameStalls++; + } + frameTimer.reset(); - resumeMainloopTimeout(); - - pingMainloopTimeout("Main:End"); - } + resumeMainloopTimeout(); + + pingMainloopTimeout("Main:End"); } - catch(std::bad_alloc) - { - LLMemory::logMemoryInfo(TRUE) ; + } + catch (const LLContinueError&) + { + LOG_UNHANDLED_EXCEPTION(""); + } + catch(std::bad_alloc) + { + LLMemory::logMemoryInfo(TRUE) ; - //stop memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); - if(mem_leak_instance) - { - mem_leak_instance->stop() ; - LL_WARNS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ; - } - else - { - //output possible call stacks to log file. - LLError::LLCallStacks::print() ; + //stop memory leaking simulation + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); + if(mem_leak_instance) + { + mem_leak_instance->stop() ; + LL_WARNS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ; + } + else + { + //output possible call stacks to log file. + LLError::LLCallStacks::print() ; - LL_ERRS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ; - } + LL_ERRS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ; } } + catch (...) + { + CRASH_ON_UNHANDLED_EXCEPTION(""); + } if (LLApp::isExiting()) { @@ -1602,7 +1586,7 @@ bool LLAppViewer::mainLoop() catch(std::bad_alloc) { LL_WARNS() << "Bad memory allocation when saveFinalSnapshot() is called!" << LL_ENDL ; - + //stop memory leaking simulation LLFloaterMemLeak* mem_leak_instance = LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); @@ -1611,16 +1595,20 @@ bool LLAppViewer::mainLoop() mem_leak_instance->stop() ; } } + catch (...) + { + CRASH_ON_UNHANDLED_EXCEPTION("saveFinalSnapshot()"); + } } - + delete gServicePump; - + destroyMainloopTimeout(); - + LL_INFOS() << "Exiting main_loop" << LL_ENDL; } - return LLApp::isExiting(); + return ! LLApp::isRunning(); } S32 LLAppViewer::updateTextureThreads(F32 max_time) @@ -3344,6 +3332,19 @@ LLSD LLAppViewer::getViewerInfo() const info["LLCEFLIB_VERSION"] = LLCEFLIB_VERSION; #else info["LLCEFLIB_VERSION"] = "Undefined"; + +#endif + +#if LL_WINDOWS + std::ostringstream ver_codec; + ver_codec << LIBVLC_VERSION_MAJOR; + ver_codec << "."; + ver_codec << LIBVLC_VERSION_MINOR; + ver_codec << "."; + ver_codec << LIBVLC_VERSION_REVISION; + info["LIBVLC_VERSION"] = ver_codec.str(); +#else + info["LIBVLC_VERSION"] = "Undefined"; #endif S32 packets_in = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN); @@ -5548,8 +5549,7 @@ void LLAppViewer::forceErrorInfiniteLoop() void LLAppViewer::forceErrorSoftwareException() { LL_WARNS() << "Forcing a deliberate exception" << LL_ENDL; - // *FIX: Any way to insure it won't be handled? - throw; + LLTHROW(LLException("User selected Force Software Exception")); } void LLAppViewer::forceErrorDriverCrash() diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index b5e674bd7b..f7c1bb58b4 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -79,7 +79,7 @@ public: // virtual bool init(); // Override to do application initialization virtual bool cleanup(); // Override to do application cleanup - virtual bool mainLoop(); // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. + virtual bool frame(); // Override for application body logic // Application control void flushVFSIO(); // waits for vfs transfers to complete @@ -283,7 +283,6 @@ private: std::string mSerialNumber; bool mPurgeCache; bool mPurgeOnExit; - bool mMainLoopInitialized; LLViewerJoystick* joystick; bool mSavedFinalSnapshot; diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index f5742b29cf..6f32aab851 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -95,10 +95,8 @@ int main( int argc, char **argv ) } // Run the application main loop - if(!LLApp::isQuitting()) - { - viewer_app_ptr->mainLoop(); - } + while (! viewer_app_ptr->frame()) + {} if (!LLApp::isError()) { diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index ca219fda59..4fe1e31668 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -117,12 +117,17 @@ void handleQuit() LLAppViewer::instance()->userQuit(); } -bool runMainLoop() +// This function is called pumpMainLoop() rather than runMainLoop() because +// it passes control to the viewer's main-loop logic for a single frame. Like +// LLAppViewer::frame(), it returns 'true' when it's done. Until then, it +// expects to be called again by the timer in LLAppDelegate +// (llappdelegate-objc.mm). +bool pumpMainLoop() { bool ret = LLApp::isQuitting(); if (!ret && gViewerAppPtr != NULL) { - ret = gViewerAppPtr->mainLoop(); + ret = gViewerAppPtr->frame(); } else { ret = true; } diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 4786f83bfd..af0cd27fd5 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -317,10 +317,8 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, } // Run the application main loop - if(!LLApp::isQuitting()) - { - viewer_app_ptr->mainLoop(); - } + while (! viewer_app_ptr->frame()) + {} if (!LLApp::isError()) { @@ -330,33 +328,33 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, // app cleanup if there was a problem. // #if WINDOWS_CRT_MEM_CHECKS - LL_INFOS() << "CRT Checking memory:" << LL_ENDL; - if (!_CrtCheckMemory()) - { - LL_WARNS() << "_CrtCheckMemory() failed at prior to cleanup!" << LL_ENDL; - } - else - { - LL_INFOS() << " No corruption detected." << LL_ENDL; - } + LL_INFOS() << "CRT Checking memory:" << LL_ENDL; + if (!_CrtCheckMemory()) + { + LL_WARNS() << "_CrtCheckMemory() failed at prior to cleanup!" << LL_ENDL; + } + else + { + LL_INFOS() << " No corruption detected." << LL_ENDL; + } #endif - - gGLActive = TRUE; - viewer_app_ptr->cleanup(); - + gGLActive = TRUE; + + viewer_app_ptr->cleanup(); + #if WINDOWS_CRT_MEM_CHECKS - LL_INFOS() << "CRT Checking memory:" << LL_ENDL; - if (!_CrtCheckMemory()) - { - LL_WARNS() << "_CrtCheckMemory() failed after cleanup!" << LL_ENDL; - } - else - { - LL_INFOS() << " No corruption detected." << LL_ENDL; - } + LL_INFOS() << "CRT Checking memory:" << LL_ENDL; + if (!_CrtCheckMemory()) + { + LL_WARNS() << "_CrtCheckMemory() failed after cleanup!" << LL_ENDL; + } + else + { + LL_INFOS() << " No corruption detected." << LL_ENDL; + } #endif - + } delete viewer_app_ptr; viewer_app_ptr = NULL; diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index 1819fc74ee..90a5483dc9 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -26,6 +26,7 @@ #include "llviewerprecompiledheaders.h" #include "llcommandlineparser.h" +#include "llexception.h" // *NOTE: The boost::lexical_cast generates // the warning C4701(local used with out assignment) in VC7.1. @@ -50,6 +51,7 @@ #include "llsdserialize.h" #include "llerror.h" #include "stringize.h" +#include "llexception.h" #include <string> #include <set> #include <iostream> @@ -98,14 +100,14 @@ namespace bool gPastLastOption = false; } -class LLCLPError : public std::logic_error { +class LLCLPError : public LLException { public: - LLCLPError(const std::string& what) : std::logic_error(what) {} + LLCLPError(const std::string& what) : LLException(what) {} }; -class LLCLPLastOption : public std::logic_error { +class LLCLPLastOption : public LLException { public: - LLCLPLastOption(const std::string& what) : std::logic_error(what) {} + LLCLPLastOption(const std::string& what) : LLException(what) {} }; class LLCLPValue : public po::value_semantic_codecvt_helper<char> @@ -202,17 +204,17 @@ protected: { if(gPastLastOption) { - throw(LLCLPLastOption("Don't parse no more!")); + LLTHROW(LLCLPLastOption("Don't parse no more!")); } // Error checks. Needed? if (!value_store.empty() && !is_composing()) { - throw(LLCLPError("Non composing value with multiple occurences.")); + LLTHROW(LLCLPError("Non composing value with multiple occurences.")); } if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens()) { - throw(LLCLPError("Illegal number of tokens specified.")); + LLTHROW(LLCLPError("Illegal number of tokens specified.")); } if(value_store.empty()) @@ -466,7 +468,7 @@ onevalue(const std::string& option, { // What does it mean when the user specifies a command-line switch // that requires a value, but omits the value? Complain. - throw LLCLPError(STRINGIZE("No value specified for --" << option << "!")); + LLTHROW(LLCLPError(STRINGIZE("No value specified for --" << option << "!"))); } else if (value.size() > 1) { @@ -484,9 +486,9 @@ void badvalue(const std::string& option, // If the user passes an unusable value for a command-line switch, it // seems like a really bad idea to just ignore it, even with a log // warning. - throw LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option - << "' for variable '" << varname << "' of type " << type - << ": '" << value << "'")); + LLTHROW(LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option + << "' for variable '" << varname << "' of type " << type + << ": '" << value << "'"))); } template <typename T> diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 7721e67290..76e16f5a1f 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -67,7 +67,9 @@ namespace const std::string QUEUE_EVENTPUMP_NAME("ScriptActionQueue"); - + // ObjectIventoryFetcher is an adapter between the LLVOInventoryListener::inventoryChanged + // callback mechanism and the LLEventPump coroutine architecture allowing the + // coroutine to wait for the inventory event. class ObjectInventoryFetcher: public LLVOInventoryListener { public: @@ -144,7 +146,7 @@ public: queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM); } - return LLSD().with("success", LLSD::Boolean(true)); + return LLSDMap("success", LLSD::Boolean(true)); } @@ -254,7 +256,6 @@ LLFloaterCompileQueue::LLFloaterCompileQueue(const LLSD& key) setTitle(LLTrans::getString("CompileQueueTitle")); setStartString(LLTrans::getString("CompileQueueStart")); -// mUploadQueue = new LLAssetUploadQueue(new LLCompileFloaterUploadQueueSupplier(key.asUUID())); } LLFloaterCompileQueue::~LLFloaterCompileQueue() @@ -267,7 +268,6 @@ void LLFloaterCompileQueue::experienceIdsReceived( const LLSD& content ) { mExperienceIds.insert(it->asUUID()); } -// nextObject(); } BOOL LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const @@ -277,11 +277,6 @@ BOOL LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const // //Attempt to record this asset ID. If it can not be inserted into the set // //then it has already been processed so return false. -// bool LLFloaterCompileQueue::checkAssetId(const LLUUID &assetId) -// { -// std::pair<uuid_list_t::iterator, bool> result = mAssetIds.insert(assetId); -// return result.second; -// } void LLFloaterCompileQueue::handleHTTPResponse(std::string pumpName, const LLSD &expresult) { @@ -331,8 +326,10 @@ void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID paren queue->experienceIdsReceived(result["experience_ids"]); + // getDerived handle gets a handle that can be resolved to a parent class of the derived object. LLHandle<LLFloaterScriptQueue> hFloater(queue->getDerivedHandle<LLFloaterScriptQueue>()); + // note subtle difference here: getDerivedHandle in this case is for an LLFloaterCompileQueue fnQueueAction_t fn = boost::bind(LLFloaterCompileQueue::processScript, queue->getDerivedHandle<LLFloaterCompileQueue>(), _1, _2, _3); @@ -345,37 +342,35 @@ void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID paren } +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { LLSD result; - LLFloaterCompileQueue *that = hfloater.get(); - bool monocompile = that->mMono; + LLCheckedHandle<LLFloaterCompileQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + bool monocompile = floater->mMono; F32 fetch_timeout = gSavedSettings.getF32("QueueInventoryFetchTimeout"); - if (!that) - return false; // Initial test to see if we can (or should) attempt to compile the script. LLInventoryItem *item = dynamic_cast<LLInventoryItem *>(inventory); - { - if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || - !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) - { - std::string buffer = "Skipping: " + item->getName() + "(Permissions)"; - that->addStringMessage(buffer); - return true; - } + if (!item) + { + LL_WARNS("SCRIPTQ") << "item retrieved is not an LLInventoryItem." << LL_ENDL; + return true; + } -// if (!that->checkAssetId(item->getAssetUUID())) -// { -// std::string buffer = "Skipping: " + item->getName() + "(Repeat)"; -// that->addStringMessage(buffer); -// return true; -// } + if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || + !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) + { + std::string buffer = "Skipping: " + item->getName() + "(Permissions)"; + floater->addStringMessage(buffer); + return true; } - that = NULL; // Attempt to retrieve the experience LLUUID experienceId; @@ -384,37 +379,30 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat boost::bind(&LLFloaterCompileQueue::handleHTTPResponse, pump.getName(), _1)); result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, - LLSD().with("timeout", LLSD::Boolean(true))); + LLSDMap("timeout", LLSD::Boolean(true))); - that = hfloater.get(); - if (!that) - { - return false; - } - - if (result.has("timeout") && result["timeout"].asBoolean()) - { + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. LLStringUtil::format_map_t args; args["[OBJECT_NAME]"] = inventory->getName(); - std::string buffer = that->getString("Timeout", args); - that->addStringMessage(buffer); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); return true; } if (result.has(LLExperienceCache::EXPERIENCE_ID)) { experienceId = result[LLExperienceCache::EXPERIENCE_ID].asUUID(); - if (!that->hasExperience(experienceId)) + if (!floater->hasExperience(experienceId)) { - that->addProcessingMessage("CompileNoExperiencePerm", LLSD() - .with("SCRIPT", inventory->getName()) - .with("EXPERIENCE", result[LLExperienceCache::NAME].asString())); + floater->addProcessingMessage("CompileNoExperiencePerm", + LLSDMap("SCRIPT", inventory->getName()) + ("EXPERIENCE", result[LLExperienceCache::NAME].asString())); return true; } } } - that = NULL; { HandleScriptUserData userData(pump.getName()); @@ -433,32 +421,23 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat &userData); result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, - LLSD().with("timeout", LLSD::Boolean(true))); - } - - that = hfloater.get(); - if (!that) - { - return false; + LLSDMap("timeout", LLSD::Boolean(true))); } if (result.has("timeout")) - { - if (result.has("timeout") && result["timeout"].asBoolean()) - { - LLStringUtil::format_map_t args; - args["[OBJECT_NAME]"] = inventory->getName(); - std::string buffer = that->getString("Timeout", args); - that->addStringMessage(buffer); - return true; - } + { // A timeout filed in the result will always be true if present. + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = inventory->getName(); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); + return true; } if (result.has("error")) { LL_WARNS("SCRIPTQ") << "Inventory fetch returned with error. Code: " << result["error"].asString() << LL_ENDL; std::string buffer = result["message"].asString() + " " + inventory->getName(); - that->addStringMessage(buffer); + floater->addStringMessage(buffer); if (result.has("alert")) { @@ -470,12 +449,9 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat } LLUUID assetId = result["asset_id"]; - that = NULL; - std::string url = object->getRegion()->getCapability("UpdateScriptTask"); - { LLResourceUploadInfo::ptr_t uploadInfo(new LLQueuedScriptAssetUpload(object->getID(), inventory->getUUID(), @@ -490,24 +466,15 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); } - result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSD().with("timeout", LLSD::Boolean(true))); - - that = hfloater.get(); - if (!that) - { - return false; - } + result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSDMap("timeout", LLSD::Boolean(true))); if (result.has("timeout")) - { - if (result.has("timeout") && result["timeout"].asBoolean()) - { - LLStringUtil::format_map_t args; - args["[OBJECT_NAME]"] = inventory->getName(); - std::string buffer = that->getString("Timeout", args); - that->addStringMessage(buffer); - return true; - } + { // A timeout filed in the result will always be true if present. + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = inventory->getName(); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); + return true; } // Bytecode save completed @@ -515,21 +482,21 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat { std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" succeeded"); - that->addStringMessage(buffer); + floater->addStringMessage(buffer); LL_INFOS() << buffer << LL_ENDL; } else { LLSD compile_errors = result["errors"]; std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" failed:"); - that->addStringMessage(buffer); + floater->addStringMessage(buffer); for (LLSD::array_const_iterator line = compile_errors.beginArray(); line < compile_errors.endArray(); line++) { std::string str = line->asString(); str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); - that->addStringMessage(str); + floater->addStringMessage(str); } LL_INFOS() << result["errors"] << LL_ENDL; } @@ -576,16 +543,18 @@ LLFloaterResetQueue::~LLFloaterResetQueue() { } -bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { - LLFloaterScriptQueue *that = hfloater.get(); - if (that) - { - std::string buffer; - buffer = that->getString("Resetting") + (": ") + inventory->getName(); - that->addStringMessage(buffer); - } + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("Resetting") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_ScriptReset); @@ -602,6 +571,8 @@ bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hflo bool LLFloaterResetQueue::startQueue() { + // Bind the resetObjectScripts method into a QueueAction function and pass it + // into the object queue processing coroutine. fnQueueAction_t fn = boost::bind(LLFloaterResetQueue::resetObjectScripts, getDerivedHandle<LLFloaterScriptQueue>(), _1, _2, _3); @@ -629,16 +600,18 @@ LLFloaterRunQueue::~LLFloaterRunQueue() { } -bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { - LLFloaterScriptQueue *that = hfloater.get(); - if (that) - { - std::string buffer; - buffer = that->getString("Running") + (": ") + inventory->getName(); - that->addStringMessage(buffer); - } + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("Running") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_SetScriptRunning); @@ -684,16 +657,18 @@ LLFloaterNotRunQueue::~LLFloaterNotRunQueue() { } +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. bool LLFloaterNotRunQueue::stopObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { - LLFloaterScriptQueue *that = hfloater.get(); - if (that) - { - std::string buffer; - buffer = that->getString("NotRunning") + (": ") + inventory->getName(); - that->addStringMessage(buffer); - } + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("NotRunning") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_SetScriptRunning); @@ -732,7 +707,7 @@ void ObjectInventoryFetcher::inventoryChanged(LLViewerObject* object, mInventoryList.clear(); mInventoryList.assign(inventory->begin(), inventory->end()); - mPump.post(LLSD().with("changed", LLSD::Boolean(true))); + mPump.post(LLSDMap("changed", LLSD::Boolean(true))); } @@ -740,115 +715,97 @@ void LLFloaterScriptQueue::objectScriptProcessingQueueCoro(std::string action, L object_data_list_t objectList, fnQueueAction_t func) { LLCoros::set_consuming(true); - LLFloaterScriptQueue * floater(NULL); + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // This is expected if the dialog closes. LLEventMailDrop maildrop(QUEUE_EVENTPUMP_NAME, true); F32 fetch_timeout = gSavedSettings.getF32("QueueInventoryFetchTimeout"); -// floater = hfloater.get(); -// floater->addProcessingMessage("Starting", -// LLSD() -// .with("[START]", action) -// .with("[COUNT]", LLSD::Integer(objectList.size()))); -// floater = NULL; - for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj) + try { - bool firstForObject = true; - LLUUID object_id = (*itObj).mObjectId; - LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL; - - LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id); - LLInventoryObject::object_list_t inventory; - if (obj) + for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj) { - ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL)); + bool firstForObject = true; + LLUUID object_id = (*itObj).mObjectId; + LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL; - fetcher->fetchInventory(); - - floater = hfloater.get(); - if (floater) + LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id); + LLInventoryObject::object_list_t inventory; + if (obj) { + ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL)); + + fetcher->fetchInventory(); + LLStringUtil::format_map_t args; args["[OBJECT_NAME]"] = (*itObj).mObjectName; floater->addStringMessage(floater->getString("LoadingObjInv", args)); - } - LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, fetch_timeout, - LLSD().with("timeout", LLSD::Boolean(true))); + LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, fetch_timeout, + LLSDMap("timeout", LLSD::Boolean(true))); - if (result.has("timeout") && result["timeout"].asBoolean()) - { - LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() << - ". Skipping to next object." << LL_ENDL; + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. + LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() << + ". Skipping to next object." << LL_ENDL; - // floater could have been closed - floater = hfloater.get(); - if (floater) - { LLStringUtil::format_map_t args; args["[OBJECT_NAME]"] = (*itObj).mObjectName; floater->addStringMessage(floater->getString("Timeout", args)); + + continue; } + inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end()); + } + else + { + LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id << + ". Skipping to next." << LL_ENDL; continue; } - inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end()); - } - else - { - LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id << - ". Skipping to next." << LL_ENDL; - continue; - } + // TODO: Get the name of the object we are looking at here so that we can display it below. + //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName(); + LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL; - // TODO: Get the name of the object we are looking at here so that we can display it below. - //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName(); - LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL; - - for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin(); - itInv != inventory.end(); ++itInv) - { - floater = hfloater.get(); - if (!floater) + for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin(); + itInv != inventory.end(); ++itInv) { - LL_WARNS("SCRIPTQ") << "Script Queue floater closed! Canceling remaining ops" << LL_ENDL; - break; - } + floater.check(); - // note, we have a smart pointer to the obj above... but if we didn't we'd check that - // it still exists here. + // note, we have a smart pointer to the obj above... but if we didn't we'd check that + // it still exists here. - if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT)) - { - LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL; - if (firstForObject) + if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT)) { - //floater->addStringMessage(objName + ":"); - firstForObject = false; + LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL; + if (firstForObject) + { + //floater->addStringMessage(objName + ":"); + firstForObject = false; + } + + if (!func(obj, (*itInv), maildrop)) + { + continue; + } } - if (!func(obj, (*itInv), maildrop)) - { - continue; - } + // no other explicit suspension point in this loop. func(...) MIGHT suspend + // but offers no guarantee of doing so. + llcoro::suspend(); } - - llcoro::suspend(); - } - // Just test to be sure the floater is still present before calling the func - if (!hfloater.get()) - { - LL_WARNS("SCRIPTQ") << "Script Queue floater dismissed." << LL_ENDL; - break; } - } - - floater = hfloater.get(); - if (floater) - { floater->addStringMessage("Done"); floater->getChildView("close")->setEnabled(TRUE); } + catch (LLCheckedHandleBase::Stale &) + { + // This is expected. It means that floater has been closed before + // processing was completed. + LL_DEBUGS("SCRIPTQ") << "LLExeceptionStaleHandle caught! Floater has most likely been closed." << LL_ENDL; + } } diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp index 024e315632..dece3fc1ea 100644 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -55,7 +55,8 @@ LLFloaterWebContent::_Params::_Params() preferred_media_size("preferred_media_size"), trusted_content("trusted_content", false), show_page_title("show_page_title", true), - clean_browser("clean_browser", false) + clean_browser("clean_browser", false), + dev_mode("dev_mode", false) {} LLFloaterWebContent::LLFloaterWebContent( const Params& params ) @@ -74,14 +75,16 @@ LLFloaterWebContent::LLFloaterWebContent( const Params& params ) mShowPageTitle(params.show_page_title), mAllowNavigation(true), mCurrentURL(""), - mDisplayURL("") + mDisplayURL(""), + mDevelopMode(params.dev_mode) // if called from "Develop" Menu, set a flag and change things to be more useful for devs { mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this )); mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this )); mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this )); - mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this )); + mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind(&LLFloaterWebContent::onPopExternal, this)); + mCommitCallbackRegistrar.add( "WebContent.TestURL", boost::bind(&LLFloaterWebContent::onTestURL, this, _2)); } BOOL LLFloaterWebContent::postBuild() @@ -195,8 +198,6 @@ void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) width + getRect().getWidth() - browser_rect.getWidth(), height + getRect().getHeight() - browser_rect.getHeight()); - LL_DEBUGS() << "geometry change: " << geom << LL_ENDL; - LLRect new_rect; getParent()->screenRectToLocal(geom, &new_rect); setShape(new_rect); @@ -205,8 +206,6 @@ void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) // static void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) { - LL_DEBUGS() << "url = " << p.url() << ", target = " << p.target() << ", uuid = " << p.id() << LL_ENDL; - if (!p.id.isProvided()) { p.id = LLUUID::generateNewID().asString(); @@ -224,12 +223,6 @@ void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) // and close the least recently opened one if this will put us over the limit. LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList(p.window_class); - LL_DEBUGS() << "total instance count is " << instances.size() << LL_ENDL; - - for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++) - { - LL_DEBUGS() << " " << (*iter)->getKey()["target"] << LL_ENDL; - } if(instances.size() >= (size_t)browser_window_limit) { @@ -241,16 +234,19 @@ void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) void LLFloaterWebContent::open_media(const Params& p) { - // Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin. LLViewerMedia::proxyWindowOpened(p.target(), p.id()); - mWebBrowser->setHomePageUrl(p.url, HTTP_CONTENT_TEXT_HTML); + mWebBrowser->setHomePageUrl(p.url); mWebBrowser->setTarget(p.target); - mWebBrowser->navigateTo(p.url, HTTP_CONTENT_TEXT_HTML, p.clean_browser); + mWebBrowser->navigateTo(p.url); set_current_url(p.url); getChild<LLLayoutPanel>("status_bar")->setVisible(p.show_chrome); getChild<LLLayoutPanel>("nav_controls")->setVisible(p.show_chrome); + + // turn additional debug controls on but only for Develop mode (Develop menu open) + getChild<LLLayoutPanel>("debug_controls")->setVisible(mDevelopMode); + bool address_entry_enabled = p.allow_address_entry && !p.trusted_content; mAllowNavigation = p.allow_back_forward_navigation; getChildView("address")->setEnabled(address_entry_enabled); @@ -499,7 +495,7 @@ void LLFloaterWebContent::onEnterAddress() LLStringUtil::trim(url); if ( url.length() > 0 ) { - mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + mWebBrowser->navigateTo(url); }; } @@ -508,9 +504,18 @@ void LLFloaterWebContent::onPopExternal() // make sure there is at least something there. // (perhaps this test should be for minimum length of a URL) std::string url = mAddressCombo->getValue().asString(); - LLStringUtil::trim(url); - if ( url.length() > 0 ) + LLStringUtil::trim(url); + if (url.length() > 0) + { + LLWeb::loadURLExternal(url); + }; +} + +void LLFloaterWebContent::onTestURL(std::string url) +{ + LLStringUtil::trim(url); + if (url.length() > 0) { - LLWeb::loadURLExternal( url ); + mWebBrowser->navigateTo(url); }; } diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h index 4291fd9f2c..0bf93504c2 100644 --- a/indra/newview/llfloaterwebcontent.h +++ b/indra/newview/llfloaterwebcontent.h @@ -58,7 +58,8 @@ public: allow_back_forward_navigation, trusted_content, show_page_title, - clean_browser; + clean_browser, + dev_mode; Optional<LLRect> preferred_media_size; _Params(); @@ -92,6 +93,7 @@ protected: void onClickStop(); void onEnterAddress(); void onPopExternal(); + void onTestURL(std::string url); static void preCreate(Params& p); void open_media(const Params& ); @@ -113,6 +115,7 @@ protected: std::string mUUID; bool mShowPageTitle; bool mAllowNavigation; + bool mDevelopMode; }; #endif // LL_LLFLOATERWEBCONTENT_H diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 763657ebad..0bcd8a9e63 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -95,7 +95,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mVolumeSliderVisible(0), mWindowShade(NULL), mHideImmediately(false), - mSecureURL(false) + mSecureURL(false), + mMediaPlaySliderCtrlMouseDownValue(0.0) { mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this)); mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this)); @@ -109,7 +110,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mCommitCallbackRegistrar.add("MediaCtrl.Open", boost::bind(&LLPanelPrimMediaControls::onClickOpen, this)); mCommitCallbackRegistrar.add("MediaCtrl.Zoom", boost::bind(&LLPanelPrimMediaControls::onClickZoom, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitURL", boost::bind(&LLPanelPrimMediaControls::onCommitURL, this)); - mCommitCallbackRegistrar.add("MediaCtrl.JumpProgress", boost::bind(&LLPanelPrimMediaControls::onCommitSlider, this)); + mCommitCallbackRegistrar.add("MediaCtrl.MouseDown", boost::bind(&LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseDown, this)); + mCommitCallbackRegistrar.add("MediaCtrl.MouseUp", boost::bind(&LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseUp, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeUp", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeUp, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeDown", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeDown, this)); mCommitCallbackRegistrar.add("MediaCtrl.Volume", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeSlider, this)); @@ -1246,26 +1248,38 @@ void LLPanelPrimMediaControls::setCurrentURL() #endif // USE_COMBO_BOX_FOR_MEDIA_URL } -void LLPanelPrimMediaControls::onCommitSlider() + +void LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseDown() { - focusOnTarget(); + mMediaPlaySliderCtrlMouseDownValue = mMediaPlaySliderCtrl->getValue().asReal(); - LLViewerMediaImpl* media_impl = getTargetMediaImpl(); - if (media_impl) + mUpdateSlider = false; +} + +void LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseUp() +{ + F64 cur_value = mMediaPlaySliderCtrl->getValue().asReal(); + + if (mMediaPlaySliderCtrlMouseDownValue != cur_value) { - // get slider value - F64 slider_value = mMediaPlaySliderCtrl->getValue().asReal(); - if(slider_value <= 0.0) - { - media_impl->stop(); - } - else + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) { - media_impl->seek(slider_value*mMovieDuration); - //mUpdateSlider= false; + if (cur_value <= 0.0) + { + media_impl->stop(); + } + else + { + media_impl->seek(cur_value * mMovieDuration); + } } + + mUpdateSlider = true; } -} +} void LLPanelPrimMediaControls::onCommitVolumeUp() { diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index 6d2eb3430e..21d5236074 100644 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -107,8 +107,10 @@ private: void updateZoom(); void setCurrentURL(); - void onCommitSlider(); - + + void onMediaPlaySliderCtrlMouseDown(); + void onMediaPlaySliderCtrlMouseUp(); + void onCommitVolumeUp(); void onCommitVolumeDown(); void onCommitVolumeSlider(); @@ -219,6 +221,8 @@ private: S32 mVolumeSliderVisible; LLNotificationPtr mActiveNotification; + + F64 mMediaPlaySliderCtrlMouseDownValue; }; #endif // LL_PANELPRIMMEDIACONTROLS_H diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp index 4f9f83b6f2..72d7cf1e45 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -29,6 +29,8 @@ #include "llviewerprecompiledheaders.h" #include "llsecapi.h" #include "llsechandler_basic.h" +#include "llexception.h" +#include "stringize.h" #include <openssl/evp.h> #include <openssl/err.h> #include <map> @@ -64,12 +66,12 @@ void initializeSecHandler() } catch (LLProtectedDataException e) { - exception_msg = e.getMessage(); + exception_msg = e.what(); } } if (!exception_msg.empty()) // an exception was thrown. { - throw LLProtectedDataException(exception_msg.c_str()); + LLTHROW(LLProtectedDataException(exception_msg)); } } @@ -101,6 +103,7 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred) LLSD LLCredential::getLoginParams() { LLSD result = LLSD::emptyMap(); + std::string username; try { if (mIdentifier["type"].asString() == "agent") @@ -109,17 +112,19 @@ LLSD LLCredential::getLoginParams() result["passwd"] = "$1$" + mAuthenticator["secret"].asString(); result["first"] = mIdentifier["first_name"]; result["last"] = mIdentifier["last_name"]; - + username = result["first"].asString() + " " + result["last"].asString(); } else if (mIdentifier["type"].asString() == "account") { result["username"] = mIdentifier["account_name"]; result["passwd"] = mAuthenticator["secret"]; - + username = result["username"].asString(); } } catch (...) { + // nat 2016-08-18: not clear what exceptions the above COULD throw?! + LOG_UNHANDLED_EXCEPTION(STRINGIZE("for '" << username << "'")); // we could have corrupt data, so simply return a null login param if so LL_WARNS("AppInit") << "Invalid credential" << LL_ENDL; } diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 6fe3ee31cf..6af5a28fa5 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -32,6 +32,7 @@ #include <openssl/x509.h> #include <ostream> #include "llpointer.h" +#include "llexception.h" #ifdef LL_WINDOWS #pragma warning(disable:4250) @@ -116,17 +117,13 @@ -class LLProtectedDataException +struct LLProtectedDataException: public LLException { -public: - LLProtectedDataException(const char *msg) + LLProtectedDataException(const std::string& msg): + LLException(msg) { - LL_WARNS("SECAPI") << "Protected Data Error: " << (std::string)msg << LL_ENDL; - mMsg = (std::string)msg; + LL_WARNS("SECAPI") << "Protected Data Error: " << msg << LL_ENDL; } - std::string getMessage() { return mMsg; } -protected: - std::string mMsg; }; // class LLCertificate @@ -334,22 +331,21 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred); // All error handling is via exceptions. -class LLCertException +class LLCertException: public LLException { public: - LLCertException(LLPointer<LLCertificate> cert, const char* msg) + LLCertException(LLPointer<LLCertificate> cert, const std::string& msg): + LLException(msg) { mCert = cert; - LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL; - mMsg = (std::string)msg; + LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; } - LLPointer<LLCertificate> getCert() { return mCert; } - std::string getMessage() { return mMsg; } + virtual ~LLCertException() throw() {} + LLPointer<LLCertificate> getCert() const { return mCert; } protected: LLPointer<LLCertificate> mCert; - std::string mMsg; }; class LLInvalidCertificate : public LLCertException @@ -358,6 +354,7 @@ public: LLInvalidCertificate(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalid") { } + virtual ~LLInvalidCertificate() throw() {} protected: }; @@ -367,6 +364,7 @@ public: LLCertValidationTrustException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertUntrusted") { } + virtual ~LLCertValidationTrustException() throw() {} protected: }; @@ -378,7 +376,7 @@ public: { mHostname = hostname; } - + virtual ~LLCertValidationHostnameException() throw() {} std::string getHostname() { return mHostname; } protected: std::string mHostname; @@ -392,6 +390,7 @@ public: { mTime = current_time; } + virtual ~LLCertValidationExpirationException() throw() {} LLDate GetTime() { return mTime; } protected: LLDate mTime; @@ -403,6 +402,7 @@ public: LLCertKeyUsageValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertKeyUsage") { } + virtual ~LLCertKeyUsageValidationException() throw() {} protected: }; @@ -412,6 +412,7 @@ public: LLCertBasicConstraintsValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertBasicConstraints") { } + virtual ~LLCertBasicConstraintsValidationException() throw() {} protected: }; @@ -421,6 +422,7 @@ public: LLCertValidationInvalidSignatureException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidSignature") { } + virtual ~LLCertValidationInvalidSignatureException() throw() {} protected: }; diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 40516f9bbb..d6fb801cc0 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -35,6 +35,8 @@ #include "llfile.h" #include "lldir.h" #include "llviewercontrol.h" +#include "llexception.h" +#include "stringize.h" #include <vector> #include <ios> #include <openssl/ossl_typ.h> @@ -72,14 +74,14 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) if(pem_bio == NULL) { LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; - throw LLInvalidCertificate(this); + LLTHROW(LLInvalidCertificate(this)); } mCert = NULL; PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); BIO_free(pem_bio); if (!mCert) { - throw LLInvalidCertificate(this); + LLTHROW(LLInvalidCertificate(this)); } } @@ -88,7 +90,7 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert) { if (!pCert || !pCert->cert_info) { - throw LLInvalidCertificate(this); + LLTHROW(LLInvalidCertificate(this)); } mCert = X509_dup(pCert); } @@ -617,7 +619,7 @@ void LLBasicCertificateStore::load_from_file(const std::string& filename) } catch (...) { - LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; + LOG_UNHANDLED_EXCEPTION("creating certificate from the certificate store file"); } X509_free(cert_x509); cert_x509 = NULL; @@ -873,22 +875,22 @@ void _validateCert(int validation_policy, // check basic properties exist in the cert if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) { - throw LLCertException(cert, "Cert doesn't have a Subject Name"); + LLTHROW(LLCertException(cert, "Cert doesn't have a Subject Name")); } if(!current_cert_info.has(CERT_ISSUER_NAME_STRING)) { - throw LLCertException(cert, "Cert doesn't have an Issuer Name"); + LLTHROW(LLCertException(cert, "Cert doesn't have an Issuer Name")); } // check basic properties exist in the cert if(!current_cert_info.has(CERT_VALID_FROM) || !current_cert_info.has(CERT_VALID_TO)) { - throw LLCertException(cert, "Cert doesn't have an expiration period"); + LLTHROW(LLCertException(cert, "Cert doesn't have an expiration period")); } if (!current_cert_info.has(CERT_SHA1_DIGEST)) { - throw LLCertException(cert, "No SHA1 digest"); + LLTHROW(LLCertException(cert, "No SHA1 digest")); } if (validation_policy & VALIDATION_POLICY_TIME) @@ -903,7 +905,7 @@ void _validateCert(int validation_policy, if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || (validation_date > current_cert_info[CERT_VALID_TO].asDate())) { - throw LLCertValidationExpirationException(cert, validation_date); + LLTHROW(LLCertValidationExpirationException(cert, validation_date)); } } if (validation_policy & VALIDATION_POLICY_SSL_KU) @@ -914,14 +916,14 @@ void _validateCert(int validation_policy, !(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) { - throw LLCertKeyUsageValidationException(cert); + LLTHROW(LLCertKeyUsageValidationException(cert)); } // only validate EKU if the cert has it if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() && (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE], LLSD((std::string)CERT_EKU_SERVER_AUTH)))) { - throw LLCertKeyUsageValidationException(cert); + LLTHROW(LLCertKeyUsageValidationException(cert)); } } if (validation_policy & VALIDATION_POLICY_CA_KU) @@ -930,7 +932,7 @@ void _validateCert(int validation_policy, (!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], (std::string)CERT_KU_CERT_SIGN))) { - throw LLCertKeyUsageValidationException(cert); + LLTHROW(LLCertKeyUsageValidationException(cert)); } } @@ -942,13 +944,13 @@ void _validateCert(int validation_policy, if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) || !current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA]) { - throw LLCertBasicConstraintsValidationException(cert); + LLTHROW(LLCertBasicConstraintsValidationException(cert)); } if (current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_PATHLEN) && ((current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger() != 0) && (depth > current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger()))) { - throw LLCertBasicConstraintsValidationException(cert); + LLTHROW(LLCertBasicConstraintsValidationException(cert)); } } } @@ -1018,7 +1020,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if(cert_chain->size() < 1) { - throw LLCertException(NULL, "No certs in chain"); + LLTHROW(LLCertException(NULL, "No certs in chain")); } iterator current_cert = cert_chain->begin(); LLSD current_cert_info; @@ -1033,11 +1035,11 @@ void LLBasicCertificateStore::validate(int validation_policy, (*current_cert)->getLLSD(current_cert_info); if(!validation_params.has(CERT_HOSTNAME)) { - throw LLCertException((*current_cert), "No hostname passed in for validation"); + LLTHROW(LLCertException((*current_cert), "No hostname passed in for validation")); } if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN)) { - throw LLInvalidCertificate((*current_cert)); + LLTHROW(LLInvalidCertificate((*current_cert))); } LL_DEBUGS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << @@ -1054,7 +1056,7 @@ void LLBasicCertificateStore::validate(int validation_policy, X509* cert_x509 = (*current_cert)->getOpenSSLX509(); if(!cert_x509) { - throw LLInvalidCertificate((*current_cert)); + LLTHROW(LLInvalidCertificate((*current_cert))); } std::string sha1_hash((const char *)cert_x509->sha1_hash, SHA_DIGEST_LENGTH); X509_free( cert_x509 ); @@ -1075,7 +1077,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if((validation_date < cache_entry->second.first) || (validation_date > cache_entry->second.second)) { - throw LLCertValidationExpirationException((*current_cert), validation_date); + LLTHROW(LLCertValidationExpirationException((*current_cert), validation_date)); } } // successfully found in cache @@ -1107,7 +1109,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if(!_verify_signature((*current_cert), previous_cert)) { - throw LLCertValidationInvalidSignatureException(previous_cert); + LLTHROW(LLCertValidationInvalidSignatureException(previous_cert)); } } _validateCert(local_validation_policy, @@ -1156,7 +1158,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if(!_verify_signature((*found_store_cert), (*current_cert))) { - throw LLCertValidationInvalidSignatureException(*current_cert); + LLTHROW(LLCertValidationInvalidSignatureException(*current_cert)); } // successfully validated. mTrustedCertCache[sha1_hash] = std::pair<LLDate, LLDate>(from_time, to_time); @@ -1173,7 +1175,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if (validation_policy & VALIDATION_POLICY_TRUSTED) { // we reached the end without finding a trusted cert. - throw LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1]); + LLTHROW(LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1])); } mTrustedCertCache[sha1_hash] = std::pair<LLDate, LLDate>(from_time, to_time); @@ -1261,7 +1263,7 @@ void LLSecAPIBasicHandler::_readProtectedData() protected_data_stream.read((char *)salt, STORE_SALT_SIZE); if (protected_data_stream.gcount() < STORE_SALT_SIZE) { - throw LLProtectedDataException("Config file too short."); + LLTHROW(LLProtectedDataException("Config file too short.")); } cipher.decrypt(salt, STORE_SALT_SIZE); @@ -1301,7 +1303,7 @@ void LLSecAPIBasicHandler::_readProtectedData() if (parser->parse(parse_stream, mProtectedDataMap, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) { - throw LLProtectedDataException("Config file cannot be decrypted."); + LLTHROW(LLProtectedDataException("Config file cannot be decrypted.")); } } } @@ -1364,7 +1366,7 @@ void LLSecAPIBasicHandler::_writeProtectedData() } catch (...) { - LL_WARNS() << "LLProtectedDataException(Error writing Protected Data Store)" << LL_ENDL; + LOG_UNHANDLED_EXCEPTION("LLProtectedDataException(Error writing Protected Data Store)"); // it's good practice to clean up any secure information on error // (even though this file isn't really secure. Perhaps in the future // it may be, however. @@ -1372,39 +1374,39 @@ void LLSecAPIBasicHandler::_writeProtectedData() // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData() // Decided throwing an exception here was overkill until we figure out why this happens - //throw LLProtectedDataException("Error writing Protected Data Store"); + //LLTHROW(LLProtectedDataException("Error writing Protected Data Store")); } - try - { - // move the temporary file to the specified file location. - if((( (LLFile::isfile(mProtectedDataFilename) != 0) - && (LLFile::remove(mProtectedDataFilename) != 0))) - || (LLFile::rename(tmp_filename, mProtectedDataFilename))) - { - LL_WARNS() << "LLProtectedDataException(Could not overwrite protected data store)" << LL_ENDL; - LLFile::remove(tmp_filename); + try + { + // move the temporary file to the specified file location. + if((( (LLFile::isfile(mProtectedDataFilename) != 0) + && (LLFile::remove(mProtectedDataFilename) != 0))) + || (LLFile::rename(tmp_filename, mProtectedDataFilename))) + { + LL_WARNS() << "LLProtectedDataException(Could not overwrite protected data store)" << LL_ENDL; + LLFile::remove(tmp_filename); - // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData() - // Decided throwing an exception here was overkill until we figure out why this happens - //throw LLProtectedDataException("Could not overwrite protected data store"); - } + // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData() + // Decided throwing an exception here was overkill until we figure out why this happens + //LLTHROW(LLProtectedDataException("Could not overwrite protected data store")); + } } catch (...) { - LL_WARNS() << "LLProtectedDataException(Error renaming '" << tmp_filename - << "' to '" << mProtectedDataFilename << "')" << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("renaming '" << tmp_filename << "' to '" + << mProtectedDataFilename << "'")); // it's good practice to clean up any secure information on error // (even though this file isn't really secure. Perhaps in the future - // it may be, however. + // it may be, however). LLFile::remove(tmp_filename); //crash in LLSecAPIBasicHandler::_writeProtectedData() // Decided throwing an exception here was overkill until we figure out why this happens - //throw LLProtectedDataException("Error writing Protected Data Store"); + //LLTHROW(LLProtectedDataException("Error writing Protected Data Store")); } } - + // instantiate a certificate from a pem string LLPointer<LLCertificate> LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert) { diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp index 2d06b8599c..d28a7cc048 100644 --- a/indra/newview/llsurfacepatch.cpp +++ b/indra/newview/llsurfacepatch.cpp @@ -230,8 +230,8 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 const F32 xyScaleInv = (1.f / xyScale)*(0.2222222222f); F32 vec[3] = { - fmod((F32)(mOriginGlobal.mdV[0] + x)*xyScaleInv, 256.f), - fmod((F32)(mOriginGlobal.mdV[1] + y)*xyScaleInv, 256.f), + (F32)fmod((F32)(mOriginGlobal.mdV[0] + x)*xyScaleInv, 256.f), + (F32)fmod((F32)(mOriginGlobal.mdV[1] + y)*xyScaleInv, 256.f), 0.f }; F32 rand_val = llclamp(noise2(vec)* 0.75f + 0.5f, 0.f, 1.f); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index f5b06fbd19..697199df6b 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -7906,7 +7906,7 @@ void handle_web_browser_test(const LLSD& param) void handle_web_content_test(const LLSD& param) { std::string url = param.asString(); - LLWeb::loadURLInternal(url); + LLWeb::loadURLInternal(url, LLStringUtil::null, LLStringUtil::null, true); } void handle_show_url(const LLSD& param) diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index b37e41fb85..8026dc3ea8 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -104,10 +104,10 @@ void LLWeb::loadURL(const std::string& url, const std::string& target, const std // static // Explicitly open a Web URL using the Web content floater -void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid) +void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid, bool dev_mode) { LLFloaterWebContent::Params p; - p.url(url).target(target).id(uuid); + p.url(url).target(target).id(uuid).dev_mode(dev_mode); LLFloaterReg::showInstance("web_content", p); } diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h index 7c90badbfe..7149ce9baf 100644 --- a/indra/newview/llweb.h +++ b/indra/newview/llweb.h @@ -57,7 +57,7 @@ public: static void loadURL(const std::string& url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); // load content using built-in browser - static void loadURLInternal(const std::string &url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); + static void loadURLInternal(const std::string &url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null, bool dev_mode = false); /// Returns escaped url (eg, " " to "%20") - used by all loadURL methods static std::string escapeURL(const std::string& url); diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 11d3706821..cee47a591e 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -957,10 +957,10 @@ void LLWorld::updateWaterObjects() center_y = min_y + (wy >> 1); S32 add_boundary[4] = { - 512 - (max_x - region_x), - 512 - (max_y - region_y), - 512 - (region_x - min_x), - 512 - (region_y - min_y) }; + (S32)(512 - (max_x - region_x)), + (S32)(512 - (max_y - region_y)), + (S32)(512 - (region_x - min_x)), + (S32)(512 - (region_y - min_y)) }; S32 dir; for (dir = 0; dir < 8; dir++) diff --git a/indra/newview/skins/default/textures/icons/Video_URL_Off.png b/indra/newview/skins/default/textures/icons/Video_URL_Off.png Binary files differnew file mode 100644 index 0000000000..40e5df7d81 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Video_URL_Off.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index a9a4913b21..670410c3d4 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -676,7 +676,10 @@ with the same filename but different name <texture name="Unread_Chiclet" file_name="bottomtray/Unread_Chiclet.png" preload="false" /> <texture name="UpArrow_Off" file_name="icons/UpArrow_Off.png" preload="false" /> - <texture name="Vertical Drag Handle" file_name="widgets/vertical_drag_handle.png" scale.left="2" scale.right="7" scale.bottom="8" scale.top="120" scale_type="scale_outer"/> + + <texture name="Video_URL_Off" file_name="icons/Video_URL_Off.png" preload="true" /> + + <texture name="Vertical Drag Handle" file_name="widgets/vertical_drag_handle.png" scale.left="2" scale.right="7" scale.bottom="8" scale.top="120" scale_type="scale_outer"/> <texture name="Volume_Background" file_name="windows/Volume_Background.png" preload="false" scale.left="6" scale.top="33" scale.right="63" scale.bottom="10" /> diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml index a80440e844..4473ce0cda 100644 --- a/indra/newview/skins/default/xui/en/floater_web_content.xml +++ b/indra/newview/skins/default/xui/en/floater_web_content.xml @@ -154,6 +154,136 @@ </button> </layout_panel> <layout_panel + height="22" + layout="topleft" + left_delta="0" + name="debug_controls" + top_delta="0" + auto_resize="false" + width="585"> + <button + image_overlay="Home_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="Web tests home page" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="1" + name="web_test_home_page" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/ceftests.html"/> + </button> + + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="MPEG4 Video Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="27" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/ss.mp4"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="MKV Video Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="51" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/jellyfish.mkv"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="WebM Video Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="75" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/jumprope.webm"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="MP3 audio Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="99" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/alegria.mp3"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="FLV Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="123" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/vandal.flv"/> + </button> + </layout_panel> + <layout_panel height="40" layout="topleft" left_delta="0" diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index dcf2da52f1..a39ee5fddd 100644 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -225,11 +225,11 @@ parameter="message_critical" /> </menu_item_call> <menu_item_call - label="Web Content Floater Debug Test" - name="Web Content Floater Debug Test"> + label="Media Browser" + name="Media Browser"> <menu_item_call.on_click function="Advanced.WebContentTest" - parameter="https://callum-linden.s3.amazonaws.com/ceftests.html"/> + parameter="http://google.com"/> </menu_item_call> <menu create_jump_keys="true" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index b189d1038f..8a649a57d1 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3141,30 +3141,13 @@ label="UI" name="UI" tear_off="true"> - <!-- <menu_item_check - label="New Bottom Bar" - name="New Bottom Bar"> - <menu_item_check.on_check - function="CheckControl" - parameter="BottomPanelNew" /> - <menu_item_check.on_click - function="ToggleControl" - parameter="BottomPanelNew" /> - </menu_item_check>--> - <menu_item_call - label="Media Browser Test" - name="Web Browser Test"> - <menu_item_call.on_click - function="Advanced.WebBrowserTest" - parameter="http://secondlife.com/app/search/slurls.html"/> - </menu_item_call> <menu_item_call - label="Web Content Browser" - name="Web Content Browser" - shortcut="control|shift|Z"> + label="Media Browser" + name="Media Browser" + shortcut="control|alt|shift|Z"> <menu_item_call.on_click function="Advanced.WebContentTest" - parameter="https://callum-linden.s3.amazonaws.com/ceftests.html"/> + parameter="http://google.com"/> </menu_item_call> <menu_item_call label="FB Connect Test" diff --git a/indra/newview/skins/default/xui/en/mime_types.xml b/indra/newview/skins/default/xui/en/mime_types.xml index 7cb4a6e53b..c27fac6731 100644 --- a/indra/newview/skins/default/xui/en/mime_types.xml +++ b/indra/newview/skins/default/xui/en/mime_types.xml @@ -130,10 +130,21 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </scheme> - <mimetype name="blank"> + <scheme name="libvlc"> + <label name="libvlc_label"> + LibVLC supported media + </label> + <widgettype> + movie + </widgettype> + <impl> + media_plugin_libvlc + </impl> + </scheme> + <mimetype name="blank"> <label name="blank_label"> - None - </label> @@ -163,7 +174,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/*"> @@ -174,7 +185,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="image/*"> @@ -196,7 +207,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="application/javascript"> @@ -218,7 +229,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> <mimetype name="application/pdf"> @@ -295,7 +306,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> <mimetype name="audio/mpeg"> @@ -306,7 +317,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-aiff"> @@ -317,7 +328,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-wav"> @@ -328,7 +339,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="image/bmp"> @@ -438,7 +449,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/mp4"> @@ -449,10 +460,21 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> - <mimetype menu="1" name="video/quicktime"> + <mimetype name="application/octet-stream"> + <label name="video/octet-stream"> + Movie + </label> + <widgettype> + movie + </widgettype> + <impl> + media_plugin_libvlc + </impl> + </mimetype> + <mimetype menu="1" name="video/quicktime"> <label name="video/quicktime_label"> Movie (QuickTime) </label> @@ -460,7 +482,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-asf"> @@ -471,7 +493,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-wmv"> @@ -482,7 +504,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="video/x-msvideo"> @@ -493,7 +515,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> </mimetypes> diff --git a/indra/newview/skins/default/xui/en/mime_types_linux.xml b/indra/newview/skins/default/xui/en/mime_types_linux.xml index 84aeaf3b54..7188b1e699 100644 --- a/indra/newview/skins/default/xui/en/mime_types_linux.xml +++ b/indra/newview/skins/default/xui/en/mime_types_linux.xml @@ -130,7 +130,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </scheme> <mimetype name="blank"> @@ -163,7 +163,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/*"> @@ -174,7 +174,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="image/*"> @@ -196,7 +196,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="application/javascript"> @@ -218,7 +218,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="application/pdf"> @@ -295,7 +295,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/mpeg"> @@ -306,7 +306,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-aiff"> @@ -317,7 +317,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-wav"> @@ -328,7 +328,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="image/bmp"> @@ -438,7 +438,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/mp4"> @@ -449,7 +449,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="video/quicktime"> @@ -460,7 +460,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-asf"> @@ -471,7 +471,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-wmv"> @@ -482,7 +482,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="video/x-msvideo"> @@ -493,7 +493,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> </mimetypes> diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml index eb67d07601..068e4420bc 100644 --- a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml +++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml @@ -374,9 +374,11 @@ layout="topleft" tool_tip="Movie play progress" width="200"> - <slider_bar.commit_callback - function="MediaCtrl.JumpProgress" /> - </slider_bar> + <slider_bar.mouse_down_callback + function="MediaCtrl.MouseDown" /> + <slider_bar.mouse_up_callback + function="MediaCtrl.MouseUp" /> + </slider_bar> </layout_panel> <layout_panel name="skip_back" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 8988c3e028..b70ed5c306 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -51,13 +51,14 @@ OpenGL Version: [OPENGL_VERSION] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] LLCEFLib/CEF Version: [LLCEFLIB_VERSION] +LibVLC Version: [LIBVLC_VERSION] Voice Server Version: [VOICE_VERSION] </string> <string name="AboutTraffic">Packets Lost: [PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number,1]%)</string> <string name="AboutTime">[month, datetime, slt] [day, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string> <string name="ErrorFetchingServerReleaseNotesURL">Error fetching server release notes URL.</string> <string name="BuildConfiguration">Build Configuration</string> - + <!-- progress --> <string name="ProgressRestoring">Restoring...</string> <string name="ProgressChangingResolution">Changing resolution...</string> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index f3d89bb866..66d730d1ac 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -444,6 +444,11 @@ class Windows_i686_Manifest(ViewerManifest): self.path("media_plugin_cef.dll") self.end_prefix() + # Media plugins - LibVLC + if self.prefix(src='../media_plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): + self.path("media_plugin_libvlc.dll") + self.end_prefix() + # winmm.dll shim if self.prefix(src='../media_plugins/winmmshim/%s' % self.args['configuration'], dst=""): self.path("winmm.dll") @@ -550,6 +555,12 @@ class Windows_i686_Manifest(ViewerManifest): self.path("zh-TW.pak") self.end_prefix() + if self.prefix(src=os.path.join(os.pardir, 'packages', 'bin', 'release'), dst="llplugin"): + self.path("libvlc.dll") + self.path("libvlccore.dll") + self.path("plugins/") + self.end_prefix() + # pull in the crash logger and updater from other projects # tag:"crash-logger" here as a cue to the exporter self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'], @@ -1089,8 +1100,18 @@ class LinuxManifest(ViewerManifest): # plugins if self.prefix(src="", dst="bin/llplugin"): self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") + self.path("../media_plugins/libvlc/libmedia_plugin_libvlc.so", "libmedia_plugin_libvlc.so") self.end_prefix("bin/llplugin") + if self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"): + self.path( "plugins.dat" ) + self.path( "*/*.so" ) + self.end_prefix() + + if self.prefix(src=os.path.join(os.pardir, 'packages', 'lib' ), dst="lib"): + self.path( "libvlc*.so*" ) + self.end_prefix() + # llcommon if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): print "Skipping llcommon.so (assuming llcommon was linked statically)" @@ -1144,7 +1165,7 @@ class LinuxManifest(ViewerManifest): def strip_binaries(self): if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer(): print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build" - self.run_command(r"find %(d)r/bin %(d)r/lib -type f \! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure + self.run_command(r"find %(d)r/bin %(d)r/lib -type f \! -name update_install \! -name *.dat | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure class Linux_i686_Manifest(LinuxManifest): def construct(self): diff --git a/indra/test/llapp_tut.cpp b/indra/test/llapp_tut.cpp index aa5c0672e6..98e714a497 100644 --- a/indra/test/llapp_tut.cpp +++ b/indra/test/llapp_tut.cpp @@ -41,7 +41,7 @@ namespace tut public: virtual bool init() { return true; } virtual bool cleanup() { return true; } - virtual bool mainLoop() { return true; } + virtual bool frame() { return true; } }; LLTestApp* mApp; application() diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 53d4acc9e0..c767d52c7b 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -42,6 +42,8 @@ #include "llevents.h" #include "lleventfilter.h" #include "lleventcoro.h" +#include "llexception.h" +#include "stringize.h" //********************* // LLLogin @@ -128,30 +130,23 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params) void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) { - try - { - LLSD printable_params = login_params; - //if(printable_params.has("params") - // && printable_params["params"].has("passwd")) - //{ - // printable_params["params"]["passwd"] = "*******"; - //} - LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName() + LLSD printable_params = login_params; + if (printable_params.has("params") + && printable_params["params"].has("passwd")) + { + printable_params["params"]["passwd"] = "*******"; + } + try + { + LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName() << " with uri '" << uri << "', parameters " << printable_params << LL_ENDL; - // Arriving in SRVRequest state - LLEventStream replyPump("SRVreply", true); - // Should be an array of one or more uri strings. - LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction")); // EXT-4193: use a DIFFERENT reply pump than for the SRV request. We used // to share them -- but the EXT-3934 fix made it possible for an abandoned // SRV response to arrive just as we were expecting the XMLRPC response. LLEventStream loginReplyPump("loginreply", true); - // Loop through the rewrittenURIs, counting attempts along the way. - // Because of possible redirect responses, we may make more than one - // attempt per rewrittenURIs entry. LLSD::Integer attempts = 0; LLSD request(login_params); @@ -167,11 +162,11 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) LLSD progress_data; progress_data["attempt"] = attempts; progress_data["request"] = request; - if(progress_data["request"].has("params") - && progress_data["request"]["params"].has("passwd")) - { - progress_data["request"]["params"]["passwd"] = "*******"; - } + if (progress_data["request"].has("params") + && progress_data["request"]["params"].has("passwd")) + { + progress_data["request"]["params"]["passwd"] = "*******"; + } sendProgressEvent("offline", "authenticating", progress_data); // We expect zero or more "Downloading" status events, followed by @@ -189,8 +184,8 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) // Still Downloading -- send progress update. sendProgressEvent("offline", "downloading"); } - - LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL; + + LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL; status = mAuthResponse["status"].asString(); // Okay, we've received our final status event for this @@ -202,7 +197,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) break; } - sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]); + sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]); // Here the login service at the current URI is redirecting us // to some other URI ("indeterminate" -- why not "redirect"?). @@ -212,8 +207,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) request["method"] = mAuthResponse["responses"]["next_method"].asString(); } // loop back to try the redirected URI - // Here we're done with redirects for the current rewrittenURIs - // entry. + // Here we're done with redirects. if (status == "Complete") { // StatusComplete does not imply auth success. Check the @@ -230,14 +224,14 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) return; // Done! } -// /* Sometimes we end with "Started" here. Slightly slow server? -// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. -// */ -// if( status == "Started") -// { -// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; -// continue; -// } +// /* Sometimes we end with "Started" here. Slightly slow server? +// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. +// */ +// if( status == "Started") +// { +// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; +// continue; +// } // If we don't recognize status at all, trouble if (! (status == "CURLError" @@ -250,27 +244,25 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) } // Here status IS one of the errors tested above. - - // Here we got through all the rewrittenURIs without succeeding. Tell - // caller this didn't work out so well. Of course, the only failure data - // we can reasonably show are from the last of the rewrittenURIs. - - // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an - // llsd with no "responses" node. To make the output from an incomplete login symmetrical - // to success, add a data/message and data/reason fields. - LLSD error_response; - error_response["reason"] = mAuthResponse["status"]; - error_response["errorcode"] = mAuthResponse["errorcode"]; - error_response["message"] = mAuthResponse["error"]; - if(mAuthResponse.has("certificate")) - { - error_response["certificate"] = mAuthResponse["certificate"]; - } - sendProgressEvent("offline", "fail.login", error_response); - } - catch (...) { - LL_ERRS() << "login exception caught" << LL_ENDL; - } + // Tell caller this didn't work out so well. + + // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an + // llsd with no "responses" node. To make the output from an incomplete login symmetrical + // to success, add a data/message and data/reason fields. + LLSD error_response; + error_response["reason"] = mAuthResponse["status"]; + error_response["errorcode"] = mAuthResponse["errorcode"]; + error_response["message"] = mAuthResponse["error"]; + if(mAuthResponse.has("certificate")) + { + error_response["certificate"] = mAuthResponse["certificate"]; + } + sendProgressEvent("offline", "fail.login", error_response); + } + catch (...) { + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + << "('" << uri << "', " << printable_params << ")")); + } } void LLLogin::Impl::disconnect() diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp index 382689afa0..04e0395c50 100644 --- a/indra/viewer_components/updater/llupdatedownloader.cpp +++ b/indra/viewer_components/updater/llupdatedownloader.cpp @@ -27,7 +27,7 @@ #include "llupdatedownloader.h" #include "httpcommon.h" -#include <stdexcept> +#include "llexception.h" #include <boost/format.hpp> #include <boost/lexical_cast.hpp> #include <curl/curl.h> @@ -85,11 +85,11 @@ private: namespace { class DownloadError: - public std::runtime_error + public LLException { public: DownloadError(const char * message): - std::runtime_error(message) + LLException(message) { ; // No op. } @@ -467,7 +467,7 @@ void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & u if(!mCurl) { - throw DownloadError("failed to initialize curl"); + LLTHROW(DownloadError("failed to initialize curl")); } throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_NOSIGNAL, true)); throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_FOLLOWLOCATION, true)); @@ -508,7 +508,7 @@ void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte) mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str()); if(mHeaderList == 0) { - throw DownloadError("cannot add Range header"); + LLTHROW(DownloadError("cannot add Range header")); } throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HTTPHEADER, mHeaderList)); @@ -524,7 +524,7 @@ void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std mDownloadData["hash"] = hash; mDownloadData["current_version"] = ll_get_version(); LLSD path = uri.pathArray(); - if(path.size() == 0) throw DownloadError("no file path"); + if(path.size() == 0) LLTHROW(DownloadError("no file path")); std::string fileName = path[path.size() - 1].asString(); std::string filePath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, fileName); mDownloadData["path"] = filePath; @@ -547,9 +547,9 @@ void LLUpdateDownloader::Implementation::throwOnCurlError(CURLcode code) if(code != CURLE_OK) { const char * errorString = curl_easy_strerror(code); if(errorString != 0) { - throw DownloadError(curl_easy_strerror(code)); + LLTHROW(DownloadError(curl_easy_strerror(code))); } else { - throw DownloadError("unknown curl error"); + LLTHROW(DownloadError("unknown curl error")); } } else { ; // No op. diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp index a0e2c0b362..1c7629da23 100644 --- a/indra/viewer_components/updater/llupdateinstaller.cpp +++ b/indra/viewer_components/updater/llupdateinstaller.cpp @@ -30,23 +30,25 @@ #include "llupdateinstaller.h" #include "lldir.h" #include "llsd.h" +#include "llexception.h" #if defined(LL_WINDOWS) #pragma warning(disable: 4702) // disable 'unreachable code' so we can use lexical_cast (really!). #endif #include <boost/lexical_cast.hpp> - namespace { - class RelocateError {}; - - + struct RelocateError: public LLException + { + RelocateError(): LLException("llupdateinstaller: RelocateError") {} + }; + std::string copy_to_temp(std::string const & path) { std::string scriptFile = gDirUtilp->getBaseFileName(path); std::string newPath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, scriptFile); apr_status_t status = apr_file_copy(path.c_str(), newPath.c_str(), APR_FILE_SOURCE_PERMS, gAPRPoolp); - if(status != APR_SUCCESS) throw RelocateError(); + if(status != APR_SUCCESS) LLTHROW(RelocateError()); return newPath; } diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp index 788955a1b2..1665e41e70 100644 --- a/indra/viewer_components/updater/llupdaterservice.cpp +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -32,6 +32,7 @@ #include "lltimer.h" #include "llupdatechecker.h" #include "llupdateinstaller.h" +#include "llexception.h" #include <boost/scoped_ptr.hpp> #include <boost/weak_ptr.hpp> @@ -190,8 +191,8 @@ void LLUpdaterServiceImpl::initialize(const std::string& channel, { if(mIsChecking || mIsDownloading) { - throw LLUpdaterService::UsageError("LLUpdaterService::initialize call " - "while updater is running."); + LLTHROW(LLUpdaterService::UsageError("LLUpdaterService::initialize call " + "while updater is running.")); } mChannel = channel; @@ -222,8 +223,8 @@ void LLUpdaterServiceImpl::startChecking(bool install_if_ready) { if(mChannel.empty() || mVersion.empty()) { - throw LLUpdaterService::UsageError("Set params before call to " - "LLUpdaterService::startCheck()."); + LLTHROW(LLUpdaterService::UsageError("Set params before call to " + "LLUpdaterService::startCheck().")); } mIsChecking = true; diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h index 95bbe1695c..78e8c6b290 100644 --- a/indra/viewer_components/updater/llupdaterservice.h +++ b/indra/viewer_components/updater/llupdaterservice.h @@ -29,16 +29,17 @@ #include <boost/shared_ptr.hpp> #include <boost/function.hpp> #include "llhasheduniqueid.h" +#include "llexception.h" class LLUpdaterServiceImpl; class LLUpdaterService { public: - class UsageError: public std::runtime_error + class UsageError: public LLException { public: - UsageError(const std::string& msg) : std::runtime_error(msg) {} + UsageError(const std::string& msg) : LLException(msg) {} }; // Name of the event pump through which update events will be delivered. diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp index 23c29e6b18..167acf6ac7 100644 --- a/indra/win_crash_logger/llcrashloggerwindows.cpp +++ b/indra/win_crash_logger/llcrashloggerwindows.cpp @@ -454,7 +454,7 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles() //mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo(); //Not initialized. } -bool LLCrashLoggerWindows::mainLoop() +bool LLCrashLoggerWindows::frame() { LL_INFOS() << "CrashSubmitBehavior is " << mCrashBehavior << LL_ENDL; @@ -503,14 +503,14 @@ bool LLCrashLoggerWindows::mainLoop() TranslateMessage(&msg); DispatchMessage(&msg); } - return msg.wParam; + return true; // msg.wParam; } else { LL_WARNS() << "Unknown crash behavior " << mCrashBehavior << LL_ENDL; - return 1; + return true; // 1; } - return 0; + return true; // 0; } void LLCrashLoggerWindows::updateApplication(const std::string& message) diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h index 1812e2737e..f89b8708dc 100644 --- a/indra/win_crash_logger/llcrashloggerwindows.h +++ b/indra/win_crash_logger/llcrashloggerwindows.h @@ -46,7 +46,7 @@ public: static LLCrashLoggerWindows* sInstance; virtual bool init(); - virtual bool mainLoop(); + virtual bool frame(); virtual void updateApplication(const std::string& message = LLStringUtil::null); virtual bool cleanup(); virtual void gatherPlatformSpecificFiles(); diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp index 366edd894b..7466dbb766 100644 --- a/indra/win_crash_logger/win_crash_logger.cpp +++ b/indra/win_crash_logger/win_crash_logger.cpp @@ -52,7 +52,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, } app.processingLoop(); - app.mainLoop(); + app.frame(); app.cleanup(); LL_INFOS() << "Crash reporter finished normally." << LL_ENDL; return 0; |