536 lines
16 KiB
C++
536 lines
16 KiB
C++
/* Copyright (c) 2006-2010, Linden Research, Inc.
|
|
*
|
|
* LLQtWebKit Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in GPL-license.txt in this distribution, or online at
|
|
* http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
*/
|
|
|
|
#include "llwebpage.h"
|
|
|
|
#include <qnetworkrequest.h>
|
|
#include <qwebframe.h>
|
|
#include <qgraphicswebview.h>
|
|
#include <qevent.h>
|
|
#include <qdebug.h>
|
|
#include <qmessagebox.h>
|
|
#include <qwebelement.h>
|
|
#include <qgraphicsproxywidget.h>
|
|
|
|
#include <ctime>
|
|
|
|
#include "llqtwebkit.h"
|
|
#include "llembeddedbrowser.h"
|
|
#include "llembeddedbrowserwindow.h"
|
|
#include "llembeddedbrowserwindow_p.h"
|
|
#include "lljsobject.h"
|
|
|
|
LLWebPage::LLWebPage(QObject *parent)
|
|
: QWebPage(parent)
|
|
, window(0)
|
|
, mHostLanguage( "en" )
|
|
, mWhiteListRegex( "" )
|
|
{
|
|
mJsObject = new LLJsObject( parent );
|
|
|
|
connect(this, SIGNAL(loadProgress(int)),
|
|
this, SLOT(loadProgressSlot(int)));
|
|
connect(this, SIGNAL(linkHovered(const QString &, const QString &, const QString &)),
|
|
this, SLOT(linkHoveredSlot(const QString &, const QString &, const QString &)));
|
|
connect(this, SIGNAL(statusBarMessage(const QString &)),
|
|
this, SLOT(statusBarMessageSlot(const QString &)));
|
|
connect(mainFrame(), SIGNAL(urlChanged(const QUrl&)),
|
|
this, SLOT(urlChangedSlot(const QUrl&)));
|
|
connect(this, SIGNAL(loadStarted()),
|
|
this, SLOT(loadStarted()));
|
|
connect(this, SIGNAL(loadFinished(bool)),
|
|
this, SLOT(loadFinished(bool)));
|
|
connect(this, SIGNAL(windowCloseRequested()),
|
|
this, SLOT(windowCloseRequested()));
|
|
connect(this, SIGNAL(geometryChangeRequested(const QRect&)),
|
|
this, SLOT(geometryChangeRequested(const QRect&)));
|
|
connect(mainFrame(), SIGNAL(titleChanged(const QString&)),
|
|
this, SLOT(titleChangedSlot(const QString&)));
|
|
connect(mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
|
|
this, SLOT(extendNavigatorObject()));
|
|
}
|
|
|
|
LLWebPage::~LLWebPage()
|
|
{
|
|
delete mJsObject;
|
|
}
|
|
|
|
void LLWebPage::loadProgressSlot(int progress)
|
|
{
|
|
if (!window)
|
|
return;
|
|
window->d->mPercentComplete = progress;
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(window->getCurrentUri());
|
|
event.setIntValue(progress);
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onUpdateProgress, event);
|
|
|
|
if ( progress >= 100 )
|
|
window->d->mShowLoadingOverlay = false;
|
|
|
|
window->d->mDirty = true;
|
|
window->grabWindow(0,0,webView->boundingRect().width(),webView->boundingRect().height());
|
|
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onPageChanged, event);
|
|
}
|
|
|
|
void LLWebPage::linkHoveredSlot(const QString &link, const QString &title, const QString &textContent)
|
|
{
|
|
if (!window)
|
|
return;
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(llToStdString(link));
|
|
event.setStringValue(llToStdString(title));
|
|
event.setStringValue2(llToStdString(textContent));
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onLinkHovered, event);
|
|
}
|
|
|
|
void LLWebPage::statusBarMessageSlot(const QString& text)
|
|
{
|
|
if (!window)
|
|
return;
|
|
window->d->mStatusText = llToStdString(text);
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(window->getCurrentUri());
|
|
event.setStringValue(window->d->mStatusText);
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onStatusTextChange, event);
|
|
}
|
|
|
|
void LLWebPage::titleChangedSlot(const QString& text)
|
|
{
|
|
if (!window)
|
|
return;
|
|
window->d->mTitle = llToStdString(text);
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(window->getCurrentUri());
|
|
event.setStringValue(window->d->mTitle);
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onTitleChange, event);
|
|
}
|
|
|
|
// set the regex used to determine if a page is trusted or not
|
|
void LLWebPage::setWhiteListRegex( const std::string& regex )
|
|
{
|
|
mWhiteListRegex = regex;
|
|
}
|
|
|
|
void LLWebPage::configureTrustedPage( bool is_trusted )
|
|
{
|
|
// action happens in browser window parent
|
|
LLEmbeddedBrowser* parent_browser = 0;
|
|
if ( window && window->d && window->d->mParent )
|
|
{
|
|
parent_browser = window->d->mParent;
|
|
if ( parent_browser )
|
|
{
|
|
if ( is_trusted )
|
|
{
|
|
//qDebug() << "Whitelist passed - turning on";
|
|
|
|
// trusted so turn everything on
|
|
parent_browser->enableJavaScriptTransient( true );
|
|
parent_browser->enableCookiesTransient( true );
|
|
parent_browser->enablePluginsTransient( true );
|
|
}
|
|
else
|
|
{
|
|
//qDebug() << "Whitelist failed - reverting to default state";
|
|
|
|
// restore default state set by client
|
|
parent_browser->enableJavaScript( parent_browser->isJavaScriptEnabled() );
|
|
parent_browser->enableCookies( parent_browser->areCookiesEnabled() );
|
|
parent_browser->enablePlugins( parent_browser->arePluginsEnabled() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool LLWebPage::checkRegex( const QUrl& url )
|
|
{
|
|
QRegExp reg_exp( QString::fromStdString( mWhiteListRegex ) );
|
|
reg_exp.setCaseSensitivity( Qt::CaseInsensitive );
|
|
reg_exp.setMinimal( true );
|
|
|
|
if ( reg_exp.exactMatch( url.host() ) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void LLWebPage::checkWhiteList( const QUrl& url )
|
|
{
|
|
if ( mWhiteListRegex.length() )
|
|
{
|
|
if ( checkRegex( url ) )
|
|
{
|
|
configureTrustedPage( true ); // page is "trusted" - go ahead and configure it as such
|
|
}
|
|
else
|
|
{
|
|
configureTrustedPage( false ); // page is "NOT trusted" - go ahead and configure it as such
|
|
}
|
|
}
|
|
else
|
|
// no regex specified, don't do anything (i.e. don't change trust state)
|
|
{
|
|
}
|
|
}
|
|
|
|
void LLWebPage::urlChangedSlot(const QUrl& url)
|
|
{
|
|
if (!window)
|
|
return;
|
|
|
|
checkWhiteList( url );
|
|
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(window->getCurrentUri());
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onLocationChange, event);
|
|
}
|
|
|
|
bool LLWebPage::event(QEvent *event)
|
|
{
|
|
bool result = QWebPage::event(event);
|
|
|
|
if (event->type() == QEvent::GraphicsSceneMousePress)
|
|
currentPoint = ((QGraphicsSceneMouseEvent*)event)->pos().toPoint();
|
|
else if(event->type() == QEvent::GraphicsSceneMouseRelease)
|
|
currentPoint = QPoint();
|
|
|
|
return result;
|
|
}
|
|
|
|
bool LLWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type)
|
|
{
|
|
Q_UNUSED( frame );
|
|
|
|
if (!window)
|
|
return false;
|
|
|
|
if (request.url().scheme() == window->d->mNoFollowScheme)
|
|
{
|
|
QString encodedUrl = request.url().toEncoded();
|
|
// QUrl is turning foo:///home/bar into foo:/home/bar for some reason while Firefox does not
|
|
// http://bugs.webkit.org/show_bug.cgi?id=24695
|
|
if (!encodedUrl.startsWith(window->d->mNoFollowScheme + "://")) {
|
|
encodedUrl = encodedUrl.mid(window->d->mNoFollowScheme.length() + 1);
|
|
encodedUrl = window->d->mNoFollowScheme + "://" + encodedUrl;
|
|
}
|
|
std::string rawUri = llToStdString(encodedUrl);
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(rawUri);
|
|
|
|
// pass over the navigation type as per this page: http://apidocs.meego.com/1.1/core/html/qt4/qwebpage.html#NavigationType-enum
|
|
// pass as strings because telling everyone who needs to know about enums is too invasive.
|
|
std::string nav_type("unknown");
|
|
if (type == QWebPage::NavigationTypeLinkClicked) nav_type="clicked";
|
|
else
|
|
if (type == QWebPage::NavigationTypeFormSubmitted) nav_type="form_submited";
|
|
else
|
|
if (type == QWebPage::NavigationTypeBackOrForward) nav_type="back_forward";
|
|
else
|
|
if (type == QWebPage::NavigationTypeReload) nav_type="reloaded";
|
|
else
|
|
if (type == QWebPage::NavigationTypeFormResubmitted) nav_type="form_resubmited";
|
|
event.setNavigationType(nav_type);
|
|
|
|
if ( mWhiteListRegex.length() )
|
|
{
|
|
if ( frame )
|
|
{
|
|
if ( checkRegex( frame->url() ) )
|
|
{
|
|
event.setTrustedHost( true );
|
|
}
|
|
else
|
|
{
|
|
event.setTrustedHost( false );
|
|
}
|
|
}
|
|
else
|
|
// no frame - no trust (TODO: when can this happen?)
|
|
{
|
|
event.setTrustedHost( false );
|
|
}
|
|
}
|
|
else
|
|
// no regex is like switching it off and indicating everything is trusted
|
|
{
|
|
event.setTrustedHost( true );
|
|
}
|
|
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onClickLinkNoFollow, event);
|
|
|
|
//qDebug() << "LLWebPage::acceptNavigationRequest: sending onClickLinkNoFollow, NavigationType is " << type << ", url is " << QString::fromStdString(rawUri) ;
|
|
return false;
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void LLWebPage::loadStarted()
|
|
{
|
|
if (!window)
|
|
return;
|
|
|
|
QUrl url( QString::fromStdString( window->getCurrentUri() ) );
|
|
checkWhiteList( url );
|
|
|
|
window->d->mShowLoadingOverlay = true;
|
|
|
|
window->d->mTimeLoadStarted=time(NULL);
|
|
|
|
window->d->mDirty = true;
|
|
window->grabWindow(0,0,webView->boundingRect().width(),webView->boundingRect().height());
|
|
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(window->getCurrentUri());
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onNavigateBegin, event);
|
|
}
|
|
|
|
void LLWebPage::loadFinished(bool)
|
|
{
|
|
if (!window)
|
|
return;
|
|
|
|
window->d->mShowLoadingOverlay = false;
|
|
|
|
window->d->mDirty = true;
|
|
window->grabWindow(0,0,webView->boundingRect().width(),webView->boundingRect().height());
|
|
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
event.setEventUri(window->getCurrentUri());
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onPageChanged, event);
|
|
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onNavigateComplete, event);
|
|
}
|
|
|
|
void LLWebPage::windowCloseRequested()
|
|
{
|
|
if (!window)
|
|
return;
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onWindowCloseRequested, event);
|
|
}
|
|
|
|
void LLWebPage::geometryChangeRequested(const QRect& geom)
|
|
{
|
|
if (!window)
|
|
return;
|
|
|
|
LLEmbeddedBrowserWindowEvent event(window->getWindowId());
|
|
// empty UUID indicates this is targeting the main window
|
|
// event.setStringValue(window->getUUID());
|
|
event.setRectValue(geom.x(), geom.y(), geom.width(), geom.height());
|
|
window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onWindowGeometryChangeRequested, event);
|
|
}
|
|
|
|
QString LLWebPage::chooseFile(QWebFrame* parentFrame, const QString& suggestedFile)
|
|
{
|
|
Q_UNUSED(parentFrame);
|
|
Q_UNUSED(suggestedFile);
|
|
|
|
return QString::fromStdString( window->requestFilePicker() );
|
|
}
|
|
|
|
void LLWebPage::javaScriptAlert(QWebFrame* frame, const QString& msg)
|
|
{
|
|
Q_UNUSED(frame);
|
|
QMessageBox *msgBox = new QMessageBox;
|
|
msgBox->setWindowTitle(tr("JavaScript Alert - %1").arg(mainFrame()->url().host()));
|
|
msgBox->setText(msg);
|
|
msgBox->addButton(QMessageBox::Ok);
|
|
|
|
QGraphicsProxyWidget *proxy = webView->scene()->addWidget(msgBox);
|
|
proxy->setWindowFlags(Qt::Window); // this makes the item a panel (and will make it get a window 'frame')
|
|
proxy->setPanelModality(QGraphicsItem::SceneModal);
|
|
proxy->setPos((webView->boundingRect().width() - msgBox->sizeHint().width())/2,
|
|
(webView->boundingRect().height() - msgBox->sizeHint().height())/2);
|
|
proxy->setActive(true); // make it the active item
|
|
|
|
connect(msgBox, SIGNAL(finished(int)), proxy, SLOT(deleteLater()));
|
|
msgBox->show();
|
|
|
|
webView->scene()->setFocusItem(proxy);
|
|
}
|
|
|
|
bool LLWebPage::javaScriptConfirm(QWebFrame* frame, const QString& msg)
|
|
{
|
|
Q_UNUSED(frame);
|
|
Q_UNUSED(msg);
|
|
qWarning() << "LLWebPage::" << __FUNCTION__ << "not implemented" << msg << "returning true";
|
|
return true;
|
|
}
|
|
|
|
bool LLWebPage::javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
|
|
{
|
|
Q_UNUSED(frame);
|
|
Q_UNUSED(msg);
|
|
Q_UNUSED(defaultValue);
|
|
Q_UNUSED(result);
|
|
qWarning() << "LLWebPage::" << __FUNCTION__ << "not implemented" << msg << defaultValue << "returning false";
|
|
return false;
|
|
}
|
|
|
|
void LLWebPage::extendNavigatorObject()
|
|
{
|
|
// legacy - will go away in the future
|
|
QString q_host_language = QString::fromStdString( mHostLanguage );
|
|
mainFrame()->evaluateJavaScript(QString("navigator.hostLanguage=\"%1\"").arg( q_host_language ));
|
|
|
|
// the new way
|
|
if ( mJsObject )
|
|
{
|
|
bool enabled = mJsObject->getSLObjectEnabled();
|
|
if ( enabled )
|
|
{
|
|
mainFrame()->addToJavaScriptWindowObject("slviewer", mJsObject );
|
|
};
|
|
};
|
|
}
|
|
|
|
QWebPage *LLWebPage::createWindow(WebWindowType type)
|
|
{
|
|
Q_UNUSED(type);
|
|
QWebPage *result = NULL;
|
|
|
|
if(window)
|
|
{
|
|
result = window->createWindow();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void LLWebPage::setHostLanguage(const std::string& host_language)
|
|
{
|
|
mHostLanguage = host_language;
|
|
}
|
|
|
|
bool LLWebPage::supportsExtension(QWebPage::Extension extension) const
|
|
{
|
|
if (extension == QWebPage::ErrorPageExtension)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool LLWebPage::extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
|
|
{
|
|
const QWebPage::ErrorPageExtensionOption* info = static_cast<const QWebPage::ErrorPageExtensionOption*>(option);
|
|
QWebPage::ErrorPageExtensionReturn* errorPage = static_cast<QWebPage::ErrorPageExtensionReturn*>(output);
|
|
|
|
errorPage->content = QString("<html><head><title>Failed loading page</title></head><body bgcolor=\"#ffe0e0\" text=\"#000000\"><h3><tt>%1</tt></h3></body></html>")
|
|
.arg(info->errorString).toUtf8();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Second Life viewer specific functions
|
|
void LLWebPage::setSLObjectEnabled( bool enabled )
|
|
{
|
|
if ( mJsObject )
|
|
mJsObject->setSLObjectEnabled( enabled );
|
|
}
|
|
|
|
void LLWebPage::setAgentLanguage( const std::string& agent_language )
|
|
{
|
|
if ( mJsObject )
|
|
mJsObject->setAgentLanguage( QString::fromStdString( agent_language ) );
|
|
}
|
|
|
|
void LLWebPage::setAgentRegion( const std::string& agent_region )
|
|
{
|
|
if ( mJsObject )
|
|
mJsObject->setAgentRegion( QString::fromStdString( agent_region ) );
|
|
}
|
|
|
|
void LLWebPage::setAgentLocation( double x, double y, double z )
|
|
{
|
|
if ( mJsObject )
|
|
{
|
|
QVariantMap location;
|
|
location["x"] = x;
|
|
location["y"] = y;
|
|
location["z"] = z;
|
|
mJsObject->setAgentLocation( location );
|
|
}
|
|
}
|
|
|
|
void LLWebPage::setAgentGlobalLocation( double x, double y, double z )
|
|
{
|
|
if ( mJsObject )
|
|
{
|
|
QVariantMap global_location;
|
|
global_location["x"] = x;
|
|
global_location["y"] = y;
|
|
global_location["z"] = z;
|
|
mJsObject->setAgentGlobalLocation( global_location );
|
|
}
|
|
}
|
|
|
|
void LLWebPage::setAgentOrientation( double angle )
|
|
{
|
|
if ( mJsObject )
|
|
{
|
|
mJsObject->setAgentOrientation( angle );
|
|
}
|
|
}
|
|
|
|
void LLWebPage::setAgentMaturity( const std::string& agent_maturity )
|
|
{
|
|
if ( mJsObject )
|
|
mJsObject->setAgentMaturity( QString::fromStdString( agent_maturity ) );
|
|
}
|
|
|
|
void LLWebPage::emitLocation()
|
|
{
|
|
if ( mJsObject )
|
|
mJsObject->emitLocation();
|
|
}
|
|
|
|
void LLWebPage::emitMaturity()
|
|
{
|
|
if ( mJsObject )
|
|
mJsObject->emitMaturity();
|
|
}
|
|
|
|
void LLWebPage::emitLanguage()
|
|
{
|
|
if ( mJsObject )
|
|
mJsObject->emitLanguage();
|
|
}
|
|
|
|
void LLWebPage::setPageZoomFactor( double factor )
|
|
{
|
|
if ( webView )
|
|
{
|
|
webView->setZoomFactor( factor );
|
|
}
|
|
} |