diff options
Diffstat (limited to 'indra/media_plugins')
| -rw-r--r-- | indra/media_plugins/cef/mac_volume_catcher.cpp | 550 | ||||
| -rw-r--r-- | indra/media_plugins/cef/media_plugin_cef.cpp | 2294 | 
2 files changed, 1422 insertions, 1422 deletions
| diff --git a/indra/media_plugins/cef/mac_volume_catcher.cpp b/indra/media_plugins/cef/mac_volume_catcher.cpp index 32251c0999..26b1977e55 100644 --- a/indra/media_plugins/cef/mac_volume_catcher.cpp +++ b/indra/media_plugins/cef/mac_volume_catcher.cpp @@ -1,275 +1,275 @@ -/** - * @file mac_volume_catcher.cpp - * @brief A Mac OS X specific hack to control the volume level of all audio channels opened by a process. - * - * @cond - * $LicenseInfo:firstyear=2010&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 - */ - -/************************************************************************************************************** -    This code works by using CaptureComponent to capture the "Default Output" audio component -    (kAudioUnitType_Output/kAudioUnitSubType_DefaultOutput) and delegating all calls to the original component. -    It does this just to keep track of all instances of the default output component, so that it can set the -    kHALOutputParam_Volume parameter on all of them to adjust the output volume. -**************************************************************************************************************/ - -#include "volume_catcher.h" - -#include <QuickTime/QuickTime.h> -#include <AudioUnit/AudioUnit.h> -#include <list> - -#if LL_DARWIN -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -struct VolumeCatcherStorage; - -class VolumeCatcherImpl -{ -public: - -    void setVolume(F32 volume); -    void setPan(F32 pan); - -    void setInstanceVolume(VolumeCatcherStorage *instance); - -    std::list<VolumeCatcherStorage*> mComponentInstances; -    Component mOriginalDefaultOutput; -    Component mVolumeAdjuster; - -    static VolumeCatcherImpl *getInstance(); -private: -    // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance. -    VolumeCatcherImpl(); -    static VolumeCatcherImpl *sInstance; - -    // The singlar instance of this class is expected to last until the process exits. -    // To ensure this, we declare the destructor here but never define it, so any code which attempts to destroy the instance will not link. -    ~VolumeCatcherImpl(); - -    F32 mVolume; -    F32 mPan; -}; - -VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;; - -struct VolumeCatcherStorage -{ -    ComponentInstance self; -    ComponentInstance delegate; -}; - -static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage); -static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self); -static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self); - -VolumeCatcherImpl *VolumeCatcherImpl::getInstance() -{ -    if(!sInstance) -    { -        sInstance = new VolumeCatcherImpl; -    } - -    return sInstance; -} - -VolumeCatcherImpl::VolumeCatcherImpl() -{ -    mVolume = 1.0;  // default to full volume -    mPan = 0.0;     // and center pan - -    ComponentDescription desc; -    desc.componentType = kAudioUnitType_Output; -    desc.componentSubType = kAudioUnitSubType_DefaultOutput; -    desc.componentManufacturer = kAudioUnitManufacturer_Apple; -    desc.componentFlags = 0; -    desc.componentFlagsMask = 0; - -    // Find the original default output component -    mOriginalDefaultOutput = FindNextComponent(NULL, &desc); - -    // Register our own output component with the same parameters -    mVolumeAdjuster = RegisterComponent(&desc, NewComponentRoutineUPP(volume_catcher_component_entry), 0, NULL, NULL, NULL); - -    // Capture the original component, so we always get found instead. -    CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster); - -} - -static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage) -{ -    ComponentResult result = badComponentSelector; -    VolumeCatcherStorage *storage = (VolumeCatcherStorage*)componentStorage; - -    switch(cp->what) -    { -        case kComponentOpenSelect: -//          std::cerr << "kComponentOpenSelect" << std::endl; -            result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_open, uppCallComponentOpenProcInfo); -        break; - -        case kComponentCloseSelect: -//          std::cerr << "kComponentCloseSelect" << std::endl; -            result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_close, uppCallComponentCloseProcInfo); -            // CallComponentFunctionWithStorageProcInfo -        break; - -        default: -//          std::cerr << "Delegating selector: " << cp->what << " to component instance " << storage->delegate << std::endl; -            result = DelegateComponentCall(cp, storage->delegate); -        break; -    } - -    return result; -} - -static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self) -{ -    ComponentResult result = noErr; -    VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); - -    storage = new VolumeCatcherStorage; - -    storage->self = self; -    storage->delegate = NULL; - -    result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate)); - -    if(result != noErr) -    { -//      std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl; - -        // If we failed to open the delagate component, our open is going to fail.  Clean things up. -        delete storage; -    } -    else -    { -        // Success -- set up this component's storage -        SetComponentInstanceStorage(self, (Handle)storage); - -        // add this instance to the global list -        impl->mComponentInstances.push_back(storage); - -        // and set up the initial volume -        impl->setInstanceVolume(storage); -    } - -    return result; -} - -static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self) -{ -    ComponentResult result = noErr; - -    if(storage) -    { -        if(storage->delegate) -        { -            CloseComponent(storage->delegate); -            storage->delegate = NULL; -        } - -        VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); -        impl->mComponentInstances.remove(storage); -        delete[] storage; -    } - -    return result; -} - -void VolumeCatcherImpl::setVolume(F32 volume) -{ -    VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); -    impl->mVolume = volume; - -    // Iterate through all known instances, setting the volume on each. -    for(std::list<VolumeCatcherStorage*>::iterator iter = mComponentInstances.begin(); iter != mComponentInstances.end(); ++iter) -    { -        impl->setInstanceVolume(*iter); -    } -} - -void VolumeCatcherImpl::setPan(F32 pan) -{ -    VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); -    impl->mPan = pan; - -    // TODO: implement this. -    // This will probably require adding a "panner" audio unit to the chain somehow. -    // There's also a "3d mixer" component that we might be able to use... -} - -void VolumeCatcherImpl::setInstanceVolume(VolumeCatcherStorage *instance) -{ -//  std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl; - -    OSStatus err = noErr; - -    if(instance && instance->delegate) -    { -        err = AudioUnitSetParameter( -                instance->delegate, -                kHALOutputParam_Volume, -                kAudioUnitScope_Global, -                0, -                mVolume, -                0); -    } - -    if(err) -    { -//      std::cerr << "    AudioUnitSetParameter returned " << err << std::endl; -    } -} - -///////////////////////////////////////////////////// - -VolumeCatcher::VolumeCatcher() -{ -    pimpl = VolumeCatcherImpl::getInstance(); -} - -VolumeCatcher::~VolumeCatcher() -{ -    // Let the instance persist until exit. -} - -void VolumeCatcher::setVolume(F32 volume) -{ -    pimpl->setVolume(volume); -} - -void VolumeCatcher::setPan(F32 pan) -{ -    pimpl->setPan(pan); -} - -void VolumeCatcher::pump() -{ -    // No periodic tasks are necessary for this implementation. -} - -#if LL_DARWIN -#pragma GCC diagnostic warning "-Wdeprecated-declarations" -#endif +/**
 + * @file mac_volume_catcher.cpp
 + * @brief A macOS specific hack to control the volume level of all audio channels opened by a process.
 + *
 + * @cond
 + * $LicenseInfo:firstyear=2010&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
 + */
 +
 +/**************************************************************************************************************
 +    This code works by using CaptureComponent to capture the "Default Output" audio component
 +    (kAudioUnitType_Output/kAudioUnitSubType_DefaultOutput) and delegating all calls to the original component.
 +    It does this just to keep track of all instances of the default output component, so that it can set the
 +    kHALOutputParam_Volume parameter on all of them to adjust the output volume.
 +**************************************************************************************************************/
 +
 +#include "volume_catcher.h"
 +
 +#include <QuickTime/QuickTime.h>
 +#include <AudioUnit/AudioUnit.h>
 +#include <list>
 +
 +#if LL_DARWIN
 +#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 +#endif
 +
 +struct VolumeCatcherStorage;
 +
 +class VolumeCatcherImpl
 +{
 +public:
 +
 +    void setVolume(F32 volume);
 +    void setPan(F32 pan);
 +
 +    void setInstanceVolume(VolumeCatcherStorage *instance);
 +
 +    std::list<VolumeCatcherStorage*> mComponentInstances;
 +    Component mOriginalDefaultOutput;
 +    Component mVolumeAdjuster;
 +
 +    static VolumeCatcherImpl *getInstance();
 +private:
 +    // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
 +    VolumeCatcherImpl();
 +    static VolumeCatcherImpl *sInstance;
 +
 +    // The singlar instance of this class is expected to last until the process exits.
 +    // To ensure this, we declare the destructor here but never define it, so any code which attempts to destroy the instance will not link.
 +    ~VolumeCatcherImpl();
 +
 +    F32 mVolume;
 +    F32 mPan;
 +};
 +
 +VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;;
 +
 +struct VolumeCatcherStorage
 +{
 +    ComponentInstance self;
 +    ComponentInstance delegate;
 +};
 +
 +static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage);
 +static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self);
 +static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self);
 +
 +VolumeCatcherImpl *VolumeCatcherImpl::getInstance()
 +{
 +    if(!sInstance)
 +    {
 +        sInstance = new VolumeCatcherImpl;
 +    }
 +
 +    return sInstance;
 +}
 +
 +VolumeCatcherImpl::VolumeCatcherImpl()
 +{
 +    mVolume = 1.0;  // default to full volume
 +    mPan = 0.0;     // and center pan
 +
 +    ComponentDescription desc;
 +    desc.componentType = kAudioUnitType_Output;
 +    desc.componentSubType = kAudioUnitSubType_DefaultOutput;
 +    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
 +    desc.componentFlags = 0;
 +    desc.componentFlagsMask = 0;
 +
 +    // Find the original default output component
 +    mOriginalDefaultOutput = FindNextComponent(NULL, &desc);
 +
 +    // Register our own output component with the same parameters
 +    mVolumeAdjuster = RegisterComponent(&desc, NewComponentRoutineUPP(volume_catcher_component_entry), 0, NULL, NULL, NULL);
 +
 +    // Capture the original component, so we always get found instead.
 +    CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster);
 +
 +}
 +
 +static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage)
 +{
 +    ComponentResult result = badComponentSelector;
 +    VolumeCatcherStorage *storage = (VolumeCatcherStorage*)componentStorage;
 +
 +    switch(cp->what)
 +    {
 +        case kComponentOpenSelect:
 +//          std::cerr << "kComponentOpenSelect" << std::endl;
 +            result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_open, uppCallComponentOpenProcInfo);
 +        break;
 +
 +        case kComponentCloseSelect:
 +//          std::cerr << "kComponentCloseSelect" << std::endl;
 +            result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_close, uppCallComponentCloseProcInfo);
 +            // CallComponentFunctionWithStorageProcInfo
 +        break;
 +
 +        default:
 +//          std::cerr << "Delegating selector: " << cp->what << " to component instance " << storage->delegate << std::endl;
 +            result = DelegateComponentCall(cp, storage->delegate);
 +        break;
 +    }
 +
 +    return result;
 +}
 +
 +static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self)
 +{
 +    ComponentResult result = noErr;
 +    VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
 +
 +    storage = new VolumeCatcherStorage;
 +
 +    storage->self = self;
 +    storage->delegate = NULL;
 +
 +    result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate));
 +
 +    if(result != noErr)
 +    {
 +//      std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl;
 +
 +        // If we failed to open the delagate component, our open is going to fail.  Clean things up.
 +        delete storage;
 +    }
 +    else
 +    {
 +        // Success -- set up this component's storage
 +        SetComponentInstanceStorage(self, (Handle)storage);
 +
 +        // add this instance to the global list
 +        impl->mComponentInstances.push_back(storage);
 +
 +        // and set up the initial volume
 +        impl->setInstanceVolume(storage);
 +    }
 +
 +    return result;
 +}
 +
 +static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self)
 +{
 +    ComponentResult result = noErr;
 +
 +    if(storage)
 +    {
 +        if(storage->delegate)
 +        {
 +            CloseComponent(storage->delegate);
 +            storage->delegate = NULL;
 +        }
 +
 +        VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
 +        impl->mComponentInstances.remove(storage);
 +        delete[] storage;
 +    }
 +
 +    return result;
 +}
 +
 +void VolumeCatcherImpl::setVolume(F32 volume)
 +{
 +    VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
 +    impl->mVolume = volume;
 +
 +    // Iterate through all known instances, setting the volume on each.
 +    for(std::list<VolumeCatcherStorage*>::iterator iter = mComponentInstances.begin(); iter != mComponentInstances.end(); ++iter)
 +    {
 +        impl->setInstanceVolume(*iter);
 +    }
 +}
 +
 +void VolumeCatcherImpl::setPan(F32 pan)
 +{
 +    VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
 +    impl->mPan = pan;
 +
 +    // TODO: implement this.
 +    // This will probably require adding a "panner" audio unit to the chain somehow.
 +    // There's also a "3d mixer" component that we might be able to use...
 +}
 +
 +void VolumeCatcherImpl::setInstanceVolume(VolumeCatcherStorage *instance)
 +{
 +//  std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl;
 +
 +    OSStatus err = noErr;
 +
 +    if(instance && instance->delegate)
 +    {
 +        err = AudioUnitSetParameter(
 +                instance->delegate,
 +                kHALOutputParam_Volume,
 +                kAudioUnitScope_Global,
 +                0,
 +                mVolume,
 +                0);
 +    }
 +
 +    if(err)
 +    {
 +//      std::cerr << "    AudioUnitSetParameter returned " << err << std::endl;
 +    }
 +}
 +
 +/////////////////////////////////////////////////////
 +
 +VolumeCatcher::VolumeCatcher()
 +{
 +    pimpl = VolumeCatcherImpl::getInstance();
 +}
 +
 +VolumeCatcher::~VolumeCatcher()
 +{
 +    // Let the instance persist until exit.
 +}
 +
 +void VolumeCatcher::setVolume(F32 volume)
 +{
 +    pimpl->setVolume(volume);
 +}
 +
 +void VolumeCatcher::setPan(F32 pan)
 +{
 +    pimpl->setPan(pan);
 +}
 +
 +void VolumeCatcher::pump()
 +{
 +    // No periodic tasks are necessary for this implementation.
 +}
 +
 +#if LL_DARWIN
 +#pragma GCC diagnostic warning "-Wdeprecated-declarations"
 +#endif
 diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index bcab0eea72..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 OS X 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;
 +}
 | 
