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 3aa73ed9a2..64fc7e452b 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; +} |