/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Linden Lab Inc. (http://lindenlab.com) code.
 *
 * The Initial Developer of the Original Code is:
 *   Callum Prentice (callum@ubrowser.com)
 *
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *  Callum Prentice (callum@ubrowser.com)
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

// seems to be required by LibXUL/Mozilla code to avoid crashes in their debug code, but only on Windows.
// undef'd at end of this
#ifdef _DEBUG
	#ifdef WIN32
		#define DEBUG 1
	#endif
#endif

// needed for the code in LLEmbeddedBrowserWindow::NotifyInvalidated() which will
// one day be moved to platform agnostic code when I find out how...
#ifdef WIN32
#include "windows.h"
#endif

#include "llembeddedbrowser.h"
#include "llembeddedbrowserwindow.h"

// Mozilla code has non-virtual destructors
#ifdef WIN32
#pragma warning( disable : 4291 ) // (no matching operator delete found; memory will not be freed if initialization throws an exception)
#pragma warning( disable : 4265 ) // "class has virtual functions, but destructor is not virtual"
#endif

#include "nsCWebBrowser.h"
#include "nsGUIEvent.h"
#include "nsICaret.h"
#include "nsIContent.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDOMWindow.h"
#include "nsIDOMEvent.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocument.h"
#include "nsIFrame.h"
#include "nsIHttpChannel.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIScrollableView.h"
#include "nsISelection.h"
#include "nsISelectionController.h"
#include "nsIWebBrowserChrome.h"
#include "nsIWebBrowserChromeFocus.h"
#include "nsIWebBrowserFocus.h"
#include "nsIWebProgress.h"
#include "nsIWebProgressListener.h"
#include "nsPresContext.h"
#include "nsProfileDirServiceProvider.h"
#include "nsXPCOMGlue.h"
#include "nsXULAppAPI.h"

#include "llembeddedbrowserwindow.h"

#include <iostream>

#ifdef WIN32
#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual"
#endif

////////////////////////////////////////////////////////////////////////////////
//
LLEmbeddedBrowserWindow::LLEmbeddedBrowserWindow() :
	mParent( 0 ),
	mWebBrowser( nsnull ),
	mBaseWindow( nsnull ),
	mWindowId( 0 ),
	mPercentComplete( 0 ),
	mBrowserWidth( 0 ),
	mBrowserHeight( 0 ),
	mBrowserDepth( 4 ),
	mPageBuffer( 0 ),
	mEnabled( true ),
	mCurrentUri( "" ),
	mStatusText( "" ),
	mClickHref( "" ),
	mClickTarget( "" ),
	mNoFollowScheme( "secondlife://" ),
	mBkgRed( 0xff ),
	mBkgGreen( 0xff ),
	mBkgBlue( 0xff ),
	mCaretRed( 0x0 ),
	mCaretGreen( 0x0 ),
	mCaretBlue( 0x0 ),
	m404RedirectUrl( "" ),
	mFlipBitmap( false )
{
}

////////////////////////////////////////////////////////////////////////////////
//
LLEmbeddedBrowserWindow::~LLEmbeddedBrowserWindow()
{
	if ( mWebNav )
	{
		mWebNav->Stop ( nsIWebNavigation::STOP_ALL );
		mWebNav = nsnull;
	};

	if ( mBaseWindow )
	{
		mBaseWindow->Destroy();
		mBaseWindow = nsnull;
	};

	if ( mPageBuffer )
	{
		delete[] mPageBuffer;
		mPageBuffer = 0;
	};
}

////////////////////////////////////////////////////////////////////////////////
//
nsresult LLEmbeddedBrowserWindow::createBrowser( void* nativeWindowHandleIn, PRInt32 widthIn, PRInt32 heightIn, nsIWebBrowser **aBrowser )
{
	NS_ENSURE_ARG_POINTER(aBrowser);
	*aBrowser = nsnull;

	nsresult rv;
	mWebBrowser = do_CreateInstance( NS_WEBBROWSER_CONTRACTID, &rv );
	if ( ! mWebBrowser )
	{
		return NS_ERROR_FAILURE;
	}

    (void)mWebBrowser->SetContainerWindow( NS_STATIC_CAST( nsIWebBrowserChrome*, this ) );

    nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface( mWebBrowser );
    dsti->SetItemType( nsIDocShellTreeItem::typeContentWrapper );

    mBaseWindow = do_QueryInterface( mWebBrowser );

    mBaseWindow->InitWindow( nativeWindowHandleIn, nsnull,  0, 0, mBrowserWidth, mBrowserHeight );
    mBaseWindow->Create();

    nsCOMPtr< nsIWebProgressListener > listener( NS_STATIC_CAST( nsIWebProgressListener*, this ) );
    nsCOMPtr< nsIWeakReference > thisListener( do_GetWeakReference( listener ) );
    mWebBrowser->AddWebBrowserListener( thisListener, NS_GET_IID( nsIWebProgressListener ) );

#if LL_DARWIN
	// Without this, the mac doesn't get upates for animating gifs, mouseovers, etc.
	mBaseWindow->SetVisibility( PR_TRUE );
#else
	mBaseWindow->SetVisibility( PR_FALSE );
#endif

	nsresult result;
	mWebNav = do_QueryInterface( mWebBrowser, &result );
	if ( NS_FAILED( result ) || ! mWebNav )
	{
		return NS_ERROR_FAILURE;
	};

	setSize( widthIn, heightIn );

    if ( mWebBrowser )
    {
		*aBrowser = mWebBrowser;
		NS_ADDREF( *aBrowser );

		return NS_OK;
    };

    return NS_ERROR_FAILURE;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMPL_ADDREF( LLEmbeddedBrowserWindow )
NS_IMPL_RELEASE( LLEmbeddedBrowserWindow )

////////////////////////////////////////////////////////////////////////////////
//
NS_INTERFACE_MAP_BEGIN( LLEmbeddedBrowserWindow )
	NS_INTERFACE_MAP_ENTRY_AMBIGUOUS( nsISupports, nsIWebBrowserChrome )
	NS_INTERFACE_MAP_ENTRY( nsIInterfaceRequestor )
	NS_INTERFACE_MAP_ENTRY( nsIWebBrowserChrome )
	NS_INTERFACE_MAP_ENTRY( nsIWebProgressListener )
	NS_INTERFACE_MAP_ENTRY( nsIURIContentListener )
	NS_INTERFACE_MAP_ENTRY( nsISupportsWeakReference )
	NS_INTERFACE_MAP_ENTRY( nsIToolkitObserver )
NS_INTERFACE_MAP_END

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::GetInterface( const nsIID &aIID, void** aInstancePtr )
{
	if ( aIID.Equals( NS_GET_IID( nsIDOMWindow ) ) )
	{
		if ( mWebBrowser )
		{
			return mWebBrowser->GetContentDOMWindow( ( nsIDOMWindow** )aInstancePtr );
		};

		return NS_ERROR_NOT_INITIALIZED;
	};

	return QueryInterface( aIID, aInstancePtr );
}

////////////////////////////////////////////////////////////////////////////////
// called when something changes the status text - emits event to consumer
NS_IMETHODIMP LLEmbeddedBrowserWindow::SetStatus( PRUint32 aType, const PRUnichar* aStatus )
{
	mStatusText = std::string( NS_ConvertUTF16toUTF8( aStatus ).get() );

	LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), mStatusText );
	mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onStatusTextChange, event );

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::GetWebBrowser( nsIWebBrowser** aWebBrowser )
{
	NS_ENSURE_ARG_POINTER( aWebBrowser );

	*aWebBrowser = mWebBrowser;

	NS_IF_ADDREF( *aWebBrowser );

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::SetWebBrowser( nsIWebBrowser* aWebBrowser )
{
	NS_ENSURE_ARG_POINTER( aWebBrowser );

	mWebBrowser = aWebBrowser;

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::GetChromeFlags( PRUint32* aChromeMask )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::SetChromeFlags( PRUint32 aChromeMask )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::DestroyBrowserWindow()
{
	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::SizeBrowserTo( PRInt32 aCX, PRInt32 aCY )
{
	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::ShowAsModal()
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::IsWindowModal( PRBool* retval )
{
	*retval = PR_FALSE;

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::ExitModalEventLoop( nsresult aStatus )
{
	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// called when the page loading progress changes - emits event to consumer
NS_IMETHODIMP LLEmbeddedBrowserWindow::OnProgressChange( nsIWebProgress* progress, nsIRequest* request,
													PRInt32 curSelfProgress, PRInt32 maxSelfProgress,
														PRInt32 curTotalProgress, PRInt32 maxTotalProgress )
{
	mPercentComplete = static_cast< PRInt16 >
		( static_cast< float >( curTotalProgress * 100.0f ) / static_cast< float >( maxTotalProgress ) );

	if ( mPercentComplete < 0 )
		mPercentComplete = 0;

	if ( mPercentComplete > 100 )
		mPercentComplete = 100;

	LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), mPercentComplete );
	mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onUpdateProgress, event );

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// called when the browser state changes - as described below - emits event to consumer
NS_IMETHODIMP LLEmbeddedBrowserWindow::OnStateChange( nsIWebProgress* progress, nsIRequest* request,
													PRUint32 progressStateFlags, nsresult status )
{
 	if ( ( progressStateFlags & STATE_START ) && ( progressStateFlags & STATE_IS_DOCUMENT ) && ( status == NS_OK ) )
	{
		// TODO: move this to a better place.
		enableToolkitObserver( false );
		enableToolkitObserver( true );

		// page load is starting so remove listener that catches "click" events
		nsCOMPtr< nsIDOMWindow > window;
		nsresult result = progress->GetDOMWindow( getter_AddRefs( window ) );
		if ( result == NS_OK )
		{
			nsCOMPtr< nsIDOMEventTarget > target = do_QueryInterface( window );
			if ( target )
				target->RemoveEventListener(NS_ConvertUTF8toUTF16( "click" ), this, PR_TRUE );
		};

		// set the listener to we can catch nsURIContentListener events
		if ( mWebBrowser )
		{
			mWebBrowser->SetParentURIContentListener( NS_STATIC_CAST( nsIURIContentListener*, this ) );
		};

		// emit event that navigation is beginning
		mStatusText = std::string( "Browser loaded" );
		LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), mStatusText );
		mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onNavigateBegin, event );

		// about to move to a different page so have to stop grabbing a page
		// but done one final grab in case the app doesn't ever call grabWindow again
		grabWindow( 0, 0, mBrowserWidth, mBrowserHeight );
	};

	if ( ( progressStateFlags & STATE_STOP ) && ( progressStateFlags & STATE_IS_WINDOW ) && ( status == NS_OK ) )
	{
		// page load is complete so add listener that catches "click" events
		nsCOMPtr< nsIDOMWindow > window;
		nsresult result = progress->GetDOMWindow( getter_AddRefs( window ) );
		if ( result == NS_OK )
		{
			nsCOMPtr< nsIDOMEventTarget > target = do_QueryInterface( window );
			if ( target )
				target->AddEventListener(NS_ConvertUTF8toUTF16( "click" ), this, PR_TRUE );
		};

		// pick up raw HTML response status code
		PRUint32 responseStatus = 0;
		if ( request )
		{
			nsCOMPtr< nsIHttpChannel > httpChannel = do_QueryInterface( request );
			if ( httpChannel )
			{
				httpChannel->GetResponseStatus( &responseStatus );
			};
		};

		// emit event that navigation is finished
		mStatusText = std::string( "Done" );
		LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), mStatusText, (int)responseStatus );
		mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onNavigateComplete, event );

		// also set the flag here since back/forward navigation doesn't call progress change
		grabWindow( 0, 0, mBrowserWidth, mBrowserHeight );
	};

	if ( progressStateFlags & STATE_REDIRECTING )
	{
		mStatusText = std::string( "Redirecting..." );
	};

	if ( progressStateFlags & STATE_TRANSFERRING )
	{
		mStatusText = std::string( "Transferring..." );
	};

	if ( progressStateFlags & STATE_NEGOTIATING )
	{
		mStatusText = std::string( "Negotiating..." );
	};

	LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), mStatusText );
	mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onStatusTextChange, event );

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// call when the location changes - e.g. when a site redirects - emits event to consumer
// TODO: ought to check that this change is on the top frame and
// indicate this to the consumer of this class
NS_IMETHODIMP LLEmbeddedBrowserWindow::OnLocationChange( nsIWebProgress* webProgress,
														nsIRequest* request,
															nsIURI* location )
{
	if ( request )
	{
		nsCOMPtr< nsIHttpChannel > http_channel = do_QueryInterface( request );
		if ( http_channel )
		{
			PRUint32 response_status = 0;
			http_channel->GetResponseStatus( &response_status );

			if ( response_status == 404 )
			{
				if ( ! m404RedirectUrl.empty() )
				{
					if ( mWebNav )
					{
						mWebNav->LoadURI( reinterpret_cast< const PRUnichar* >
							( NS_ConvertUTF8toUTF16( m404RedirectUrl.c_str() ).get() ),
								nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY,
									nsnull, nsnull, nsnull );
					};
				};
			};
		};
	};

	nsCAutoString newURI;
	location->GetSpec( newURI );

	mCurrentUri = newURI.get();

	LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri() );
	mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onLocationChange, event );

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// change the background color that gets used between pages (usually white)
void LLEmbeddedBrowserWindow::setBackgroundColor( const PRUint8 redIn, const PRUint8 greenIn, const PRUint8 blueIn )
{
	mBkgRed = redIn;
	mBkgGreen = greenIn;
	mBkgBlue = blueIn;
}

////////////////////////////////////////////////////////////////////////////////
// change the caret color (we have different backgrounds to edit fields - black caret on black background == bad)
void LLEmbeddedBrowserWindow::setCaretColor( const PRUint8 redIn, const PRUint8 greenIn, const PRUint8 blueIn )
{
	mCaretRed = redIn;
	mCaretGreen = greenIn;
	mCaretBlue = blueIn;
}

////////////////////////////////////////////////////////////////////////////////
//
void LLEmbeddedBrowserWindow::setEnabled( PRBool enabledIn )
{
	mEnabled = enabledIn;
}

////////////////////////////////////////////////////////////////////////////////
// allow consumers of this class to observe events - add themselves as an observer
bool LLEmbeddedBrowserWindow::addObserver( LLEmbeddedBrowserWindowObserver* observerIn )
{
	return mEventEmitter.addObserver( observerIn );
}

////////////////////////////////////////////////////////////////////////////////
// allow consumers of this class to observe events - remove themselves as an observer
bool LLEmbeddedBrowserWindow::remObserver( LLEmbeddedBrowserWindowObserver* observerIn )
{
	return mEventEmitter.remObserver( observerIn );
}

////////////////////////////////////////////////////////////////////////////////
// used by observers of this class to get the current URI
const std::string& LLEmbeddedBrowserWindow::getCurrentUri()
{
	return mCurrentUri;
}

////////////////////////////////////////////////////////////////////////////////
// utility method that is used by observers to retrieve data after an event
const PRInt16 LLEmbeddedBrowserWindow::getPercentComplete()
{
	return mPercentComplete;
}

////////////////////////////////////////////////////////////////////////////////
// utility method that is used by observers to retrieve data after an event
const std::string& LLEmbeddedBrowserWindow::getStatusMsg()
{
	return mStatusText;
}

////////////////////////////////////////////////////////////////////////////////
// utility method that is used by observers to retrieve data after an event
const std::string& LLEmbeddedBrowserWindow::getClickLinkHref()
{
	return mClickHref;
}

////////////////////////////////////////////////////////////////////////////////
// utility method that is used by observers to retrieve data after an event
const std::string& LLEmbeddedBrowserWindow::getClickLinkTarget()
{
	return mClickTarget;
}

////////////////////////////////////////////////////////////////////////////////
// called when the status text is changed - emits event to consumer
NS_IMETHODIMP LLEmbeddedBrowserWindow::OnStatusChange( nsIWebProgress* aWebProgress,
														nsIRequest* aRequest,
															nsresult aStatus,
																const PRUnichar* aMessage )
{
	mStatusText = std::string( NS_ConvertUTF16toUTF8( aMessage ).get() );

	LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), mStatusText );
	mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onStatusTextChange, event );

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// implement this if you want to do something when the security state changtes
NS_IMETHODIMP LLEmbeddedBrowserWindow::OnSecurityChange( nsIWebProgress* aWebProgress,
													nsIRequest* aRequest,
														PRUint32 state )
{
	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// render a page into memory and grab the window
// TODO: 0,0, browser width, browser height is always passed in right now
//       need to make this work with arbitrary rects (i.e. the dirty rect)
unsigned char* LLEmbeddedBrowserWindow::grabWindow( int xIn, int yIn, int widthIn, int heightIn )
{
	// sanity check
	if ( ! mWebBrowser )
		return 0;

	// only grab the window if it's enabled
	if ( ! mEnabled )
		return false;

	// get the docshell
	nsCOMPtr< nsIDocShell > docShell = do_GetInterface( mWebBrowser );
	if ( ! docShell )
		return PR_FALSE;

	// get pres context
	nsCOMPtr< nsPresContext > presContext;
	nsresult result = docShell->GetPresContext( getter_AddRefs( presContext ) );
	if ( NS_FAILED( result ) || ( ! presContext ) )
		return PR_FALSE;

	// get view manager
	nsIViewManager* viewManager = presContext->GetViewManager();
	if ( ! viewManager )
		return PR_FALSE;

	// get the view
	nsIScrollableView* scrollableView = NULL;
	viewManager->GetRootScrollableView( &scrollableView );
	nsIView* view = NULL;
	if ( scrollableView )
		scrollableView->GetScrolledView( view );
	else
		viewManager->GetRootView( view );

	// get the rectangle we want to render in twips (this looks odd but takees care of scrolling too)
	nsRect rect = view->GetBounds() - view->GetPosition() - view->GetPosition();
	if ( rect.IsEmpty() )
		return 0;

	float p2t = presContext->PixelsToTwips();
	rect.width = NSIntPixelsToTwips( widthIn, p2t );
	rect.height = NSIntPixelsToTwips( heightIn, p2t );

	// render the page
	nsCOMPtr< nsIRenderingContext > context;
	result = viewManager->RenderOffscreen( view, rect, PR_FALSE, PR_FALSE, NS_RGB( mBkgRed, mBkgGreen, mBkgBlue  ), getter_AddRefs( context ) );
	if ( NS_FAILED( result ) )
		return 0;

	// retrieve the surface we rendered to
	nsIDrawingSurface* surface = nsnull;
	context->GetDrawingSurface( &surface );
	if ( ! surface )
		return 0;

	// lock the surface and retrieve a pointer to the rendered data and current row span
	PRUint8* data;
	PRInt32 rowLen;
	// sometime rowspan ! width in pixels * bytes per pixel so save row span value and use in application
	result = surface->Lock( xIn, yIn, widthIn, heightIn, reinterpret_cast< void** >( &data ), &mBrowserRowSpan, &rowLen, NS_LOCK_SURFACE_READ_ONLY );
	if ( NS_FAILED ( result ) )
		return 0;

	// save row span - it *can* change during the life of the app
	mBrowserDepth = rowLen / mBrowserWidth;

	// create memory buffer here so it can be deleted and recreated elsewhere
	if ( ! mPageBuffer )
		mPageBuffer = new unsigned char[ mBrowserRowSpan * mBrowserHeight ];

	// save the pixels and optionally invert them 
	// (it's useful the SL client to get bitmaps that are inverted compared
	// to the way that Mozilla renders them - allow to optionally flip
	if ( mFlipBitmap )
	{
		for( int y = mBrowserHeight - 1; y > -1; --y )
		{
			memcpy( mPageBuffer + y * mBrowserRowSpan, 
						data + ( mBrowserHeight - y - 1 ) * mBrowserRowSpan, 
							mBrowserRowSpan );
		};
	}
	else
	{
		memcpy( mPageBuffer, data, mBrowserRowSpan * mBrowserHeight );
	};

	// release and destroy the surface we rendered to
	surface->Unlock();
	context->DestroyDrawingSurface( surface );

	renderCaret();

	return mPageBuffer;
}

////////////////////////////////////////////////////////////////////////////////
// all this just to render a caret!
PRBool LLEmbeddedBrowserWindow::renderCaret()
{
	nsCOMPtr< nsIWebBrowserFocus > focus = do_QueryInterface( mWebBrowser );

	nsCOMPtr< nsIDOMElement > focusedElement;
	focus->GetFocusedElement( getter_AddRefs( focusedElement ) );
	if ( ! focusedElement )
		return NS_ERROR_FAILURE;

	nsCOMPtr<nsIContent> focusedContent = do_QueryInterface( focusedElement );

	nsCOMPtr< nsIDOMWindow > domWindow;
	mWebBrowser->GetContentDOMWindow( getter_AddRefs( domWindow ) );
	if ( ! domWindow )
		return NS_ERROR_FAILURE;

	nsCOMPtr< nsIDOMDocument > domDocument;
	domWindow->GetDocument( getter_AddRefs( domDocument ) );
	if ( ! domDocument )
		return NS_ERROR_FAILURE;

	nsCOMPtr< nsIDocument> document = do_QueryInterface( domDocument );
	if ( ! document )
		return NS_ERROR_FAILURE;

	nsIPresShell* presShell = document->GetShellAt( 0 );
	if ( ! presShell )
		return NS_ERROR_FAILURE;

	nsCOMPtr< nsICaret > caret;
	presShell->GetCaret( getter_AddRefs( caret ) );

	nsIFrame* frame = nsnull;
	presShell->GetPrimaryFrameFor( focusedContent, &frame );
	if ( ! frame )
		return NS_ERROR_FAILURE;

	nsCOMPtr<nsISelectionController> selCtrl;
	frame->GetSelectionController( presShell->GetPresContext(), getter_AddRefs( selCtrl ) );

	nsCOMPtr<nsISelection> selection;
	selCtrl->GetSelection( nsISelectionController::SELECTION_NORMAL, getter_AddRefs( selection ) );

	PRBool collapsed;
	nsRect coords;
	nsIView* caretView;
	caret->GetCaretCoordinates( nsICaret::eTopLevelWindowCoordinates, selection, &coords, &collapsed, &caretView );

	float twips2Pixls = presShell->GetPresContext()->TwipsToPixels();

	PRInt32 caretX = NSTwipsToIntPixels( coords.x, twips2Pixls );
	PRInt32 caretY = NSTwipsToIntPixels( coords.y, twips2Pixls );
	PRInt32 caretHeight = NSTwipsToIntPixels( coords.height, twips2Pixls );

	if ( caretX > -1 && caretX < mBrowserWidth && caretY > -1 && caretY < mBrowserHeight )
	{
		if ( mPageBuffer )
		{
			for( int y = 1; y < caretHeight - 1; ++y )
			{
				PRInt32 base_pos = caretY + y;
				if ( mFlipBitmap )
					base_pos = mBrowserHeight - ( caretY + y );

				// sometimes the caret seems valid when it really isn't - cap it to size of screen
				if ( caretY + y + caretHeight < mBrowserHeight )
				{
					mPageBuffer[ base_pos * getBrowserRowSpan() + ( caretX + 1 ) * mBrowserDepth + 0 ] = mCaretBlue;
					mPageBuffer[ base_pos * getBrowserRowSpan() + ( caretX + 1 ) * mBrowserDepth + 1 ] = mCaretGreen;
					mPageBuffer[ base_pos * getBrowserRowSpan() + ( caretX + 1 ) * mBrowserDepth + 2 ] = mCaretRed;
				};
			};
		};
	};

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// return the buffer that contains the rendered page
unsigned char* LLEmbeddedBrowserWindow::getPageBuffer()
{
	return mPageBuffer;
}

////////////////////////////////////////////////////////////////////////////////
//
PRInt16 LLEmbeddedBrowserWindow::getBrowserWidth()
{
	return mBrowserWidth;
}

////////////////////////////////////////////////////////////////////////////////
//
PRInt16 LLEmbeddedBrowserWindow::getBrowserHeight()
{
	return mBrowserHeight;
}

////////////////////////////////////////////////////////////////////////////////
//
PRInt16 LLEmbeddedBrowserWindow::getBrowserDepth()
{
	return mBrowserDepth;
}

////////////////////////////////////////////////////////////////////////////////
//
PRInt32 LLEmbeddedBrowserWindow::getBrowserRowSpan()
{
	return mBrowserRowSpan;
}

////////////////////////////////////////////////////////////////////////////////
//
PRBool LLEmbeddedBrowserWindow::navigateTo( const std::string uriIn )
{
	if ( mWebNav )
	{
		mWebNav->LoadURI( reinterpret_cast< const PRUnichar* >( NS_ConvertUTF8toUTF16( uriIn.c_str() ).get() ),
			nsIWebNavigation::LOAD_FLAGS_NONE,
				nsnull, nsnull, nsnull );

		return PR_TRUE;
	};

	return PR_FALSE;
};

////////////////////////////////////////////////////////////////////////////////
//
PRBool LLEmbeddedBrowserWindow::canNavigateBack()
{
	if ( ! mWebNav )
	{
		return PR_FALSE;
	};

	PRBool canGoBack = PR_FALSE;

	nsresult result = mWebNav->GetCanGoBack( &canGoBack );
	if ( NS_FAILED( result ) )
	{
		return PR_FALSE;
	};

	return canGoBack;
};

////////////////////////////////////////////////////////////////////////////////
//
void LLEmbeddedBrowserWindow::navigateStop()
{
	if ( mWebNav )
		mWebNav->Stop( nsIWebNavigation::STOP_ALL );
};

////////////////////////////////////////////////////////////////////////////////
//
void LLEmbeddedBrowserWindow::navigateBack()
{
	if ( mWebNav )
		mWebNav->GoBack();
};

////////////////////////////////////////////////////////////////////////////////
//
PRBool LLEmbeddedBrowserWindow::canNavigateForward()
{
	if ( ! mWebNav )
		return PR_FALSE;

	PRBool canGoForward = PR_FALSE;

	nsresult result = mWebNav->GetCanGoForward( &canGoForward );
	if ( NS_FAILED( result ) )
	{
		return PR_FALSE;
	};

	return canGoForward;
};

////////////////////////////////////////////////////////////////////////////////
//
void LLEmbeddedBrowserWindow::navigateForward()
{
	if ( mWebNav )
		mWebNav->GoForward();
};

////////////////////////////////////////////////////////////////////////////////
// set the size of the browser window
PRBool LLEmbeddedBrowserWindow::setSize( PRInt16 widthIn, PRInt16 heightIn )
{
	if ( mBaseWindow )
	{
		// if there is a buffer already, get rid of it (it will get created as required in grabWindow())
		if ( mPageBuffer )
		{
			delete[] mPageBuffer;
			mPageBuffer = 0;
		};

		// record new size (important: may change after grabWindow() is called);
		mBrowserWidth = widthIn;
		mBrowserHeight = heightIn;
		mBrowserRowSpan = mBrowserWidth * mBrowserDepth;

		// On the Mac, these calls do strange things to the main viewer window, and they don't seem necessary in any case.
		#ifdef WIN32
		// this is the actual OS (on Win32) Window so it needs to be hidden
		mBaseWindow->SetVisibility( PR_FALSE );

		// move WAY off screen (and in a place that makes the combobox hack work)
		mBaseWindow->SetPosition( 8000, -6000 );
		#endif

		// tell Mozilla about the new size
		mBaseWindow->SetSize( widthIn, heightIn, PR_FALSE );

		return PR_TRUE;
	};

	return PR_FALSE;
}

////////////////////////////////////////////////////////////////////////////////
//
PRBool LLEmbeddedBrowserWindow::flipWindow( PRBool flip )
{
	mFlipBitmap = flip;

	return true;
}

////////////////////////////////////////////////////////////////////////////////
// higher level mouse event
void LLEmbeddedBrowserWindow::mouseLeftDoubleClick( PRInt16 xPosIn, PRInt16 yPosIn )
{
	// Internally Mozilla represents double-click as a 2-count mouse down event.
	// TODO: support triple-click
	const PRUint32 clickCount = 2;
	sendMozillaMouseEvent( NS_MOUSE_LEFT_BUTTON_DOWN, xPosIn, yPosIn, clickCount );
}

////////////////////////////////////////////////////////////////////////////////
// higher level mouse event
void LLEmbeddedBrowserWindow::mouseDown( PRInt16 xPosIn, PRInt16 yPosIn )
{
	const PRUint32 clickCount = 1;
	sendMozillaMouseEvent( NS_MOUSE_LEFT_BUTTON_DOWN, xPosIn, yPosIn, clickCount );
}

////////////////////////////////////////////////////////////////////////////////
// higher level mouse event
void LLEmbeddedBrowserWindow::mouseUp( PRInt16 xPosIn, PRInt16 yPosIn )
{
	const PRUint32 clickCount = 1;
	sendMozillaMouseEvent( NS_MOUSE_LEFT_BUTTON_UP, xPosIn, yPosIn, clickCount );
}

////////////////////////////////////////////////////////////////////////////////
// higher level mouse event
void LLEmbeddedBrowserWindow::mouseMove( PRInt16 xPosIn, PRInt16 yPosIn )
{
	const PRUint32 clickCount = 1;	// ignored?
	sendMozillaMouseEvent( NS_MOUSE_MOVE, xPosIn, yPosIn, clickCount );
}

////////////////////////////////////////////////////////////////////////////////
// utility methods to set an error message so something else can look at it
void LLEmbeddedBrowserWindow::scrollByLines( PRInt16 linesIn )
{
	if ( mWebBrowser )
	{
		nsCOMPtr< nsIDOMWindow > window;
		nsresult result = mWebBrowser->GetContentDOMWindow( getter_AddRefs( window ) );

		if ( ! NS_FAILED( result ) && window )
		{
			result = window->ScrollByLines( linesIn );
		};
	};
}

////////////////////////////////////////////////////////////////////////////////
// synthesizes a mouse event and sends into the embedded instance
// eventIn - NS_MOUSE_LEFT_BUTTON_DOWN, NS_MOUSE_LEFT_BUTTON_UP, etc.
// xPosIn, yPosIn - coordinates (in browser window space)
// clickCountIn - use 1 for single click, 2 for double-click, etc.
PRBool LLEmbeddedBrowserWindow::sendMozillaMouseEvent( PRInt16 eventIn, PRInt16 xPosIn, PRInt16 yPosIn, PRUint32 clickCountIn )
{
	if ( ! mEnabled )
		return PR_FALSE;

	if ( ! mWebBrowser )
		return PR_FALSE;

	nsCOMPtr< nsIDocShell > docShell = do_GetInterface( mWebBrowser );
	if ( ! docShell )
		return PR_FALSE;

	nsCOMPtr< nsPresContext > presContext;
	nsresult result = docShell->GetPresContext( getter_AddRefs( presContext ) );
	if ( NS_FAILED( result ) || ( ! presContext ) )
		return PR_FALSE;

	nsIViewManager* viewManager = presContext->GetViewManager();
	if ( ! viewManager )
		return PR_FALSE;

	nsIView* rootView;
	result = viewManager->GetRootView( rootView );
	if ( NS_FAILED( result ) || ( ! rootView ) )
		return PR_FALSE;

	nsCOMPtr< nsIWidget > widget = rootView->GetWidget();
	if ( ! widget )
		return PR_FALSE;

	nsMouseEvent mouseEvent( PR_TRUE, eventIn, widget, nsMouseEvent::eReal );
	mouseEvent.clickCount = clickCountIn;
	mouseEvent.isShift = 0;
	mouseEvent.isControl = 0;
	mouseEvent.isAlt = 0;
	mouseEvent.isMeta = 0;
	mouseEvent.widget = widget;
	mouseEvent.nativeMsg = nsnull;
	mouseEvent.point.x = xPosIn;
	mouseEvent.point.y = yPosIn;
	mouseEvent.refPoint.x = xPosIn;
	mouseEvent.refPoint.y = yPosIn;
	mouseEvent.flags = 0;

	nsEventStatus status;
	result = viewManager->DispatchEvent( &mouseEvent, &status );
	if ( NS_FAILED( result ) )
		return PR_FALSE;

	return PR_TRUE;
};

////////////////////////////////////////////////////////////////////////////////
// higher level keyboard functions

// accept a (mozilla-style) keycode
void LLEmbeddedBrowserWindow::keyPress( PRInt16 keyCode )
{
	sendMozillaKeyboardEvent( 0, keyCode );
}

// accept keyboard input that's already been translated into a unicode char.
void LLEmbeddedBrowserWindow::unicodeInput( PRUint32 uni_char )
{
	sendMozillaKeyboardEvent( uni_char, 0 );
}

////////////////////////////////////////////////////////////////////////////////
// synthesizes a keyboard event and sends into the embedded instance
PRBool LLEmbeddedBrowserWindow::sendMozillaKeyboardEvent( PRUint32 uni_char, PRUint32 ns_vk_code )
{
	if ( ! mEnabled )
		return PR_FALSE;

	if ( ! mWebBrowser )
		return PR_FALSE;

	nsCOMPtr< nsIDocShell > docShell = do_GetInterface( mWebBrowser );
	if ( ! docShell )
		return PR_FALSE;

	nsCOMPtr< nsPresContext > presContext;
	docShell->GetPresContext( getter_AddRefs( presContext ) );
	if ( ! presContext )
		return PR_FALSE;

	nsIViewManager* viewManager = presContext->GetViewManager();
	if ( ! viewManager )
		return PR_FALSE;

	nsIView* rootView;
	viewManager->GetRootView( rootView );
	if ( ! rootView )
		return PR_FALSE;

	nsCOMPtr< nsIWidget > widget = rootView->GetWidget();
	if ( ! widget )
		return PR_FALSE;

	nsKeyEvent keyEvent( PR_TRUE, NS_KEY_PRESS, widget );
	keyEvent.keyCode = ns_vk_code;
	keyEvent.charCode = uni_char;
	keyEvent.isChar = uni_char ? PR_TRUE : PR_FALSE;
	keyEvent.isShift = 0;
	keyEvent.isControl = 0;
	keyEvent.isAlt = 0;
	keyEvent.isMeta = 0;
	keyEvent.widget = widget;
	keyEvent.nativeMsg = nsnull;
	keyEvent.point.x = 0;
	keyEvent.point.y = 0;
	keyEvent.refPoint.x = 0;
	keyEvent.refPoint.y = 0;
	keyEvent.flags = 0;

	nsEventStatus status;
	nsresult result = viewManager->DispatchEvent( &keyEvent, &status );
	if ( NS_FAILED( result ) )
		return PR_FALSE;

	return PR_TRUE;
}

////////////////////////////////////////////////////////////////////////////////
// override nsIWebBrowserChrome::HandleEvent ()
NS_IMETHODIMP LLEmbeddedBrowserWindow::HandleEvent( nsIDOMEvent* anEvent )
{
	nsCOMPtr< nsIDOMEventTarget > eventTarget;
	anEvent->GetTarget( getter_AddRefs( eventTarget ) );

	nsCOMPtr<nsIDOMElement> linkElement ( do_QueryInterface ( eventTarget ) );
	if ( linkElement )
	{
		// look for an href link
		nsString name;
		linkElement->GetAttribute( NS_ConvertUTF8toUTF16( "href" ), name );
		mClickHref = std::string( NS_ConvertUTF16toUTF8( name ).get() );

		// look for a target element
		linkElement->GetAttribute( NS_ConvertUTF8toUTF16( "target" ), name );
		mClickTarget = std::string( NS_ConvertUTF16toUTF8( name ).get() );

		// if the href link contains something
		if ( mClickHref.length() )
		{
			LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), mClickHref, mClickTarget );
			mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onClickLinkHref, event );
		};
	};

	return NS_OK;
};

////////////////////////////////////////////////////////////////////////////////
// override nsIURIContentListener methods
NS_IMETHODIMP LLEmbeddedBrowserWindow::OnStartURIOpen( nsIURI *aURI, PRBool *_retval )
{
	nsCAutoString newURI;
	aURI->GetSpec( newURI );
	std::string rawUri = newURI.get();

	// are we navigating to a 'nofollow' link
	if ( mNoFollowScheme.length() && rawUri.substr( 0, mNoFollowScheme.length() ) == mNoFollowScheme )
	{
		LLEmbeddedBrowserWindowEvent event( getWindowId(), rawUri, rawUri );
		mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onClickLinkNoFollow, event );

		// tell browser we're handling things and don't follow link
		*_retval = PR_TRUE;
	}
	else
	{
		// tell browser to proceed as normal
		*_retval = PR_FALSE;
	};

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
void LLEmbeddedBrowserWindow::setNoFollowScheme( std::string schemeIn )
{
	mNoFollowScheme = schemeIn;
}

////////////////////////////////////////////////////////////////////////////////
//
std::string LLEmbeddedBrowserWindow::getNoFollowScheme()
{
	return mNoFollowScheme;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::DoContent( const char *aContentType,
													PRBool aIsContentPreferred,
														nsIRequest *aRequest,
															nsIStreamListener **aContentHandler,
																PRBool *_retval )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::IsPreferred( const char *aContentType,
														char **aDesiredContentType,
															PRBool *_retval )
{
	// important (otherwise, links try to open in a new window and trigger the window watcher code)
	*_retval = PR_TRUE;
	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::CanHandleContent( const char *aContentType,
															PRBool aIsContentPreferred,
																char **aDesiredContentType,
																	PRBool *_retval )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::GetLoadCookie( nsISupports * *aLoadCookie )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::SetLoadCookie( nsISupports * aLoadCookie )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::GetParentContentListener( nsIURIContentListener** aParentContentListener )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
//
NS_IMETHODIMP LLEmbeddedBrowserWindow::SetParentContentListener( nsIURIContentListener* aParentContentListener )
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

////////////////////////////////////////////////////////////////////////////////
// give focus to the browser so that input keyboard events work
void LLEmbeddedBrowserWindow::focusBrowser( PRBool focusBrowserIn )
{
	if ( mWebBrowser )
	{
		if ( focusBrowserIn )
		{
			nsCOMPtr< nsIWebBrowserFocus > focus( do_GetInterface( mWebBrowser ) );
			focus->Activate();
		}
		else
		{
			nsCOMPtr< nsIWebBrowserFocus > focus( do_GetInterface( mWebBrowser ) );
			focus->Deactivate();
		};
	};
}

////////////////////////////////////////////////////////////////////////////////
//
void LLEmbeddedBrowserWindow::setWindowId( int windowIdIn )
{
	mWindowId = windowIdIn;
}

////////////////////////////////////////////////////////////////////////////////
//
int LLEmbeddedBrowserWindow::getWindowId()
{
	//printf("## Getting id for %p and it is %d\n", this, mWindowId );

	return mWindowId;
}

////////////////////////////////////////////////////////////////////////////////
// add / remove the toolkit (and therefore 'page changed') observer
PRBool LLEmbeddedBrowserWindow::enableToolkitObserver( PRBool enableIn )
{
	//TODO: AddObserver fails if I grab the toolkit this way.
	//static NS_DEFINE_CID(kNS_TOOLKIT_CID, NS_TOOLKIT_CID);
	//nsresult result1;
	//nsCOMPtr< nsIToolkit > toolkit = do_GetService( kNS_TOOLKIT_CID, &result1 );
	//if ( ( result1 == NS_OK ) && toolkit )
	//{
	//	if ( toolkit->AddObserver( this ) )
	//	{
	//		return true;
	//	};
	//};
	//return false;

	// TODO: this is horrible but seems to work - need a better way to get the toolkit
	nsCOMPtr< nsIDocShell > docShell = do_GetInterface( mWebBrowser );
	if ( ! docShell )
		return false;

	nsCOMPtr< nsPresContext > presContext;
	nsresult result = docShell->GetPresContext( getter_AddRefs( presContext ) );
	if ( NS_FAILED( result ) || ( ! presContext ) )
		return false;

	nsIViewManager* viewManager = presContext->GetViewManager();
	if ( ! viewManager )
		return false;

	nsIView* rootView;
	result = viewManager->GetRootView( rootView );
	if ( NS_FAILED( result ) || ( ! rootView ) )
		return false;

	nsCOMPtr< nsIWidget > widget = rootView->GetWidget();
	if ( ! widget )
		return false;

	nsCOMPtr< nsIToolkit > mToolkit = widget->GetToolkit();
	if ( ! mToolkit )
		return false;

	if ( enableIn )
		mToolkit->AddObserver( this );
	else
		mToolkit->RemoveObserver( this );

	return true;
}

////////////////////////////////////////////////////////////////////////////////
// something on the page changed - e.g. a new page loaded, scrolling, user
// input or as the result of some kind of animation.
// NOTE: we don't call grabWindow here as this can stall graphics apps -
//       we merely pass back an event to the app and let it decide when/if
//       to call grabWindow()
NS_METHOD LLEmbeddedBrowserWindow::NotifyInvalidated( nsIWidget *aWidget, PRInt32 x, PRInt32 y, PRInt32 width, PRInt32 height )
{
//	printf("LLEmbeddedBrowserWindow::NotifyInvalidated(%p, %d, %d, %d, %d)\n", (void*)aWidget, (int)x, (int)y, (int)width, (int)height);

	// try to match widget-window against ourselves to see if we need to update the texture
	// only works using native widgets (on Windows) at the moment - needs to be moved to platform agnostic code ASAP
	#ifdef WIN32

	// this is horrible beyond words but it seems to work...
	// nsToolkit tells us that a widget changed and we need to see if it's this instance
	// so we can emit an event that causes the parent app to update the browser texture
	nsIWidget* mainWidget;
	mBaseWindow->GetMainWidget( &mainWidget );

	HWND nativeWidget = (HWND)aWidget->GetNativeData( NS_NATIVE_WIDGET );
	HWND nativeWidgetChild;
	while ( ::GetParent( nativeWidget ) )
	{
		nativeWidgetChild = nativeWidget;
		nativeWidget = ::GetParent( nativeWidget );
	};

	if ( ( (HWND)mainWidget->GetNativeData( NS_NATIVE_WIDGET ) ) == nativeWidgetChild )
	{
		LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), x, y, width, height );
		mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onPageChanged, event );
	};

	// other platforms will always update - desperately inefficient but you'll see something.
	#else
		LLEmbeddedBrowserWindowEvent event( getWindowId(), getCurrentUri(), x, y, width, height );
		mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onPageChanged, event );
	#endif

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
std::string LLEmbeddedBrowserWindow::evaluateJavascript( std::string scriptIn )
{
	nsCOMPtr< nsIScriptGlobalObjectOwner > theGlobalObjectOwner( do_GetInterface( mWebBrowser ) );

	if ( theGlobalObjectOwner )
	{
		nsIScriptGlobalObject* theGlobalObject;
		theGlobalObject = theGlobalObjectOwner->GetScriptGlobalObject();

		nsIScriptContext* theScriptContext = theGlobalObject->GetContext();

		PRBool IsUndefined;
		nsString output;
		nsresult result = theScriptContext->EvaluateString(NS_ConvertUTF8toUTF16(scriptIn.c_str()),
		   nsnull, nsnull, "", 1, nsnull, &output, &IsUndefined);

		if( NS_FAILED( result ) )
			return "";

		return std::string( NS_ConvertUTF16toUTF8( output ).get() );
	}

	return "";
}

////////////////////////////////////////////////////////////////////////////////
//
bool LLEmbeddedBrowserWindow::set404RedirectUrl( std::string redirect_url )
{
	m404RedirectUrl = redirect_url;

	return true;
}

////////////////////////////////////////////////////////////////////////////////
//
bool LLEmbeddedBrowserWindow::clr404RedirectUrl()
{
	m404RedirectUrl = std::string( "" );

	return true;
}

// #define required by this file for LibXUL/Mozilla code to avoid crashes in their debug code
#ifdef _DEBUG
	#ifdef WIN32
		#undef DEBUG
	#endif
#endif
