diff options
Diffstat (limited to 'tests')
36 files changed, 1732 insertions, 239 deletions
diff --git a/tests/auto/quick/qmltests/BLACKLIST b/tests/auto/quick/qmltests/BLACKLIST index 16abe5ff6..d281020df 100644 --- a/tests/auto/quick/qmltests/BLACKLIST +++ b/tests/auto/quick/qmltests/BLACKLIST @@ -1,23 +1,11 @@ [DesktopWebEngineViewLinkHovered::test_linkHovered] * +[DesktopWebEngineViewLinkHovered::test_linkHoveredDoesntEmitRepeated] +* + [WebViewGeopermission::test_deniedGeolocationByUser] osx [WebViewGeopermission::test_geoPermissionRequest] osx - -[WebEngineViewFocusOnNavigation::test_focusOnNavigation] -* - -[WebEngineViewLoadUrl::test_urlProperty] -windows - -[WebEngineViewSettings::test_javascriptDisabled] -windows - -[WebEngineViewUnhandledKeyEventPropagation::test_keyboardModifierMapping] -windows - -[WebEngineViewSource::test_viewSourceURL] -windows diff --git a/tests/auto/quick/qmltests/data/TestWebEngineView.qml b/tests/auto/quick/qmltests/data/TestWebEngineView.qml index 0d2a34645..aa1f77942 100644 --- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml +++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml @@ -27,7 +27,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtTest 1.0 +import QtTest 1.1 import QtWebEngine 1.3 WebEngineView { @@ -62,7 +62,27 @@ WebEngineView { return predicate() } + function getActiveElementId() { + var activeElementId; + runJavaScript("document.activeElement.id", function(result) { + activeElementId = result; + }); + testCase.tryVerify(function() { return activeElementId != undefined }); + return activeElementId; + } + + function verifyElementHasFocus(element) { + testCase.tryVerify(function() { return getActiveElementId() == element; }, 5000, + "Element \"" + element + "\" has focus"); + } + + function setFocusToElement(element) { + runJavaScript("document.getElementById('" + element + "').focus()"); + verifyElementHasFocus(element); + } + TestResult { id: testResult } + TestCase { id: testCase } onLoadingChanged: { loadStatus = loadRequest.status diff --git a/tests/auto/quick/qmltests/data/favicon-candidates-gray.html b/tests/auto/quick/qmltests/data/favicon-candidates-gray.html index 3cbc4a4c3..ebea35b02 100644 --- a/tests/auto/quick/qmltests/data/favicon-candidates-gray.html +++ b/tests/auto/quick/qmltests/data/favicon-candidates-gray.html @@ -11,11 +11,11 @@ <h1>Gray Candidate Favicons Test</h1> <table style="width:100%"> <tr> - <td align="center"><img src="icons/gray16.png" height="16" width="16" /></td> - <td align="center"><img src="icons/gray32.png" height="32" width="32" /></td> - <td align="center"><img src="icons/gray64.png" height="64" width="64" /></td> - <td align="center"><img src="icons/gray128.png" height="128" width="128" /></td> - <td align="center"><img src="icons/gray255.png" height="255" width="255" /></td> + <td align="center"><img src="icons/gray16.png" height="16" width="16" border="1" /></td> + <td align="center"><img src="icons/gray32.png" height="32" width="32" border="1" /></td> + <td align="center"><img src="icons/gray64.png" height="64" width="64" border="1" /></td> + <td align="center"><img src="icons/gray128.png" height="128" width="128" border="1" /></td> + <td align="center"><img src="icons/gray255.png" height="255" width="255" border="1" /></td> </tr> <tr> <td align="center">16x16</td> diff --git a/tests/auto/quick/qmltests/data/favicon-multi-gray.html b/tests/auto/quick/qmltests/data/favicon-multi-gray.html index 9b9b7432d..24b71640f 100644 --- a/tests/auto/quick/qmltests/data/favicon-multi-gray.html +++ b/tests/auto/quick/qmltests/data/favicon-multi-gray.html @@ -7,11 +7,11 @@ <h1>Gray Multi-sized Favicon Test</h1> <table style="width:100%"> <tr> - <td align="center"><img src="icons/gray16.png" height="16" width="16" /></td> - <td align="center"><img src="icons/gray32.png" height="32" width="32" /></td> - <td align="center"><img src="icons/gray64.png" height="64" width="64" /></td> - <td align="center"><img src="icons/gray128.png" height="128" width="128" /></td> - <td align="center"><img src="icons/gray255.png" height="255" width="255" /></td> + <td align="center"><img src="icons/gray16.png" height="16" width="16" border="1" /></td> + <td align="center"><img src="icons/gray32.png" height="32" width="32" border="1" /></td> + <td align="center"><img src="icons/gray64.png" height="64" width="64" border="1" /></td> + <td align="center"><img src="icons/gray128.png" height="128" width="128" border="1" /></td> + <td align="center"><img src="icons/gray255.png" height="255" width="255" border="1" /></td> </tr> <tr> <td align="center">16x16</td> diff --git a/tests/auto/quick/qmltests/data/keyboardEvents.html b/tests/auto/quick/qmltests/data/keyboardEvents.html new file mode 100644 index 000000000..d536d849f --- /dev/null +++ b/tests/auto/quick/qmltests/data/keyboardEvents.html @@ -0,0 +1,98 @@ +<html> +<head> + <style> + div { + width: 300px; + margin-bottom: 10px; + } + + #div_container { + display: inline-block; + } + + #div_container div { + width: 100px; + height: 100px; + border: 2px solid black; + padding: 10px; + margin: 0 10px 0 10px; + float: left; + } + + #div_container div:focus { + border: 2px solid red; + } + + #form_container span { + display: block; + padding-bottom: 10px; + } + + #form_container label { + float: left; + text-align: right; + width: 50px; + margin-right: 20px; + } + </style> +</head> + +<body onload="document.getElementById('first_div').focus()"> + <div id="div_container"> + <div id="first_div" tabindex="0">First</div> + <div id="second_div" tabindex="0">Second</div> + </div> + + <div id="form_container"> + <form><fieldset> + <legend>Form</legend> + + <span> + <label for="text_input">text</label> + <input id="text_input" type="text" /> + </span> + + <span> + <input id="radio1" type="radio" name="radio">radio1</input> + <input id="radio2" type="radio" name="radio">radio2</input> + </span> + + <span> + <input id="checkbox1" type="checkbox" name="checkbox1" checked="true">checkbox1</input> + <input id="checkbox2" type="checkbox" name="checkbox2" checked="true">checkbox2</input> + </span> + + <span> + <label for="number_input">number</label> + <input id="number_input" type="number" min="0" max="10" value="5" /> + </span> + + <span> + <label for="range_input">range</label> + <input id="range_input" type="range" min="0" max="10" value="5" /> + </span> + + <span> + <label for="search_input">search</label> + <input id="search_input" type="search" value="test" /> + </span> + + <input id="submit_button" type="submit" value="Submit" /> + </fieldset></form> + </div> + + <div> + <select id="combobox"> + <option value="a">a</option> + <option value="b">b</option> + <option value="c">c</option> + </select> + </div> + + <div> + <a id="first_hyperlink" href="">First</a> + <br /> + <a id="second_hyperlink" href="">Second</a> + </div> +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/redirect.html b/tests/auto/quick/qmltests/data/redirect.html index 914e5e35a..44eb6cd28 100644 --- a/tests/auto/quick/qmltests/data/redirect.html +++ b/tests/auto/quick/qmltests/data/redirect.html @@ -1,8 +1,9 @@ <!doctype html> <html> <head> -<meta http-equiv="refresh" content="2; url=test1.html" + <meta http-equiv="refresh" content="2; url=test1.html"> </head> <body> + Redirecting to test1.html </body> </html> diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index 904a8feb2..4761513c1 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -107,5 +107,24 @@ TestWebEngineView { tryCompare(webEngineView, "matchCount", 0) verify(findFailed) } + + function test_findTextAfterNotFound() { + var findFlags = 0 + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.clear() + webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) + tryCompare(webEngineView, "matchCount", 0) + verify(findFailed) + + webEngineView.url = Qt.resolvedUrl("test1.html") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.clear() + webEngineView.findText("hello", findFlags, webEngineView.findTextCallback) + tryCompare(webEngineView, "matchCount", 1) + verify(!findFailed) + } } } diff --git a/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml b/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml index ce0fa2e31..93410a727 100644 --- a/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml +++ b/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml @@ -67,8 +67,6 @@ Item { TestCase { name: "WebEngineViewFocusOnNavigation" when: windowShown - function init() { - } function test_focusOnNavigation_data() { return [ @@ -79,18 +77,9 @@ Item { ] } - function triggerJavascriptFocus() { - var callbackCalled = false; - webView.runJavaScript("document.getElementById(\"input\").focus()", function(result) { - callbackCalled = true; - }); - wait(100); - verify(callbackCalled); - } - function loadAndTriggerFocusAndCompare(data) { verify(webView.waitForLoadSucceeded()); - triggerJavascriptFocus(); + webView.setFocusToElement("input"); compare(webView.activeFocus, data.viewReceivedFocus); } diff --git a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml new file mode 100644 index 000000000..677727632 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.4 + +TestWebEngineView { + id: webEngineView + width: 350 + height: 480 + + TestCase { + name: "WebEngineViewKeyboardEvents" + when: windowShown + + function isElementChecked(element) { + var elementChecked; + runJavaScript("document.getElementById('" + element + "').checked", function(result) { + elementChecked = result; + }); + tryVerify(function() { return elementChecked != undefined; }); + return elementChecked; + } + + function verifyElementChecked(element, expected) { + tryVerify(function() { return expected == isElementChecked(element); }, 5000, + "Element \"" + element + "\" is " + (expected ? "" : "not") + " checked"); + } + + function getElementValue(element) { + var elementValue; + runJavaScript("document.getElementById('" + element + "').value", function(result) { + elementValue = result; + }); + tryVerify(function() { return elementValue != undefined; }); + return elementValue; + } + + function compareElementValue(element, expected) { + tryVerify(function() { return expected == getElementValue(element); }, 5000, + "Value of element \"" + element + "\" is \"" + expected + "\""); + } + + function test_keyboardEvents() { + webEngineView.url = Qt.resolvedUrl("keyboardEvents.html"); + verify(webEngineView.waitForLoadSucceeded()); + + var elements = [ + "first_div", "second_div", + "text_input", "radio1", "checkbox1", "checkbox2", + "number_input", "range_input", "search_input", + "submit_button", "combobox", "first_hyperlink", "second_hyperlink" + ]; + + // Iterate over the elements of the test page with the Tab key. This tests whether any + // element blocks the in-page navigation by Tab. + for (var i = 0; i < elements.length; ++i) { + verifyElementHasFocus(elements[i]) + keyPress(Qt.Key_Tab); + } + + // Move back to the radio buttons with the Shift+Tab key combination + for (var i = 0; i < 10; ++i) + keyPress(Qt.Key_Tab, Qt.ShiftModifier); + verifyElementHasFocus("radio2"); + + // Test the Space key by checking a radio button + verifyElementChecked("radio2", false); + keyClick(Qt.Key_Space); + verifyElementChecked("radio2", true); + + // Test the Left key by switching the radio button + verifyElementChecked("radio1", false); + keyPress(Qt.Key_Left); + verifyElementHasFocus("radio1"); + verifyElementChecked("radio1", true); + + // Test the Space key by unchecking a checkbox + setFocusToElement("checkbox1"); + verifyElementChecked("checkbox1", true); + keyClick(Qt.Key_Space); + verifyElementChecked("checkbox1", false); + + // Test the Up and Down keys by changing the value of a spinbox + setFocusToElement("number_input"); + compareElementValue("number_input", 5); + keyPress(Qt.Key_Up); + compareElementValue("number_input", 6); + keyPress(Qt.Key_Down); + compareElementValue("number_input", 5); + + // Test the Left, Right, Home, PageUp, End and PageDown keys by changing the value of a slider + setFocusToElement("range_input"); + compareElementValue("range_input", 5); + keyPress(Qt.Key_Left); + compareElementValue("range_input", 4); + keyPress(Qt.Key_Right); + compareElementValue("range_input", 5); + keyPress(Qt.Key_Home); + compareElementValue("range_input", 0); + keyPress(Qt.Key_PageUp); + compareElementValue("range_input", 1); + keyPress(Qt.Key_End); + compareElementValue("range_input", 10); + keyPress(Qt.Key_PageDown); + compareElementValue("range_input", 9); + + // Test the Escape key by removing the content of a search field + setFocusToElement("search_input"); + compareElementValue("search_input", "test"); + keyPress(Qt.Key_Escape); + compareElementValue("search_input", ""); + + // Test the alpha keys by changing the values in a combobox + setFocusToElement("combobox"); + compareElementValue("combobox", "a"); + keyPress(Qt.Key_B); + compareElementValue("combobox", "b"); + // Must wait with the second key press to simulate selection of another element + wait(1000); + keyPress(Qt.Key_C); + compareElementValue("combobox", "c"); + + // Test the Enter key by loading a page with a hyperlink + setFocusToElement("first_hyperlink"); + keyPress(Qt.Key_Enter); + verify(webEngineView.waitForLoadSucceeded()); + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml index ae60e5f5e..e0a8c0a41 100644 --- a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml +++ b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml @@ -52,24 +52,26 @@ TestWebEngineView { onTriggered: parent.when = true } + function getPressedModifiers() { + var pressedModifiers; + runJavaScript("getPressedModifiers()", function(result) { + pressedModifiers = result; + }); + tryVerify(function() { return pressedModifiers != undefined }); + return pressedModifiers; + } + function test_keyboardModifierMapping() { webEngineView.url = Qt.resolvedUrl("keyboardModifierMapping.html") waitForLoadSucceeded(); titleSpy.wait() - var callbackCalled = false; // Alt keyPress(Qt.Key_Alt); titleSpy.wait() - runJavaScript("getPressedModifiers()", function(result) { - compare(result, "alt:pressed ctrl:no meta:no"); - callbackCalled = true; - }); - wait(100); - verify(callbackCalled); + compare(getPressedModifiers(), "alt:pressed ctrl:no meta:no"); keyRelease(Qt.Key_Alt) titleSpy.wait() - callbackCalled = false; // Ctrl // On mac Qt automatically translates Meta to Ctrl and vice versa. @@ -78,36 +80,18 @@ TestWebEngineView { // For testing we assume that the flag Qt::AA_MacDontSwapCtrlAndMeta is NOT set. keyPress(Qt.platform.os == "osx" ? Qt.Key_Meta : Qt.Key_Control); titleSpy.wait() - runJavaScript("getPressedModifiers()", function(result) { - compare(result, "alt:released ctrl:pressed meta:no"); - callbackCalled = true; - }); - wait(100); - verify(callbackCalled); + compare(getPressedModifiers(), "alt:released ctrl:pressed meta:no"); keyRelease(Qt.platform.os == "osx" ? Qt.Key_Meta : Qt.Key_Control); titleSpy.wait() - callbackCalled = false; // Meta (Command on Mac) keyPress(Qt.platform.os == "osx" ? Qt.Key_Control : Qt.Key_Meta); titleSpy.wait() - runJavaScript("getPressedModifiers()", function(result) { - compare(result, "alt:released ctrl:released meta:pressed"); - callbackCalled = true; - }); - wait(100); - verify(callbackCalled); + compare(getPressedModifiers(), "alt:released ctrl:released meta:pressed"); keyRelease(Qt.platform.os == "osx" ? Qt.Key_Control : Qt.Key_Meta); titleSpy.wait() - callbackCalled = false; - runJavaScript("getPressedModifiers()", function(result) { - compare(result, "alt:released ctrl:released meta:released"); - callbackCalled = true; - }); - wait(100); - verify(callbackCalled); - callbackCalled = false; + compare(getPressedModifiers(), "alt:released ctrl:released meta:released"); } } } diff --git a/tests/auto/quick/qmltests/data/tst_loadUrl.qml b/tests/auto/quick/qmltests/data/tst_loadUrl.qml index 3ce03df70..2a43e1577 100644 --- a/tests/auto/quick/qmltests/data/tst_loadUrl.qml +++ b/tests/auto/quick/qmltests/data/tst_loadUrl.qml @@ -35,152 +35,249 @@ TestWebEngineView { width: 400 height: 300 - property var lastUrl - property bool watchProgress: false - property int numLoadStarted: 0 - property int numLoadSucceeded: 0 + property var loadRequestArray: [] - focus: true - - onLoadProgressChanged: { - if (watchProgress && webEngineView.loadProgress != 100) { - watchProgress = false - url = '' - } + onLoadingChanged: { + loadRequestArray.push({ + "status": loadRequest.status, + "url": loadRequest.url, + "activeUrl": webEngineView.url + }); } - onLoadingChanged: { - if (loadRequest.status == WebEngineView.LoadStartedStatus) - ++numLoadStarted - if (loadRequest.status == WebEngineView.LoadSucceededStatus) - ++numLoadSucceeded + function clear() { + // Reset loadStatus for waitForLoadSucceded + webEngineView.loadStatus = null; + loadRequestArray = []; } TestCase { name: "WebEngineViewLoadUrl" when: windowShown + function init() { + webEngineView.clear(); + } + function test_loadIgnoreEmptyUrl() { - var url = Qt.resolvedUrl("test1.html") - - webEngineView.url = url - verify(webEngineView.waitForLoadSucceeded()) - compare(numLoadStarted, 1) - compare(numLoadSucceeded, 1) - compare(webEngineView.url, url) - - lastUrl = webEngineView.url - webEngineView.url = '' - wait(1000) - compare(numLoadStarted, 1) - compare(numLoadSucceeded, 1) - compare(webEngineView.url, lastUrl) - - webEngineView.url = 'about:blank' - verify(webEngineView.waitForLoadSucceeded()) - compare(numLoadStarted, 2) - compare(numLoadSucceeded, 2) - compare(webEngineView.url, 'about:blank') + var url = Qt.resolvedUrl("test1.html"); + webEngineView.url = url; + verify(webEngineView.waitForLoadSucceeded()); + compare(loadRequestArray[0].status, WebEngineView.LoadStartedStatus); + compare(loadRequestArray[1].status, WebEngineView.LoadSucceededStatus); + compare(loadRequestArray.length, 2); + compare(webEngineView.url, url); + webEngineView.clear(); + + var lastUrl = webEngineView.url; + webEngineView.url = ""; + wait(1000); + compare(loadRequestArray.length, 0); + compare(webEngineView.url, lastUrl); + webEngineView.clear(); + + var aboutBlank = "about:blank"; + webEngineView.url = aboutBlank; + verify(webEngineView.waitForLoadSucceeded()); + compare(loadRequestArray[0].status, WebEngineView.LoadStartedStatus); + compare(loadRequestArray[1].status, WebEngineView.LoadSucceededStatus); + compare(loadRequestArray.length, 2); + compare(webEngineView.url, aboutBlank); + webEngineView.clear(); // It shouldn't interrupt any ongoing load when an empty url is used. - watchProgress = true - webEngineView.url = url - webEngineView.waitForLoadSucceeded() - compare(numLoadStarted, 3) - compare(numLoadSucceeded, 3) - verify(!watchProgress) - compare(webEngineView.url, url) + var watchProgress = true; + var handleLoadProgress = function() { + if (webEngineView.loadProgress != 100) { + webEngineView.url = ""; + watchProgress = false; + } + } + webEngineView.loadProgressChanged.connect(handleLoadProgress); + webEngineView.url = url; + verify(webEngineView.waitForLoadSucceeded()); + compare(loadRequestArray[0].status, WebEngineView.LoadStartedStatus); + compare(loadRequestArray[1].status, WebEngineView.LoadSucceededStatus); + compare(loadRequestArray.length, 2); + verify(!watchProgress); + compare(webEngineView.url, url); + webEngineView.loadProgressChanged.disconnect(handleLoadProgress); + webEngineView.clear(); } function test_urlProperty() { - WebEngine.settings.errorPageEnabled = false + WebEngine.settings.errorPageEnabled = false; - var url = Qt.resolvedUrl("test1.html") + var loadRequest = null; - webEngineView.url = url - compare(webEngineView.url, url) - verify(webEngineView.waitForLoadSucceeded()) - compare(webEngineView.url, url) + // Test succeeded load + var url = Qt.resolvedUrl("test1.html"); + webEngineView.url = url; + tryCompare(loadRequestArray, "length", 2); - var bogusSite = "http://www.somesitethatdoesnotexist.abc/" - webEngineView.url = bogusSite - compare(webEngineView.url, bogusSite) - verify(webEngineView.waitForLoadFailed()) - compare(webEngineView.url, url) + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, url); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadSucceededStatus); + compare(loadRequest.activeUrl, url); + webEngineView.clear(); + + // Test failed load + var bogusSite = "http://www.somesitethatdoesnotexist.abc/"; + webEngineView.url = bogusSite; + tryCompare(loadRequestArray, "length", 2); + + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, bogusSite); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadFailedStatus); + compare(loadRequest.activeUrl, url); + webEngineView.clear(); + + // Test page redirection + var redirectUrl = Qt.resolvedUrl("redirect.html"); + webEngineView.url = redirectUrl; + tryCompare(loadRequestArray, "length", 4); - webEngineView.url = "about:blank" // Reset from previous test - verify(webEngineView.waitForLoadSucceeded()) + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, redirectUrl); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadSucceededStatus); + compare(loadRequest.activeUrl, redirectUrl); + loadRequest = loadRequestArray[2]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, redirectUrl); + loadRequest = loadRequestArray[3]; + compare(loadRequest.status, WebEngineView.LoadSucceededStatus); + compare(loadRequest.activeUrl, url); + webEngineView.clear(); + // Test clicking on a hyperlink + var linkUrl = Qt.resolvedUrl("link.html"); + webEngineView.url = linkUrl; + tryCompare(loadRequestArray, "length", 2); + + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, linkUrl); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadSucceededStatus); + compare(loadRequest.activeUrl, linkUrl); + webEngineView.clear(); + + var lastUrl = webEngineView.url; + mouseClick(webEngineView, 10, 10, Qt.LeftButton, Qt.NoModifiers, 50); + tryCompare(loadRequestArray, "length", 2); + + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.url, url); + compare(loadRequest.activeUrl, lastUrl); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadSucceededStatus); + compare(loadRequest.url, url); + compare(loadRequest.activeUrl, url); + webEngineView.clear(); + } + + function test_loadDataUrl() { + WebEngine.settings.errorPageEnabled = false; + + var loadRequest = null; + + // Test load of a data URL + var dataUrl = "data:text/html,foo"; + webEngineView.url = dataUrl; + tryCompare(loadRequestArray, "length", 2); + + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, dataUrl); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadSucceededStatus); + compare(loadRequest.activeUrl, dataUrl); + webEngineView.clear(); + + // Test loadHtml after a failed load + var aboutBlank = "about:blank"; + webEngineView.url = aboutBlank; // Reset from previous test + verify(webEngineView.waitForLoadSucceeded()); + webEngineView.clear(); + + var bogusSite = "http://www.somesitethatdoesnotexist.abc/"; var handleLoadFailed = function(loadRequest) { if (loadRequest.status == WebEngineView.LoadFailedStatus) { - webEngineView.loadHtml("load failed", bogusSite) - // Since the load did not succeed the active url is the - // url of the previous successful load. - compare(webEngineView.url, "about:blank") - compare(loadRequest.url, bogusSite) + // loadHtml constructs data URL + webEngineView.loadHtml("load failed", bogusSite); + compare(loadRequest.url, bogusSite); } } - webEngineView.loadingChanged.connect(handleLoadFailed) + webEngineView.loadingChanged.connect(handleLoadFailed); webEngineView.url = bogusSite - compare(webEngineView.url, bogusSite) - verify(webEngineView.waitForLoadSucceeded()) - compare(webEngineView.url, bogusSite) - webEngineView.loadingChanged.disconnect(handleLoadFailed) - - var dataUrl = "data:text/html,foo" - webEngineView.url = dataUrl - compare(webEngineView.url, dataUrl) - verify(webEngineView.waitForLoadSucceeded()) // data:text/html,foo is loaded - compare(webEngineView.url, dataUrl) - - var redirectUrl = Qt.resolvedUrl("redirect.html") - webEngineView.url = redirectUrl - compare(webEngineView.url, redirectUrl) - verify(webEngineView.waitForLoadSucceeded()) // redirect.html is loaded - compare(webEngineView.url, redirectUrl) - verify(webEngineView.waitForLoadSucceeded()) // test1.html is loaded - compare(webEngineView.url, url) - - var linkUrl = Qt.resolvedUrl("link.html") - webEngineView.url = linkUrl - compare(webEngineView.url, linkUrl) - verify(webEngineView.waitForLoadSucceeded()) - compare(webEngineView.url, linkUrl) - - var handleLoadRequest = function(loadRequest) { - if (loadRequest.status == WebEngineView.LoadStartedStatus) { - compare(webEngineView.url, lastUrl) - compare(loadRequest.url, url) - } - if (loadRequest.status == WebEngineView.LoadSuceededStatus) { - compare(webEngineView.url, loadRequest.url) - compare(webEngineView.url, url) - } - } - lastUrl = webEngineView.url - webEngineView.loadingChanged.connect(handleLoadRequest) - mouseClick(webEngineView, 10, 10, Qt.LeftButton, Qt.NoModifiers, 50) - verify(webEngineView.waitForLoadSucceeded()) - compare(webEngineView.url, url) - webEngineView.loadingChanged.disconnect(handleLoadRequest) + tryCompare(loadRequestArray, "length", 4); + webEngineView.loadingChanged.disconnect(handleLoadFailed); + + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, bogusSite); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadFailedStatus); + // Since the load did not succeed the active url is the + // URL of the previous successful load. + compare(loadRequest.activeUrl, aboutBlank); + loadRequest = loadRequestArray[2]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.activeUrl, aboutBlank); + loadRequest = loadRequestArray[3]; + compare(loadRequest.status, WebEngineView.LoadSucceededStatus); + compare(loadRequest.activeUrl, bogusSite); + webEngineView.clear(); + } + + function test_QTBUG_56661() { + var url = Qt.resolvedUrl("test1.html"); + + // Warm up phase + webEngineView.url = url; + verify(webEngineView.waitForLoadSucceeded()); + + // Load data URL + var dataUrl = "data:text/html,foo"; + webEngineView.url = dataUrl; + verify(webEngineView.waitForLoadSucceeded()); + + // WebEngine should not try to execute user scripts in the + // render frame of the warm up phase otherwise the renderer + // crashes. + webEngineView.url = url; + verify(webEngineView.waitForLoadSucceeded()); } function test_stopStatus() { - var url = Qt.resolvedUrl("test1.html") + var loadRequest = null; - var handleLoadRequest = function(loadRequest) { - if (loadRequest.status == WebEngineView.LoadStoppedStatus) { - compare(webEngineView.url, url) - compare(loadRequest.url, url) - } + var handleLoadStarted = function(loadRequest) { + if (loadRequest.status == WebEngineView.LoadStartedStatus) + webEngineView.stop(); } - webEngineView.loadingChanged.connect(handleLoadRequest) - webEngineView.url = url - compare(webEngineView.url, url) - webEngineView.stop() - verify(webEngineView.waitForLoadStopped()) - compare(webEngineView.url, url) - webEngineView.loadingChanged.disconnect(handleLoadRequest) + webEngineView.loadingChanged.connect(handleLoadStarted); + var url = Qt.resolvedUrl("test1.html"); + webEngineView.url = url; + tryCompare(loadRequestArray, "length", 2); + webEngineView.loadingChanged.disconnect(handleLoadStarted); + + loadRequest = loadRequestArray[0]; + compare(loadRequest.status, WebEngineView.LoadStartedStatus); + compare(loadRequest.url, url); + compare(loadRequest.activeUrl, url); + loadRequest = loadRequestArray[1]; + compare(loadRequest.status, WebEngineView.LoadStoppedStatus); + compare(loadRequest.url, url); + compare(loadRequest.activeUrl, url); + webEngineView.clear(); } } } diff --git a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml index 5013f2fbe..b1c94e601 100644 --- a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml +++ b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml @@ -83,9 +83,6 @@ TestWebEngineView { when: windowShown function init() { - // Workaround for QTBUG-56223 - mouseClick(webEngineView, 0, 0) - attributes.clear() navigationSpy.clear() shouldIgnoreLinkClicks = false diff --git a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml new file mode 100644 index 000000000..754a9e018 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.2 + +TestWebEngineView { + id: webEngineView + width: 200 + height: 400 + + property var newViewRequest: null + property var dialog: null + + SignalSpy { + id: newViewRequestedSpy + target: webEngineView + signalName: "newViewRequested" + } + + onNewViewRequested: { + newViewRequest = { + "destination": request.destination, + "userInitiated": request.userInitiated + }; + + dialog = Qt.createQmlObject( + "import QtQuick.Window 2.0\n" + + "Window {\n" + + " width: 100; height: 100\n" + + " visible: true; flags: Qt.Dialog\n" + + " property alias webEngineView: webView\n" + + " TestWebEngineView { id: webView; anchors.fill: parent }\n" + + "}", webEngineView); + + request.openIn(dialog.webEngineView); + } + + TestCase { + id: test + name: "NewViewRequest" + when: windowShown + + function init() { + webEngineView.url = Qt.resolvedUrl("about:blank"); + verify(webEngineView.waitForLoadSucceeded()); + + newViewRequestedSpy.clear(); + newViewRequest = null; + } + + function cleanup() { + if (dialog) + dialog.destroy(); + } + + function test_jsWindowOpen() { + // Open an empty page in a new tab + webEngineView.loadHtml( + "<html><head><script>" + + " function popup() { window.open(''); }" + + "</script></head>" + + "<body onload='popup()'></body></html>"); + verify(webEngineView.waitForLoadSucceeded()); + tryCompare(newViewRequestedSpy, "count", 1); + + compare(newViewRequest.destination, WebEngineView.NewViewInTab); + verify(!newViewRequest.userInitiated); + + verify(dialog.webEngineView.waitForLoadSucceeded); + compare(dialog.webEngineView.url, ""); + newViewRequestedSpy.clear(); + dialog.destroy(); + + // Open an empty page in a new dialog + webEngineView.loadHtml( + "<html><head><script>" + + " function popup() { window.open('', '_blank', 'width=200,height=100'); }" + + "</script></head>" + + "<body onload='popup()'></body></html>"); + verify(webEngineView.waitForLoadSucceeded()); + tryCompare(newViewRequestedSpy, "count", 1); + + compare(newViewRequest.destination, WebEngineView.NewViewInDialog); + verify(!newViewRequest.userInitiated); + verify(dialog.webEngineView.waitForLoadSucceeded); + newViewRequestedSpy.clear(); + dialog.destroy(); + + // Open an empty page in a new dialog by user + webEngineView.loadHtml( + "<html><head><script>" + + " function popup() { window.open('', '_blank', 'width=200,height=100'); }" + + "</script></head>" + + "<body onload=\"document.getElementById('popupButton').focus();\">" + + " <button id='popupButton' onclick='popup()'>Pop Up!</button>" + + "</body></html>"); + verify(webEngineView.waitForLoadSucceeded()); + verifyElementHasFocus("popupButton"); + keyPress(Qt.Key_Enter); + tryCompare(newViewRequestedSpy, "count", 1); + + compare(newViewRequest.destination, WebEngineView.NewViewInDialog); + verify(newViewRequest.userInitiated); + verify(dialog.webEngineView.waitForLoadSucceeded); + newViewRequestedSpy.clear(); + dialog.destroy(); + } + } +} + diff --git a/tests/auto/quick/qmltests/data/tst_settings.qml b/tests/auto/quick/qmltests/data/tst_settings.qml index aa3a6dc60..0c37d9569 100644 --- a/tests/auto/quick/qmltests/data/tst_settings.qml +++ b/tests/auto/quick/qmltests/data/tst_settings.qml @@ -56,7 +56,7 @@ TestWebEngineView { webEngineView.url = Qt.resolvedUrl("javascript.html"); verify(webEngineView.waitForLoadSucceeded()); - compare(webEngineView.title, "New Title"); + tryCompare(webEngineView, "title", "New Title"); } function test_javascriptDisabled() { @@ -64,7 +64,7 @@ TestWebEngineView { webEngineView.url = Qt.resolvedUrl("javascript.html"); verify(webEngineView.waitForLoadSucceeded()); - compare(webEngineView.title, "Original Title"); + tryCompare(webEngineView, "title", "Original Title"); } function test_localStorageDisabled() { @@ -73,7 +73,7 @@ TestWebEngineView { webEngineView.url = Qt.resolvedUrl("localStorage.html"); verify(webEngineView.waitForLoadSucceeded()); - compare(webEngineView.title, "Original Title"); + tryCompare(webEngineView, "title", "Original Title"); } function test_localStorageEnabled() { @@ -84,7 +84,7 @@ TestWebEngineView { verify(webEngineView.waitForLoadSucceeded()); webEngineView.reload(); verify(webEngineView.waitForLoadSucceeded()); - compare(webEngineView.title, "New Title"); + tryCompare(webEngineView, "title", "New Title"); } function test_settingsAffectCurrentViewOnly() { @@ -100,8 +100,8 @@ TestWebEngineView { webEngineView2.url = testUrl; verify(webEngineView2.waitForLoadSucceeded()); - compare(webEngineView.title, "New Title"); - compare(webEngineView2.title, "New Title"); + tryCompare(webEngineView, "title", "New Title"); + tryCompare(webEngineView2, "title", "New Title"); webEngineView.settings.javascriptEnabled = false; @@ -110,8 +110,8 @@ TestWebEngineView { webEngineView2.url = testUrl; verify(webEngineView2.waitForLoadSucceeded()); - compare(webEngineView.title, "Original Title"); - compare(webEngineView2.title, "New Title"); + tryCompare(webEngineView, "title", "Original Title"); + tryCompare(webEngineView2, "title", "New Title"); webEngineView2.destroy(); } diff --git a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml index 87ef16aa2..906dc1658 100644 --- a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml +++ b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml @@ -68,12 +68,10 @@ Item { keyPress(Qt.Key_Left) keyRelease(Qt.Key_Left) - for (var i = 0; i < 20 && parentItem.releaseEvents.length < 3; i++) - wait(100) - - compare(parentItem.pressEvents.length, 1) + tryCompare(parentItem.pressEvents, "length", 1) compare(parentItem.pressEvents[0], Qt.Key_Left) - compare(parentItem.releaseEvents.length, 3) + + tryCompare(parentItem.releaseEvents, "length", 3) compare(parentItem.releaseEvents[0], Qt.Key_A) compare(parentItem.releaseEvents[1], Qt.Key_Left) compare(parentItem.releaseEvents[2], Qt.Key_Left) diff --git a/tests/auto/quick/qmltests/data/tst_viewSource.qml b/tests/auto/quick/qmltests/data/tst_viewSource.qml index 79b097b80..8076d99f8 100644 --- a/tests/auto/quick/qmltests/data/tst_viewSource.qml +++ b/tests/auto/quick/qmltests/data/tst_viewSource.qml @@ -74,7 +74,7 @@ TestWebEngineView { function test_viewSource() { webEngineView.url = Qt.resolvedUrl("test1.html"); verify(webEngineView.waitForLoadSucceeded()); - compare(webEngineView.title, "Test page 1"); + tryCompare(webEngineView, "title", "Test page 1"); // FIXME(pvarga): Reintroduce this check in the fix for QTBUG-56117 //verify(webEngineView.canViewSource, true); @@ -83,14 +83,14 @@ TestWebEngineView { tryCompare(newViewRequestedSpy, "count", 1); verify(webEngineView.waitForLoadSucceeded()); // The first titleChanged signal is emitted by adoptWebContents() - tryCompare(titleChangedSpy, "count", 2); + tryVerify(function() { return titleChangedSpy.count >= 2; }); compare(viewRequest.destination, WebEngineView.NewViewInTab); verify(viewRequest.userInitiated); // FIXME(pvarga): Reintroduce this check in the fix for QTBUG-56117 //verify(!webEngineView.canViewSource); - compare(webEngineView.title, "test1.html"); + tryCompare(webEngineView, "title", "test1.html"); compare(webEngineView.url, "view-source:" + Qt.resolvedUrl("test1.html")); } @@ -114,14 +114,14 @@ TestWebEngineView { if (row.loadSucceed) { verify(webEngineView.waitForLoadSucceeded()); - tryCompare(titleChangedSpy, "count", 1); + tryVerify(function() { return titleChangedSpy.count >= 1; }); } else { verify(webEngineView.waitForLoadFailed()); - tryCompare(titleChangedSpy, "count", 2); + tryVerify(function() { return titleChangedSpy.count >= 2; }); } compare(webEngineView.url, row.url); - compare(webEngineView.title, row.title); + tryCompare(webEngineView, "title", row.title); // FIXME(pvarga): Reintroduce this check in the fix for QTBUG-56117 //verify(!webEngineView.canViewSource); } diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index 30c3aab30..4d4268324 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -39,12 +39,14 @@ OTHER_FILES += \ $$PWD/data/test3.html \ $$PWD/data/test4.html \ $$PWD/data/keyboardModifierMapping.html \ + $$PWD/data/keyboardEvents.html \ $$PWD/data/titleupdate.js \ $$PWD/data/tst_desktopBehaviorLoadHtml.qml \ $$PWD/data/tst_download.qml \ $$PWD/data/tst_favicon.qml \ $$PWD/data/tst_faviconDownload.qml \ $$PWD/data/tst_filePicker.qml \ + $$PWD/data/tst_findText.qml \ $$PWD/data/tst_focusOnNavigation.qml \ $$PWD/data/tst_formValidation.qml \ $$PWD/data/tst_geopermission.qml \ @@ -58,6 +60,7 @@ OTHER_FILES += \ $$PWD/data/tst_loadUrl.qml \ $$PWD/data/tst_navigationHistory.qml \ $$PWD/data/tst_navigationRequested.qml \ + $$PWD/data/tst_newViewRequest.qml \ $$PWD/data/tst_properties.qml \ $$PWD/data/tst_runJavaScript.qml \ $$PWD/data/tst_scrollPosition.qml \ @@ -68,6 +71,7 @@ OTHER_FILES += \ $$PWD/data/tst_webchannel.qml \ $$PWD/data/tst_settings.qml \ $$PWD/data/tst_keyboardModifierMapping.qml \ + $$PWD/data/tst_keyboardEvents.qml \ $$PWD/data/icons/favicon.png \ $$PWD/data/icons/gray128.png \ $$PWD/data/icons/gray16.png \ diff --git a/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp b/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp index 63ca25396..3ed4bcc71 100644 --- a/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp +++ b/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp @@ -25,7 +25,7 @@ #include <qwebenginepage.h> #include <qwidget.h> -class tst_QWebEngineView : public QObject +class tst_QWebEngineAccessibility : public QObject { Q_OBJECT @@ -44,27 +44,27 @@ private Q_SLOTS: // This will be called before the first test function is executed. // It is only called once. -void tst_QWebEngineView::initTestCase() +void tst_QWebEngineAccessibility::initTestCase() { } // This will be called after the last test function is executed. // It is only called once. -void tst_QWebEngineView::cleanupTestCase() +void tst_QWebEngineAccessibility::cleanupTestCase() { } // This will be called before each test function is executed. -void tst_QWebEngineView::init() +void tst_QWebEngineAccessibility::init() { } // This will be called after every test function. -void tst_QWebEngineView::cleanup() +void tst_QWebEngineAccessibility::cleanup() { } -void tst_QWebEngineView::noPage() +void tst_QWebEngineAccessibility::noPage() { QWebEngineView webView; webView.show(); @@ -80,7 +80,7 @@ void tst_QWebEngineView::noPage() QCOMPARE(document->childCount(), 0); } -void tst_QWebEngineView::hierarchy() +void tst_QWebEngineAccessibility::hierarchy() { QWebEngineView webView; webView.setHtml("<html><body>" \ @@ -139,7 +139,7 @@ void tst_QWebEngineView::hierarchy() QCOMPARE(input, child); } -void tst_QWebEngineView::text() +void tst_QWebEngineAccessibility::text() { QWebEngineView webView; webView.setHtml("<html><body>" \ @@ -207,7 +207,7 @@ void tst_QWebEngineView::text() QCOMPARE(input3->text(QAccessible::Value), QStringLiteral("Good day!")); } -void tst_QWebEngineView::value() +void tst_QWebEngineAccessibility::value() { QWebEngineView webView; webView.setHtml("<html><body>" \ @@ -248,5 +248,5 @@ void tst_QWebEngineView::value() static QByteArrayList params = QByteArrayList() << "--force-renderer-accessibility"; -W_QTEST_MAIN(tst_QWebEngineView, params) +W_QTEST_MAIN(tst_QWebEngineAccessibility, params) #include "tst_qwebengineaccessibility.moc" diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 166c5a499..f16c42976 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -296,6 +296,7 @@ void tst_QWebEnginePage::cleanupFiles() void tst_QWebEnginePage::initTestCase() { + QLocale::setDefault(QLocale("en")); cleanupFiles(); // In case there are old files from previous runs // Set custom path since the CI doesn't install test plugins. @@ -4992,6 +4993,8 @@ void tst_QWebEnginePage::viewSource() QCOMPARE(page.createdWindows.size(), 1); QTRY_COMPARE(page.createdWindows[0]->url().toString(), QStringLiteral("view-source:%1").arg(url.toString())); + // The requested URL should not be about:blank if the qrc scheme is supported + QTRY_COMPARE(page.createdWindows[0]->requestedUrl(), url); QTRY_COMPARE(page.createdWindows[0]->title(), QStringLiteral("view-source:%1").arg(url.toString())); QVERIFY(!page.createdWindows[0]->action(QWebEnginePage::ViewSource)->isEnabled()); } @@ -5001,21 +5004,24 @@ void tst_QWebEnginePage::viewSourceURL_data() QTest::addColumn<QUrl>("userInputUrl"); QTest::addColumn<bool>("loadSucceed"); QTest::addColumn<QUrl>("url"); + QTest::addColumn<QUrl>("requestedUrl"); QTest::addColumn<QString>("title"); - QTest::newRow("view-source:") << QUrl("view-source:") << true << QUrl("view-source:") << QString("view-source:"); - QTest::newRow("view-source:about:blank") << QUrl("view-source:about:blank") << true << QUrl("view-source:about:blank") << QString("view-source:about:blank"); + QTest::newRow("view-source:") << QUrl("view-source:") << true << QUrl("view-source:") << QUrl("about:blank") << QString("view-source:"); + QTest::newRow("view-source:about:blank") << QUrl("view-source:about:blank") << true << QUrl("view-source:about:blank") << QUrl("about:blank") << QString("view-source:about:blank"); - QUrl testLocalUrl = QUrl(QString("view-source:%1").arg(QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginepage/resources/test1.html")).toString())); - QUrl testLocalUrlWithoutScheme = QUrl(QString("view-source:%1").arg(TESTS_SOURCE_DIR + QLatin1String("qwebenginepage/resources/test1.html"))); - QTest::newRow(testLocalUrl.toString().toStdString().c_str()) << testLocalUrl << true << testLocalUrl << QString("test1.html"); - QTest::newRow(testLocalUrlWithoutScheme.toString().toStdString().c_str()) << testLocalUrlWithoutScheme << true << testLocalUrl << QString("test1.html"); + QString localFilePath = QString("%1qwebenginepage/resources/test1.html").arg(TESTS_SOURCE_DIR); + QUrl testLocalUrl = QUrl(QString("view-source:%1").arg(QUrl::fromLocalFile(localFilePath).toString())); + QUrl testLocalUrlWithoutScheme = QUrl(QString("view-source:%1").arg(localFilePath)); + QTest::newRow(testLocalUrl.toString().toStdString().c_str()) << testLocalUrl << true << testLocalUrl << QUrl::fromLocalFile(localFilePath) << QString("test1.html"); + QTest::newRow(testLocalUrlWithoutScheme.toString().toStdString().c_str()) << testLocalUrlWithoutScheme << true << testLocalUrl << QUrl::fromLocalFile(localFilePath) << QString("test1.html"); - QUrl testResourceUrl = QUrl("view-source:qrc:/resources/test1.html"); - QTest::newRow(testResourceUrl.toString().toStdString().c_str()) << testResourceUrl << true << testResourceUrl << testResourceUrl.toString(); + QString resourcePath = QLatin1String("qrc:/resources/test1.html"); + QUrl testResourceUrl = QUrl(QString("view-source:%1").arg(resourcePath)); + QTest::newRow(testResourceUrl.toString().toStdString().c_str()) << testResourceUrl << true << testResourceUrl << QUrl(resourcePath) << testResourceUrl.toString(); - QTest::newRow("view-source:http://non.existent") << QUrl("view-source:non.existent") << false << QUrl("view-source:http://non.existent/") << QString("http://non.existent/ is not available"); - QTest::newRow("view-source:non.existent") << QUrl("view-source:non.existent") << false << QUrl("view-source:http://non.existent/") << QString("http://non.existent/ is not available"); + QTest::newRow("view-source:http://non.existent") << QUrl("view-source:non.existent") << false << QUrl("view-source:http://non.existent/") << QUrl("http://non.existent/") << QString("http://non.existent/ is not available"); + QTest::newRow("view-source:non.existent") << QUrl("view-source:non.existent") << false << QUrl("view-source:http://non.existent/") << QUrl("http://non.existent/") << QString("http://non.existent/ is not available"); } void tst_QWebEnginePage::viewSourceURL() @@ -5026,6 +5032,7 @@ void tst_QWebEnginePage::viewSourceURL() QFETCH(QUrl, userInputUrl); QFETCH(bool, loadSucceed); QFETCH(QUrl, url); + QFETCH(QUrl, requestedUrl); QFETCH(QString, title); QWebEnginePage *page = new QWebEnginePage; @@ -5037,6 +5044,7 @@ void tst_QWebEnginePage::viewSourceURL() QCOMPARE(arguments.at(0).toBool(), loadSucceed); QCOMPARE(page->url(), url); + QCOMPARE(page->requestedUrl(), requestedUrl); QCOMPARE(page->title(), title); QVERIFY(!page->action(QWebEnginePage::ViewSource)->isEnabled()); diff --git a/tests/auto/widgets/qwebenginespellcheck/dict/de-DE.aff b/tests/auto/widgets/qwebenginespellcheck/dict/de-DE.aff new file mode 100644 index 000000000..ff8185771 --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/dict/de-DE.aff @@ -0,0 +1,5 @@ +SET UTF-8 +TRY esianrtolcdugmphbyfvkwzqESIANRTOLCDUGMPHBYFVKWZQ + +PFX Q Y 1 +PFX Q 0 q . diff --git a/tests/auto/widgets/qwebenginespellcheck/dict/de-DE.dic b/tests/auto/widgets/qwebenginespellcheck/dict/de-DE.dic new file mode 100644 index 000000000..d10ae2600 --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/dict/de-DE.dic @@ -0,0 +1,14 @@ +15 +du/Q +er/Q +es/Q +ich/Q +ihr/Q +liebe/Q +lieben/Q +liebst/Q +liebt/Q +qt/Q +sie/Q +Sie/Q +wir/Q diff --git a/tests/auto/widgets/qwebenginespellcheck/qwebenginespellcheck.pro b/tests/auto/widgets/qwebenginespellcheck/qwebenginespellcheck.pro index 437aad937..a36c82e20 100644 --- a/tests/auto/widgets/qwebenginespellcheck/qwebenginespellcheck.pro +++ b/tests/auto/widgets/qwebenginespellcheck/qwebenginespellcheck.pro @@ -2,7 +2,9 @@ include(../tests.pri) DISTFILES += \ dict/en-US.dic \ - dict/en-US.aff + dict/en-US.aff \ + dict/de-DE.dic \ + dict/de-DE.aff \ qtPrepareTool(CONVERT_TOOL, qwebengine_convert_dict) @@ -13,7 +15,7 @@ debug_and_release { DICTIONARIES_DIR = qtwebengine_dictionaries } -dict.files = $$PWD/dict/en-US.dic +dict.files = $$PWD/dict/en-US.dic $$PWD/dict/de-DE.dic dictoolbuild.input = dict.files dictoolbuild.output = $${DICTIONARIES_DIR}/${QMAKE_FILE_BASE}.bdic dictoolbuild.commands = $${CONVERT_TOOL} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} diff --git a/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp index 534d40f45..4db5b9477 100644 --- a/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp +++ b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp @@ -73,8 +73,10 @@ private Q_SLOTS: void cleanup(); void initTestCase(); void spellCheckLanguage(); + void spellCheckLanguages(); void spellCheckEnabled(); void spellcheck(); + void spellcheck_data(); private: void load(); @@ -86,14 +88,14 @@ void tst_QWebEngineSpellcheck::initTestCase() QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); QVERIFY(profile); QVERIFY(!profile->isSpellCheckEnabled()); - QVERIFY(profile->spellCheckLanguage().isEmpty()); + QVERIFY(profile->spellCheckLanguages().isEmpty()); } void tst_QWebEngineSpellcheck::init() { QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); profile->setSpellCheckEnabled(false); - profile->setSpellCheckLanguage(QString::null); + profile->setSpellCheckLanguages(QStringList()); m_view = new WebView(); } @@ -113,10 +115,19 @@ void tst_QWebEngineSpellcheck::spellCheckLanguage() { QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); QVERIFY(profile); - profile->setSpellCheckLanguage("en-US"); - QVERIFY(profile->spellCheckLanguage() == "en-US"); + profile->setSpellCheckLanguages({"en-US"}); + QVERIFY(profile->spellCheckLanguages() == QStringList({"en-US"})); } +void tst_QWebEngineSpellcheck::spellCheckLanguages() +{ + QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); + QVERIFY(profile); + profile->setSpellCheckLanguages({"en-US","de-DE"}); + QVERIFY(profile->spellCheckLanguages() == QStringList({"en-US","de-DE"})); +} + + void tst_QWebEngineSpellcheck::spellCheckEnabled() { QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); @@ -127,9 +138,12 @@ void tst_QWebEngineSpellcheck::spellCheckEnabled() void tst_QWebEngineSpellcheck::spellcheck() { + QFETCH(QStringList, languages); + QFETCH(QStringList, suggestions); + QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); QVERIFY(profile); - profile->setSpellCheckLanguage("en-US"); + profile->setSpellCheckLanguages(languages); profile->setSpellCheckEnabled(true); load(); @@ -166,8 +180,7 @@ void tst_QWebEngineSpellcheck::spellcheck() QVERIFY(m_view->data().misspelledWord() == "lovee"); // check suggestions - QStringList expected {"love", "loves"}; - QVERIFY(m_view->data().spellCheckerSuggestions() == expected); + QVERIFY(m_view->data().spellCheckerSuggestions() == suggestions); // check replace word m_view->page()->replaceMisspelledWord("love"); @@ -175,5 +188,13 @@ void tst_QWebEngineSpellcheck::spellcheck() QTRY_VERIFY(evaluateJavaScriptSync(m_view->page(), "text();").toString() == text); } +void tst_QWebEngineSpellcheck::spellcheck_data() +{ + QTest::addColumn<QStringList>("languages"); + QTest::addColumn<QStringList>("suggestions"); + QTest::newRow("en-US") << QStringList({"en-US"}) << QStringList({"love", "loves"}); + QTest::newRow("en-US,de-DE") << QStringList({"en-US","de-DE"}) << QStringList({"love", "liebe", "loves"}); +} + QTEST_MAIN(tst_QWebEngineSpellcheck) #include "tst_qwebenginespellcheck.moc" diff --git a/tests/auto/widgets/qwebengineview/resources/keyboardEvents.html b/tests/auto/widgets/qwebengineview/resources/keyboardEvents.html new file mode 100644 index 000000000..d536d849f --- /dev/null +++ b/tests/auto/widgets/qwebengineview/resources/keyboardEvents.html @@ -0,0 +1,98 @@ +<html> +<head> + <style> + div { + width: 300px; + margin-bottom: 10px; + } + + #div_container { + display: inline-block; + } + + #div_container div { + width: 100px; + height: 100px; + border: 2px solid black; + padding: 10px; + margin: 0 10px 0 10px; + float: left; + } + + #div_container div:focus { + border: 2px solid red; + } + + #form_container span { + display: block; + padding-bottom: 10px; + } + + #form_container label { + float: left; + text-align: right; + width: 50px; + margin-right: 20px; + } + </style> +</head> + +<body onload="document.getElementById('first_div').focus()"> + <div id="div_container"> + <div id="first_div" tabindex="0">First</div> + <div id="second_div" tabindex="0">Second</div> + </div> + + <div id="form_container"> + <form><fieldset> + <legend>Form</legend> + + <span> + <label for="text_input">text</label> + <input id="text_input" type="text" /> + </span> + + <span> + <input id="radio1" type="radio" name="radio">radio1</input> + <input id="radio2" type="radio" name="radio">radio2</input> + </span> + + <span> + <input id="checkbox1" type="checkbox" name="checkbox1" checked="true">checkbox1</input> + <input id="checkbox2" type="checkbox" name="checkbox2" checked="true">checkbox2</input> + </span> + + <span> + <label for="number_input">number</label> + <input id="number_input" type="number" min="0" max="10" value="5" /> + </span> + + <span> + <label for="range_input">range</label> + <input id="range_input" type="range" min="0" max="10" value="5" /> + </span> + + <span> + <label for="search_input">search</label> + <input id="search_input" type="search" value="test" /> + </span> + + <input id="submit_button" type="submit" value="Submit" /> + </fieldset></form> + </div> + + <div> + <select id="combobox"> + <option value="a">a</option> + <option value="b">b</option> + <option value="c">c</option> + </select> + </div> + + <div> + <a id="first_hyperlink" href="">First</a> + <br /> + <a id="second_hyperlink" href="">Second</a> + </div> +</body> +</html> diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 156c56933..9966ad9ee 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -82,6 +82,7 @@ private Q_SLOTS: void changeLocale(); void inputMethodsTextFormat_data(); void inputMethodsTextFormat(); + void keyboardEvents(); }; // This will be called before the first test function is executed. @@ -924,5 +925,94 @@ void tst_QWebEngineView::inputMethodsTextFormat() QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), string); } +void tst_QWebEngineView::keyboardEvents() +{ + QWebEngineView view; + view.show(); + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + view.load(QUrl("qrc:///resources/keyboardEvents.html")); + QVERIFY(loadFinishedSpy.wait()); + + QStringList elements; + elements << "first_div" << "second_div"; + elements << "text_input" << "radio1" << "checkbox1" << "checkbox2"; + elements << "number_input" << "range_input" << "search_input"; + elements << "submit_button" << "combobox" << "first_hyperlink" << "second_hyperlink"; + + // Iterate over the elements of the test page with the Tab key. This tests whether any + // element blocks the in-page navigation by Tab. + for (const QString &elementId : elements) { + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), elementId); + QTest::keyPress(view.focusProxy(), Qt::Key_Tab); + } + + // Move back to the radio buttons with the Shift+Tab key combination + for (int i = 0; i < 10; ++i) + QTest::keyPress(view.focusProxy(), Qt::Key_Tab, Qt::ShiftModifier); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("radio2")); + + // Test the Space key by checking a radio button + QVERIFY(!evaluateJavaScriptSync(view.page(), "document.getElementById('radio2').checked").toBool()); + QTest::keyClick(view.focusProxy(), Qt::Key_Space); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.getElementById('radio2').checked").toBool()); + + // Test the Left key by switching the radio button + QVERIFY(!evaluateJavaScriptSync(view.page(), "document.getElementById('radio1').checked").toBool()); + QTest::keyPress(view.focusProxy(), Qt::Key_Left); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("radio1")); + QVERIFY(!evaluateJavaScriptSync(view.page(), "document.getElementById('radio2').checked").toBool()); + QVERIFY(evaluateJavaScriptSync(view.page(), "document.getElementById('radio1').checked").toBool()); + + // Test the Space key by unchecking a checkbox + evaluateJavaScriptSync(view.page(), "document.getElementById('checkbox1').focus()"); + QVERIFY(evaluateJavaScriptSync(view.page(), "document.getElementById('checkbox1').checked").toBool()); + QTest::keyClick(view.focusProxy(), Qt::Key_Space); + QTRY_VERIFY(!evaluateJavaScriptSync(view.page(), "document.getElementById('checkbox1').checked").toBool()); + + // Test the Up and Down keys by changing the value of a spinbox + evaluateJavaScriptSync(view.page(), "document.getElementById('number_input').focus()"); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('number_input').value").toInt(), 5); + QTest::keyPress(view.focusProxy(), Qt::Key_Up); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('number_input').value").toInt(), 6); + QTest::keyPress(view.focusProxy(), Qt::Key_Down); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('number_input').value").toInt(), 5); + + // Test the Left, Right, Home, PageUp, End and PageDown keys by changing the value of a slider + evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').focus()"); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').value").toString(), QStringLiteral("5")); + QTest::keyPress(view.focusProxy(), Qt::Key_Left); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').value").toString(), QStringLiteral("4")); + QTest::keyPress(view.focusProxy(), Qt::Key_Right); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').value").toString(), QStringLiteral("5")); + QTest::keyPress(view.focusProxy(), Qt::Key_Home); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').value").toString(), QStringLiteral("0")); + QTest::keyPress(view.focusProxy(), Qt::Key_PageUp); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').value").toString(), QStringLiteral("1")); + QTest::keyPress(view.focusProxy(), Qt::Key_End); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').value").toString(), QStringLiteral("10")); + QTest::keyPress(view.focusProxy(), Qt::Key_PageDown); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('range_input').value").toString(), QStringLiteral("9")); + + // Test the Escape key by removing the content of a search field + evaluateJavaScriptSync(view.page(), "document.getElementById('search_input').focus()"); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('search_input').value").toString(), QStringLiteral("test")); + QTest::keyPress(view.focusProxy(), Qt::Key_Escape); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.getElementById('search_input').value").toString().isEmpty()); + + // Test the alpha keys by changing the values in a combobox + evaluateJavaScriptSync(view.page(), "document.getElementById('combobox').focus()"); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('combobox').value").toString(), QStringLiteral("a")); + QTest::keyPress(view.focusProxy(), Qt::Key_B); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('combobox').value").toString(), QStringLiteral("b")); + // Must wait with the second key press to simulate selection of another element + QTest::keyPress(view.focusProxy(), Qt::Key_C, Qt::NoModifier, 1000); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('combobox').value").toString(), QStringLiteral("c")); + + // Test the Enter key by loading a page with a hyperlink + evaluateJavaScriptSync(view.page(), "document.getElementById('first_hyperlink').focus()"); + QTest::keyPress(view.focusProxy(), Qt::Key_Enter); + QVERIFY(loadFinishedSpy.wait()); +} + QTEST_MAIN(tst_QWebEngineView) #include "tst_qwebengineview.moc" diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc index b32b533c2..4809bbebf 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc @@ -5,5 +5,6 @@ <file>resources/input_types.html</file> <file>resources/scrolltest_page.html</file> <file>resources/basic_printing_page.html</file> + <file>resources/keyboardEvents.html</file> </qresource> </RCC> diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index 2f5416701..c7096b6b6 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -13,7 +13,10 @@ SUBDIRS += \ qwebenginesettings \ qwebengineview -# QTBUG-53135, osx does not use hunspell -!contains(WEBENGINE_CONFIG, no_spellcheck):!osx:!cross_compile { - SUBDIRS += qwebenginespellcheck +!contains(WEBENGINE_CONFIG, use_spellchecker):!cross_compile { + !contains(WEBENGINE_CONFIG, use_native_spellchecker) { + SUBDIRS += qwebenginespellcheck + } else { + message("Spellcheck test will not be built because it depends on usage of Hunspell dictionaries.") + } } diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 16f56dbe9..edf95846c 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs SUBDIRS += \ - widgets + widgets \ + quick !qtHaveModule(webenginewidgets): SUBDIRS -= widgets diff --git a/tests/manual/quick/faviconbrowser/AddressBar.qml b/tests/manual/quick/faviconbrowser/AddressBar.qml new file mode 100644 index 000000000..7122b2862 --- /dev/null +++ b/tests/manual/quick/faviconbrowser/AddressBar.qml @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Rectangle { + id: root + + property int progress: 0 + property url iconUrl: "" + property url pageUrl: "" + + signal accepted(url addressUrl) + + clip: true + + onActiveFocusChanged: { + if (activeFocus) + addressField.forceActiveFocus(); + } + + Rectangle { + width: addressField.width / 100 * root.progress + height: root.height + + visible: root.progress < 100 + + color: "#b6dca6" + radius: root.radius + } + + TextField { + id: addressField + anchors.fill: parent + + Image { + anchors.verticalCenter: addressField.verticalCenter + x: 5; z: parent.z + 1 + width: 16; height: 16 + sourceSize: Qt.size(width, height) + source: root.iconUrl + visible: root.progress == 100 + } + + Text { + text: root.progress < 0 ? "" : root.progress + "%" + x: 5; z: parent.z + 1 + font.bold: true + anchors.verticalCenter: parent.verticalCenter + + visible: root.progress < 100 + } + + style: TextFieldStyle { + padding.left: 30 + + background: Rectangle { + color: "transparent" + border.color: "black" + border.width: 1 + radius: root.radius + } + } + + onActiveFocusChanged: { + if (activeFocus) + selectAll(); + else + deselect(); + } + + text: root.pageUrl + onAccepted: root.accepted(utils.fromUserInput(text)) + } +} diff --git a/tests/manual/quick/faviconbrowser/FaviconPanel.qml b/tests/manual/quick/faviconbrowser/FaviconPanel.qml new file mode 100644 index 000000000..ce768b82d --- /dev/null +++ b/tests/manual/quick/faviconbrowser/FaviconPanel.qml @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.1 + +Item { + id: root + + property url iconUrl: "" + property int iconSize: 128 + property int iconMinimumSize: 8 + property int iconMaximumSize: 256 + + RowLayout { + anchors.fill: parent + spacing: 2 + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + + RowLayout { + anchors.fill: parent + spacing: 2 + + Rectangle { + Layout.fillHeight: true + Layout.preferredWidth: (parent.width - 2 * parent.spacing) / 3 + + border.color: "black" + border.width: 1 + clip: true + + Text { + z: parent.z + 1 + anchors.top: parent.top + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 5 + font.bold: true + + text: "faviconImage" + } + + Image { + id: faviconImage + anchors.centerIn: parent + + width: root.iconSize + height: width + sourceSize: Qt.size(width, height) + + source: root.iconUrl + + onStatusChanged: { + if (status == Image.Ready) { + grabToImage(function(result) { + grabImage.source = result.url; + }); + } + + if (status == Image.Null) + grabImage.source = ""; + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.preferredWidth: (parent.width - 2 * parent.spacing) / 3 + + border.color: "black" + border.width: 1 + clip: true + + Text { + z: parent.z + 1 + anchors.top: parent.top + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 5 + font.bold: true + + text: "grabImage" + } + + Image { + id: grabImage + anchors.centerIn: parent + + width: root.iconSize + height: width + + onStatusChanged: { + if (status == Image.Ready || status == Image.Null) + faviconCanvas.requestPaint(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.preferredWidth: (parent.width - 2 * parent.spacing) / 3 + + border.color: "black" + border.width: 1 + clip: true + + Text { + z: parent.z + 1 + anchors.top: parent.top + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 5 + font.bold: true + + text: "faviconCanvas" + } + + Canvas { + id: faviconCanvas + anchors.centerIn: parent + + width: root.iconSize + height: width + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + ctx.clearRect(0, 0, width, height); + + if (grabImage.source == "") + return; + + ctx.drawImage(grabImage, 0, 0, width, height); + + var imageData = ctx.getImageData(width/2, height/2, width/2, height/2); + var pixel = imageData.data; + + verbose.append("pixel(" + width/2 + ", " + height/2 + "): " + pixel[0] + ", " + pixel[1] + ", " + pixel[2]); + } + } + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.preferredWidth: 100 + + Slider { + id: faviconSizeSlider + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.bottom: faviconSizeSpin.top + + orientation: Qt.Vertical + minimumValue: root.iconMinimumSize + maximumValue: root.iconMaximumSize + stepSize: 1 + tickmarksEnabled: true + value: root.iconSize + + onValueChanged: { + if (pressed && value != root.iconSize) + root.iconSize = value; + } + } + + SpinBox { + id: faviconSizeSpin + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + + minimumValue: root.iconMinimumSize + maximumValue: root.iconMaximumSize + value: root.iconSize + + onEditingFinished: { + if (value != root.iconSize) + root.iconSize = value; + } + } + } + + TextArea { + id: verbose + + Layout.fillHeight: true + Layout.preferredWidth: 310 + + readOnly: true + tabChangesFocus: true + + font.family: "Monospace" + font.pointSize: 12 + + textFormat: TextEdit.RichText + frameVisible: false + + style: TextAreaStyle { + backgroundColor: "lightgray" + } + } + } +} diff --git a/tests/manual/quick/faviconbrowser/faviconbrowser.pro b/tests/manual/quick/faviconbrowser/faviconbrowser.pro new file mode 100644 index 000000000..d4368ab3e --- /dev/null +++ b/tests/manual/quick/faviconbrowser/faviconbrowser.pro @@ -0,0 +1,23 @@ +QT += qml quick webengine +qtHaveModule(widgets) { + QT += widgets # QApplication is required to get native styling with QtQuickControls +} + +TARGET = faviconbrowser +TEMPLATE = app + + +SOURCES = \ + main.cpp + +HEADERS = \ + utils.h + +OTHER_FILES += \ + main.qml \ + AddressBar.qml \ + FaviconPanel.qml + +RESOURCES += \ + faviconbrowser.qrc + diff --git a/tests/manual/quick/faviconbrowser/faviconbrowser.qrc b/tests/manual/quick/faviconbrowser/faviconbrowser.qrc new file mode 100644 index 000000000..95d0dc2c4 --- /dev/null +++ b/tests/manual/quick/faviconbrowser/faviconbrowser.qrc @@ -0,0 +1,17 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource prefix="/"> + <file>main.qml</file> + <file>AddressBar.qml</file> + <file>FaviconPanel.qml</file> + </qresource> + <qresource prefix="test"> + <file alias="favicon-multi-gray.html">../../../auto/quick/qmltests/data/favicon-multi-gray.html</file> + <file alias="favicon-candidates-gray.html">../../../auto/quick/qmltests/data/favicon-candidates-gray.html</file> + <file alias="icons/grayicons.ico">../../../auto/quick/qmltests/data/icons/grayicons.ico</file> + <file alias="icons/gray16.png">../../../auto/quick/qmltests/data/icons/gray16.png</file> + <file alias="icons/gray32.png">../../../auto/quick/qmltests/data/icons/gray32.png</file> + <file alias="icons/gray64.png">../../../auto/quick/qmltests/data/icons/gray64.png</file> + <file alias="icons/gray128.png">../../../auto/quick/qmltests/data/icons/gray128.png</file> + <file alias="icons/gray255.png">../../../auto/quick/qmltests/data/icons/gray255.png</file> + </qresource> +</RCC> diff --git a/tests/manual/quick/faviconbrowser/main.cpp b/tests/manual/quick/faviconbrowser/main.cpp new file mode 100644 index 000000000..b75be2483 --- /dev/null +++ b/tests/manual/quick/faviconbrowser/main.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "utils.h" + +#ifndef QT_NO_WIDGETS +#include <QtWidgets/QApplication> +typedef QApplication Application; +#else +#include <QtGui/QGuiApplication> +typedef QGuiApplication Application; +#endif +#include <QtQml/QQmlApplicationEngine> +#include <QtQml/QQmlContext> +#include <QtWebEngine/qtwebengineglobal.h> + +int main(int argc, char **argv) +{ + Application app(argc, argv); + + QtWebEngine::initialize(); + + QQmlApplicationEngine appEngine; + Utils utils; + appEngine.rootContext()->setContextProperty("utils", &utils); + appEngine.load(QUrl("qrc:/main.qml")); + + return app.exec(); +} diff --git a/tests/manual/quick/faviconbrowser/main.qml b/tests/manual/quick/faviconbrowser/main.qml new file mode 100644 index 000000000..1ad5fc2e8 --- /dev/null +++ b/tests/manual/quick/faviconbrowser/main.qml @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.1 +import QtWebEngine 1.3 +import Qt.labs.settings 1.0 + +ApplicationWindow { + width: 1300 + height: 900 + visible: true + + Item { + id: bookmarkUrls + + property url multiTest: Qt.resolvedUrl("qrc:/test/favicon-multi-gray.html") + property url candidatesTest: Qt.resolvedUrl("qrc:/test/favicon-candidates-gray.html") + property url aboutBlank: Qt.resolvedUrl("about:blank") + property url qtHome: Qt.resolvedUrl("http://www.qt.io/") + } + + Settings { + id: appSettings + + property alias autoLoadIconsForPage: autoLoadIconsForPage.checked + property alias touchIconsEnabled: touchIconsEnabled.checked + } + + SplitView { + anchors.fill: parent + orientation: Qt.Vertical + + FaviconPanel { + id: faviconPanel + + Layout.fillWidth: true + Layout.minimumHeight: 200 + + Layout.margins: 2 + + iconUrl: webEngineView && webEngineView.icon + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: 2 + + AddressBar { + id: addressBar + + Layout.fillWidth: true + Layout.leftMargin: 5 + Layout.rightMargin: 5 + height: 25 + + color: "white" + radius: 4 + + progress: webEngineView && webEngineView.loadProgress + iconUrl: webEngineView && webEngineView.icon + pageUrl: webEngineView && webEngineView.url + + onAccepted: webEngineView.url = addressUrl + } + + Rectangle { + id: toolBar + + Layout.fillWidth: true + Layout.leftMargin: 5 + Layout.rightMargin: 5 + Layout.preferredHeight: 25 + + RowLayout { + anchors.verticalCenter: parent.verticalCenter + + Button { + text: "Multi-sized Favicon Test" + onClicked: webEngineView.url = bookmarkUrls.multiTest + enabled: webEngineView.url != bookmarkUrls.multiTest + } + + Button { + text: "Candidate Favicons Test" + onClicked: webEngineView.url = bookmarkUrls.candidatesTest + enabled: webEngineView.url != bookmarkUrls.candidatesTest + } + + Button { + text: "About Blank" + onClicked: webEngineView.url = bookmarkUrls.aboutBlank + enabled: webEngineView.url != bookmarkUrls.aboutBlank + } + + Button { + text: "Qt Home Page" + onClicked: webEngineView.url = bookmarkUrls.qtHome + enabled: webEngineView.url != bookmarkUrls.qtHome + } + } + + ToolButton { + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + + menu: Menu { + MenuItem { + id: autoLoadIconsForPage + text: "Icons On" + checkable: true + checked: WebEngine.settings.autoLoadIconsForPage + + onCheckedChanged: webEngineView.reload() + } + + MenuItem { + id: touchIconsEnabled + text: "Touch Icons On" + checkable: true + checked: WebEngine.settings.touchIconsEnabled + enabled: autoLoadIconsForPage.checked + + onCheckedChanged: webEngineView.reload() + } + } + } + } + + WebEngineView { + id: webEngineView + + Layout.fillWidth: true + Layout.fillHeight: true + + settings.autoLoadIconsForPage: appSettings.autoLoadIconsForPage + settings.touchIconsEnabled: appSettings.touchIconsEnabled + + Component.onCompleted: webEngineView.url = bookmarkUrls.multiTest + } + } + } +} diff --git a/tests/manual/quick/faviconbrowser/utils.h b/tests/manual/quick/faviconbrowser/utils.h new file mode 100644 index 000000000..79aa38cc7 --- /dev/null +++ b/tests/manual/quick/faviconbrowser/utils.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef UTILS_H +#define UTILS_H + +#include <QtCore/QFileInfo> +#include <QtCore/QUrl> + +class Utils : public QObject { + Q_OBJECT +public: + Q_INVOKABLE static QUrl fromUserInput(const QString& userInput); +}; + +inline QUrl Utils::fromUserInput(const QString& userInput) +{ + QFileInfo fileInfo(userInput); + if (fileInfo.exists()) + return QUrl::fromLocalFile(fileInfo.absoluteFilePath()); + return QUrl::fromUserInput(userInput); +} + +#endif // UTILS_H diff --git a/tests/manual/quick/quick.pro b/tests/manual/quick/quick.pro new file mode 100644 index 000000000..5251c4b42 --- /dev/null +++ b/tests/manual/quick/quick.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + faviconbrowser |