/** * @file llwebbrowserctrl.cpp * @brief Web browser UI control * * $LicenseInfo:firstyear=2006&license=viewergpl$ * * Copyright (c) 2006-2008, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llwebbrowserctrl.h" // viewer includes #include "llfloaterhtml.h" #include "llfloaterworldmap.h" #include "lluictrlfactory.h" #include "llurldispatcher.h" #include "llurlsimstring.h" #include "llviewborder.h" #include "llviewercontrol.h" #include "llviewerwindow.h" #include "llnotify.h" #include "llweb.h" #include "llrender.h" // linden library includes #include "llfocusmgr.h" extern BOOL gRestoreGL; // Setting the mozilla buffer width to 2048 exactly doesn't work, since it pads its rowbytes a bit, pushing the texture width over 2048. // 2000 should give enough headroom for any amount of padding it cares to add. const S32 MAX_DIMENSION = 2000; const S32 MAX_TEXTURE_DIMENSION = 2048; static LLRegisterWidget r("web_browser"); LLWebBrowserCtrl::LLWebBrowserCtrl( const std::string& name, const LLRect& rect ) : LLUICtrl( name, rect, FALSE, NULL, NULL ), mTextureDepthBytes( 4 ), mWebBrowserImage( 0 ), mEmbeddedBrowserWindowId( 0 ), mBorder(NULL), mFrequentUpdates( true ), mForceUpdate( false ), mOpenLinksInExternalBrowser( false ), mOpenLinksInInternalBrowser( false ), mOpenAppSLURLs( false ), mHomePageUrl( "" ), mIgnoreUIScale( true ), mAlwaysRefresh( false ), mExternalUrl( "" ), mMediaSource( 0 ), mTakeFocusOnClick( true ), mCurrentNavUrl( "about:blank" ) { S32 screen_width = mIgnoreUIScale ? llround((F32)getRect().getWidth() * LLUI::sGLScaleFactor.mV[VX]) : getRect().getWidth(); S32 screen_height = mIgnoreUIScale ? llround((F32)getRect().getHeight() * LLUI::sGLScaleFactor.mV[VY]) : getRect().getHeight(); LLMediaManager *mgr = LLMediaManager::getInstance(); if (!mgr) { llwarns << "cannot get media manager" << llendl; return; } mMediaSource = mgr->createSourceFromMimeType("http", "text/html" ); if ( !mMediaSource ) { llwarns << "media source create failed " << llendl; // return; } else { // mMediaSource->init(); mMediaSource->addCommand( LLMediaBase::COMMAND_START ); // observe the browser so we can trap HREF events) mMediaSource->addObserver(this); // create a new texture (based on LLDynamic texture) that will be used to display the output mWebBrowserImage = new LLWebBrowserTexture( screen_width, screen_height, this, mMediaSource ); } LLRect border_rect( 0, getRect().getHeight() + 2, getRect().getWidth() + 2, 0 ); mBorder = new LLViewBorder( std::string("web control border"), border_rect, LLViewBorder::BEVEL_IN ); addChild( mBorder ); } //////////////////////////////////////////////////////////////////////////////// // note: this is now a singleton and destruction happens via initClass() now LLWebBrowserCtrl::~LLWebBrowserCtrl() { LLMediaManager *mgr = LLMediaManager::getInstance(); if (!mgr) { llwarns << "cannot get media manager" << llendl; return; } if (mMediaSource) { mgr->destroySource(mMediaSource); mMediaSource = NULL; } if ( mWebBrowserImage ) { delete mWebBrowserImage; mWebBrowserImage = 0; }; } //////////////////////////////////////////////////////////////////////////////// // bool LLWebBrowserCtrl::addObserver( LLWebBrowserCtrlObserver* subjectIn ) { return mEventEmitter.addObserver( subjectIn ); } //////////////////////////////////////////////////////////////////////////////// // bool LLWebBrowserCtrl::remObserver( LLWebBrowserCtrlObserver* subjectIn ) { return mEventEmitter.remObserver( subjectIn ); } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::setBorderVisible( BOOL border_visible ) { if ( mBorder ) { mBorder->setVisible( border_visible ); }; }; //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::setTakeFocusOnClick( bool take_focus ) { mTakeFocusOnClick = take_focus; } //////////////////////////////////////////////////////////////////////////////// // set flag that forces the embedded browser to open links in the external system browser void LLWebBrowserCtrl::setOpenInExternalBrowser( bool valIn ) { mOpenLinksInExternalBrowser = valIn; }; //////////////////////////////////////////////////////////////////////////////// // set flag that forces the embedded browser to open links in the internal browser floater void LLWebBrowserCtrl::setOpenInInternalBrowser( bool valIn ) { mOpenLinksInInternalBrowser = valIn; }; //////////////////////////////////////////////////////////////////////////////// void LLWebBrowserCtrl::setOpenAppSLURLs( bool valIn ) { mOpenAppSLURLs = valIn; }; //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserCtrl::handleHover( S32 x, S32 y, MASK mask ) { convertInputCoords(x, y); if (mMediaSource) mMediaSource->mouseMove(x, y); return TRUE; } //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks ) { if (mMediaSource) mMediaSource->scrollByLines(clicks); return TRUE; } //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) { convertInputCoords(x, y); if (mMediaSource) { mMediaSource->mouseUp(x, y); // *HACK: LLMediaImplLLMozLib automatically takes focus on mouseup, // in addition to the onFocusReceived() call below. Undo this. JC if (!mTakeFocusOnClick) { mMediaSource->focus(false); gViewerWindow->focusClient(); } } gFocusMgr.setMouseCapture( NULL ); return TRUE; } //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserCtrl::handleMouseDown( S32 x, S32 y, MASK mask ) { convertInputCoords(x, y); if (mMediaSource) mMediaSource->mouseDown(x, y); gFocusMgr.setMouseCapture( this ); if (mTakeFocusOnClick) { setFocus( TRUE ); } return TRUE; } //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserCtrl::handleDoubleClick( S32 x, S32 y, MASK mask ) { convertInputCoords(x, y); if (mMediaSource) mMediaSource->mouseLeftDoubleClick( x, y ); gFocusMgr.setMouseCapture( this ); if (mTakeFocusOnClick) { setFocus( TRUE ); } return TRUE; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::onFocusReceived() { if (mMediaSource) mMediaSource->focus(true); LLUICtrl::onFocusReceived(); } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::onFocusLost() { if (mMediaSource) mMediaSource->focus(false); gViewerWindow->focusClient(); LLUICtrl::onFocusLost(); } //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserCtrl::handleKeyHere( KEY key, MASK mask ) { unsigned long media_key; // First, turn SL internal keycode enum into Mozilla keycode enum // We don't have to deal with printable characters here - they should // go through handleUnicodeChar(). This table could be more complete // than it is, but I think this covers all of the important // non-printables. switch (key) { case KEY_BACKSPACE: media_key = LL_MEDIA_KEY_BACKSPACE; break; case KEY_TAB: media_key = LL_MEDIA_KEY_TAB; break; case KEY_RETURN: media_key = LL_MEDIA_KEY_RETURN; break; case KEY_PAD_RETURN: media_key = LL_MEDIA_KEY_PAD_RETURN; break; case KEY_ESCAPE: media_key = LL_MEDIA_KEY_ESCAPE; break; case KEY_PAGE_UP: media_key = LL_MEDIA_KEY_PAGE_UP; break; case KEY_PAGE_DOWN: media_key = LL_MEDIA_KEY_PAGE_DOWN; break; case KEY_END: media_key = LL_MEDIA_KEY_END; break; case KEY_HOME: media_key = LL_MEDIA_KEY_HOME; break; case KEY_LEFT: media_key = LL_MEDIA_KEY_LEFT; break; case KEY_UP: media_key = LL_MEDIA_KEY_UP; break; case KEY_RIGHT: media_key = LL_MEDIA_KEY_RIGHT; break; case KEY_DOWN: media_key = LL_MEDIA_KEY_DOWN; break; case KEY_INSERT: media_key = LL_MEDIA_KEY_INSERT; break; case KEY_DELETE: media_key = LL_MEDIA_KEY_DELETE; break; default: llinfos << "Don't know how to map LL keycode " << U32(key) << " to DOM key. Ignoring." << llendl; return FALSE; // don't know how to map this key. } if (mMediaSource) mMediaSource->keyPress(media_key); return TRUE; } BOOL LLWebBrowserCtrl::handleUnicodeCharHere(llwchar uni_char) { // only accept 'printable' characters, sigh... if (uni_char >= 32 // discard 'control' characters && uni_char != 127) // SDL thinks this is 'delete' - yuck. { if (mMediaSource) mMediaSource->unicodeInput(uni_char); } return TRUE; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::onVisibilityChange ( BOOL new_visibility ) { // set state of frequent updates automatically if visibility changes if ( new_visibility ) { mFrequentUpdates = true; } else { mFrequentUpdates = false; } LLUICtrl::onVisibilityChange(new_visibility); } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) { S32 screen_width = mIgnoreUIScale ? llround((F32)width * LLUI::sGLScaleFactor.mV[VX]) : width; S32 screen_height = mIgnoreUIScale ? llround((F32)height * LLUI::sGLScaleFactor.mV[VY]) : height; // when floater is minimized, these sizes are negative if ( mWebBrowserImage && screen_height > 0 && screen_width > 0 ) { mWebBrowserImage->resize( screen_width, screen_height ); mForceUpdate = true; } LLUICtrl::reshape( width, height, called_from_parent ); } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::navigateBack() { if (mMediaSource) mMediaSource->navigateBack(); } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::navigateForward() { if (mMediaSource) mMediaSource->navigateForward(); } //////////////////////////////////////////////////////////////////////////////// // bool LLWebBrowserCtrl::canNavigateBack() { if (mMediaSource) return mMediaSource->canNavigateBack(); else return false; } //////////////////////////////////////////////////////////////////////////////// // bool LLWebBrowserCtrl::canNavigateForward() { if (mMediaSource) return mMediaSource->canNavigateForward(); else return false; } //////////////////////////////////////////////////////////////////////////////// // bool LLWebBrowserCtrl::set404RedirectUrl( std::string redirect_url ) { if(mMediaSource) return mMediaSource->set404RedirectUrl( redirect_url ); else return false; } //////////////////////////////////////////////////////////////////////////////// // bool LLWebBrowserCtrl::clr404RedirectUrl() { if(mMediaSource) return mMediaSource->clr404RedirectUrl(); else return false; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::navigateTo( std::string urlIn ) { // don't browse to anything that starts with secondlife:// or sl:// const std::string protocol1 = "secondlife://"; const std::string protocol2 = "sl://"; if ((LLStringUtil::compareInsensitive(urlIn.substr(0, protocol1.length()), protocol1) == 0) || (LLStringUtil::compareInsensitive(urlIn.substr(0, protocol2.length()), protocol2) == 0)) { // TODO: Print out/log this attempt? // llinfos << "Rejecting attempt to load restricted website :" << urlIn << llendl; return; } if (mMediaSource) { mCurrentNavUrl = urlIn; mMediaSource->navigateTo(urlIn); } } void LLWebBrowserCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in ) { std::string language = gSavedSettings.getString("Language"); if(language == "default") { language = gSavedSettings.getString("SystemLanguage"); } std::string delim = gDirUtilp->getDirDelimiter(); std::string filename; filename += subdir; filename += delim; filename += filename_in; std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename); if (gDirUtilp->fileExists(expanded_filename)) { navigateTo(expanded_filename); return; } if (language != "en-us") { expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename); if (gDirUtilp->fileExists(expanded_filename)) { navigateTo(expanded_filename); return; } } llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::navigateHome() { if( mHomePageUrl.length() ) { if (mMediaSource) mMediaSource->navigateTo(mHomePageUrl); }; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::setHomePageUrl( const std::string urlIn ) { mHomePageUrl = urlIn; } //////////////////////////////////////////////////////////////////////////////// // bool LLWebBrowserCtrl::setCaretColor(unsigned int red, unsigned int green, unsigned int blue) { if (mMediaSource) return mMediaSource->setCaretColor(red, green, blue); else return false; } //////////////////////////////////////////////////////////////////////////////// // std::string LLWebBrowserCtrl::getHomePageUrl() { return mHomePageUrl; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserCtrl::draw() { if ( ! mWebBrowserImage ) return; if ( gRestoreGL == 1 ) { LLRect r = getRect(); mMediaSource->updateMedia(); reshape( r.getWidth(), r.getHeight(), FALSE ); return; }; // NOTE: optimization needed here - probably only need to do this once // unless tearoffs change the parent which they probably do. const LLUICtrl* ptr = findRootMostFocusRoot(); if ( ptr && ptr->hasFocus() ) { setFrequentUpdates( true ); } else { setFrequentUpdates( false ); }; // alpha off for this LLGLSUIDefault gls_ui; LLGLDisable gls_alphaTest( GL_ALPHA_TEST ); gGL.pushMatrix(); { if (mIgnoreUIScale) { glLoadIdentity(); // font system stores true screen origin, need to scale this by UI scale factor // to get render origin for this view (with unit scale) gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::sGLScaleFactor.mV[VX]), floorf(LLFontGL::sCurOrigin.mY * LLUI::sGLScaleFactor.mV[VY]), LLFontGL::sCurOrigin.mZ); } // scale texture to fit the space using texture coords mWebBrowserImage->bindTexture(); gGL.color4fv( LLColor4::white.mV ); F32 max_u = ( F32 )mWebBrowserImage->getBrowserWidth() / ( F32 )mWebBrowserImage->getWidth(); F32 max_v = ( F32 )mWebBrowserImage->getBrowserHeight() / ( F32 )mWebBrowserImage->getHeight(); // draw the browser gGL.setSceneBlendType(LLRender::BT_REPLACE); gGL.begin( LLVertexBuffer::QUADS ); { // render using web browser reported width and height, instead of trying to invert GL scale gGL.texCoord2f( max_u, max_v ); gGL.vertex2i( mWebBrowserImage->getBrowserWidth(), mWebBrowserImage->getBrowserHeight() ); gGL.texCoord2f( 0.f, max_v ); gGL.vertex2i( 0, mWebBrowserImage->getBrowserHeight() ); gGL.texCoord2f( 0.f, 0.f ); gGL.vertex2i( 0, 0 ); gGL.texCoord2f( max_u, 0.f ); gGL.vertex2i( mWebBrowserImage->getBrowserWidth(), 0 ); } gGL.end(); gGL.setSceneBlendType(LLRender::BT_ALPHA); } gGL.popMatrix(); // highlight if keyboard focus here. (TODO: this needs some work) if ( mBorder->getVisible() ) mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) ); LLUICtrl::draw(); } void LLWebBrowserCtrl::convertInputCoords(S32& x, S32& y) { x = mIgnoreUIScale ? llround((F32)x * LLUI::sGLScaleFactor.mV[VX]) : x; y = mIgnoreUIScale ? llround((F32)(getRect().getHeight() - y) * LLUI::sGLScaleFactor.mV[VY]) : getRect().getHeight() - y; } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onNavigateBegin( const EventType& eventIn ) { LLWebBrowserCtrlEvent event( eventIn.getStringValue() ); mEventEmitter.update( &LLWebBrowserCtrlObserver::onNavigateBegin, event ); } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onNavigateComplete( const EventType& eventIn ) { // chain this event on to observers of an instance of LLWebBrowserCtrl LLWebBrowserCtrlEvent event( eventIn.getStringValue() ); mEventEmitter.update( &LLWebBrowserCtrlObserver::onNavigateComplete, event ); } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onUpdateProgress( const EventType& eventIn ) { // chain this event on to observers of an instance of LLWebBrowserCtrl LLWebBrowserCtrlEvent event( eventIn.getIntValue() ); mEventEmitter.update( &LLWebBrowserCtrlObserver::onUpdateProgress, event ); } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onStatusTextChange( const EventType& eventIn ) { // chain this event on to observers of an instance of LLWebBrowserCtrl LLWebBrowserCtrlEvent event( eventIn.getStringValue() ); mEventEmitter.update( &LLWebBrowserCtrlObserver::onStatusTextChange, event ); } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onLocationChange( const EventType& eventIn ) { const LLURI url(eventIn.getStringValue()); LLSD queryMap(url.queryMap()); std::string redirect_http_hack = queryMap["redirect-http-hack"].asString(); if (!redirect_http_hack.empty()) { const bool from_external_browser = false; LLURLDispatcher::dispatch(redirect_http_hack, from_external_browser); return; } // chain this event on to observers of an instance of LLWebBrowserCtrl LLWebBrowserCtrlEvent event( eventIn.getStringValue() ); mEventEmitter.update( &LLWebBrowserCtrlObserver::onLocationChange, event ); } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onMediaContentsChange( const EventType& event_in ) { if ( mWebBrowserImage ) { mWebBrowserImage->setNeedsUpdate(); mForceUpdate = true; } } //////////////////////////////////////////////////////////////////////////////// // static void LLWebBrowserCtrl::onClickLinkExternalTarget( S32 option, void* userdata ) { if ( 0 == option ) { // open in external browser because we don't support // creation of our own secondary browser windows LLWeb::loadURLExternal( ((LLWebBrowserCtrl*)userdata)->mExternalUrl ); }; } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onClickLinkHref( const EventType& eventIn ) { // if there is a value for the target (passed in stringValueEx) if ( eventIn.getStringValueEx().length() ) { // if the target = "_new" if ( eventIn.getStringValueEx() == "_external" ) { mExternalUrl = eventIn.getStringValue(); gViewerWindow->alertXml( "WebLaunchExternalTarget", onClickLinkExternalTarget, (void*)this ); return; }; }; const std::string protocol1( "http://" ); const std::string protocol2( "https://" ); if( mOpenLinksInExternalBrowser ) { if ( eventIn.getStringValue().length() ) { if ( LLStringUtil::compareInsensitive( eventIn.getStringValue().substr( 0, protocol1.length() ), protocol1 ) == 0 || LLStringUtil::compareInsensitive( eventIn.getStringValue().substr( 0, protocol2.length() ), protocol2 ) == 0 ) { LLWeb::loadURLExternal( eventIn.getStringValue() ); }; }; } else if( mOpenLinksInInternalBrowser ) { if ( eventIn.getStringValue().length() ) { if ( LLStringUtil::compareInsensitive( eventIn.getStringValue().substr( 0, protocol1.length() ), protocol1 ) == 0 || LLStringUtil::compareInsensitive( eventIn.getStringValue().substr( 0, protocol2.length() ), protocol2 ) == 0 ) { // If we spawn a new LLFloaterHTML, assume we want it to // follow this LLWebBrowserCtrl's setting for whether or // not to open secondlife:///app/ links. JC. const bool open_links_externally = false; LLFloaterHtml::getInstance()->show( eventIn.getStringValue(), "Second Life Browser", open_links_externally, mOpenAppSLURLs); }; }; }; // chain this event on to observers of an instance of LLWebBrowserCtrl LLWebBrowserCtrlEvent event( eventIn.getStringValue(), eventIn.getStringValueEx() ); mEventEmitter.update( &LLWebBrowserCtrlObserver::onClickLinkHref, event ); } //////////////////////////////////////////////////////////////////////////////// // virtual void LLWebBrowserCtrl::onClickLinkNoFollow( const EventType& eventIn ) { std::string url = eventIn.getStringValue(); if (LLURLDispatcher::isSLURLCommand(url) && !mOpenAppSLURLs) { // block handling of this secondlife:///app/ URL LLNotifyBox::showXml("UnableToOpenCommandURL"); return; } const bool from_external_browser = false; LLURLDispatcher::dispatch(url, from_external_browser); // chain this event on to observers of an instance of LLWebBrowserCtrl LLWebBrowserCtrlEvent event( eventIn.getStringValue() ); mEventEmitter.update( &LLWebBrowserCtrlObserver::onClickLinkNoFollow, event ); } //////////////////////////////////////////////////////////////////////////////// // LLWebBrowserTexture::LLWebBrowserTexture( S32 width, S32 height, LLWebBrowserCtrl* browserCtrl, LLMediaBase *media_source ) : LLDynamicTexture( 512, 512, 4, ORDER_FIRST, TRUE ), mLastBrowserDepth( 0 ), mNeedsUpdate( true ), mWebBrowserCtrl( browserCtrl ), mMediaSource(media_source) { mElapsedTime.start(); resize( width, height ); } //////////////////////////////////////////////////////////////////////////////// // LLWebBrowserTexture::~LLWebBrowserTexture() { mElapsedTime.stop(); } //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserTexture::needsRender() { if ( mWebBrowserCtrl->getFrequentUpdates() || mWebBrowserCtrl->getAlwaysRefresh() || mWebBrowserCtrl->getForceUpdate() ) { // only update mozilla/texture occasionally if ( mElapsedTime.getElapsedTimeF32() > ( 1.0f / 15.0f ) ) { return TRUE; } } return FALSE; } //////////////////////////////////////////////////////////////////////////////// // BOOL LLWebBrowserTexture::render() { if (!mMediaSource) return FALSE; // frequent updates turned on? if ( mWebBrowserCtrl->getFrequentUpdates() || mWebBrowserCtrl->getAlwaysRefresh() || mWebBrowserCtrl->getForceUpdate() ) { if ( mNeedsUpdate ) { const unsigned char* pixels = mMediaSource->getMediaData(); if ( ! pixels ) return FALSE; mNeedsUpdate = false; mWebBrowserCtrl->setForceUpdate(false); S32 media_depth = mMediaSource->getMediaDepth(); S32 media_width = mMediaSource->getMediaWidth(); S32 media_height = mMediaSource->getMediaHeight(); // these are both invalid conditions and should never happen but SL-27583 indicates it does if ((media_width < 1) || (media_depth < 2)) return FALSE; // Browser depth hasn't changed. if(mLastBrowserDepth == media_depth) { S32 width = llmin(media_width, mBrowserWidth); S32 height = llmin(media_height, mBrowserHeight); S32 media_data_width = mMediaSource->getMediaDataWidth(); S32 media_data_height = mMediaSource->getMediaDataHeight(); // Just grab the pixels. if ( media_data_width > 0 && media_data_height > 0 && media_data_width < MAX_DIMENSION && media_data_height < MAX_DIMENSION ) { mTexture->setSubImage( pixels, media_data_width, media_data_height, 0, 0, llmin(width, media_data_width), llmin(media_data_height, height) ); }; } else { // Browser depth has changed -- need to recreate texture to match. resize(mBrowserWidth, mBrowserHeight); }; }; }; return TRUE; } //////////////////////////////////////////////////////////////////////////////// // S32 LLWebBrowserTexture::getBrowserWidth() { return mBrowserWidth; } //////////////////////////////////////////////////////////////////////////////// // S32 LLWebBrowserTexture::getBrowserHeight() { return mBrowserHeight; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserTexture::setNeedsUpdate() { mNeedsUpdate = true; } //////////////////////////////////////////////////////////////////////////////// // void LLWebBrowserTexture::resize( S32 new_width, S32 new_height ) { if (!mMediaSource) return; F32 scale_ratio = 1.f; if (new_width > MAX_DIMENSION) { scale_ratio = (F32)MAX_DIMENSION / (F32)new_width; } if (new_height > MAX_DIMENSION) { scale_ratio = llmin(scale_ratio, (F32)MAX_DIMENSION / (F32)new_height); } mBrowserWidth = llround(scale_ratio * (F32)new_width); mBrowserHeight = llround(scale_ratio * (F32)new_height); mMediaSource->setRequestedMediaSize(mBrowserWidth, mBrowserHeight); // HACK - this code is executing a render - resize should call render() instead // (and render() should be refactored so it doesn't call resize()) mMediaSource->updateMedia(); const unsigned char* pixels = mMediaSource->getMediaData(); S32 media_width = mMediaSource->getMediaWidth(); S32 media_height = mMediaSource->getMediaHeight(); S32 media_depth = mMediaSource->getMediaDepth(); // these are both invalid conditions and should never happen but SL-27583 indicates it does if ( media_width < 1 || media_depth < 2 ) return; releaseGLTexture(); // calculate the next power of 2 bigger than reqquested size for width and height for ( mWidth = 1; mWidth < mBrowserWidth; mWidth <<= 1 ) { if ( mWidth >= MAX_TEXTURE_DIMENSION ) { break; }; }; for ( mHeight = 1; mHeight < mBrowserHeight; mHeight <<= 1 ) { if ( mHeight >= MAX_TEXTURE_DIMENSION ) { break; }; }; LLGLint internal_format; LLGLenum primary_format; LLGLenum type_format; BOOL swap_bytes = FALSE; switch(media_depth) { default: case 4: internal_format = GL_RGBA8; primary_format = GL_BGRA_EXT; #if LL_DARWIN #if LL_BIG_ENDIAN type_format = GL_UNSIGNED_INT_8_8_8_8_REV; #else type_format = GL_UNSIGNED_INT_8_8_8_8; #endif #else // windows or linux type_format = GL_UNSIGNED_BYTE; #endif break; case 2: #if LL_DARWIN internal_format = GL_RGBA8; primary_format = GL_BGRA_EXT; type_format = GL_UNSIGNED_SHORT_1_5_5_5_REV; #if LL_LITTLE_ENDIAN swap_bytes = TRUE; #endif #else // windows or linux // MBW -- XXX -- This is just a guess on my part. Someone needs to verify which GL texture format matches the 16-bit format used on windows. internal_format = GL_RGB8; primary_format = GL_RGB; type_format = GL_UNSIGNED_SHORT_5_6_5; #endif break; } // will create mWidth * mHeight sized texture, using BGR ordering LLDynamicTexture::generateGLTexture(internal_format, primary_format, type_format, swap_bytes); S32 width = llmin(media_width, mBrowserWidth); S32 height = llmin(media_height, mBrowserHeight); S32 media_data_width = mMediaSource->getMediaDataWidth(); S32 media_data_height = mMediaSource->getMediaDataHeight(); if (pixels) { mTexture->setSubImage( pixels, media_data_width, media_data_height, 0, 0, width, height ); } mLastBrowserDepth = media_depth; } LLView* LLWebBrowserCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { std::string name("web_browser"); node->getAttributeString("name", name); std::string start_url("start_url"); node->getAttributeString("start_url", start_url ); BOOL border_visible = true; node->getAttributeBOOL("border_visible", border_visible); LLRect rect; createRect(node, rect, parent, LLRect()); LLWebBrowserCtrl* web_browser = new LLWebBrowserCtrl( name, rect ); if(node->hasAttribute("caret_color")) { LLColor4 color; LLUICtrlFactory::getAttributeColor(node, "caret_color", color); LLColor4U colorU = LLColor4U(color); web_browser->setCaretColor( colorU.mV[0], colorU.mV[1], colorU.mV[2] ); } BOOL ignore_ui_scale = web_browser->getIgnoreUIScale(); node->getAttributeBOOL("ignore_ui_scale", ignore_ui_scale); web_browser->setIgnoreUIScale((bool)ignore_ui_scale); web_browser->initFromXML(node, parent); web_browser->setHomePageUrl( start_url ); web_browser->setBorderVisible( border_visible ); web_browser->navigateHome(); return web_browser; } std::string LLWebBrowserCtrl::getCurrentNavUrl() { return mCurrentNavUrl; }