Merge lp:~system-apps-team/webbrowser-app/multiple-windows into lp:webbrowser-app
- multiple-windows
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Olivier Tilloy |
Approved revision: | 1515 |
Merged at revision: | 1536 |
Proposed branch: | lp:~system-apps-team/webbrowser-app/multiple-windows |
Merge into: | lp:webbrowser-app |
Diff against target: |
2873 lines (+946/-1059) 35 files modified
src/Ubuntu/Web/UbuntuWebContext.qml (+5/-2) src/Ubuntu/Web/UserAgent02.qml (+3/-3) src/app/BrowserView.qml (+2/-5) src/app/BrowserWindow.qml (+0/-13) src/app/CMakeLists.txt (+0/-1) src/app/actions/OpenLinkInNewWindow.qml (+23/-0) src/app/actions/OpenLinkInPrivateWindow.qml (+23/-0) src/app/browserapplication.cpp (+10/-45) src/app/browserapplication.h (+3/-7) src/app/webbrowser-window.cpp (+0/-51) src/app/webbrowser-window.h (+0/-57) src/app/webbrowser/Browser.qml (+95/-418) src/app/webbrowser/BrowserTab.qml (+6/-0) src/app/webbrowser/LeavePrivateModeDialog.qml (+0/-43) src/app/webbrowser/webbrowser-app.cpp (+34/-6) src/app/webbrowser/webbrowser-app.desktop.in.in (+9/-0) src/app/webbrowser/webbrowser-app.h (+4/-1) src/app/webbrowser/webbrowser-app.qml (+424/-49) src/app/webcontainer/WebApp.qml (+2/-0) src/app/webcontainer/WebViewImplOxide.qml (+5/-7) src/app/webcontainer/WebappContainerWebview.qml (+5/-1) src/app/webcontainer/webapp-container.cpp (+51/-26) src/app/webcontainer/webapp-container.h (+7/-2) src/app/webcontainer/webapp-container.qml (+12/-3) tests/autopilot/webbrowser_app/__init__.py (+4/-1) tests/autopilot/webbrowser_app/emulators/browser.py (+0/-55) tests/autopilot/webbrowser_app/tests/__init__.py (+22/-0) tests/autopilot/webbrowser_app/tests/test_contextmenu.py (+29/-0) tests/autopilot/webbrowser_app/tests/test_fullscreen.py (+0/-54) tests/autopilot/webbrowser_app/tests/test_keyboard.py (+22/-0) tests/autopilot/webbrowser_app/tests/test_multiple_windows.py (+40/-0) tests/autopilot/webbrowser_app/tests/test_new_tab_view.py (+0/-49) tests/autopilot/webbrowser_app/tests/test_private.py (+0/-115) tests/autopilot/webbrowser_app/tests/test_tabs.py (+0/-45) tests/unittests/qml/tst_BrowserWindow.qml (+106/-0) |
To merge this branch: | bzr merge lp:~system-apps-team/webbrowser-app/multiple-windows |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Hayzen (community) | Approve | ||
system-apps-ci-bot | continuous-integration | Needs Fixing | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+303310@code.launchpad.net |
Commit message
Multiple window support in webbrowser-app.
Description of the change
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
- 1486. By Olivier Tilloy
-
Add static unity quicklist entries to the desktop file to open a new [private] window.
- 1487. By Olivier Tilloy
-
Remove an unused QML import.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1486
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1487
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1488. By Olivier Tilloy
-
Add keyboard shortcuts to open a new window (Ctrl+N) and a new private window (Ctrl+Shift+N).
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1488
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1489. By Olivier Tilloy
-
The Window.width attached property is new in Qt 5.5, and we need to support Qt 5.4.
Use Window.contentItem. width instead. - 1490. By Olivier Tilloy
-
Do not handle ESC as a window-level shortcut as it would take precedence over key events in web content.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1489
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1490
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1491. By Olivier Tilloy
-
Merge the latest changes from trunk and resolve a minor conflict.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1491
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1492. By Andrew Hayzen
-
* Fix for ua-overrides not being loaded on mobile due to screenDiagonal being 0 to start with and then not triggering a reload when a valid value appears
- 1493. By Andrew Hayzen
-
* Rename smallScreen to screenSize
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1492
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1493
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1494. By Andrew Hayzen
-
* Display the current tab title in the window title
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1494
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1495. By Andrew Hayzen
-
* New windows copy the size of the window that they are created from
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1495
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1496. By Andrew Hayzen
-
* Save the session when the first window closes and delay further saves incase the application is quiting
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1496
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1497. By Olivier Tilloy
-
Revert window title to the current trunk implementation.
- 1498. By Olivier Tilloy
-
Inline the definition of screenSize.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1497
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1498
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1499. By Andrew Hayzen
-
* Delay focusing of webview until it has loaded, fixes issue where html text fields aren't focused on startup
- 1500. By Andrew Hayzen
-
* Revert workaround for now, until we find a full solution
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1499
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1500
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1501. By Olivier Tilloy
-
Revert revision 1496 as this workaround is not really needed.
It turns out that unity7 doesn’t actually request an application to quit, it simply closes all its windows one by one.
So there is no way to reliably know that the application is shutting down, and we should just ignore this case, like firefox does. - 1502. By Olivier Tilloy
-
Add a keyboard shortcut to quit the application (thus properly saving the current session and all its open windows).
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1502
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1503. By Olivier Tilloy
-
Remove autopilot tests that correspond to a feature that was removed.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1503
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 1504. By Olivier Tilloy
-
Restore the code that cleans unused tab previews at startup.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1504
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1505. By Olivier Tilloy
-
Do not close the window when the last open tab is closed in narrow layout.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1505
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1506. By Olivier Tilloy
-
Restore Andrew’s workaround for the focus issue, until we figure out a proper fix.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1506
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1507. By Olivier Tilloy
-
Remove unused code.
- 1508. By Olivier Tilloy
-
Use QQmlProperty everywhere, for consistency.
- 1509. By Olivier Tilloy
-
Re-implement the unity webapps proxy’s onAppRaised function.
- 1510. By Andrew Hayzen
-
* Revert workaround for focus issue, now change forceActiveFocus() in resetFocus() to instead set the focus to true of the FocusScope
- 1511. By Olivier Tilloy
-
Do not attempt to save the current session if it’s currently being restored.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1509
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1512. By Olivier Tilloy
-
Do not change the order of tabs while restoring the current session or if the window is not visible yet.
- 1513. By Olivier Tilloy
-
Remove unused code.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1511
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1514. By Olivier Tilloy
-
New, basic autopilot tests for multiple windows.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1513
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1514
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 1515. By Olivier Tilloy
-
Do not test windows’ activeFocus as this appears to be broken on phones at the moment (most likely bug #1623861).
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1515
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Andrew Hayzen (ahayzen) wrote : | # |
Look good so far, two inline comments.
L1609 1) Does this case need a window.
L1906 2) is window always valid? Or should this have a check around it?
There are a few TODO, FIXME and XXX's added are these all being fixed in future branches?
L1469 // FIXME: do this asynchronously
L1715 // TODO: do we want to save/restore window positions too (https:/
L1888 // XXX: Ideally, we would use the Window.window
// attached property, but it is new in Qt 5.7.
I don't see any tests specifically for multiple window session restore, do you think there should be some or are the general session storage tests OK?
Olivier Tilloy (osomon) wrote : | # |
I replied inline to your comments.
Regarding the TODO and FIXME, yes those are things we want to address in followup branches.
There’s not much we can do about the XXX until we move to Qt 5.7 (but that doesn’t matter as we have a clean solution until then).
Regarding the tests: we already didn’t have tests for session restore with one single window, because the way autopilot drives an application makes it hard/impossible to launch it twice in the same test (whenever the application exits autopilot thinks the test is over).
Ideally, this should be tested with integration tests of course. Something to think about for a followup branch, IÂ would say.
Andrew Hayzen (ahayzen) wrote : | # |
All my comments have been satisfied. LGTM :-)
Preview Diff
1 | === modified file 'src/Ubuntu/Web/UbuntuWebContext.qml' |
2 | --- src/Ubuntu/Web/UbuntuWebContext.qml 2016-06-14 18:49:01 +0000 |
3 | +++ src/Ubuntu/Web/UbuntuWebContext.qml 2016-09-21 07:59:37 +0000 |
4 | @@ -70,13 +70,16 @@ |
5 | ] |
6 | |
7 | property QtObject __ua: UserAgent02 { |
8 | - onSmallScreenChanged: reloadOverrides() |
9 | + onScreenSizeChanged: reloadOverrides() |
10 | Component.onCompleted: reloadOverrides() |
11 | |
12 | property string _target: "" |
13 | |
14 | function reloadOverrides() { |
15 | - var target = smallScreen ? "mobile" : "desktop" |
16 | + if (screenSize === "unknown") { |
17 | + return |
18 | + } |
19 | + var target = screenSize === "small" ? "mobile" : "desktop" |
20 | if (target == _target) return |
21 | _target = target |
22 | var script = "ua-overrides-%1.js".arg(target) |
23 | |
24 | === modified file 'src/Ubuntu/Web/UserAgent02.qml' |
25 | --- src/Ubuntu/Web/UserAgent02.qml 2016-06-03 08:08:45 +0000 |
26 | +++ src/Ubuntu/Web/UserAgent02.qml 2016-09-21 07:59:37 +0000 |
27 | @@ -31,7 +31,7 @@ |
28 | QtObject { |
29 | // Empirical value: screens smaller than 19cm are considered small enough that a |
30 | // mobile UA string is used, screens bigger than that will get desktop content. |
31 | - readonly property bool smallScreen: screenDiagonal < 190 |
32 | + readonly property string screenSize: (screenDiagonal === 0) ? "unknown" : (screenDiagonal > 0 && screenDiagonal < 190) ? "small" : "large" |
33 | |
34 | // %1: Ubuntu version, e.g. "14.04" |
35 | // %2: optional token to specify further attributes of the platform, e.g. "like Android" |
36 | @@ -48,7 +48,7 @@ |
37 | // difference in the content served by certain sites (e.g. gmail.com) |
38 | readonly property string _template: "Mozilla/5.0 (Linux; Ubuntu %1%2%3) AppleWebKit/%4 Chromium/%5 %6Safari/%7%8" |
39 | |
40 | - readonly property string _attributes: smallScreen ? "like Android 4.4" : "" |
41 | + readonly property string _attributes: screenSize === "small" ? "like Android 4.4" : "" |
42 | |
43 | readonly property string _hardwareID: "" |
44 | |
45 | @@ -57,7 +57,7 @@ |
46 | |
47 | readonly property string _chromiumVersion: Oxide.chromiumVersion |
48 | |
49 | - readonly property string _formFactor: smallScreen ? "Mobile" : "" |
50 | + readonly property string _formFactor: screenSize === "small" ? "Mobile" : "" |
51 | |
52 | readonly property string _more: "" |
53 | |
54 | |
55 | === modified file 'src/app/BrowserView.qml' |
56 | --- src/app/BrowserView.qml 2016-06-16 18:21:58 +0000 |
57 | +++ src/app/BrowserView.qml 2016-09-21 07:59:37 +0000 |
58 | @@ -17,6 +17,7 @@ |
59 | */ |
60 | |
61 | import QtQuick 2.4 |
62 | +import QtQuick.Window 2.2 |
63 | import Ubuntu.Components 1.3 |
64 | import Ubuntu.Unity.Action 1.1 as UnityActions |
65 | import com.canonical.Oxide 1.15 as Oxide |
66 | @@ -27,16 +28,12 @@ |
67 | property var currentWebview: null |
68 | property string title: currentWebview ? currentWebview.title : "" |
69 | |
70 | - property var initialUrls |
71 | - |
72 | - property var webbrowserWindow: null |
73 | - |
74 | property var osk: _osk |
75 | |
76 | property bool hasTouchScreen: false |
77 | |
78 | // See http://design.canonical.com/2015/05/to-converge-onto-mobile-tablet-and-desktop-think-grid-units/ |
79 | - readonly property bool wide: width >= units.gu(90) |
80 | + readonly property bool wide: Window.contentItem.width >= units.gu(90) |
81 | |
82 | focus: true |
83 | |
84 | |
85 | === modified file 'src/app/BrowserWindow.qml' |
86 | --- src/app/BrowserWindow.qml 2016-05-25 15:46:41 +0000 |
87 | +++ src/app/BrowserWindow.qml 2016-09-21 07:59:37 +0000 |
88 | @@ -28,8 +28,6 @@ |
89 | property var currentWebview: null |
90 | property bool hasTouchScreen: false |
91 | |
92 | - signal openUrls(var urls) |
93 | - |
94 | contentOrientation: Screen.orientation |
95 | |
96 | minimumWidth: units.gu(50) |
97 | @@ -58,15 +56,4 @@ |
98 | } |
99 | } |
100 | } |
101 | - |
102 | - // Handle runtime requests to open urls as defined |
103 | - // by the freedesktop application dbus interface's open |
104 | - // method for DBUS application activation: |
105 | - // http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus |
106 | - // The dispatch on the org.freedesktop.Application if is done per appId at the |
107 | - // url-dispatcher/upstart level. |
108 | - Connections { |
109 | - target: UriHandler |
110 | - onOpened: window.openUrls(uris) |
111 | - } |
112 | } |
113 | |
114 | === modified file 'src/app/CMakeLists.txt' |
115 | --- src/app/CMakeLists.txt 2016-04-12 19:59:00 +0000 |
116 | +++ src/app/CMakeLists.txt 2016-09-21 07:59:37 +0000 |
117 | @@ -26,7 +26,6 @@ |
118 | mime-database.cpp |
119 | session-storage.cpp |
120 | single-instance-manager.cpp |
121 | - webbrowser-window.cpp |
122 | qquickshortcut.cpp |
123 | ) |
124 | |
125 | |
126 | === added file 'src/app/actions/OpenLinkInNewWindow.qml' |
127 | --- src/app/actions/OpenLinkInNewWindow.qml 1970-01-01 00:00:00 +0000 |
128 | +++ src/app/actions/OpenLinkInNewWindow.qml 2016-09-21 07:59:37 +0000 |
129 | @@ -0,0 +1,23 @@ |
130 | +/* |
131 | + * Copyright 2016 Canonical Ltd. |
132 | + * |
133 | + * This file is part of webbrowser-app. |
134 | + * |
135 | + * webbrowser-app is free software; you can redistribute it and/or modify |
136 | + * it under the terms of the GNU General Public License as published by |
137 | + * the Free Software Foundation; version 3. |
138 | + * |
139 | + * webbrowser-app is distributed in the hope that it will be useful, |
140 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
141 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
142 | + * GNU General Public License for more details. |
143 | + * |
144 | + * You should have received a copy of the GNU General Public License |
145 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
146 | + */ |
147 | + |
148 | +import Ubuntu.Components 1.3 |
149 | + |
150 | +Action { |
151 | + text: i18n.tr("Open link in new window") |
152 | +} |
153 | |
154 | === added file 'src/app/actions/OpenLinkInPrivateWindow.qml' |
155 | --- src/app/actions/OpenLinkInPrivateWindow.qml 1970-01-01 00:00:00 +0000 |
156 | +++ src/app/actions/OpenLinkInPrivateWindow.qml 2016-09-21 07:59:37 +0000 |
157 | @@ -0,0 +1,23 @@ |
158 | +/* |
159 | + * Copyright 2016 Canonical Ltd. |
160 | + * |
161 | + * This file is part of webbrowser-app. |
162 | + * |
163 | + * webbrowser-app is free software; you can redistribute it and/or modify |
164 | + * it under the terms of the GNU General Public License as published by |
165 | + * the Free Software Foundation; version 3. |
166 | + * |
167 | + * webbrowser-app is distributed in the hope that it will be useful, |
168 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
169 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
170 | + * GNU General Public License for more details. |
171 | + * |
172 | + * You should have received a copy of the GNU General Public License |
173 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
174 | + */ |
175 | + |
176 | +import Ubuntu.Components 1.3 |
177 | + |
178 | +Action { |
179 | + text: i18n.tr("Open link in private window") |
180 | +} |
181 | |
182 | === modified file 'src/app/browserapplication.cpp' |
183 | --- src/app/browserapplication.cpp 2016-06-16 13:30:01 +0000 |
184 | +++ src/app/browserapplication.cpp 2016-09-21 07:59:37 +0000 |
185 | @@ -29,8 +29,8 @@ |
186 | #include <QtQml/QQmlComponent> |
187 | #include <QtQml/QQmlContext> |
188 | #include <QtQml/QQmlEngine> |
189 | +#include <QtQml/QQmlProperty> |
190 | #include <QtQml/QtQml> |
191 | -#include <QtQuick/QQuickWindow> |
192 | |
193 | // local |
194 | #include "browserapplication.h" |
195 | @@ -40,16 +40,14 @@ |
196 | #include "mime-database.h" |
197 | #include "qquickshortcut_p.h" |
198 | #include "session-storage.h" |
199 | -#include "webbrowser-window.h" |
200 | |
201 | #include "Unity/InputInfo/qdeclarativeinputdevicemodel_p.h" |
202 | |
203 | BrowserApplication::BrowserApplication(int& argc, char** argv) |
204 | : QApplication(argc, argv) |
205 | , m_engine(0) |
206 | - , m_window(0) |
207 | , m_component(0) |
208 | - , m_webbrowserWindowProxy(0) |
209 | + , m_object(nullptr) |
210 | { |
211 | m_arguments = arguments(); |
212 | m_arguments.removeFirst(); |
213 | @@ -57,11 +55,7 @@ |
214 | |
215 | BrowserApplication::~BrowserApplication() |
216 | { |
217 | - if (m_webbrowserWindowProxy) { |
218 | - m_webbrowserWindowProxy->setWindow(NULL); |
219 | - } |
220 | - delete m_window; |
221 | - delete m_webbrowserWindowProxy; |
222 | + delete m_object; |
223 | delete m_component; |
224 | delete m_engine; |
225 | } |
226 | @@ -108,7 +102,7 @@ |
227 | bool BrowserApplication::initialize(const QString& qmlFileSubPath |
228 | , const QString& appId) |
229 | { |
230 | - Q_ASSERT(m_window == 0); |
231 | + Q_ASSERT(m_object == nullptr); |
232 | |
233 | if (helpRequested()) { |
234 | printUsage(); |
235 | @@ -195,7 +189,6 @@ |
236 | if (!isRunningInstalled()) { |
237 | m_engine->addImportPath(UbuntuBrowserImportsDirectory()); |
238 | } |
239 | - |
240 | qmlEngineCreated(m_engine); |
241 | |
242 | QQmlContext* context = m_engine->rootContext(); |
243 | @@ -208,15 +201,10 @@ |
244 | qWarning() << m_component->errorString(); |
245 | return false; |
246 | } |
247 | - m_webbrowserWindowProxy = new WebBrowserWindow(); |
248 | - context->setContextProperty("webbrowserWindowProxy", m_webbrowserWindowProxy); |
249 | - |
250 | - QObject* browser = m_component->beginCreate(context); |
251 | - m_window = qobject_cast<QQuickWindow*>(browser); |
252 | - m_webbrowserWindowProxy->setWindow(m_window); |
253 | - |
254 | - browser->setProperty("developerExtrasEnabled", inspectorEnabled); |
255 | - browser->setProperty("forceFullscreen", m_arguments.contains("--fullscreen")); |
256 | + |
257 | + m_object = m_component->beginCreate(context); |
258 | + |
259 | + QQmlProperty::write(m_object, QStringLiteral("developerExtrasEnabled"), inspectorEnabled); |
260 | |
261 | bool hasTouchScreen = false; |
262 | Q_FOREACH(const QTouchDevice* device, QTouchDevice::devices()) { |
263 | @@ -224,40 +212,17 @@ |
264 | hasTouchScreen = true; |
265 | } |
266 | } |
267 | - browser->setProperty("hasTouchScreen", hasTouchScreen); |
268 | + QQmlProperty::write(m_object, QStringLiteral("hasTouchScreen"), hasTouchScreen); |
269 | |
270 | return true; |
271 | } |
272 | |
273 | -void BrowserApplication::onNewInstanceLaunched(const QStringList& arguments) const |
274 | -{ |
275 | - QVariantList urls; |
276 | - Q_FOREACH(const QString& argument, arguments) { |
277 | - if (!argument.startsWith(QStringLiteral("-"))) { |
278 | - QUrl url = QUrl::fromUserInput(argument); |
279 | - if (url.isValid()) { |
280 | - urls.append(url); |
281 | - } |
282 | - } |
283 | - } |
284 | - QMetaObject::invokeMethod(m_window, "openUrls", Q_ARG(QVariant, QVariant(urls))); |
285 | - m_window->requestActivate(); |
286 | -} |
287 | - |
288 | void BrowserApplication::qmlEngineCreated(QQmlEngine*) |
289 | {} |
290 | |
291 | int BrowserApplication::run() |
292 | { |
293 | - Q_ASSERT(m_window != 0); |
294 | - |
295 | - if (m_arguments.contains("--fullscreen")) { |
296 | - m_window->showFullScreen(); |
297 | - } else if (m_arguments.contains("--maximized")) { |
298 | - m_window->showMaximized(); |
299 | - } else { |
300 | - m_window->show(); |
301 | - } |
302 | + Q_ASSERT(m_object != nullptr); |
303 | return exec(); |
304 | } |
305 | |
306 | |
307 | === modified file 'src/app/browserapplication.h' |
308 | --- src/app/browserapplication.h 2016-06-16 13:30:01 +0000 |
309 | +++ src/app/browserapplication.h 2016-09-21 07:59:37 +0000 |
310 | @@ -31,8 +31,6 @@ |
311 | |
312 | class QQmlComponent; |
313 | class QQmlEngine; |
314 | -class QQuickWindow; |
315 | -class WebBrowserWindow; |
316 | |
317 | // We want the browser to be QApplication based rather than QGuiApplication |
318 | // to provide a widget based file picker on the desktop, rather than the |
319 | @@ -58,18 +56,16 @@ |
320 | |
321 | QStringList m_arguments; |
322 | QQmlEngine* m_engine; |
323 | - QQuickWindow* m_window; |
324 | QQmlComponent* m_component; |
325 | + QObject* m_object; |
326 | |
327 | -private Q_SLOTS: |
328 | - void onNewInstanceLaunched(const QStringList& arguments) const; |
329 | +protected Q_SLOTS: |
330 | + virtual void onNewInstanceLaunched(const QStringList& arguments) const = 0; |
331 | |
332 | private: |
333 | QString inspectorPort() const; |
334 | QString inspectorHost() const; |
335 | |
336 | - WebBrowserWindow *m_webbrowserWindowProxy; |
337 | - |
338 | SingleInstanceManager m_singleton; |
339 | }; |
340 | |
341 | |
342 | === removed file 'src/app/webbrowser-window.cpp' |
343 | --- src/app/webbrowser-window.cpp 2013-09-14 13:49:29 +0000 |
344 | +++ src/app/webbrowser-window.cpp 1970-01-01 00:00:00 +0000 |
345 | @@ -1,51 +0,0 @@ |
346 | -/* |
347 | - * Copyright 2013 Canonical Ltd. |
348 | - * |
349 | - * This file is part of webbrowser-app. |
350 | - * |
351 | - * webbrowser-app is free software; you can redistribute it and/or modify |
352 | - * it under the terms of the GNU General Public License as published by |
353 | - * the Free Software Foundation; version 3. |
354 | - * |
355 | - * webbrowser-app is distributed in the hope that it will be useful, |
356 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
357 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
358 | - * GNU General Public License for more details. |
359 | - * |
360 | - * You should have received a copy of the GNU General Public License |
361 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
362 | - */ |
363 | - |
364 | -#include <QDebug> |
365 | -#include "webbrowser-window.h" |
366 | - |
367 | - |
368 | -WebBrowserWindow::WebBrowserWindow(QObject *parent) : |
369 | - QObject(parent), |
370 | - _window(0) |
371 | -{} |
372 | - |
373 | -QQuickWindow * WebBrowserWindow::window() const |
374 | -{ |
375 | - return _window; |
376 | -} |
377 | - |
378 | -void WebBrowserWindow::setWindow(QQuickWindow * window) |
379 | -{ |
380 | - if (_window != window) |
381 | - { |
382 | - _window = window; |
383 | - Q_EMIT windowChanged(window); |
384 | - } |
385 | -} |
386 | - |
387 | -void WebBrowserWindow::raise() |
388 | -{ |
389 | - if ( ! _window) |
390 | - return; |
391 | - |
392 | - _window->raise(); |
393 | - _window->show(); |
394 | -} |
395 | - |
396 | - |
397 | |
398 | === removed file 'src/app/webbrowser-window.h' |
399 | --- src/app/webbrowser-window.h 2013-09-14 13:49:29 +0000 |
400 | +++ src/app/webbrowser-window.h 1970-01-01 00:00:00 +0000 |
401 | @@ -1,57 +0,0 @@ |
402 | -/* |
403 | - * Copyright 2013 Canonical Ltd. |
404 | - * |
405 | - * This file is part of webbrowser-app. |
406 | - * |
407 | - * webbrowser-app is free software; you can redistribute it and/or modify |
408 | - * it under the terms of the GNU General Public License as published by |
409 | - * the Free Software Foundation; version 3. |
410 | - * |
411 | - * webbrowser-app is distributed in the hope that it will be useful, |
412 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
413 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
414 | - * GNU General Public License for more details. |
415 | - * |
416 | - * You should have received a copy of the GNU General Public License |
417 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
418 | - */ |
419 | - |
420 | -#ifndef __WEBBROWSER_WINDOW_H__ |
421 | -#define __WEBBROWSER_WINDOW_H__ |
422 | - |
423 | -#include <QObject> |
424 | -#include <QQuickWindow> |
425 | - |
426 | - |
427 | -class WebBrowserWindow : public QObject |
428 | -{ |
429 | - Q_OBJECT |
430 | - Q_PROPERTY(QQuickWindow *window READ window WRITE setWindow NOTIFY windowChanged) |
431 | - |
432 | - |
433 | -public: |
434 | - explicit WebBrowserWindow(QObject *parent = 0); |
435 | - |
436 | - |
437 | - QQuickWindow * window() const; |
438 | - void setWindow(QQuickWindow *); |
439 | - |
440 | - |
441 | -public slots: |
442 | - |
443 | - void raise(); |
444 | - |
445 | - |
446 | -signals: |
447 | - |
448 | - void windowChanged(QQuickWindow *); |
449 | - |
450 | - |
451 | -private: |
452 | - |
453 | - QQuickWindow * _window; |
454 | -}; |
455 | - |
456 | - |
457 | -#endif // __WEBBROWSER_WINDOW_H__ |
458 | - |
459 | |
460 | === modified file 'src/app/webbrowser/Browser.qml' |
461 | --- src/app/webbrowser/Browser.qml 2016-07-01 08:52:37 +0000 |
462 | +++ src/app/webbrowser/Browser.qml 2016-09-21 07:59:37 +0000 |
463 | @@ -38,30 +38,39 @@ |
464 | // Should be true when the containing window is fullscreen. |
465 | property bool fullscreen: false |
466 | |
467 | + property Settings settings |
468 | + |
469 | currentWebview: tabsModel && tabsModel.currentTab ? tabsModel.currentTab.webview : null |
470 | |
471 | - readonly property var downloadManager: (downloadHandlerLoader.status == Loader.Ready) ? downloadHandlerLoader.item : null |
472 | - |
473 | - property bool newSession: false |
474 | - |
475 | property bool incognito: false |
476 | |
477 | - readonly property var tabsModel: incognito ? privateTabsModelLoader.item : publicTabsModel |
478 | - |
479 | - // Restore only the n most recent tabs at startup, |
480 | - // to limit the overhead of instantiating too many |
481 | - // tab objects (see http://pad.lv/1376433). |
482 | - readonly property int maxTabsToRestore: 10 |
483 | - |
484 | - onTabsModelChanged: { |
485 | - if (incognito && privateTabsModelLoader.item) { |
486 | - browser.openUrlInNewTab("", true) |
487 | - } else if (!incognito && tabsModel.currentTab) { |
488 | - // If the system is low on memory, the current public tab might |
489 | - // have been unloaded while browsing incognito, so reload it. |
490 | - tabsModel.currentTab.load() |
491 | - } |
492 | - } |
493 | + property var tabsModel: TabsModel {} |
494 | + |
495 | + function serializeTabState(tab) { |
496 | + var state = {} |
497 | + state.uniqueId = tab.uniqueId |
498 | + state.url = tab.url.toString() |
499 | + state.title = tab.title |
500 | + state.icon = tab.icon.toString() |
501 | + state.preview = tab.preview.toString() |
502 | + state.savedState = tab.webview ? tab.webview.currentState : tab.restoreState |
503 | + return state |
504 | + } |
505 | + |
506 | + function restoreTabState(state) { |
507 | + var properties = {'initialUrl': state.url, 'initialTitle': state.title, |
508 | + 'uniqueId': state.uniqueId, 'initialIcon': state.icon, |
509 | + 'preview': state.preview, 'restoreState': state.savedState, |
510 | + 'restoreType': Oxide.WebView.RestoreLastSessionExitedCleanly} |
511 | + return createTab(properties) |
512 | + } |
513 | + |
514 | + function createTab(properties) { |
515 | + return tabComponent.createObject(tabContainer, properties) |
516 | + } |
517 | + |
518 | + signal newWindowRequested(bool incognito) |
519 | + signal openLinkInWindowRequested(url url, bool incognito) |
520 | |
521 | Connections { |
522 | target: currentWebview |
523 | @@ -112,7 +121,7 @@ |
524 | |
525 | Component { |
526 | id: mediaAccessDialogComponent |
527 | - MediaAccessDialog { } |
528 | + MediaAccessDialog {} |
529 | } |
530 | |
531 | actions: [ |
532 | @@ -136,7 +145,7 @@ |
533 | onTriggered: internal.addBookmark(currentWebview.url, currentWebview.title, currentWebview.icon) |
534 | }, |
535 | Actions.NewTab { |
536 | - onTriggered: browser.openUrlInNewTab("", true) |
537 | + onTriggered: internal.openUrlInNewTab("", true) |
538 | }, |
539 | Actions.ClearHistory { |
540 | onTriggered: HistoryModel.clearAll() |
541 | @@ -150,37 +159,6 @@ |
542 | } |
543 | ] |
544 | |
545 | - Settings { |
546 | - id: settings |
547 | - |
548 | - property url homepage: settingsDefaults.homepage |
549 | - property string searchEngine: settingsDefaults.searchEngine |
550 | - property bool restoreSession: settingsDefaults.restoreSession |
551 | - property int newTabDefaultSection: settingsDefaults.newTabDefaultSection |
552 | - property string defaultAudioDevice |
553 | - property string defaultVideoDevice |
554 | - |
555 | - function restoreDefaults() { |
556 | - homepage = settingsDefaults.homepage |
557 | - searchEngine = settingsDefaults.searchEngine |
558 | - restoreSession = settingsDefaults.restoreSession |
559 | - newTabDefaultSection = settingsDefaults.newTabDefaultSection |
560 | - defaultAudioDevice = settingsDefaults.defaultAudioDevice |
561 | - defaultVideoDevice = settingsDefaults.defaultVideoDevice |
562 | - } |
563 | - } |
564 | - |
565 | - QtObject { |
566 | - id: settingsDefaults |
567 | - |
568 | - readonly property url homepage: "http://start.ubuntu.com" |
569 | - readonly property string searchEngine: "google" |
570 | - readonly property bool restoreSession: true |
571 | - readonly property int newTabDefaultSection: 0 |
572 | - readonly property string defaultAudioDevice: "" |
573 | - readonly property string defaultVideoDevice: "" |
574 | - } |
575 | - |
576 | FocusScope { |
577 | id: contentsContainer |
578 | anchors.fill: parent |
579 | @@ -465,7 +443,7 @@ |
580 | |
581 | onClicked: { |
582 | recentView.reset() |
583 | - browser.openUrlInNewTab("", true) |
584 | + internal.openUrlInNewTab("", true) |
585 | } |
586 | } |
587 | } |
588 | @@ -539,7 +517,7 @@ |
589 | } |
590 | |
591 | onSwitchToTab: internal.switchToTab(index, true) |
592 | - onRequestNewTab: browser.openUrlInNewTab("", makeCurrent, true, index) |
593 | + onRequestNewTab: internal.openUrlInNewTab("", makeCurrent, true, index) |
594 | onTabClosed: internal.closeTab(index) |
595 | |
596 | onFindInPageModeChanged: { |
597 | @@ -554,6 +532,18 @@ |
598 | |
599 | drawerActions: [ |
600 | Action { |
601 | + objectName: "newwindow" |
602 | + text: i18n.tr("New window") |
603 | + iconName: "browser-tabs" |
604 | + onTriggered: browser.newWindowRequested(false) |
605 | + }, |
606 | + Action { |
607 | + objectName: "newprivatewindow" |
608 | + text: i18n.tr("New private window") |
609 | + iconName: "private-browsing" |
610 | + onTriggered: browser.newWindowRequested(true) |
611 | + }, |
612 | + Action { |
613 | objectName: "share" |
614 | text: i18n.tr("Share") |
615 | iconName: "share" |
616 | @@ -588,24 +578,6 @@ |
617 | onTriggered: downloadsViewLoader.active = true |
618 | }, |
619 | Action { |
620 | - objectName: "privatemode" |
621 | - text: browser.incognito ? i18n.tr("Leave Private Mode") : i18n.tr("Private Mode") |
622 | - iconName: "private-browsing" |
623 | - iconSource: browser.incognito ? Qt.resolvedUrl("assets/private-browsing-exit.svg") : "" |
624 | - onTriggered: { |
625 | - if (browser.incognito) { |
626 | - if (tabsModel.count > 1) { |
627 | - PopupUtils.open(leavePrivateModeDialog) |
628 | - } else { |
629 | - browser.incognito = false |
630 | - internal.resetFocus() |
631 | - } |
632 | - } else { |
633 | - browser.incognito = true |
634 | - } |
635 | - } |
636 | - }, |
637 | - Action { |
638 | objectName: "settings" |
639 | text: i18n.tr("Settings") |
640 | iconName: "settings" |
641 | @@ -842,12 +814,12 @@ |
642 | target: bookmarksViewLoader.item |
643 | |
644 | onBookmarkEntryClicked: { |
645 | - browser.openUrlInNewTab(url, true) |
646 | + internal.openUrlInNewTab(url, true) |
647 | bookmarksViewLoader.active = false |
648 | } |
649 | onBack: bookmarksViewLoader.active = false |
650 | onNewTabClicked: { |
651 | - browser.openUrlInNewTab("", true) |
652 | + internal.openUrlInNewTab("", true) |
653 | bookmarksViewLoader.active = false |
654 | } |
655 | } |
656 | @@ -907,7 +879,7 @@ |
657 | expandedHistoryViewLoader.model = model |
658 | expandedHistoryViewLoader.active = true |
659 | } |
660 | - onNewTabRequested: browser.openUrlInNewTab("", true) |
661 | + onNewTabRequested: internal.openUrlInNewTab("", true) |
662 | onBack: historyViewLoader.active = false |
663 | } |
664 | |
665 | @@ -922,7 +894,7 @@ |
666 | focus: true |
667 | model: expandedHistoryViewLoader.model |
668 | onHistoryEntryClicked: { |
669 | - browser.openUrlInNewTab(url, true) |
670 | + internal.openUrlInNewTab(url, true) |
671 | historyViewLoader.active = false |
672 | } |
673 | onHistoryEntryRemoved: { |
674 | @@ -946,11 +918,11 @@ |
675 | |
676 | onHistoryEntryClicked: { |
677 | historyViewLoader.active = false |
678 | - browser.openUrlInNewTab(url, true) |
679 | + internal.openUrlInNewTab(url, true) |
680 | } |
681 | onNewTabRequested: { |
682 | historyViewLoader.active = false |
683 | - browser.openUrlInNewTab("", true) |
684 | + internal.openUrlInNewTab("", true) |
685 | } |
686 | onDone: { |
687 | historyViewLoader.active = false |
688 | @@ -993,7 +965,7 @@ |
689 | Binding { |
690 | target: downloadsViewLoader.item |
691 | property: "downloadManager" |
692 | - value: browser.downloadManager |
693 | + value: downloadHandlerLoader.item |
694 | } |
695 | Binding { |
696 | target: downloadsViewLoader.item |
697 | @@ -1014,31 +986,6 @@ |
698 | } |
699 | } |
700 | |
701 | - TabsModel { |
702 | - id: publicTabsModel |
703 | - } |
704 | - |
705 | - Loader { |
706 | - id: privateTabsModelLoader |
707 | - |
708 | - sourceComponent: browser.incognito ? privateTabsModelComponent : undefined |
709 | - |
710 | - Component { |
711 | - id: privateTabsModelComponent |
712 | - |
713 | - TabsModel { |
714 | - Component.onDestruction: { |
715 | - while (count > 0) { |
716 | - var tab = remove(count - 1) |
717 | - if (tab) { |
718 | - tab.close() |
719 | - } |
720 | - } |
721 | - } |
722 | - } |
723 | - } |
724 | - } |
725 | - |
726 | Loader { |
727 | id: downloadHandlerLoader |
728 | source: "DownloadHandler.qml" |
729 | @@ -1050,6 +997,7 @@ |
730 | |
731 | BrowserTab { |
732 | anchors.fill: parent |
733 | + incognito: browser.incognito |
734 | current: tabsModel && tabsModel.currentTab === this |
735 | focus: current |
736 | |
737 | @@ -1068,6 +1016,7 @@ |
738 | filePicker: filePickerLoader.item |
739 | |
740 | anchors.fill: parent |
741 | + |
742 | focus: true |
743 | |
744 | enabled: current && !bottomEdgeHandle.dragging && !recentView.visible |
745 | @@ -1086,12 +1035,22 @@ |
746 | Actions.OpenLinkInNewTab { |
747 | objectName: "OpenLinkInNewTabContextualAction" |
748 | enabled: contextModel && contextModel.linkUrl.toString() |
749 | - onTriggered: browser.openUrlInNewTab(contextModel.linkUrl, true) |
750 | + onTriggered: internal.openUrlInNewTab(contextModel.linkUrl, true) |
751 | } |
752 | Actions.OpenLinkInNewBackgroundTab { |
753 | objectName: "OpenLinkInNewBackgroundTabContextualAction" |
754 | enabled: contextModel && contextModel.linkUrl.toString() |
755 | - onTriggered: browser.openUrlInNewTab(contextModel.linkUrl, false) |
756 | + onTriggered: internal.openUrlInNewTab(contextModel.linkUrl, false) |
757 | + } |
758 | + Actions.OpenLinkInNewWindow { |
759 | + objectName: "OpenLinkInNewWindowContextualAction" |
760 | + enabled: contextModel && contextModel.linkUrl.toString() |
761 | + onTriggered: browser.openLinkInWindowRequested(contextModel.linkUrl, false) |
762 | + } |
763 | + Actions.OpenLinkInPrivateWindow { |
764 | + objectName: "OpenLinkInPrivateWindowContextualAction" |
765 | + enabled: contextModel && contextModel.linkUrl.toString() |
766 | + onTriggered: browser.openLinkInWindowRequested(contextModel.linkUrl, true) |
767 | } |
768 | Actions.BookmarkLink { |
769 | objectName: "BookmarkLinkContextualAction" |
770 | @@ -1134,7 +1093,7 @@ |
771 | enabled: contextModel && |
772 | (contextModel.mediaType === Oxide.WebView.MediaTypeImage) && |
773 | contextModel.srcUrl.toString() |
774 | - onTriggered: browser.openUrlInNewTab(contextModel.srcUrl, true) |
775 | + onTriggered: internal.openUrlInNewTab(contextModel.srcUrl, true) |
776 | } |
777 | Actions.CopyImage { |
778 | objectName: "CopyImageContextualAction" |
779 | @@ -1156,7 +1115,7 @@ |
780 | enabled: contextModel && |
781 | (contextModel.mediaType === Oxide.WebView.MediaTypeVideo) && |
782 | contextModel.srcUrl.toString() |
783 | - onTriggered: browser.openUrlInNewTab(contextModel.srcUrl, true) |
784 | + onTriggered: internal.openUrlInNewTab(contextModel.srcUrl, true) |
785 | } |
786 | Actions.SaveVideo { |
787 | objectName: "SaveVideoContextualAction" |
788 | @@ -1244,7 +1203,7 @@ |
789 | contextMenu: browser.wide ? contextMenuWideComponent : contextMenuNarrowComponent |
790 | |
791 | onNewViewRequested: { |
792 | - var tab = tabComponent.createObject(tabContainer, {"request": request, 'incognito': browser.incognito}) |
793 | + var tab = tabComponent.createObject(tabContainer, {"request": request}) |
794 | var setCurrent = (request.disposition == Oxide.NewViewRequest.DispositionNewForegroundTab) |
795 | internal.addTab(tab, setCurrent) |
796 | if (setCurrent) tabContainer.forceActiveFocus() |
797 | @@ -1263,7 +1222,7 @@ |
798 | tab.close() |
799 | } |
800 | if (tabsModel.count === 0) { |
801 | - browser.openUrlInNewTab("", true, true) |
802 | + internal.openUrlInNewTab("", true, true) |
803 | } |
804 | } |
805 | } |
806 | @@ -1480,15 +1439,6 @@ |
807 | } |
808 | } |
809 | |
810 | - function getOpenPages() { |
811 | - var urls = [] |
812 | - for (var i = 0; i < tabsModel.count; i++) { |
813 | - var url = tabsModel.get(i).url |
814 | - if (url.toString()) urls.push(url) // exclude "new tab" tabs |
815 | - } |
816 | - return urls |
817 | - } |
818 | - |
819 | function instantiateShareComponent() { |
820 | var component = Qt.createComponent("../Share.qml") |
821 | if (component.status == Component.Ready) { |
822 | @@ -1509,6 +1459,18 @@ |
823 | if (share) share.shareText(text) |
824 | } |
825 | |
826 | + function openUrlInNewTab(url, setCurrent, load, index) { |
827 | + load = typeof load !== 'undefined' ? load : true |
828 | + var tab = tabComponent.createObject(tabContainer, {"initialUrl": url}) |
829 | + addTab(tab, setCurrent, index) |
830 | + if (load) { |
831 | + tab.load() |
832 | + } |
833 | + if (!url.toString()) { |
834 | + maybeFocusAddressBar() |
835 | + } |
836 | + } |
837 | + |
838 | function addTab(tab, setCurrent, index) { |
839 | if (index === undefined) index = tabsModel.add(tab) |
840 | else index = tabsModel.insert(tab, index) |
841 | @@ -1519,23 +1481,22 @@ |
842 | } |
843 | |
844 | function closeTab(index) { |
845 | - // Save the incognito state before removing the tab, because |
846 | - // removing the last tab in the model will switch out incognito |
847 | - // mode, thus causing the check below to fail and save the tab |
848 | - // into the undo stack when it should be forgotten instead. |
849 | - var wasIncognito = incognito |
850 | - var tab = tabsModel.remove(index) |
851 | + var tab = tabsModel.get(index) |
852 | if (tab) { |
853 | - if (!wasIncognito && tab.url.toString().length > 0) { |
854 | + if (!incognito && tab.url.toString().length > 0) { |
855 | closedTabHistory.push({ |
856 | - state: session.serializeTabState(tab), |
857 | + state: serializeTabState(tab), |
858 | index: index |
859 | }) |
860 | } |
861 | tab.close() |
862 | } |
863 | + tabsModel.remove(index) |
864 | + if (tabsModel.currentTab) { |
865 | + tabsModel.currentTab.load() |
866 | + } |
867 | if (tabsModel.count === 0) { |
868 | - browser.openUrlInNewTab("", true) |
869 | + internal.openUrlInNewTab("", true) |
870 | recentView.reset() |
871 | } |
872 | } |
873 | @@ -1549,7 +1510,7 @@ |
874 | function undoCloseTab() { |
875 | if (!incognito && closedTabHistory.length > 0) { |
876 | var tabInfo = closedTabHistory.pop() |
877 | - var tab = session.createTabFromState(tabInfo.state) |
878 | + var tab = restoreTabState(tabInfo.state) |
879 | addTab(tab, true, tabInfo.index) |
880 | } |
881 | } |
882 | @@ -1601,7 +1562,7 @@ |
883 | if (!browser.currentWebview.url.toString()) { |
884 | internal.maybeFocusAddressBar() |
885 | } else { |
886 | - contentsContainer.forceActiveFocus() |
887 | + contentsContainer.focus = true; |
888 | } |
889 | } |
890 | } |
891 | @@ -1672,296 +1633,12 @@ |
892 | onTriggered: internal.switchToTab(internal.nextTabIndex, false) |
893 | } |
894 | |
895 | - function openUrlInNewTab(url, setCurrent, load, index) { |
896 | - load = typeof load !== 'undefined' ? load : true |
897 | - var tab = tabComponent.createObject(tabContainer, {"initialUrl": url, 'incognito': browser.incognito}) |
898 | - internal.addTab(tab, setCurrent, index) |
899 | - if (load) { |
900 | - tab.load() |
901 | - } |
902 | - if (!url.toString()) { |
903 | - internal.maybeFocusAddressBar() |
904 | - } |
905 | - } |
906 | - |
907 | - SessionStorage { |
908 | - id: session |
909 | - |
910 | - dataFile: dataLocation + "/session.json" |
911 | - |
912 | - function save() { |
913 | - if (!locked) { |
914 | - return |
915 | - } |
916 | - var tabs = [] |
917 | - for (var i = 0; i < publicTabsModel.count; ++i) { |
918 | - var tab = publicTabsModel.get(i) |
919 | - tabs.push(serializeTabState(tab)) |
920 | - } |
921 | - store(JSON.stringify({tabs: tabs, currentIndex: publicTabsModel.currentIndex})) |
922 | - } |
923 | - |
924 | - property bool restoring: false |
925 | - function restore() { |
926 | - restoring = true |
927 | - _doRestore() |
928 | - restoring = false |
929 | - } |
930 | - function _doRestore() { |
931 | - if (!locked) { |
932 | - return |
933 | - } |
934 | - var state = null |
935 | - try { |
936 | - state = JSON.parse(retrieve()) |
937 | - } catch (e) { |
938 | - return |
939 | - } |
940 | - if (state) { |
941 | - var tabs = state.tabs |
942 | - if (tabs) { |
943 | - for (var i = 0; i < Math.min(tabs.length, browser.maxTabsToRestore); ++i) { |
944 | - var tab = createTabFromState(tabs[i]) |
945 | - internal.addTab(tab, false) |
946 | - } |
947 | - } |
948 | - if ('currentIndex' in state) { |
949 | - internal.switchToTab(state.currentIndex, false) |
950 | - } |
951 | - } |
952 | - } |
953 | - |
954 | - // Those two functions are used to save/restore the current state of a tab. |
955 | - function serializeTabState(tab) { |
956 | - var state = {} |
957 | - state.uniqueId = tab.uniqueId |
958 | - state.url = tab.url.toString() |
959 | - state.title = tab.title |
960 | - state.icon = tab.icon.toString() |
961 | - state.preview = tab.preview.toString() |
962 | - state.savedState = tab.webview ? tab.webview.currentState : tab.restoreState |
963 | - return state |
964 | - } |
965 | - |
966 | - function createTabFromState(state) { |
967 | - var properties = {'initialUrl': state.url, 'initialTitle': state.title} |
968 | - if ('uniqueId' in state) { |
969 | - properties["uniqueId"] = state.uniqueId |
970 | - } |
971 | - if ('icon' in state) { |
972 | - properties["initialIcon"] = state.icon |
973 | - } |
974 | - if ('preview' in state) { |
975 | - properties["preview"] = state.preview |
976 | - } |
977 | - if ('savedState' in state) { |
978 | - properties['restoreState'] = state.savedState |
979 | - properties['restoreType'] = Oxide.WebView.RestoreLastSessionExitedCleanly |
980 | - } |
981 | - return tabComponent.createObject(tabContainer, properties) |
982 | - } |
983 | - } |
984 | - Timer { |
985 | - id: delayedSessionSaver |
986 | - interval: 500 |
987 | - onTriggered: session.save() |
988 | - } |
989 | - Timer { |
990 | - // Save session periodically to mitigate state loss when the application crashes |
991 | - interval: 60000 // every minute |
992 | - repeat: true |
993 | - running: !browser.incognito |
994 | - onTriggered: delayedSessionSaver.restart() |
995 | - } |
996 | - Timer { |
997 | - id: exitFullscreenOnLostFocus |
998 | - interval: 500 |
999 | - onTriggered: { |
1000 | - if (browser.currentWebview) browser.currentWebview.fullscreen = false |
1001 | - } |
1002 | - } |
1003 | - Connections { |
1004 | - target: Qt.application |
1005 | - onStateChanged: { |
1006 | - if (Qt.application.state != Qt.ApplicationActive) { |
1007 | - if (!browser.incognito) { |
1008 | - session.save() |
1009 | - } |
1010 | - if (browser.currentWebview) { |
1011 | - // Workaround for a desktop bug where changing volume causes |
1012 | - // the app to briefly lose focus to notify-osd, and therefore |
1013 | - // exit fullscreen mode. We prevent this by exiting fullscreen |
1014 | - // only if the focus remains lost for longer than a certain |
1015 | - // threshold. See: https://launchpad.net/bugs/694224. |
1016 | - if (__platformName == "xcb") exitFullscreenOnLostFocus.start() |
1017 | - else browser.currentWebview.fullscreen = false |
1018 | - } |
1019 | - } else exitFullscreenOnLostFocus.stop() |
1020 | - } |
1021 | - onAboutToQuit: { |
1022 | - if (!browser.incognito) { |
1023 | - session.save() |
1024 | - } |
1025 | - } |
1026 | - } |
1027 | - Connections { |
1028 | - target: browser.incognito ? null : publicTabsModel |
1029 | - onCurrentTabChanged: delayedSessionSaver.restart() |
1030 | - onCountChanged: delayedSessionSaver.restart() |
1031 | - } |
1032 | - onIncognitoChanged: { |
1033 | - if (incognito) { |
1034 | - // When going incognito, save the current session right |
1035 | - // away, as periodic session saving is disabled. |
1036 | - session.save() |
1037 | - } |
1038 | - } |
1039 | - |
1040 | - // Schedule various expensive tasks to a point after the initialization and |
1041 | - // first rendering of the application have already happened. |
1042 | - // |
1043 | - // Scheduling a Timer with the shortest non-zero interval possible (1ms) will |
1044 | - // effectively queue its onTriggered function to run immediately after anything |
1045 | - // that is currently in the event loop queue at the moment the Timer starts. |
1046 | - // |
1047 | - // The tasks are: |
1048 | - // - creating the webviews for all initial tabs. This should ideally be done |
1049 | - // asynchronously via object incubation, but http://pad.lv/1359911 prevents it |
1050 | - // - loading the HistoryModel and BookmarksModel from the database |
1051 | - // - deleting any page screenshots that are no longer needed |
1052 | - Timer { |
1053 | - running: true |
1054 | - interval: 1 |
1055 | - onTriggered: { |
1056 | - if (!browser.newSession && settings.restoreSession) { |
1057 | - session.restore() |
1058 | - } |
1059 | - |
1060 | - // Sanity check |
1061 | - console.assert(tabsModel.count <= browser.maxTabsToRestore, |
1062 | - "WARNING: too many tabs were restored") |
1063 | - for (var i in browser.initialUrls) { |
1064 | - browser.openUrlInNewTab(browser.initialUrls[i], true, false) |
1065 | - } |
1066 | - if (tabsModel.count == 0) { |
1067 | - browser.openUrlInNewTab(settings.homepage, true, false) |
1068 | - } |
1069 | - if (!delayedTabSwitcher.running) { |
1070 | - tabsModel.currentTab.load() |
1071 | - } |
1072 | - if (!tabsModel.currentTab.url.toString() && !tabsModel.currentTab.restoreState) { |
1073 | - internal.maybeFocusAddressBar() |
1074 | - } |
1075 | - |
1076 | - BookmarksModel.databasePath = dataLocation + "/bookmarks.sqlite" |
1077 | - HistoryModel.databasePath = dataLocation + "/history.sqlite" |
1078 | - DownloadsModel.databasePath = dataLocation + "/downloads.sqlite" |
1079 | - |
1080 | - // Note that the property setter for databasePath won't return until |
1081 | - // the entire model has been loaded, so it is safe to call this here |
1082 | - PreviewManager.cleanUnusedPreviews(internal.getOpenPages()) |
1083 | - } |
1084 | - } |
1085 | - |
1086 | - Connections { |
1087 | - target: MemInfo |
1088 | - onFreeChanged: { |
1089 | - var freeMemRatio = (MemInfo.total > 0) ? (MemInfo.free / MemInfo.total) : 1.0 |
1090 | - // Under that threshold, available memory is considered "low", and the |
1091 | - // browser is going to try and free up memory from unused tabs. This |
1092 | - // value was chosen empirically, it is subject to change to better |
1093 | - // reflect what a system under memory pressure might look like. |
1094 | - var lowOnMemory = (freeMemRatio < 0.2) |
1095 | - if (lowOnMemory) { |
1096 | - // Unload an inactive tab to (hopefully) free up some memory |
1097 | - function getCandidate(model) { |
1098 | - // Naive implementation that only takes into account the |
1099 | - // last time a tab was current. In the future we might |
1100 | - // want to take into account other parameters such as |
1101 | - // whether the tab is currently playing audio/video. |
1102 | - var candidate = null |
1103 | - for (var i = 0; i < model.count; ++i) { |
1104 | - var tab = model.get(i) |
1105 | - if (tab.current || !tab.webview) { |
1106 | - continue |
1107 | - } |
1108 | - if (!candidate || (candidate.lastCurrent > tab.lastCurrent)) { |
1109 | - candidate = tab |
1110 | - } |
1111 | - } |
1112 | - return candidate |
1113 | - } |
1114 | - var candidate = getCandidate(publicTabsModel) |
1115 | - if (candidate) { |
1116 | - console.warn("Unloading background tab (%1) to free up some memory".arg(candidate.url)) |
1117 | - candidate.unload() |
1118 | - return |
1119 | - } else if (browser.incognito) { |
1120 | - candidate = getCandidate(privateTabsModelLoader.item) |
1121 | - if (candidate) { |
1122 | - console.warn("Unloading a background incognito tab to free up some memory") |
1123 | - candidate.unload() |
1124 | - return |
1125 | - } |
1126 | - } |
1127 | - console.warn("System low on memory, but unable to pick a tab to unload") |
1128 | - } |
1129 | - } |
1130 | - } |
1131 | - |
1132 | - Connections { |
1133 | - target: session.restoring ? null : tabsModel |
1134 | - onCurrentIndexChanged: { |
1135 | - // In narrow mode, the tabslist is a stack: |
1136 | - // the current tab is always at the top. |
1137 | - if (!browser.wide) { |
1138 | - tabsModel.move(tabsModel.currentIndex, 0) |
1139 | - } |
1140 | - } |
1141 | + Connections { |
1142 | + target: tabsModel |
1143 | onCurrentTabChanged: { |
1144 | chrome.findInPageMode = false |
1145 | - var tab = tabsModel.currentTab |
1146 | - if (tab) { |
1147 | - tab.load() |
1148 | - } |
1149 | internal.resetFocus() |
1150 | } |
1151 | - onCountChanged: { |
1152 | - if (tabsModel.count == 0) { |
1153 | - if (browser.incognito) { |
1154 | - browser.incognito = false |
1155 | - internal.resetFocus() |
1156 | - } else if (browser.wide) { |
1157 | - Qt.quit() |
1158 | - } |
1159 | - } |
1160 | - } |
1161 | - } |
1162 | - |
1163 | - Component { |
1164 | - id: leavePrivateModeDialog |
1165 | - |
1166 | - LeavePrivateModeDialog { |
1167 | - id: dialogue |
1168 | - objectName: "leavePrivateModeDialog" |
1169 | - |
1170 | - // This dialog inherits from PopupBase, which has a restoreActiveFocus |
1171 | - // function that is called when the dialog is hidden. That keeps the |
1172 | - // focus in the address bar/webview when we leave private mode. So any |
1173 | - // change on the active focus should be done after the run of such |
1174 | - // function |
1175 | - Component.onDestruction: { |
1176 | - if (!browser.incognito) { |
1177 | - internal.resetFocus() |
1178 | - } |
1179 | - } |
1180 | - |
1181 | - onCancelButtonClicked: PopupUtils.close(dialogue) |
1182 | - onOkButtonClicked: { |
1183 | - PopupUtils.close(dialogue) |
1184 | - browser.incognito = false |
1185 | - } |
1186 | - } |
1187 | } |
1188 | |
1189 | // TODO: internationalize non-standard key sequences? |
1190 | @@ -2025,7 +1702,7 @@ |
1191 | enabled: tabContainer.visible || recentView.visible || |
1192 | bookmarksViewLoader.active || historyViewLoader.active |
1193 | onActivated: { |
1194 | - openUrlInNewTab("", true) |
1195 | + internal.openUrlInNewTab("", true) |
1196 | if (recentView.visible) recentView.reset() |
1197 | bookmarksViewLoader.active = false |
1198 | historyViewLoader.active = false |
1199 | |
1200 | === modified file 'src/app/webbrowser/BrowserTab.qml' |
1201 | --- src/app/webbrowser/BrowserTab.qml 2016-05-09 17:54:20 +0000 |
1202 | +++ src/app/webbrowser/BrowserTab.qml 2016-09-21 07:59:37 +0000 |
1203 | @@ -17,6 +17,7 @@ |
1204 | */ |
1205 | |
1206 | import QtQuick 2.4 |
1207 | +import QtQuick.Window 2.2 |
1208 | import Ubuntu.Web 0.2 |
1209 | import com.canonical.Oxide 1.4 as Oxide |
1210 | import webbrowserapp.private 0.1 |
1211 | @@ -159,6 +160,11 @@ |
1212 | return |
1213 | } |
1214 | |
1215 | + if (Window.visibility == Window.Hidden) { |
1216 | + visible = false |
1217 | + return |
1218 | + } |
1219 | + |
1220 | internal.hiding = true |
1221 | webview.grabToImage(function(result) { |
1222 | if (!internal.hiding) { |
1223 | |
1224 | === removed file 'src/app/webbrowser/LeavePrivateModeDialog.qml' |
1225 | --- src/app/webbrowser/LeavePrivateModeDialog.qml 2016-05-23 13:25:46 +0000 |
1226 | +++ src/app/webbrowser/LeavePrivateModeDialog.qml 1970-01-01 00:00:00 +0000 |
1227 | @@ -1,43 +0,0 @@ |
1228 | -/* |
1229 | - * Copyright 2015-2016 Canonical Ltd. |
1230 | - * |
1231 | - * This file is part of webbrowser-app. |
1232 | - * |
1233 | - * webbrowser-app is free software; you can redistribute it and/or modify |
1234 | - * it under the terms of the GNU General Public License as published by |
1235 | - * the Free Software Foundation; version 3. |
1236 | - * |
1237 | - * webbrowser-app is distributed in the hope that it will be useful, |
1238 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1239 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1240 | - * GNU General Public License for more details. |
1241 | - * |
1242 | - * You should have received a copy of the GNU General Public License |
1243 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1244 | - */ |
1245 | - |
1246 | -import QtQuick 2.4 |
1247 | -import Ubuntu.Components 1.3 |
1248 | -import Ubuntu.Components.Popups 1.3 |
1249 | - |
1250 | -Dialog { |
1251 | - title: i18n.tr("Going to public mode will close all private tabs") |
1252 | - |
1253 | - signal cancelButtonClicked() |
1254 | - signal okButtonClicked() |
1255 | - |
1256 | - Button { |
1257 | - objectName: "leavePrivateModeDialog.cancelButton" |
1258 | - anchors { left: parent.left; right: parent.right } |
1259 | - text: i18n.tr("Cancel") |
1260 | - onClicked: cancelButtonClicked() |
1261 | - } |
1262 | - |
1263 | - Button { |
1264 | - objectName: "leavePrivateModeDialog.okButton" |
1265 | - anchors { left: parent.left; right: parent.right } |
1266 | - text: i18n.tr("OK") |
1267 | - color: theme.palette.normal.positive |
1268 | - onClicked: okButtonClicked() |
1269 | - } |
1270 | -} |
1271 | |
1272 | === modified file 'src/app/webbrowser/webbrowser-app.cpp' |
1273 | --- src/app/webbrowser/webbrowser-app.cpp 2016-05-09 22:51:50 +0000 |
1274 | +++ src/app/webbrowser/webbrowser-app.cpp 2016-09-21 07:59:37 +0000 |
1275 | @@ -39,6 +39,7 @@ |
1276 | #include <QtCore/QTextStream> |
1277 | #include <QtCore/QtGlobal> |
1278 | #include <QtCore/QVariant> |
1279 | +#include <QtQml/QQmlProperty> |
1280 | #include <QtQml/QtQml> |
1281 | #include <QtQuick/QQuickWindow> |
1282 | |
1283 | @@ -84,15 +85,19 @@ |
1284 | |
1285 | m_engine->rootContext()->setContextProperty("__platformName", platformName()); |
1286 | |
1287 | - m_window->setProperty("newSession", m_arguments.contains("--new-session")); |
1288 | + m_component->completeCreate(); |
1289 | |
1290 | QVariantList urls; |
1291 | Q_FOREACH(const QUrl& url, this->urls()) { |
1292 | urls.append(url); |
1293 | } |
1294 | - m_window->setProperty("urls", urls); |
1295 | + bool newSession = m_arguments.contains(QStringLiteral("--new-session")); |
1296 | + bool incognito = m_arguments.contains(QStringLiteral("--incognito")); |
1297 | + QMetaObject::invokeMethod(m_object, "init", |
1298 | + Q_ARG(QVariant, QVariant(urls)), |
1299 | + Q_ARG(QVariant, newSession), |
1300 | + Q_ARG(QVariant, incognito)); |
1301 | |
1302 | - m_component->completeCreate(); |
1303 | return true; |
1304 | } else { |
1305 | return false; |
1306 | @@ -103,15 +108,38 @@ |
1307 | { |
1308 | QTextStream out(stdout); |
1309 | QString command = QFileInfo(QCoreApplication::applicationFilePath()).fileName(); |
1310 | - out << "Usage: " << command << " [-h|--help] [--fullscreen] [--maximized] [--inspector]" |
1311 | + out << "Usage: " << command << " [-h|--help] [--inspector]" |
1312 | << " [--app-id=APP_ID] [--new-session] [URL]" << endl; |
1313 | out << "Options:" << endl; |
1314 | out << " -h, --help display this help message and exit" << endl; |
1315 | - out << " --fullscreen display full screen" << endl; |
1316 | - out << " --maximized opens the application maximized" << endl; |
1317 | out << " --inspector[=PORT] run a remote inspector on a specified port or " << REMOTE_INSPECTOR_PORT << " as the default port" << endl; |
1318 | out << " --app-id=APP_ID run the application with a specific APP_ID" << endl; |
1319 | out << " --new-session do not restore open tabs from the last session" << endl; |
1320 | + out << " --new-window open (the passed URLs in) a new browser window" << endl; |
1321 | + out << " --incognito open (the passed URLs in) an incognito window" << endl; |
1322 | +} |
1323 | + |
1324 | +void WebbrowserApp::onNewInstanceLaunched(const QStringList& arguments) const |
1325 | +{ |
1326 | + bool newWindow = false; |
1327 | + bool incognito = false; |
1328 | + QVariantList urls; |
1329 | + Q_FOREACH(const QString& argument, arguments) { |
1330 | + if (argument == QStringLiteral("--new-window")) { |
1331 | + newWindow = true; |
1332 | + } else if (argument == QStringLiteral("--incognito")) { |
1333 | + incognito = true; |
1334 | + } else if (!argument.startsWith(QStringLiteral("-"))) { |
1335 | + QUrl url = QUrl::fromUserInput(argument); |
1336 | + if (url.isValid()) { |
1337 | + urls.append(url); |
1338 | + } |
1339 | + } |
1340 | + } |
1341 | + QMetaObject::invokeMethod(m_object, "openUrls", |
1342 | + Q_ARG(QVariant, QVariant(urls)), |
1343 | + Q_ARG(QVariant, newWindow), |
1344 | + Q_ARG(QVariant, incognito)); |
1345 | } |
1346 | |
1347 | int main(int argc, char** argv) |
1348 | |
1349 | === modified file 'src/app/webbrowser/webbrowser-app.desktop.in.in' |
1350 | --- src/app/webbrowser/webbrowser-app.desktop.in.in 2015-01-28 17:06:59 +0000 |
1351 | +++ src/app/webbrowser/webbrowser-app.desktop.in.in 2016-09-21 07:59:37 +0000 |
1352 | @@ -16,3 +16,12 @@ |
1353 | X-Ubuntu-Default-Department-ID=web-browsers |
1354 | X-Screenshot=@CMAKE_INSTALL_FULL_DATADIR@/webbrowser-app/screenshot.png |
1355 | X-Ubuntu-Splash-Color=#FFFFFF |
1356 | +Actions=NewWindow;Incognito; |
1357 | + |
1358 | +[Desktop Action NewWindow] |
1359 | +_Name=Open a New Window |
1360 | +Exec=webbrowser-app --new-window |
1361 | + |
1362 | +[Desktop Action Incognito] |
1363 | +_Name=Open a New Private Window |
1364 | +Exec=webbrowser-app --incognito |
1365 | |
1366 | === modified file 'src/app/webbrowser/webbrowser-app.h' |
1367 | --- src/app/webbrowser/webbrowser-app.h 2016-01-15 09:29:22 +0000 |
1368 | +++ src/app/webbrowser/webbrowser-app.h 2016-09-21 07:59:37 +0000 |
1369 | @@ -31,7 +31,10 @@ |
1370 | bool initialize(); |
1371 | |
1372 | private: |
1373 | - virtual void printUsage() const; |
1374 | + void printUsage() const final; |
1375 | + |
1376 | +private Q_SLOTS: |
1377 | + void onNewInstanceLaunched(const QStringList& arguments) const final; |
1378 | }; |
1379 | |
1380 | #endif // __WEBBROWSER_APP_H__ |
1381 | |
1382 | === modified file 'src/app/webbrowser/webbrowser-app.qml' |
1383 | --- src/app/webbrowser/webbrowser-app.qml 2016-01-15 09:29:22 +0000 |
1384 | +++ src/app/webbrowser/webbrowser-app.qml 2016-09-21 07:59:37 +0000 |
1385 | @@ -18,57 +18,432 @@ |
1386 | |
1387 | import QtQuick 2.4 |
1388 | import QtQuick.Window 2.2 |
1389 | +import Qt.labs.settings 1.0 |
1390 | import Ubuntu.Components 1.3 |
1391 | +import "." |
1392 | import ".." |
1393 | - |
1394 | -BrowserWindow { |
1395 | - id: window |
1396 | - |
1397 | - property alias urls: browser.initialUrls |
1398 | - property alias newSession: browser.newSession |
1399 | - |
1400 | - currentWebview: browser.currentWebview |
1401 | - |
1402 | - title: { |
1403 | - if (browser.title) { |
1404 | - // TRANSLATORS: %1 refers to the current page’s title |
1405 | - return i18n.tr("%1 - Ubuntu Web Browser").arg(browser.title) |
1406 | - } else { |
1407 | - return i18n.tr("Ubuntu Web Browser") |
1408 | - } |
1409 | - } |
1410 | - |
1411 | - Browser { |
1412 | - id: browser |
1413 | - anchors.fill: parent |
1414 | - webbrowserWindow: webbrowserWindowProxy |
1415 | - developerExtrasEnabled: window.developerExtrasEnabled |
1416 | - |
1417 | - fullscreen: window.visibility === Window.FullScreen |
1418 | - |
1419 | - Component.onCompleted: i18n.domain = "webbrowser-app" |
1420 | - |
1421 | - Keys.onPressed: { |
1422 | - if ((event.key === Qt.Key_F11) && (event.modifiers === Qt.NoModifier)) { |
1423 | - // F11 to toggle application-level fullscreen |
1424 | - window.setFullscreen(window.visibility !== Window.FullScreen) |
1425 | - if (currentWebview.fullscreen) { |
1426 | - currentWebview.fullscreen = false |
1427 | - } |
1428 | - } |
1429 | - } |
1430 | - Keys.onEscapePressed: { |
1431 | - // ESC to exit fullscreen, regardless of whether it was requested |
1432 | - // by the page or toggled on by the user. |
1433 | - window.setFullscreen(false) |
1434 | - currentWebview.fullscreen = false |
1435 | - } |
1436 | - } |
1437 | - |
1438 | - onOpenUrls: { |
1439 | - for (var i = 0; i < urls.length; ++i) { |
1440 | - var setCurrent = (i == urls.length - 1) |
1441 | - browser.openUrlInNewTab(urls[i], setCurrent, setCurrent) |
1442 | +import webbrowsercommon.private 0.1 |
1443 | +import webbrowserapp.private 0.1 |
1444 | + |
1445 | +QtObject { |
1446 | + id: webbrowserapp |
1447 | + |
1448 | + function init(urls, newSession, incognito) { |
1449 | + i18n.domain = "webbrowser-app" |
1450 | + if (!newSession && settings.restoreSession && !incognito) { |
1451 | + session.restore() |
1452 | + } |
1453 | + if (allWindows.length == 0) { |
1454 | + windowFactory.createObject(null, {"incognito": incognito}).show() |
1455 | + } |
1456 | + var window = allWindows[allWindows.length - 1] |
1457 | + for (var i in urls) { |
1458 | + window.addTab(urls[i]).load() |
1459 | + window.tabsModel.currentIndex = window.tabsModel.count - 1 |
1460 | + } |
1461 | + if (window.tabsModel.count == 0) { |
1462 | + window.addTab(incognito ? "" : settings.homepage).load() |
1463 | + window.tabsModel.currentIndex = 0 |
1464 | + } |
1465 | + for (var w in allWindows) { |
1466 | + allWindows[w].tabsModel.currentTab.load() |
1467 | + } |
1468 | + |
1469 | + // FIXME: do this asynchronously |
1470 | + BookmarksModel.databasePath = dataLocation + "/bookmarks.sqlite" |
1471 | + HistoryModel.databasePath = dataLocation + "/history.sqlite" |
1472 | + DownloadsModel.databasePath = dataLocation + "/downloads.sqlite" |
1473 | + |
1474 | + var doNotCleanUrls = [] |
1475 | + for (var x in allWindows) { |
1476 | + var tabs = allWindows[x].tabsModel |
1477 | + for (var t = 0; t < tabs.count; ++t) { |
1478 | + doNotCleanUrls.push(tabs.get(t).url) |
1479 | + } |
1480 | + } |
1481 | + PreviewManager.cleanUnusedPreviews(doNotCleanUrls) |
1482 | + } |
1483 | + |
1484 | + // Array of all windows, sorted chronologically (most recently active last) |
1485 | + readonly property var allWindows: [] |
1486 | + |
1487 | + function getLastActiveWindow(incognito) { |
1488 | + for (var i = allWindows.length - 1; i >= 0; --i) { |
1489 | + var window = allWindows[i] |
1490 | + if (window.incognito == incognito) { |
1491 | + return window |
1492 | + } |
1493 | + } |
1494 | + return null |
1495 | + } |
1496 | + |
1497 | + function openUrls(urls, newWindow, incognito) { |
1498 | + var window = getLastActiveWindow(incognito) |
1499 | + if (!window || newWindow) { |
1500 | + window = windowFactory.createObject(null, {"incognito": incognito}) |
1501 | + } |
1502 | + for (var i in urls) { |
1503 | + window.addTab(urls[i]).load() |
1504 | + } |
1505 | + if (window.tabsModel.count == 0) { |
1506 | + window.addTab().load() |
1507 | + } |
1508 | + window.tabsModel.currentIndex = window.tabsModel.count - 1 |
1509 | + window.show() |
1510 | + window.requestActivate() |
1511 | + } |
1512 | + |
1513 | + property var windowFactory: Component { |
1514 | + BrowserWindow { |
1515 | + id: window |
1516 | + |
1517 | + property alias incognito: browser.incognito |
1518 | + readonly property var tabsModel: browser.tabsModel |
1519 | + |
1520 | + currentWebview: browser.currentWebview |
1521 | + |
1522 | + title: { |
1523 | + if (browser.title) { |
1524 | + // TRANSLATORS: %1 refers to the current page’s title |
1525 | + return i18n.tr("%1 - Ubuntu Web Browser").arg(browser.title) |
1526 | + } else { |
1527 | + return i18n.tr("Ubuntu Web Browser") |
1528 | + } |
1529 | + } |
1530 | + |
1531 | + onActiveChanged: { |
1532 | + if (active) { |
1533 | + var index = allWindows.indexOf(this) |
1534 | + if (index > -1) { |
1535 | + allWindows.push(allWindows.splice(index, 1)[0]) |
1536 | + } |
1537 | + } |
1538 | + } |
1539 | + |
1540 | + onClosing: { |
1541 | + if (allWindows.length == 1) { |
1542 | + if (tabsModel.count > 0) { |
1543 | + session.save() |
1544 | + } else { |
1545 | + session.clear() |
1546 | + } |
1547 | + } |
1548 | + destroy() |
1549 | + } |
1550 | + |
1551 | + Shortcut { |
1552 | + sequence: StandardKey.Quit |
1553 | + onActivated: Qt.quit() |
1554 | + } |
1555 | + |
1556 | + function toggleApplicationLevelFullscreen() { |
1557 | + setFullscreen(visibility !== Window.FullScreen) |
1558 | + if (browser.currentWebview.fullscreen) { |
1559 | + browser.currentWebview.fullscreen = false |
1560 | + } |
1561 | + } |
1562 | + |
1563 | + Shortcut { |
1564 | + sequence: StandardKey.FullScreen |
1565 | + onActivated: window.toggleApplicationLevelFullscreen() |
1566 | + } |
1567 | + |
1568 | + Shortcut { |
1569 | + sequence: "F11" |
1570 | + onActivated: window.toggleApplicationLevelFullscreen() |
1571 | + } |
1572 | + |
1573 | + Shortcut { |
1574 | + sequence: "Ctrl+N" |
1575 | + onActivated: browser.newWindowRequested(false) |
1576 | + } |
1577 | + |
1578 | + Shortcut { |
1579 | + sequence: "Ctrl+Shift+N" |
1580 | + onActivated: browser.newWindowRequested(true) |
1581 | + } |
1582 | + |
1583 | + Component.onCompleted: allWindows.push(this) |
1584 | + Component.onDestruction: { |
1585 | + for (var w in allWindows) { |
1586 | + if (this === allWindows[w]) { |
1587 | + allWindows.splice(w, 1) |
1588 | + return |
1589 | + } |
1590 | + } |
1591 | + } |
1592 | + |
1593 | + Browser { |
1594 | + id: browser |
1595 | + anchors.fill: parent |
1596 | + settings: webbrowserapp.settings |
1597 | + onNewWindowRequested: { |
1598 | + var window = windowFactory.createObject( |
1599 | + null, |
1600 | + { |
1601 | + "incognito": incognito, |
1602 | + "height": parent.height, |
1603 | + "width": parent.width, |
1604 | + } |
1605 | + ) |
1606 | + window.addTab() |
1607 | + window.tabsModel.currentIndex = 0 |
1608 | + window.tabsModel.currentTab.load() |
1609 | + window.show() |
1610 | + } |
1611 | + onOpenLinkInWindowRequested: { |
1612 | + var window = null |
1613 | + if (incognito) { |
1614 | + window = getLastActiveWindow(true) |
1615 | + } |
1616 | + if (!window) { |
1617 | + window = windowFactory.createObject( |
1618 | + null, |
1619 | + { |
1620 | + "incognito": incognito, |
1621 | + "height": parent.height, |
1622 | + "width": parent.width, |
1623 | + } |
1624 | + ) |
1625 | + } |
1626 | + window.addTab(url) |
1627 | + window.tabsModel.currentIndex = window.tabsModel.count - 1 |
1628 | + window.tabsModel.currentTab.load() |
1629 | + window.show() |
1630 | + window.requestActivate() |
1631 | + } |
1632 | + |
1633 | + // Not handled as a window-level shortcut as it would take |
1634 | + // precedence over key events in web content. |
1635 | + Keys.onEscapePressed: { |
1636 | + // ESC to exit fullscreen, regardless of whether it was |
1637 | + // requested by the page or toggled on by the user. |
1638 | + window.setFullscreen(false) |
1639 | + browser.currentWebview.fullscreen = false |
1640 | + } |
1641 | + } |
1642 | + |
1643 | + Connections { |
1644 | + target: window.tabsModel |
1645 | + onCountChanged: { |
1646 | + if ((window.tabsModel.count === 0) && browser.wide) { |
1647 | + window.close() |
1648 | + } |
1649 | + } |
1650 | + } |
1651 | + |
1652 | + Connections { |
1653 | + target: window.incognito ? null : window.tabsModel |
1654 | + onCurrentIndexChanged: delayedSessionSaver.restart() |
1655 | + onCountChanged: delayedSessionSaver.restart() |
1656 | + } |
1657 | + |
1658 | + Connections { |
1659 | + target: (session.restoring || !window.visible || browser.wide) ? null : window.tabsModel |
1660 | + onCurrentIndexChanged: { |
1661 | + // In narrow mode, the tabslist is a stack: |
1662 | + // the current tab is always at the top. |
1663 | + window.tabsModel.move(window.tabsModel.currentIndex, 0) |
1664 | + } |
1665 | + } |
1666 | + |
1667 | + function serializeTabState(tab) { |
1668 | + return browser.serializeTabState(tab) |
1669 | + } |
1670 | + |
1671 | + function restoreTabState(state) { |
1672 | + return browser.restoreTabState(state) |
1673 | + } |
1674 | + |
1675 | + function addTab(url) { |
1676 | + var tab = browser.createTab({"initialUrl": url}) |
1677 | + tabsModel.add(tab) |
1678 | + return tab |
1679 | + } |
1680 | + } |
1681 | + } |
1682 | + |
1683 | + property var settings: Settings { |
1684 | + property url homepage: "http://start.ubuntu.com" |
1685 | + property string searchEngine: "google" |
1686 | + property bool restoreSession: true |
1687 | + property int newTabDefaultSection: 0 |
1688 | + property string defaultAudioDevice: "" |
1689 | + property string defaultVideoDevice: "" |
1690 | + |
1691 | + function restoreDefaults() { |
1692 | + homepage = "http://start.ubuntu.com" |
1693 | + searchEngine = "google" |
1694 | + restoreSession = true |
1695 | + newTabDefaultSection = 0 |
1696 | + defaultAudioDevice = "" |
1697 | + defaultVideoDevice = "" |
1698 | + } |
1699 | + } |
1700 | + |
1701 | + // Handle runtime requests to open urls as defined |
1702 | + // by the freedesktop application dbus interface's open |
1703 | + // method for DBUS application activation: |
1704 | + // http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus |
1705 | + // The dispatch on the org.freedesktop.Application if is done per appId at the |
1706 | + // url-dispatcher/upstart level. |
1707 | + property var openUrlsHandler: Connections { |
1708 | + target: UriHandler |
1709 | + onOpened: webbrowserapp.openUrls(uris, false, false) |
1710 | + } |
1711 | + |
1712 | + property var session: SessionStorage { |
1713 | + dataFile: dataLocation + "/session.json" |
1714 | + |
1715 | + // TODO: do we want to save/restore window positions too (https://launchpad.net/bugs/1312892)? |
1716 | + |
1717 | + function save() { |
1718 | + if (!locked || restoring) { |
1719 | + return |
1720 | + } |
1721 | + var windows = [] |
1722 | + for (var w in allWindows) { |
1723 | + var window = allWindows[w] |
1724 | + if (window.incognito) { |
1725 | + continue |
1726 | + } |
1727 | + windows.push(serializeWindowState(window)) |
1728 | + } |
1729 | + if (windows.length > 0) { |
1730 | + store(JSON.stringify({windows: windows})) |
1731 | + } |
1732 | + } |
1733 | + |
1734 | + property bool restoring: false |
1735 | + function restore() { |
1736 | + restoring = true |
1737 | + _doRestore() |
1738 | + restoring = false |
1739 | + } |
1740 | + function _doRestore() { |
1741 | + if (!locked) { |
1742 | + return |
1743 | + } |
1744 | + var state = null |
1745 | + try { |
1746 | + state = JSON.parse(retrieve()) |
1747 | + } catch (e) { |
1748 | + return |
1749 | + } |
1750 | + if (state) { |
1751 | + var windows = state.windows |
1752 | + if (windows) { |
1753 | + for (var w in windows) { |
1754 | + restoreWindowState(windows[w]) |
1755 | + } |
1756 | + } else if (state.tabs) { |
1757 | + // One-off code path: when launching the app for the first time |
1758 | + // after the upgrade that adds support for multiple windows, the |
1759 | + // saved session contains a list of tabs, not windows. |
1760 | + restoreWindowState(state) |
1761 | + } |
1762 | + if (allWindows.length > 0) { |
1763 | + var window = allWindows[allWindows.length - 1] |
1764 | + window.requestActivate() |
1765 | + window.raise() |
1766 | + } |
1767 | + } |
1768 | + } |
1769 | + |
1770 | + function serializeWindowState(window) { |
1771 | + var tabs = [] |
1772 | + for (var i = 0; i < window.tabsModel.count; ++i) { |
1773 | + tabs.push(window.serializeTabState(window.tabsModel.get(i))) |
1774 | + } |
1775 | + return {tabs: tabs, currentIndex: window.tabsModel.currentIndex} |
1776 | + } |
1777 | + |
1778 | + function restoreWindowState(state) { |
1779 | + var window = windowFactory.createObject(null) |
1780 | + for (var i in state.tabs) { |
1781 | + window.tabsModel.add(window.restoreTabState(state.tabs[i])) |
1782 | + } |
1783 | + window.tabsModel.currentIndex = state.currentIndex |
1784 | + window.show() |
1785 | + } |
1786 | + |
1787 | + function clear() { |
1788 | + if (!locked) { |
1789 | + return |
1790 | + } |
1791 | + store("") |
1792 | + } |
1793 | + } |
1794 | + |
1795 | + property var delayedSessionSaver: Timer { |
1796 | + interval: 500 |
1797 | + onTriggered: session.save() |
1798 | + } |
1799 | + |
1800 | + property var periodicSessionSaver: Timer { |
1801 | + // Save session periodically to mitigate state loss when the application crashes |
1802 | + interval: 60000 // every minute |
1803 | + repeat: true |
1804 | + running: true |
1805 | + onTriggered: delayedSessionSaver.restart() |
1806 | + } |
1807 | + |
1808 | + property var applicationMonitor: Connections { |
1809 | + target: Qt.application |
1810 | + onStateChanged: { |
1811 | + if (Qt.application.state != Qt.ApplicationActive) { |
1812 | + session.save() |
1813 | + } |
1814 | + } |
1815 | + onAboutToQuit: { |
1816 | + if (allWindows.length > 0) { |
1817 | + session.save() |
1818 | + } |
1819 | + } |
1820 | + } |
1821 | + |
1822 | + property var memoryPressureMonitor: Connections { |
1823 | + target: MemInfo |
1824 | + onFreeChanged: { |
1825 | + var freeMemRatio = (MemInfo.total > 0) ? (MemInfo.free / MemInfo.total) : 1.0 |
1826 | + // Under that threshold, available memory is considered "low", and the |
1827 | + // browser is going to try and free up memory from unused tabs. This |
1828 | + // value was chosen empirically, it is subject to change to better |
1829 | + // reflect what a system under memory pressure might look like. |
1830 | + var lowOnMemory = (freeMemRatio < 0.2) |
1831 | + if (lowOnMemory) { |
1832 | + // Unload an inactive tab to (hopefully) free up some memory |
1833 | + function getCandidate(model) { |
1834 | + // Naive implementation that only takes into account the |
1835 | + // last time a tab was current. In the future we might |
1836 | + // want to take into account other parameters such as |
1837 | + // whether the tab is currently playing audio/video. |
1838 | + var candidate = null |
1839 | + for (var i = 0; i < model.count; ++i) { |
1840 | + var tab = model.get(i) |
1841 | + if (tab.current || !tab.webview) { |
1842 | + continue |
1843 | + } |
1844 | + if (!candidate || (candidate.lastCurrent > tab.lastCurrent)) { |
1845 | + candidate = tab |
1846 | + } |
1847 | + } |
1848 | + return candidate |
1849 | + } |
1850 | + for (var w in allWindows) { |
1851 | + var candidate = getCandidate(allWindows[w].tabsModel) |
1852 | + if (candidate) { |
1853 | + if (browser.incognito) { |
1854 | + console.warn("Unloading a background incognito tab to free up some memory") |
1855 | + } else { |
1856 | + console.warn("Unloading background tab (%1) to free up some memory".arg(candidate.url)) |
1857 | + } |
1858 | + candidate.unload() |
1859 | + return |
1860 | + } |
1861 | + } |
1862 | + console.warn("System low on memory, but unable to pick a tab to unload") |
1863 | + } |
1864 | } |
1865 | } |
1866 | } |
1867 | |
1868 | === modified file 'src/app/webcontainer/WebApp.qml' |
1869 | --- src/app/webcontainer/WebApp.qml 2016-08-03 09:28:48 +0000 |
1870 | +++ src/app/webcontainer/WebApp.qml 2016-09-21 07:59:37 +0000 |
1871 | @@ -33,6 +33,8 @@ |
1872 | |
1873 | currentWebview: containerWebView.currentWebview |
1874 | |
1875 | + property alias window: containerWebView.window |
1876 | + |
1877 | property alias url: containerWebView.url |
1878 | |
1879 | property bool accountSwitcher |
1880 | |
1881 | === modified file 'src/app/webcontainer/WebViewImplOxide.qml' |
1882 | --- src/app/webcontainer/WebViewImplOxide.qml 2016-07-13 16:23:18 +0000 |
1883 | +++ src/app/webcontainer/WebViewImplOxide.qml 2016-09-21 07:59:37 +0000 |
1884 | @@ -28,6 +28,10 @@ |
1885 | WebappWebview { |
1886 | id: webview |
1887 | |
1888 | + // XXX: Ideally, we would use the Window.window |
1889 | + // attached property, but it is new in Qt 5.7. |
1890 | + property Window window |
1891 | + |
1892 | property bool developerExtrasEnabled: false |
1893 | property string webappName: "" |
1894 | property string localUserAgentOverride: "" |
1895 | @@ -261,13 +265,7 @@ |
1896 | function getUnityWebappsProxies() { |
1897 | var eventHandlers = { |
1898 | onAppRaised: function () { |
1899 | - if (webbrowserWindow) { |
1900 | - try { |
1901 | - webbrowserWindow.raise(); |
1902 | - } catch (e) { |
1903 | - console.debug('Error while raising: ' + e); |
1904 | - } |
1905 | - } |
1906 | + window.raise(); |
1907 | } |
1908 | }; |
1909 | return UnityWebAppsUtils.makeProxiesForWebViewBindee(webview, eventHandlers) |
1910 | |
1911 | === modified file 'src/app/webcontainer/WebappContainerWebview.qml' |
1912 | --- src/app/webcontainer/WebappContainerWebview.qml 2016-07-13 16:23:18 +0000 |
1913 | +++ src/app/webcontainer/WebappContainerWebview.qml 2016-09-21 07:59:37 +0000 |
1914 | @@ -17,6 +17,7 @@ |
1915 | */ |
1916 | |
1917 | import QtQuick 2.4 |
1918 | +import QtQuick.Window 2.2 |
1919 | import Ubuntu.Components 1.3 |
1920 | import Ubuntu.Unity.Action 1.1 as UnityActions |
1921 | import Ubuntu.UnityWebApps 0.1 as UnityWebApps |
1922 | @@ -26,6 +27,8 @@ |
1923 | FocusScope { |
1924 | id: containerWebview |
1925 | |
1926 | + property Window window |
1927 | + |
1928 | property string url: "" |
1929 | property bool developerExtrasEnabled: false |
1930 | property string webappName: "" |
1931 | @@ -115,7 +118,8 @@ |
1932 | |
1933 | webappContainerWebViewLoader.setSource( |
1934 | webappEngineSource, |
1935 | - { localUserAgentOverride: containerWebview.localUserAgentOverride |
1936 | + { window: containerWebView.window |
1937 | + , localUserAgentOverride: containerWebview.localUserAgentOverride |
1938 | , url: containerWebview.url |
1939 | , webappName: containerWebview.webappName |
1940 | , dataPath: dataPath |
1941 | |
1942 | === modified file 'src/app/webcontainer/webapp-container.cpp' |
1943 | --- src/app/webcontainer/webapp-container.cpp 2016-06-20 15:07:52 +0000 |
1944 | +++ src/app/webcontainer/webapp-container.cpp 2016-09-21 07:59:37 +0000 |
1945 | @@ -41,6 +41,7 @@ |
1946 | #include <QtQml/QQmlComponent> |
1947 | #include <QtQml/QQmlContext> |
1948 | #include <QtQml/QQmlEngine> |
1949 | +#include <QtQml/QQmlProperty> |
1950 | #include <QtQml> |
1951 | #include <QtQuick/QQuickWindow> |
1952 | |
1953 | @@ -88,7 +89,9 @@ |
1954 | m_addressBarVisible(false), |
1955 | m_localWebappManifest(false), |
1956 | m_openExternalUrlInOverlay(false), |
1957 | - m_webappContainerHelper(new WebappContainerHelper()) |
1958 | + m_webappContainerHelper(new WebappContainerHelper()), |
1959 | + m_fullscreen(false), |
1960 | + m_maximized(false) |
1961 | { |
1962 | } |
1963 | |
1964 | @@ -135,34 +138,33 @@ |
1965 | QDir searchDir(m_webappModelSearchPath); |
1966 | searchDir.makeAbsolute(); |
1967 | if (searchDir.exists()) { |
1968 | - m_window->setProperty("webappModelSearchPath", searchDir.path()); |
1969 | + QQmlProperty::write(m_object, QStringLiteral("webappModelSearchPath"), searchDir.path()); |
1970 | } |
1971 | } |
1972 | if ( ! m_localCookieStoreDbPath.isEmpty()) { |
1973 | - m_window->setProperty("localCookieStoreDbPath", m_localCookieStoreDbPath); |
1974 | + QQmlProperty::write(m_object, QStringLiteral("localCookieStoreDbPath"), m_localCookieStoreDbPath); |
1975 | } |
1976 | |
1977 | - m_window->setProperty("webappName", m_webappName); |
1978 | + QQmlProperty::write(m_object, QStringLiteral("webappName"), m_webappName); |
1979 | QFileInfo iconInfo(m_webappIcon); |
1980 | QUrl iconUrl; |
1981 | if (iconInfo.isReadable()) { |
1982 | iconUrl = QUrl::fromLocalFile(iconInfo.absoluteFilePath()); |
1983 | } |
1984 | - m_window->setProperty("webappIcon", iconUrl); |
1985 | - m_window->setProperty("backForwardButtonsVisible", m_backForwardButtonsVisible); |
1986 | - m_window->setProperty("chromeVisible", m_addressBarVisible); |
1987 | - m_window->setProperty("accountProvider", m_accountProvider); |
1988 | - m_window->setProperty("accountSwitcher", m_accountSwitcher); |
1989 | - m_window->setProperty("openExternalUrlInOverlay", m_openExternalUrlInOverlay); |
1990 | - m_window->setProperty("defaultVideoCaptureCameraPosition", m_defaultVideoCaptureCameraPosition); |
1991 | - |
1992 | - m_window->setProperty("webappUrlPatterns", m_webappUrlPatterns); |
1993 | + QQmlProperty::write(m_object, QStringLiteral("webappIcon"), iconUrl); |
1994 | + QQmlProperty::write(m_object, QStringLiteral("backForwardButtonsVisible"), m_backForwardButtonsVisible); |
1995 | + QQmlProperty::write(m_object, QStringLiteral("chromeVisible"), m_addressBarVisible); |
1996 | + QQmlProperty::write(m_object, QStringLiteral("accountProvider"), m_accountProvider); |
1997 | + QQmlProperty::write(m_object, QStringLiteral("accountSwitcher"), m_accountSwitcher); |
1998 | + QQmlProperty::write(m_object, QStringLiteral("openExternalUrlInOverlay"), m_openExternalUrlInOverlay); |
1999 | + QQmlProperty::write(m_object, QStringLiteral("defaultVideoCaptureCameraPosition"), m_defaultVideoCaptureCameraPosition); |
2000 | + QQmlProperty::write(m_object, QStringLiteral("webappUrlPatterns"), m_webappUrlPatterns); |
2001 | QQmlContext* context = m_engine->rootContext(); |
2002 | if (m_storeSessionCookies) { |
2003 | QString sessionCookieMode = SessionUtils::firstRun(m_webappName) ? |
2004 | QStringLiteral("persistent") : QStringLiteral("restored"); |
2005 | qDebug() << "Setting session cookie mode to" << sessionCookieMode; |
2006 | - m_window->setProperty("webContextSessionCookieMode", sessionCookieMode); |
2007 | + QQmlProperty::write(m_object, QStringLiteral("webContextSessionCookieMode"), sessionCookieMode); |
2008 | } |
2009 | |
2010 | context->setContextProperty("webappContainerHelper", m_webappContainerHelper.data()); |
2011 | @@ -170,35 +172,36 @@ |
2012 | if ( ! m_popupRedirectionUrlPrefixPattern.isEmpty()) { |
2013 | const QString WEBAPP_CONTAINER_DO_NOT_FILTER_PATTERN_URL_ENV_VAR = |
2014 | qgetenv("WEBAPP_CONTAINER_DO_NOT_FILTER_PATTERN_URL"); |
2015 | - m_window->setProperty( |
2016 | - "popupRedirectionUrlPrefixPattern", |
2017 | - WEBAPP_CONTAINER_DO_NOT_FILTER_PATTERN_URL_ENV_VAR == "1" |
2018 | - ? m_popupRedirectionUrlPrefixPattern |
2019 | - : UrlPatternUtils::transformWebappSearchPatternToSafePattern( |
2020 | - m_popupRedirectionUrlPrefixPattern, false)); |
2021 | + QQmlProperty::write( |
2022 | + m_object, QStringLiteral("popupRedirectionUrlPrefixPattern"), |
2023 | + WEBAPP_CONTAINER_DO_NOT_FILTER_PATTERN_URL_ENV_VAR == "1" |
2024 | + ? m_popupRedirectionUrlPrefixPattern |
2025 | + : UrlPatternUtils::transformWebappSearchPatternToSafePattern( |
2026 | + m_popupRedirectionUrlPrefixPattern, false)); |
2027 | } |
2028 | |
2029 | if (!m_userAgentOverride.isEmpty()) { |
2030 | - m_window->setProperty("localUserAgentOverride", m_userAgentOverride); |
2031 | + QQmlProperty::write(m_object, QStringLiteral("localUserAgentOverride"), m_userAgentOverride); |
2032 | } |
2033 | |
2034 | // Experimental, unsupported API, to override the webview |
2035 | QFileInfo overrideFile("webview-override.qml"); |
2036 | if (overrideFile.exists()) { |
2037 | - m_window->setProperty("webviewOverrideFile", QUrl::fromLocalFile(overrideFile.absoluteFilePath())); |
2038 | + QQmlProperty::write(m_object, QStringLiteral("webviewOverrideFile"), |
2039 | + QUrl::fromLocalFile(overrideFile.absoluteFilePath())); |
2040 | } |
2041 | |
2042 | const QString WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY_ENV_VAR = |
2043 | qgetenv("WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY"); |
2044 | if (WEBAPP_CONTAINER_BLOCK_OPEN_URL_EXTERNALLY_ENV_VAR == "1") { |
2045 | - m_window->setProperty("blockOpenExternalUrls", true); |
2046 | + QQmlProperty::write(m_object, QStringLiteral("blockOpenExternalUrls"), true); |
2047 | } |
2048 | |
2049 | bool runningLocalApp = false; |
2050 | QList<QUrl> urls = this->urls(); |
2051 | if (!urls.isEmpty()) { |
2052 | QUrl homeUrl = urls.last(); |
2053 | - m_window->setProperty("url", homeUrl); |
2054 | + QQmlProperty::write(m_object, QStringLiteral("url"), homeUrl); |
2055 | if (UrlPatternUtils::isLocalHtml5ApplicationHomeUrl(homeUrl)) { |
2056 | qDebug() << "Started as a local application container."; |
2057 | runningLocalApp = true; |
2058 | @@ -213,7 +216,7 @@ |
2059 | // webapp-properties.json file pulled from the webapp model element |
2060 | // or from a default local system install (if any). |
2061 | |
2062 | - m_window->setProperty("runningLocalApplication", runningLocalApp); |
2063 | + QQmlProperty::write(m_object, QStringLiteral("runningLocalApplication"), runningLocalApp); |
2064 | |
2065 | // Handle the invalid runtime conditions for the local apps |
2066 | if (runningLocalApp && !isValidLocalApplicationRunningContext()) { |
2067 | @@ -226,9 +229,12 @@ |
2068 | |
2069 | if (qEnvironmentVariableIsSet("WEBAPP_CONTAINER_BLOCKER_DISABLED") |
2070 | && QString(qgetenv("WEBAPP_CONTAINER_BLOCKER_DISABLED")) == "1") { |
2071 | - m_window->setProperty("popupBlockerEnabled", false); |
2072 | + QQmlProperty::write(m_object, QStringLiteral("popupBlockerEnabled"), false); |
2073 | } |
2074 | |
2075 | + QQmlProperty::write(m_object, QStringLiteral("forceFullscreen"), m_fullscreen); |
2076 | + QQmlProperty::write(m_object, QStringLiteral("startMaximized"), m_maximized); |
2077 | + |
2078 | m_component->completeCreate(); |
2079 | |
2080 | return true; |
2081 | @@ -393,6 +399,10 @@ |
2082 | m_openExternalUrlInOverlay = true; |
2083 | } else if (argument.startsWith("--camera-capture-default=")) { |
2084 | m_defaultVideoCaptureCameraPosition = argument.split("--camera-capture-default=")[1]; |
2085 | + } else if (argument == QStringLiteral("--fullscreen")) { |
2086 | + m_fullscreen = true; |
2087 | + } else if (argument == QStringLiteral("--maximized")) { |
2088 | + m_maximized = true; |
2089 | } |
2090 | } |
2091 | } |
2092 | @@ -482,6 +492,21 @@ |
2093 | return urls; |
2094 | } |
2095 | |
2096 | +void WebappContainer::onNewInstanceLaunched(const QStringList& arguments) const |
2097 | +{ |
2098 | + QVariantList urls; |
2099 | + Q_FOREACH(const QString& argument, arguments) { |
2100 | + if (!argument.startsWith(QStringLiteral("-"))) { |
2101 | + QUrl url = QUrl::fromUserInput(argument); |
2102 | + if (url.isValid()) { |
2103 | + urls.append(url); |
2104 | + } |
2105 | + } |
2106 | + } |
2107 | + QMetaObject::invokeMethod(m_object, "openUrls", Q_ARG(QVariant, QVariant(urls))); |
2108 | + QMetaObject::invokeMethod(m_object, "requestActivate"); |
2109 | +} |
2110 | + |
2111 | int main(int argc, char** argv) |
2112 | { |
2113 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); |
2114 | |
2115 | === modified file 'src/app/webcontainer/webapp-container.h' |
2116 | --- src/app/webcontainer/webapp-container.h 2016-06-06 18:46:49 +0000 |
2117 | +++ src/app/webcontainer/webapp-container.h 2016-09-21 07:59:37 +0000 |
2118 | @@ -1,5 +1,5 @@ |
2119 | /* |
2120 | - * Copyright 2013 Canonical Ltd. |
2121 | + * Copyright 2013-2016 Canonical Ltd. |
2122 | * |
2123 | * This file is part of webbrowser-app. |
2124 | * |
2125 | @@ -44,7 +44,7 @@ |
2126 | virtual QList<QUrl> urls() const; |
2127 | |
2128 | private: |
2129 | - virtual void printUsage() const; |
2130 | + void printUsage() const final; |
2131 | void earlyEnvironment(); |
2132 | void parseCommandLine(); |
2133 | void parseExtraConfiguration(); |
2134 | @@ -56,6 +56,9 @@ |
2135 | void setupLocalSchemeFilterIfAny(QQmlContext* context, const QString& webappSearchPath); |
2136 | QString appId() const; |
2137 | |
2138 | +private Q_SLOTS: |
2139 | + void onNewInstanceLaunched(const QStringList& arguments) const final; |
2140 | + |
2141 | private: |
2142 | QString m_webappName; |
2143 | QString m_webappIcon; |
2144 | @@ -74,6 +77,8 @@ |
2145 | QScopedPointer<WebappContainerHelper> m_webappContainerHelper; |
2146 | QScopedPointer<SchemeFilter> m_schemeFilter; |
2147 | QString m_defaultVideoCaptureCameraPosition; |
2148 | + bool m_fullscreen; |
2149 | + bool m_maximized; |
2150 | |
2151 | static const QString URL_PATTERN_SEPARATOR; |
2152 | static const QString LOCAL_SCHEME_FILTER_FILENAME; |
2153 | |
2154 | === modified file 'src/app/webcontainer/webapp-container.qml' |
2155 | --- src/app/webcontainer/webapp-container.qml 2016-07-13 16:23:18 +0000 |
2156 | +++ src/app/webcontainer/webapp-container.qml 2016-09-21 07:59:37 +0000 |
2157 | @@ -53,6 +53,8 @@ |
2158 | |
2159 | property bool runningLocalApplication: false |
2160 | |
2161 | + property bool startMaximized: false |
2162 | + |
2163 | title: getWindowTitle() |
2164 | |
2165 | // Used for testing |
2166 | @@ -79,6 +81,8 @@ |
2167 | WebApp { |
2168 | id: browser |
2169 | |
2170 | + window: root |
2171 | + |
2172 | url: accountProvider.length !== 0 ? "" : root.url |
2173 | |
2174 | accountSwitcher: root.accountSwitcher |
2175 | @@ -108,8 +112,6 @@ |
2176 | |
2177 | anchors.fill: parent |
2178 | |
2179 | - webbrowserWindow: webbrowserWindowProxy |
2180 | - |
2181 | onWebappNameChanged: { |
2182 | if (root.webappName !== browser.webappName) { |
2183 | root.webappName = browser.webappName; |
2184 | @@ -215,6 +217,13 @@ |
2185 | |
2186 | Component.onCompleted: { |
2187 | i18n.domain = "webbrowser-app" |
2188 | + if (forceFullscreen) { |
2189 | + showFullScreen() |
2190 | + } else if (startMaximized) { |
2191 | + showMaximized() |
2192 | + } else { |
2193 | + show() |
2194 | + } |
2195 | } |
2196 | |
2197 | function showWebView() { |
2198 | @@ -300,7 +309,7 @@ |
2199 | return uri |
2200 | } |
2201 | |
2202 | - onOpenUrls: { |
2203 | + function openUrls(urls) { |
2204 | // only consider the first one (if multiple) |
2205 | if (urls.length === 0 || !root.currentWebview) { |
2206 | return; |
2207 | |
2208 | === modified file 'tests/autopilot/webbrowser_app/__init__.py' |
2209 | --- tests/autopilot/webbrowser_app/__init__.py 2015-05-06 22:33:59 +0000 |
2210 | +++ tests/autopilot/webbrowser_app/__init__.py 2016-09-21 07:59:37 +0000 |
2211 | @@ -1,6 +1,6 @@ |
2212 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2213 | # |
2214 | -# Copyright 2013-2015 Canonical |
2215 | +# Copyright 2013-2016 Canonical |
2216 | # |
2217 | # This program is free software: you can redistribute it and/or modify it |
2218 | # under the terms of the GNU General Public License version 3, as published |
2219 | @@ -39,3 +39,6 @@ |
2220 | @property |
2221 | def main_window(self): |
2222 | return self.select_single(browser.Browser) |
2223 | + |
2224 | + def get_windows(self, **kwargs): |
2225 | + return self.select_many(browser.Browser, **kwargs) |
2226 | |
2227 | === modified file 'tests/autopilot/webbrowser_app/emulators/browser.py' |
2228 | --- tests/autopilot/webbrowser_app/emulators/browser.py 2016-05-03 11:28:56 +0000 |
2229 | +++ tests/autopilot/webbrowser_app/emulators/browser.py 2016-09-21 07:59:37 +0000 |
2230 | @@ -52,39 +52,6 @@ |
2231 | def go_forward(self): |
2232 | self.chrome.go_forward() |
2233 | |
2234 | - @autopilot.logging.log_action(logger.info) |
2235 | - def enter_private_mode(self): |
2236 | - if not self.is_in_private_mode(): |
2237 | - self.chrome.toggle_private_mode() |
2238 | - else: |
2239 | - logger.warning('The browser is already in private mode.') |
2240 | - |
2241 | - def is_in_private_mode(self): |
2242 | - return self.get_current_webview().incognito |
2243 | - |
2244 | - @autopilot.logging.log_action(logger.info) |
2245 | - def leave_private_mode(self): |
2246 | - if self.is_in_private_mode(): |
2247 | - self.chrome.toggle_private_mode() |
2248 | - else: |
2249 | - logger.warning('The browser is not in private mode.') |
2250 | - |
2251 | - @autopilot.logging.log_action(logger.info) |
2252 | - def leave_private_mode_with_confirmation(self, confirm=True): |
2253 | - if self.is_in_private_mode(): |
2254 | - self.chrome.toggle_private_mode() |
2255 | - dialog = self._get_leave_private_mode_dialog() |
2256 | - if confirm: |
2257 | - dialog.confirm() |
2258 | - else: |
2259 | - dialog.cancel() |
2260 | - dialog.wait_until_destroyed() |
2261 | - else: |
2262 | - logger.warning('The browser is not in private mode.') |
2263 | - |
2264 | - def _get_leave_private_mode_dialog(self): |
2265 | - return self.wait_select_single(LeavePrivateModeDialog, visible=True) |
2266 | - |
2267 | # Since the NewPrivateTabView does not define any new QML property in its |
2268 | # extended file, it does not report itself to autopilot with the same name |
2269 | # as the extended file. (See http://pad.lv/1454394) |
2270 | @@ -307,13 +274,6 @@ |
2271 | forward_button = self._get_forward_button() |
2272 | return forward_button.enabled |
2273 | |
2274 | - def toggle_private_mode(self): |
2275 | - drawer_button = self.get_drawer_button() |
2276 | - self.pointing_device.click_object(drawer_button) |
2277 | - self.get_drawer() |
2278 | - private_mode_action = self.get_drawer_action("privatemode") |
2279 | - self.pointing_device.click_object(private_mode_action) |
2280 | - |
2281 | def get_drawer_button(self): |
2282 | return self.select_single("ChromeButton", objectName="drawerButton") |
2283 | |
2284 | @@ -573,21 +533,6 @@ |
2285 | key=lambda item: item.globalRect.y) |
2286 | |
2287 | |
2288 | -class LeavePrivateModeDialog(uitk.Dialog): |
2289 | - |
2290 | - @autopilot.logging.log_action(logger.info) |
2291 | - def confirm(self): |
2292 | - confirm_button = self.select_single( |
2293 | - "Button", objectName="leavePrivateModeDialog.okButton") |
2294 | - self.pointing_device.click_object(confirm_button) |
2295 | - |
2296 | - @autopilot.logging.log_action(logger.info) |
2297 | - def cancel(self): |
2298 | - cancel_button = self.select_single( |
2299 | - "Button", objectName="leavePrivateModeDialog.cancelButton") |
2300 | - self.pointing_device.click_object(cancel_button) |
2301 | - |
2302 | - |
2303 | class NewTabView(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
2304 | |
2305 | def get_bookmarks_more_button(self): |
2306 | |
2307 | === modified file 'tests/autopilot/webbrowser_app/tests/__init__.py' |
2308 | --- tests/autopilot/webbrowser_app/tests/__init__.py 2016-03-21 16:13:49 +0000 |
2309 | +++ tests/autopilot/webbrowser_app/tests/__init__.py 2016-09-21 07:59:37 +0000 |
2310 | @@ -176,6 +176,28 @@ |
2311 | |
2312 | return new_tab_view |
2313 | |
2314 | + def open_new_window(self): |
2315 | + windows = self.app.get_windows(incognito=False) |
2316 | + chrome = self.main_window.chrome |
2317 | + drawer_button = chrome.get_drawer_button() |
2318 | + self.pointing_device.click_object(drawer_button) |
2319 | + chrome.get_drawer() |
2320 | + new_window_action = chrome.get_drawer_action("newwindow") |
2321 | + self.pointing_device.click_object(new_window_action) |
2322 | + self.assertThat(lambda: len(self.app.get_windows(incognito=False)), |
2323 | + Eventually(Equals(len(windows) + 1))) |
2324 | + |
2325 | + def open_new_private_window(self): |
2326 | + windows = self.app.get_windows(incognito=True) |
2327 | + chrome = self.main_window.chrome |
2328 | + drawer_button = chrome.get_drawer_button() |
2329 | + self.pointing_device.click_object(drawer_button) |
2330 | + chrome.get_drawer() |
2331 | + new_window_action = chrome.get_drawer_action("newprivatewindow") |
2332 | + self.pointing_device.click_object(new_window_action) |
2333 | + self.assertThat(lambda: len(self.app.get_windows(incognito=True)), |
2334 | + Eventually(Equals(len(windows) + 1))) |
2335 | + |
2336 | def open_settings(self): |
2337 | chrome = self.main_window.chrome |
2338 | drawer_button = chrome.get_drawer_button() |
2339 | |
2340 | === modified file 'tests/autopilot/webbrowser_app/tests/test_contextmenu.py' |
2341 | --- tests/autopilot/webbrowser_app/tests/test_contextmenu.py 2016-03-07 11:35:28 +0000 |
2342 | +++ tests/autopilot/webbrowser_app/tests/test_contextmenu.py 2016-09-21 07:59:37 +0000 |
2343 | @@ -54,6 +54,19 @@ |
2344 | self.assertThat(webview.url, |
2345 | Eventually(StartsWith(self.data_uri_prefix))) |
2346 | |
2347 | + def verify_link_opened_in_a_new_window(self, incognito): |
2348 | + self.assertThat(lambda: len(self.app.get_windows(incognito=incognito)), |
2349 | + Eventually(Equals(1 if incognito else 2))) |
2350 | + windows = self.app.get_windows() |
2351 | + urls = [window.get_current_webview().url for window in windows] |
2352 | + self.assertThat(sorted(urls), |
2353 | + Equals(sorted([self.base_url + "/test1", self.url]))) |
2354 | + if incognito: |
2355 | + windows = self.app.get_windows(incognito=True) |
2356 | + self.assertThat(len(windows), Equals(1)) |
2357 | + self.assertThat(windows[0].get_current_webview().url, |
2358 | + Equals(self.base_url + "/test1")) |
2359 | + |
2360 | |
2361 | class TestContextMenuLink(TestContextMenuBase): |
2362 | |
2363 | @@ -66,6 +79,14 @@ |
2364 | self.menu.click_action("OpenLinkInNewTabContextualAction") |
2365 | self.verify_link_opened_in_a_new_tab() |
2366 | |
2367 | + def test_open_link_in_new_window(self): |
2368 | + self.menu.click_action("OpenLinkInNewWindowContextualAction") |
2369 | + self.verify_link_opened_in_a_new_window(False) |
2370 | + |
2371 | + def test_open_link_in_private_window(self): |
2372 | + self.menu.click_action("OpenLinkInPrivateWindowContextualAction") |
2373 | + self.verify_link_opened_in_a_new_window(True) |
2374 | + |
2375 | def test_bookmark_link(self): |
2376 | self.menu.click_action("BookmarkLinkContextualAction") |
2377 | bookmark_options = self.main_window.get_bookmark_options() |
2378 | @@ -111,6 +132,14 @@ |
2379 | self.menu.click_action("OpenLinkInNewTabContextualAction") |
2380 | self.verify_link_opened_in_a_new_tab() |
2381 | |
2382 | + def test_open_link_in_new_window(self): |
2383 | + self.menu.click_action("OpenLinkInNewWindowContextualAction") |
2384 | + self.verify_link_opened_in_a_new_window(False) |
2385 | + |
2386 | + def test_open_link_in_private_window(self): |
2387 | + self.menu.click_action("OpenLinkInPrivateWindowContextualAction") |
2388 | + self.verify_link_opened_in_a_new_window(True) |
2389 | + |
2390 | def test_bookmark_link(self): |
2391 | self.menu.click_action("BookmarkLinkContextualAction") |
2392 | bookmark_options = self.main_window.get_bookmark_options() |
2393 | |
2394 | === modified file 'tests/autopilot/webbrowser_app/tests/test_fullscreen.py' |
2395 | --- tests/autopilot/webbrowser_app/tests/test_fullscreen.py 2015-07-03 16:04:21 +0000 |
2396 | +++ tests/autopilot/webbrowser_app/tests/test_fullscreen.py 2016-09-21 07:59:37 +0000 |
2397 | @@ -98,57 +98,3 @@ |
2398 | self.main_window.press_key('F11') |
2399 | self.assert_window_fullscreen(False) |
2400 | self.assert_webview_fullscreen(False) |
2401 | - |
2402 | - |
2403 | -class TestForcedFullscreen(TestFullscreenBase): |
2404 | - |
2405 | - def setUp(self): |
2406 | - self.ARGS = self.ARGS + ["--fullscreen"] |
2407 | - super(TestForcedFullscreen, self).setUp(path="/fullscreen") |
2408 | - self.assert_window_fullscreen(True) |
2409 | - self.assert_webview_fullscreen(False) |
2410 | - |
2411 | - def tearDown(self): |
2412 | - super(TestForcedFullscreen, self).tearDown() |
2413 | - self.assert_window_fullscreen(True) |
2414 | - |
2415 | - @testtools.skipIf(model() == "Desktop", "on touch devices only") |
2416 | - def test_disabled_user_exit_swipe_up(self): |
2417 | - self.open_tabs_view() |
2418 | - |
2419 | - @testtools.skipIf(model() != "Desktop", "on desktop only") |
2420 | - def test_disabled_user_exit_ESC(self): |
2421 | - self.main_window.press_key('Escape') |
2422 | - |
2423 | - @testtools.skipIf(model() != "Desktop", "on desktop only") |
2424 | - def test_disabled_user_exit_F11(self): |
2425 | - self.main_window.press_key('F11') |
2426 | - |
2427 | - def trigger_page_fullscreen(self): |
2428 | - webview = self.main_window.get_current_webview() |
2429 | - self.pointing_device.click_object(webview) |
2430 | - self.assert_webview_fullscreen(True) |
2431 | - |
2432 | - def test_page_exit(self): |
2433 | - self.trigger_page_fullscreen() |
2434 | - webview = self.main_window.get_current_webview() |
2435 | - self.pointing_device.click_object(webview) |
2436 | - self.assert_webview_fullscreen(False) |
2437 | - |
2438 | - @testtools.skipIf(model() == "Desktop", "on touch devices only") |
2439 | - def test_user_exit_swipe_up(self): |
2440 | - self.trigger_page_fullscreen() |
2441 | - self.open_tabs_view() |
2442 | - self.assert_webview_fullscreen(False) |
2443 | - |
2444 | - @testtools.skipIf(model() != "Desktop", "on desktop only") |
2445 | - def test_user_exit_ESC(self): |
2446 | - self.trigger_page_fullscreen() |
2447 | - self.main_window.press_key('Escape') |
2448 | - self.assert_webview_fullscreen(False) |
2449 | - |
2450 | - @testtools.skipIf(model() != "Desktop", "on desktop only") |
2451 | - def test_user_exit_F11(self): |
2452 | - self.trigger_page_fullscreen() |
2453 | - self.main_window.press_key('F11') |
2454 | - self.assert_webview_fullscreen(False) |
2455 | |
2456 | === modified file 'tests/autopilot/webbrowser_app/tests/test_keyboard.py' |
2457 | --- tests/autopilot/webbrowser_app/tests/test_keyboard.py 2016-05-23 15:49:49 +0000 |
2458 | +++ tests/autopilot/webbrowser_app/tests/test_keyboard.py 2016-09-21 07:59:37 +0000 |
2459 | @@ -572,3 +572,25 @@ |
2460 | self.check_tab_number(0) |
2461 | webview = self.main_window.get_current_webview() |
2462 | self.assertThat(webview.zoomFactor, Eventually(AlmostEquals(1.1))) |
2463 | + |
2464 | + def test_new_window(self): |
2465 | + self.main_window.press_key('Ctrl+n') |
2466 | + self.assertThat(lambda: len(self.app.get_windows(incognito=False)), |
2467 | + Eventually(Equals(2))) |
2468 | + window = self.app.get_windows(activeFocus=True)[0] |
2469 | + if window.wide: |
2470 | + window.press_key('Ctrl+w') |
2471 | + window.wait_until_destroyed() |
2472 | + windows = self.app.get_windows(activeFocus=True, incognito=False) |
2473 | + self.assertThat(len(windows), Equals(1)) |
2474 | + |
2475 | + def test_new_private_window(self): |
2476 | + self.main_window.press_key('Ctrl+Shift+n') |
2477 | + self.assertThat(lambda: len(self.app.get_windows(incognito=True)), |
2478 | + Eventually(Equals(1))) |
2479 | + window = self.app.get_windows(activeFocus=True, incognito=True)[0] |
2480 | + if window.wide: |
2481 | + window.press_key('Ctrl+w') |
2482 | + window.wait_until_destroyed() |
2483 | + windows = self.app.get_windows(activeFocus=True, incognito=False) |
2484 | + self.assertThat(len(windows), Equals(1)) |
2485 | |
2486 | === added file 'tests/autopilot/webbrowser_app/tests/test_multiple_windows.py' |
2487 | --- tests/autopilot/webbrowser_app/tests/test_multiple_windows.py 1970-01-01 00:00:00 +0000 |
2488 | +++ tests/autopilot/webbrowser_app/tests/test_multiple_windows.py 2016-09-21 07:59:37 +0000 |
2489 | @@ -0,0 +1,40 @@ |
2490 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2491 | +# |
2492 | +# Copyright 2016 Canonical |
2493 | +# |
2494 | +# This program is free software: you can redistribute it and/or modify it |
2495 | +# under the terms of the GNU General Public License version 3, as published |
2496 | +# by the Free Software Foundation. |
2497 | +# |
2498 | +# This program is distributed in the hope that it will be useful, |
2499 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2500 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2501 | +# GNU General Public License for more details. |
2502 | +# |
2503 | +# You should have received a copy of the GNU General Public License |
2504 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2505 | + |
2506 | +from testtools.matchers import Equals |
2507 | + |
2508 | +from webbrowser_app.tests import StartOpenRemotePageTestCaseBase |
2509 | + |
2510 | + |
2511 | +class TestMultipleWindows(StartOpenRemotePageTestCaseBase): |
2512 | + |
2513 | + def test_open_new_window(self): |
2514 | + windows = self.app.get_windows() |
2515 | + self.assertThat(len(windows), Equals(1)) |
2516 | + self.open_new_window() |
2517 | + windows = self.app.get_windows() |
2518 | + self.assertThat(len(windows), Equals(2)) |
2519 | + for window in windows: |
2520 | + self.assertThat(window.incognito, Equals(False)) |
2521 | + |
2522 | + def test_open_new_private_window(self): |
2523 | + windows = self.app.get_windows() |
2524 | + self.assertThat(len(windows), Equals(1)) |
2525 | + self.open_new_private_window() |
2526 | + windows = self.app.get_windows() |
2527 | + self.assertThat(len(windows), Equals(2)) |
2528 | + self.assertThat(len(self.app.get_windows(incognito=False)), Equals(1)) |
2529 | + self.assertThat(len(self.app.get_windows(incognito=True)), Equals(1)) |
2530 | |
2531 | === modified file 'tests/autopilot/webbrowser_app/tests/test_new_tab_view.py' |
2532 | --- tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2016-06-15 16:21:51 +0000 |
2533 | +++ tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2016-09-21 07:59:37 +0000 |
2534 | @@ -95,55 +95,6 @@ |
2535 | self.assertThat(new_tab_view.visible, Equals(True)) |
2536 | |
2537 | |
2538 | -class TestNewPrivateTabViewLifetime(StartOpenRemotePageTestCaseBase): |
2539 | - |
2540 | - def test_new_private_tab_view_destroyed_when_browsing(self): |
2541 | - self.main_window.enter_private_mode() |
2542 | - new_private_tab_view = self.main_window.get_new_private_tab_view() |
2543 | - self.main_window.go_to_url(self.base_url + "/test2") |
2544 | - new_private_tab_view.wait_until_destroyed() |
2545 | - |
2546 | - def test_new_private_tab_view_destroyed_when_leaving_private_mode(self): |
2547 | - self.main_window.enter_private_mode() |
2548 | - new_private_tab_view = self.main_window.get_new_private_tab_view() |
2549 | - self.main_window.leave_private_mode() |
2550 | - new_private_tab_view.wait_until_destroyed() |
2551 | - |
2552 | - def test_new_private_tab_view_is_shared_between_tabs(self): |
2553 | - self.main_window.enter_private_mode() |
2554 | - new_private_tab_view = self.main_window.get_new_private_tab_view() |
2555 | - self.main_window.go_to_url(self.base_url + "/test2") |
2556 | - new_private_tab_view.wait_until_destroyed() |
2557 | - # Open one new private tab |
2558 | - new_private_tab_view = self.open_new_tab(open_tabs_view=True) |
2559 | - # Open a second new private tab |
2560 | - new_private_tab_view_2 = self.open_new_tab(open_tabs_view=True) |
2561 | - # Verify that they share the same NewPrivateTabView instance |
2562 | - self.assertThat(new_private_tab_view_2.id, |
2563 | - Equals(new_private_tab_view.id)) |
2564 | - # Close the second new private tab, and verify that the |
2565 | - # NewPrivateTabView instance is still there |
2566 | - if self.main_window.wide: |
2567 | - self.main_window.chrome.get_tabs_bar().close_tab(2) |
2568 | - else: |
2569 | - tabs_view = self.open_tabs_view() |
2570 | - tabs_view.get_previews()[0].close() |
2571 | - toolbar = self.main_window.get_recent_view_toolbar() |
2572 | - toolbar.click_button("doneButton") |
2573 | - tabs_view.visible.wait_for(False) |
2574 | - self.assertThat(new_private_tab_view.visible, Equals(True)) |
2575 | - # Close the first new private tab, and verify that the |
2576 | - # NewPrivateTabView instance is destroyed |
2577 | - if self.main_window.wide: |
2578 | - self.main_window.chrome.get_tabs_bar().close_tab(1) |
2579 | - else: |
2580 | - tabs_view = self.open_tabs_view() |
2581 | - tabs_view.get_previews()[0].close() |
2582 | - toolbar = self.main_window.get_recent_view_toolbar() |
2583 | - toolbar.click_button("doneButton") |
2584 | - new_private_tab_view.wait_until_destroyed() |
2585 | - |
2586 | - |
2587 | class TestNewTabViewContentsBase(StartOpenRemotePageTestCaseBase): |
2588 | |
2589 | def setUp(self): |
2590 | |
2591 | === removed file 'tests/autopilot/webbrowser_app/tests/test_private.py' |
2592 | --- tests/autopilot/webbrowser_app/tests/test_private.py 2016-03-29 20:08:14 +0000 |
2593 | +++ tests/autopilot/webbrowser_app/tests/test_private.py 1970-01-01 00:00:00 +0000 |
2594 | @@ -1,115 +0,0 @@ |
2595 | -# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2596 | -# |
2597 | -# Copyright 2015-2016 Canonical |
2598 | -# |
2599 | -# This program is free software: you can redistribute it and/or modify it |
2600 | -# under the terms of the GNU General Public License version 3, as published |
2601 | -# by the Free Software Foundation. |
2602 | -# |
2603 | -# This program is distributed in the hope that it will be useful, |
2604 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2605 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2606 | -# GNU General Public License for more details. |
2607 | -# |
2608 | -# You should have received a copy of the GNU General Public License |
2609 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2610 | - |
2611 | -from testtools.matchers import Equals, NotEquals |
2612 | -from autopilot.matchers import Eventually |
2613 | -from autopilot.platform import model |
2614 | - |
2615 | -from webbrowser_app.tests import StartOpenRemotePageTestCaseBase |
2616 | - |
2617 | - |
2618 | -class TestPrivateView(StartOpenRemotePageTestCaseBase): |
2619 | - |
2620 | - def test_going_in_and_out_private_mode(self): |
2621 | - address_bar = self.main_window.address_bar |
2622 | - self.main_window.enter_private_mode() |
2623 | - self.assertThat(self.main_window.is_in_private_mode, |
2624 | - Eventually(Equals(True))) |
2625 | - self.assert_number_incognito_webviews_eventually(1) |
2626 | - self.assertTrue(self.main_window.is_new_private_tab_view_visible()) |
2627 | - self.assertThat(address_bar.activeFocus, |
2628 | - Eventually(Equals(model() == 'Desktop'))) |
2629 | - self.assertThat(address_bar.text, Eventually(Equals(""))) |
2630 | - |
2631 | - self.main_window.leave_private_mode() |
2632 | - self.assertThat(self.main_window.is_in_private_mode, |
2633 | - Eventually(Equals(False))) |
2634 | - self.assert_number_incognito_webviews_eventually(0) |
2635 | - self.assertThat(address_bar.text, Eventually(NotEquals(""))) |
2636 | - self.assertThat(address_bar.activeFocus, Eventually(Equals(False))) |
2637 | - |
2638 | - def test_leaving_private_mode_with_multiples_tabs_ask_confirmation(self): |
2639 | - self.main_window.enter_private_mode() |
2640 | - self.assertThat(self.main_window.is_in_private_mode, |
2641 | - Eventually(Equals(True))) |
2642 | - self.assertTrue(self.main_window.is_new_private_tab_view_visible()) |
2643 | - if not self.main_window.wide: |
2644 | - self.open_tabs_view() |
2645 | - self.open_new_tab() |
2646 | - self.main_window.leave_private_mode_with_confirmation() |
2647 | - self.assertThat(self.main_window.is_in_private_mode, |
2648 | - Eventually(Equals(False))) |
2649 | - |
2650 | - def test_cancel_leaving_private_mode(self): |
2651 | - self.main_window.enter_private_mode() |
2652 | - self.assertThat(self.main_window.is_in_private_mode, |
2653 | - Eventually(Equals(True))) |
2654 | - self.assertTrue(self.main_window.is_new_private_tab_view_visible()) |
2655 | - if not self.main_window.wide: |
2656 | - self.open_tabs_view() |
2657 | - self.open_new_tab() |
2658 | - self.main_window.leave_private_mode_with_confirmation(confirm=False) |
2659 | - self.assertThat(self.main_window.is_in_private_mode, |
2660 | - Eventually(Equals(True))) |
2661 | - self.assertTrue(self.main_window.is_new_private_tab_view_visible()) |
2662 | - |
2663 | - def test_url_showing_in_top_sites_in_and_out_private_mode(self): |
2664 | - new_tab = self.open_new_tab(open_tabs_view=True) |
2665 | - urls = [site.url for site in new_tab.get_top_site_items()] |
2666 | - self.assertIn(self.url, urls) |
2667 | - |
2668 | - self.main_window.enter_private_mode() |
2669 | - self.assertThat(self.main_window.is_in_private_mode, |
2670 | - Eventually(Equals(True))) |
2671 | - url = self.base_url + "/test2" |
2672 | - self.main_window.go_to_url(url) |
2673 | - self.main_window.wait_until_page_loaded(url) |
2674 | - self.main_window.leave_private_mode() |
2675 | - self.assertThat(self.main_window.is_in_private_mode, |
2676 | - Eventually(Equals(False))) |
2677 | - |
2678 | - new_tab = self.open_new_tab(open_tabs_view=True) |
2679 | - urls = [site.url for site in new_tab.get_top_site_items()] |
2680 | - self.assertNotIn(url, urls) |
2681 | - |
2682 | - def test_public_tabs_should_not_be_visible_in_private_mode(self): |
2683 | - self.open_new_tab(open_tabs_view=True) |
2684 | - new_tab_view = self.main_window.get_new_tab_view() |
2685 | - url = self.base_url + "/test2" |
2686 | - self.main_window.go_to_url(url) |
2687 | - new_tab_view.wait_until_destroyed() |
2688 | - if self.main_window.wide: |
2689 | - tabs = self.main_window.chrome.get_tabs_bar().get_tabs() |
2690 | - self.assertThat(len(tabs), Equals(2)) |
2691 | - else: |
2692 | - tabs_view = self.open_tabs_view() |
2693 | - previews = tabs_view.get_previews() |
2694 | - self.assertThat(len(previews), Equals(2)) |
2695 | - toolbar = self.main_window.get_recent_view_toolbar() |
2696 | - toolbar.click_button("doneButton") |
2697 | - tabs_view.visible.wait_for(False) |
2698 | - |
2699 | - self.main_window.enter_private_mode() |
2700 | - self.assertThat(self.main_window.is_in_private_mode, |
2701 | - Eventually(Equals(True))) |
2702 | - self.assertTrue(self.main_window.is_new_private_tab_view_visible()) |
2703 | - if self.main_window.wide: |
2704 | - tabs = self.main_window.chrome.get_tabs_bar().get_tabs() |
2705 | - self.assertThat(len(tabs), Equals(1)) |
2706 | - else: |
2707 | - tabs_view = self.open_tabs_view() |
2708 | - previews = tabs_view.get_previews() |
2709 | - self.assertThat(len(previews), Equals(1)) |
2710 | |
2711 | === modified file 'tests/autopilot/webbrowser_app/tests/test_tabs.py' |
2712 | --- tests/autopilot/webbrowser_app/tests/test_tabs.py 2016-01-21 10:29:17 +0000 |
2713 | +++ tests/autopilot/webbrowser_app/tests/test_tabs.py 2016-09-21 07:59:37 +0000 |
2714 | @@ -319,48 +319,3 @@ |
2715 | self.main_window.press_key('Ctrl+Shift+t') |
2716 | self.assert_number_webviews_eventually(3) |
2717 | self.check_current_tab(url2) |
2718 | - |
2719 | - @testtools.skipIf(model() != "Desktop", "on desktop only") |
2720 | - def test_undo_close_tab_incognito(self): |
2721 | - start_url = self.main_window.get_current_webview().url |
2722 | - self.open_new_tab(open_tabs_view=not self.main_window.wide) |
2723 | - url = self.base_url + "/tab/1" |
2724 | - self.main_window.go_to_url(url) |
2725 | - self.main_window.wait_until_page_loaded(url) |
2726 | - |
2727 | - self.main_window.press_key('Ctrl+w') |
2728 | - self.assert_number_webviews_eventually(1) |
2729 | - self.check_current_tab(start_url) |
2730 | - |
2731 | - incognito_url = self.base_url + "/tab/2" |
2732 | - self.main_window.enter_private_mode() |
2733 | - self.assertThat(self.main_window.is_in_private_mode, |
2734 | - Eventually(Equals(True))) |
2735 | - self.main_window.go_to_url(incognito_url) |
2736 | - self.main_window.wait_until_page_loaded(incognito_url) |
2737 | - |
2738 | - self.open_new_tab(open_tabs_view=not self.main_window.wide) |
2739 | - self.main_window.go_to_url(incognito_url) |
2740 | - self.main_window.wait_until_page_loaded(incognito_url) |
2741 | - self.main_window.press_key('Ctrl+w') |
2742 | - self.assert_number_incognito_webviews_eventually(1) |
2743 | - |
2744 | - # Test that no tabs will be restored in incognito mode. |
2745 | - # We sleep for a bit after the keypress to give more confidence that |
2746 | - # the tab still hasn't appeared within a reasonable amount of time. |
2747 | - self.main_window.press_key('Ctrl+Shift+w') |
2748 | - time.sleep(1) |
2749 | - self.assert_number_incognito_webviews_eventually(1) |
2750 | - |
2751 | - # Close the last incognito tab to exit the mode. This is done on |
2752 | - # purpose instead of using leave_private_mode since we want to |
2753 | - # make sure the last tab is also not saved, as it is a corner case |
2754 | - self.main_window.press_key('Ctrl+w') |
2755 | - self.assertThat(self.main_window.is_in_private_mode, |
2756 | - Eventually(Equals(False))) |
2757 | - |
2758 | - # Tabs that we closed before going incognito will be restored |
2759 | - # when going back to default mode |
2760 | - self.main_window.press_key('Ctrl+Shift+w') |
2761 | - self.assert_number_webviews_eventually(2) |
2762 | - self.check_current_tab(url) |
2763 | |
2764 | === added file 'tests/unittests/qml/tst_BrowserWindow.qml' |
2765 | --- tests/unittests/qml/tst_BrowserWindow.qml 1970-01-01 00:00:00 +0000 |
2766 | +++ tests/unittests/qml/tst_BrowserWindow.qml 2016-09-21 07:59:37 +0000 |
2767 | @@ -0,0 +1,106 @@ |
2768 | +/* |
2769 | + * Copyright 2016 Canonical Ltd. |
2770 | + * |
2771 | + * This file is part of webbrowser-app. |
2772 | + * |
2773 | + * webbrowser-app is free software; you can redistribute it and/or modify |
2774 | + * it under the terms of the GNU General Public License as published by |
2775 | + * the Free Software Foundation; version 3. |
2776 | + * |
2777 | + * webbrowser-app is distributed in the hope that it will be useful, |
2778 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2779 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2780 | + * GNU General Public License for more details. |
2781 | + * |
2782 | + * You should have received a copy of the GNU General Public License |
2783 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2784 | + */ |
2785 | + |
2786 | +import QtQuick 2.4 |
2787 | +import QtQuick.Window 2.2 |
2788 | +import QtTest 1.0 |
2789 | +import "../../../src/app" |
2790 | + |
2791 | +Item { |
2792 | + width: 200 |
2793 | + height: 200 |
2794 | + |
2795 | + QtObject { |
2796 | + id: webviewMock |
2797 | + property bool fullscreen: false |
2798 | + } |
2799 | + |
2800 | + Component { |
2801 | + id: windowFactory |
2802 | + BrowserWindow { |
2803 | + currentWebview: webviewMock |
2804 | + } |
2805 | + } |
2806 | + |
2807 | + SignalSpy { |
2808 | + id: visibilitySpy |
2809 | + target: testcase.currentWindow |
2810 | + signalName: "visibilityChanged" |
2811 | + } |
2812 | + |
2813 | + TestCase { |
2814 | + id: testcase |
2815 | + name: "BrowserWindow" |
2816 | + when: windowShown |
2817 | + |
2818 | + property var currentWindow |
2819 | + |
2820 | + function init() { |
2821 | + currentWindow = windowFactory.createObject(null) |
2822 | + currentWindow.show() |
2823 | + compare(currentWindow.visibility, Window.Windowed) |
2824 | + visibilitySpy.clear() |
2825 | + } |
2826 | + |
2827 | + function cleanup() { |
2828 | + currentWindow.destroy() |
2829 | + } |
2830 | + |
2831 | + function test_fullscreen_data() { |
2832 | + return [ |
2833 | + {forceFullscreen: false, state: Window.Windowed}, |
2834 | + {forceFullscreen: false, state: Window.Minimized}, |
2835 | + {forceFullscreen: false, state: Window.Maximized}, |
2836 | + {forceFullscreen: false, state: Window.Hidden}, |
2837 | + {forceFullscreen: true, state: Window.FullScreen}, |
2838 | + ] |
2839 | + } |
2840 | + |
2841 | + function test_fullscreen_on_webview_fullscreen_data() { |
2842 | + return test_fullscreen_data() |
2843 | + } |
2844 | + |
2845 | + function test_fullscreen_on_webview_fullscreen(data) { |
2846 | + currentWindow.forceFullscreen = data.forceFullscreen |
2847 | + currentWindow.visibility = data.state |
2848 | + visibilitySpy.clear() |
2849 | + webviewMock.fullscreen = true |
2850 | + tryCompare(visibilitySpy, "count", data.forceFullscreen ? 0 : 1) |
2851 | + compare(currentWindow.visibility, Window.FullScreen) |
2852 | + webviewMock.fullscreen = false |
2853 | + tryCompare(visibilitySpy, "count", data.forceFullscreen ? 0 : 2) |
2854 | + compare(currentWindow.visibility, data.state) |
2855 | + } |
2856 | + |
2857 | + function test_setfullscreen_data() { |
2858 | + return test_fullscreen_data() |
2859 | + } |
2860 | + |
2861 | + function test_setfullscreen(data) { |
2862 | + currentWindow.forceFullscreen = data.forceFullscreen |
2863 | + currentWindow.visibility = data.state |
2864 | + visibilitySpy.clear() |
2865 | + currentWindow.setFullscreen(true) |
2866 | + tryCompare(visibilitySpy, "count", data.forceFullscreen ? 0 : 1) |
2867 | + compare(currentWindow.visibility, Window.FullScreen) |
2868 | + currentWindow.setFullscreen(false) |
2869 | + tryCompare(visibilitySpy, "count", data.forceFullscreen ? 0 : 2) |
2870 | + compare(currentWindow.visibility, data.state) |
2871 | + } |
2872 | + } |
2873 | +} |
FAILED: Continuous integration, rev:1485 /jenkins. canonical. com/system- apps/job/ lp-webbrowser- app-ci/ 602/ /jenkins. canonical. com/system- apps/job/ build/1245/ console /jenkins. canonical. com/system- apps/job/ build-0- fetch/1245 /jenkins. canonical. com/system- apps/job/ build-1- sourcepkg/ release= vivid+overlay/ 1117 /jenkins. canonical. com/system- apps/job/ build-1- sourcepkg/ release= xenial+ overlay/ 1117 /jenkins. canonical. com/system- apps/job/ build-1- sourcepkg/ release= yakkety/ 1117 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 1104 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 1104/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 1104 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 1104/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 1104/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 1104 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 1104/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 1104 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 1104/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 1104/console /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 1104 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 1104/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 1104 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 1104/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= yakkety/ 1104/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/system- apps/job/ lp-webbrowser- app-ci/ 602/rebuild
https:/