From 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 Mon Sep 17 00:00:00 2001
From: Jacek Antonelli
Date: Fri, 15 Aug 2008 23:44:46 -0500
Subject: Second Life viewer sources 1.13.2.12

---
 linden/indra/newview/llwebbrowserctrl.cpp | 641 ++++++++++++++++++++++++++++++
 1 file changed, 641 insertions(+)
 create mode 100644 linden/indra/newview/llwebbrowserctrl.cpp

(limited to 'linden/indra/newview/llwebbrowserctrl.cpp')

diff --git a/linden/indra/newview/llwebbrowserctrl.cpp b/linden/indra/newview/llwebbrowserctrl.cpp
new file mode 100644
index 0000000..afbff65
--- /dev/null
+++ b/linden/indra/newview/llwebbrowserctrl.cpp
@@ -0,0 +1,641 @@
+/**
+ * @file llwebbrowserctrl.cpp
+ * @brief Web browser UI control
+ *
+ * Copyright (c) 2006-2007, Linden Research, Inc.
+ * 
+ * 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#if LL_LIBXUL_ENABLED
+
+#include "llwebbrowserctrl.h"
+#include "llviewborder.h"
+#include "llviewerwindow.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llweb.h"
+#include "viewer.h"
+#include "llpanellogin.h"
+
+// 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;
+
+LLWebBrowserCtrl::LLWebBrowserCtrl( const std::string& name, const LLRect& rect ) :
+	LLUICtrl( name, rect, FALSE, NULL, NULL ),
+	mEmbeddedBrowserWindowId( 0 ),
+	mTextureDepthBytes( 4 ),
+	mBorder( 0 ),
+	mFrequentUpdates( true ),
+	mOpenLinksInExternalBrowser( false ),
+	mHomePageUrl( "" )
+{
+	// create a new browser window
+	mEmbeddedBrowserWindowId = LLMozLib::getInstance()->createBrowserWindow( gViewerWindow->getPlatformWindow(), mRect.getWidth(), mRect.getHeight() );
+
+	// change color to black so transisitons aren't so noticable (this should be in XML eventually)
+	LLMozLib::getInstance()->setBackgroundColor( mEmbeddedBrowserWindowId, 0x00, 0x00, 0x00 );
+
+	// observe the browser so we can trap HREF events)
+	LLMozLib::getInstance()->addObserver( mEmbeddedBrowserWindowId, this );
+
+	// create a new texture (based on LLDynamic texture) that will be used to display the output
+	mWebBrowserImage = new LLWebBrowserTexture( mRect.getWidth(), mRect.getHeight(), this, mEmbeddedBrowserWindowId );
+
+	LLRect border_rect( 0, mRect.getHeight() + 2, mRect.getWidth() + 2, 0 );
+	mBorder = new LLViewBorder( "web control border", border_rect, LLViewBorder::BEVEL_IN );
+	addChild( mBorder );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// note: this is now a singleton and destruction happens via initClass() now
+LLWebBrowserCtrl::~LLWebBrowserCtrl()
+{
+	LLMozLib::getInstance()->remObserver( mEmbeddedBrowserWindowId, this );
+
+	LLMozLib::getInstance()->destroyBrowserWindow( mEmbeddedBrowserWindowId );
+
+	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 );
+	};
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// allows access to the raw web browser window by consumers of this class
+void LLWebBrowserCtrl::setOpenInExternalBrowser( bool valIn )
+{
+	mOpenLinksInExternalBrowser = valIn;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLWebBrowserCtrl::handleHover( S32 x, S32 y, MASK mask )
+{
+	LLMozLib::getInstance()->mouseMove( mEmbeddedBrowserWindowId, x, mRect.getHeight() - y );
+
+	return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLWebBrowserCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+	LLMozLib::getInstance()->scrollByLines( mEmbeddedBrowserWindowId, clicks );
+
+	// note: this isn't really necessary right now since the page is updated
+	// on a timer but if that becomes too burdensome and the page is only updated
+	// once after load then this will be nexessary
+	LLMozLib::getInstance()->grabBrowserWindow( mEmbeddedBrowserWindowId );
+
+	return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLWebBrowserCtrl::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+	LLMozLib::getInstance()->mouseUp( mEmbeddedBrowserWindowId, x, mRect.getHeight() - y );
+
+	gViewerWindow->setMouseCapture( 0, 0 );
+
+	return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLWebBrowserCtrl::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+	LLMozLib::getInstance()->mouseDown( mEmbeddedBrowserWindowId, x, mRect.getHeight() - y );
+
+	gViewerWindow->setMouseCapture( this, 0 );
+
+	setFocus( TRUE );
+
+	return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::onFocusReceived()
+{
+	LLMozLib::getInstance()->focusBrowser( mEmbeddedBrowserWindowId, true );
+
+	LLUICtrl::onFocusReceived();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::onFocusLost()
+{
+	LLMozLib::getInstance()->focusBrowser( mEmbeddedBrowserWindowId, false );
+
+	gViewerWindow->focusClient();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLWebBrowserCtrl::handleKey( KEY key, MASK mask, BOOL called_from_parent )
+{
+	LLMozLib::getInstance()->keyPress( mEmbeddedBrowserWindowId, key );
+
+	return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::onVisibilityChange ( BOOL curVisibilityIn )
+{
+	// set state of frequent updates automatically if visibility changes
+	if ( curVisibilityIn )
+	{
+		mFrequentUpdates = true;
+	}
+	else
+	{
+		mFrequentUpdates = false;
+	};
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::reshape( S32 width, S32 height, BOOL called_from_parent )
+{
+	// when floater is minimized, these sizes are negative
+	if ( height > 0 && width > 0 )
+		mWebBrowserImage->resize( width, height );
+
+	LLUICtrl::reshape( width, height, called_from_parent );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::navigateBack()
+{
+	LLMozLib::getInstance()->navigateBack( mEmbeddedBrowserWindowId );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::navigateForward()
+{
+	LLMozLib::getInstance()->navigateForward( mEmbeddedBrowserWindowId );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool LLWebBrowserCtrl::canNavigateBack()
+{
+	return LLMozLib::getInstance()->canNavigateBack( mEmbeddedBrowserWindowId );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool LLWebBrowserCtrl::canNavigateForward()
+{
+	return LLMozLib::getInstance()->canNavigateForward( mEmbeddedBrowserWindowId );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::navigateTo( std::string urlIn )
+{
+	const std::string protocol( "secondlife://" );
+
+	// don't browse to anything that starts with secondlife://
+	if ( urlIn.length() >= protocol.length() )
+		if ( LLString::compareInsensitive( urlIn.substr( 0, protocol.length() ).c_str(), protocol.c_str() ) != 0 )
+			LLMozLib::getInstance()->navigateTo( mEmbeddedBrowserWindowId, urlIn );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::navigateHome()
+{
+	if( mHomePageUrl.length() )
+	{
+		LLMozLib::getInstance()->navigateTo( mEmbeddedBrowserWindowId, mHomePageUrl );
+	};
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::setHomePageUrl( const std::string urlIn )
+{
+	mHomePageUrl = urlIn;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+std::string LLWebBrowserCtrl::getHomePageUrl()
+{
+	return 	mHomePageUrl;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLWebBrowserCtrl::draw()
+{
+	if ( ! getVisible() )
+		return;
+
+	// NOTE: optimization needed here - probably only need to do this once
+	// unless tearoffs change the parent which they problably do.
+	LLUICtrl* ptr = (LLUICtrl*)findRootMostFocusRoot();
+	if ( ptr && ptr->hasFocus() )
+	{
+		setFrequentUpdates( true );
+	}
+	else
+	{
+		setFrequentUpdates( false );
+	};
+
+	// alpha off for this
+	LLGLSUIDefault gls_ui;
+	LLGLDisable gls_alphaTest( GL_ALPHA_TEST );
+
+	// scale texture to fit the space using texture coords
+	mWebBrowserImage->bindTexture();
+	glColor4fv( LLColor4::white.mV );
+	F32 max_u = ( F32 )mWebBrowserImage->getBrowserWidth() / ( F32 )mWebBrowserImage->getWidth();
+	F32 max_v = ( F32 )mWebBrowserImage->getBrowserHeight() / ( F32 )mWebBrowserImage->getHeight();
+
+	// draw the browser
+	glBlendFunc( GL_ONE, GL_ZERO );
+	glBegin( GL_QUADS );
+	{
+		glTexCoord2f( max_u, 0.f );
+		glVertex2i( mRect.getWidth(), mRect.getHeight() );
+
+		glTexCoord2f( 0.f, 0.f );
+		glVertex2i( 0, mRect.getHeight() );
+
+		glTexCoord2f( 0.f, max_v );
+		glVertex2i( 0, 0 );
+
+		glTexCoord2f( max_u, max_v );
+		glVertex2i( mRect.getWidth(), 0 );
+	}
+	glEnd();
+	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA  );
+
+	// highlight if keyboard focus here. (TODO: this needs some work)
+	if ( mBorder->getVisible() )
+		mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) );
+
+	LLUICtrl::draw();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// virtual
+void LLWebBrowserCtrl::onNavigateBegin( const EventType& eventIn )
+{
+	LLWebBrowserCtrlEvent event;
+	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;
+	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 )
+{
+	// chain this event on to observers of an instance of LLWebBrowserCtrl
+	LLWebBrowserCtrlEvent event( eventIn.getStringValue() );
+	mEventEmitter.update( &LLWebBrowserCtrlObserver::onLocationChange, event );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// virtual
+void LLWebBrowserCtrl::onClickLinkHref( const EventType& eventIn )
+{
+	const std::string protocol( "http://" );
+
+	if( mOpenLinksInExternalBrowser )
+		if ( eventIn.getStringValue().length() )
+			if ( LLString::compareInsensitive( eventIn.getStringValue().substr( 0, protocol.length() ).c_str(), protocol.c_str() ) == 0 )
+				LLWeb::loadURL( eventIn.getStringValue() );
+
+	// chain this event on to observers of an instance of LLWebBrowserCtrl
+	LLWebBrowserCtrlEvent event( eventIn.getStringValue() );
+	mEventEmitter.update( &LLWebBrowserCtrlObserver::onClickLinkHref, event );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// virtual
+void LLWebBrowserCtrl::onClickLinkSecondLife( const EventType& eventIn )
+{
+	const std::string protocol( "secondlife://" );
+
+	if ( eventIn.getStringValue().length() )
+	{
+		if ( LLString::compareInsensitive( eventIn.getStringValue().substr( 0, protocol.length() ).c_str(), protocol.c_str() ) == 0 )
+		{
+			// parse out sim name and coordinates
+			LLURLSimString::setString( eventIn.getStringValue() );
+			LLURLSimString::parse();
+
+			// if there is a world map
+			if ( gFloaterWorldMap )
+			{
+				#if ! LL_RELEASE_FOR_DOWNLOAD
+				llinfos << "MOZ> opening map to " << LLURLSimString::sInstance.mSimName.c_str() << " at " << LLURLSimString::sInstance.mX << "," << LLURLSimString::sInstance.mY << "," << LLURLSimString::sInstance.mZ << llendl;
+				#endif
+
+				// mark where the destination is
+				gFloaterWorldMap->trackURL( LLURLSimString::sInstance.mSimName.c_str(),
+											LLURLSimString::sInstance.mX,
+											LLURLSimString::sInstance.mY,
+											LLURLSimString::sInstance.mZ );
+
+				// display map
+				LLFloaterWorldMap::show( NULL, TRUE );
+			}
+			else
+			// if there is no world map, assume we're on the login page.. (this might be bad but I don't see a way to tell if you're at login or not)
+			{
+				// refresh the login page and force the location combo box to be visible
+				LLPanelLogin::refreshLocation( true );
+			};
+		};
+	};		
+
+	// chain this event on to observers of an instance of LLWebBrowserCtrl
+	LLWebBrowserCtrlEvent event( eventIn.getStringValue() );
+	mEventEmitter.update( &LLWebBrowserCtrlObserver::onClickLinkSecondLife, event );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+LLWebBrowserTexture::LLWebBrowserTexture( S32 width, S32 height, LLWebBrowserCtrl* browserCtrl, int browserWindowId ) :
+	LLDynamicTexture( 512, 512, 4, ORDER_FIRST, TRUE ),
+	mLastBrowserDepth( 0 ),
+	mWebBrowserCtrl( browserCtrl ),
+	mEmbeddedBrowserWindowId( browserWindowId )
+{
+	mElapsedTime.start();
+
+	resize( width, height );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+LLWebBrowserTexture::~LLWebBrowserTexture()
+{
+	mElapsedTime.stop();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLWebBrowserTexture::render()
+{
+	// frequent updates turned on?
+	if ( mWebBrowserCtrl->getFrequentUpdates() )
+	{
+		// only update mozilla/texture occasionally
+		if ( mElapsedTime.getElapsedTimeF32() > ( 1.0f / 15.0f ) )
+		{
+			mElapsedTime.reset();
+
+			const unsigned char* pixels = LLMozLib::getInstance()->grabBrowserWindow( mEmbeddedBrowserWindowId );
+
+			S32 actual_rowspan = LLMozLib::getInstance()->getBrowserRowSpan( mEmbeddedBrowserWindowId );
+			S32 browser_depth = LLMozLib::getInstance()->getBrowserDepth( mEmbeddedBrowserWindowId );
+
+			// these are both invalid conditions and should never happen but SL-27583 indicates it does
+			if ( actual_rowspan < 1 || browser_depth < 2 )
+				return FALSE;
+
+			S32 pagebuffer_width = actual_rowspan / browser_depth;
+			
+			if(mLastBrowserDepth == browser_depth)
+			{
+				// Browser depth hasn't changed.  Just grab the pixels.
+
+				mTexture->setSubImage( pixels,
+										pagebuffer_width, mBrowserHeight,
+											0, 0, pagebuffer_width, mBrowserHeight );
+			}
+			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::resize( S32 new_width, S32 new_height )
+{
+	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);
+
+	LLMozLib::getInstance()->setSize( mEmbeddedBrowserWindowId, mBrowserWidth, mBrowserHeight );
+
+	const unsigned char* pixels = LLMozLib::getInstance()->grabBrowserWindow( mEmbeddedBrowserWindowId );
+
+	S32 actual_rowspan = LLMozLib::getInstance()->getBrowserRowSpan( mEmbeddedBrowserWindowId );
+	S32 browser_depth = LLMozLib::getInstance()->getBrowserDepth( mEmbeddedBrowserWindowId );
+
+	// these are both invalid conditions and should never happen but SL-27583 indicates it does
+	if ( actual_rowspan < 1 || browser_depth < 2 )
+		return;
+
+	S32 pagebuffer_width = actual_rowspan / browser_depth;
+
+	// calculate the next power of 2 bigger than reqquested size for width and height
+	for ( mWidth = 1; mWidth < pagebuffer_width; 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(browser_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);
+	
+	mTexture->setSubImage( pixels,
+		pagebuffer_width, mBrowserHeight,
+			0, 0, pagebuffer_width, mBrowserHeight );
+	
+	mLastBrowserDepth = browser_depth;
+}
+
+LLView* LLWebBrowserCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+	LLString name("web_browser");
+	node->getAttributeString("name", name);
+
+	LLString 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 );
+
+	web_browser->initFromXML(node, parent);
+
+	web_browser->setHomePageUrl( start_url );
+
+	web_browser->setBorderVisible( border_visible );
+
+	web_browser->navigateHome();
+
+	return web_browser;
+}
+
+#endif // LL_LIBXUL_ENABLED
-- 
cgit v1.1