Merge lp:~osomon/webbrowser-app/remove-formFactor into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 1367
Merged at revision: 1361
Proposed branch: lp:~osomon/webbrowser-app/remove-formFactor
Merge into: lp:webbrowser-app
Diff against target: 1105 lines (+475/-141)
22 files modified
README (+0/-5)
debian/control (+1/-0)
src/Ubuntu/Web/UbuntuWebView02.qml (+2/-2)
src/Ubuntu/Web/plugin.cpp (+1/-38)
src/app/BrowserView.qml (+2/-0)
src/app/CMakeLists.txt (+7/-1)
src/app/browserapplication.cpp (+42/-14)
src/app/meminfo.cpp (+137/-0)
src/app/meminfo.h (+65/-0)
src/app/webbrowser/Browser.qml (+69/-31)
src/app/webbrowser/BrowserTab.qml (+3/-0)
src/app/webbrowser/Chrome.qml (+3/-1)
src/app/webbrowser/SettingsPage.qml (+1/-20)
src/app/webbrowser/TabItem.qml (+9/-8)
src/app/webbrowser/TabsBar.qml (+4/-1)
src/app/webbrowser/webbrowser-app.cpp (+2/-0)
src/app/webcontainer/WebApp.qml (+3/-2)
src/app/webcontainer/WebViewImplOxide.qml (+6/-11)
tests/autopilot/webbrowser_app/tests/__init__.py (+2/-7)
tests/unittests/CMakeLists.txt (+1/-0)
tests/unittests/meminfo/CMakeLists.txt (+15/-0)
tests/unittests/meminfo/tst_MemInfoTests.cpp (+100/-0)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/remove-formFactor
Reviewer Review Type Date Requested Status
Alexandre Abreu (community) Approve
PS Jenkins bot continuous-integration Needs Fixing
Review via email: mp+285539@code.launchpad.net

Commit message

Remove the 'formFactor' context property, and replace all its uses by more meaningful conditions.

Description of the change

Note: as of 2016-02-07, there are 7 applications in the store that import Ubuntu.Web and have at least one reference to the 'formFactor' context property. Those are:

 com.nokia.heremaps 1.0.7
 com.ubuntu.developer.webapps.googleplus 0.7.1
 googleapps.mattirn 1.0.1
 mynewsapps.mattirn 1.0.4
 mywebsites.mattirn 1.0.1
 ubuntu.mattirn 1.0.1
 wikifoundation.mattirn 1.0.2

Canonical is responsible for the first two, we need to update them. I’m going to reach out to mattirn to suggest how to fix his apps so they don’t refer to 'formFactor'.

In any case, I don’t expect any important functionality to suddenly break even if the property was removed without updating those apps.

UPDATE: as of 2016-02-18, the last five apps in the above list (all by mattirn) have been updated, they don’t have references to 'formFactor' any longer. com.ubuntu.developer.webapps.googleplus has also been updated, the last remaining app is com.nokia.heremaps for which I’ve submitted an update (waiting for a review).

To post a comment you must log in.
Revision history for this message
Chris Coulson (chrisccoulson) wrote :

I've added a comment inline to this

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

Verify that a geolocation permission request originates from the webapp’s domain before silently accepting it.

1364. By Olivier Tilloy

When low on memory, the current public tab might have been unloaded, so reload it when exiting incognito mode.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

Overall looks good, a few questions below

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

Thanks for your thorough review, I’ve replied to your questions (with more questions!) inline.

Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

See replies

1365. By Olivier Tilloy

Document the value of the lowOnMemory threshold.

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

Updated.

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

Do not try to load an non existent tab.

1367. By Olivier Tilloy

Raise the threshold for which a system is considered low on memory, from tests run on a MX4.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2015-04-01 05:57:42 +0000
3+++ README 2016-02-23 11:14:53 +0000
4@@ -86,11 +86,6 @@
5 the OpenSearch document description format
6 (http://www.opensearch.org/Specifications/OpenSearch/1.1)
7
8- - 'allowOpenInBackgroundTab': whether to offer an option to open a link in a
9- new background tab in the contextual menu. Possible values are "true",
10- "false", and "default" (which resolves to true on desktop and false on
11- mobile).
12-
13 - restoreSession: whether to restore the previous browsing session at startup
14 (defaults to true)
15
16
17=== modified file 'debian/control'
18--- debian/control 2016-01-28 16:23:17 +0000
19+++ debian/control 2016-02-23 11:14:53 +0000
20@@ -10,6 +10,7 @@
21 dh-apparmor,
22 dh-translations,
23 hardening-wrapper,
24+ libapparmor-dev,
25 libevdev-dev,
26 liboxideqt-qmlplugin (>= 1.9),
27 libqt5sql5-sqlite,
28
29=== modified file 'src/Ubuntu/Web/UbuntuWebView02.qml'
30--- src/Ubuntu/Web/UbuntuWebView02.qml 2016-01-05 08:35:58 +0000
31+++ src/Ubuntu/Web/UbuntuWebView02.qml 2016-02-23 11:14:53 +0000
32@@ -1,5 +1,5 @@
33 /*
34- * Copyright 2013-2015 Canonical Ltd.
35+ * Copyright 2013-2016 Canonical Ltd.
36 *
37 * This file is part of webbrowser-app.
38 *
39@@ -51,7 +51,7 @@
40 navigationRequestedDelegate(request);
41 }
42
43- preferences.passwordEchoEnabled: formFactor === "mobile"
44+ preferences.passwordEchoEnabled: Qt.inputMethod.visible
45
46 popupMenu: ItemSelector02 {
47 automaticOrientation: false
48
49=== modified file 'src/Ubuntu/Web/plugin.cpp'
50--- src/Ubuntu/Web/plugin.cpp 2015-12-10 22:01:05 +0000
51+++ src/Ubuntu/Web/plugin.cpp 2016-02-23 11:14:53 +0000
52@@ -1,5 +1,5 @@
53 /*
54- * Copyright 2013-2015 Canonical Ltd.
55+ * Copyright 2013-2016 Canonical Ltd.
56 *
57 * This file is part of webbrowser-app.
58 *
59@@ -40,7 +40,6 @@
60
61 Q_PROPERTY(QString cacheLocation READ cacheLocation NOTIFY cacheLocationChanged)
62 Q_PROPERTY(QString dataLocation READ dataLocation NOTIFY dataLocationChanged)
63- Q_PROPERTY(QString formFactor READ formFactor CONSTANT)
64 Q_PROPERTY(qreal screenDiagonal READ screenDiagonal NOTIFY screenDiagonalChanged)
65 Q_PROPERTY(int cacheSizeHint READ cacheSizeHint NOTIFY cacheSizeHintChanged)
66 Q_PROPERTY(QString webviewDevtoolsDebugHost READ devtoolsHost CONSTANT)
67@@ -52,7 +51,6 @@
68
69 QString cacheLocation() const;
70 QString dataLocation() const;
71- QString formFactor();
72 qreal screenDiagonal() const;
73 int cacheSizeHint() const;
74 QString devtoolsHost();
75@@ -71,7 +69,6 @@
76
77 private:
78 qreal m_screenDiagonal; // in millimeters
79- QString m_formFactor;
80 QString m_devtoolsHost;
81 int m_devtoolsPort;
82 QStringList m_hostMappingRules;
83@@ -129,40 +126,6 @@
84 return location.absolutePath();
85 }
86
87-QString UbuntuWebPluginContext::formFactor()
88-{
89- if (m_formFactor.isEmpty()) {
90- // This implementation only considers two possible form factors: desktop,
91- // and mobile (which includes phones and tablets).
92- // XXX: do we need to consider other form factors, such as tablet?
93- const char* DESKTOP = "desktop";
94- const char* MOBILE = "mobile";
95-
96- // The "DESKTOP_MODE" environment variable can be used to force the form
97- // factor to desktop, when set to any valid value other than 0.
98- const char* DESKTOP_MODE_ENV_VAR = "DESKTOP_MODE";
99- if (qEnvironmentVariableIsSet(DESKTOP_MODE_ENV_VAR)) {
100- QByteArray stringValue = qgetenv(DESKTOP_MODE_ENV_VAR);
101- bool ok = false;
102- int value = stringValue.toInt(&ok);
103- if (ok) {
104- m_formFactor = (value == 0) ? MOBILE : DESKTOP;
105- return m_formFactor;
106- }
107- }
108-
109- // XXX: Assume that QtUbuntu means mobile, which is currently the case,
110- // but may not remain true forever.
111- QString platform = QGuiApplication::platformName();
112- if ((platform == "ubuntu") || (platform == "ubuntumirclient")) {
113- m_formFactor = MOBILE;
114- } else {
115- m_formFactor = DESKTOP;
116- }
117- }
118- return m_formFactor;
119-}
120-
121 qreal UbuntuWebPluginContext::screenDiagonal() const
122 {
123 return m_screenDiagonal;
124
125=== modified file 'src/app/BrowserView.qml'
126--- src/app/BrowserView.qml 2016-01-12 09:37:08 +0000
127+++ src/app/BrowserView.qml 2016-02-23 11:14:53 +0000
128@@ -32,6 +32,8 @@
129
130 property var osk: _osk
131
132+ property bool hasTouchScreen: false
133+
134 // See http://design.canonical.com/2015/05/to-converge-onto-mobile-tablet-and-desktop-think-grid-units/
135 readonly property bool wide: width >= units.gu(90)
136
137
138=== modified file 'src/app/CMakeLists.txt'
139--- src/app/CMakeLists.txt 2016-01-28 18:54:35 +0000
140+++ src/app/CMakeLists.txt 2016-02-23 11:14:53 +0000
141@@ -7,6 +7,9 @@
142 find_package(Qt5Quick REQUIRED)
143 find_package(Qt5Widgets REQUIRED)
144
145+include(FindPkgConfig)
146+pkg_check_modules(LIBAPPARMOR REQUIRED libapparmor)
147+
148 add_subdirectory(unity8)
149
150 configure_file(
151@@ -19,6 +22,7 @@
152 set(COMMONLIB_SRC
153 browserapplication.cpp
154 favicon-fetcher.cpp
155+ meminfo.cpp
156 mime-database.cpp
157 session-storage.cpp
158 single-instance-manager.cpp
159@@ -28,7 +32,8 @@
160 add_library(${COMMONLIB} STATIC ${COMMONLIB_SRC})
161
162 include_directories(${unity8_SOURCE_DIR}/libs/UbuntuGestures
163- ${unity8_SOURCE_DIR}/plugins)
164+ ${unity8_SOURCE_DIR}/plugins
165+ ${LIBAPPARMOR_INCLUDE_DIRS})
166 target_link_libraries(${COMMONLIB}
167 Qt5::Core
168 Qt5::Gui
169@@ -38,6 +43,7 @@
170 Qt5::Widgets
171 UbuntuGesturesQml
172 InputInfo
173+ ${LIBAPPARMOR_LDFLAGS}
174 )
175
176 file(GLOB QML_FILES *.qml)
177
178=== modified file 'src/app/browserapplication.cpp'
179--- src/app/browserapplication.cpp 2016-01-28 18:54:35 +0000
180+++ src/app/browserapplication.cpp 2016-02-23 11:14:53 +0000
181@@ -16,9 +16,15 @@
182 * along with this program. If not, see <http://www.gnu.org/licenses/>.
183 */
184
185+// system
186+#include <cerrno>
187+#include <cstring>
188+#include <sys/apparmor.h>
189+
190 // Qt
191 #include <QtCore/QMetaObject>
192 #include <QtCore/QtGlobal>
193+#include <QtGui/QTouchDevice>
194 #include <QtNetwork/QNetworkInterface>
195 #include <QtQml/QQmlComponent>
196 #include <QtQml/QQmlContext>
197@@ -30,6 +36,7 @@
198 #include "browserapplication.h"
199 #include "config.h"
200 #include "favicon-fetcher.h"
201+#include "meminfo.h"
202 #include "mime-database.h"
203 #include "session-storage.h"
204 #include "webbrowser-window.h"
205@@ -100,19 +107,17 @@
206 return QString();
207 }
208
209-static QObject* MimeDatabase_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
210-{
211- Q_UNUSED(engine);
212- Q_UNUSED(scriptEngine);
213- return new MimeDatabase();
214-}
215-
216-static QObject* Direction_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
217-{
218- Q_UNUSED(engine);
219- Q_UNUSED(scriptEngine);
220- return new Direction();
221-}
222+#define MAKE_SINGLETON_FACTORY(type) \
223+ static QObject* type##_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine) { \
224+ Q_UNUSED(engine); \
225+ Q_UNUSED(scriptEngine); \
226+ return new type(); \
227+ }
228+
229+MAKE_SINGLETON_FACTORY(MemInfo)
230+MAKE_SINGLETON_FACTORY(MimeDatabase)
231+MAKE_SINGLETON_FACTORY(Direction)
232+
233
234 bool BrowserApplication::initialize(const QString& qmlFileSubPath)
235 {
236@@ -149,6 +154,18 @@
237 return false;
238 }
239
240+ bool runningConfined = true;
241+ char* label;
242+ char* mode;
243+ if (aa_getcon(&label, &mode) != -1) {
244+ if (strcmp(label, "unconfined") == 0) {
245+ runningConfined = false;
246+ }
247+ free(label);
248+ } else if (errno == EINVAL) {
249+ runningConfined = false;
250+ }
251+
252 QString devtoolsPort = inspectorPort();
253 QString devtoolsHost = inspectorHost();
254 bool inspectorEnabled = !devtoolsPort.isEmpty();
255@@ -159,6 +176,7 @@
256
257 const char* uri = "webbrowsercommon.private";
258 qmlRegisterType<FaviconFetcher>(uri, 0, 1, "FaviconFetcher");
259+ qmlRegisterSingletonType<MemInfo>(uri, 0, 1, "MemInfo", MemInfo_singleton_factory);
260 qmlRegisterSingletonType<MimeDatabase>(uri, 0, 1, "MimeDatabase", MimeDatabase_singleton_factory);
261 qmlRegisterType<SessionStorage>(uri, 0, 1, "SessionStorage");
262
263@@ -179,6 +197,9 @@
264 qmlEngineCreated(m_engine);
265
266 QQmlContext* context = m_engine->rootContext();
267+ context->setContextProperty("__runningConfined", runningConfined);
268+ context->setContextProperty("unversionedAppId", unversionedAppId);
269+
270 m_component = new QQmlComponent(m_engine);
271 m_component->loadUrl(QUrl::fromLocalFile(UbuntuBrowserDirectory() + "/" + qmlFileSubPath));
272 if (!m_component->isReady()) {
273@@ -187,7 +208,6 @@
274 }
275 m_webbrowserWindowProxy = new WebBrowserWindow();
276 context->setContextProperty("webbrowserWindowProxy", m_webbrowserWindowProxy);
277- context->setContextProperty("unversionedAppId", unversionedAppId);
278
279 QObject* browser = m_component->beginCreate(context);
280 m_window = qobject_cast<QQuickWindow*>(browser);
281@@ -198,6 +218,14 @@
282 browser->setProperty("developerExtrasEnabled", inspectorEnabled);
283 browser->setProperty("forceFullscreen", m_arguments.contains("--fullscreen"));
284
285+ bool hasTouchScreen = false;
286+ Q_FOREACH(const QTouchDevice* device, QTouchDevice::devices()) {
287+ if (device->type() == QTouchDevice::TouchScreen) {
288+ hasTouchScreen = true;
289+ }
290+ }
291+ browser->setProperty("hasTouchScreen", hasTouchScreen);
292+
293 return true;
294 }
295
296
297=== added file 'src/app/meminfo.cpp'
298--- src/app/meminfo.cpp 1970-01-01 00:00:00 +0000
299+++ src/app/meminfo.cpp 2016-02-23 11:14:53 +0000
300@@ -0,0 +1,137 @@
301+/*
302+ * Copyright 2016 Canonical Ltd.
303+ *
304+ * This file is part of webbrowser-app.
305+ *
306+ * webbrowser-app is free software; you can redistribute it and/or modify
307+ * it under the terms of the GNU General Public License as published by
308+ * the Free Software Foundation; version 3.
309+ *
310+ * webbrowser-app is distributed in the hope that it will be useful,
311+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
312+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
313+ * GNU General Public License for more details.
314+ *
315+ * You should have received a copy of the GNU General Public License
316+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
317+ */
318+
319+#include "meminfo.h"
320+
321+// Qt
322+#include <QtCore/QByteArray>
323+#include <QtCore/QFile>
324+#include <QtCore/QRegExp>
325+#include <QtCore/QString>
326+#include <QtCore/QtGlobal>
327+
328+MemInfo::MemInfo(QObject* parent)
329+ : QObject(parent)
330+ , m_total(0)
331+ , m_free(0)
332+{
333+ // Default interval: 5000 ms
334+ m_timer.setInterval(5000);
335+ connect(&m_timer, SIGNAL(timeout()), SLOT(update()));
336+ // Active by default
337+ m_timer.start();
338+}
339+
340+MemInfo::~MemInfo()
341+{}
342+
343+const bool MemInfo::active() const
344+{
345+ return m_timer.isActive();
346+}
347+
348+void MemInfo::setActive(bool active)
349+{
350+ if (active != m_timer.isActive()) {
351+ if (active) {
352+ m_timer.start();
353+ } else {
354+ m_timer.stop();
355+ }
356+ Q_EMIT activeChanged();
357+ }
358+}
359+
360+const int MemInfo::interval() const
361+{
362+ return m_timer.interval();
363+}
364+
365+void MemInfo::setInterval(int interval)
366+{
367+ if (interval != m_timer.interval()) {
368+ m_timer.setInterval(interval);
369+ Q_EMIT intervalChanged();
370+ }
371+}
372+
373+const int MemInfo::total() const
374+{
375+ return m_total;
376+}
377+
378+const int MemInfo::free() const
379+{
380+ return m_free;
381+}
382+
383+void MemInfo::update()
384+{
385+#if defined(Q_OS_LINUX)
386+ // Inspired by glibtop_get_mem_s()
387+ QFile meminfo(QStringLiteral("/proc/meminfo"));
388+ if (!meminfo.open(QIODevice::ReadOnly)) {
389+ return;
390+ }
391+ static QRegExp memTotalRegexp(QStringLiteral("MemTotal:\\s*(\\d+) kB\\n"));
392+ static QRegExp memFreeRegexp(QStringLiteral("MemFree:\\s*(\\d+) kB\\n"));
393+ static QRegExp buffersRegexp(QStringLiteral("Buffers:\\s*(\\d+) kB\\n"));
394+ static QRegExp cachedRegexp(QStringLiteral("Cached:\\s*(\\d+) kB\\n"));
395+ int parsedTotal = -1;
396+ int parsedFree = -1;
397+ int parsedBuffers = -1;
398+ int parsedCached = -1;
399+ while ((parsedTotal == -1) || (parsedFree == -1) ||
400+ (parsedBuffers == -1) || (parsedCached == -1)) {
401+ QByteArray line = meminfo.readLine();
402+ if (line.isEmpty()) {
403+ break;
404+ }
405+ if (memTotalRegexp.exactMatch(line)) {
406+ parsedTotal = memTotalRegexp.cap(1).toInt();
407+ } else if (memFreeRegexp.exactMatch(line)) {
408+ parsedFree = memFreeRegexp.cap(1).toInt();
409+ } else if (buffersRegexp.exactMatch(line)) {
410+ parsedBuffers = buffersRegexp.cap(1).toInt();
411+ } else if (cachedRegexp.exactMatch(line)) {
412+ parsedCached = cachedRegexp.cap(1).toInt();
413+ }
414+ }
415+ meminfo.close();
416+ if ((parsedTotal != -1) && (parsedFree != -1) &&
417+ (parsedBuffers != -1) && (parsedCached != -1)) {
418+ bool totalUpdated = false;
419+ if (parsedTotal != m_total) {
420+ m_total = parsedTotal;
421+ totalUpdated = true;
422+ }
423+ bool freeUpdated = false;
424+ int newFree = parsedFree + parsedCached + parsedBuffers;
425+ if (newFree != m_free) {
426+ m_free = newFree;
427+ freeUpdated = true;
428+ }
429+ if (totalUpdated) {
430+ Q_EMIT totalChanged();
431+ }
432+ if (freeUpdated) {
433+ Q_EMIT freeChanged();
434+ }
435+ }
436+#endif // Q_OS_LINUX
437+}
438
439=== added file 'src/app/meminfo.h'
440--- src/app/meminfo.h 1970-01-01 00:00:00 +0000
441+++ src/app/meminfo.h 2016-02-23 11:14:53 +0000
442@@ -0,0 +1,65 @@
443+/*
444+ * Copyright 2016 Canonical Ltd.
445+ *
446+ * This file is part of webbrowser-app.
447+ *
448+ * webbrowser-app is free software; you can redistribute it and/or modify
449+ * it under the terms of the GNU General Public License as published by
450+ * the Free Software Foundation; version 3.
451+ *
452+ * webbrowser-app is distributed in the hope that it will be useful,
453+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
454+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
455+ * GNU General Public License for more details.
456+ *
457+ * You should have received a copy of the GNU General Public License
458+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
459+ */
460+
461+#ifndef __MEMINFO_H__
462+#define __MEMINFO_H__
463+
464+// Qt
465+#include <QtCore/QObject>
466+#include <QtCore/QTimer>
467+
468+class MemInfo : public QObject
469+{
470+ Q_OBJECT
471+
472+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
473+ Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged)
474+
475+ // Expressed in kB
476+ Q_PROPERTY(int total READ total NOTIFY totalChanged)
477+ Q_PROPERTY(int free READ free NOTIFY freeChanged)
478+
479+public:
480+ MemInfo(QObject* parent=nullptr);
481+ ~MemInfo();
482+
483+ const bool active() const;
484+ void setActive(bool active);
485+
486+ const int interval() const;
487+ void setInterval(int interval);
488+
489+ const int total() const;
490+ const int free() const;
491+
492+Q_SIGNALS:
493+ void activeChanged() const;
494+ void intervalChanged() const;
495+ void totalChanged() const;
496+ void freeChanged() const;
497+
498+private Q_SLOTS:
499+ void update();
500+
501+private:
502+ QTimer m_timer;
503+ int m_total;
504+ int m_free;
505+};
506+
507+#endif // __MEMINFO_H__
508
509=== modified file 'src/app/webbrowser/Browser.qml'
510--- src/app/webbrowser/Browser.qml 2016-02-15 13:33:11 +0000
511+++ src/app/webbrowser/Browser.qml 2016-02-23 11:14:53 +0000
512@@ -47,10 +47,6 @@
513
514 readonly property var tabsModel: incognito ? privateTabsModelLoader.item : publicTabsModel
515
516- // XXX: we might want to tweak this value depending
517- // on the form factor and/or the available memory
518- readonly property int maxLiveWebviews: 2
519-
520 // Restore only the n most recent tabs at startup,
521 // to limit the overhead of instantiating too many
522 // tab objects (see http://pad.lv/1376433).
523@@ -59,6 +55,10 @@
524 onTabsModelChanged: {
525 if (incognito && privateTabsModelLoader.item) {
526 browser.openUrlInNewTab("", true)
527+ } else if (!incognito && tabsModel.currentTab) {
528+ // If the system is low on memory, the current public tab might
529+ // have been unloaded while browsing incognito, so reload it.
530+ tabsModel.currentTab.load()
531 }
532 }
533
534@@ -106,6 +106,11 @@
535 deviceFilter: InputInfo.TouchPad
536 }
537
538+ InputDeviceModel {
539+ id: touchScreenModel
540+ deviceFilter: InputInfo.TouchScreen
541+ }
542+
543 Component {
544 id: mediaAccessDialogComponent
545 MediaAccessDialog { }
546@@ -151,7 +156,6 @@
547
548 property url homepage: settingsDefaults.homepage
549 property string searchEngine: settingsDefaults.searchEngine
550- property string allowOpenInBackgroundTab: settingsDefaults.allowOpenInBackgroundTab
551 property bool restoreSession: settingsDefaults.restoreSession
552 property int newTabDefaultSection: settingsDefaults.newTabDefaultSection
553 property string defaultAudioDevice
554@@ -160,7 +164,6 @@
555 function restoreDefaults() {
556 homepage = settingsDefaults.homepage
557 searchEngine = settingsDefaults.searchEngine
558- allowOpenInBackgroundTab = settingsDefaults.allowOpenInBackgroundTab
559 restoreSession = settingsDefaults.restoreSession
560 newTabDefaultSection = settingsDefaults.newTabDefaultSection
561 defaultAudioDevice = settingsDefaults.defaultAudioDevice
562@@ -173,7 +176,6 @@
563
564 readonly property url homepage: "http://start.ubuntu.com"
565 readonly property string searchEngine: "google"
566- readonly property string allowOpenInBackgroundTab: "default"
567 readonly property bool restoreSession: true
568 readonly property int newTabDefaultSection: 0
569 readonly property string defaultAudioDevice: ""
570@@ -461,8 +463,9 @@
571 webview: browser.currentWebview
572 forceHide: browser.fullscreen
573 forceShow: recentView.visible
574- defaultMode: (formFactor == "desktop") ? Oxide.LocationBarController.ModeShown
575- : Oxide.LocationBarController.ModeAuto
576+ defaultMode: (internal.hasMouse && !internal.hasTouchScreen)
577+ ? Oxide.LocationBarController.ModeShown
578+ : Oxide.LocationBarController.ModeAuto
579 }
580
581 Chrome {
582@@ -480,6 +483,8 @@
583
584 availableHeight: tabContainer.height - height - y
585
586+ touchEnabled: internal.hasTouchScreen
587+
588 property bool hidden: false
589 y: hidden ? -height : webview ? webview.locationBarController.offset : 0
590 Behavior on y {
591@@ -1080,10 +1085,7 @@
592 }
593 Actions.OpenLinkInNewBackgroundTab {
594 objectName: "OpenLinkInNewBackgroundTabContextualAction"
595- enabled: contextModel && contextModel.linkUrl.toString() &&
596- ((settings.allowOpenInBackgroundTab === "true") ||
597- ((settings.allowOpenInBackgroundTab === "default") &&
598- (formFactor === "desktop")))
599+ enabled: contextModel && contextModel.linkUrl.toString()
600 onTriggered: browser.openUrlInNewTab(contextModel.linkUrl, false)
601 }
602 Actions.BookmarkLink {
603@@ -1112,7 +1114,7 @@
604 }
605 Actions.Share {
606 objectName: "ShareContextualAction"
607- enabled: (formFactor == "mobile") && contextModel &&
608+ enabled: (contentHandlerLoader.status == Loader.Ready) && contextModel &&
609 (contextModel.linkUrl.toString() || contextModel.selectionText)
610 onTriggered: {
611 if (contextModel.linkUrl.toString()) {
612@@ -1339,9 +1341,9 @@
613 color: "white"
614 font.weight: Font.Light
615 anchors.centerIn: parent
616- text: (formFactor == "mobile") ?
617- i18n.tr("Swipe Up To Exit Full Screen") :
618- i18n.tr("Press ESC To Exit Full Screen")
619+ text: bottomEdgeHandle.enabled
620+ ? i18n.tr("Swipe Up To Exit Full Screen")
621+ : i18n.tr("Press ESC To Exit Full Screen")
622 }
623
624 Timer {
625@@ -1453,6 +1455,15 @@
626 }
627
628 readonly property bool hasMouse: (miceModel.count + touchPadModel.count) > 0
629+ readonly property bool hasTouchScreen: touchScreenModel.count > 0
630+
631+ readonly property real freeMemRatio: (MemInfo.total > 0) ? (MemInfo.free / MemInfo.total) : 1.0
632+ // Under that threshold, available memory is considered "low", and the
633+ // browser is going to try and free up memory from unused tabs. This
634+ // value was chosen empirically, it is subject to change to better
635+ // reflect what a system under memory pressure might look like.
636+ readonly property real lowOnMemoryThreshold: 0.3
637+ readonly property bool lowOnMemory: freeMemRatio < lowOnMemoryThreshold
638
639 function getOpenPages() {
640 var urls = []
641@@ -1765,11 +1776,12 @@
642 session.save()
643 }
644 if (browser.currentWebview) {
645- // Workaround for a desktop bug where changing volume causes the app to
646- // briefly lose focus to notify-osd, and therefore exit fullscreen mode.
647- // We prevent this by exiting fullscreen only if the focus remains lost
648- // for longer than a certain threshold. See: http://pad.lv/1477308
649- if (formFactor == "desktop") exitFullscreenOnLostFocus.start()
650+ // Workaround for a desktop bug where changing volume causes
651+ // the app to briefly lose focus to notify-osd, and therefore
652+ // exit fullscreen mode. We prevent this by exiting fullscreen
653+ // only if the focus remains lost for longer than a certain
654+ // threshold. See: https://launchpad.net/bugs/694224.
655+ if (__platformName == "xcb") exitFullscreenOnLostFocus.start()
656 else browser.currentWebview.fullscreen = false
657 }
658 } else exitFullscreenOnLostFocus.stop()
659@@ -1838,15 +1850,41 @@
660 }
661
662 Connections {
663- // On mobile, ensure that at most n webviews are instantiated at all
664- // times, to reduce memory consumption (see http://pad.lv/1376418).
665- // Note: this works only in narrow mode, where the list of tabs is a
666- // stack. Switching from wide mode to narrow mode will result in
667- // undefined behaviour (tabs previously loaded won’t be unloaded).
668- target: ((formFactor == "mobile") && !browser.wide) ? tabsModel : null
669- onCurrentTabChanged: {
670- if (tabsModel.count > browser.maxLiveWebviews) {
671- tabsModel.get(browser.maxLiveWebviews).unload()
672+ target: internal
673+ onFreeMemRatioChanged: {
674+ if (internal.lowOnMemory) {
675+ // Unload an inactive tab to (hopefully) free up some memory
676+ function getCandidate(model) {
677+ // Naive implementation that only takes into account the
678+ // last time a tab was current. In the future we might
679+ // want to take into account other parameters such as
680+ // whether the tab is currently playing audio/video.
681+ var candidate = null
682+ for (var i = 0; i < model.count; ++i) {
683+ var tab = model.get(i)
684+ if (tab.current || !tab.webview) {
685+ continue
686+ }
687+ if (!candidate || (candidate.lastCurrent > tab.lastCurrent)) {
688+ candidate = tab
689+ }
690+ }
691+ return candidate
692+ }
693+ var candidate = getCandidate(publicTabsModel)
694+ if (candidate) {
695+ console.warn("Unloading background tab (%1) to free up some memory".arg(candidate.url))
696+ candidate.unload()
697+ return
698+ } else if (browser.incognito) {
699+ candidate = getCandidate(privateTabsModelLoader.item)
700+ if (candidate) {
701+ console.warn("Unloading a background incognito tab to free up some memory")
702+ candidate.unload()
703+ return
704+ }
705+ }
706+ console.warn("System low on memory, but unable to pick a tab to unload")
707 }
708 }
709 }
710
711=== modified file 'src/app/webbrowser/BrowserTab.qml'
712--- src/app/webbrowser/BrowserTab.qml 2016-02-11 10:38:44 +0000
713+++ src/app/webbrowser/BrowserTab.qml 2016-02-23 11:14:53 +0000
714@@ -39,6 +39,7 @@
715 readonly property url icon: webview ? webview.icon : initialIcon
716 property url preview
717 property bool current: false
718+ readonly property int lastCurrent: internal.lastCurrent
719 property bool incognito
720 visible: false
721
722@@ -126,6 +127,7 @@
723 id: internal
724 property bool hiding: false
725 property var incubator: null
726+ property int lastCurrent: 0
727 }
728
729 // When current is set to false, delay hiding the tab contents to give it
730@@ -133,6 +135,7 @@
731 // only if embedders do not set the 'visible' property directly or
732 // indirectly on instances of a BrowserTab.
733 onCurrentChanged: {
734+ internal.lastCurrent = Date.now()
735 if (current) {
736 internal.hiding = false
737 z = 1
738
739=== modified file 'src/app/webbrowser/Chrome.qml'
740--- src/app/webbrowser/Chrome.qml 2016-01-26 17:44:49 +0000
741+++ src/app/webbrowser/Chrome.qml 2016-02-23 11:14:53 +0000
742@@ -40,6 +40,7 @@
743 property alias showFaviconInAddressBar: navigationBar.showFaviconInAddressBar
744 property alias availableHeight: navigationBar.availableHeight
745 readonly property alias bookmarkTogglePlaceHolder: navigationBar.bookmarkTogglePlaceHolder
746+ property bool touchEnabled: true
747
748 signal switchToTab(int index)
749 signal requestNewTab(int index, bool makeCurrent)
750@@ -72,6 +73,7 @@
751 model: tabsModel
752 incognito: chrome.incognito
753 fgColor: navigationBar.fgColor
754+ touchEnabled: chrome.touchEnabled
755 onSwitchToTab: chrome.switchToTab(index)
756 onRequestNewTab: chrome.requestNewTab(index, makeCurrent)
757 onTabClosed: chrome.tabClosed(index)
758@@ -82,7 +84,7 @@
759 left: parent.left
760 right: parent.right
761 }
762- height: active ? (formFactor == "desktop" ? units.gu(3) : units.gu(4)) : 0
763+ height: active ? (touchEnabled ? units.gu(4) : units.gu(3)) : 0
764 }
765
766 NavigationBar {
767
768=== modified file 'src/app/webbrowser/SettingsPage.qml'
769--- src/app/webbrowser/SettingsPage.qml 2015-11-30 09:38:08 +0000
770+++ src/app/webbrowser/SettingsPage.qml 2016-02-23 11:14:53 +0000
771@@ -1,5 +1,5 @@
772 /*
773- * Copyright 2015 Canonical Ltd.
774+ * Copyright 2015-2016 Canonical Ltd.
775 *
776 * This file is part of webbrowser-app.
777 *
778@@ -113,25 +113,6 @@
779 }
780
781 ListItems.Standard {
782- objectName: "backgroundTabs"
783-
784- text: i18n.tr("Allow opening new tabs in background")
785- highlightWhenPressed: false
786-
787- control: CheckBox {
788- id: allowOpenInBackgroundTabCheckbox
789- onTriggered: settingsObject.allowOpenInBackgroundTab = checked ? 'true' : 'false'
790- }
791-
792- Binding {
793- target: allowOpenInBackgroundTabCheckbox
794- property: "checked"
795- value: settingsObject.allowOpenInBackgroundTab === 'true' ||
796- (settingsObject.allowOpenInBackgroundTab === 'default' && formFactor === "desktop")
797- }
798- }
799-
800- ListItems.Standard {
801 objectName: "privacy"
802
803 text: i18n.tr("Privacy & permissions")
804
805=== modified file 'src/app/webbrowser/TabItem.qml'
806--- src/app/webbrowser/TabItem.qml 2016-01-26 17:44:49 +0000
807+++ src/app/webbrowser/TabItem.qml 2016-02-23 11:14:53 +0000
808@@ -37,6 +37,8 @@
809
810 property color fgColor: Theme.palette.normal.baseText
811
812+ property bool touchEnabled: true
813+
814 signal selected()
815 signal closed()
816 signal contextMenu()
817@@ -47,7 +49,7 @@
818 anchors.rightMargin: tabItem.rightMargin
819 source: "assets/tab-%1%2.sci".arg((active) ? "active" :
820 (hoverArea.containsMouse ? "hover" : "non-active"))
821- .arg(formFactor == "desktop" ? "-desktop" : "")
822+ .arg(touchEnabled ? "" : "-desktop")
823
824 Favicon {
825 id: favicon
826@@ -121,15 +123,14 @@
827 id: closeButton
828 objectName: "closeButton"
829
830- // On mobile the tap area to close the tab occupies the whole right
831+ // On touch the tap area to close the tab occupies the whole right
832 // hand side of the tab, while it covers only the close icon in
833 // other form factors
834- readonly property bool mobile: formFactor == "mobile"
835- anchors.fill: mobile ? undefined : closeIcon
836- anchors.top: mobile ? parent.top : undefined
837- anchors.bottom: mobile ? parent.bottom : undefined
838- anchors.right: mobile ? parent.right : undefined
839- width: mobile ? units.gu(4) : closeIcon.width
840+ anchors.fill: touchEnabled ? undefined : closeIcon
841+ anchors.top: touchEnabled ? parent.top : undefined
842+ anchors.bottom: touchEnabled ? parent.bottom : undefined
843+ anchors.right: touchEnabled ? parent.right : undefined
844+ width: touchEnabled ? units.gu(4) : closeIcon.width
845
846 onClicked: closed()
847
848
849=== modified file 'src/app/webbrowser/TabsBar.qml'
850--- src/app/webbrowser/TabsBar.qml 2016-01-26 17:44:49 +0000
851+++ src/app/webbrowser/TabsBar.qml 2016-02-23 11:14:53 +0000
852@@ -34,6 +34,8 @@
853
854 property color fgColor: Theme.palette.normal.baseText
855
856+ property bool touchEnabled: true
857+
858 signal switchToTab(int index)
859 signal requestNewTab(int index, bool makeCurrent)
860 signal tabClosed(int index)
861@@ -111,7 +113,6 @@
862 anchors {
863 top: parent.top
864 bottom: parent.bottom
865- bottomMargin: tabsContainer.verticalGap
866 left: parent.left
867 }
868 width: tabWidth * root.model.count
869@@ -153,6 +154,8 @@
870 icon: model.icon
871 fgColor: root.fgColor
872
873+ touchEnabled: root.touchEnabled
874+
875 rightMargin: tabDelegate.rightMargin
876
877 onClosed: root.tabClosed(index)
878
879=== modified file 'src/app/webbrowser/webbrowser-app.cpp'
880--- src/app/webbrowser/webbrowser-app.cpp 2016-01-22 10:23:29 +0000
881+++ src/app/webbrowser/webbrowser-app.cpp 2016-02-23 11:14:53 +0000
882@@ -82,6 +82,8 @@
883 searchEnginesSearchPaths << UbuntuBrowserDirectory() + "/webbrowser/searchengines";
884 m_engine->rootContext()->setContextProperty("searchEnginesSearchPaths", searchEnginesSearchPaths);
885
886+ m_engine->rootContext()->setContextProperty("__platformName", platformName());
887+
888 m_window->setProperty("newSession", m_arguments.contains("--new-session"));
889
890 QVariantList urls;
891
892=== modified file 'src/app/webcontainer/WebApp.qml'
893--- src/app/webcontainer/WebApp.qml 2016-01-08 16:09:01 +0000
894+++ src/app/webcontainer/WebApp.qml 2016-02-23 11:14:53 +0000
895@@ -250,8 +250,9 @@
896 ChromeController {
897 webview: webapp.currentWebview
898 forceHide: webapp.chromeless
899- defaultMode: (formFactor == "desktop") ? Oxide.LocationBarController.ModeShown
900- : Oxide.LocationBarController.ModeAuto
901+ defaultMode: webapp.hasTouchScreen
902+ ? Oxide.LocationBarController.ModeAuto
903+ : Oxide.LocationBarController.ModeShown
904 }
905 }
906
907
908=== modified file 'src/app/webcontainer/WebViewImplOxide.qml'
909--- src/app/webcontainer/WebViewImplOxide.qml 2016-01-08 16:09:01 +0000
910+++ src/app/webcontainer/WebViewImplOxide.qml 2016-02-23 11:14:53 +0000
911@@ -1,5 +1,5 @@
912 /*
913- * Copyright 2014-2015 Canonical Ltd.
914+ * Copyright 2014-2016 Canonical Ltd.
915 *
916 * This file is part of webbrowser-app.
917 *
918@@ -207,10 +207,6 @@
919 samlRequestUrlPatternReceived(urlPattern)
920 }
921
922- function shouldOpenPopupsInDefaultBrowser() {
923- return formFactor !== "desktop";
924- }
925-
926 function isRunningAsANamedWebapp() {
927 return webview.webappName && typeof(webview.webappName) === 'string' && webview.webappName.length != 0
928 }
929@@ -328,13 +324,12 @@
930 }
931
932 onGeolocationPermissionRequested: {
933- if (formFactor == "desktop") {
934+ if (__runningConfined && (request.origin == request.embedder)) {
935+ // When running confined, querying the location service will trigger
936+ // a system prompt (trust store), so no need for a custom one.
937+ request.accept()
938+ } else {
939 requestGeolocationPermission(request)
940- } else {
941- // On devices where webapps are confined, trying to access the
942- // location service will trigger a system prompt from the trust
943- // store, so we don’t need a custom prompt.
944- request.accept()
945 }
946 }
947
948
949=== modified file 'tests/autopilot/webbrowser_app/tests/__init__.py'
950--- tests/autopilot/webbrowser_app/tests/__init__.py 2016-02-03 11:56:56 +0000
951+++ tests/autopilot/webbrowser_app/tests/__init__.py 2016-02-23 11:14:53 +0000
952@@ -158,16 +158,11 @@
953 toolbar.click_action("newTabButton")
954 tabs_view.visible.wait_for(False)
955
956- if self.main_window.wide or (model() == 'Desktop'):
957- new_count = count + 1
958- else:
959- max_webviews = self.main_window.maxLiveWebviews
960- new_count = (count + 1) if (count < max_webviews) else max_webviews
961 if (self.main_window.incognito):
962- self.assert_number_incognito_webviews_eventually(new_count)
963+ self.assert_number_incognito_webviews_eventually(count + 1)
964 new_tab_view = self.main_window.get_new_private_tab_view()
965 else:
966- self.assert_number_webviews_eventually(new_count)
967+ self.assert_number_webviews_eventually(count + 1)
968 new_tab_view = self.main_window.get_new_tab_view()
969
970 if self.main_window.wide:
971
972=== modified file 'tests/unittests/CMakeLists.txt'
973--- tests/unittests/CMakeLists.txt 2016-01-18 14:45:12 +0000
974+++ tests/unittests/CMakeLists.txt 2016-02-23 11:14:53 +0000
975@@ -22,3 +22,4 @@
976 add_subdirectory(text-search-filter-model)
977 add_subdirectory(downloads-model)
978 add_subdirectory(single-instance-manager)
979+add_subdirectory(meminfo)
980
981=== added directory 'tests/unittests/meminfo'
982=== added file 'tests/unittests/meminfo/CMakeLists.txt'
983--- tests/unittests/meminfo/CMakeLists.txt 1970-01-01 00:00:00 +0000
984+++ tests/unittests/meminfo/CMakeLists.txt 2016-02-23 11:14:53 +0000
985@@ -0,0 +1,15 @@
986+find_package(Qt5Core REQUIRED)
987+find_package(Qt5Test REQUIRED)
988+set(TEST tst_MemInfoTests)
989+set(SOURCES
990+ ${webbrowser-common_SOURCE_DIR}/meminfo.cpp
991+ tst_MemInfoTests.cpp
992+)
993+add_executable(${TEST} ${SOURCES})
994+include_directories(${webbrowser-common_SOURCE_DIR})
995+target_link_libraries(${TEST}
996+ Qt5::Core
997+ Qt5::Test
998+)
999+add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
1000+set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
1001
1002=== added file 'tests/unittests/meminfo/tst_MemInfoTests.cpp'
1003--- tests/unittests/meminfo/tst_MemInfoTests.cpp 1970-01-01 00:00:00 +0000
1004+++ tests/unittests/meminfo/tst_MemInfoTests.cpp 2016-02-23 11:14:53 +0000
1005@@ -0,0 +1,100 @@
1006+/*
1007+ * Copyright 2016 Canonical Ltd.
1008+ *
1009+ * This file is part of webbrowser-app.
1010+ *
1011+ * webbrowser-app is free software; you can redistribute it and/or modify
1012+ * it under the terms of the GNU General Public License as published by
1013+ * the Free Software Foundation; version 3.
1014+ *
1015+ * webbrowser-app is distributed in the hope that it will be useful,
1016+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1017+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1018+ * GNU General Public License for more details.
1019+ *
1020+ * You should have received a copy of the GNU General Public License
1021+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1022+ */
1023+
1024+// Qt
1025+#include <QtCore/QObject>
1026+#include <QtTest/QSignalSpy>
1027+#include <QtTest/QtTest>
1028+
1029+// local
1030+#include "meminfo.h"
1031+
1032+class MemInfoTests : public QObject
1033+{
1034+ Q_OBJECT
1035+
1036+private:
1037+ MemInfo* meminfo;
1038+
1039+private Q_SLOTS:
1040+ void init()
1041+ {
1042+ meminfo = new MemInfo(this);
1043+ }
1044+
1045+ void cleanup()
1046+ {
1047+ delete meminfo;
1048+ }
1049+
1050+ void test_active_property()
1051+ {
1052+ QVERIFY(meminfo->active());
1053+ QSignalSpy spy(meminfo, SIGNAL(activeChanged()));
1054+
1055+ meminfo->setActive(true);
1056+ QVERIFY(spy.isEmpty());
1057+
1058+ meminfo->setActive(false);
1059+ QCOMPARE(spy.count(), 1);
1060+ QVERIFY(!meminfo->active());
1061+ spy.clear();
1062+
1063+ meminfo->setActive(false);
1064+ QVERIFY(spy.isEmpty());
1065+
1066+ meminfo->setActive(true);
1067+ QCOMPARE(spy.count(), 1);
1068+ QVERIFY(meminfo->active());
1069+ }
1070+
1071+ void test_interval_property()
1072+ {
1073+ QCOMPARE(meminfo->interval(), 5000);
1074+ QSignalSpy spy(meminfo, SIGNAL(intervalChanged()));
1075+
1076+ meminfo->setInterval(5000);
1077+ QVERIFY(spy.isEmpty());
1078+
1079+ meminfo->setInterval(1500);
1080+ QCOMPARE(spy.count(), 1);
1081+ QCOMPARE(meminfo->interval(), 1500);
1082+ }
1083+
1084+ void test_initial_values()
1085+ {
1086+ QCOMPARE(meminfo->total(), 0);
1087+ QCOMPARE(meminfo->free(), 0);
1088+ }
1089+
1090+ void test_update()
1091+ {
1092+ QSignalSpy totalSpy(meminfo, SIGNAL(totalChanged()));
1093+ QSignalSpy freeSpy(meminfo, SIGNAL(freeChanged()));
1094+ meminfo->setInterval(100);
1095+ totalSpy.wait();
1096+ freeSpy.wait();
1097+ QVERIFY(meminfo->total() > 0);
1098+ QVERIFY(meminfo->free() > 0);
1099+ QVERIFY(meminfo->total() > meminfo->free());
1100+ }
1101+};
1102+
1103+QTEST_MAIN(MemInfoTests)
1104+
1105+#include "tst_MemInfoTests.moc"

Subscribers

People subscribed via source and target branches

to status/vote changes: