Merge lp:~osomon/webbrowser-app/find-in-page into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 1034
Merged at revision: 1122
Proposed branch: lp:~osomon/webbrowser-app/find-in-page
Merge into: lp:webbrowser-app
Diff against target: 825 lines (+430/-59)
11 files modified
debian/control (+2/-2)
src/app/actions/FindInPage.qml (+27/-0)
src/app/webbrowser/AddressBar.qml (+76/-25)
src/app/webbrowser/Browser.qml (+49/-9)
src/app/webbrowser/Chrome.qml (+1/-0)
src/app/webbrowser/NavigationBar.qml (+74/-23)
tests/autopilot/webbrowser_app/emulators/browser.py (+11/-0)
tests/autopilot/webbrowser_app/tests/http_server.py (+11/-0)
tests/autopilot/webbrowser_app/tests/test_findinpage.py (+156/-0)
tests/autopilot/webbrowser_app/tests/test_keyboard.py (+8/-0)
tests/unittests/qml/tst_AddressBar.qml (+15/-0)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/find-in-page
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Riccardo Padovani (community) Approve
Ubuntu Phablet Team Pending
Review via email: mp+263547@code.launchpad.net

Commit message

Implement the "Find in Page" feature.
This bumps the runtime dependency on liboxideqt-qmlplugin to 1.8.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Riccardo Padovani (rpadovani) wrote :

# Issue

If you click on a link while it's in search mode it doesn't exit the search mode but it insert the url in the address bar and then when you insert a new url it search it in the page.

When you open a new tab it should exit the search mode

In all the other browsers the search mode is sticked to the tab itself, so I can have it enabled on one tab and switch the tab and return to the same tab and having the same search inserted.
I appreciate this is difficult to implement, so could be done in a second branch, but I urge you to dismiss the search when you change the tab

# Nitpick

49 + text: i18n.tr("Find in Page")
243 + text: i18n.tr("Find in page")

Be coherent :-)

# Opinions

80 + // Only start searches when the user types two or more characters

Could I say I find it not well designed? If I insert one letter it gives 0/0 so I think there isn't any occurence of that letter in the page. All other browsers search also with only one char.

Also, I think we need an icon to put in the addressbar when it's in find-in-page mode

review: Needs Fixing
1028. By Olivier Tilloy

Exit find in page mode when navigating.

1029. By Olivier Tilloy

Exit find in page mode when opening a new tab.

1030. By Olivier Tilloy

Make i18n’d strings consistent.

Revision history for this message
Olivier Tilloy (osomon) wrote :

Thanks for your thorough review Riccardo, I’m glad you caught those really nasty issues.

I addressed them in the revisions I just pushed.

Regarding the issue with the search starting only when 2 or more characters are input, I’ve asked the UX designer in charge the rationale for this specification, still waiting for an answer. Note that I tend to agree with you, I would expect the search to start with the first character input.

> I think we need an icon to put in the addressbar
> when it's in find-in-page mode

This is per specification: https://docs.google.com/presentation/d/1woHjO8K4iqyVZZlfQ4BXL0DhYbwkEmZ7wvcUhYzHDRk/edit#slide=id.g9af1803c6_6_0

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Now works like a charm, thanks so much :-)

review: Approve
1031. By Olivier Tilloy

Remove the lower limit on two characters, as the UX specification was updated.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> Regarding the issue with the search starting only when 2 or more
> characters are input, I’ve asked the UX designer in charge the
> rationale for this specification, still waiting for an answer.
> Note that I tend to agree with you, I would expect the search to
> start with the first character input.

And the UX designer agrees with us too. The specification was updated, and I updated the implementation accordingly.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Riccardo Padovani (rpadovani) wrote :

\o/

review: Approve
1032. By Olivier Tilloy

Merge the latest changes from trunk and resolve a trivial conflict.

1033. By Olivier Tilloy

Fix positioning of the bookmark toggle placeholder item, which got messed up during the merge.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1034. By Olivier Tilloy

Merge the latest changes from trunk and resolve numerous conflicts.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1035. By Olivier Tilloy

Merge the latest changes from trunk and resolve a few conflicts.

1036. By Olivier Tilloy

Update import.

1037. By Olivier Tilloy

Fix a failing autopilot test on devices.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2015-08-10 15:33:43 +0000
3+++ debian/control 2015-08-11 11:17:03 +0000
4@@ -6,7 +6,7 @@
5 debhelper (>= 9),
6 dh-translations,
7 hardening-wrapper,
8- liboxideqt-qmlplugin (>= 1.6),
9+ liboxideqt-qmlplugin (>= 1.8),
10 libqt5sql5-sqlite,
11 python3-all,
12 python3-flake8,
13@@ -34,7 +34,7 @@
14 Depends: ${misc:Depends},
15 ${shlibs:Depends},
16 fonts-liberation,
17- liboxideqt-qmlplugin (>= 1.7),
18+ liboxideqt-qmlplugin (>= 1.8),
19 libqt5sql5-sqlite,
20 qml-module-qt-labs-folderlistmodel,
21 qml-module-qt-labs-settings,
22
23=== added file 'src/app/actions/FindInPage.qml'
24--- src/app/actions/FindInPage.qml 1970-01-01 00:00:00 +0000
25+++ src/app/actions/FindInPage.qml 2015-08-11 11:17:03 +0000
26@@ -0,0 +1,27 @@
27+/*
28+ * Copyright 2015 Canonical Ltd.
29+ *
30+ * This file is part of webbrowser-app.
31+ *
32+ * webbrowser-app is free software; you can redistribute it and/or modify
33+ * it under the terms of the GNU General Public License as published by
34+ * the Free Software Foundation; version 3.
35+ *
36+ * webbrowser-app is distributed in the hope that it will be useful,
37+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
38+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39+ * GNU General Public License for more details.
40+ *
41+ * You should have received a copy of the GNU General Public License
42+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
43+ */
44+
45+import Ubuntu.Components 1.3
46+import Ubuntu.Unity.Action 1.1 as UnityActions
47+
48+UnityActions.Action {
49+ text: i18n.tr("Find in page")
50+ // TRANSLATORS: This is a free-form list of keywords associated to the 'Find in Page' action.
51+ // Keywords may actually be sentences, and must be separated by semi-colons.
52+ keywords: i18n.tr("Search in Page")
53+}
54
55=== modified file 'src/app/webbrowser/AddressBar.qml'
56--- src/app/webbrowser/AddressBar.qml 2015-08-10 16:16:02 +0000
57+++ src/app/webbrowser/AddressBar.qml 2015-08-11 11:17:03 +0000
58@@ -19,7 +19,7 @@
59 import QtQuick 2.4
60 import Ubuntu.Components 1.3
61 import Ubuntu.Components.Popups 1.3
62-import com.canonical.Oxide 1.0 as Oxide
63+import com.canonical.Oxide 1.8 as Oxide
64 import ".."
65 import "urlManagement.js" as UrlManagement
66
67@@ -40,6 +40,8 @@
68 property bool canSimplifyText: true
69 property bool editing: false
70 property bool showFavicon: true
71+ property bool findInPageMode: false
72+ property var findController: null
73
74 property var securityStatus: null
75
76@@ -57,6 +59,12 @@
77 textField.selectAll()
78 }
79
80+ Binding {
81+ target: findController
82+ property: "text"
83+ value: findInPageMode ? textField.text : ""
84+ }
85+
86 TextField {
87 id: textField
88 objectName: "addressBarTextField"
89@@ -68,6 +76,7 @@
90
91 width: iconsRow.width + units.gu(1)
92 height: units.gu(2)
93+ visible: !findInPageMode
94
95 Row {
96 id: iconsRow
97@@ -179,29 +188,47 @@
98 }
99 }
100
101- secondaryItem: MouseArea {
102- id: bookmarkToggle
103- objectName: "bookmarkToggle"
104-
105+ secondaryItem: Row {
106 height: textField.height
107- width: visible ? height : 0
108-
109- visible: internal.idle && addressbar.actualUrl.toString()
110-
111- Icon {
112- height: parent.height - units.gu(2)
113- width: height
114- anchors.centerIn: parent
115-
116- name: addressbar.bookmarked ? "starred" : "non-starred"
117- color: addressbar.bookmarked ? UbuntuColors.orange : UbuntuColors.darkGrey
118+
119+ Label {
120+ objectName: "findInPageCounter"
121+ anchors.verticalCenter: parent.verticalCenter
122+ fontSize: "x-small"
123+ color: UbuntuColors.darkGrey
124+ opacity: findController && findController.count > 0 ? 1.0 : 0.6
125+ visible: findInPageMode
126+
127+ // TRANSLATORS: %2 refers to the total number of find in page results and %1 to the highlighted result
128+ text: i18n.tr("%1/%2").arg(current).arg(count)
129+ property int current: findController ? findController.current : 0
130+ property int count: findController ? findController.count : 0
131 }
132
133- onClicked: addressbar.bookmarked = !addressbar.bookmarked
134-
135- Item {
136- id: bookmarkTogglePlaceHolderItem
137- anchors.fill: parent
138+ MouseArea {
139+ id: bookmarkToggle
140+ objectName: "bookmarkToggle"
141+
142+ height: parent.height
143+ width: visible ? height : 0
144+
145+ visible: !findInPageMode && internal.idle && addressbar.actualUrl.toString()
146+
147+ Icon {
148+ height: parent.height - units.gu(2)
149+ width: height
150+ anchors.centerIn: parent
151+
152+ name: addressbar.bookmarked ? "starred" : "non-starred"
153+ color: addressbar.bookmarked ? UbuntuColors.orange : UbuntuColors.darkGrey
154+ }
155+
156+ onClicked: addressbar.bookmarked = !addressbar.bookmarked
157+
158+ Item {
159+ id: bookmarkTogglePlaceHolderItem
160+ anchors.fill: parent
161+ }
162 }
163 }
164
165@@ -209,7 +236,8 @@
166 color: UbuntuColors.darkGrey
167 inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhUrlCharactersOnly
168
169- placeholderText: i18n.tr("search or enter an address")
170+ placeholderText: findInPageMode ? i18n.tr("find in page")
171+ : i18n.tr("search or enter an address")
172
173 // Work around the "fix" for http://pad.lv/1089370 which
174 // unsets focus on the TextField when it becomes invisible
175@@ -224,6 +252,16 @@
176 highlighted: true
177
178 onAccepted: if (!internal.idle) internal.validate()
179+
180+ Keys.onReturnPressed: {
181+ if (!findInPageMode) {
182+ accepted()
183+ } else if (event.modifiers & Qt.ShiftModifier) {
184+ findController.previous()
185+ } else {
186+ findController.next()
187+ }
188+ }
189 }
190
191 // Make sure that all the text is selected at the first click
192@@ -233,6 +271,7 @@
193 leftMargin: icons.width
194 rightMargin: bookmarkToggle.width
195 }
196+
197 enabled: !addressbar.activeFocus
198 onClicked: {
199 textField.forceActiveFocus()
200@@ -309,6 +348,7 @@
201 }
202
203 onEditingChanged: {
204+ if (findInPageMode) return
205 if (editing && internal.simplified) {
206 text = actualUrl
207 internal.simplified = false
208@@ -324,7 +364,7 @@
209 }
210
211 onCanSimplifyTextChanged: {
212- if (editing) return
213+ if (editing || findInPageMode) return
214 if (canSimplifyText && !loading && actualUrl.toString()) {
215 text = internal.simplifyUrl(actualUrl)
216 internal.simplified = true
217@@ -335,7 +375,7 @@
218 }
219
220 onActualUrlChanged: {
221- if (editing && actualUrl.toString()) return
222+ if ((editing && actualUrl.toString()) || findInPageMode) return
223 if (canSimplifyText) {
224 text = internal.simplifyUrl(actualUrl)
225 internal.simplified = true
226@@ -346,7 +386,7 @@
227 }
228
229 onRequestedUrlChanged: {
230- if (editing) return
231+ if (editing || findInPageMode) return
232 if (canSimplifyText) {
233 text = internal.simplifyUrl(requestedUrl)
234 internal.simplified = true
235@@ -356,6 +396,17 @@
236 }
237 }
238
239+ onFindInPageModeChanged: {
240+ if (findInPageMode) return
241+ if (canSimplifyText) {
242+ text = internal.simplifyUrl(actualUrl)
243+ internal.simplified = true
244+ } else {
245+ text = actualUrl
246+ internal.simplified = false
247+ }
248+ }
249+
250 function showSecurityCertificateDetails() {
251 if (!internal.securityCertificateDetails) {
252 internal.securityCertificateDetails = PopupUtils.open(Qt.resolvedUrl("SecurityCertificatePopover.qml"), certificatePopoverPositioner, {"securityStatus": securityStatus})
253
254=== modified file 'src/app/webbrowser/Browser.qml'
255--- src/app/webbrowser/Browser.qml 2015-08-10 15:33:43 +0000
256+++ src/app/webbrowser/Browser.qml 2015-08-11 11:17:03 +0000
257@@ -29,7 +29,6 @@
258 import "../UrlUtils.js" as UrlUtils
259 import "urlManagement.js" as UrlManagement
260
261-
262 BrowserView {
263 id: browser
264
265@@ -103,6 +102,13 @@
266 Actions.ClearHistory {
267 enabled: browser.historyModel
268 onTriggered: browser.historyModel.clearAll()
269+ },
270+ Actions.FindInPage {
271+ enabled: !chrome.findInPageMode
272+ onTriggered: {
273+ chrome.findInPageMode = true
274+ chrome.focus = true
275+ }
276 }
277 ]
278
279@@ -301,6 +307,8 @@
280
281 onRequestNewTab: browser.openUrlInNewTab("", true)
282
283+ onFindInPageModeChanged: if (!chrome.findInPageMode) internal.resetFocus()
284+
285 anchors {
286 left: parent.left
287 right: parent.right
288@@ -342,12 +350,13 @@
289 onTriggered: browser.openUrlInNewTab("", true)
290 },
291 Action {
292- objectName: "settings"
293- text: i18n.tr("Settings")
294- iconName: "settings"
295+ objectName: "findinpage"
296+ text: i18n.tr("Find in page")
297+ iconName: "search"
298+ enabled: !chrome.findInPageMode
299 onTriggered: {
300- settingsComponent.createObject(settingsContainer)
301- settingsContainer.focus = true
302+ chrome.findInPageMode = true
303+ chrome.focus = true
304 }
305 },
306 Action {
307@@ -367,6 +376,15 @@
308 browser.incognito = true
309 }
310 }
311+ },
312+ Action {
313+ objectName: "settings"
314+ text: i18n.tr("Settings")
315+ iconName: "settings"
316+ onTriggered: {
317+ settingsComponent.createObject(settingsContainer)
318+ settingsContainer.focus = true
319+ }
320 }
321 ]
322
323@@ -374,7 +392,13 @@
324 editing: activeFocus || suggestionsList.activeFocus
325
326 Keys.onDownPressed: if (suggestionsList.count) suggestionsList.focus = true
327- Keys.onEscapePressed: internal.resetFocus()
328+ Keys.onEscapePressed: {
329+ if (chrome.findInPageMode) {
330+ chrome.findInPageMode = false
331+ } else {
332+ internal.resetFocus()
333+ }
334+ }
335 }
336
337 ChromeController {
338@@ -387,7 +411,8 @@
339
340 Suggestions {
341 id: suggestionsList
342- opacity: ((chrome.state == "shown") && (activeFocus || chrome.activeFocus) && count > 0 && !chrome.drawerOpen) ? 1.0 : 0.0
343+ opacity: ((chrome.state == "shown") && (activeFocus || chrome.activeFocus) &&
344+ (count > 0) && !chrome.drawerOpen && !chrome.findInPageMode) ? 1.0 : 0.0
345 Behavior on opacity {
346 UbuntuNumberAnimation {}
347 }
348@@ -437,7 +462,7 @@
349 terms: suggestionsList.searchTerms
350 searchEngine: currentSearchEngine
351 active: (chrome.activeFocus || suggestionsList.activeFocus) &&
352- !browser.incognito &&
353+ !browser.incognito && !chrome.findInPageMode &&
354 !UrlManagement.looksLikeAUrl(chrome.text.replace(/ /g, "+"))
355
356 function limit(number) {
357@@ -897,6 +922,10 @@
358 }
359
360 onLoadEvent: {
361+ if (event.type == Oxide.LoadEvent.TypeCommitted) {
362+ chrome.findInPageMode = false
363+ }
364+
365 if (webviewimpl.incognito) {
366 return
367 }
368@@ -1277,6 +1306,7 @@
369 Connections {
370 target: tabsModel
371 onCurrentTabChanged: {
372+ chrome.findInPageMode = false
373 var tab = tabsModel.currentTab
374 if (tab) {
375 tab.load()
376@@ -1466,5 +1496,15 @@
377 enabled: chrome.visible
378 onTriggered: if (currentWebview) currentWebview.reload()
379 }
380+
381+ // Ctrl + F: Find in Page
382+ KeyboardShortcut {
383+ modifiers: Qt.ControlModifier
384+ key: Qt.Key_F
385+ onTriggered: {
386+ chrome.findInPageMode = true
387+ chrome.focus = true
388+ }
389+ }
390 }
391 }
392
393=== modified file 'src/app/webbrowser/Chrome.qml'
394--- src/app/webbrowser/Chrome.qml 2015-08-10 15:22:00 +0000
395+++ src/app/webbrowser/Chrome.qml 2015-08-11 11:17:03 +0000
396@@ -31,6 +31,7 @@
397 property alias drawerOpen: navigationBar.drawerOpen
398 property alias requestedUrl: navigationBar.requestedUrl
399 property alias canSimplifyText: navigationBar.canSimplifyText
400+ property alias findInPageMode: navigationBar.findInPageMode
401 property alias editing: navigationBar.editing
402 property alias incognito: navigationBar.incognito
403 property alias showTabsBar: tabsBar.active
404
405=== modified file 'src/app/webbrowser/NavigationBar.qml'
406--- src/app/webbrowser/NavigationBar.qml 2015-08-10 15:22:00 +0000
407+++ src/app/webbrowser/NavigationBar.qml 2015-08-11 11:17:03 +0000
408@@ -31,12 +31,16 @@
409 readonly property bool drawerOpen: internal.openDrawer
410 property alias requestedUrl: addressbar.requestedUrl
411 property alias canSimplifyText: addressbar.canSimplifyText
412+ property alias findInPageMode: addressbar.findInPageMode
413 property alias editing: addressbar.editing
414 property alias incognito: addressbar.incognito
415 property alias showFaviconInAddressBar: addressbar.showFavicon
416 readonly property alias bookmarkTogglePlaceHolder: addressbar.bookmarkTogglePlaceHolder
417 property color iconColor: UbuntuColors.darkGrey
418
419+ onFindInPageModeChanged: if (findInPageMode) addressbar.text = ""
420+ onIncognitoChanged: findInPageMode = false
421+
422 function selectAll() {
423 addressbar.selectAll()
424 }
425@@ -65,8 +69,8 @@
426 verticalCenter: parent.verticalCenter
427 }
428
429- enabled: webview ? webview.canGoBack : false
430- onTriggered: webview.goBack()
431+ enabled: findInPageMode || (webview ? webview.canGoBack : false)
432+ onTriggered: findInPageMode ? (findInPageMode = false) : webview.goBack()
433 }
434
435 ChromeButton {
436@@ -86,7 +90,8 @@
437 verticalCenter: parent.verticalCenter
438 }
439
440- enabled: webview ? webview.canGoForward : false
441+ enabled: findInPageMode ? false :
442+ (webview ? webview.canGoForward : false)
443 onTriggered: webview.goForward()
444 }
445
446@@ -95,10 +100,13 @@
447
448 focus: true
449
450+ findInPageMode: findInPageMode
451+ findController: webview ? webview.findController : null
452+
453 anchors {
454 left: forwardButton.right
455 leftMargin: units.gu(1)
456- right: drawerButton.left
457+ right: rightButtonsBar.left
458 rightMargin: units.gu(1)
459 verticalCenter: parent.verticalCenter
460 }
461@@ -108,8 +116,10 @@
462 loading: webview ? webview.loading : false
463
464 onValidated: {
465- webview.forceActiveFocus()
466- webview.url = requestedUrl
467+ if (!findInPageMode) {
468+ webview.forceActiveFocus()
469+ webview.url = requestedUrl
470+ }
471 }
472 onRequestReload: {
473 webview.forceActiveFocus()
474@@ -130,26 +140,67 @@
475 }
476 }
477
478- ChromeButton {
479- id: drawerButton
480- objectName: "drawerButton"
481-
482- iconName: "contextual-menu"
483- iconSize: 0.5 * height
484- iconColor: root.iconColor
485-
486- height: root.height
487- width: height * 0.8
488+ Row {
489+ id: rightButtonsBar
490
491 anchors {
492 right: parent.right
493- verticalCenter: parent.verticalCenter
494- }
495-
496- onTriggered: {
497- if (!internal.openDrawer) {
498- internal.openDrawer = drawerComponent.createObject(chrome)
499- internal.openDrawer.opened = true
500+ top: parent.top
501+ bottom: parent.bottom
502+ }
503+
504+ ChromeButton {
505+ id: findPreviousButton
506+ objectName: "findPreviousButton"
507+
508+ iconName: "up"
509+ iconSize: 0.5 * height
510+
511+ height: root.height
512+ width: height * 0.8
513+
514+ anchors.verticalCenter: parent.verticalCenter
515+
516+ visible: findInPageMode
517+ enabled: webview && webview.findController && webview.findController.count > 1
518+ onTriggered: webview.findController.previous()
519+ }
520+
521+ ChromeButton {
522+ id: findNextButton
523+ objectName: "findNextButton"
524+
525+ iconName: "down"
526+ iconSize: 0.5 * height
527+
528+ height: root.height
529+ width: height * 0.8
530+
531+ anchors.verticalCenter: parent.verticalCenter
532+
533+ visible: findInPageMode
534+ enabled: webview && webview.findController && webview.findController.count > 1
535+ onTriggered: webview.findController.next()
536+ }
537+
538+ ChromeButton {
539+ id: drawerButton
540+ objectName: "drawerButton"
541+
542+ iconName: "contextual-menu"
543+ iconSize: 0.5 * height
544+ iconColor: root.iconColor
545+
546+ height: root.height
547+ width: height * 0.8
548+
549+ anchors.verticalCenter: parent.verticalCenter
550+
551+ onTriggered: {
552+ if (!internal.openDrawer) {
553+ internal.openDrawer = drawerComponent.createObject(chrome)
554+ internal.openDrawer.opened = true
555+ }
556 }
557 }
558 }
559
560=== modified file 'tests/autopilot/webbrowser_app/emulators/browser.py'
561--- tests/autopilot/webbrowser_app/emulators/browser.py 2015-08-10 16:15:43 +0000
562+++ tests/autopilot/webbrowser_app/emulators/browser.py 2015-08-11 11:17:03 +0000
563@@ -230,6 +230,14 @@
564 def get_tabs_bar(self):
565 return self.select_single(TabsBar)
566
567+ def get_find_next_button(self):
568+ return self.select_single("ChromeButton",
569+ objectName="findNextButton")
570+
571+ def get_find_prev_button(self):
572+ return self.select_single("ChromeButton",
573+ objectName="findPreviousButton")
574+
575
576 class AddressBar(uitk.UbuntuUIToolkitCustomProxyObjectBase):
577
578@@ -267,6 +275,9 @@
579 return self.select_single("QQuickMouseArea",
580 objectName="bookmarkToggle")
581
582+ def get_find_in_page_counter(self):
583+ return self.select_single("Label", objectName="findInPageCounter")
584+
585
586 class TabsBar(uitk.UbuntuUIToolkitCustomProxyObjectBase):
587
588
589=== modified file 'tests/autopilot/webbrowser_app/tests/http_server.py'
590--- tests/autopilot/webbrowser_app/tests/http_server.py 2015-05-26 12:30:23 +0000
591+++ tests/autopilot/webbrowser_app/tests/http_server.py 2015-08-11 11:17:03 +0000
592@@ -49,6 +49,12 @@
593 body = "<p>test page 2</p>"
594 html = self.make_html(title, body)
595 self.send_html(html)
596+ elif self.path == "/link":
597+ self.send_response(200)
598+ html = '<html><body style="margin: 0">'
599+ html += '<a href="/test1"><div style="height: 100%"></div></a>'
600+ html += '</body></html>'
601+ self.send_html(html)
602 elif self.path.startswith("/wait/"):
603 delay = int(self.path[6:])
604 self.send_response(200)
605@@ -132,6 +138,11 @@
606 html += '<div style="height: 100%"></div>'
607 html += '</a></body></html>'
608 self.send_html(html)
609+ elif self.path == "/findinpage":
610+ # send a page with some searchable text
611+ self.send_response(200)
612+ html = '<html><body>hello this is text and more text</body></html>'
613+ self.send_html(html)
614 elif self.path.startswith("/suggest"):
615 self.send_response(200)
616 self.send_header("Content-Type", "text/x-suggestions+json")
617
618=== added file 'tests/autopilot/webbrowser_app/tests/test_findinpage.py'
619--- tests/autopilot/webbrowser_app/tests/test_findinpage.py 1970-01-01 00:00:00 +0000
620+++ tests/autopilot/webbrowser_app/tests/test_findinpage.py 2015-08-11 11:17:03 +0000
621@@ -0,0 +1,156 @@
622+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
623+#
624+# Copyright 2015 Canonical
625+#
626+# This program is free software: you can redistribute it and/or modify it
627+# under the terms of the GNU General Public License version 3, as published
628+# by the Free Software Foundation.
629+#
630+# This program is distributed in the hope that it will be useful,
631+# but WITHOUT ANY WARRANTY; without even the implied warranty of
632+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
633+# GNU General Public License for more details.
634+#
635+# You should have received a copy of the GNU General Public License
636+# along with this program. If not, see <http://www.gnu.org/licenses/>.
637+
638+from autopilot.matchers import Eventually
639+from testtools.matchers import Equals
640+
641+from webbrowser_app.tests import StartOpenRemotePageTestCaseBase
642+
643+
644+class TestFindInPage(StartOpenRemotePageTestCaseBase):
645+
646+ """Tests the find in page functionality."""
647+
648+ def setUp(self):
649+ super().setUp()
650+ self.chrome = self.main_window.chrome
651+ self.url = self.base_url + "/findinpage"
652+
653+ def activate_find_in_page(self, navigateFirst=True):
654+ if navigateFirst:
655+ self.main_window.go_to_url(self.url)
656+ self.main_window.wait_until_page_loaded(self.url)
657+
658+ drawer_button = self.chrome.get_drawer_button()
659+ self.pointing_device.click_object(drawer_button)
660+ self.chrome.get_drawer()
661+ action = self.chrome.get_drawer_action("findinpage")
662+ self.pointing_device.click_object(action)
663+
664+ def test_activation(self):
665+ next = self.chrome.get_find_next_button()
666+ prev = self.chrome.get_find_prev_button()
667+ bar = self.chrome.address_bar
668+ counter = bar.get_find_in_page_counter()
669+
670+ self.assertThat(bar.findInPageMode, Equals(False))
671+ self.assertThat(next.visible, Equals(False))
672+ self.assertThat(prev.visible, Equals(False))
673+ self.assertThat(counter.visible, Equals(False))
674+ self.assertThat(bar.activeFocus, Equals(False))
675+
676+ previous_text = bar.text
677+
678+ self.activate_find_in_page(False)
679+ self.assertThat(bar.findInPageMode, Eventually(Equals(True)))
680+ self.assertThat(next.visible, Eventually(Equals(True)))
681+ self.assertThat(prev.visible, Eventually(Equals(True)))
682+ self.assertThat(counter.visible, Eventually(Equals(True)))
683+ self.assertThat(bar.activeFocus, Eventually(Equals(True)))
684+
685+ self.assertThat(self.chrome.is_back_button_enabled(), Equals(True))
686+ self.assertThat(self.chrome.is_forward_button_enabled(), Equals(False))
687+ self.chrome.go_back()
688+
689+ self.assertThat(bar.findInPageMode, Eventually(Equals(False)))
690+ self.assertThat(next.visible, Eventually(Equals(False)))
691+ self.assertThat(prev.visible, Eventually(Equals(False)))
692+ self.assertThat(counter.visible, Eventually(Equals(False)))
693+ self.assertThat(bar.activeFocus, Eventually(Equals(False)))
694+ self.assertThat(bar.text, Eventually(Equals(previous_text)))
695+
696+ def test_counter(self):
697+ bar = self.chrome.address_bar
698+ counter = bar.get_find_in_page_counter()
699+
700+ self.activate_find_in_page()
701+ bar.write("text")
702+ self.assertThat(counter.current, Eventually(Equals(1)))
703+ self.assertThat(counter.count, Eventually(Equals(2)))
704+
705+ bar.write("hello")
706+ self.assertThat(counter.current, Eventually(Equals(1)))
707+ self.assertThat(counter.count, Eventually(Equals(1)))
708+
709+ bar.write("")
710+ self.assertThat(counter.current, Eventually(Equals(0)))
711+ self.assertThat(counter.count, Eventually(Equals(0)))
712+
713+ def test_navigation(self):
714+ bar = self.chrome.address_bar
715+ counter = bar.get_find_in_page_counter()
716+ next = self.chrome.get_find_next_button()
717+ prev = self.chrome.get_find_prev_button()
718+
719+ self.activate_find_in_page()
720+ self.assertThat(next.enabled, Eventually(Equals(False)))
721+ self.assertThat(prev.enabled, Eventually(Equals(False)))
722+
723+ bar.write("text")
724+ self.assertThat(next.enabled, Eventually(Equals(True)))
725+ self.assertThat(prev.enabled, Eventually(Equals(True)))
726+
727+ self.pointing_device.click_object(next)
728+ self.assertThat(counter.current, Eventually(Equals(2)))
729+ self.assertThat(counter.count, Eventually(Equals(2)))
730+ self.pointing_device.click_object(next)
731+ self.assertThat(counter.current, Eventually(Equals(1)))
732+ self.assertThat(counter.count, Eventually(Equals(2)))
733+ self.pointing_device.click_object(prev)
734+ self.assertThat(counter.current, Eventually(Equals(2)))
735+ self.assertThat(counter.count, Eventually(Equals(2)))
736+ self.pointing_device.click_object(next)
737+ self.assertThat(counter.current, Eventually(Equals(1)))
738+ self.assertThat(counter.count, Eventually(Equals(2)))
739+
740+ bar.write("hello")
741+ self.assertThat(next.enabled, Eventually(Equals(False)))
742+ self.assertThat(prev.enabled, Eventually(Equals(False)))
743+
744+ def test_navigation_exits_findinpage_mode(self):
745+ url = self.base_url + "/link"
746+ self.main_window.go_to_url(url)
747+ self.main_window.wait_until_page_loaded(url)
748+ self.activate_find_in_page(False)
749+ bar = self.chrome.address_bar
750+ self.assertThat(bar.findInPageMode, Eventually(Equals(True)))
751+ webview = self.main_window.get_current_webview()
752+ self.pointing_device.click_object(webview)
753+ self.assertThat(bar.findInPageMode, Eventually(Equals(False)))
754+
755+ def test_opening_new_tab_exits_findinpage_mode(self):
756+ self.activate_find_in_page(False)
757+ bar = self.chrome.address_bar
758+ self.assertThat(bar.findInPageMode, Eventually(Equals(True)))
759+ if not self.main_window.wide:
760+ # Remove focus from the address bar to hide the OSK
761+ # (that would otherwise prevent a bottom edge swipe gesture)
762+ webview = self.main_window.get_current_webview()
763+ self.pointing_device.click_object(webview)
764+ self.open_tabs_view()
765+ self.open_new_tab()
766+ self.assertThat(bar.findInPageMode, Eventually(Equals(False)))
767+
768+ def test_navigation_in_new_tab_exits_findinpage_mode(self):
769+ url = self.base_url + "/blanktargetlink"
770+ self.main_window.go_to_url(url)
771+ self.main_window.wait_until_page_loaded(url)
772+ self.activate_find_in_page(False)
773+ bar = self.chrome.address_bar
774+ self.assertThat(bar.findInPageMode, Eventually(Equals(True)))
775+ webview = self.main_window.get_current_webview()
776+ self.pointing_device.click_object(webview)
777+ self.assertThat(bar.findInPageMode, Eventually(Equals(False)))
778
779=== modified file 'tests/autopilot/webbrowser_app/tests/test_keyboard.py'
780--- tests/autopilot/webbrowser_app/tests/test_keyboard.py 2015-07-03 15:06:08 +0000
781+++ tests/autopilot/webbrowser_app/tests/test_keyboard.py 2015-08-11 11:17:03 +0000
782@@ -294,3 +294,11 @@
783 settings.wait_until_destroyed()
784 webview = self.main_window.get_current_webview()
785 self.assertThat(webview.activeFocus, Eventually(Equals(True)))
786+
787+ def test_find_in_page_ctrl_f(self):
788+ address_bar = self.main_window.chrome.address_bar
789+ self.assertThat(address_bar.findInPageMode, Equals(False))
790+ self.main_window.press_key('Ctrl+F')
791+ self.assertThat(address_bar.findInPageMode, Eventually(Equals(True)))
792+ self.main_window.press_key('Escape')
793+ self.assertThat(address_bar.findInPageMode, Eventually(Equals(False)))
794
795=== modified file 'tests/unittests/qml/tst_AddressBar.qml'
796--- tests/unittests/qml/tst_AddressBar.qml 2015-08-10 15:22:00 +0000
797+++ tests/unittests/qml/tst_AddressBar.qml 2015-08-11 11:17:03 +0000
798@@ -42,6 +42,11 @@
799
800 editing: activeFocus
801 canSimplifyText: true
802+
803+ findController: QtObject {
804+ property int current
805+ property int count
806+ }
807 }
808
809 // only exists to steal focus from the address bar
810@@ -367,5 +372,15 @@
811 clickItem(addressBar)
812 compare(addressBar.text, url)
813 }
814+
815+ function test_exitingFindInPageRestoresUrl() {
816+ addressBar.actualUrl = "http://example.org/"
817+ addressBar.findInPageMode = true
818+ verify(addressBar.activeFocus)
819+ compare(addressBar.text, "")
820+ typeString("hello")
821+ addressBar.findInPageMode = false
822+ compare(addressBar.text, "example.org")
823+ }
824 }
825 }

Subscribers

People subscribed via source and target branches

to status/vote changes: