2020-09-22 15:12:48 +00:00
import QtQuick 2.13
2020-09-28 18:33:37 +00:00
import QtQuick . Controls 2.13
import QtQuick . Controls 1.0 as QQC1
2020-09-22 15:12:48 +00:00
import QtQuick . Layouts 1.13
2020-09-22 19:16:44 +00:00
import QtWebEngine 1.10
import QtWebChannel 1.13
2020-09-28 18:33:37 +00:00
import Qt . labs . settings 1.0
import QtQuick . Controls . Styles 1.0
import QtQuick . Dialogs 1.2
2020-09-25 19:05:07 +00:00
import "../../../shared"
2020-10-08 18:08:50 +00:00
import "../../../shared/status"
2020-09-25 19:05:07 +00:00
import "../../../imports"
2020-10-02 17:23:33 +00:00
import "../Chat/ChatColumn/ChatComponents"
2020-09-22 15:12:48 +00:00
2020-09-28 18:33:37 +00:00
// Code based on https://code.qt.io/cgit/qt/qtwebengine.git/tree/examples/webengine/quicknanobrowser/BrowserWindow.qml?h=5.15
// Licensed under BSD
2020-10-08 18:08:50 +00:00
Rectangle {
2020-09-28 18:33:37 +00:00
id: browserWindow
2020-10-08 18:08:50 +00:00
color: Style . current . inputBackground
border.width: 0
2020-09-28 18:33:37 +00:00
property Item currentWebView: tabs . currentIndex < tabs . count ? tabs . getTab ( tabs . currentIndex ) . item : null
2020-10-22 17:34:14 +00:00
2020-09-28 18:33:37 +00:00
property Component browserDialogComponent: BrowserDialog {
onClosing: destroy ( )
}
2020-10-19 20:26:16 +00:00
property bool currentTabConnected: false
2020-09-28 18:33:37 +00:00
2020-10-22 17:34:14 +00:00
ListModel {
id: downloadModel
property var downloads: [ ]
}
2020-09-28 18:33:37 +00:00
2020-10-22 19:51:49 +00:00
function removeDownloadFromModel ( index ) {
downloadModel . downloads = downloadModel . downloads . filter ( function ( el ) {
return el . id !== downloadModel . downloads [ index ] . id ;
} ) ;
downloadModel . remove ( index ) ;
}
2020-09-22 15:12:48 +00:00
Layout.fillHeight: true
Layout.fillWidth: true
2020-09-30 18:59:36 +00:00
property var urlENSDictionary: ( { } )
2020-09-30 14:31:04 +00:00
function determineRealURL ( text ) {
2020-10-07 19:49:26 +00:00
var url = _utilsModel . urlFromUserInput ( text ) ;
var host = _web3Provider . getHost ( url ) ;
2020-09-30 14:31:04 +00:00
if ( host . endsWith ( ".eth" ) ) {
2020-10-07 19:49:26 +00:00
var ensResource = _web3Provider . ensResourceURL ( host , url ) ;
2020-11-02 14:11:40 +00:00
if ( /^https\:\/\/swarm\-gateways\.net\/bzz:\/([0-9a-fA-F]{64}|.+\.eth)(\/?)/ . test ( ensResource ) ) {
// TODO: populate urlENSDictionary for prettier url instead of swarm-gateway big URL
return ensResource ;
} else {
urlENSDictionary [ _web3Provider . getHost ( ensResource ) ] = host ;
}
2020-09-30 18:59:36 +00:00
url = ensResource ;
2020-09-30 14:31:04 +00:00
}
return url ;
}
2020-09-25 19:05:07 +00:00
2020-10-19 19:00:43 +00:00
property Component accessDialogComponent: BrowserConnectionModal {
currentTab: tabs . getTab ( tabs . currentIndex ) && tabs . getTab ( tabs . currentIndex ) . item
x: browserWindow . width - width - Style . current . halfPadding
y: browserHeader . y + browserHeader . height + Style . current . halfPadding
2020-09-25 19:05:07 +00:00
}
2020-10-02 17:23:33 +00:00
// TODO we'll need a new dialog at one point because this one is not using the same call, but it's good for now
property Component sendTransactionModalComponent: SignTransactionModal { }
2020-09-25 19:05:07 +00:00
2020-10-05 16:24:43 +00:00
property Component signMessageModalComponent: SignMessageModal { }
2020-10-02 17:23:33 +00:00
property MessageDialog sendingError: MessageDialog {
id: sendingError
//% "Error sending the transaction"
title: qsTrId ( "error-sending-the-transaction" )
icon: StandardIcon . Critical
standardButtons: StandardButton . Ok
}
2020-10-02 17:30:27 +00:00
2020-10-05 16:24:43 +00:00
property MessageDialog signingError: MessageDialog {
id: signingError
title: qsTr ( "Error signing message" )
icon: StandardIcon . Critical
standardButtons: StandardButton . Ok
}
2020-09-22 19:16:44 +00:00
QtObject {
id: provider
WebChannel.id: "backend"
signal web3Response ( string data ) ;
2020-10-27 16:49:55 +00:00
function signValue ( input ) {
if ( Utils . isHex ( input ) && Utils . startsWith0x ( input ) ) {
return input
}
return utilsModel . ascii2Hex ( input )
}
2020-10-02 17:30:27 +00:00
function postMessage ( data ) {
var request ;
try {
request = JSON . parse ( data )
} catch ( e ) {
console . error ( "Error parsing the message data" , e )
return ;
}
2020-09-30 18:59:36 +00:00
var ensAddr = urlENSDictionary [ request . hostname ] ;
2020-10-02 17:30:27 +00:00
if ( ensAddr ) {
2020-09-30 18:59:36 +00:00
request . hostname = ensAddr ;
}
2020-10-02 17:30:27 +00:00
if ( request . type === Constants . api_request ) {
2020-10-07 19:49:26 +00:00
if ( ! _web3Provider . hasPermission ( request . hostname , request . permission ) ) {
2020-10-19 20:26:16 +00:00
browserWindow . currentTabConnected = false
2020-09-29 17:02:20 +00:00
var dialog = accessDialogComponent . createObject ( browserWindow ) ;
dialog . request = request ;
dialog . open ( ) ;
} else {
2020-10-19 20:26:16 +00:00
browserWindow . currentTabConnected = true
2020-09-29 17:02:20 +00:00
request . isAllowed = true ;
2020-10-07 19:49:26 +00:00
web3Response ( _web3Provider . postMessage ( JSON . stringify ( request ) ) ) ;
2020-09-29 17:02:20 +00:00
}
2020-10-02 17:30:27 +00:00
} else if ( request . type === Constants . web3SendAsyncReadOnly &&
request . payload . method === "eth_sendTransaction" ) {
2020-10-19 14:32:25 +00:00
walletModel . setFocusedAccountByAddress ( request . payload . params [ 0 ] . from )
var acc = walletModel . focusedAccount
2020-11-24 17:15:59 +00:00
const value = utilsModel . wei2Eth ( request . payload . params [ 0 ] . value , 18 ) ;
2020-10-19 14:32:25 +00:00
const sendDialog = sendTransactionModalComponent . createObject ( browserWindow , {
trxData: request . payload . params [ 0 ] . data ,
selectedAccount: {
name: acc . name ,
address: request . payload . params [ 0 ] . from ,
iconColor: acc . iconColor ,
assets: acc . assets
} ,
selectedRecipient: {
address: request . payload . params [ 0 ] . to ,
identicon: chatsModel . generateIdenticon ( request . payload . params [ 0 ] . to ) ,
name: chatsModel . activeChannel . name ,
type: RecipientSelector . Type . Address
} ,
selectedAsset: {
name: "ETH" ,
symbol: "ETH" ,
address: Constants . zeroAddress
} ,
selectedFiatAmount: "42" , // TODO calculate that
selectedAmount: value
} ) ;
2020-10-02 17:23:33 +00:00
// TODO change sendTransaction function to the postMessage one
sendDialog . sendTransaction = function ( selectedGasLimit , selectedGasPrice , enteredPassword ) {
request . payload . selectedGasLimit = selectedGasLimit
request . payload . selectedGasPrice = selectedGasPrice
request . payload . password = enteredPassword
request . payload . params [ 0 ] . value = value
2020-10-07 19:49:26 +00:00
const response = _web3Provider . postMessage ( JSON . stringify ( request ) )
2020-10-02 17:23:33 +00:00
provider . web3Response ( response )
let responseObj
try {
responseObj = JSON . parse ( response )
if ( responseObj . error ) {
throw new Error ( responseObj . error )
}
//% "Transaction pending..."
toastMessage . title = qsTrId ( "ens-transaction-pending" )
toastMessage . source = "../../img/loading.svg"
toastMessage . iconColor = Style . current . primary
toastMessage . iconRotates = true
2020-10-07 19:49:26 +00:00
toastMessage . link = ` $ { _walletModel . etherscanLink } / $ { responseObj . result } `
2020-10-02 17:23:33 +00:00
toastMessage . open ( )
} catch ( e ) {
if ( e . message . includes ( "could not decrypt key with given password" ) ) {
//% "Wrong password"
sendDialog . transactionSigner . validationError = qsTrId ( "wrong-password" )
return
}
sendingError . text = e . message
return sendingError . open ( )
}
sendDialog . close ( )
sendDialog . destroy ( )
}
2020-10-19 14:32:25 +00:00
sendDialog . estimateGas ( )
2020-10-02 17:30:27 +00:00
sendDialog . open ( ) ;
2020-10-05 16:24:43 +00:00
walletModel . getGasPricePredictions ( )
} else if ( request . type === Constants . web3SendAsyncReadOnly && [ "eth_sign" , "personal_sign" , "eth_signTypedData" , "eth_signTypedData_v3" ] . indexOf ( request . payload . method ) > - 1 ) {
const signDialog = signMessageModalComponent . createObject ( browserWindow , { request } ) ;
signDialog . web3Response = web3Response
signDialog . signMessage = function ( enteredPassword ) {
signDialog . interactedWith = true ;
request . payload . password = enteredPassword ;
2020-10-27 16:49:55 +00:00
switch ( request . payload . method ) {
case Constants.personal_sign:
request . payload . params [ 0 ] = signValue ( request . payload . params [ 0 ] ) ;
case Constants.eth_sign:
request . payload . params [ 1 ] = signValue ( request . payload . params [ 1 ] ) ;
}
2020-10-05 16:24:43 +00:00
const response = web3Provider . postMessage ( JSON . stringify ( request ) ) ;
provider . web3Response ( response ) ;
try {
let responseObj = JSON . parse ( response )
if ( responseObj . error ) {
throw new Error ( responseObj . error )
}
} catch ( e ) {
if ( e . message . includes ( "could not decrypt key with given password" ) ) {
//% "Wrong password"
signDialog . transactionSigner . validationError = qsTrId ( "wrong-password" )
return
}
signingError . text = e . message
return signingError . open ( )
}
signDialog . close ( )
signDialog . destroy ( )
}
signDialog . open ( ) ;
2020-09-25 19:05:07 +00:00
} else {
2020-10-07 19:49:26 +00:00
web3Response ( _web3Provider . postMessage ( data ) ) ;
2020-09-25 19:05:07 +00:00
}
2020-09-22 19:16:44 +00:00
}
2020-09-24 02:52:41 +00:00
2020-10-26 17:10:14 +00:00
property int networkId: ( _web3Provider && _web3Provider . networkId ) || - 1
2020-09-22 19:16:44 +00:00
}
WebChannel {
id: channel
registeredObjects: [ provider ]
}
2020-09-28 18:33:37 +00:00
property QtObject defaultProfile: WebEngineProfile {
storageName: "Profile"
offTheRecord: false
2020-10-20 20:24:41 +00:00
httpUserAgent: {
2020-10-21 16:50:21 +00:00
if ( appSettings . compatibilityMode ) {
2020-10-20 20:24:41 +00:00
// Google doesn't let you connect if the user agent is Chrome-ish and doesn't satisfy some sort of hidden requirement
return "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0"
}
return ""
}
2020-09-28 18:33:37 +00:00
useForGlobalCertificateVerification: true
userScripts: [
WebEngineScript {
injectionPoint: WebEngineScript . DocumentCreation
sourceUrl: Qt . resolvedUrl ( "provider.js" )
worldId: WebEngineScript . MainWorld // TODO: check https://doc.qt.io/qt-5/qml-qtwebengine-webenginescript.html#worldId-prop
}
]
}
property QtObject otrProfile: WebEngineProfile {
offTheRecord: true
persistentCookiesPolicy: WebEngineProfile . NoPersistentCookies
2020-10-07 19:28:45 +00:00
httpUserAgent: defaultProfile . httpUserAgent
2020-09-28 18:33:37 +00:00
userScripts: [
WebEngineScript {
injectionPoint: WebEngineScript . DocumentCreation
sourceUrl: Qt . resolvedUrl ( "provider.js" )
worldId: WebEngineScript . MainWorld // TODO: check https://doc.qt.io/qt-5/qml-qtwebengine-webenginescript.html#worldId-prop
}
]
}
2020-10-20 13:54:47 +00:00
function obtainAddress ( ) {
var ensAddr = urlENSDictionary [ web3Provider . getHost ( currentWebView . url ) ] ;
browserHeader . addressBar . text = ensAddr ? web3Provider . replaceHostByENS ( currentWebView . url , ensAddr ) : currentWebView . url ;
}
2020-09-28 18:33:37 +00:00
onCurrentWebViewChanged: {
findBar . reset ( ) ;
2020-10-20 13:54:47 +00:00
obtainAddress ( ) ;
2020-09-28 18:33:37 +00:00
}
Action {
shortcut: "Ctrl+D"
onTriggered: {
2020-11-20 12:45:41 +00:00
addNewDownloadTab ( )
2020-09-28 18:33:37 +00:00
}
}
2020-11-20 12:45:41 +00:00
function addNewDownloadTab ( ) {
tabs . createDownloadTab ( tabs . count !== 0 ? currentWebView.profile : defaultProfile ) ;
tabs . currentIndex = tabs . count - 1 ;
}
2020-09-28 18:33:37 +00:00
Action {
id: focus
shortcut: "Ctrl+L"
onTriggered: {
2020-10-08 18:08:50 +00:00
browserHeader . addressBar . forceActiveFocus ( ) ;
browserHeader . addressBar . selectAll ( ) ;
2020-09-28 18:33:37 +00:00
}
}
Action {
shortcut: StandardKey . Refresh
onTriggered: {
if ( currentWebView )
currentWebView . reload ( ) ;
}
}
2020-10-08 18:36:44 +00:00
function addNewTab ( ) {
2020-10-21 19:55:40 +00:00
tabs . createEmptyTab ( tabs . count !== 0 ? currentWebView.profile : defaultProfile ) ;
2020-10-08 18:36:44 +00:00
tabs . currentIndex = tabs . count - 1 ;
browserHeader . addressBar . forceActiveFocus ( ) ;
browserHeader . addressBar . selectAll ( ) ;
}
2020-10-09 17:56:42 +00:00
2020-09-28 18:33:37 +00:00
Action {
shortcut: StandardKey . Close
onTriggered: {
currentWebView . triggerWebAction ( WebEngineView . RequestClose ) ;
}
}
Action {
shortcut: "Escape"
onTriggered: {
if ( findBar . visible )
findBar . visible = false ;
}
}
2020-10-09 17:56:42 +00:00
2020-09-28 18:33:37 +00:00
Action {
shortcut: StandardKey . Copy
onTriggered: currentWebView . triggerWebAction ( WebEngineView . Copy )
}
Action {
shortcut: StandardKey . Cut
onTriggered: currentWebView . triggerWebAction ( WebEngineView . Cut )
}
Action {
shortcut: StandardKey . Paste
onTriggered: currentWebView . triggerWebAction ( WebEngineView . Paste )
}
Action {
shortcut: "Shift+" + StandardKey . Paste
onTriggered: currentWebView . triggerWebAction ( WebEngineView . PasteAndMatchStyle )
}
Action {
shortcut: StandardKey . SelectAll
onTriggered: currentWebView . triggerWebAction ( WebEngineView . SelectAll )
}
Action {
shortcut: StandardKey . Undo
onTriggered: currentWebView . triggerWebAction ( WebEngineView . Undo )
}
Action {
shortcut: StandardKey . Redo
onTriggered: currentWebView . triggerWebAction ( WebEngineView . Redo )
}
Action {
shortcut: StandardKey . Back
onTriggered: currentWebView . triggerWebAction ( WebEngineView . Back )
}
Action {
shortcut: StandardKey . Forward
onTriggered: currentWebView . triggerWebAction ( WebEngineView . Forward )
}
Action {
shortcut: StandardKey . FindNext
onTriggered: findBar . findNext ( )
}
Action {
shortcut: StandardKey . FindPrevious
onTriggered: findBar . findPrevious ( )
}
2020-10-07 20:14:55 +00:00
BrowserHeader {
id: browserHeader
2020-10-08 18:08:50 +00:00
anchors.top: parent . top
anchors.topMargin: tabs . tabHeight + tabs . anchors . topMargin
z: 52
2020-10-09 17:56:42 +00:00
addNewTab: browserWindow . addNewTab
2020-09-28 18:33:37 +00:00
}
QQC1 . TabView {
2020-10-08 18:08:50 +00:00
property int tabHeight: 40
2020-09-28 18:33:37 +00:00
id: tabs
2020-10-21 19:55:40 +00:00
function createEmptyTab ( profile , createAsStartPage ) {
2020-09-28 18:33:37 +00:00
var tab = addTab ( "" , tabComponent ) ;
// We must do this first to make sure that tab.active gets set so that tab.item gets instantiated immediately.
tab . active = true ;
2020-10-21 19:55:40 +00:00
createAsStartPage = createAsStartPage || tabs . count === 1
tab . title = Qt . binding ( function ( ) {
if ( tab . item . title ) {
return tab . item . title
}
if ( createAsStartPage ) {
return qsTr ( "Start Page" )
}
return qsTr ( "New Tab" )
} )
2020-09-28 18:33:37 +00:00
tab . item . profile = profile ;
return tab ;
}
2020-11-20 12:45:41 +00:00
function createDownloadTab ( profile ) {
var tab = addTab ( "" , tabComponent ) ;
tab . active = true ;
tab . title = qsTr ( "Downloads Page" )
tab . item . profile = profile
tab . item . url = "status://downloads" ;
}
2020-09-28 18:33:37 +00:00
function indexOfView ( view ) {
for ( let i = 0 ; i < tabs . count ; ++ i )
2020-10-08 18:08:50 +00:00
if ( tabs . getTab ( i ) . item === view )
2020-09-28 18:33:37 +00:00
return i
return - 1
}
function removeView ( index ) {
2020-10-21 19:55:40 +00:00
if ( tabs . count === 1 ) {
tabs . createEmptyTab ( currentWebView . profile , true )
}
tabs . removeTab ( index )
2020-09-28 18:33:37 +00:00
}
2020-10-08 18:08:50 +00:00
z: 50
anchors.top: parent . top
anchors.topMargin: Style . current . halfPadding
2020-09-28 18:33:37 +00:00
anchors.bottom: devToolsView . top
2020-10-08 20:13:14 +00:00
anchors.bottomMargin: browserHeader . height
2020-09-28 18:33:37 +00:00
anchors.left: parent . left
anchors.right: parent . right
Component.onCompleted: {
2020-10-19 20:16:02 +00:00
defaultProfile . downloadRequested . connect ( onDownloadRequested ) ;
otrProfile . downloadRequested . connect ( onDownloadRequested ) ;
2020-09-30 14:31:04 +00:00
var tab = createEmptyTab ( defaultProfile ) ;
2020-10-21 19:55:40 +00:00
// For Devs: Uncomment the next lien if you want to use the simpeldapp on first load
// tab.item.url = determineRealURL("https://simpledapp.eth");
2020-09-28 18:33:37 +00:00
}
2020-10-21 18:49:49 +00:00
style: BrowserTabStyle { }
2020-10-20 13:54:47 +00:00
2020-09-28 18:33:37 +00:00
Component {
id: tabComponent
WebEngineView {
id: webEngineView
2020-10-08 18:08:50 +00:00
anchors.top: parent . top
2020-10-08 18:36:44 +00:00
anchors.topMargin: browserHeader . height
2020-09-28 18:33:37 +00:00
focus: true
webChannel: channel
onLinkHovered: function ( hoveredUrl ) {
2020-10-07 20:14:55 +00:00
if ( hoveredUrl === "" )
2020-09-28 18:33:37 +00:00
hideStatusText . start ( ) ;
else {
statusText . text = hoveredUrl ;
statusBubble . visible = true ;
hideStatusText . stop ( ) ;
}
}
2020-10-08 20:08:20 +00:00
backgroundColor: Style . current . background
2020-09-28 18:33:37 +00:00
2020-10-09 17:56:42 +00:00
function changeZoomFactor ( newFactor ) {
// FIXME there seems to be a bug in the WebEngine where the zoomFactor only update 1/2 times
zoomFactor = newFactor
zoomFactor = newFactor
zoomFactor = newFactor
}
2020-10-21 14:49:13 +00:00
settings.autoLoadImages: appSettings . autoLoadImages
settings.javascriptEnabled: appSettings . javaScriptEnabled
settings.errorPageEnabled: appSettings . errorPageEnabled
settings.pluginsEnabled: appSettings . pluginsEnabled
settings.autoLoadIconsForPage: appSettings . autoLoadIconsForPage
settings.touchIconsEnabled: appSettings . touchIconsEnabled
settings.webRTCPublicInterfacesOnly: appSettings . webRTCPublicInterfacesOnly
settings.pdfViewerEnabled: appSettings . pdfViewerEnabled
2020-11-19 19:57:37 +00:00
settings.focusOnNavigationEnabled: true
2020-09-28 18:33:37 +00:00
onCertificateError: function ( error ) {
error . defer ( ) ;
sslDialog . enqueue ( error ) ;
}
onNewViewRequested: function ( request ) {
2020-10-08 20:08:20 +00:00
if ( ! request . userInitiated ) {
2020-09-28 18:33:37 +00:00
print ( "Warning: Blocked a popup window." ) ;
2020-10-08 20:08:20 +00:00
} else if ( request . destination === WebEngineView . NewViewInTab ) {
2020-09-28 18:33:37 +00:00
var tab = tabs . createEmptyTab ( currentWebView . profile ) ;
tabs . currentIndex = tabs . count - 1 ;
request . openIn ( tab . item ) ;
} else if ( request . destination === WebEngineView . NewViewInBackgroundTab ) {
var backgroundTab = tabs . createEmptyTab ( currentWebView . profile ) ;
request . openIn ( backgroundTab . item ) ;
} else if ( request . destination === WebEngineView . NewViewInDialog ) {
var dialog = browserDialogComponent . createObject ( ) ;
dialog . currentWebView . profile = currentWebView . profile ;
request . openIn ( dialog . currentWebView ) ;
} else {
// Instead of opening a new window, we open a new tab
// TODO: remove "open in new window" from context menu
var tab = tabs . createEmptyTab ( currentWebView . profile ) ;
tabs . currentIndex = tabs . count - 1 ;
request . openIn ( tab . item ) ;
}
}
onQuotaRequested: function ( request ) {
if ( request . requestedSize <= 5 * 1024 * 1024 )
request . accept ( ) ;
else
request . reject ( ) ;
}
onRegisterProtocolHandlerRequested: function ( request ) {
console . log ( "accepting registerProtocolHandler request for "
+ request . scheme + " from " + request . origin ) ;
request . accept ( ) ;
}
onRenderProcessTerminated: function ( terminationStatus , exitCode ) {
var status = "" ;
switch ( terminationStatus ) {
case WebEngineView.NormalTerminationStatus:
status = "(normal exit)" ;
break ;
case WebEngineView.AbnormalTerminationStatus:
status = "(abnormal exit)" ;
break ;
case WebEngineView.CrashedTerminationStatus:
status = "(crashed)" ;
break ;
case WebEngineView.KilledTerminationStatus:
status = "(killed)" ;
break ;
}
print ( "Render process exited with code " + exitCode + " " + status ) ;
reloadTimer . running = true ;
}
onWindowCloseRequested: tabs . removeView ( tabs . indexOfView ( webEngineView ) )
onSelectClientCertificate: function ( selection ) {
selection . certificates [ 0 ] . select ( ) ;
}
onFindTextFinished: function ( result ) {
if ( ! findBar . visible )
findBar . visible = true ;
findBar . numberOfMatches = result . numberOfMatches ;
findBar . activeMatch = result . activeMatch ;
}
onLoadingChanged: function ( loadRequest ) {
2020-10-08 18:08:50 +00:00
if ( loadRequest . status === WebEngineView . LoadStartedStatus )
2020-09-28 18:33:37 +00:00
findBar . reset ( ) ;
}
2020-11-20 12:45:41 +00:00
Loader {
active: webEngineView . url . toString ( ) === "status://downloads"
width: parent . width
height: parent . height
z: 54
sourceComponent: DownloadView {
id: downloadView
}
}
2020-10-08 20:08:20 +00:00
Loader {
active: ! webEngineView . url . toString ( )
width: parent . width
height: parent . height
z: 54
sourceComponent: Item {
width: parent . width
height: parent . height
Image {
id: emptyPageImage
source: "../../img/browser/compass.png"
width: 294
height: 294
anchors.horizontalCenter: parent . horizontalCenter
anchors.top: parent . top
anchors.topMargin: 60
}
Row {
anchors.horizontalCenter: emptyPageImage . horizontalCenter
anchors.top: emptyPageImage . bottom
anchors.topMargin: 30
Item {
width: bookmarkItem . width
height: bookmarkItem . height
Item {
id: bookmarkItem
width: childrenRect . width
height: childrenRect . height
SVGImage {
id: bookmarkImage
source: "../../img/globe.svg"
width: 48
height: 48
}
StyledText {
text: "site.com"
anchors.horizontalCenter: bookmarkImage . horizontalCenter
anchors.top: bookmarkImage . bottom
anchors.topMargin: Style . current . halfPadding
}
}
MouseArea {
anchors.fill: bookmarkItem
cursorShape: Qt . PointingHandCursor
onClicked: console . log ( 'Go to bookmark' )
}
}
}
}
}
2020-09-28 18:33:37 +00:00
Timer {
id: reloadTimer
interval: 0
running: false
repeat: false
onTriggered: currentWebView . reload ( )
}
}
}
}
2020-10-20 13:54:47 +00:00
Connections {
target: currentWebView
onUrlChanged: obtainAddress ( )
}
2020-09-28 18:33:37 +00:00
ProgressBar {
id: progressBar
height: 3
from: 0
to: 100
visible: value != 0 && value != 100
value: ( currentWebView && currentWebView . loadProgress < 100 ) ? currentWebView.loadProgress : 0
anchors.bottom: parent . bottom
anchors.bottomMargin: Style . current . padding
anchors.right: parent . right
anchors.rightMargin: Style . current . padding
}
2020-09-22 19:16:44 +00:00
WebEngineView {
2020-09-28 18:33:37 +00:00
id: devToolsView
2020-10-21 14:49:13 +00:00
visible: appSettings . devToolsEnabled
2020-09-28 18:33:37 +00:00
height: visible ? 400 : 0
inspectedView: visible && tabs . currentIndex < tabs . count ? tabs . getTab ( tabs . currentIndex ) . item : null
anchors.left: parent . left
anchors.right: parent . right
anchors.bottom: parent . bottom
2020-09-22 19:16:44 +00:00
onNewViewRequested: function ( request ) {
2020-09-28 18:33:37 +00:00
var tab = tabs . createEmptyTab ( currentWebView . profile ) ;
tabs . currentIndex = tabs . count - 1 ;
request . openIn ( tab . item ) ;
}
2020-11-19 19:57:37 +00:00
z: 100
2020-09-28 18:33:37 +00:00
}
2020-10-19 20:16:02 +00:00
function onDownloadRequested ( download ) {
2020-10-22 19:51:49 +00:00
downloadBar . isVisible = true
2020-10-19 20:16:02 +00:00
download . accept ( ) ;
2020-11-20 12:45:41 +00:00
downloadModel . append ( download ) ;
downloadModel . downloads . push ( download ) ;
2020-10-19 20:16:02 +00:00
}
2020-09-28 18:33:37 +00:00
MessageDialog {
id: sslDialog
property var certErrors: [ ]
icon: StandardIcon . Warning
standardButtons: StandardButton . No | StandardButton . Yes
title: qsTr ( "Server's certificate not trusted" )
text: qsTr ( "Do you wish to continue?" )
detailedText: qsTr ( "If you wish so, you may continue with an unverified certificate. Accepting an unverified certificate means you may not be connected with the host you tried to connect to.\nDo you wish to override the security check and continue?" )
onYes: {
certErrors . shift ( ) . ignoreCertificateError ( ) ;
presentError ( ) ;
}
onNo: reject ( )
onRejected: reject ( )
function reject ( ) {
certErrors . shift ( ) . rejectCertificate ( ) ;
presentError ( ) ;
}
function enqueue ( error ) {
certErrors . push ( error ) ;
presentError ( ) ;
}
function presentError ( ) {
visible = certErrors . length > 0
}
}
2020-11-20 12:45:41 +00:00
2020-09-28 18:33:37 +00:00
2020-10-22 17:34:14 +00:00
DownloadBar {
id: downloadBar
anchors.bottom: parent . bottom
z: 60
}
2020-09-28 18:33:37 +00:00
FindBar {
id: findBar
visible: false
anchors.right: parent . right
2020-10-09 17:56:42 +00:00
anchors.top: browserHeader . bottom
z: 60
2020-09-28 18:33:37 +00:00
onFindNext: {
if ( text )
currentWebView && currentWebView . findText ( text ) ;
else if ( ! visible )
visible = true ;
}
onFindPrevious: {
if ( text )
currentWebView && currentWebView . findText ( text , WebEngineView . FindBackward ) ;
else if ( ! visible )
visible = true ;
}
}
Rectangle {
id: statusBubble
color: "oldlace"
property int padding: 8
visible: false
anchors.left: parent . left
anchors.bottom: parent . bottom
width: statusText . paintedWidth + padding
height: statusText . paintedHeight + padding
Text {
id: statusText
anchors.centerIn: statusBubble
elide: Qt . ElideMiddle
Timer {
id: hideStatusText
interval: 750
onTriggered: {
statusText . text = "" ;
statusBubble . visible = false ;
}
}
2020-09-22 19:16:44 +00:00
}
2020-09-22 15:12:48 +00:00
}
}
/ * # # ^ # #
Designer {
D { i: 0 ; autoSize: true ; height: 480 ; width: 640 }
}
# # ^ # # * /