diff --git a/CMakeLists.txt b/CMakeLists.txt index db3f983bd..c86ffb4eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,6 +344,7 @@ IF(PKG_CONFIG_FOUND) PKG_CHECK_MODULES(RSVG librsvg-2.0) PKG_CHECK_MODULES(CAIRO cairo) PKG_CHECK_MODULES(POPPLER poppler-glib) + PKG_CHECK_MODULES(XUL xulrunner-xpcom xulrunner-nspr xulrunner-nss xulrunner-plugin xulrunner-js) ENDIF(PKG_CONFIG_FOUND) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2521251f4..2fa1aa7a4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -112,6 +112,9 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgpdf) ENDIF(POPPLER_FOUND AND CAIRO_FOUND) + IF (XUL_FOUND) + ADD_SUBDIRECTORY(osgbrowser) + ENDIF(XUL_FOUND) IF (BUILD_OSG_WRAPPERS) diff --git a/examples/osgbrowser/CMakeLists.txt b/examples/osgbrowser/CMakeLists.txt new file mode 100644 index 000000000..7b74c3c71 --- /dev/null +++ b/examples/osgbrowser/CMakeLists.txt @@ -0,0 +1,43 @@ +SET(TARGET_SRC + llembeddedbrowser.cpp + llembeddedbrowserwindow.cpp + llmozlib2.cpp + osgbrowser.cpp +) + +SET(TARGET_H + llembeddedbrowser.h + llembeddedbrowserwindow.h + llmozlib2.h +) + +ADD_DEFINITIONS(-DMOZILLA_INTERNAL_API) + +# MESSAGE("XUL_LIB_DIRS = " ${XUL_LIB_DIRS}) +# MESSAGE("XUL_LIBRARIES = " ${XUL_LIBRARIES}) +# MESSAGE("XUL_INCLUDE_DIRS = " ${XUL_INCLUDE_DIRS}) + +INCLUDE_DIRECTORIES( + ${XUL_INCLUDE_DIRS} + /usr/include/xulrunner/locale + /usr/include/xulrunner/view + /usr/include/xulrunner/content + /usr/include/xulrunner/gfx + /usr/include/xulrunner/layout + /usr/include/xulrunner/webbrwsr + /usr/include/xulrunner/widget + /usr/include/xulrunner/dom + /usr/include/xulrunner/docshell + /usr/include/xulrunner/pref + /usr/include/xulrunner/nkcache + /usr/include/xulrunner/necko/ + /usr/include/xulrunner/profdirserviceprovider + /usr/include/xulrunner/nkcache + /usr/include/xulrunner/xulapp +) +LINK_DIRECTORIES( ${XUL_LIB_DIRS}) +SET(TARGET_EXTERNAL_LIBRARIES ${XUL_LIBRARIES}) + +#### end var setup ### +SETUP_EXAMPLE(osgbrowser) + diff --git a/examples/osgbrowser/llembeddedbrowser.cpp b/examples/osgbrowser/llembeddedbrowser.cpp new file mode 100644 index 000000000..660fa1a95 --- /dev/null +++ b/examples/osgbrowser/llembeddedbrowser.cpp @@ -0,0 +1,451 @@ +/* ***** 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 ***** */ + +// Windows specific switches +#ifdef WIN32 + // appears to be required by LibXUL/Mozilla code to avoid crashes in debug versions of their code (undef'd at end of this file) + #ifdef _DEBUG + #define DEBUG 1 + #endif +#endif // WIN32 + +#include + +#include "llembeddedbrowser.h" +#include "llembeddedbrowserwindow.h" + +#ifdef WIN32 + #pragma warning( disable : 4265 ) // "class has virtual functions, but destructor is not virtual" + #pragma warning( disable : 4291 ) // (no matching operator delete found; memory will not be freed if initialization throws an exception) +#endif // WIN32 + +#include "nsBuildID.h" +#include "nsICacheService.h" +#include "nsICookieManager.h" +#include "nsIPref.h" +#include "nsNetCID.h" +#include "nsProfileDirServiceProvider.h" +#include "nsXULAppAPI.h" +#include "nsIAppShell.h" +#include "nsIPromptService.h" +#include "time.h" +#include "nsWidgetsCID.h" +#include "nsNetCID.h" + +static nsIAppShell *sAppShell = nsnull; + +// singleton pattern - initialization +LLEmbeddedBrowser* LLEmbeddedBrowser::sInstance = 0; + +//////////////////////////////////////////////////////////////////////////////// +// +LLEmbeddedBrowser::LLEmbeddedBrowser() : + mErrorNum( 0 ), + mNativeWindowHandle( 0 ) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLEmbeddedBrowser::~LLEmbeddedBrowser() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLEmbeddedBrowser* LLEmbeddedBrowser::getInstance() +{ + if ( ! sInstance ) + { + sInstance = new LLEmbeddedBrowser; + }; + + return sInstance; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLEmbeddedBrowser::setLastError( int errorNumIn ) +{ + mErrorNum = errorNumIn; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLEmbeddedBrowser::clearLastError() +{ + mErrorNum = 0x0000; +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLEmbeddedBrowser::getLastError() +{ + return mErrorNum; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLEmbeddedBrowser::getGREVersion() +{ + // take the string directly from Mozilla + return std::string( GRE_BUILD_ID ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::init( std::string applicationDir, + std::string componentDir, + std::string profileDir, + void* nativeWindowHandleIn ) +{ + mNativeWindowHandle = nativeWindowHandleIn; + + NS_ConvertUTF8toUTF16 applicationDirUTF16(applicationDir.c_str()); + NS_ConvertUTF8toUTF16 componentDirUTF16(componentDir.c_str()); + NS_ConvertUTF8toUTF16 profileDirUTF16(profileDir.c_str()); + + nsCOMPtr< nsILocalFile > applicationDirNative; + nsresult result = NS_NewLocalFile( applicationDirUTF16, PR_FALSE, getter_AddRefs( applicationDirNative ) ); + if ( NS_FAILED( result ) ) + { + setLastError( 0x1000 ); + return false; + }; + + nsCOMPtr< nsILocalFile > componentDirNative; + result = NS_NewLocalFile( componentDirUTF16 , PR_FALSE, getter_AddRefs( componentDirNative ) ); + if ( NS_FAILED( result ) ) + { + setLastError( 0x1001 ); + return false; + }; + + result = XRE_InitEmbedding( componentDirNative, applicationDirNative, nsnull, nsnull, 0 ); + if ( NS_FAILED( result ) ) + { + setLastError( 0x1002 ); + return false; + }; + + nsCOMPtr< nsILocalFile > profileDirNative; + result = NS_NewLocalFile( profileDirUTF16 , PR_TRUE, getter_AddRefs( profileDirNative ) ); + if ( NS_FAILED( result ) ) + { + setLastError( 0x1007 ); + return false; + }; +#if 0 + nsCOMPtr< nsProfileDirServiceProvider > locProvider; + NS_NewProfileDirServiceProvider( PR_TRUE, getter_AddRefs( locProvider ) ); + if ( ! locProvider ) + { + setLastError( 0x1003 ); + XRE_TermEmbedding(); + return PR_FALSE; + }; + result = locProvider->Register(); + if ( NS_FAILED( result ) ) + { + setLastError( 0x1004 ); + XRE_TermEmbedding(); + return PR_FALSE; + }; + + result = locProvider->SetProfileDir( profileDirNative ); + if ( NS_FAILED( result ) ) + { + setLastError( 0x1005 ); + XRE_TermEmbedding(); + return PR_FALSE; + }; +#endif + + nsCOMPtr pref = do_CreateInstance( NS_PREF_CONTRACTID ); + if ( pref ) + { + pref->SetBoolPref( "security.warn_entering_secure", PR_FALSE ); + pref->SetBoolPref( "security.warn_entering_weak", PR_FALSE ); + pref->SetBoolPref( "security.warn_leaving_secure", PR_FALSE ); + pref->SetBoolPref( "security.warn_submit_insecure", PR_FALSE ); + pref->SetBoolPref( "network.protocol-handler.warn-external-default", PR_FALSE ); + } + else + { + setLastError( 0x1006 ); + }; + + // disable proxy by default + enableProxy( false, "", 0 ); + + // Originally from Linux version but seems to help other platforms too + nsresult rv; + nsCOMPtr appShell; + NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + appShell = do_CreateInstance(kAppShellCID, &rv); + if (!appShell) + { + setLastError( 0x1008 ); + return PR_FALSE; + } + sAppShell = appShell.get(); + NS_ADDREF(sAppShell); + sAppShell->Create(0, nsnull); + sAppShell->Spinup(); + + clearLastError(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::reset() +{ + XRE_TermEmbedding(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::clearCache() +{ + nsCOMPtr< nsICacheService > cacheService = do_GetService( NS_CACHESERVICE_CONTRACTID ); + if (! cacheService) + return false; + + cacheService->EvictEntries( nsICache::STORE_ANYWHERE ); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::enableProxy( bool proxyEnabledIn, std::string proxyHostNameIn, int proxyPortIn ) +{ + nsCOMPtr pref = do_CreateInstance( NS_PREF_CONTRACTID ); + if ( pref ) + { + if ( proxyEnabledIn ) + pref->SetIntPref( "network.proxy.type", 1 ); + else + pref->SetIntPref( "network.proxy.type", 0 ); + + pref->SetCharPref( "network.proxy.ssl", proxyHostNameIn.c_str() ); + pref->SetIntPref( "network.proxy.ssl_port", proxyPortIn ); + + pref->SetCharPref( "network.proxy.ftp", proxyHostNameIn.c_str() ); + pref->SetIntPref( "network.proxy.ftp_port", proxyPortIn ); + + pref->SetCharPref( "network.proxy.gopher", proxyHostNameIn.c_str() ); + pref->SetIntPref( "network.proxy.gopher_port", proxyPortIn ); + + pref->SetCharPref( "network.proxy.http", proxyHostNameIn.c_str() ); + pref->SetIntPref( "network.proxy.http_port", proxyPortIn ); + + pref->SetBoolPref( "network.proxy.share_proxy_settings", true ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::enableCookies( bool enabledIn ) +{ + nsCOMPtr pref = do_CreateInstance( NS_PREF_CONTRACTID ); + if ( pref ) + { + if ( enabledIn ) + pref->SetIntPref( "network.cookie.cookieBehavior", 0 ); + else + pref->SetIntPref( "network.cookie.cookieBehavior", 2 ); + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::clearAllCookies() +{ + nsCOMPtr< nsICookieManager > cookieManager = do_GetService( NS_COOKIEMANAGER_CONTRACTID ); + if ( ! cookieManager ) + return false; + + cookieManager->RemoveAll(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::enablePlugins( bool enabledIn ) +{ + nsCOMPtr pref = do_CreateInstance( NS_PREF_CONTRACTID ); + if ( pref ) + { + if ( enabledIn ) + { + pref->SetBoolPref( "plugin.scan.plid.all", PR_TRUE ); + pref->SetBoolPref( "xpinstall-enabled", PR_TRUE ); + } + else + { + pref->SetBoolPref( "plugin.scan.plid.all", PR_FALSE ); + pref->SetBoolPref( "xpinstall-enabled", PR_FALSE ); + pref->SetBoolPref( "plugin.scan.4xPluginFolder", PR_FALSE ); + pref->SetCharPref( "plugin.scan.Quicktime", "20.0" ); + pref->SetCharPref( "plugin.scan.Acrobat", "99.0" ); + pref->SetCharPref( "plugin.scan.SunJRE", "99.0" ); + pref->SetCharPref( "plugin.scan.WindowsMediaPlayer", "99.0" ); + }; + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLEmbeddedBrowser::setBrowserAgentId( std::string idIn ) +{ + nsCOMPtr pref = do_CreateInstance( NS_PREF_CONTRACTID ); + if ( pref ) + { + pref->SetCharPref( "general.useragent.extra.* ", idIn.c_str() ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLEmbeddedBrowserWindow* LLEmbeddedBrowser::createBrowserWindow( int browserWidthIn, int browserHeightIn ) +{ + nsCOMPtr< nsIWebBrowserChrome > chrome; + + LLEmbeddedBrowserWindow* newWin = new LLEmbeddedBrowserWindow(); + if ( ! newWin ) + { + return 0; + }; + + nsIWebBrowserChrome** aNewWindow = getter_AddRefs( chrome ); + + CallQueryInterface( NS_STATIC_CAST( nsIWebBrowserChrome*, newWin ), aNewWindow ); + + NS_ADDREF( *aNewWindow ); + + newWin->SetChromeFlags( nsIWebBrowserChrome::CHROME_ALL ); + + nsCOMPtr< nsIWebBrowser > newBrowser; + + newWin->createBrowser( mNativeWindowHandle, browserWidthIn, browserHeightIn, getter_AddRefs( newBrowser ) ); + if ( ! newBrowser ) + { + return 0; + }; + + if ( newWin && chrome ) + { + newWin->setParent( this ); + nsCOMPtr< nsIWebBrowser > newBrowser; + chrome->GetWebBrowser( getter_AddRefs( newBrowser ) ); + nsCOMPtr< nsIWebNavigation > webNav( do_QueryInterface ( newBrowser ) ); + webNav->LoadURI( NS_ConvertUTF8toUTF16( "about:blank" ).get(), nsIWebNavigation::LOAD_FLAGS_NONE, nsnull, nsnull, nsnull ); + + clearLastError(); + + return newWin; + }; + + setLastError( 0x2001 ); + return 0; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::destroyBrowserWindow( LLEmbeddedBrowserWindow* browserWindowIn ) +{ + nsCOMPtr< nsIWebBrowser > webBrowser; + nsCOMPtr< nsIWebNavigation > webNavigation; + + browserWindowIn->GetWebBrowser( getter_AddRefs( webBrowser ) ); + webNavigation = do_QueryInterface( webBrowser ); + if ( webNavigation ) + { + webNavigation->Stop( nsIWebNavigation::STOP_ALL ); + }; + + nsCOMPtr< nsIWebBrowser > browser = nsnull; + browserWindowIn->GetWebBrowser( getter_AddRefs( browser ) ); + nsCOMPtr< nsIBaseWindow > browserAsWin = do_QueryInterface( browser ); + if ( browserAsWin ) + { + browserAsWin->Destroy(); + }; + + + browserWindowIn->SetWebBrowser( nsnull ); + + NS_RELEASE( browserWindowIn ); + + delete browserWindowIn; + + clearLastError(); + + return true; +} + +// Windows specific switches +#ifdef WIN32 + #pragma warning( 3 : 4291 ) // (no matching operator delete found; memory will not be freed if initialization throws an exception) + #pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual" + + // #define required by this file for LibXUL/Mozilla code to avoid crashes in their debug code + #ifdef _DEBUG + #undef DEBUG + #endif + +#endif // WIN32 diff --git a/examples/osgbrowser/llembeddedbrowser.h b/examples/osgbrowser/llembeddedbrowser.h new file mode 100644 index 000000000..8ef786761 --- /dev/null +++ b/examples/osgbrowser/llembeddedbrowser.h @@ -0,0 +1,78 @@ +/* ***** 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 ***** */ + +#ifndef LLEMBEDDEDBROWSER_H +#define LLEMBEDDEDBROWSER_H + +#include +#include + +class LLEmbeddedBrowserWindow; +class LLEmbeddedBrowserWindowObserver; + +class LLEmbeddedBrowser +{ + public: + LLEmbeddedBrowser(); + virtual ~LLEmbeddedBrowser(); + + static LLEmbeddedBrowser* getInstance(); + + bool init( std::string applicationDir, std::string componentDir, std::string profileDir, void* nativeWindowHandleIn ); + bool reset(); + bool clearCache(); + bool enableProxy( bool proxyEnabledIn, std::string proxyHostNameIn, int proxyPortIn ); + bool enableCookies( bool enabledIn ); + bool clearAllCookies(); + bool enablePlugins( bool enabledIn ); + std::string getGREVersion(); + void setBrowserAgentId( std::string idIn ); + LLEmbeddedBrowserWindow* createBrowserWindow( int browserWidthIn, int browserHeightIn ); + bool destroyBrowserWindow( LLEmbeddedBrowserWindow* browserWindowIn ); + void setLastError( int errorNumIn ); + void clearLastError(); + int getLastError(); + + private: + static LLEmbeddedBrowser* sInstance; + void* mNativeWindowHandle; + int mErrorNum; +}; + +#endif // LLEMBEDDEDBROWSER_H + diff --git a/examples/osgbrowser/llembeddedbrowserwindow.cpp b/examples/osgbrowser/llembeddedbrowserwindow.cpp new file mode 100644 index 000000000..07a4bc5f9 --- /dev/null +++ b/examples/osgbrowser/llembeddedbrowserwindow.cpp @@ -0,0 +1,1418 @@ +/* ***** 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 + +#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( 0x00 ), + mCaretGreen( 0x00 ), + mCaretBlue( 0x00 ), + 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 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 ) +#ifdef NS_DECL_NSITOOLKITOBSERVER + NS_INTERFACE_MAP_ENTRY( nsIToolkitObserver ) +#endif +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 ); + if ( ! focus ) + return NS_ERROR_FAILURE; + + nsCOMPtr< nsIDOMElement > focusedElement; + focus->GetFocusedElement( getter_AddRefs( focusedElement ) ); + if ( ! focusedElement ) + return NS_ERROR_FAILURE; + + nsCOMPtr focusedContent = do_QueryInterface( focusedElement ); + if ( ! focusedContent ) + return NS_ERROR_FAILURE; + + nsIPresShell* presShell = focusedContent->GetCurrentDoc()->GetShellAt( 0 ); + if ( ! presShell ) + return NS_ERROR_FAILURE; + + nsCOMPtr< nsICaret > caret; + presShell->GetCaret( getter_AddRefs( caret ) ); + if ( ! caret ) + return NS_ERROR_FAILURE; + + nsIFrame* frame = nsnull; + presShell->GetPrimaryFrameFor( focusedContent, &frame ); + if ( ! frame ) + return NS_ERROR_FAILURE; + + nsCOMPtr selCtrl; + frame->GetSelectionController( presShell->GetPresContext(), getter_AddRefs( selCtrl ) ); + if ( ! selCtrl ) + return NS_ERROR_FAILURE; + + nsCOMPtr selection; + selCtrl->GetSelection( nsISelectionController::SELECTION_NORMAL, getter_AddRefs( selection ) ); + if ( ! selection ) + return NS_ERROR_FAILURE; + + PRBool collapsed; + nsRect coords; + nsIView* caretView; + caret->GetCaretCoordinates( nsICaret::eTopLevelWindowCoordinates, selection, &coords, &collapsed, &caretView ); + if ( ! caretView ) + return NS_ERROR_FAILURE; + + 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(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLEmbeddedBrowserWindow::navigateReload() +{ + // maybe need a cache version of this too? + if ( mWebNav ) + mWebNav->Reload( nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// 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 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; + +#ifdef NS_DECL_NSITOOLKITOBSERVER + if ( enableIn ) + mToolkit->AddObserver( this ); + else + mToolkit->RemoveObserver( this ); +#endif + return true; +} + +#ifdef NS_DECL_NSITOOLKITOBSERVER +//////////////////////////////////////////////////////////////////////////////// +// 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 = 0; + 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; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +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 diff --git a/examples/osgbrowser/llembeddedbrowserwindow.h b/examples/osgbrowser/llembeddedbrowserwindow.h new file mode 100644 index 000000000..3c75745f2 --- /dev/null +++ b/examples/osgbrowser/llembeddedbrowserwindow.h @@ -0,0 +1,260 @@ +/* ***** 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 ***** */ + +#ifndef LLEMBEDDEDBROWSERWINDOW_H +#define LLEMBEDDEDBROWSERWINDOW_H + +// Mozilla code has non-virtual destructors +#ifdef WIN32 +#pragma warning( disable : 4265 ) // "class has virtual functions, but destructor is not virtual" +#endif + +#include "nsIBaseWindow.h" +#include "nsIDOMEventListener.h" +#include "nsIDOMEventTarget.h" +#include "nsIInterfaceRequestor.h" +#include "nsIWebBrowserChrome.h" +#include "nsIWebNavigation.h" +#include "nsIWebProgressListener.h" +#include "nsIURIContentListener.h" +#include "nsWeakReference.h" +#include "nsIWebBrowser.h" +#include "nsIToolkit.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptGlobalObjectOwner.h" +#include "nsIScriptContext.h" + +#ifdef WIN32 +#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual" +#endif + +#include +#include +#include + +#include "llmozlib2.h" + +/////////////////////////////////////////////////////////////////////////////// +// manages the process of storing and emitting events that the consumer +// of the embedding class can observe +template< class T > +class LLEmbeddedBrowserWindowEmitter +{ + public: + LLEmbeddedBrowserWindowEmitter() { }; + ~LLEmbeddedBrowserWindowEmitter() { }; + + typedef typename T::EventType EventType; + typedef std::list< T* > ObserverContainer; + typedef void( T::*observerMethod )( const EventType& ); + + /////////////////////////////////////////////////////////////////////////////// + // + bool addObserver( T* observerIn ) + { + if ( ! observerIn ) + return false; + + if ( std::find( observers.begin(), observers.end(), observerIn ) != observers.end() ) + return false; + + observers.push_back( observerIn ); + + return true; + }; + + /////////////////////////////////////////////////////////////////////////////// + // + bool remObserver( T* observerIn ) + { + if ( ! observerIn ) + return false; + + observers.remove( observerIn ); + + return true; + }; + + /////////////////////////////////////////////////////////////////////////////// + // + void update( observerMethod method, const EventType& msgIn ) + { + typename std::list< T* >::iterator iter = observers.begin(); + + while( iter != observers.end() ) + { + ( ( *iter )->*method )( msgIn ); + + ++iter; + }; + }; + + protected: + ObserverContainer observers; +}; + +class LLEmbeddedBrowser; + +//////////////////////////////////////////////////////////////////////////////// +// class for a "window" that holds a browser - there can be lots of these +class LLEmbeddedBrowserWindow : + public nsIInterfaceRequestor, + public nsIWebBrowserChrome, + public nsIWebProgressListener, + public nsIURIContentListener, + public nsSupportsWeakReference, + public nsIDOMEventListener +#ifdef NS_DECL_NSITOOLKITOBSERVER + ,public nsIToolkitObserver +#endif +{ + public: + LLEmbeddedBrowserWindow(); + virtual ~LLEmbeddedBrowserWindow(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIWEBBROWSERCHROME + NS_DECL_NSIWEBPROGRESSLISTENER + NS_DECL_NSIURICONTENTLISTENER + NS_DECL_NSIDOMEVENTLISTENER +#ifdef NS_DECL_NSITOOLKITOBSERVER + NS_DECL_NSITOOLKITOBSERVER +#endif + // housekeeping + nsresult createBrowser( void* nativeWindowHandleIn, PRInt32 widthIn, PRInt32 heightIn, nsIWebBrowser** aBrowser ); + void setParent( LLEmbeddedBrowser* parentIn ) { mParent = parentIn; }; + PRBool setSize( PRInt16 widthIn, PRInt16 heightIn ); + void focusBrowser( PRBool focusBrowserIn ); + void scrollByLines( PRInt16 linesIn ); + void setWindowId( int windowIdIn ); + int getWindowId(); + + // random accessors + const PRInt16 getPercentComplete(); + const std::string& getStatusMsg(); + const std::string& getCurrentUri(); + const std::string& getClickLinkHref(); + const std::string& getClickLinkTarget(); + + // memory buffer management + unsigned char* grabWindow( int xIn, int yIn, int widthIn, int heightIn ); + PRBool flipWindow( PRBool flip ); + unsigned char* getPageBuffer(); + PRInt16 getBrowserWidth(); + PRInt16 getBrowserHeight(); + PRInt16 getBrowserDepth(); + PRInt32 getBrowserRowSpan(); + + // set background color that you see in between pages - default is white but sometimes useful to change + void setBackgroundColor( const PRUint8 redIn, const PRUint8 greenIn, const PRUint8 blueIn ); + + // change the caret color (we have different backgrounds to edit fields - black caret on black background == bad) + void setCaretColor( const PRUint8 redIn, const PRUint8 greenIn, const PRUint8 blueIn ); + + // can turn off updates to a page - e.g. when it's hidden by your windowing system + void setEnabled( PRBool enabledIn ); + + // navigation + void navigateStop(); + PRBool navigateTo( const std::string uriIn ); + PRBool canNavigateBack(); + void navigateBack(); + PRBool canNavigateForward(); + void navigateForward(); + void navigateReload(); + + // javascript access/control + std::string evaluateJavascript( std::string scriptIn ); + + // redirection when you hit a missing page + bool set404RedirectUrl( std::string redirect_url ); + bool clr404RedirectUrl(); + + // mouse & keyboard events + void mouseDown( PRInt16 xPosIn, PRInt16 yPosIn ); + void mouseUp( PRInt16 xPosIn, PRInt16 yPosIn ); + void mouseMove( PRInt16 xPosIn, PRInt16 yPosIn ); + void mouseLeftDoubleClick( PRInt16 xPosIn, PRInt16 yPosIn ); + void keyPress( PRInt16 keyCode ); + void unicodeInput( PRUint32 uni_char ); + + // allow consumers of this class and to observe browser events + bool addObserver( LLEmbeddedBrowserWindowObserver* observerIn ); + bool remObserver( LLEmbeddedBrowserWindowObserver* observerIn ); + + // accessor/mutator for scheme that browser doesn't follow - e.g. secondlife.com:// + void setNoFollowScheme( std::string schemeIn ); + std::string getNoFollowScheme(); + + private: + PRBool sendMozillaMouseEvent( PRInt16 eventIn, PRInt16 xPosIn, PRInt16 yPosIn, PRUint32 clickCountIn ); + PRBool sendMozillaKeyboardEvent( PRUint32 keyIn, PRUint32 ns_vk_code ); + PRBool renderCaret(); + PRBool enableToolkitObserver( PRBool enableIn ); + + LLEmbeddedBrowserWindowEmitter< LLEmbeddedBrowserWindowObserver > mEventEmitter; + + LLEmbeddedBrowser* mParent; + PRInt16 mPercentComplete; + std::string mStatusText; + std::string mCurrentUri; + std::string mClickHref; + std::string mClickTarget; + std::string mNoFollowScheme; + nsCOMPtr< nsIWebBrowser > mWebBrowser; + nsCOMPtr< nsIBaseWindow > mBaseWindow; + nsCOMPtr< nsIWebNavigation > mWebNav; + int mWindowId; + unsigned char* mPageBuffer; + std::string m404RedirectUrl; + PRBool mEnabled; + PRBool mFlipBitmap; + PRInt32 mBrowserRowSpan; + PRInt16 mBrowserWidth; + PRInt16 mBrowserHeight; + PRInt16 mBrowserDepth; + PRUint8 mBkgRed; + PRUint8 mBkgGreen; + PRUint8 mBkgBlue; + PRUint8 mCaretRed; + PRUint8 mCaretGreen; + PRUint8 mCaretBlue; +}; + +#endif // LLEMBEDEDDBROWSERWINDOW_H diff --git a/examples/osgbrowser/llmozlib2.cpp b/examples/osgbrowser/llmozlib2.cpp new file mode 100644 index 000000000..a829f6485 --- /dev/null +++ b/examples/osgbrowser/llmozlib2.cpp @@ -0,0 +1,686 @@ +/* ***** 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 ***** */ + +#include +#include +#include +#include + +#include "llmozlib2.h" + +#include "llembeddedbrowser.h" +#include "llembeddedbrowserwindow.h" + +LLMozLib* LLMozLib::sInstance = 0; + +//////////////////////////////////////////////////////////////////////////////// +// +LLMozLib::LLMozLib() : + mMaxBrowserWindows( 16 ) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLMozLib* LLMozLib::getInstance() +{ + if ( ! sInstance ) + { + sInstance = new LLMozLib; + }; + + return sInstance; +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLMozLib::~LLMozLib() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::init( std::string applicationDir, std::string componentDir, std::string profileDir, void* nativeWindowHandleIn ) +{ + return LLEmbeddedBrowser::getInstance()->init( applicationDir, + componentDir, + profileDir, + nativeWindowHandleIn ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLMozLib::getLastError() +{ + return LLEmbeddedBrowser::getInstance()->getLastError(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::reset() +{ + return LLEmbeddedBrowser::getInstance()->reset(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::clearCache() +{ + return LLEmbeddedBrowser::getInstance()->clearCache(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLMozLib::getVersion() +{ + const int majorVersion = 2; + const int minorVersion = 1; + + // number of hours since "time began" for this library - used to identify builds of same version + const int magicNumber = static_cast< int >( ( time( NULL ) / 3600L ) - ( 321190L ) ); + + // return as a string for now - don't think we need to expose actual version numbers + std::ostringstream codec; + codec << std::setw( 1 ) << std::setfill( '0' ); + codec << majorVersion << "."; + codec << std::setw( 2 ) << std::setfill( '0' ); + codec << minorVersion << "."; + codec << std::setw( 5 ) << std::setfill( '0' ); + codec << magicNumber; + codec << " (Mozilla GRE version "; + codec << LLEmbeddedBrowser::getInstance()->getGREVersion(); + codec << ")"; + + return codec.str(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMozLib::setBrowserAgentId( std::string idIn ) +{ + LLEmbeddedBrowser::getInstance()->setBrowserAgentId( idIn ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::enableProxy( bool proxyEnabledIn, std::string proxyHostNameIn, int proxyPortIn ) +{ + return LLEmbeddedBrowser::getInstance()->enableProxy( proxyEnabledIn, proxyHostNameIn, proxyPortIn ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLMozLib::createBrowserWindow( int browserWindowWidthIn, int browserWindowHeightIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = LLEmbeddedBrowser::getInstance()->createBrowserWindow( browserWindowWidthIn, browserWindowHeightIn ); + + if ( browserWindow ) + { + // arbitrary limit so we don't exhaust system resources + int id( 0 ); + while ( ++id < mMaxBrowserWindows ) + { + std::pair< BrowserWindowMapIter, bool > result = mBrowserWindowMap.insert( std::make_pair( id, browserWindow ) ); + + // find first place the insert succeeds and use that index as the id + if ( result.second ) + { + browserWindow->setWindowId( id ); + + return id; + }; + }; + }; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::destroyBrowserWindow( int browserWindowIdIn ) +{ + // don't use the utility method here since we need the iterator to remove the entry from the map + BrowserWindowMapIter iter = mBrowserWindowMap.find( browserWindowIdIn ); + LLEmbeddedBrowserWindow* browserWindow = (*iter).second; + + if ( browserWindow ) + { + LLEmbeddedBrowser::getInstance()->destroyBrowserWindow( browserWindow ); + }; + + mBrowserWindowMap.erase( iter ); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::setBackgroundColor( int browserWindowIdIn, const int redIn, const int greenIn, const int blueIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->setBackgroundColor( redIn, greenIn, blueIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::setCaretColor( int browserWindowIdIn, const int redIn, const int greenIn, const int blueIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->setCaretColor( redIn, greenIn, blueIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::setEnabled( int browserWindowIdIn, bool enabledIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->setEnabled( enabledIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::setSize( int browserWindowIdIn, int widthIn, int heightIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->setSize( widthIn, heightIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::scrollByLines( int browserWindowIdIn, int linesIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->scrollByLines( linesIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::addObserver( int browserWindowIdIn, LLEmbeddedBrowserWindowObserver* subjectIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->addObserver( subjectIn ); + }; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::remObserver( int browserWindowIdIn, LLEmbeddedBrowserWindowObserver* subjectIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->remObserver( subjectIn ); + }; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::navigateTo( int browserWindowIdIn, const std::string uriIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->navigateTo( uriIn ) ? true : false; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::navigateStop( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->navigateStop(); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::canNavigateBack( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->canNavigateBack() ? true : false; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::navigateBack( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->navigateBack(); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::canNavigateForward( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->canNavigateForward() ? true : false; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::navigateForward( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->navigateForward(); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::navigateReload( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->navigateReload(); + + return true; + }; + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +const unsigned char* LLMozLib::grabBrowserWindow( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->grabWindow( 0, 0, getBrowserWidth( browserWindowIdIn ), getBrowserHeight( browserWindowIdIn ) ); + }; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const unsigned char* LLMozLib::getBrowserWindowPixels( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->getPageBuffer(); + }; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const bool LLMozLib::flipWindow( int browserWindowIdIn, bool flipIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->flipWindow( flipIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const int LLMozLib::getBrowserWidth( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->getBrowserWidth(); + }; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const int LLMozLib::getBrowserHeight( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->getBrowserHeight(); + }; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const int LLMozLib::getBrowserDepth( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->getBrowserDepth(); + }; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const int LLMozLib::getBrowserRowSpan( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->getBrowserRowSpan(); + }; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::mouseDown( int browserWindowIdIn, int xPosIn, int yPosIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->mouseDown( xPosIn, yPosIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::mouseUp( int browserWindowIdIn, int xPosIn, int yPosIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->mouseUp( xPosIn, yPosIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::mouseMove( int browserWindowIdIn, int xPosIn, int yPosIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->mouseMove( xPosIn, yPosIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::mouseLeftDoubleClick( int browserWindowIdIn, int xPosIn, int yPosIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->mouseLeftDoubleClick( xPosIn, yPosIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::keyPress( int browserWindowIdIn, int keyCodeIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->keyPress( keyCodeIn ); + + return true; + }; + + return false; +} + +bool LLMozLib::unicodeInput( int browserWindowIdIn, unsigned long uni_char ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->unicodeInput( uni_char ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::focusBrowser( int browserWindowIdIn, bool focusBrowserIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->focusBrowser( focusBrowserIn ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMozLib::setNoFollowScheme( int browserWindowIdIn, std::string schemeIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + browserWindow->setNoFollowScheme( schemeIn ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMozLib::getNoFollowScheme( int browserWindowIdIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->getNoFollowScheme(); + }; + + return ( "" ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::enableCookies( bool enabledIn ) +{ + return LLEmbeddedBrowser::getInstance()->enableCookies( enabledIn ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::clearAllCookies() +{ + return LLEmbeddedBrowser::getInstance()->clearAllCookies(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::enablePlugins( bool enabledIn ) +{ + return LLEmbeddedBrowser::getInstance()->enablePlugins( enabledIn ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMozLib::evaluateJavascript( int browserWindowIdIn, const std::string scriptIn ) +{ + LLEmbeddedBrowserWindow* browserWindow = getBrowserWindowFromWindowId( browserWindowIdIn ); + if ( browserWindow ) + { + return browserWindow->evaluateJavascript( scriptIn ); + }; + + return ""; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::set404RedirectUrl( int browser_window_in, std::string redirect_url ) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId( browser_window_in ); + if ( browser_window ) + { + browser_window->set404RedirectUrl( redirect_url ); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMozLib::clr404RedirectUrl( int browser_window_in ) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId( browser_window_in ); + if ( browser_window ) + { + browser_window->clr404RedirectUrl(); + + return true; + }; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// utility method to get an LLEmbeddedBrowserWindow* from a window id (int) +LLEmbeddedBrowserWindow* LLMozLib::getBrowserWindowFromWindowId( int browserWindowIdIn ) +{ + BrowserWindowMapIter iter = mBrowserWindowMap.find( browserWindowIdIn ); + + if ( iter != mBrowserWindowMap.end() ) + return ( *iter ).second; + else + return 0; +} + diff --git a/examples/osgbrowser/llmozlib2.h b/examples/osgbrowser/llmozlib2.h new file mode 100644 index 000000000..633ca8da3 --- /dev/null +++ b/examples/osgbrowser/llmozlib2.h @@ -0,0 +1,384 @@ +/* ***** 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 ***** */ + +#ifndef LLMOZLIB_H +#define LLMOZLIB_H + +#include +#include + +class LLEmbeddedBrowser; +class LLEmbeddedBrowserWindow; + +//////////////////////////////////////////////////////////////////////////////// +// data class that is passed with an event +class LLEmbeddedBrowserWindowEvent +{ + public: + LLEmbeddedBrowserWindowEvent( int eventWindowIdIn, std::string uriIn ) : + mEventWindowId( eventWindowIdIn ), + mEventUri( uriIn ) + { + }; + + // single int passed with the event - e.g. progress + LLEmbeddedBrowserWindowEvent( int eventWindowIdIn, std::string uriIn, int intValIn ) : + mEventWindowId( eventWindowIdIn ), + mEventUri( uriIn ), + mIntVal( intValIn ) + { + }; + + // string passed with the event + LLEmbeddedBrowserWindowEvent( int eventWindowIdIn, std::string uriIn, std::string stringValIn ) : + mEventWindowId( eventWindowIdIn ), + mEventUri( uriIn ), + mStringVal( stringValIn ) + { + }; + + // 2 strings passed with the event + LLEmbeddedBrowserWindowEvent( int eventWindowIdIn, std::string uriIn, std::string stringValIn, std::string stringVal2In ) : + mEventWindowId( eventWindowIdIn ), + mEventUri( uriIn ), + mStringVal( stringValIn ), + mStringVal2( stringVal2In ) + { + }; + + // string and an int passed with the event + LLEmbeddedBrowserWindowEvent( int eventWindowIdIn, std::string uriIn, std::string stringValIn, int intValIn ) : + mEventWindowId( eventWindowIdIn ), + mEventUri( uriIn ), + mStringVal( stringValIn ), + mIntVal( intValIn ) + { + }; + + // 4 ints passed (semantically as a rectangle but could be anything - didn't want to make a RECT type structure) + LLEmbeddedBrowserWindowEvent( int eventWindowIdIn, std::string uriIn, int xIn, int yIn, int widthIn, int heightIn ) : + mEventWindowId( eventWindowIdIn ), + mEventUri( uriIn ), + mXVal( xIn ), + mYVal( yIn ), + mWidthVal( widthIn ), + mHeightVal( heightIn ) + { + }; + + virtual ~LLEmbeddedBrowserWindowEvent() + { + }; + + int getEventWindowId() const + { + return mEventWindowId; + }; + + std::string getEventUri() const + { + return mEventUri; + }; + + int getIntValue() const + { + return mIntVal; + }; + + std::string getStringValue() const + { + return mStringVal; + }; + + std::string getStringValue2() const + { + return mStringVal2; + }; + + void getRectValue( int& xOut, int& yOut, int& widthOut, int& heightOut ) const + { + xOut = mXVal; + yOut = mYVal; + widthOut = mWidthVal; + heightOut = mHeightVal; + }; + + private: + int mEventWindowId; + std::string mEventUri; + int mIntVal; + std::string mStringVal; + std::string mStringVal2; + int mXVal; + int mYVal; + int mWidthVal; + int mHeightVal; +}; + +//////////////////////////////////////////////////////////////////////////////// +// derrive from this class and override these methods to observe these events +class LLEmbeddedBrowserWindowObserver +{ + public: + virtual ~LLEmbeddedBrowserWindowObserver() { }; + typedef LLEmbeddedBrowserWindowEvent EventType; + + virtual void onPageChanged( const EventType& eventIn ) { }; + virtual void onNavigateBegin( const EventType& eventIn ) { }; + virtual void onNavigateComplete( const EventType& eventIn ) { }; + virtual void onUpdateProgress( const EventType& eventIn ) { }; + virtual void onStatusTextChange( const EventType& eventIn ) { }; + virtual void onLocationChange( const EventType& eventIn ) { }; + virtual void onClickLinkHref( const EventType& eventIn ) { }; + virtual void onClickLinkNoFollow( const EventType& eventIn ) { }; +}; + +//////////////////////////////////////////////////////////////////////////////// +// main library class +class LLMozLib +{ + public: + virtual ~LLMozLib(); + + // singleton access + static LLMozLib* getInstance(); + + // housekeeping + bool init( std::string applicationDir, std::string componentDir, std::string profileDir, void* nativeWindowHandleIn ); + bool reset(); + bool clearCache(); + int getLastError(); + const std::string getVersion(); + void setBrowserAgentId( std::string idIn ); + bool enableProxy( bool proxyEnabledIn, std::string proxyHostNameIn, int proxyPortIn ); + bool enableCookies( bool enabledIn ); + bool clearAllCookies(); + bool enablePlugins( bool enabledIn ); + + // browser window - creation/deletion, mutation etc. + int createBrowserWindow( int browserWindowWidthIn, int browserWindowHeightIn ); + bool destroyBrowserWindow( int browserWindowIdIn ); + bool setSize( int browserWindowIdIn, int widthIn, int heightIn ); + bool scrollByLines( int browserWindowIdIn, int linesIn ); + bool setBackgroundColor( int browserWindowIdIn, const int redIn, const int greenIn, const int blueIn ); + bool setCaretColor( int browserWindowIdIn, const int redIn, const int greenIn, const int blueIn ); + bool setEnabled( int browserWindowIdIn, bool enabledIn ); + + // add/remove yourself as an observer on browser events - see LLEmbeddedBrowserWindowObserver declaration + bool addObserver( int browserWindowIdIn, LLEmbeddedBrowserWindowObserver* subjectIn ); + bool remObserver( int browserWindowIdIn, LLEmbeddedBrowserWindowObserver* subjectIn ); + + // navigation - self explanatory + bool navigateTo( int browserWindowIdIn, const std::string uriIn ); + bool navigateStop( int browserWindowIdIn ); + bool canNavigateBack( int browserWindowIdIn ); + bool navigateBack( int browserWindowIdIn ); + bool canNavigateForward( int browserWindowIdIn ); + bool navigateForward( int browserWindowIdIn ); + bool navigateReload( int browserWindowIdIn ); + + // javascript access/control + std::string evaluateJavascript( int browserWindowIdIn, const std::string scriptIn ); + + // set/clear URL to redirect to when a 404 page is reached + bool set404RedirectUrl( int browser_window_in, std::string redirect_url ); + bool clr404RedirectUrl( int browser_window_in ); + + // access to rendered bitmap data + const unsigned char* grabBrowserWindow( int browserWindowIdIn ); // renders page to memory and returns pixels + const unsigned char* getBrowserWindowPixels( int browserWindowIdIn ); // just returns pixels - no render + const bool flipWindow( int browserWindowIdIn, bool flipIn ); // optionally flip window (pixels) you get back + const int getBrowserWidth( int browserWindowIdIn ); // current browser width (can vary slightly after page is rendered) + const int getBrowserHeight( int browserWindowIdIn ); // current height + const int getBrowserDepth( int browserWindowIdIn ); // depth in bytes + const int getBrowserRowSpan( int browserWindowIdIn ); // width in pixels * depth in bytes + + // mouse/keyboard interaction + bool mouseDown( int browserWindowIdIn, int xPosIn, int yPosIn ); // send a mouse down event to a browser window at given XY in browser space + bool mouseUp( int browserWindowIdIn, int xPosIn, int yPosIn ); // send a mouse up event to a browser window at given XY in browser space + bool mouseMove( int browserWindowIdIn, int xPosIn, int yPosIn ); // send a mouse move event to a browser window at given XY in browser space + bool mouseLeftDoubleClick( int browserWindowIdIn, int xPosIn, int yPosIn ); // send a mouse left button double click to a browser window at given XY in browser space + bool keyPress( int browserWindowIdIn, int keyCodeIn ); // send a key press event to a browser window + bool unicodeInput ( int browserWindowIdIn, unsigned long uni_char ); // send a unicode keypress event to a browser window + bool focusBrowser( int browserWindowIdIn, bool focusBrowserIn ); // set/remove focus to given browser window + + // accessor/mutator for scheme that browser doesn't follow - e.g. secondlife.com:// + void setNoFollowScheme( int browserWindowIdIn, std::string schemeIn ); + std::string getNoFollowScheme( int browserWindowIdIn ); + + private: + LLMozLib(); + LLEmbeddedBrowserWindow* getBrowserWindowFromWindowId( int browserWindowIdIn ); + static LLMozLib* sInstance; + const int mMaxBrowserWindows; + typedef std::map< int, LLEmbeddedBrowserWindow* > BrowserWindowMap; + typedef std::map< int, LLEmbeddedBrowserWindow* >::iterator BrowserWindowMapIter; + BrowserWindowMap mBrowserWindowMap; +}; + +// Mozilla virtual keycodes. +// We don't want to suck in Mozilla headers so we copy these consts +// from nsIDOMKeyEvent.idl. + +const unsigned long LL_DOM_VK_CANCEL = 0x03; +const unsigned long LL_DOM_VK_HELP = 0x06; +const unsigned long LL_DOM_VK_BACK_SPACE = 0x08; +const unsigned long LL_DOM_VK_TAB = 0x09; +const unsigned long LL_DOM_VK_CLEAR = 0x0C; +const unsigned long LL_DOM_VK_RETURN = 0x0D; +const unsigned long LL_DOM_VK_ENTER = 0x0E; +const unsigned long LL_DOM_VK_SHIFT = 0x10; +const unsigned long LL_DOM_VK_CONTROL = 0x11; +const unsigned long LL_DOM_VK_ALT = 0x12; +const unsigned long LL_DOM_VK_PAUSE = 0x13; +const unsigned long LL_DOM_VK_CAPS_LOCK = 0x14; +const unsigned long LL_DOM_VK_ESCAPE = 0x1B; +const unsigned long LL_DOM_VK_SPACE = 0x20; +const unsigned long LL_DOM_VK_PAGE_UP = 0x21; +const unsigned long LL_DOM_VK_PAGE_DOWN = 0x22; +const unsigned long LL_DOM_VK_END = 0x23; +const unsigned long LL_DOM_VK_HOME = 0x24; +const unsigned long LL_DOM_VK_LEFT = 0x25; +const unsigned long LL_DOM_VK_UP = 0x26; +const unsigned long LL_DOM_VK_RIGHT = 0x27; +const unsigned long LL_DOM_VK_DOWN = 0x28; +const unsigned long LL_DOM_VK_PRINTSCREEN = 0x2C; +const unsigned long LL_DOM_VK_INSERT = 0x2D; +const unsigned long LL_DOM_VK_DELETE = 0x2E; + +// LL_DOM_VK_0 - LL_DOM_VK_9 match their ASCII values +const unsigned long LL_DOM_VK_0 = 0x30; +const unsigned long LL_DOM_VK_1 = 0x31; +const unsigned long LL_DOM_VK_2 = 0x32; +const unsigned long LL_DOM_VK_3 = 0x33; +const unsigned long LL_DOM_VK_4 = 0x34; +const unsigned long LL_DOM_VK_5 = 0x35; +const unsigned long LL_DOM_VK_6 = 0x36; +const unsigned long LL_DOM_VK_7 = 0x37; +const unsigned long LL_DOM_VK_8 = 0x38; +const unsigned long LL_DOM_VK_9 = 0x39; + +const unsigned long LL_DOM_VK_SEMICOLON = 0x3B; +const unsigned long LL_DOM_VK_EQUALS = 0x3D; + +// LL_DOM_VK_A - LL_DOM_VK_Z match their ASCII values +const unsigned long LL_DOM_VK_A = 0x41; +const unsigned long LL_DOM_VK_B = 0x42; +const unsigned long LL_DOM_VK_C = 0x43; +const unsigned long LL_DOM_VK_D = 0x44; +const unsigned long LL_DOM_VK_E = 0x45; +const unsigned long LL_DOM_VK_F = 0x46; +const unsigned long LL_DOM_VK_G = 0x47; +const unsigned long LL_DOM_VK_H = 0x48; +const unsigned long LL_DOM_VK_I = 0x49; +const unsigned long LL_DOM_VK_J = 0x4A; +const unsigned long LL_DOM_VK_K = 0x4B; +const unsigned long LL_DOM_VK_L = 0x4C; +const unsigned long LL_DOM_VK_M = 0x4D; +const unsigned long LL_DOM_VK_N = 0x4E; +const unsigned long LL_DOM_VK_O = 0x4F; +const unsigned long LL_DOM_VK_P = 0x50; +const unsigned long LL_DOM_VK_Q = 0x51; +const unsigned long LL_DOM_VK_R = 0x52; +const unsigned long LL_DOM_VK_S = 0x53; +const unsigned long LL_DOM_VK_T = 0x54; +const unsigned long LL_DOM_VK_U = 0x55; +const unsigned long LL_DOM_VK_V = 0x56; +const unsigned long LL_DOM_VK_W = 0x57; +const unsigned long LL_DOM_VK_X = 0x58; +const unsigned long LL_DOM_VK_Y = 0x59; +const unsigned long LL_DOM_VK_Z = 0x5A; + +const unsigned long LL_DOM_VK_CONTEXT_MENU = 0x5D; + +const unsigned long LL_DOM_VK_NUMPAD0 = 0x60; +const unsigned long LL_DOM_VK_NUMPAD1 = 0x61; +const unsigned long LL_DOM_VK_NUMPAD2 = 0x62; +const unsigned long LL_DOM_VK_NUMPAD3 = 0x63; +const unsigned long LL_DOM_VK_NUMPAD4 = 0x64; +const unsigned long LL_DOM_VK_NUMPAD5 = 0x65; +const unsigned long LL_DOM_VK_NUMPAD6 = 0x66; +const unsigned long LL_DOM_VK_NUMPAD7 = 0x67; +const unsigned long LL_DOM_VK_NUMPAD8 = 0x68; +const unsigned long LL_DOM_VK_NUMPAD9 = 0x69; +const unsigned long LL_DOM_VK_MULTIPLY = 0x6A; +const unsigned long LL_DOM_VK_ADD = 0x6B; +const unsigned long LL_DOM_VK_SEPARATOR = 0x6C; +const unsigned long LL_DOM_VK_SUBTRACT = 0x6D; +const unsigned long LL_DOM_VK_DECIMAL = 0x6E; +const unsigned long LL_DOM_VK_DIVIDE = 0x6F; +const unsigned long LL_DOM_VK_F1 = 0x70; +const unsigned long LL_DOM_VK_F2 = 0x71; +const unsigned long LL_DOM_VK_F3 = 0x72; +const unsigned long LL_DOM_VK_F4 = 0x73; +const unsigned long LL_DOM_VK_F5 = 0x74; +const unsigned long LL_DOM_VK_F6 = 0x75; +const unsigned long LL_DOM_VK_F7 = 0x76; +const unsigned long LL_DOM_VK_F8 = 0x77; +const unsigned long LL_DOM_VK_F9 = 0x78; +const unsigned long LL_DOM_VK_F10 = 0x79; +const unsigned long LL_DOM_VK_F11 = 0x7A; +const unsigned long LL_DOM_VK_F12 = 0x7B; +const unsigned long LL_DOM_VK_F13 = 0x7C; +const unsigned long LL_DOM_VK_F14 = 0x7D; +const unsigned long LL_DOM_VK_F15 = 0x7E; +const unsigned long LL_DOM_VK_F16 = 0x7F; +const unsigned long LL_DOM_VK_F17 = 0x80; +const unsigned long LL_DOM_VK_F18 = 0x81; +const unsigned long LL_DOM_VK_F19 = 0x82; +const unsigned long LL_DOM_VK_F20 = 0x83; +const unsigned long LL_DOM_VK_F21 = 0x84; +const unsigned long LL_DOM_VK_F22 = 0x85; +const unsigned long LL_DOM_VK_F23 = 0x86; +const unsigned long LL_DOM_VK_F24 = 0x87; + +const unsigned long LL_DOM_VK_NUM_LOCK = 0x90; +const unsigned long LL_DOM_VK_SCROLL_LOCK = 0x91; + +const unsigned long LL_DOM_VK_COMMA = 0xBC; +const unsigned long LL_DOM_VK_PERIOD = 0xBE; +const unsigned long LL_DOM_VK_SLASH = 0xBF; +const unsigned long LL_DOM_VK_BACK_QUOTE = 0xC0; +const unsigned long LL_DOM_VK_OPEN_BRACKET = 0xDB; +const unsigned long LL_DOM_VK_BACK_SLASH = 0xDC; +const unsigned long LL_DOM_VK_CLOSE_BRACKET = 0xDD; +const unsigned long LL_DOM_VK_QUOTE = 0xDE; + +const unsigned long LL_DOM_VK_META = 0xE0; + +#endif // LLMOZLIB_H diff --git a/examples/osgbrowser/osgbrowser.cpp b/examples/osgbrowser/osgbrowser.cpp new file mode 100644 index 000000000..b629db58d --- /dev/null +++ b/examples/osgbrowser/osgbrowser.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + + +class HttpImage : public osg::Image +{ + public: + + HttpImage() {} + + bool open(const std::string& filename) + { + osg::notify(osg::NOTICE)<<"open("<getOrigin()==osg::Image::TOP_LEFT; + + osg::Geometry* pictureQuad = osg::createTexturedQuadGeometry(origin, widthAxis, heightAxis, + 0.0f, flip ? 1.0f : 0.0f , 1.0f, flip ? 0.0f : 1.0f); + + osg::Texture2D* texture = new osg::Texture2D(image); + texture->setResizeNonPowerOfTwoHint(false); + texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR); + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + + pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0, + texture, + osg::StateAttribute::ON); + + pictureQuad->setEventCallback(new osgViewer::InteractiveImageHandler(image)); + + osg::Geode* geode = new osg::Geode; + geode->addDrawable(pictureQuad); + + return geode; +} + +int main(int argc,char** argv) +{ + + osg::ArgumentParser arguments(&argc, argv); + osgViewer::Viewer viewer(arguments); + + typedef std::list< osg::ref_ptr > Images; + Images images; + + for(int i=1; i httpImage= new HttpImage; + if (httpImage->open(arguments[i])) + { + images.push_back(httpImage.get()); + } + } + } + + bool xyPlane = false; + + osg::Group* group = new osg::Group; + + osg::Vec3 origin = osg::Vec3(0.0f,0.0f,0.0f); + for(Images::iterator itr = images.begin(); + itr != images.end(); + ++itr) + { + osg::Image* image = itr->get(); + float width = 1.0; + float height = float(image->t())/float(image->s()); + osg::Vec3 widthAxis = osg::Vec3(width,0.0f,0.0f); + osg::Vec3 heightAxis = xyPlane ? osg::Vec3(0.0f,height,0.0f) : osg::Vec3(0.0f,0.0f,height); + group->addChild(createInteractiveQuad(origin, widthAxis, heightAxis, image)); + + origin += widthAxis*1.1f; + } + + viewer.setSceneData(group); + + viewer.addEventHandler(new osgViewer::StatsHandler); + + //viewer.addEventHandler(new PageHandler); + + return viewer.run(); +} +