diff options
Diffstat (limited to 'indra/media_plugins/cef/media_plugin_cef.cpp')
-rw-r--r-- | indra/media_plugins/cef/media_plugin_cef.cpp | 2294 |
1 files changed, 1147 insertions, 1147 deletions
diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index d5e17ba536..3aa73ed9a2 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -1,1147 +1,1147 @@ -/** -* @file media_plugin_cef.cpp -* @brief CEF (Chromium Embedding Framework) 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 "indra_constants.h" // for indra keyboard codes - -#include "llglheaders.h" // for GL_* constants -#include "llsdutil.h" -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" -#include "llstring.h" -#include "volume_catcher.h" -#include "media_plugin_base.h" - -#include "dullahan.h" - -//////////////////////////////////////////////////////////////////////////////// -// -class MediaPluginCEF : - public MediaPluginBase -{ -public: - MediaPluginCEF(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginCEF(); - - /*virtual*/ - void receiveMessage(const char* message_string); - -private: - bool init(); - - void onPageChangedCallback(const unsigned char* pixels, int x, int y, const int width, const int height); - void onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect); - void onConsoleMessageCallback(std::string message, std::string source, int line); - void onStatusMessageCallback(std::string value); - void onTitleChangeCallback(std::string title); - void onTooltipCallback(std::string text); - void onLoadStartCallback(); - void onRequestExitCallback(); - void onLoadEndCallback(int httpStatusCode, std::string url); - void onLoadError(int status, const std::string error_text); - void onAddressChangeCallback(std::string url); - void onOpenPopupCallback(std::string url, std::string target); - bool onHTTPAuthCallback(const std::string host, const std::string realm, std::string& username, std::string& password); - void onCursorChangedCallback(dullahan::ECursorType type); - const std::vector<std::string> onFileDialog(dullahan::EFileDialogType dialog_type, const std::string dialog_title, const std::string default_file, const std::string dialog_accept_filter, bool& use_default); - bool onJSDialogCallback(const std::string origin_url, const std::string message_text, const std::string default_prompt_text); - bool onJSBeforeUnloadCallback(); - - void postDebugMessage(const std::string& msg); - void authResponse(LLPluginMessage &message); - - void keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_data); - void unicodeInput(std::string event, LLSD native_key_data); - - void checkEditState(); - void setVolume(); - - bool mEnableMediaPluginDebugging; - std::string mHostLanguage; - bool mCookiesEnabled; - bool mPluginsEnabled; - bool mJavascriptEnabled; - bool mProxyEnabled; - std::string mProxyHost; - int mProxyPort; - bool mDisableGPU; - bool mDisableNetworkService; - bool mUseMockKeyChain; - bool mDisableWebSecurity; - bool mFileAccessFromFileUrls; - std::string mUserAgentSubtring; - std::string mAuthUsername; - std::string mAuthPassword; - bool mAuthOK; - bool mCanCut; - bool mCanCopy; - bool mCanPaste; - std::string mRootCachePath; - std::string mCachePath; - std::string mContextCachePath; - std::string mCefLogFile; - bool mCefLogVerbose; - std::vector<std::string> mPickedFiles; - VolumeCatcher mVolumeCatcher; - F32 mCurVolume; - dullahan* mCEFLib; -}; - -//////////////////////////////////////////////////////////////////////////////// -// -MediaPluginCEF::MediaPluginCEF(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : -MediaPluginBase(host_send_func, host_user_data) -{ - mWidth = 0; - mHeight = 0; - mDepth = 4; - mPixels = 0; - mEnableMediaPluginDebugging = true; - mHostLanguage = "en"; - mCookiesEnabled = true; - mPluginsEnabled = false; - mJavascriptEnabled = true; - mProxyEnabled = false; - mProxyHost = ""; - mProxyPort = 0; - mDisableGPU = false; - mDisableNetworkService = true; - mUseMockKeyChain = true; - mDisableWebSecurity = false; - mFileAccessFromFileUrls = false; - mUserAgentSubtring = ""; - mAuthUsername = ""; - mAuthPassword = ""; - mAuthOK = false; - mCanCut = false; - mCanCopy = false; - mCanPaste = false; - mCachePath = ""; - mCefLogFile = ""; - mCefLogVerbose = false; - mPickedFiles.clear(); - mCurVolume = 0.0; - - mCEFLib = new dullahan(); - - setVolume(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -MediaPluginCEF::~MediaPluginCEF() -{ - mCEFLib->shutdown(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::postDebugMessage(const std::string& msg) -{ - if (mEnableMediaPluginDebugging) - { - std::stringstream str; - str << "@Media Msg> " << msg; - - LLPluginMessage debug_message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "debug_message"); - debug_message.setValue("message_text", str.str()); - debug_message.setValue("message_level", "info"); - sendMessage(debug_message); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onPageChangedCallback(const unsigned char* pixels, int x, int y, const int width, const int height) -{ - if( mPixels && pixels ) - { - if (mWidth == width && mHeight == height) - { - memcpy(mPixels, pixels, mWidth * mHeight * mDepth); - } - else - { - mCEFLib->setSize(mWidth, mHeight); - } - setDirty(0, 0, mWidth, mHeight); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onConsoleMessageCallback(std::string message, std::string source, int line) -{ - std::stringstream str; - str << "Console message: " << message << " in file(" << source << ") at line " << line; - postDebugMessage(str.str()); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onStatusMessageCallback(std::string value) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text"); - message.setValue("status", value); - sendMessage(message); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onTitleChangeCallback(std::string title) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); - message.setValue("name", title); - message.setValueBoolean("history_back_available", mCEFLib->canGoBack()); - message.setValueBoolean("history_forward_available", mCEFLib->canGoForward()); - sendMessage(message); -} - -void MediaPluginCEF::onTooltipCallback(std::string text) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "tooltip_text"); - message.setValue("tooltip", text); - sendMessage(message); -} -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onLoadStartCallback() -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); - //message.setValue("uri", event.getEventUri()); // not easily available here in CEF - needed? - message.setValueBoolean("history_back_available", mCEFLib->canGoBack()); - message.setValueBoolean("history_forward_available", mCEFLib->canGoForward()); - sendMessage(message); -} - -///////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onLoadError(int status, const std::string error_text) -{ - std::stringstream msg; - - msg << "<b>Loading error!</b>"; - msg << "<p>"; - msg << "Message: " << error_text; - msg << "<br>"; - msg << "Code: " << status; - - mCEFLib->showBrowserMessage(msg.str()); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onRequestExitCallback() -{ - LLPluginMessage message("base", "goodbye"); - sendMessage(message); - - // Will trigger delete on next staticReceiveMessage() - mDeleteMe = true; -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onLoadEndCallback(int httpStatusCode, std::string url) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); - //message.setValue("uri", event.getEventUri()); // not easily available here in CEF - needed? - message.setValueS32("result_code", httpStatusCode); - message.setValueBoolean("history_back_available", mCEFLib->canGoBack()); - message.setValueBoolean("history_forward_available", mCEFLib->canGoForward()); - message.setValue("uri", url); - sendMessage(message); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onAddressChangeCallback(std::string url) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed"); - message.setValue("uri", url); - sendMessage(message); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onOpenPopupCallback(std::string url, std::string target) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href"); - message.setValue("uri", url); - message.setValue("target", target); - sendMessage(message); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); - message.setValue("uri", url); - - // indicate if this interaction was from a user click (okay on a SLAPP) or - // via a navigation (e.g. a data URL - see SL-18151) (not okay on a SLAPP) - const std::string nav_type = user_gesture ? "clicked" : "navigated"; - - message.setValue("nav_type", nav_type); - message.setValueBoolean("is_redirect", is_redirect); - - sendMessage(message); -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool MediaPluginCEF::onHTTPAuthCallback(const std::string host, const std::string realm, std::string& username, std::string& password) -{ - mAuthOK = false; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "auth_request"); - message.setValue("url", host); - message.setValue("realm", realm); - message.setValueBoolean("blocking_request", true); - - // The "blocking_request" key in the message means this sendMessage call will block until a response is received. - sendMessage(message); - - if (mAuthOK) - { - username = mAuthUsername; - password = mAuthPassword; - } - - return mAuthOK; -} - -//////////////////////////////////////////////////////////////////////////////// -// -const std::vector<std::string> MediaPluginCEF::onFileDialog(dullahan::EFileDialogType dialog_type, const std::string dialog_title, const std::string default_file, std::string dialog_accept_filter, bool& use_default) -{ - // do not use the default CEF file picker - use_default = false; - - if (dialog_type == dullahan::FD_OPEN_FILE) - { - mPickedFiles.clear(); - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file"); - message.setValueBoolean("blocking_request", true); - message.setValueBoolean("multiple_files", false); - - sendMessage(message); - - return mPickedFiles; - } - else if (dialog_type == dullahan::FD_OPEN_MULTIPLE_FILES) - { - mPickedFiles.clear(); - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file"); - message.setValueBoolean("blocking_request", true); - message.setValueBoolean("multiple_files", true); - - sendMessage(message); - - return mPickedFiles; - } - else if (dialog_type == dullahan::FD_SAVE_FILE) - { - mPickedFiles.clear(); - mAuthOK = false; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "file_download"); - message.setValueBoolean("blocking_request", true); - message.setValue("filename", default_file); - - sendMessage(message); - - return mPickedFiles; - } - - return std::vector<std::string>(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool MediaPluginCEF::onJSDialogCallback(const std::string origin_url, const std::string message_text, const std::string default_prompt_text) -{ - // return true indicates we suppress the JavaScript alert UI entirely - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool MediaPluginCEF::onJSBeforeUnloadCallback() -{ - // return true indicates we suppress the JavaScript UI entirely - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::onCursorChangedCallback(dullahan::ECursorType type) -{ - std::string name = ""; - - switch (type) - { - case dullahan::CT_POINTER: - name = "UI_CURSOR_ARROW"; - break; - case dullahan::CT_CROSS: - name = "UI_CURSOR_CROSS"; - break; - case dullahan::CT_HAND: - name = "UI_CURSOR_HAND"; - break; - case dullahan::CT_IBEAM: - name = "UI_CURSOR_IBEAM"; - break; - case dullahan::CT_WAIT: - name = "UI_CURSOR_WAIT"; - break; - //case dullahan::CT_HELP: - case dullahan::CT_ROWRESIZE: - case dullahan::CT_NORTHRESIZE: - case dullahan::CT_SOUTHRESIZE: - case dullahan::CT_NORTHSOUTHRESIZE: - name = "UI_CURSOR_SIZENS"; - break; - case dullahan::CT_COLUMNRESIZE: - case dullahan::CT_EASTRESIZE: - case dullahan::CT_WESTRESIZE: - case dullahan::CT_EASTWESTRESIZE: - name = "UI_CURSOR_SIZEWE"; - break; - case dullahan::CT_NORTHEASTRESIZE: - case dullahan::CT_SOUTHWESTRESIZE: - case dullahan::CT_NORTHEASTSOUTHWESTRESIZE: - name = "UI_CURSOR_SIZENESW"; - break; - case dullahan::CT_SOUTHEASTRESIZE: - case dullahan::CT_NORTHWESTRESIZE: - case dullahan::CT_NORTHWESTSOUTHEASTRESIZE: - name = "UI_CURSOR_SIZENWSE"; - break; - case dullahan::CT_MOVE: - name = "UI_CURSOR_SIZEALL"; - break; - //case dullahan::CT_MIDDLEPANNING: - //case dullahan::CT_EASTPANNING: - //case dullahan::CT_NORTHPANNING: - //case dullahan::CT_NORTHEASTPANNING: - //case dullahan::CT_NORTHWESTPANNING: - //case dullahan::CT_SOUTHPANNING: - //case dullahan::CT_SOUTHEASTPANNING: - //case dullahan::CT_SOUTHWESTPANNING: - //case dullahan::CT_WESTPANNING: - //case dullahan::CT_VERTICALTEXT: - //case dullahan::CT_CELL: - //case dullahan::CT_CONTEXTMENU: - case dullahan::CT_ALIAS: - name = "UI_CURSOR_TOOLMEDIAOPEN"; - break; - case dullahan::CT_PROGRESS: - name = "UI_CURSOR_WORKING"; - break; - case dullahan::CT_COPY: - name = "UI_CURSOR_ARROWCOPY"; - break; - case dullahan::CT_NONE: - name = "UI_CURSOR_NO"; - break; - case dullahan::CT_NODROP: - case dullahan::CT_NOTALLOWED: - name = "UI_CURSOR_NOLOCKED"; - break; - case dullahan::CT_ZOOMIN: - name = "UI_CURSOR_TOOLZOOMIN"; - break; - case dullahan::CT_ZOOMOUT: - name = "UI_CURSOR_TOOLZOOMOUT"; - break; - case dullahan::CT_GRAB: - name = "UI_CURSOR_TOOLGRAB"; - break; - //case dullahan::CT_GRABING: - //case dullahan::CT_CUSTOM: - - default: - LL_WARNS() << "Unknown cursor ID: " << (int)type << LL_ENDL; - break; - } - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed"); - message.setValue("name", name); - sendMessage(message); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::authResponse(LLPluginMessage &message) -{ - mAuthOK = message.getValueBoolean("ok"); - if (mAuthOK) - { - mAuthUsername = message.getValue("username"); - mAuthPassword = message.getValue("password"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::receiveMessage(const char* message_string) -{ - // std::cerr << "MediaPluginCEF::receiveMessage: received message: \"" << message_string << "\"" << std::endl; - 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") - { - 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_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; - message.setValueLLSD("versions", versions); - - std::string plugin_version = "CEF plugin 1.1.412"; - message.setValue("plugin_version", plugin_version); - sendMessage(message); - } - else if (message_name == "idle") - { - mCEFLib->update(); - - mVolumeCatcher.pump(); - - // this seems bad but unless the state changes (it won't until we figure out - // how to get CEF to tell us if copy/cut/paste is available) then this function - // will return immediately - checkEditState(); - } - else if (message_name == "cleanup") - { - mCEFLib->requestExit(); - } - else if (message_name == "force_exit") - { - mDeleteMe = true; - } - 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) - { - mPixels = NULL; - mTextureSegmentName.clear(); - } - mSharedSegments.erase(iter); - } - else - { - } - - LLPluginMessage message("base", "shm_remove_response"); - message.setValue("name", name); - sendMessage(message); - } - else - { - } - } - else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) - { - if (message_name == "init") - { - // event callbacks from Dullahan - mCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1)); - mCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1)); - mCEFLib->setOnTooltipCallback(std::bind(&MediaPluginCEF::onTooltipCallback, this, std::placeholders::_1)); - mCEFLib->setOnLoadStartCallback(std::bind(&MediaPluginCEF::onLoadStartCallback, this)); - mCEFLib->setOnLoadEndCallback(std::bind(&MediaPluginCEF::onLoadEndCallback, this, std::placeholders::_1, std::placeholders::_2)); - mCEFLib->setOnLoadErrorCallback(std::bind(&MediaPluginCEF::onLoadError, this, std::placeholders::_1, std::placeholders::_2)); - mCEFLib->setOnAddressChangeCallback(std::bind(&MediaPluginCEF::onAddressChangeCallback, this, std::placeholders::_1)); - mCEFLib->setOnOpenPopupCallback(std::bind(&MediaPluginCEF::onOpenPopupCallback, this, std::placeholders::_1, std::placeholders::_2)); - mCEFLib->setOnHTTPAuthCallback(std::bind(&MediaPluginCEF::onHTTPAuthCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - mCEFLib->setOnFileDialogCallback(std::bind(&MediaPluginCEF::onFileDialog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - mCEFLib->setOnCursorChangedCallback(std::bind(&MediaPluginCEF::onCursorChangedCallback, this, std::placeholders::_1)); - mCEFLib->setOnRequestExitCallback(std::bind(&MediaPluginCEF::onRequestExitCallback, this)); - mCEFLib->setOnJSDialogCallback(std::bind(&MediaPluginCEF::onJSDialogCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - mCEFLib->setOnJSBeforeUnloadCallback(std::bind(&MediaPluginCEF::onJSBeforeUnloadCallback, this)); - - dullahan::dullahan_settings settings; -#if LL_WINDOWS - // As of CEF version 83+, for Windows versions, we need to tell CEF - // where the host helper process is since this DLL is not in the same - // dir as the executable that loaded it (SLPlugin.exe). The code in - // Dullahan that tried to figure out the location automatically uses - // the location of the exe which isn't helpful so we tell it explicitly. - std::vector<wchar_t> buffer(MAX_PATH + 1); - GetCurrentDirectoryW(MAX_PATH, &buffer[0]); - settings.host_process_path = ll_convert_wide_to_string(&buffer[0]); -#endif - settings.accept_language_list = mHostLanguage; - - // SL-15560: Product team overruled my change to set the default - // embedded background color to match the floater background - // and set it to white - settings.background_color = 0xffffffff; // white - - settings.cache_enabled = true; - settings.root_cache_path = mRootCachePath; - settings.cache_path = mCachePath; - settings.context_cache_path = mContextCachePath; - settings.cookies_enabled = mCookiesEnabled; - - // configure proxy argument if enabled and valid - if (mProxyEnabled && mProxyHost.length()) - { - std::ostringstream proxy_url; - proxy_url << mProxyHost << ":" << mProxyPort; - settings.proxy_host_port = proxy_url.str(); - } - settings.disable_gpu = mDisableGPU; -#if LL_DARWIN - settings.disable_network_service = mDisableNetworkService; - settings.use_mock_keychain = mUseMockKeyChain; -#endif - // these were added to facilitate loading images directly into a local - // web page for the prototype 360 project in 2017 - something that is - // disallowed normally by the browser security model. Now the the source - // (cubemap) images are stores as JavaScript, we can avoid opening up - // this security hole (it was only set for the 360 floater but still - // a concern). Leaving them here, explicitly turn off vs removing - // entirely from this source file so that others are aware of them - // in the future. - settings.disable_web_security = false; - settings.file_access_from_file_urls = false; - - settings.flash_enabled = mPluginsEnabled; - - // This setting applies to all plugins, not just Flash - // Regarding, SL-15559 PDF files do not load in CEF v91, - // it turns out that on Windows, PDF support is treated - // as a plugin on Windows only so turning all plugins - // off, disabled built in PDF support. (Works okay in - // macOS surprisingly). To mitigrate this, we set the global - // media enabled flag to whatever the consumer wants and - // explicitly disable Flash with a different setting (below) - settings.plugins_enabled = mPluginsEnabled; - - // SL-14897 Disable Flash support in the embedded browser - settings.flash_enabled = false; - - settings.flip_mouse_y = false; - settings.flip_pixels_y = true; - settings.frame_rate = 60; - settings.force_wave_audio = true; - settings.initial_height = 1024; - settings.initial_width = 1024; - settings.java_enabled = false; - settings.javascript_enabled = mJavascriptEnabled; - settings.media_stream_enabled = false; // MAINT-6060 - WebRTC media removed until we can add granularity/query UI - - settings.user_agent_substring = mCEFLib->makeCompatibleUserAgentString(mUserAgentSubtring); - settings.webgl_enabled = true; - settings.log_file = mCefLogFile; - settings.log_verbose = mCefLogVerbose; - settings.autoplay_without_gesture = true; - - std::vector<std::string> custom_schemes(1, "secondlife"); - mCEFLib->setCustomSchemes(custom_schemes); - - bool result = mCEFLib->init(settings); - if (!result) - { - // if this fails, the media system in viewer will put up a message - } - - // now we can set page zoom factor - F32 factor = (F32)message_in.getValueReal("factor"); -#if LL_DARWIN - //temporary fix for SL-10473: issue with displaying checkboxes on Mojave - factor*=1.001; -#endif - mCEFLib->setPageZoom(factor); - - // Plugin gets to decide the texture parameters to use. - mDepth = 4; - 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); - message.setValueU32("type", GL_UNSIGNED_BYTE); - message.setValueBoolean("coords_opengl", true); - sendMessage(message); - } - else if (message_name == "set_user_data_path") - { - std::string user_data_path_cache = message_in.getValue("cache_path"); - std::string subfolder = message_in.getValue("username"); - - mRootCachePath = user_data_path_cache + "cef_cache"; - if (!subfolder.empty()) - { - std::string delim; -#if LL_WINDOWS - // media plugin doesn't have access to gDirUtilp - delim = "\\"; -#else - delim = "/"; -#endif - mCachePath = mRootCachePath + delim + subfolder; - } - else - { - mCachePath = mRootCachePath; - } - mContextCachePath = ""; // disabled by "" - mCefLogFile = message_in.getValue("cef_log_file"); - mCefLogVerbose = message_in.getValueBoolean("cef_verbose_log"); - } - 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; - - mCEFLib->setSize(mWidth, mHeight); - }; - }; - - 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 == "set_language_code") - { - mHostLanguage = message_in.getValue("language"); - } - else if (message_name == "load_uri") - { - std::string uri = message_in.getValue("uri"); - mCEFLib->navigate(uri); - } - else if (message_name == "execute_javascript") - { - std::string code = message_in.getValue("code"); - mCEFLib->executeJavaScript(code); - } - else if (message_name == "set_cookie") - { - std::string uri = message_in.getValue("uri"); - std::string name = message_in.getValue("name"); - std::string value = message_in.getValue("value"); - std::string domain = message_in.getValue("domain"); - std::string path = message_in.getValue("path"); - bool httponly = message_in.getValueBoolean("httponly"); - bool secure = message_in.getValueBoolean("secure"); - mCEFLib->setCookie(uri, name, value, domain, path, httponly, secure); - } - else if (message_name == "mouse_event") - { - std::string event = message_in.getValue("event"); - - S32 x = message_in.getValueS32("x"); - S32 y = message_in.getValueS32("y"); - - // only even send left mouse button events to the CEF library - // (partially prompted by crash in macOS CEF when sending right button events) - // we catch the right click in viewer and display our own context menu anyway - S32 button = message_in.getValueS32("button"); - dullahan::EMouseButton btn = dullahan::MB_MOUSE_BUTTON_LEFT; - - if (event == "down" && button == 0) - { - mCEFLib->mouseButton(btn, dullahan::ME_MOUSE_DOWN, x, y); - mCEFLib->setFocus(); - - std::stringstream str; - str << "Mouse down at = " << x << ", " << y; - postDebugMessage(str.str()); - } - else if (event == "up" && button == 0) - { - mCEFLib->mouseButton(btn, dullahan::ME_MOUSE_UP, x, y); - - std::stringstream str; - str << "Mouse up at = " << x << ", " << y; - postDebugMessage(str.str()); - } - else if (event == "double_click") - { - mCEFLib->mouseButton(btn, dullahan::ME_MOUSE_DOUBLE_CLICK, x, y); - } - else - { - mCEFLib->mouseMove(x, y); - } - } - else if (message_name == "scroll_event") - { - // Mouse coordinates for cef to be able to scroll 'containers' - S32 x = message_in.getValueS32("x"); - S32 y = message_in.getValueS32("y"); - - // Wheel's clicks - S32 delta_x = message_in.getValueS32("clicks_x"); - S32 delta_y = message_in.getValueS32("clicks_y"); - const int scaling_factor = 40; - delta_x *= -scaling_factor; - delta_y *= -scaling_factor; - - mCEFLib->mouseWheel(x, y, delta_x, delta_y); - } - else if (message_name == "text_event") - { - std::string event = message_in.getValue("event"); - LLSD native_key_data = message_in.getValueLLSD("native_key_data"); - unicodeInput(event, native_key_data); - } - else if (message_name == "key_event") - { -#if LL_DARWIN - std::string event = message_in.getValue("event"); - LLSD native_key_data = message_in.getValueLLSD("native_key_data"); - - dullahan::EKeyEvent key_event = dullahan::KE_KEY_UP; - if (event == "down") - { - key_event = dullahan::KE_KEY_DOWN; - } - else if (event == "repeat") - { - key_event = dullahan::KE_KEY_REPEAT; - } - - keyEvent(key_event, native_key_data); - -#elif LL_WINDOWS - std::string event = message_in.getValue("event"); - LLSD native_key_data = message_in.getValueLLSD("native_key_data"); - - // Treat unknown events as key-up for safety. - dullahan::EKeyEvent key_event = dullahan::KE_KEY_UP; - if (event == "down") - { - key_event = dullahan::KE_KEY_DOWN; - } - else if (event == "repeat") - { - key_event = dullahan::KE_KEY_REPEAT; - } - - keyEvent(key_event, native_key_data); -#endif - } - else if (message_name == "enable_media_plugin_debugging") - { - mEnableMediaPluginDebugging = message_in.getValueBoolean("enable"); - } - if (message_name == "pick_file_response") - { - LLSD file_list_llsd = message_in.getValueLLSD("file_list"); - - LLSD::array_const_iterator iter = file_list_llsd.beginArray(); - LLSD::array_const_iterator end = file_list_llsd.endArray(); - for (; iter != end; ++iter) - { - mPickedFiles.push_back(((*iter).asString())); - } - } - if (message_name == "auth_response") - { - authResponse(message_in); - } - if (message_name == "edit_cut") - { - mCEFLib->editCut(); - } - if (message_name == "edit_copy") - { - mCEFLib->editCopy(); - } - if (message_name == "edit_paste") - { - mCEFLib->editPaste(); - } - } - else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) - { - if (message_name == "set_page_zoom_factor") - { - F32 factor = (F32)message_in.getValueReal("factor"); -#if LL_DARWIN - //temporary fix for SL-10473: issue with displaying checkboxes on Mojave - factor*=1.001; -#endif - mCEFLib->setPageZoom(factor); - } - if (message_name == "browse_stop") - { - mCEFLib->stop(); - } - else if (message_name == "browse_reload") - { - bool ignore_cache = true; - mCEFLib->reload(ignore_cache); - } - else if (message_name == "browse_forward") - { - mCEFLib->goForward(); - } - else if (message_name == "browse_back") - { - mCEFLib->goBack(); - } - else if (message_name == "cookies_enabled") - { - mCookiesEnabled = message_in.getValueBoolean("enable"); - } - else if (message_name == "clear_cookies") - { - mCEFLib->deleteAllCookies(); - } - else if (message_name == "set_user_agent") - { - mUserAgentSubtring = message_in.getValue("user_agent"); - } - else if (message_name == "show_web_inspector") - { - mCEFLib->showDevTools(); - } - else if (message_name == "plugins_enabled") - { - mPluginsEnabled = message_in.getValueBoolean("enable"); - } - else if (message_name == "javascript_enabled") - { - mJavascriptEnabled = message_in.getValueBoolean("enable"); - } - else if (message_name == "gpu_disabled") - { - mDisableGPU = message_in.getValueBoolean("disable"); - } - else if (message_name == "proxy_setup") - { - mProxyEnabled = message_in.getValueBoolean("enable"); - mProxyHost = message_in.getValue("host"); - mProxyPort = message_in.getValueS32("port"); - } - else if (message_name == "web_security_disabled") - { - mDisableWebSecurity = message_in.getValueBoolean("disabled"); - } - else if (message_name == "file_access_from_file_urls") - { - mFileAccessFromFileUrls = message_in.getValueBoolean("enabled"); - } - } - else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) - { - if (message_name == "set_volume") - { - F32 volume = (F32)message_in.getValueReal("volume"); - mCurVolume = volume; - setVolume(); - } - } - else - { - }; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_data = LLSD::emptyMap()) -{ -#if LL_DARWIN - U32 event_modifiers = native_key_data["event_modifiers"].asInteger(); - U32 event_keycode = native_key_data["event_keycode"].asInteger(); - U32 event_chars = native_key_data["event_chars"].asInteger(); - U32 event_umodchars = native_key_data["event_umodchars"].asInteger(); - bool event_isrepeat = native_key_data["event_isrepeat"].asBoolean(); - - // adding new code below in unicodeInput means we don't send ascii chars - // here too or we get double key presses on a mac. - bool esc_key = (event_umodchars == 27); - bool tab_key_up = (event_umodchars == 9) && (key_event == dullahan::EKeyEvent::KE_KEY_UP); - if ((esc_key || ((unsigned char)event_chars < 0x10 || (unsigned char)event_chars >= 0x7f )) && !tab_key_up) - { - mCEFLib->nativeKeyboardEventOSX(key_event, event_modifiers, - event_keycode, event_chars, - event_umodchars, event_isrepeat); - } -#elif LL_WINDOWS - U32 msg = ll_U32_from_sd(native_key_data["msg"]); - U32 wparam = ll_U32_from_sd(native_key_data["w_param"]); - U64 lparam = ll_U32_from_sd(native_key_data["l_param"]); - - mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam); -#endif -}; - -void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD::emptyMap()) -{ -#if LL_DARWIN - // i didn't think this code was needed for macOS but without it, the IME - // input in japanese (and likely others too) doesn't work correctly. - // see maint-7654 - U32 event_modifiers = native_key_data["event_modifiers"].asInteger(); - U32 event_keycode = native_key_data["event_keycode"].asInteger(); - U32 event_chars = native_key_data["event_chars"].asInteger(); - U32 event_umodchars = native_key_data["event_umodchars"].asInteger(); - bool event_isrepeat = native_key_data["event_isrepeat"].asBoolean(); - - dullahan::EKeyEvent key_event = dullahan::KE_KEY_UP; - if (event == "down") - { - key_event = dullahan::KE_KEY_DOWN; - } - - mCEFLib->nativeKeyboardEventOSX(key_event, event_modifiers, - event_keycode, event_chars, - event_umodchars, event_isrepeat); -#elif LL_WINDOWS - event = ""; // not needed here but prevents unused var warning as error - U32 msg = ll_U32_from_sd(native_key_data["msg"]); - U32 wparam = ll_U32_from_sd(native_key_data["w_param"]); - U64 lparam = ll_U32_from_sd(native_key_data["l_param"]); - mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam); -#endif -}; - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginCEF::checkEditState() -{ - bool can_cut = mCEFLib->editCanCut(); - bool can_copy = mCEFLib->editCanCopy(); - bool can_paste = mCEFLib->editCanPaste(); - - if ((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste)) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state"); - - if (can_cut != mCanCut) - { - mCanCut = can_cut; - message.setValueBoolean("cut", can_cut); - } - - if (can_copy != mCanCopy) - { - mCanCopy = can_copy; - message.setValueBoolean("copy", can_copy); - } - - if (can_paste != mCanPaste) - { - mCanPaste = can_paste; - message.setValueBoolean("paste", can_paste); - } - - sendMessage(message); - } -} - -void MediaPluginCEF::setVolume() -{ - mVolumeCatcher.setVolume(mCurVolume); -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool MediaPluginCEF::init() -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); - message.setValue("name", "CEF 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) -{ - MediaPluginCEF* self = new MediaPluginCEF(host_send_func, host_user_data); - *plugin_send_func = MediaPluginCEF::staticReceiveMessage; - *plugin_user_data = (void*)self; - - return 0; -} +/**
+* @file media_plugin_cef.cpp
+* @brief CEF (Chromium Embedding Framework) 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 "indra_constants.h" // for indra keyboard codes
+
+#include "llglheaders.h" // for GL_* constants
+#include "llsdutil.h"
+#include "llplugininstance.h"
+#include "llpluginmessage.h"
+#include "llpluginmessageclasses.h"
+#include "llstring.h"
+#include "volume_catcher.h"
+#include "media_plugin_base.h"
+
+#include "dullahan.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+class MediaPluginCEF :
+ public MediaPluginBase
+{
+public:
+ MediaPluginCEF(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginCEF();
+
+ /*virtual*/
+ void receiveMessage(const char* message_string);
+
+private:
+ bool init();
+
+ void onPageChangedCallback(const unsigned char* pixels, int x, int y, const int width, const int height);
+ void onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect);
+ void onConsoleMessageCallback(std::string message, std::string source, int line);
+ void onStatusMessageCallback(std::string value);
+ void onTitleChangeCallback(std::string title);
+ void onTooltipCallback(std::string text);
+ void onLoadStartCallback();
+ void onRequestExitCallback();
+ void onLoadEndCallback(int httpStatusCode, std::string url);
+ void onLoadError(int status, const std::string error_text);
+ void onAddressChangeCallback(std::string url);
+ void onOpenPopupCallback(std::string url, std::string target);
+ bool onHTTPAuthCallback(const std::string host, const std::string realm, std::string& username, std::string& password);
+ void onCursorChangedCallback(dullahan::ECursorType type);
+ const std::vector<std::string> onFileDialog(dullahan::EFileDialogType dialog_type, const std::string dialog_title, const std::string default_file, const std::string dialog_accept_filter, bool& use_default);
+ bool onJSDialogCallback(const std::string origin_url, const std::string message_text, const std::string default_prompt_text);
+ bool onJSBeforeUnloadCallback();
+
+ void postDebugMessage(const std::string& msg);
+ void authResponse(LLPluginMessage &message);
+
+ void keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_data);
+ void unicodeInput(std::string event, LLSD native_key_data);
+
+ void checkEditState();
+ void setVolume();
+
+ bool mEnableMediaPluginDebugging;
+ std::string mHostLanguage;
+ bool mCookiesEnabled;
+ bool mPluginsEnabled;
+ bool mJavascriptEnabled;
+ bool mProxyEnabled;
+ std::string mProxyHost;
+ int mProxyPort;
+ bool mDisableGPU;
+ bool mDisableNetworkService;
+ bool mUseMockKeyChain;
+ bool mDisableWebSecurity;
+ bool mFileAccessFromFileUrls;
+ std::string mUserAgentSubtring;
+ std::string mAuthUsername;
+ std::string mAuthPassword;
+ bool mAuthOK;
+ bool mCanCut;
+ bool mCanCopy;
+ bool mCanPaste;
+ std::string mRootCachePath;
+ std::string mCachePath;
+ std::string mContextCachePath;
+ std::string mCefLogFile;
+ bool mCefLogVerbose;
+ std::vector<std::string> mPickedFiles;
+ VolumeCatcher mVolumeCatcher;
+ F32 mCurVolume;
+ dullahan* mCEFLib;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+MediaPluginCEF::MediaPluginCEF(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) :
+MediaPluginBase(host_send_func, host_user_data)
+{
+ mWidth = 0;
+ mHeight = 0;
+ mDepth = 4;
+ mPixels = 0;
+ mEnableMediaPluginDebugging = true;
+ mHostLanguage = "en";
+ mCookiesEnabled = true;
+ mPluginsEnabled = false;
+ mJavascriptEnabled = true;
+ mProxyEnabled = false;
+ mProxyHost = "";
+ mProxyPort = 0;
+ mDisableGPU = false;
+ mDisableNetworkService = true;
+ mUseMockKeyChain = true;
+ mDisableWebSecurity = false;
+ mFileAccessFromFileUrls = false;
+ mUserAgentSubtring = "";
+ mAuthUsername = "";
+ mAuthPassword = "";
+ mAuthOK = false;
+ mCanCut = false;
+ mCanCopy = false;
+ mCanPaste = false;
+ mCachePath = "";
+ mCefLogFile = "";
+ mCefLogVerbose = false;
+ mPickedFiles.clear();
+ mCurVolume = 0.0;
+
+ mCEFLib = new dullahan();
+
+ setVolume();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+MediaPluginCEF::~MediaPluginCEF()
+{
+ mCEFLib->shutdown();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::postDebugMessage(const std::string& msg)
+{
+ if (mEnableMediaPluginDebugging)
+ {
+ std::stringstream str;
+ str << "@Media Msg> " << msg;
+
+ LLPluginMessage debug_message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "debug_message");
+ debug_message.setValue("message_text", str.str());
+ debug_message.setValue("message_level", "info");
+ sendMessage(debug_message);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onPageChangedCallback(const unsigned char* pixels, int x, int y, const int width, const int height)
+{
+ if( mPixels && pixels )
+ {
+ if (mWidth == width && mHeight == height)
+ {
+ memcpy(mPixels, pixels, mWidth * mHeight * mDepth);
+ }
+ else
+ {
+ mCEFLib->setSize(mWidth, mHeight);
+ }
+ setDirty(0, 0, mWidth, mHeight);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onConsoleMessageCallback(std::string message, std::string source, int line)
+{
+ std::stringstream str;
+ str << "Console message: " << message << " in file(" << source << ") at line " << line;
+ postDebugMessage(str.str());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onStatusMessageCallback(std::string value)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text");
+ message.setValue("status", value);
+ sendMessage(message);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onTitleChangeCallback(std::string title)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
+ message.setValue("name", title);
+ message.setValueBoolean("history_back_available", mCEFLib->canGoBack());
+ message.setValueBoolean("history_forward_available", mCEFLib->canGoForward());
+ sendMessage(message);
+}
+
+void MediaPluginCEF::onTooltipCallback(std::string text)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "tooltip_text");
+ message.setValue("tooltip", text);
+ sendMessage(message);
+}
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onLoadStartCallback()
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin");
+ //message.setValue("uri", event.getEventUri()); // not easily available here in CEF - needed?
+ message.setValueBoolean("history_back_available", mCEFLib->canGoBack());
+ message.setValueBoolean("history_forward_available", mCEFLib->canGoForward());
+ sendMessage(message);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onLoadError(int status, const std::string error_text)
+{
+ std::stringstream msg;
+
+ msg << "<b>Loading error!</b>";
+ msg << "<p>";
+ msg << "Message: " << error_text;
+ msg << "<br>";
+ msg << "Code: " << status;
+
+ mCEFLib->showBrowserMessage(msg.str());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onRequestExitCallback()
+{
+ LLPluginMessage message("base", "goodbye");
+ sendMessage(message);
+
+ // Will trigger delete on next staticReceiveMessage()
+ mDeleteMe = true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onLoadEndCallback(int httpStatusCode, std::string url)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete");
+ //message.setValue("uri", event.getEventUri()); // not easily available here in CEF - needed?
+ message.setValueS32("result_code", httpStatusCode);
+ message.setValueBoolean("history_back_available", mCEFLib->canGoBack());
+ message.setValueBoolean("history_forward_available", mCEFLib->canGoForward());
+ message.setValue("uri", url);
+ sendMessage(message);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onAddressChangeCallback(std::string url)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed");
+ message.setValue("uri", url);
+ sendMessage(message);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onOpenPopupCallback(std::string url, std::string target)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href");
+ message.setValue("uri", url);
+ message.setValue("target", target);
+ sendMessage(message);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow");
+ message.setValue("uri", url);
+
+ // indicate if this interaction was from a user click (okay on a SLAPP) or
+ // via a navigation (e.g. a data URL - see SL-18151) (not okay on a SLAPP)
+ const std::string nav_type = user_gesture ? "clicked" : "navigated";
+
+ message.setValue("nav_type", nav_type);
+ message.setValueBoolean("is_redirect", is_redirect);
+
+ sendMessage(message);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool MediaPluginCEF::onHTTPAuthCallback(const std::string host, const std::string realm, std::string& username, std::string& password)
+{
+ mAuthOK = false;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "auth_request");
+ message.setValue("url", host);
+ message.setValue("realm", realm);
+ message.setValueBoolean("blocking_request", true);
+
+ // The "blocking_request" key in the message means this sendMessage call will block until a response is received.
+ sendMessage(message);
+
+ if (mAuthOK)
+ {
+ username = mAuthUsername;
+ password = mAuthPassword;
+ }
+
+ return mAuthOK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+const std::vector<std::string> MediaPluginCEF::onFileDialog(dullahan::EFileDialogType dialog_type, const std::string dialog_title, const std::string default_file, std::string dialog_accept_filter, bool& use_default)
+{
+ // do not use the default CEF file picker
+ use_default = false;
+
+ if (dialog_type == dullahan::FD_OPEN_FILE)
+ {
+ mPickedFiles.clear();
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file");
+ message.setValueBoolean("blocking_request", true);
+ message.setValueBoolean("multiple_files", false);
+
+ sendMessage(message);
+
+ return mPickedFiles;
+ }
+ else if (dialog_type == dullahan::FD_OPEN_MULTIPLE_FILES)
+ {
+ mPickedFiles.clear();
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file");
+ message.setValueBoolean("blocking_request", true);
+ message.setValueBoolean("multiple_files", true);
+
+ sendMessage(message);
+
+ return mPickedFiles;
+ }
+ else if (dialog_type == dullahan::FD_SAVE_FILE)
+ {
+ mPickedFiles.clear();
+ mAuthOK = false;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "file_download");
+ message.setValueBoolean("blocking_request", true);
+ message.setValue("filename", default_file);
+
+ sendMessage(message);
+
+ return mPickedFiles;
+ }
+
+ return std::vector<std::string>();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool MediaPluginCEF::onJSDialogCallback(const std::string origin_url, const std::string message_text, const std::string default_prompt_text)
+{
+ // return true indicates we suppress the JavaScript alert UI entirely
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool MediaPluginCEF::onJSBeforeUnloadCallback()
+{
+ // return true indicates we suppress the JavaScript UI entirely
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onCursorChangedCallback(dullahan::ECursorType type)
+{
+ std::string name = "";
+
+ switch (type)
+ {
+ case dullahan::CT_POINTER:
+ name = "UI_CURSOR_ARROW";
+ break;
+ case dullahan::CT_CROSS:
+ name = "UI_CURSOR_CROSS";
+ break;
+ case dullahan::CT_HAND:
+ name = "UI_CURSOR_HAND";
+ break;
+ case dullahan::CT_IBEAM:
+ name = "UI_CURSOR_IBEAM";
+ break;
+ case dullahan::CT_WAIT:
+ name = "UI_CURSOR_WAIT";
+ break;
+ //case dullahan::CT_HELP:
+ case dullahan::CT_ROWRESIZE:
+ case dullahan::CT_NORTHRESIZE:
+ case dullahan::CT_SOUTHRESIZE:
+ case dullahan::CT_NORTHSOUTHRESIZE:
+ name = "UI_CURSOR_SIZENS";
+ break;
+ case dullahan::CT_COLUMNRESIZE:
+ case dullahan::CT_EASTRESIZE:
+ case dullahan::CT_WESTRESIZE:
+ case dullahan::CT_EASTWESTRESIZE:
+ name = "UI_CURSOR_SIZEWE";
+ break;
+ case dullahan::CT_NORTHEASTRESIZE:
+ case dullahan::CT_SOUTHWESTRESIZE:
+ case dullahan::CT_NORTHEASTSOUTHWESTRESIZE:
+ name = "UI_CURSOR_SIZENESW";
+ break;
+ case dullahan::CT_SOUTHEASTRESIZE:
+ case dullahan::CT_NORTHWESTRESIZE:
+ case dullahan::CT_NORTHWESTSOUTHEASTRESIZE:
+ name = "UI_CURSOR_SIZENWSE";
+ break;
+ case dullahan::CT_MOVE:
+ name = "UI_CURSOR_SIZEALL";
+ break;
+ //case dullahan::CT_MIDDLEPANNING:
+ //case dullahan::CT_EASTPANNING:
+ //case dullahan::CT_NORTHPANNING:
+ //case dullahan::CT_NORTHEASTPANNING:
+ //case dullahan::CT_NORTHWESTPANNING:
+ //case dullahan::CT_SOUTHPANNING:
+ //case dullahan::CT_SOUTHEASTPANNING:
+ //case dullahan::CT_SOUTHWESTPANNING:
+ //case dullahan::CT_WESTPANNING:
+ //case dullahan::CT_VERTICALTEXT:
+ //case dullahan::CT_CELL:
+ //case dullahan::CT_CONTEXTMENU:
+ case dullahan::CT_ALIAS:
+ name = "UI_CURSOR_TOOLMEDIAOPEN";
+ break;
+ case dullahan::CT_PROGRESS:
+ name = "UI_CURSOR_WORKING";
+ break;
+ case dullahan::CT_COPY:
+ name = "UI_CURSOR_ARROWCOPY";
+ break;
+ case dullahan::CT_NONE:
+ name = "UI_CURSOR_NO";
+ break;
+ case dullahan::CT_NODROP:
+ case dullahan::CT_NOTALLOWED:
+ name = "UI_CURSOR_NOLOCKED";
+ break;
+ case dullahan::CT_ZOOMIN:
+ name = "UI_CURSOR_TOOLZOOMIN";
+ break;
+ case dullahan::CT_ZOOMOUT:
+ name = "UI_CURSOR_TOOLZOOMOUT";
+ break;
+ case dullahan::CT_GRAB:
+ name = "UI_CURSOR_TOOLGRAB";
+ break;
+ //case dullahan::CT_GRABING:
+ //case dullahan::CT_CUSTOM:
+
+ default:
+ LL_WARNS() << "Unknown cursor ID: " << (int)type << LL_ENDL;
+ break;
+ }
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed");
+ message.setValue("name", name);
+ sendMessage(message);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::authResponse(LLPluginMessage &message)
+{
+ mAuthOK = message.getValueBoolean("ok");
+ if (mAuthOK)
+ {
+ mAuthUsername = message.getValue("username");
+ mAuthPassword = message.getValue("password");
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::receiveMessage(const char* message_string)
+{
+ // std::cerr << "MediaPluginCEF::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
+ 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")
+ {
+ 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_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
+ message.setValueLLSD("versions", versions);
+
+ std::string plugin_version = "CEF plugin 1.1.412";
+ message.setValue("plugin_version", plugin_version);
+ sendMessage(message);
+ }
+ else if (message_name == "idle")
+ {
+ mCEFLib->update();
+
+ mVolumeCatcher.pump();
+
+ // this seems bad but unless the state changes (it won't until we figure out
+ // how to get CEF to tell us if copy/cut/paste is available) then this function
+ // will return immediately
+ checkEditState();
+ }
+ else if (message_name == "cleanup")
+ {
+ mCEFLib->requestExit();
+ }
+ else if (message_name == "force_exit")
+ {
+ mDeleteMe = true;
+ }
+ 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)
+ {
+ mPixels = NULL;
+ mTextureSegmentName.clear();
+ }
+ mSharedSegments.erase(iter);
+ }
+ else
+ {
+ }
+
+ LLPluginMessage message("base", "shm_remove_response");
+ message.setValue("name", name);
+ sendMessage(message);
+ }
+ else
+ {
+ }
+ }
+ else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
+ {
+ if (message_name == "init")
+ {
+ // event callbacks from Dullahan
+ mCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
+ mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1));
+ mCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1));
+ mCEFLib->setOnTooltipCallback(std::bind(&MediaPluginCEF::onTooltipCallback, this, std::placeholders::_1));
+ mCEFLib->setOnLoadStartCallback(std::bind(&MediaPluginCEF::onLoadStartCallback, this));
+ mCEFLib->setOnLoadEndCallback(std::bind(&MediaPluginCEF::onLoadEndCallback, this, std::placeholders::_1, std::placeholders::_2));
+ mCEFLib->setOnLoadErrorCallback(std::bind(&MediaPluginCEF::onLoadError, this, std::placeholders::_1, std::placeholders::_2));
+ mCEFLib->setOnAddressChangeCallback(std::bind(&MediaPluginCEF::onAddressChangeCallback, this, std::placeholders::_1));
+ mCEFLib->setOnOpenPopupCallback(std::bind(&MediaPluginCEF::onOpenPopupCallback, this, std::placeholders::_1, std::placeholders::_2));
+ mCEFLib->setOnHTTPAuthCallback(std::bind(&MediaPluginCEF::onHTTPAuthCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
+ mCEFLib->setOnFileDialogCallback(std::bind(&MediaPluginCEF::onFileDialog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
+ mCEFLib->setOnCursorChangedCallback(std::bind(&MediaPluginCEF::onCursorChangedCallback, this, std::placeholders::_1));
+ mCEFLib->setOnRequestExitCallback(std::bind(&MediaPluginCEF::onRequestExitCallback, this));
+ mCEFLib->setOnJSDialogCallback(std::bind(&MediaPluginCEF::onJSDialogCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ mCEFLib->setOnJSBeforeUnloadCallback(std::bind(&MediaPluginCEF::onJSBeforeUnloadCallback, this));
+
+ dullahan::dullahan_settings settings;
+#if LL_WINDOWS
+ // As of CEF version 83+, for Windows versions, we need to tell CEF
+ // where the host helper process is since this DLL is not in the same
+ // dir as the executable that loaded it (SLPlugin.exe). The code in
+ // Dullahan that tried to figure out the location automatically uses
+ // the location of the exe which isn't helpful so we tell it explicitly.
+ std::vector<wchar_t> buffer(MAX_PATH + 1);
+ GetCurrentDirectoryW(MAX_PATH, &buffer[0]);
+ settings.host_process_path = ll_convert_wide_to_string(&buffer[0]);
+#endif
+ settings.accept_language_list = mHostLanguage;
+
+ // SL-15560: Product team overruled my change to set the default
+ // embedded background color to match the floater background
+ // and set it to white
+ settings.background_color = 0xffffffff; // white
+
+ settings.cache_enabled = true;
+ settings.root_cache_path = mRootCachePath;
+ settings.cache_path = mCachePath;
+ settings.context_cache_path = mContextCachePath;
+ settings.cookies_enabled = mCookiesEnabled;
+
+ // configure proxy argument if enabled and valid
+ if (mProxyEnabled && mProxyHost.length())
+ {
+ std::ostringstream proxy_url;
+ proxy_url << mProxyHost << ":" << mProxyPort;
+ settings.proxy_host_port = proxy_url.str();
+ }
+ settings.disable_gpu = mDisableGPU;
+#if LL_DARWIN
+ settings.disable_network_service = mDisableNetworkService;
+ settings.use_mock_keychain = mUseMockKeyChain;
+#endif
+ // these were added to facilitate loading images directly into a local
+ // web page for the prototype 360 project in 2017 - something that is
+ // disallowed normally by the browser security model. Now the the source
+ // (cubemap) images are stores as JavaScript, we can avoid opening up
+ // this security hole (it was only set for the 360 floater but still
+ // a concern). Leaving them here, explicitly turn off vs removing
+ // entirely from this source file so that others are aware of them
+ // in the future.
+ settings.disable_web_security = false;
+ settings.file_access_from_file_urls = false;
+
+ settings.flash_enabled = mPluginsEnabled;
+
+ // This setting applies to all plugins, not just Flash
+ // Regarding, SL-15559 PDF files do not load in CEF v91,
+ // it turns out that on Windows, PDF support is treated
+ // as a plugin on Windows only so turning all plugins
+ // off, disabled built in PDF support. (Works okay in
+ // macOS surprisingly). To mitigrate this, we set the global
+ // media enabled flag to whatever the consumer wants and
+ // explicitly disable Flash with a different setting (below)
+ settings.plugins_enabled = mPluginsEnabled;
+
+ // SL-14897 Disable Flash support in the embedded browser
+ settings.flash_enabled = false;
+
+ settings.flip_mouse_y = false;
+ settings.flip_pixels_y = true;
+ settings.frame_rate = 60;
+ settings.force_wave_audio = true;
+ settings.initial_height = 1024;
+ settings.initial_width = 1024;
+ settings.java_enabled = false;
+ settings.javascript_enabled = mJavascriptEnabled;
+ settings.media_stream_enabled = false; // MAINT-6060 - WebRTC media removed until we can add granularity/query UI
+
+ settings.user_agent_substring = mCEFLib->makeCompatibleUserAgentString(mUserAgentSubtring);
+ settings.webgl_enabled = true;
+ settings.log_file = mCefLogFile;
+ settings.log_verbose = mCefLogVerbose;
+ settings.autoplay_without_gesture = true;
+
+ std::vector<std::string> custom_schemes(1, "secondlife");
+ mCEFLib->setCustomSchemes(custom_schemes);
+
+ bool result = mCEFLib->init(settings);
+ if (!result)
+ {
+ // if this fails, the media system in viewer will put up a message
+ }
+
+ // now we can set page zoom factor
+ F32 factor = (F32)message_in.getValueReal("factor");
+#if LL_DARWIN
+ //temporary fix for SL-10473: issue with displaying checkboxes on Mojave
+ factor*=1.001;
+#endif
+ mCEFLib->setPageZoom(factor);
+
+ // Plugin gets to decide the texture parameters to use.
+ mDepth = 4;
+ 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);
+ message.setValueU32("type", GL_UNSIGNED_BYTE);
+ message.setValueBoolean("coords_opengl", true);
+ sendMessage(message);
+ }
+ else if (message_name == "set_user_data_path")
+ {
+ std::string user_data_path_cache = message_in.getValue("cache_path");
+ std::string subfolder = message_in.getValue("username");
+
+ mRootCachePath = user_data_path_cache + "cef_cache";
+ if (!subfolder.empty())
+ {
+ std::string delim;
+#if LL_WINDOWS
+ // media plugin doesn't have access to gDirUtilp
+ delim = "\\";
+#else
+ delim = "/";
+#endif
+ mCachePath = mRootCachePath + delim + subfolder;
+ }
+ else
+ {
+ mCachePath = mRootCachePath;
+ }
+ mContextCachePath = ""; // disabled by ""
+ mCefLogFile = message_in.getValue("cef_log_file");
+ mCefLogVerbose = message_in.getValueBoolean("cef_verbose_log");
+ }
+ 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;
+
+ mCEFLib->setSize(mWidth, mHeight);
+ };
+ };
+
+ 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 == "set_language_code")
+ {
+ mHostLanguage = message_in.getValue("language");
+ }
+ else if (message_name == "load_uri")
+ {
+ std::string uri = message_in.getValue("uri");
+ mCEFLib->navigate(uri);
+ }
+ else if (message_name == "execute_javascript")
+ {
+ std::string code = message_in.getValue("code");
+ mCEFLib->executeJavaScript(code);
+ }
+ else if (message_name == "set_cookie")
+ {
+ std::string uri = message_in.getValue("uri");
+ std::string name = message_in.getValue("name");
+ std::string value = message_in.getValue("value");
+ std::string domain = message_in.getValue("domain");
+ std::string path = message_in.getValue("path");
+ bool httponly = message_in.getValueBoolean("httponly");
+ bool secure = message_in.getValueBoolean("secure");
+ mCEFLib->setCookie(uri, name, value, domain, path, httponly, secure);
+ }
+ else if (message_name == "mouse_event")
+ {
+ std::string event = message_in.getValue("event");
+
+ S32 x = message_in.getValueS32("x");
+ S32 y = message_in.getValueS32("y");
+
+ // only even send left mouse button events to the CEF library
+ // (partially prompted by crash in macOS CEF when sending right button events)
+ // we catch the right click in viewer and display our own context menu anyway
+ S32 button = message_in.getValueS32("button");
+ dullahan::EMouseButton btn = dullahan::MB_MOUSE_BUTTON_LEFT;
+
+ if (event == "down" && button == 0)
+ {
+ mCEFLib->mouseButton(btn, dullahan::ME_MOUSE_DOWN, x, y);
+ mCEFLib->setFocus();
+
+ std::stringstream str;
+ str << "Mouse down at = " << x << ", " << y;
+ postDebugMessage(str.str());
+ }
+ else if (event == "up" && button == 0)
+ {
+ mCEFLib->mouseButton(btn, dullahan::ME_MOUSE_UP, x, y);
+
+ std::stringstream str;
+ str << "Mouse up at = " << x << ", " << y;
+ postDebugMessage(str.str());
+ }
+ else if (event == "double_click")
+ {
+ mCEFLib->mouseButton(btn, dullahan::ME_MOUSE_DOUBLE_CLICK, x, y);
+ }
+ else
+ {
+ mCEFLib->mouseMove(x, y);
+ }
+ }
+ else if (message_name == "scroll_event")
+ {
+ // Mouse coordinates for cef to be able to scroll 'containers'
+ S32 x = message_in.getValueS32("x");
+ S32 y = message_in.getValueS32("y");
+
+ // Wheel's clicks
+ S32 delta_x = message_in.getValueS32("clicks_x");
+ S32 delta_y = message_in.getValueS32("clicks_y");
+ const int scaling_factor = 40;
+ delta_x *= -scaling_factor;
+ delta_y *= -scaling_factor;
+
+ mCEFLib->mouseWheel(x, y, delta_x, delta_y);
+ }
+ else if (message_name == "text_event")
+ {
+ std::string event = message_in.getValue("event");
+ LLSD native_key_data = message_in.getValueLLSD("native_key_data");
+ unicodeInput(event, native_key_data);
+ }
+ else if (message_name == "key_event")
+ {
+#if LL_DARWIN
+ std::string event = message_in.getValue("event");
+ LLSD native_key_data = message_in.getValueLLSD("native_key_data");
+
+ dullahan::EKeyEvent key_event = dullahan::KE_KEY_UP;
+ if (event == "down")
+ {
+ key_event = dullahan::KE_KEY_DOWN;
+ }
+ else if (event == "repeat")
+ {
+ key_event = dullahan::KE_KEY_REPEAT;
+ }
+
+ keyEvent(key_event, native_key_data);
+
+#elif LL_WINDOWS
+ std::string event = message_in.getValue("event");
+ LLSD native_key_data = message_in.getValueLLSD("native_key_data");
+
+ // Treat unknown events as key-up for safety.
+ dullahan::EKeyEvent key_event = dullahan::KE_KEY_UP;
+ if (event == "down")
+ {
+ key_event = dullahan::KE_KEY_DOWN;
+ }
+ else if (event == "repeat")
+ {
+ key_event = dullahan::KE_KEY_REPEAT;
+ }
+
+ keyEvent(key_event, native_key_data);
+#endif
+ }
+ else if (message_name == "enable_media_plugin_debugging")
+ {
+ mEnableMediaPluginDebugging = message_in.getValueBoolean("enable");
+ }
+ if (message_name == "pick_file_response")
+ {
+ LLSD file_list_llsd = message_in.getValueLLSD("file_list");
+
+ LLSD::array_const_iterator iter = file_list_llsd.beginArray();
+ LLSD::array_const_iterator end = file_list_llsd.endArray();
+ for (; iter != end; ++iter)
+ {
+ mPickedFiles.push_back(((*iter).asString()));
+ }
+ }
+ if (message_name == "auth_response")
+ {
+ authResponse(message_in);
+ }
+ if (message_name == "edit_cut")
+ {
+ mCEFLib->editCut();
+ }
+ if (message_name == "edit_copy")
+ {
+ mCEFLib->editCopy();
+ }
+ if (message_name == "edit_paste")
+ {
+ mCEFLib->editPaste();
+ }
+ }
+ else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER)
+ {
+ if (message_name == "set_page_zoom_factor")
+ {
+ F32 factor = (F32)message_in.getValueReal("factor");
+#if LL_DARWIN
+ //temporary fix for SL-10473: issue with displaying checkboxes on Mojave
+ factor*=1.001;
+#endif
+ mCEFLib->setPageZoom(factor);
+ }
+ if (message_name == "browse_stop")
+ {
+ mCEFLib->stop();
+ }
+ else if (message_name == "browse_reload")
+ {
+ bool ignore_cache = true;
+ mCEFLib->reload(ignore_cache);
+ }
+ else if (message_name == "browse_forward")
+ {
+ mCEFLib->goForward();
+ }
+ else if (message_name == "browse_back")
+ {
+ mCEFLib->goBack();
+ }
+ else if (message_name == "cookies_enabled")
+ {
+ mCookiesEnabled = message_in.getValueBoolean("enable");
+ }
+ else if (message_name == "clear_cookies")
+ {
+ mCEFLib->deleteAllCookies();
+ }
+ else if (message_name == "set_user_agent")
+ {
+ mUserAgentSubtring = message_in.getValue("user_agent");
+ }
+ else if (message_name == "show_web_inspector")
+ {
+ mCEFLib->showDevTools();
+ }
+ else if (message_name == "plugins_enabled")
+ {
+ mPluginsEnabled = message_in.getValueBoolean("enable");
+ }
+ else if (message_name == "javascript_enabled")
+ {
+ mJavascriptEnabled = message_in.getValueBoolean("enable");
+ }
+ else if (message_name == "gpu_disabled")
+ {
+ mDisableGPU = message_in.getValueBoolean("disable");
+ }
+ else if (message_name == "proxy_setup")
+ {
+ mProxyEnabled = message_in.getValueBoolean("enable");
+ mProxyHost = message_in.getValue("host");
+ mProxyPort = message_in.getValueS32("port");
+ }
+ else if (message_name == "web_security_disabled")
+ {
+ mDisableWebSecurity = message_in.getValueBoolean("disabled");
+ }
+ else if (message_name == "file_access_from_file_urls")
+ {
+ mFileAccessFromFileUrls = message_in.getValueBoolean("enabled");
+ }
+ }
+ else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
+ {
+ if (message_name == "set_volume")
+ {
+ F32 volume = (F32)message_in.getValueReal("volume");
+ mCurVolume = volume;
+ setVolume();
+ }
+ }
+ else
+ {
+ };
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_data = LLSD::emptyMap())
+{
+#if LL_DARWIN
+ U32 event_modifiers = native_key_data["event_modifiers"].asInteger();
+ U32 event_keycode = native_key_data["event_keycode"].asInteger();
+ U32 event_chars = native_key_data["event_chars"].asInteger();
+ U32 event_umodchars = native_key_data["event_umodchars"].asInteger();
+ bool event_isrepeat = native_key_data["event_isrepeat"].asBoolean();
+
+ // adding new code below in unicodeInput means we don't send ascii chars
+ // here too or we get double key presses on a mac.
+ bool esc_key = (event_umodchars == 27);
+ bool tab_key_up = (event_umodchars == 9) && (key_event == dullahan::EKeyEvent::KE_KEY_UP);
+ if ((esc_key || ((unsigned char)event_chars < 0x10 || (unsigned char)event_chars >= 0x7f )) && !tab_key_up)
+ {
+ mCEFLib->nativeKeyboardEventOSX(key_event, event_modifiers,
+ event_keycode, event_chars,
+ event_umodchars, event_isrepeat);
+ }
+#elif LL_WINDOWS
+ U32 msg = ll_U32_from_sd(native_key_data["msg"]);
+ U32 wparam = ll_U32_from_sd(native_key_data["w_param"]);
+ U64 lparam = ll_U32_from_sd(native_key_data["l_param"]);
+
+ mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam);
+#endif
+};
+
+void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD::emptyMap())
+{
+#if LL_DARWIN
+ // i didn't think this code was needed for macOS but without it, the IME
+ // input in japanese (and likely others too) doesn't work correctly.
+ // see maint-7654
+ U32 event_modifiers = native_key_data["event_modifiers"].asInteger();
+ U32 event_keycode = native_key_data["event_keycode"].asInteger();
+ U32 event_chars = native_key_data["event_chars"].asInteger();
+ U32 event_umodchars = native_key_data["event_umodchars"].asInteger();
+ bool event_isrepeat = native_key_data["event_isrepeat"].asBoolean();
+
+ dullahan::EKeyEvent key_event = dullahan::KE_KEY_UP;
+ if (event == "down")
+ {
+ key_event = dullahan::KE_KEY_DOWN;
+ }
+
+ mCEFLib->nativeKeyboardEventOSX(key_event, event_modifiers,
+ event_keycode, event_chars,
+ event_umodchars, event_isrepeat);
+#elif LL_WINDOWS
+ event = ""; // not needed here but prevents unused var warning as error
+ U32 msg = ll_U32_from_sd(native_key_data["msg"]);
+ U32 wparam = ll_U32_from_sd(native_key_data["w_param"]);
+ U64 lparam = ll_U32_from_sd(native_key_data["l_param"]);
+ mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam);
+#endif
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::checkEditState()
+{
+ bool can_cut = mCEFLib->editCanCut();
+ bool can_copy = mCEFLib->editCanCopy();
+ bool can_paste = mCEFLib->editCanPaste();
+
+ if ((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste))
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state");
+
+ if (can_cut != mCanCut)
+ {
+ mCanCut = can_cut;
+ message.setValueBoolean("cut", can_cut);
+ }
+
+ if (can_copy != mCanCopy)
+ {
+ mCanCopy = can_copy;
+ message.setValueBoolean("copy", can_copy);
+ }
+
+ if (can_paste != mCanPaste)
+ {
+ mCanPaste = can_paste;
+ message.setValueBoolean("paste", can_paste);
+ }
+
+ sendMessage(message);
+ }
+}
+
+void MediaPluginCEF::setVolume()
+{
+ mVolumeCatcher.setVolume(mCurVolume);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool MediaPluginCEF::init()
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
+ message.setValue("name", "CEF 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)
+{
+ MediaPluginCEF* self = new MediaPluginCEF(host_send_func, host_user_data);
+ *plugin_send_func = MediaPluginCEF::staticReceiveMessage;
+ *plugin_user_data = (void*)self;
+
+ return 0;
+}
|