Merge lp:~abreu-alexandre/webbrowser-app/add-to-homescreen into lp:webbrowser-app

Proposed by Alexandre Abreu
Status: Work in progress
Proposed branch: lp:~abreu-alexandre/webbrowser-app/add-to-homescreen
Merge into: lp:webbrowser-app
Diff against target: 3824 lines (+3064/-229)
28 files modified
CMakeLists.txt (+1/-0)
helper/CMakeLists.txt (+1/-0)
helper/homescreen-webapp-installer/CMakeLists.txt (+3/-0)
helper/homescreen-webapp-installer/homescreen-webapp-installer.py (+266/-0)
po/webbrowser-app.pot (+128/-227)
src/app/webbrowser/AddressBar.qml (+22/-2)
src/app/webbrowser/Browser.qml (+56/-0)
src/app/webbrowser/CMakeLists.txt (+4/-0)
src/app/webbrowser/Chrome.qml (+6/-0)
src/app/webbrowser/FavoriteOptionTabs.qml (+386/-0)
src/app/webbrowser/HomescreenWebappInstaller.qml (+157/-0)
src/app/webbrowser/NavigationBar.qml (+41/-0)
src/app/webbrowser/homescreen-installed-webapp-model.cpp (+342/-0)
src/app/webbrowser/homescreen-installed-webapp-model.h (+85/-0)
src/app/webbrowser/homescreen-manifest-content.cpp (+180/-0)
src/app/webbrowser/homescreen-manifest-content.h (+109/-0)
src/app/webbrowser/homescreen-manifest-parser.cpp (+131/-0)
src/app/webbrowser/homescreen-manifest-parser.h (+41/-0)
src/app/webbrowser/homescreen-manifest-request.cpp (+176/-0)
src/app/webbrowser/homescreen-manifest-request.h (+60/-0)
src/app/webbrowser/homescreen-webapp-model.cpp (+350/-0)
src/app/webbrowser/homescreen-webapp-model.h (+54/-0)
src/app/webbrowser/homescreen-webapp.cpp (+236/-0)
src/app/webbrowser/homescreen-webapp.h (+86/-0)
src/app/webbrowser/page-metadata-gathering.js (+101/-0)
src/app/webbrowser/webbrowser-app.cpp (+4/-0)
src/app/webcontainer/WebApp.qml (+28/-0)
src/app/webcontainer/WebappContainerWebview.qml (+10/-0)
To merge this branch: bzr merge lp:~abreu-alexandre/webbrowser-app/add-to-homescreen
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+256237@code.launchpad.net

Commit message

very much a WIP

Description of the change

very much a WIP

To post a comment you must log in.
967. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

968. By Arthur Mello

Add model support to control which history entries will be displayed based on a blacklist database
Approved by: PS Jenkins bot, Olivier Tilloy

969. By Arthur Mello

Make Top Sites format equal to Bookmarks on the New Tab view
Approved by: PS Jenkins bot

970. By CI Train Bot Account

Releasing 0.23+15.04.20150416-0ubuntu1

971. By CI Train Bot Account

Resync trunk.

972. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

973. By Ugo Riboni

Include bookmark results in the suggestions list Fixes: #1351177
Approved by: Olivier Tilloy, PS Jenkins bot

974. By Alexandre Abreu

Add missing reload button from the webapp container as specified in the design document.
Approved by: Olivier Tilloy, PS Jenkins bot

975. By Ken VanDine

added ShareLink to contextualActions

Approved by: Olivier Tilloy, PS Jenkins bot

976. By Olivier Tilloy

Save the updated homepage when pressing return. Fixes: #1441874
Approved by: PS Jenkins bot, Riccardo Padovani

977. By Leo Arias

In the autopilot tests, removed the extra focus step to write a URL. Fixes: #1441551
Approved by: Olivier Tilloy, PS Jenkins bot

978. By Olivier Tilloy

Always exit fullscreen mode when the application becomes inactive. Fixes: #1331475
Approved by: PS Jenkins bot, Bill Filler

979. By Olivier Tilloy

Recognize about:blank as a valid URL. Fixes: #1444139
Approved by: PS Jenkins bot

980. By CI Train Bot Account

Releasing 0.23+15.04.20150422.1-0ubuntu1

981. By CI Train Bot Account

Resync trunk.

982. By Olivier Tilloy

Update translation template.

983. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

984. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

985. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

986. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

987. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

988. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

989. By Alexandre Abreu

Add multi-window support for webapps Fixes: #1411722
Approved by: Olivier Tilloy

990. By CI Train Bot Account

Releasing 0.23+15.04.20150430.1-0ubuntu1

991. By CI Train Bot Account

Resync trunk.

992. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

993. By Leo Arias

Fixed flake8 error. Fixes: #1444170
Approved by: Olivier Tilloy

994. By Olivier Tilloy

Run autopilot tests in a temporary profile directory, to avoid polluting the current user’s profile. Fixes: #1448838

995. By Arthur Mello

Change asset for the bottom edge hint.
Add text with open tabs count.
Approved by: Olivier Tilloy

996. By Olivier Tilloy

Save the session periodically to mitigate the situation where multiple new tabs had been created when the browser crashes. Fixes: #1449761

997. By Olivier Tilloy

Visual feedback for pressed state of chrome buttons and drawer menu entries. Fixes: #1448336

998. By Olivier Tilloy

Remove deprecated compatibility code.
Approved by: David Barth

999. By CI Train Bot Account

Releasing 0.23+15.04.20150506-0ubuntu1

1000. By CI Train Bot Account

Resync trunk.

1001. By Olivier Tilloy

Update translation template.

1002. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1003. By Riccardo Padovani

Add support for data: URIs in the address bar, and remove length limitation for TLDs. Fixes: #1377953, #1441281, #1450154
Approved by: Olivier Tilloy

1004. By Leo Arias

Fix the base class used when launching the app in autopilot tests.
Approved by: Olivier Tilloy, Christopher Lee

1005. By Olivier Tilloy

Also set XDG_CONFIG_HOME (needed for tests not to override user-defined settings).

1006. By Ugo Riboni

Add suggestions from search engines in the suggestions list. Fixes: #1351151
Approved by: Olivier Tilloy

1007. By Olivier Tilloy

Use the new Oxide APIs to better control visibility of the chrome.
This bumps the runtime dependency on liboxideqt-qmlplugin to 1.7. Fixes: #1441064, #1453908
Approved by: Alexandre Abreu

1008. By CI Train Bot Account

Releasing 0.23+15.04.20150512-0ubuntu1

1009. By CI Train Bot Account

Resync trunk.

1010. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1011. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1012. By Olivier Tilloy

Look for custom search engines description files in several locations.
This adds a build dependency on qml-module-qt-labs-folderlistmodel, to run unit tests at package construction time. Fixes: #1455207

1013. By Olivier Tilloy

Delay showing the fullscreen hint to prevent it from jumping up while the webview is being resized. Fixes: #1454097
Approved by: Ugo Riboni

1014. By Olivier Tilloy

Use checkboxes instead of switches for verb phrases, per design guidance. Fixes: #1442851
Approved by: Riccardo Padovani

1015. By Olivier Tilloy

Change the homepage in tests so that the domain name doesn’t contain a dot, to work around https://launchpad.net/bugs/1108742.

1016. By CI Train Bot Account

Releasing 0.23+15.04.20150515-0ubuntu1

1017. By CI Train Bot Account

Resync trunk.

1018. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1019. By Olivier Tilloy

Make use of the WebContext.maxCacheSizeHint property, introduced in Oxide 1.6.
This bumps the dependency of qtdeclarative5-ubuntu-web-plugin on liboxideqt-qmlplugin to 1.6. Fixes: #1277659
Approved by: Alexandre Abreu

1020. By Olivier Tilloy

Ensure the search engine delegates do not overlap with the settings header. Fixes: #1455399
Approved by: Riccardo Padovani

1021. By Olivier Tilloy

When opening a new blank tab, always clear the address bar. Fixes: #1456199
Approved by: Arthur Mello

1022. By Arthur Mello

Implement private browsing mode per design specification. Fixes: #1351179, #1457925, #1457958
Approved by: Olivier Tilloy

1023. By Olivier Tilloy

Add Qwant to the default list of search engines.

1024. By Olivier Tilloy

Add missing import statement. Fixes: #1457060
Approved by: Alexandre Abreu

1025. By Olivier Tilloy

Remove a number of useless calls to QTest::qWait() in unit tests, thus making them significantly faster to run.

1026. By Olivier Tilloy

Fix a flaky autopilot test.
On a mobile device with network access, the URL (http://en.wikipedia.org/wiki/Linux) might be rewritten to https://en.m.wikipedia.org/wiki/Linux, thus making the test racy depending on when the check on the webview’s URL is performed. Fixes: #1456885

1027. By Olivier Tilloy

Delay hiding the tab contents to give it an opportunity to grab an up-to-date capture.
Improve the tab switching animation to not flicker and jump around that much. Fixes: #1452998

1028. By CI Train Bot Account

Releasing 0.23+15.04.20150522.1-0ubuntu1

1029. By Olivier Tilloy

Update translation template.

1030. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1031. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1032. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1033. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1034. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1035. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1036. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1037. By Olivier Tilloy

Bump build dependency on liboxideqt-qmlplugin to 1.6 to fix unit tests.

1038. By Riccardo Padovani

New tab view refactoring. Fixes: #1351157, #1371248, #1389605, #1442190, #1444023
Approved by: Olivier Tilloy

1039. By Olivier Tilloy

Remove the upstreamcomponents folder, and use components from the UITK instead.
Add autopilot tests for the new tab view.
Approved by: Riccardo Padovani

1040. By Olivier Tilloy

Do not try to remove a file that doesn’t exist.

1041. By Olivier Tilloy

Updated icon. Fixes: #1457424

1042. By Olivier Tilloy

Pass plain strings to the worker script instead of RegExps. Fixes: #1445673
Approved by: Chris Coulson

1043. By Olivier Tilloy

Do not cache favicons on disk when browsing in private mode. Fixes: #1458963
Approved by: Arthur Mello

1044. By Olivier Tilloy

Actually clear the network cache by deleting the correct set of files in the correct directory. Fixes: #1459956

1045. By CI Train Bot Account

Releasing 0.23+15.10.20150602-0ubuntu1

1046. By Olivier Tilloy

Update translation template.

1047. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1048. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1049. By Olivier Tilloy

Updated bzr ignore rules.

1050. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1051. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1052. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1053. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1054. By Alexandre Abreu

Ensure SAML requests are followed even if the app is interrupted and restarted. Fixes: #1451432, #1452142

1055. By Alexandre Abreu

Handle desktop download by forwarding to the default browser Fixes: #1309657
Approved by: Olivier Tilloy

1056. By CI Train Bot Account

Releasing 0.23+15.10.20150610-0ubuntu1

1057. By CI Train Bot Account

Resync trunk.

1058. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1059. By Timo Jyrinki

No-change rebuild against Qt 5.4.2.

1060. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1061. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1062. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1063. By Ugo Riboni

Make the browser chrome usable on desktop by implementing common keyboard shortcuts and behaviors that users normally expect in such an app Fixes: #1287361
Approved by: Olivier Tilloy

1064. By Olivier Tilloy

Toggle application-level fullscreen when pressing F11. Fixes: #1464333

1065. By Michael Terry

Fix spelling of OK in private browsing dialog.
Approved by: Olivier Tilloy

1066. By Olivier Tilloy

Do not honour fullscreen requests silently at the Ubuntu WebView level.
Instead delegate that behaviour to the webview implementation used by the browser and the webapp container. Fixes: #1464249
Approved by: PS Jenkins bot, Alexandre Abreu

1067. By Olivier Tilloy

Actually push URLs to the clipboard.
For some reason setting the "text/url-list" mime type doesn’t seem to work (or other applications do not implement pasting this kind of data), but "text/plain" works well enough for this purpose. Fixes: #1463435
Approved by: PS Jenkins bot, Alexandre Abreu

1068. By CI Train Bot Account

Releasing 0.23+15.10.20150618-0ubuntu1

1069. By CI Train Bot Account

Resync trunk.

1070. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1071. By Olivier Tilloy

Update translation template.

1072. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1073. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1074. By Alberto Mardegan

Show an error screen if the trust session could not be opened. Fixes: #1441873
Approved by: PS Jenkins bot

1075. By CI Train Bot Account

Releasing 0.23+15.10.20150624-0ubuntu1

1076. By CI Train Bot Account

Resync trunk.

1077. By Arthur Mello

Implement bookmark folders Fixes: #1259247, #1351147
Approved by: PS Jenkins bot

1078. By CI Train Bot Account

Releasing 0.23+15.10.20150702.1-0ubuntu1

1079. By CI Train Bot Account

Resync trunk.

1080. By Olivier Tilloy

Update translation template.

1081. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1082. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1083. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1084. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1085. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1086. By Olivier Tilloy

Display the full URL when focusing the address bar. Fixes: #1472161

1087. By CI Train Bot Account

Releasing 0.23+15.10.20150708-0ubuntu1

1088. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1089. By Olivier Tilloy

Desktop tabs for convergence.

1090. By CI Train Bot Account

Releasing 0.23+15.10.20150709.3-0ubuntu1

1091. By Olivier Tilloy

Speed up a bunch of autopilot tests by not requiring to input and validate a URL in the address bar.

1092. By Olivier Tilloy

Prefer target_link_libraries(…) with Qt5:: prefixed modules over qt5_use_modules,
per documentation at http://doc.qt.io/qt-5/cmake-manual.html.
Approved by: PS Jenkins bot

1093. By Olivier Tilloy

Disable periodic session saving while incognito. The public session is unlikely to change significantly while browsing incognito.

Save the public session right away when going incognito.

1094. By CI Train Bot Account

Releasing 0.23+15.10.20150709.4-0ubuntu1

1095. By CI Train Bot Account

Resync trunk.

1096. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1097. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1098. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1099. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1100. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1101. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1102. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1103. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1104. By Olivier Tilloy

Fix narrowing conversion that makes the build fail with GCC 5. Fixes: #1475621
Approved by: Alexandre Abreu, PS Jenkins bot

1105. By CI Train Bot Account

Releasing 0.23+15.10.20150728-0ubuntu1

1106. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1107. By Olivier Tilloy

When clearing browsing data, confirm action with a dialog, to give the user a chance to cancel it. Fixes: #1471747
Approved by: PS Jenkins bot, Riccardo Padovani

1108. By Olivier Tilloy

Ensure a value is always returned by BookmarksModel::addFolder(…).
Approved by: PS Jenkins bot

1109. By Olivier Tilloy

Update icon, per UI specification. Fixes: #1412732
Approved by: PS Jenkins bot, Riccardo Padovani

1110. By CI Train Bot Account

Releasing 0.23+15.10.20150729-0ubuntu1

1111. By CI Train Bot Account

Resync trunk.

1112. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1113. By Olivier Tilloy

Update translation template.

1114. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1115. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1116. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1117. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1118. By Steve Langasek

Add missing changelog entry due to direct upload.

1119. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1120. By Olivier Tilloy

Update all imports of:
 - Ubuntu.Components to 1.3
 - QtQuick to 2.4
 - QtQuick.Window to 2.2

This bumps the required versions of qtdeclarative5-ubuntu-ui-toolkit-plugin, qml-module-qtquick2 and qml-module-qtquick-window2 in debian/control. Fixes: #1483279

1121. By CI Train Bot Account

Releasing 0.23+15.10.20150810-0ubuntu1

1122. By Olivier Tilloy

Implement the "Find in Page" feature.
This bumps the runtime dependency on liboxideqt-qmlplugin to 1.8. Fixes: #1312260
Approved by: Riccardo Padovani

1123. By Olivier Tilloy

Drop old names for QML dependencies.
Original patch by Robert Ancell.

1124. By Olivier Tilloy

Highlight matching terms in one pass. Fixes: #1481206
Approved by: PS Jenkins bot, Ugo Riboni

1125. By CI Train Bot Account

Releasing 0.23+15.10.20150811-0ubuntu1

1126. By Olivier Tilloy

Update translation template.

1127. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1128. By Arthur Mello

Wide screen versions of the history view and new tab view, per design specification.
This adds a build dependency on qml-module-qt-labs-settings (for unit tests). Fixes: #1351157, #1481647

1129. By CI Train Bot Account

Releasing 0.23+15.10.20150812.1-0ubuntu1

1130. By Olivier Tilloy

Update translation template.

1131. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1132. By Olivier Tilloy

Remove strong typing of the contextualActions and selectionActions properties.
This would cause issues if an app imported Ubuntu.Web along with Ubuntu.Components < 1.3 and overrode one of these properties, because the webview expected an ActionList 1.3, and would get an earlier version. Fixes: #1484437

1133. By CI Train Bot Account

Releasing 0.23+15.10.20150813.1-0ubuntu1

1134. By CI Train Bot Account

Resync trunk.

1135. By Ugo Riboni

Delay the exit from fullscreen mode until focus remains lost for a certain amount of time. Fixes: #1477308
Approved by: Olivier Tilloy

1136. By Ugo Riboni

Disable find in page when the new tab view is active. Fixes: #1483847
Approved by: Olivier Tilloy

1137. By CI Train Bot Account

Releasing 0.23+15.10.20150814-0ubuntu1

1138. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1139. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1140. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1141. By Colin Watson

Fix versioned dependencies on qtdeclarative5-ubuntu-ui-toolkit-plugin to allow qtdeclarative5-ubuntu-ui-toolkit-plugin-gles.
Approved by: Olivier Tilloy

1142. By Arthur Mello

Wait for OptionSelector's collapsing transition to finish Fixes: #1484175
Approved by: Olivier Tilloy

1143. By Arthur Mello

Make sure to set the historyModel to the HistoryView component when browser wide property changes Fixes: #1484555
Approved by: Olivier Tilloy

1144. By CI Train Bot Account

Releasing 0.23+15.10.20150817-0ubuntu1

1145. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1146. By Ugo Riboni

Merge two url utility files into one, since they had no reason for being separate. Add unit tests for some of the functions that had none.
Approved by: Olivier Tilloy

1147. By Alexandre Abreu

Fix webapp name logic Fixes: #1473472
Approved by: Olivier Tilloy

1148. By Arthur Mello

Add support for removing history entries with delete key in HistoryViewWide Fixes: #1484562
Approved by: Olivier Tilloy

1149. By Olivier Tilloy

Remove an old workaround for an issue that was fixed since then in the content hub and that caused the file picker to accept the selected file twice.
Approved by: Michael Sheldon, Alexandre Abreu

1150. By CI Train Bot Account

Releasing 0.23+15.10.20150820-0ubuntu1

1151. By CI Train Bot Account

Resync trunk.

1152. By Olivier Tilloy

Use the contextMenu API new in oxide 1.8.
Update the visuals for the context menu in narrow and wide form factors.
Add text editing commands to the context menu.
Add unit and autopilot tests for the context menu features.
This bumps the runtime dependency of webapp-container and qtdeclarative5-ubuntu-web-plugin on liboxideqt-qmlplugin to 1.8.
This also removes the qtdeclarative5-ubuntu-web-plugin-assets binary package, which contained only one PNG asset which is not used anywhere any longer. Fixes: #1264493, #1326070, #1438046, #1450430, #1471181, #1477309, #1477310, #1477315, #1487090
Approved by: PS Jenkins bot, Ugo Riboni, Ken VanDine

1153. By CI Train Bot Account

Releasing 0.23+15.10.20150827.3-0ubuntu1

1154. By CI Train Bot Account

Resync trunk.

1155. By Olivier Tilloy

Update translation template.

1156. By Launchpad Translations on behalf of phablet-team

Launchpad automatic translations update.

1157. By Alexandre Abreu

update to trunk

1158. By Alexandre Abreu

merge trunk

Unmerged revisions

1158. By Alexandre Abreu

merge trunk

1157. By Alexandre Abreu

update to trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-04-01 09:44:20 +0000
3+++ CMakeLists.txt 2016-06-16 15:34:13 +0000
4@@ -77,3 +77,4 @@
5 DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app)
6
7 add_subdirectory(click-hooks)
8+add_subdirectory(helper)
9
10=== added directory 'helper'
11=== added file 'helper/CMakeLists.txt'
12--- helper/CMakeLists.txt 1970-01-01 00:00:00 +0000
13+++ helper/CMakeLists.txt 2016-06-16 15:34:13 +0000
14@@ -0,0 +1,1 @@
15+add_subdirectory(homescreen-webapp-installer)
16
17=== added directory 'helper/homescreen-webapp-installer'
18=== added file 'helper/homescreen-webapp-installer/CMakeLists.txt'
19--- helper/homescreen-webapp-installer/CMakeLists.txt 1970-01-01 00:00:00 +0000
20+++ helper/homescreen-webapp-installer/CMakeLists.txt 2016-06-16 15:34:13 +0000
21@@ -0,0 +1,3 @@
22+project(webapp-install-helper)
23+
24+install(FILES homescreen-webapp-installer.py DESTINATION ${CMAKE_INSTALL_BINDIR})
25
26=== added file 'helper/homescreen-webapp-installer/homescreen-webapp-installer.py'
27--- helper/homescreen-webapp-installer/homescreen-webapp-installer.py 1970-01-01 00:00:00 +0000
28+++ helper/homescreen-webapp-installer/homescreen-webapp-installer.py 2016-06-16 15:34:13 +0000
29@@ -0,0 +1,266 @@
30+#!/usr/bin/env python
31+
32+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
33+# Copyright 2015 Canonical
34+#
35+# This program is free software: you can redistribute it and/or modify it
36+# under the terms of the GNU General Public License version 3, as published
37+# by the Free Software Foundation.
38+#
39+# This program is distributed in the hope that it will be useful,
40+# but WITHOUT ANY WARRANTY; without even the implied warranty of
41+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42+# GNU General Public License for more details.
43+#
44+# You should have received a copy of the GNU General Public License
45+# along with this program. If not, see <http://www.gnu.org/licenses/>.
46+
47+""" Webapp click package installation """
48+
49+import optparse
50+import os
51+import sys
52+import subprocess
53+import tempfile
54+import urllib2
55+import urlparse
56+
57+
58+name = ''
59+domain = ''
60+homepage = ''
61+icons = ''
62+
63+parser = optparse.OptionParser()
64+
65+parser.add_option(
66+ "-d",
67+ "--domain",
68+ type="string",
69+ help="Domain name for that ",
70+ dest="domain",
71+ default="")
72+parser.add_option(
73+ "-n",
74+ "--name",
75+ type="string",
76+ help="Webapp name",
77+ dest="name",
78+ default="")
79+parser.add_option(
80+ "-p",
81+ "--homepage",
82+ type="string",
83+ help="Webapp startup homepage",
84+ dest="homepage",
85+ default="")
86+parser.add_option(
87+ "-i",
88+ "--icons",
89+ type="string",
90+ help="Icon urls for this webapp",
91+ dest="icons",
92+ default="")
93+
94+options, arguments = parser.parse_args()
95+
96+
97+
98+class Icons(object):
99+ def __init__(self, icon_uris):
100+ self.icon_uris = [
101+ icon_uri for icon_uri in icon_uris
102+ if urlparse.urlparse(icon_uri).netloc != ""
103+ ]
104+
105+ def get(self):
106+ if len(self.icon_uris) == 0:
107+ return False
108+ for icon_uri in self.icon_uris:
109+ try:
110+ # TODO: cookies & UA
111+ content = urllib2.urlopen(icon_uri).read()
112+ if content:
113+ return content
114+ except Exception, e:
115+ print "Exception while retrieving icon resource", \
116+ icon_uri, \
117+ ":", \
118+ str(e)
119+ return None
120+
121+
122+class Click(object):
123+ def __init__(self, name, domain, homepage, icon_uris):
124+ self.name = name
125+ self.package_name = self._get_package_name(name)
126+ self.domain = domain
127+ self.homepage = homepage
128+ # TODO handle sizes
129+ self.icon_content = Icons(icon_uris).get()
130+
131+ def _generate_valid_filename(self):
132+ filename = self.domain.lower().replace('.', '')
133+ return filename
134+
135+ def generate(self):
136+ temp_folder = tempfile.gettempdir()
137+ temp_click_folder = tempfile.mkdtemp(dir=temp_folder)
138+
139+ filename = self._generate_valid_filename()
140+
141+ desktop_filename = "{}.desktop".format(filename)
142+ apparmor_filename = "{}.appamor".format(filename)
143+ manifest_filename = "manifest.json"
144+ icon_filename = "{}.png".format(filename)
145+
146+ package_name = self._get_package_name(filename)
147+ version = "1.0"
148+
149+ f = open(os.path.join(temp_click_folder, desktop_filename), "w+")
150+ f.write(self._generate_desktop(icon_filename))
151+ f.close()
152+
153+ f = open(os.path.join(temp_click_folder, apparmor_filename), "w+")
154+ f.write(self._generate_apparmor())
155+ f.close()
156+
157+ f = open(os.path.join(temp_click_folder, manifest_filename), "w+")
158+ f.write(
159+ self._generate_manifest(
160+ filename,
161+ package_name,
162+ apparmor_filename,
163+ desktop_filename,
164+ version
165+ )
166+ )
167+ f.close()
168+
169+ if self.icon_content:
170+ f = open(
171+ os.path.join(
172+ temp_click_folder,
173+ icon_filename
174+ ),
175+ "wb+"
176+ )
177+ f.write(self.icon_content)
178+ f.close()
179+
180+ cwd = os.getcwd()
181+ os.chdir(temp_click_folder)
182+ output = subprocess.check_output(
183+ ['click',
184+ 'build',
185+ temp_click_folder]
186+ )
187+ os.chdir(cwd)
188+
189+ return os.path.join(
190+ temp_click_folder,
191+ "{}_{}_all.click".format(
192+ package_name,
193+ version
194+ )
195+ )
196+
197+ def _get_package_name(self, valid_app_name):
198+ return "{}.user-webapps-installer".format(valid_app_name)
199+
200+ def _generate_apparmor(self):
201+ return """
202+{
203+ "template": "ubuntu-webapp",
204+ "policy_groups": [
205+ "networking",
206+ "audio",
207+ "video",
208+ "webview"
209+ ],
210+ "policy_version": 1.2
211+}
212+"""
213+
214+ def _generate_manifest(
215+ self,
216+ base_name,
217+ package_name,
218+ apparmor_filename,
219+ desktop_filename,
220+ version):
221+ return """
222+{{
223+ "description": "{}",
224+ "framework": "ubuntu-sdk-14.10",
225+ "architecture": "all",
226+ "hooks": {{
227+ "{}": {{
228+ "apparmor": "{}",
229+ "desktop": "{}"
230+ }}
231+ }},
232+ "maintainer": "Webapps Team <webapps@lists.launchpad.net>",
233+ "name": "{}",
234+ "title": "{}",
235+ "version": "1.0"
236+}}
237+""".format(self.name,
238+ base_name,
239+ apparmor_filename,
240+ desktop_filename,
241+ package_name,
242+ self.name,
243+ version)
244+
245+ def _generate_desktop(self, icon_filename):
246+ return """
247+[Desktop Entry]
248+Type=Application
249+Terminal=false
250+Exec=webapp-container --enable-back-forward --webappUrlPatterns=https?://{}/* {} %u
251+Name={}
252+Icon=./{}
253+X-Ubuntu-Touch=true
254+X-Ubuntu-Single-Instance=true
255+X-Ubuntu-Default-Department-ID=internet
256+""".format(self.domain,
257+ self.homepage,
258+ self.name,
259+ icon_filename)
260+
261+def install_webapp(
262+ name,
263+ domain,
264+ homepage,
265+ icon_uris):
266+ click = Click(name, domain, homepage, icon_uris)
267+ click_package_filename = click.generate()
268+
269+ subprocess.check_output(
270+ ['pkcon',
271+ 'install-local',
272+ '--allow-untrusted',
273+ click_package_filename]
274+ )
275+
276+ os.remove(click_package_filename)
277+
278+
279+if options.homepage == "" or \
280+ options.name == "" or \
281+ options.domain == "":
282+ print "Invalid options", homepage, name, domain
283+ sys.exit(1)
284+
285+
286+try:
287+ install_webapp(
288+ options.name,
289+ options.domain,
290+ options.homepage,
291+ options.icons
292+ )
293+except Exception, e:
294+ print 'Error:', str(e)
295+ sys.exit(1)
296
297=== modified file 'po/webbrowser-app.pot'
298--- po/webbrowser-app.pot 2016-06-02 17:30:05 +0000
299+++ po/webbrowser-app.pot 2016-06-16 15:34:13 +0000
300@@ -1,6 +1,6 @@
301 # SOME DESCRIPTIVE TITLE.
302 # Copyright (C) YEAR Canonical Ltd.
303-# This file is distributed under the same license as the webbrowser-app package.
304+# This file is distributed under the same license as the PACKAGE package.
305 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
306 #
307 #, fuzzy
308@@ -8,7 +8,7 @@
309 msgstr ""
310 "Project-Id-Version: webbrowser-app\n"
311 "Report-Msgid-Bugs-To: \n"
312-"POT-Creation-Date: 2016-06-02 19:24+0200\n"
313+"POT-Creation-Date: 2015-09-01 15:51-0400\n"
314 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
315 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
316 "Language-Team: LANGUAGE <LL@li.org>\n"
317@@ -23,13 +23,13 @@
318 msgstr ""
319
320 #: src/app/AlertDialog.qml:26 src/app/AuthenticationDialog.qml:47
321-#: src/app/ConfirmDialog.qml:26 src/app/HttpAuthenticationDialog.qml:59
322-#: src/app/PromptDialog.qml:32 src/app/webbrowser/BookmarkOptions.qml:101
323+#: src/app/ConfirmDialog.qml:26 src/app/PromptDialog.qml:32
324+#: src/app/webbrowser/BookmarkOptions.qml:101
325 #: src/app/webbrowser/LeavePrivateModeDialog.qml:39
326 msgid "OK"
327 msgstr ""
328
329-#: src/app/AuthenticationDialog.qml:24 src/app/HttpAuthenticationDialog.qml:25
330+#: src/app/AuthenticationDialog.qml:24
331 msgid "Authentication required."
332 msgstr ""
333
334@@ -39,25 +39,23 @@
335 msgid "The website %1 requires authentication."
336 msgstr ""
337
338-#: src/app/AuthenticationDialog.qml:34 src/app/HttpAuthenticationDialog.qml:39
339+#: src/app/AuthenticationDialog.qml:34
340 msgid "Username"
341 msgstr ""
342
343-#: src/app/AuthenticationDialog.qml:41 src/app/HttpAuthenticationDialog.qml:49
344+#: src/app/AuthenticationDialog.qml:41
345 msgid "Password"
346 msgstr ""
347
348 #: src/app/AuthenticationDialog.qml:53 src/app/ConfirmDialog.qml:31
349-#: src/app/HttpAuthenticationDialog.qml:69 src/app/PromptDialog.qml:38
350-#: src/app/webbrowser/BookmarkOptions.qml:143
351-#: src/app/webbrowser/ContentDownloadDialog.qml:114
352-#: src/app/webbrowser/ContextMenuMobile.qml:139
353-#: src/app/webbrowser/DownloadDelegate.qml:187
354+#: src/app/PromptDialog.qml:38 src/app/webbrowser/BookmarkOptions.qml:143
355+#: src/app/webbrowser/ContextMenuMobile.qml:142
356+#: src/app/webbrowser/HistoryView.qml:165
357+#: src/app/webbrowser/HistoryViewWide.qml:307
358 #: src/app/webbrowser/LeavePrivateModeDialog.qml:32
359-#: src/app/webbrowser/SettingsPage.qml:258
360-#: src/app/webbrowser/SettingsPage.qml:312
361+#: src/app/webbrowser/SettingsPage.qml:289
362+#: src/app/webbrowser/SettingsPage.qml:343
363 #: src/app/webcontainer/AccountChooserDialog.qml:96
364-#: src/app/webcontainer/ContextMenuMobile.qml:141
365 msgid "Cancel"
366 msgstr ""
367
368@@ -110,13 +108,18 @@
369 msgstr ""
370
371 #: src/app/ErrorSheet.qml:48
372-msgid "Please check your network settings and try refreshing the page."
373+msgid ""
374+"Ubuntu suggests you check your network settings and try refreshing the page."
375 msgstr ""
376
377 #: src/app/ErrorSheet.qml:53
378 msgid "Refresh page"
379 msgstr ""
380
381+#: src/app/FilePickerDialog.qml:28
382+msgid "Please choose a file"
383+msgstr ""
384+
385 #: src/app/GeolocationPermissionRequest.qml:28
386 msgid "Permission Request"
387 msgstr ""
388@@ -276,36 +279,6 @@
389 "which failed our security checks for an unknown reason."
390 msgstr ""
391
392-#: src/app/MediaAccessDialog.qml:31
393-msgid "Permission"
394-msgstr ""
395-
396-#: src/app/MediaAccessDialog.qml:38
397-msgid "Allow this domain to access your camera and microphone?"
398-msgstr ""
399-
400-#: src/app/MediaAccessDialog.qml:39
401-msgid "Allow this domain to access your camera?"
402-msgstr ""
403-
404-#: src/app/MediaAccessDialog.qml:40
405-msgid "Allow this domain to access your microphone?"
406-msgstr ""
407-
408-#. TRANSLATORS: %1 is the URL of the site requesting access to camera and/or microphone and %2 is the URL of the site that embeds it
409-#: src/app/MediaAccessDialog.qml:48
410-#, qt-format
411-msgid "%1 (embedded in %2)"
412-msgstr ""
413-
414-#: src/app/MediaAccessDialog.qml:72
415-msgid "Yes"
416-msgstr ""
417-
418-#: src/app/MediaAccessDialog.qml:83
419-msgid "No"
420-msgstr ""
421-
422 #: src/app/PromptDialog.qml:23
423 msgid "JavaScript Prompt"
424 msgstr ""
425@@ -374,7 +347,7 @@
426 msgid "Erase"
427 msgstr ""
428
429-#: src/app/actions/FindInPage.qml:23 src/app/webbrowser/Browser.qml:568
430+#: src/app/actions/FindInPage.qml:23 src/app/webbrowser/Browser.qml:402
431 msgid "Find in page"
432 msgstr ""
433
434@@ -404,8 +377,7 @@
435 msgid "Address;URL;www"
436 msgstr ""
437
438-#: src/app/actions/NewTab.qml:23 src/app/webbrowser/Browser.qml:451
439-#: src/app/webbrowser/TabsBar.qml:91
440+#: src/app/actions/NewTab.qml:23 src/app/webbrowser/Browser.qml:687
441 msgid "New Tab"
442 msgstr ""
443
444@@ -419,10 +391,6 @@
445 msgid "Open image in new tab"
446 msgstr ""
447
448-#: src/app/actions/OpenLinkInBrowser.qml:22
449-msgid "Open link in default browser"
450-msgstr ""
451-
452 #: src/app/actions/OpenLinkInNewBackgroundTab.qml:22
453 msgid "Open link in new background tab"
454 msgstr ""
455@@ -431,10 +399,6 @@
456 msgid "Open link in new tab"
457 msgstr ""
458
459-#: src/app/actions/OpenVideoInNewTab.qml:22
460-msgid "Open video in new tab"
461-msgstr ""
462-
463 #: src/app/actions/Paste.qml:22
464 msgid "Paste"
465 msgstr ""
466@@ -443,8 +407,7 @@
467 msgid "Redo"
468 msgstr ""
469
470-#: src/app/actions/Reload.qml:23 src/app/webbrowser/SadTab.qml:86
471-#: src/app/webbrowser/TabsBar.qml:96 src/app/webcontainer/SadPage.qml:51
472+#: src/app/actions/Reload.qml:23
473 msgid "Reload"
474 msgstr ""
475
476@@ -462,16 +425,13 @@
477 msgid "Save link"
478 msgstr ""
479
480-#: src/app/actions/SaveVideo.qml:22
481-msgid "Save video"
482-msgstr ""
483-
484-#: src/app/actions/SelectAll.qml:22 src/app/webbrowser/DownloadsPage.qml:83
485+#: src/app/actions/SelectAll.qml:22 src/app/webbrowser/HistoryView.qml:180
486+#: src/app/webbrowser/HistoryViewWide.qml:328
487 msgid "Select all"
488 msgstr ""
489
490-#: src/app/actions/Share.qml:22 src/app/webbrowser/Browser.qml:548
491-msgid "Share"
492+#: src/app/actions/ShareLink.qml:22
493+msgid "Share…"
494 msgstr ""
495
496 #: src/app/actions/Undo.qml:22
497@@ -479,16 +439,16 @@
498 msgstr ""
499
500 #. TRANSLATORS: %2 refers to the total number of find in page results and %1 to the highlighted result
501-#: src/app/webbrowser/AddressBar.qml:208
502+#: src/app/webbrowser/AddressBar.qml:221
503 #, qt-format
504 msgid "%1/%2"
505 msgstr ""
506
507-#: src/app/webbrowser/AddressBar.qml:244
508+#: src/app/webbrowser/AddressBar.qml:259
509 msgid "find in page"
510 msgstr ""
511
512-#: src/app/webbrowser/AddressBar.qml:245
513+#: src/app/webbrowser/AddressBar.qml:260
514 msgid "search or enter an address"
515 msgstr ""
516
517@@ -507,8 +467,9 @@
518 msgstr ""
519
520 #: src/app/webbrowser/BookmarkOptions.qml:78
521-#: src/app/webbrowser/BookmarksFoldersView.qml:133
522-#: src/app/webbrowser/BookmarksFoldersViewWide.qml:105
523+#: src/app/webbrowser/BookmarksFolderListView.qml:115
524+#: src/app/webbrowser/FavoriteOptionTabs.qml:211
525+#: src/app/webbrowser/NewTabViewWide.qml:122
526 msgid "All Bookmarks"
527 msgstr ""
528
529@@ -522,141 +483,119 @@
530 msgstr ""
531
532 #: src/app/webbrowser/BookmarkOptions.qml:153
533-#: src/app/webbrowser/SettingsPage.qml:322
534+#: src/app/webbrowser/SettingsPage.qml:353
535 msgid "Save"
536 msgstr ""
537
538-#: src/app/webbrowser/BookmarksFoldersView.qml:191
539-#: src/app/webbrowser/BookmarksFoldersViewWide.qml:148
540-#: src/app/webbrowser/NewTabView.qml:253 src/app/webbrowser/SettingsPage.qml:85
541-#: src/app/webbrowser/SettingsPage.qml:286
542-msgid "Homepage"
543-msgstr ""
544-
545-#: src/app/webbrowser/BookmarksView.qml:32
546-#: src/app/webbrowser/BookmarksViewWide.qml:32
547-#: src/app/webbrowser/Browser.qml:556 src/app/webbrowser/NewTabView.qml:130
548-#: src/app/webbrowser/NewTabViewWide.qml:139
549-msgid "Bookmarks"
550-msgstr ""
551-
552-#: src/app/webbrowser/BookmarksView.qml:70
553-#: src/app/webbrowser/BookmarksViewWide.qml:69
554-#: src/app/webbrowser/Browser.qml:437 src/app/webbrowser/HistoryView.qml:128
555-#: src/app/webbrowser/HistoryViewWide.qml:418
556-msgid "Done"
557-msgstr ""
558-
559-#: src/app/webbrowser/BookmarksView.qml:84
560-#: src/app/webbrowser/BookmarksViewWide.qml:83
561-#: src/app/webbrowser/HistoryView.qml:142
562-#: src/app/webbrowser/HistoryViewWide.qml:432
563-#: src/app/webbrowser/TabsBar.qml:153 src/app/webbrowser/TabsList.qml:99
564+#: src/app/webbrowser/Browser.qml:371
565+msgid "Share"
566+msgstr ""
567+
568+#: src/app/webbrowser/Browser.qml:378
569+#: src/app/webbrowser/HistoryViewWide.qml:291
570+msgid "History"
571+msgstr ""
572+
573+#: src/app/webbrowser/Browser.qml:385
574+msgid "Open tabs"
575+msgstr ""
576+
577+#: src/app/webbrowser/Browser.qml:395 src/app/webbrowser/HistoryView.qml:121
578+#: src/app/webbrowser/HistoryViewWide.qml:396
579+#: src/app/webbrowser/TabsBar.qml:187 src/app/webbrowser/TabsList.qml:82
580 msgid "New tab"
581 msgstr ""
582
583-#: src/app/webbrowser/Browser.qml:562 src/app/webbrowser/HistoryView.qml:30
584-#: src/app/webbrowser/HistoryViewWide.qml:35
585-msgid "History"
586-msgstr ""
587-
588-#: src/app/webbrowser/Browser.qml:575 src/app/webbrowser/DownloadsPage.qml:45
589-msgid "Downloads"
590-msgstr ""
591-
592-#: src/app/webbrowser/Browser.qml:582
593+#: src/app/webbrowser/Browser.qml:412
594 msgid "Leave Private Mode"
595 msgstr ""
596
597-#: src/app/webbrowser/Browser.qml:582
598+#: src/app/webbrowser/Browser.qml:412
599 msgid "Private Mode"
600 msgstr ""
601
602-#: src/app/webbrowser/Browser.qml:600 src/app/webbrowser/SettingsPage.qml:41
603+#: src/app/webbrowser/Browser.qml:430 src/app/webbrowser/SettingsPage.qml:51
604 msgid "Settings"
605 msgstr ""
606
607+#: src/app/webbrowser/Browser.qml:673 src/app/webbrowser/HistoryView.qml:108
608+#: src/app/webbrowser/HistoryViewWide.qml:382
609+msgid "Done"
610+msgstr ""
611+
612 #. TRANSLATORS: %1 refers to the current number of tabs opened
613-#: src/app/webbrowser/Browser.qml:772 src/app/webbrowser/Browser.qml:810
614+#: src/app/webbrowser/Browser.qml:780
615 #, qt-format
616 msgid "(%1)"
617 msgstr ""
618
619-#: src/app/webbrowser/Browser.qml:1366
620+#: src/app/webbrowser/Browser.qml:1221
621 msgid "Swipe Up To Exit Full Screen"
622 msgstr ""
623
624-#: src/app/webbrowser/Browser.qml:1367
625+#: src/app/webbrowser/Browser.qml:1222
626 msgid "Press ESC To Exit Full Screen"
627 msgstr ""
628
629-#: src/app/webbrowser/ContentDownloadDialog.qml:83
630-msgid ""
631-"Choose an application to open this file or add it to the downloads folder."
632-msgstr ""
633-
634-#: src/app/webbrowser/ContentDownloadDialog.qml:89
635-msgid "Choose an application"
636-msgstr ""
637-
638-#: src/app/webbrowser/ContentDownloadDialog.qml:102
639-msgid "Download"
640-msgstr ""
641-
642-#: src/app/webbrowser/DownloadDelegate.qml:159
643-msgid "Download failed"
644-msgstr ""
645-
646-#: src/app/webbrowser/DownloadDelegate.qml:208
647-msgid "Resume"
648-msgstr ""
649-
650-#: src/app/webbrowser/DownloadsPage.qml:59
651-msgid "Confirm selection"
652-msgstr ""
653-
654-#: src/app/webbrowser/DownloadsPage.qml:99
655-msgid "Delete"
656-msgstr ""
657-
658-#: src/app/webbrowser/DownloadsPage.qml:259
659-msgid "No downloads available"
660-msgstr ""
661-
662-#: src/app/webbrowser/ExpandedHistoryView.qml:119
663+#: src/app/webbrowser/ExpandedHistoryView.qml:103
664 #, qt-format
665 msgid "%1 page"
666 msgid_plural "%1 pages"
667 msgstr[0] ""
668 msgstr[1] ""
669
670-#: src/app/webbrowser/ExpandedHistoryView.qml:134
671-#: src/app/webbrowser/NewTabView.qml:143
672+#: src/app/webbrowser/ExpandedHistoryView.qml:118
673+#: src/app/webbrowser/NewTabView.qml:123
674 msgid "Less"
675 msgstr ""
676
677+#: src/app/webbrowser/FavoriteOptionTabs.qml:115
678+msgid "Install App"
679+msgstr ""
680+
681+#: src/app/webbrowser/FavoriteOptionTabs.qml:148
682+msgid "Add bookmark"
683+msgstr ""
684+
685+#: src/app/webbrowser/FavoriteOptionTabs.qml:187
686+msgid "bookmark name"
687+msgstr ""
688+
689+#: src/app/webbrowser/FavoriteOptionTabs.qml:198
690+msgid "save in"
691+msgstr ""
692+
693+#: src/app/webbrowser/FavoriteOptionTabs.qml:360
694+msgid "Install"
695+msgstr ""
696+
697 #: src/app/webbrowser/HistorySectionDelegate.qml:26
698 msgid "Last Visited"
699 msgstr ""
700
701 #: src/app/webbrowser/HistorySectionDelegate.qml:48
702-#: src/app/webbrowser/HistoryViewWide.qml:251
703+#: src/app/webbrowser/HistoryViewWide.qml:149
704 msgid "Yesterday"
705 msgstr ""
706
707-#: src/app/webbrowser/HistoryViewWide.qml:160
708-msgid "search history"
709+#: src/app/webbrowser/HistoryView.qml:210
710+#: src/app/webbrowser/HistoryViewWide.qml:347
711+msgid "Delete"
712 msgstr ""
713
714-#: src/app/webbrowser/HistoryViewWide.qml:235
715+#: src/app/webbrowser/HistoryViewWide.qml:133
716 msgid "All History"
717 msgstr ""
718
719-#: src/app/webbrowser/HistoryViewWide.qml:249
720-#: src/app/webbrowser/HistoryViewWide.qml:347
721+#: src/app/webbrowser/HistoryViewWide.qml:147
722+#: src/app/webbrowser/HistoryViewWide.qml:212
723 msgid "Today"
724 msgstr ""
725
726+#: src/app/webbrowser/HomescreenWebappInstaller.qml:141
727+msgid "Invalid webapp meta information"
728+msgstr ""
729+
730 #: src/app/webbrowser/LeavePrivateModeDialog.qml:24
731 msgid "Going to public mode will close all private tabs"
732 msgstr ""
733@@ -671,37 +610,28 @@
734 "Bookmarks you create will be preserved, however."
735 msgstr ""
736
737-#: src/app/webbrowser/NewTabView.qml:143
738+#: src/app/webbrowser/NewTabView.qml:108
739+#: src/app/webbrowser/NewTabViewWide.qml:313
740+msgid "Bookmarks"
741+msgstr ""
742+
743+#: src/app/webbrowser/NewTabView.qml:123
744 msgid "More"
745 msgstr ""
746
747-#: src/app/webbrowser/NewTabView.qml:328
748-#: src/app/webbrowser/NewTabViewWide.qml:138
749+#: src/app/webbrowser/NewTabView.qml:226
750+#: src/app/webbrowser/NewTabViewWide.qml:312
751 msgid "Top sites"
752 msgstr ""
753
754-#: src/app/webbrowser/NewTabView.qml:359
755+#: src/app/webbrowser/NewTabView.qml:257
756 msgid "You haven't visited any site yet"
757 msgstr ""
758
759-#: src/app/webbrowser/SadTab.qml:48
760-msgid "The rendering process has been closed for this tab."
761-msgstr ""
762-
763-#. TRANSLATORS: %1 is the URL of the page that crashed the renderer process
764-#: src/app/webbrowser/SadTab.qml:65
765-#, qt-format
766-msgid "Something went wrong while displaying %1."
767-msgstr ""
768-
769-#: src/app/webbrowser/SadTab.qml:67
770-msgid ""
771-"The system is low on memory and can't display this webpage. Try closing "
772-"unneeded tabs and reloading."
773-msgstr ""
774-
775-#: src/app/webbrowser/SadTab.qml:80
776-msgid "Close tab"
777+#: src/app/webbrowser/NewTabViewWide.qml:160
778+#: src/app/webbrowser/SettingsPage.qml:92
779+#: src/app/webbrowser/SettingsPage.qml:317
780+msgid "Homepage"
781 msgstr ""
782
783 #: src/app/webbrowser/SecurityCertificatePopover.qml:69
784@@ -756,82 +686,61 @@
785 msgid "Which is run by"
786 msgstr ""
787
788-#: src/app/webbrowser/SettingsDeviceSelector.qml:43
789-msgid "Default"
790-msgstr ""
791-
792-#: src/app/webbrowser/SettingsPage.qml:70
793-#: src/app/webbrowser/SettingsPage.qml:150
794+#: src/app/webbrowser/SettingsPage.qml:81
795+#: src/app/webbrowser/SettingsPage.qml:176
796 msgid "Search engine"
797 msgstr ""
798
799-#: src/app/webbrowser/SettingsPage.qml:96
800+#: src/app/webbrowser/SettingsPage.qml:101
801 msgid "Restore previous session at startup"
802 msgstr ""
803
804-#: src/app/webbrowser/SettingsPage.qml:115
805-#: src/app/webbrowser/SettingsPage.qml:193
806-msgid "Privacy & permissions"
807-msgstr ""
808-
809-#: src/app/webbrowser/SettingsPage.qml:126
810+#: src/app/webbrowser/SettingsPage.qml:119
811+msgid "Allow opening new tabs in background"
812+msgstr ""
813+
814+#: src/app/webbrowser/SettingsPage.qml:138
815+#: src/app/webbrowser/SettingsPage.qml:228
816+msgid "Privacy"
817+msgstr ""
818+
819+#: src/app/webbrowser/SettingsPage.qml:146
820 msgid "Reset browser settings"
821 msgstr ""
822
823-#: src/app/webbrowser/SettingsPage.qml:206
824-#: src/app/webbrowser/SettingsPage.qml:342
825-msgid "Camera & microphone"
826-msgstr ""
827-
828-#: src/app/webbrowser/SettingsPage.qml:215
829+#: src/app/webbrowser/SettingsPage.qml:249
830 msgid "Clear Browsing History"
831 msgstr ""
832
833-#: src/app/webbrowser/SettingsPage.qml:219
834+#: src/app/webbrowser/SettingsPage.qml:252
835 msgid "Clear Browsing History?"
836 msgstr ""
837
838-#: src/app/webbrowser/SettingsPage.qml:227
839+#: src/app/webbrowser/SettingsPage.qml:259
840 msgid "Clear Cache"
841 msgstr ""
842
843-#: src/app/webbrowser/SettingsPage.qml:230
844+#: src/app/webbrowser/SettingsPage.qml:261
845 msgid "Clear Cache?"
846 msgstr ""
847
848-#: src/app/webbrowser/SettingsPage.qml:265
849+#: src/app/webbrowser/SettingsPage.qml:296
850 msgid "Clear"
851 msgstr ""
852
853-#: src/app/webbrowser/SettingsPage.qml:354
854-msgid "Microphone"
855-msgstr ""
856-
857-#: src/app/webbrowser/SettingsPage.qml:375
858-msgid "Camera"
859-msgstr ""
860-
861-#: src/app/webbrowser/TabPreview.qml:86
862+#: src/app/webbrowser/TabPreview.qml:99
863 msgid "Tap to view"
864 msgstr ""
865
866-#: src/app/webbrowser/TabsBar.qml:102
867-msgid "Close Tab"
868-msgstr ""
869-
870-#: src/app/webbrowser/UrlPreviewDelegate.qml:135
871-msgid "Remove"
872-msgstr ""
873-
874 #. TRANSLATORS: %1 refers to the current page’s title
875 #: src/app/webbrowser/webbrowser-app.qml:35
876-#: src/app/webcontainer/webapp-container.qml:69
877+#: src/app/webcontainer/webapp-container.qml:68
878 #, qt-format
879 msgid "%1 - Ubuntu Web Browser"
880 msgstr ""
881
882 #: src/app/webbrowser/webbrowser-app.qml:37
883-#: src/app/webcontainer/webapp-container.qml:71
884+#: src/app/webcontainer/webapp-container.qml:70
885 msgid "Ubuntu Web Browser"
886 msgstr ""
887
888@@ -863,10 +772,6 @@
889 "time; please try again after closing all other account windows."
890 msgstr ""
891
892-#: src/app/webcontainer/AccountsLogic.qml:214
893-msgid "Authentication failed"
894-msgstr ""
895-
896 #. TRANSLATORS: %1 refers to the application name, %2 refers to the account provider
897 #: src/app/webcontainer/AccountsSplashScreen.qml:45
898 #, qt-format
899@@ -896,10 +801,6 @@
900 msgid "Choose account"
901 msgstr ""
902
903-#: src/app/webcontainer/SadPage.qml:45
904-msgid "Oops, something went wrong."
905-msgstr ""
906-
907 #: po/src/app/webbrowser/webbrowser-app.desktop.in.in.h:1
908 msgid "Browser"
909 msgstr ""
910
911=== modified file 'src/app/webbrowser/AddressBar.qml'
912--- src/app/webbrowser/AddressBar.qml 2016-02-12 10:12:30 +0000
913+++ src/app/webbrowser/AddressBar.qml 2016-06-16 15:34:13 +0000
914@@ -45,10 +45,14 @@
915 property var findController: null
916 property color fgColor: Theme.palette.normal.baseText
917
918+ property bool webappCapableWebsite: false
919+
920 property var securityStatus: null
921
922 readonly property Item bookmarkTogglePlaceHolder: bookmarkTogglePlaceHolderItem
923
924+ onWebappCapableWebsiteChanged: handleWebappCapableUpdate(webappCapableWebsite)
925+
926 // XXX: for testing purposes only, do not use to modify the
927 // contents/behaviour of the internals of the component.
928 readonly property Item __textField: textField
929@@ -59,6 +63,20 @@
930 textField.selectAll()
931 }
932
933+ function handleWebappCapableUpdate(webappCapable) {
934+ bookmarkToggleIcon.color = getBookmarkToggleColor(webappCapable)
935+ }
936+
937+ function getBookmarkToggleColor(webappCapable) {
938+ console.log(addressbar.bookmarked)
939+ if (webappCapable) {
940+ return UbuntuColors.lightAubergine
941+ }
942+
943+ return addressbar.bookmarked ?
944+ UbuntuColors.orange : addressbar.fgColor
945+ }
946+
947 Binding {
948 target: findController
949 property: "text"
950@@ -220,12 +238,14 @@
951 visible: !findInPageMode && internal.idle && addressbar.actualUrl.toString()
952
953 Icon {
954+ id: bookmarkToggleIcon
955+
956 height: parent.height - units.gu(2)
957 width: height
958 anchors.centerIn: parent
959
960- name: addressbar.bookmarked ? "starred" : "non-starred"
961- color: addressbar.bookmarked ? UbuntuColors.orange : addressbar.fgColor
962+ name: addressbar.bookmarked || webappCapableWebsite ? "starred" : "non-starred"
963+ color: getBookmarkToggleColor(webappCapableWebsite)
964 }
965
966 onClicked: addressbar.toggleBookmark()
967
968=== modified file 'src/app/webbrowser/Browser.qml'
969--- src/app/webbrowser/Browser.qml 2016-05-23 15:49:49 +0000
970+++ src/app/webbrowser/Browser.qml 2016-06-16 15:34:13 +0000
971@@ -171,6 +171,11 @@
972 readonly property string defaultVideoDevice: ""
973 }
974
975+ HomescreenWebappInstaller {
976+ id: webappInstaller
977+ dbPath: dataLocation + "/homescreen-webapps.sqlite"
978+ }
979+
980 FocusScope {
981 id: contentsContainer
982 anchors.fill: parent
983@@ -487,6 +492,22 @@
984 Chrome {
985 id: chrome
986
987+ function isCurrentUrlWebappInstalled() {
988+ return ((webview && browser.webappInstaller)
989+ ? browser.webappInstaller.model.contains(webview.url) : false)
990+ }
991+ installed: isCurrentUrlWebappInstalled()
992+ onInstallWebapp: {
993+ function onInstalled(id, status, errorMessage) {
994+ webappInstaller.installed.disconnect(onInstalled)
995+ chrome.installed = status
996+ console.log('installed dd ' + status)
997+ }
998+ webappInstaller.installed.connect(onInstalled)
999+
1000+ webappInstaller.install(webview.url, metainfos)
1001+ }
1002+
1003 tab: internal.nextTab || tabsModel.currentTab
1004 webview: tab ? tab.webview : null
1005 tabsModel: browser.tabsModel
1006@@ -1034,6 +1055,11 @@
1007 asynchronous: true
1008 }
1009
1010+ function webappManifestDetected(webview, metainfos) {
1011+ chrome.webappCapableWebsite = true
1012+ chrome.webappCapableMetaInfo = metainfos
1013+ }
1014+
1015 Component {
1016 id: tabComponent
1017
1018@@ -1053,6 +1079,18 @@
1019 property BrowserTab tab
1020 readonly property bool current: tab.current
1021
1022+ Component.onCompleted: {
1023+ // TODO make sure that there is no race here between this and the url changed
1024+ webviewimpl.context.addUserScript(webappCapableDetectorUserScript)
1025+
1026+ var mhs = []
1027+ for (var i = 0; i < messageHandlers.length; i++) {
1028+ mhs.push(messageHandlers[i])
1029+ }
1030+ mhs.push(webappCapableDetectorMessageHandler)
1031+ messageHandlers = mhs
1032+ }
1033+
1034 currentWebview: browser.currentWebview
1035 filePicker: filePickerLoader.item
1036
1037@@ -1071,6 +1109,24 @@
1038 preferences.appCacheEnabled: true
1039
1040 property QtObject contextModel: null
1041+
1042+ // Handle webapp capable webapps
1043+ property var webappCapableDetectorUserScript: Oxide.UserScript {
1044+ context: "oxide://page-metadata-gathering/"
1045+ url: Qt.resolvedUrl("page-metadata-gathering.js")
1046+ incognitoEnabled: false
1047+ matchAllFrames: false
1048+ }
1049+
1050+ property var webappCapableDetectorMessageHandler: Oxide.ScriptMessageHandler {
1051+ msgId: "webapp-capable-website-detected"
1052+ contexts: ["oxide://page-metadata-gathering/"]
1053+ callback: function(msg, frame) {
1054+ console.log('dd')
1055+ webappManifestDetected(webviewimpl, msg.args)
1056+ }
1057+ }
1058+
1059 contextualActions: ActionList {
1060 Actions.OpenLinkInNewTab {
1061 objectName: "OpenLinkInNewTabContextualAction"
1062
1063=== modified file 'src/app/webbrowser/CMakeLists.txt'
1064--- src/app/webbrowser/CMakeLists.txt 2015-12-10 09:06:06 +0000
1065+++ src/app/webbrowser/CMakeLists.txt 2016-06-16 15:34:13 +0000
1066@@ -20,6 +20,10 @@
1067 history-model.cpp
1068 limit-proxy-model.cpp
1069 tabs-model.cpp
1070+ homescreen-manifest-parser.cpp
1071+ homescreen-manifest-content.cpp
1072+ homescreen-installed-webapp-model.cpp
1073+ homescreen-webapp.cpp
1074 text-search-filter-model.cpp
1075 )
1076
1077
1078=== modified file 'src/app/webbrowser/Chrome.qml'
1079--- src/app/webbrowser/Chrome.qml 2016-02-09 22:01:57 +0000
1080+++ src/app/webbrowser/Chrome.qml 2016-06-16 15:34:13 +0000
1081@@ -54,6 +54,12 @@
1082 navigationBar.selectAll()
1083 }
1084
1085+ property alias installed: navigationBar.installed
1086+ property alias webappCapableWebsite: navigationBar.webappCapableWebsite
1087+ property alias webappCapableMetaInfo: navigationBar.webappCapableMetaInfo
1088+
1089+ signal installWebapp(var metainfos)
1090+
1091 FocusScope {
1092 id: content
1093 anchors.fill: parent
1094
1095=== added file 'src/app/webbrowser/FavoriteOptionTabs.qml'
1096--- src/app/webbrowser/FavoriteOptionTabs.qml 1970-01-01 00:00:00 +0000
1097+++ src/app/webbrowser/FavoriteOptionTabs.qml 2016-06-16 15:34:13 +0000
1098@@ -0,0 +1,386 @@
1099+/*
1100+ * Copyright 2015 Canonical Ltd.
1101+ *
1102+ * This file is part of webbrowser-app.
1103+ *
1104+ * webbrowser-app is free software; you can redistribute it and/or modify
1105+ * it under the terms of the GNU General Public License as published by
1106+ * the Free Software Foundation; version 3.
1107+ *
1108+ * webbrowser-app is distributed in the hope that it will be useful,
1109+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1110+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1111+ * GNU General Public License for more details.
1112+ *
1113+ * You should have received a copy of the GNU General Public License
1114+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1115+ */
1116+
1117+import QtQuick 2.0
1118+import Ubuntu.Components 1.1
1119+
1120+
1121+Rectangle {
1122+ id: dialog
1123+
1124+ property bool isBookmarked: false
1125+ property bool isWebappInstallable: false
1126+ property string bookmarkTitle: ""
1127+
1128+ property var webappCapableMetaInfo
1129+
1130+ property bool installingWebapp: false
1131+
1132+ border.color: "black"
1133+ border.width: 2
1134+
1135+ function webappInstallationComplete() {
1136+ installActivityIndicator.running = false
1137+ }
1138+
1139+ onInstallingWebappChanged: {
1140+ installActivityIndicator.running = false
1141+ installActivityIndicator.visible = false
1142+ console.log('installingWebapp ' + installingWebapp)
1143+ }
1144+ onVisibleChanged: {
1145+ if (visible) {
1146+ setBookmarkTabAsSelected()
1147+ }
1148+ }
1149+
1150+ signal bookmarked()
1151+ signal installWebapp(var metainfos)
1152+
1153+ function setBookmarkTabAsSelected() {
1154+ installTabHeader.selected = false
1155+ installTabHeader.border.width = 2
1156+
1157+ bookmarkTabHeader.selected = true
1158+ bookmarkTabHeader.border.width = 0
1159+
1160+ installTabContent.visible = false
1161+ bookmarkTabContent.visible = true
1162+ }
1163+
1164+ function setInstallTabAsSelected() {
1165+ bookmarkTabHeader.selected = false
1166+ bookmarkTabHeader.border.width = 2
1167+
1168+ installTabHeader.selected = true
1169+ installTabHeader.border.width = 0
1170+
1171+ installTabContent.visible = true
1172+ bookmarkTabContent.visible = false
1173+ }
1174+
1175+ color: "white"
1176+
1177+ Rectangle {
1178+
1179+ anchors.centerIn: parent
1180+
1181+ height: parent.height - 2
1182+ width: parent.width - 2
1183+
1184+ border.color: "black"
1185+ border.width: 2
1186+
1187+ radius: units.gu(1)
1188+
1189+ Item {
1190+ id: content
1191+ anchors.fill: parent
1192+
1193+ Rectangle {
1194+ id: installTabHeader
1195+
1196+ property bool selected: true
1197+
1198+ opacity: 1
1199+
1200+ color: "white"
1201+
1202+ height: units.gu(6)
1203+ width: parent.width/2
1204+
1205+ anchors.right: parent.right
1206+ anchors.top: parent.top
1207+
1208+ border.color: "black"
1209+ border.width: 2
1210+
1211+ Label {
1212+ anchors.centerIn: installTabHeader
1213+ text: i18n.tr("Install App")
1214+ font.bold: true
1215+ }
1216+ }
1217+ MouseArea {
1218+ anchors.fill: installTabHeader
1219+ onClicked: {
1220+ if (! installTabHeader.selected) {
1221+ setInstallTabAsSelected()
1222+ }
1223+ }
1224+ }
1225+
1226+ Rectangle {
1227+ id: bookmarkTabHeader
1228+
1229+ property bool selected: false
1230+
1231+ opacity: 1
1232+
1233+ color: "white"
1234+
1235+ height: units.gu(6)
1236+ width: parent.width/2
1237+
1238+ anchors.right: installTabHeader.left
1239+ anchors.top: parent.top
1240+
1241+ border.color: "black"
1242+ border.width: 2
1243+
1244+ Label {
1245+ anchors.centerIn: bookmarkTabHeader
1246+ text: i18n.tr("Add bookmark")
1247+ font.bold: true
1248+ }
1249+ }
1250+ MouseArea {
1251+ anchors.fill: bookmarkTabHeader
1252+ onClicked: {
1253+ if (! bookmarkTabHeader.selected) {
1254+ setBookmarkTabAsSelected()
1255+ }
1256+ }
1257+ }
1258+
1259+ Rectangle {
1260+ id: bookmarkTabContent
1261+
1262+ visible: false
1263+
1264+ color: "white"
1265+
1266+ height: parent.height - units.gu(6)
1267+
1268+ anchors.right: parent.right
1269+ anchors.left: parent.left
1270+
1271+ anchors.top: bookmarkTabHeader.bottom
1272+ anchors.topMargin: units.gu(2)
1273+
1274+ Column {
1275+ id: bookmarkDetailsArea
1276+
1277+ spacing: units.gu(2)
1278+ anchors.leftMargin: units.gu(2)
1279+
1280+ anchors.fill: parent
1281+
1282+ Label {
1283+ id: bookmarkNameLabel
1284+
1285+ text: i18n.tr("bookmark name")
1286+ }
1287+ TextField {
1288+ id: bookmarkNameText
1289+ text: bookmarkTitle
1290+ width: parent.width - units.gu(2)
1291+ }
1292+
1293+ Label {
1294+ id: bookmarkSaveInLabel
1295+
1296+ text: i18n.tr("save in")
1297+ }
1298+ Rectangle {
1299+ id: bookmarkSeparator
1300+ color: "black"
1301+ height: 2
1302+ smooth: true
1303+ width: parent.width - units.gu(2)
1304+ opacity: 0.5
1305+ }
1306+ Label {
1307+ id: bookmarkExistingNameLabel
1308+
1309+ text: i18n.tr("All Bookmarks")
1310+ fontSize: "large"
1311+
1312+ MouseArea {
1313+ anchors.fill: parent
1314+ onClicked: bookmarked()
1315+ }
1316+ }
1317+ }
1318+ }
1319+
1320+ Rectangle {
1321+ id: installTabContent
1322+
1323+ visible: false
1324+
1325+ color: "white"
1326+
1327+ height: parent.height - units.gu(6)
1328+
1329+ anchors.right: parent.right
1330+ anchors.left: parent.left
1331+ anchors.top: installTabHeader.bottom
1332+
1333+ Rectangle {
1334+ id: installGridImageArea
1335+
1336+ border.width: 1
1337+ border.color: "black"
1338+
1339+ width: parent.width/2 - units.gu(3)
1340+ height: parent.width/2 - units.gu(3)
1341+
1342+ anchors.left: parent.left
1343+ anchors.top: parent.top
1344+ anchors.bottom: parent.bottom
1345+
1346+ anchors.leftMargin: units.gu(3)/2
1347+ anchors.rightMargin: units.gu(3)/2
1348+ anchors.topMargin: units.gu(3)/2
1349+ anchors.bottomMargin: units.gu(3)/2
1350+
1351+ Item {
1352+ anchors.fill: parent
1353+ clip: true
1354+
1355+ anchors.leftMargin: units.gu(3)/2
1356+ anchors.rightMargin: units.gu(3)/2
1357+ anchors.topMargin: units.gu(3)/2
1358+ anchors.bottomMargin: units.gu(3)/2
1359+
1360+ Column {
1361+ x: parent.x - units.gu(4)
1362+ y: parent.y - units.gu(4)
1363+
1364+ Row {
1365+ UbuntuShape {
1366+ radius: "medium"
1367+ color: "#E6E4E2"
1368+ width: installGridImageArea.width/3
1369+ height: installGridImageArea.height/3
1370+ }
1371+ UbuntuShape {
1372+ radius: "medium"
1373+ color: "#E6E4E2"
1374+ width: installGridImageArea.width/3
1375+ height: installGridImageArea.height/3
1376+ }
1377+ UbuntuShape {
1378+ radius: "medium"
1379+ color: "#E6E4E2"
1380+ width: installGridImageArea.width/3
1381+ height: installGridImageArea.height/3
1382+ }
1383+ }
1384+ Row {
1385+ UbuntuShape {
1386+ radius: "medium"
1387+ color: "#E6E4E2"
1388+ width: installGridImageArea.width/3
1389+ height: installGridImageArea.height/3
1390+ }
1391+ UbuntuShape {
1392+ radius: "medium"
1393+ color: "#E6E4E2"
1394+ width: installGridImageArea.width/3
1395+ height: installGridImageArea.height/3
1396+ Label {
1397+ anchors.centerIn: parent
1398+ text: installAppName
1399+ }
1400+ }
1401+ UbuntuShape {
1402+ radius: "medium"
1403+ color: "#E6E4E2"
1404+ width: installGridImageArea.width/3
1405+ height: installGridImageArea.height/3
1406+ }
1407+ }
1408+ Row {
1409+ UbuntuShape {
1410+ radius: "medium"
1411+ color: "#E6E4E2"
1412+ width: installGridImageArea.width/3
1413+ height: installGridImageArea.height/3
1414+ }
1415+ UbuntuShape {
1416+ radius: "medium"
1417+ color: "#E6E4E2"
1418+ width: installGridImageArea.width/3
1419+ height: installGridImageArea.height/3
1420+ }
1421+ UbuntuShape {
1422+ radius: "medium"
1423+ color: "#E6E4E2"
1424+ width: installGridImageArea.width/3
1425+ height: installGridImageArea.height/3
1426+ }
1427+ }
1428+ }
1429+ }
1430+ }
1431+
1432+ Item {
1433+ id: installButtonArea
1434+
1435+ anchors.left: installGridImageArea.right
1436+ anchors.leftMargin: units.gu(4)
1437+
1438+ anchors.right: parent.right
1439+ anchors.top: parent.top
1440+ anchors.topMargin: units.gu(10)
1441+
1442+ anchors.bottom: parent.bottom
1443+
1444+ Column {
1445+
1446+ spacing: units.gu(4)
1447+ anchors.horizontalCenter: parent.horizontalCenter
1448+
1449+ Label {
1450+ id: installLabel
1451+ text: "Install "
1452+ + (webappCapableMetaInfo ? webappCapableMetaInfo.title : "")
1453+ + " to your apps"
1454+ anchors.horizontalCenter: parent.horizontalCenter
1455+ }
1456+ Button {
1457+ id: installButton
1458+ text: i18n.tr("Install")
1459+
1460+ enabled: !installingWebapp
1461+
1462+ anchors.topMargin: units.gu(1)
1463+ anchors.horizontalCenter: parent.horizontalCenter
1464+
1465+ onClicked: {
1466+ installingWebapp = true
1467+ installActivityIndicator.running = true
1468+
1469+ installWebapp(webappCapableMetaInfo)
1470+ }
1471+
1472+ ActivityIndicator {
1473+ id: installActivityIndicator
1474+ running: installingWebapp
1475+ visible: running
1476+ anchors.fill: parent
1477+ }
1478+ }
1479+ }
1480+ }
1481+ }
1482+ }
1483+ }
1484+}
1485
1486=== added file 'src/app/webbrowser/HomescreenWebappInstaller.qml'
1487--- src/app/webbrowser/HomescreenWebappInstaller.qml 1970-01-01 00:00:00 +0000
1488+++ src/app/webbrowser/HomescreenWebappInstaller.qml 2016-06-16 15:34:13 +0000
1489@@ -0,0 +1,157 @@
1490+/*
1491+ * Copyright 2015 Canonical Ltd.
1492+ *
1493+ * This file is part of webbrowser-app.
1494+ *
1495+ * webbrowser-app is free software; you can redistribute it and/or modify
1496+ * it under the terms of the GNU General Public License as published by
1497+ * the Free Software Foundation; version 3.
1498+ *
1499+ * webbrowser-app is distributed in the hope that it will be useful,
1500+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1501+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1502+ * GNU General Public License for more details.
1503+ *
1504+ * You should have received a copy of the GNU General Public License
1505+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1506+ */
1507+
1508+import QtQuick 2.0
1509+import webbrowserapp.private 0.1
1510+
1511+Item {
1512+ id: root
1513+
1514+ width: 100
1515+ height: 62
1516+
1517+ property var model: installedWebappsModel
1518+ property alias dbPath: installedWebappsModel.dbPath
1519+
1520+ HomescreenInstalledWebappModel {
1521+ id: installedWebappsModel
1522+ }
1523+
1524+ HomescreenWebapp {
1525+ id: homescreenWebapp
1526+ onInstalled: {
1527+ if (status && 0) {
1528+ installedWebappsModel.addInstalledWebapp(
1529+ "clickPackageName",
1530+ "domain",
1531+ "name",
1532+ "homepage",
1533+ (new Date().getTime()) / 1000)
1534+ }
1535+ root.installed(id, status, errorMessage)
1536+ }
1537+ }
1538+
1539+ QtObject {
1540+ id: internal
1541+ property int nextRequestId: 0
1542+ property var requestIds
1543+ }
1544+
1545+ signal installed(int id, bool status, string errorMessage)
1546+
1547+ function __areValidMetaInfos(metaInfos) {
1548+ return true
1549+ }
1550+ function __buildResourceRequests(requestId, metaInfos) {
1551+ if (metaInfos.type === 'apple') {
1552+ // we have everything besides the icons
1553+ return {
1554+ start: function(callback) {
1555+ callback(requestId, {
1556+ success: true,
1557+ errorMessage: "",
1558+ response: {
1559+ short_name: metaInfos.title,
1560+ name: metaInfos.title,
1561+ icons: metaInfos.icons,
1562+ start_url: metaInfos.baseurl,
1563+ display: "standalone"
1564+ }
1565+ })
1566+ }
1567+ }
1568+ } else if (metaInfos.type === 'manifest') {
1569+ return {
1570+ start: function(callback) {
1571+ __requestResource(
1572+ metaInfos.manifest,
1573+ function(result) {
1574+ if (result.success) {
1575+ try {
1576+ result.response =
1577+ JSON.parse(result.response)
1578+ } catch(e) { }
1579+ }
1580+ callback(requestId, result)
1581+ })
1582+ }
1583+ }
1584+ }
1585+ return null
1586+ }
1587+
1588+ // TODO add UA, cookies etc.?
1589+ function __requestResource(url, callback) {
1590+ var xmlrequest = new XMLHttpRequest();
1591+ xmlrequest.open("GET", url, true);
1592+ xmlrequest.onreadystatechange = function() {
1593+ if (xmlrequest.readyState === XMLHttpRequest.DONE) {
1594+ callback({
1595+ errorMessage: xmlrequest.statusText,
1596+ success: xmlrequest.status == 200,
1597+ // TODO: EVIL
1598+ response: eval(xmlrequest.responseText)
1599+ });
1600+ }
1601+ };
1602+ xmlrequest.send(null);
1603+ }
1604+
1605+ function __onResourceGathered(requestId, result) {
1606+ if (!result.success) {
1607+ installed(false, result.errorMessage)
1608+ return
1609+ }
1610+ homescreenWebapp.install(
1611+ requestId,
1612+ result.response.short_name,
1613+ result.response.name,
1614+ result.response.start_url,
1615+ result.response.icons)
1616+ }
1617+
1618+ function install(url, metaInfos) {
1619+ if (installedWebappsModel.hasWebappForUrlDomain(url)) {
1620+ // already installed
1621+ installed(true)
1622+ return
1623+ }
1624+
1625+ if ( ! internal.requestIds) {
1626+ internal.requestIds = {}
1627+ }
1628+
1629+ if ( ! __areValidMetaInfos(metaInfos)) {
1630+ installed(-1, false, i18n.tr("Invalid webapp meta information"))
1631+ return
1632+ }
1633+
1634+ var id = internal.nextRequestId
1635+ var requests = __buildResourceRequests(id, metaInfos)
1636+ if ( ! requests) {
1637+ return -1
1638+ }
1639+ requests.start(__onResourceGathered)
1640+ internal.nextRequestId++
1641+
1642+ return id
1643+ }
1644+
1645+ function cancel(id) { }
1646+}
1647
1648=== modified file 'src/app/webbrowser/NavigationBar.qml'
1649--- src/app/webbrowser/NavigationBar.qml 2016-05-17 17:23:42 +0000
1650+++ src/app/webbrowser/NavigationBar.qml 2016-06-16 15:34:13 +0000
1651@@ -44,6 +44,12 @@
1652 onFindInPageModeChanged: if (findInPageMode) addressbar.text = ""
1653 onIncognitoChanged: findInPageMode = false
1654
1655+ property bool installed: false
1656+ property alias webappCapableWebsite: addressbar.webappCapableWebsite
1657+ property alias webappCapableMetaInfo: favoriteView.webappCapableMetaInfo
1658+
1659+ signal installWebapp(var metainfos)
1660+
1661 function selectAll() {
1662 addressbar.selectAll()
1663 }
1664@@ -235,6 +241,41 @@
1665 }
1666 }
1667
1668+ onInstalledChanged: {
1669+ console.log('onInstalledChanged ' + installed)
1670+ favoriteView.installingWebapp = false
1671+ }
1672+
1673+ FavoriteOptionTabs {
1674+ id: favoriteView
1675+
1676+ visible: false
1677+
1678+ height: units.gu(35)
1679+
1680+ isWebappInstallable: webappCapableWebsite
1681+
1682+ bookmarkTitle: chrome.webview.title
1683+
1684+ anchors.left: parent.left
1685+ anchors.leftMargin: units.gu(1)
1686+
1687+ anchors.right: parent.right
1688+ anchors.rightMargin: units.gu(1)
1689+
1690+ anchors.top: parent.bottom
1691+ anchors.topMargin: units.gu(2)
1692+
1693+ onBookmarked: {
1694+ addressbar.bookmarked = !addressbar.bookmarked
1695+ favoriteView.visible = false
1696+ }
1697+ onInstallWebapp: {
1698+ console.log('meta infos: ' + JSON.stringify(metainfos))
1699+ chrome.installWebapp(metainfos)
1700+ }
1701+ }
1702+
1703 Component {
1704 id: drawerComponent
1705
1706
1707=== added file 'src/app/webbrowser/homescreen-installed-webapp-model.cpp'
1708--- src/app/webbrowser/homescreen-installed-webapp-model.cpp 1970-01-01 00:00:00 +0000
1709+++ src/app/webbrowser/homescreen-installed-webapp-model.cpp 2016-06-16 15:34:13 +0000
1710@@ -0,0 +1,342 @@
1711+/*
1712+ * Copyright 2015 Canonical Ltd.
1713+ *
1714+ * This file is part of webbrowser-app.
1715+ *
1716+ * webbrowser-app is free software; you can redistribute it and/or modify
1717+ * it under the terms of the GNU General Public License as published by
1718+ * the Free Software Foundation; version 3.
1719+ *
1720+ * webbrowser-app is distributed in the hope that it will be useful,
1721+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1722+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1723+ * GNU General Public License for more details.
1724+ *
1725+ * You should have received a copy of the GNU General Public License
1726+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1727+ */
1728+
1729+#include "homescreen-installed-webapp-model.h"
1730+
1731+#include <QSqlDatabase>
1732+#include <QSqlQuery>
1733+#include <QDateTime>
1734+
1735+
1736+namespace {
1737+
1738+const char *kConnectionName = "homescreen-webapps";
1739+const char *kDbName = "installed-homescreen-webapps.sqlite";
1740+
1741+}
1742+
1743+class HomescreenInstalledWebappModelPrivate
1744+{
1745+public:
1746+
1747+ struct HomescreenInstalledWebapp {
1748+ QString clickPackageName;
1749+ QString domain;
1750+ QString name;
1751+ QUrl homepage;
1752+ QDateTime installedTime;
1753+ };
1754+
1755+
1756+public:
1757+
1758+ HomescreenInstalledWebappModelPrivate();
1759+ ~HomescreenInstalledWebappModelPrivate();
1760+
1761+ void initDatabase();
1762+ void createDbSchemaIfNecessary();
1763+ void syncFromDatabase();
1764+
1765+ int count() const;
1766+ int findWithDomain(const QString& domain) const;
1767+ void removeWithDomain(const QString& domain);
1768+ void removeWithClickPackageName(const QString& clickPackageName);
1769+
1770+ const HomescreenInstalledWebapp at(int index) const;
1771+ void add(const QString& clickPackageName,
1772+ const QString& domain,
1773+ const QString& name,
1774+ const QUrl& homepage,
1775+ const QDateTime& installedTime);
1776+
1777+ QString _dbPath;
1778+ QSqlDatabase _db;
1779+ QList<HomescreenInstalledWebapp> _installedWebapps;
1780+};
1781+
1782+
1783+HomescreenInstalledWebappModelPrivate::HomescreenInstalledWebappModelPrivate()
1784+{
1785+ _db = QSqlDatabase::addDatabase("QSQLITE", kConnectionName);
1786+}
1787+
1788+HomescreenInstalledWebappModelPrivate::~HomescreenInstalledWebappModelPrivate()
1789+{
1790+ if (_db.isOpen() && _db.isValid()) {
1791+ _db.close();
1792+ }
1793+}
1794+
1795+void HomescreenInstalledWebappModelPrivate::initDatabase()
1796+{
1797+ if (_db.isOpen()) {
1798+ _db.close();
1799+ }
1800+
1801+ _db.setDatabaseName(kDbName);
1802+ if (!_db.open()) {
1803+ return;
1804+ }
1805+
1806+ createDbSchemaIfNecessary();
1807+ syncFromDatabase();
1808+}
1809+
1810+int HomescreenInstalledWebappModelPrivate::count() const
1811+{
1812+ return _installedWebapps.count();
1813+}
1814+
1815+const HomescreenInstalledWebappModelPrivate::HomescreenInstalledWebapp
1816+HomescreenInstalledWebappModelPrivate::at(int index) const
1817+{
1818+ if (_installedWebapps.count() < index && index >= 0) {
1819+ return _installedWebapps.at(index);
1820+ }
1821+ return HomescreenInstalledWebapp();
1822+}
1823+
1824+int HomescreenInstalledWebappModelPrivate::findWithDomain(
1825+ const QString& domain) const
1826+{
1827+ int index = 0;
1828+ Q_FOREACH(HomescreenInstalledWebapp webapp, _installedWebapps)
1829+ {
1830+ if (webapp.domain == domain) {
1831+ return index;
1832+ }
1833+ ++index;
1834+ }
1835+ return -1;
1836+}
1837+
1838+void HomescreenInstalledWebappModelPrivate::syncFromDatabase()
1839+{
1840+ _installedWebapps.clear();
1841+
1842+ QSqlQuery q(_db);
1843+
1844+ q.prepare(
1845+ QLatin1String(
1846+ "SELECT clickPackageName, domain, name, homepage, installedTime "
1847+ "FROM webapps;"));
1848+ q.exec();
1849+
1850+ int count = 0;
1851+ while (q.next()) {
1852+
1853+ HomescreenInstalledWebapp webapp;
1854+
1855+ webapp.clickPackageName = q.value(0).toString();
1856+ webapp.domain = q.value(1).toString();
1857+ webapp.name = q.value(2).toString();
1858+ webapp.homepage = q.value(3).toUrl();
1859+ webapp.installedTime = QDateTime::fromTime_t(q.value(4).toInt());
1860+
1861+// beginInsertRows(QModelIndex(), count, count);
1862+ _installedWebapps.append(webapp);
1863+// endInsertRows();
1864+
1865+ ++count;
1866+ }
1867+}
1868+
1869+void HomescreenInstalledWebappModelPrivate::add(
1870+ const QString& clickPackageName,
1871+ const QString& domain,
1872+ const QString& name,
1873+ const QUrl& homepage,
1874+ const QDateTime& installedTime)
1875+{
1876+ QSqlQuery q(_db);
1877+
1878+ q.prepare(
1879+ "INSERT INTO webapps ("
1880+ "clickPackageName, "
1881+ "domain, "
1882+ "name, "
1883+ "homepage, "
1884+ "installedTime) VALUES (?, ?, ?, ?, ?);");
1885+
1886+ q.addBindValue(clickPackageName);
1887+ q.addBindValue(domain);
1888+ q.addBindValue(name);
1889+ q.addBindValue(homepage.toString());
1890+ q.addBindValue(installedTime.toTime_t());
1891+
1892+ q.exec();
1893+}
1894+
1895+void HomescreenInstalledWebappModelPrivate::removeWithDomain(const QString& domain)
1896+{
1897+ QSqlQuery q(_db);
1898+
1899+ q.prepare("DELETE FROM webapps WHERE domain=?;");
1900+
1901+ q.addBindValue(domain);
1902+
1903+ q.exec();
1904+}
1905+
1906+void HomescreenInstalledWebappModelPrivate::removeWithClickPackageName(
1907+ const QString& clickPackageName)
1908+{
1909+ QSqlQuery q(_db);
1910+
1911+ q.prepare("DELETE FROM webapps WHERE clickPackageName=?;");
1912+
1913+ q.addBindValue(clickPackageName);
1914+
1915+ q.exec();
1916+}
1917+
1918+void HomescreenInstalledWebappModelPrivate::createDbSchemaIfNecessary()
1919+{
1920+ QSqlQuery q(_db);
1921+
1922+ q.prepare(
1923+ "CREATE TABLE IF NOT EXISTS webapps "
1924+ "(clickPackageName VARCHAR, "
1925+ "domain VARCHAR PRIMARY KEY, "
1926+ "name VARCHAR, "
1927+ "homepage VARCHAR, "
1928+ "installedTime DATETIME);");
1929+
1930+ q.exec();
1931+}
1932+
1933+
1934+
1935+HomescreenInstalledWebappModel::HomescreenInstalledWebappModel(QObject *parent) :
1936+ QAbstractListModel(parent),
1937+ d_ptr(new HomescreenInstalledWebappModelPrivate())
1938+{}
1939+
1940+HomescreenInstalledWebappModel::~HomescreenInstalledWebappModel()
1941+{
1942+ delete d_ptr;
1943+}
1944+
1945+void HomescreenInstalledWebappModel::addInstalledWebapp(
1946+ const QString& clickPackageName,
1947+ const QString& domain,
1948+ const QString& name,
1949+ const QUrl& homepage,
1950+ int installedTime)
1951+{
1952+ Q_D(HomescreenInstalledWebappModel);
1953+ if (d->findWithDomain(domain) != -1) {
1954+ return;
1955+ }
1956+ d->add(clickPackageName, domain, name, homepage, QDateTime::fromTime_t(installedTime));
1957+ // TODO overkill
1958+ d->syncFromDatabase();
1959+}
1960+
1961+bool HomescreenInstalledWebappModel::hasWebappForUrlDomain(
1962+ const QUrl& url) const
1963+{
1964+ Q_D(const HomescreenInstalledWebappModel);
1965+ return d->findWithDomain(url.host()) != -1;
1966+}
1967+
1968+void HomescreenInstalledWebappModel::removeForUrl(
1969+ const QUrl& url)
1970+{
1971+ Q_D(HomescreenInstalledWebappModel);
1972+ d->removeWithDomain(url.host());
1973+ // TODO overkill
1974+ d->syncFromDatabase();
1975+}
1976+
1977+void HomescreenInstalledWebappModel::removeClickName(
1978+ const QString& clickPackageNAme)
1979+{
1980+ Q_D(HomescreenInstalledWebappModel);
1981+ d->removeWithClickPackageName(clickPackageNAme);
1982+ // TODO overkill
1983+ d->syncFromDatabase();
1984+}
1985+
1986+QHash<int, QByteArray> HomescreenInstalledWebappModel::roleNames() const
1987+{
1988+ static QHash<int, QByteArray> roles;
1989+ if (roles.isEmpty()) {
1990+ roles[ClickPackageName] = "clickPackageName";
1991+ roles[Domain] = "domain";
1992+ roles[Name] = "name";
1993+ roles[Homepage] = "homepage";
1994+ roles[InstalledTime] = "installedTime";
1995+ }
1996+ return roles;
1997+}
1998+
1999+int HomescreenInstalledWebappModel::rowCount(const QModelIndex& ) const
2000+{
2001+ Q_D(const HomescreenInstalledWebappModel);
2002+ return d->count();
2003+}
2004+
2005+QVariant HomescreenInstalledWebappModel::data(const QModelIndex& index, int role) const
2006+{
2007+ Q_D(const HomescreenInstalledWebappModel);
2008+
2009+ if (!index.isValid()) {
2010+ return QVariant();
2011+ }
2012+
2013+ const HomescreenInstalledWebappModelPrivate::HomescreenInstalledWebapp& webapp =
2014+ d->at(index.row());
2015+
2016+ switch (role) {
2017+ case ClickPackageName:
2018+ return webapp.homepage;
2019+ case Domain:
2020+ return webapp.domain;
2021+ case Name:
2022+ return webapp.name;
2023+ case Homepage:
2024+ return webapp.homepage;
2025+ case InstalledTime:
2026+ return webapp.installedTime;
2027+ default:
2028+ break;
2029+ }
2030+
2031+ return QVariant();
2032+}
2033+
2034+const QString HomescreenInstalledWebappModel::dbPath() const
2035+{
2036+ Q_D(const HomescreenInstalledWebappModel);
2037+
2038+ return d->_dbPath;
2039+}
2040+
2041+void HomescreenInstalledWebappModel::setDbPath(const QString& path)
2042+{
2043+ Q_D(HomescreenInstalledWebappModel);
2044+
2045+ if (path == d->_dbPath)
2046+ {
2047+ return;
2048+ }
2049+
2050+ d->_dbPath = path;
2051+ Q_EMIT dbPathChanged();
2052+}
2053
2054=== added file 'src/app/webbrowser/homescreen-installed-webapp-model.h'
2055--- src/app/webbrowser/homescreen-installed-webapp-model.h 1970-01-01 00:00:00 +0000
2056+++ src/app/webbrowser/homescreen-installed-webapp-model.h 2016-06-16 15:34:13 +0000
2057@@ -0,0 +1,85 @@
2058+/*
2059+ * Copyright 2015 Canonical Ltd.
2060+ *
2061+ * This file is part of webbrowser-app.
2062+ *
2063+ * webbrowser-app is free software; you can redistribute it and/or modify
2064+ * it under the terms of the GNU General Public License as published by
2065+ * the Free Software Foundation; version 3.
2066+ *
2067+ * webbrowser-app is distributed in the hope that it will be useful,
2068+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2069+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2070+ * GNU General Public License for more details.
2071+ *
2072+ * You should have received a copy of the GNU General Public License
2073+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2074+ */
2075+
2076+#ifndef _HOMESCREEN_INSTALLED_WEBAPP_MODEL_H_
2077+#define _HOMESCREEN_INSTALLED_WEBAPP_MODEL_H_
2078+
2079+#include <QtCore/QAbstractListModel>
2080+#include <QUrl>
2081+#include <QString>
2082+#include <QStringList>
2083+#include <QScopedPointer>
2084+
2085+
2086+class HomescreenInstalledWebappModelPrivate;
2087+
2088+//TODO handle the case of the click being removed offline by the user
2089+
2090+class HomescreenInstalledWebappModel : public QAbstractListModel
2091+{
2092+ Q_OBJECT
2093+ Q_PROPERTY(QString dbPath READ dbPath WRITE setDbPath NOTIFY dbPathChanged)
2094+ Q_ENUMS(Roles)
2095+
2096+public:
2097+
2098+ enum Roles {
2099+ ClickPackageName = Qt::UserRole + 1,
2100+ Domain,
2101+ Name,
2102+ Homepage,
2103+ InstalledTime
2104+ };
2105+
2106+ explicit HomescreenInstalledWebappModel(QObject *parent = 0);
2107+ ~HomescreenInstalledWebappModel();
2108+
2109+ // QAbstractListModel
2110+ QHash<int, QByteArray> roleNames() const;
2111+ int rowCount(const QModelIndex& parent=QModelIndex()) const;
2112+ QVariant data(const QModelIndex& index, int role) const;
2113+
2114+ const QString dbPath() const;
2115+ void setDbPath(const QString& path);
2116+
2117+ Q_INVOKABLE void addInstalledWebapp(const QString& clickPackageName,
2118+ const QString& domain,
2119+ const QString& name,
2120+ const QUrl& homepage,
2121+ int installedTime);
2122+
2123+ Q_INVOKABLE bool hasWebappForUrlDomain(
2124+ const QUrl& url) const;
2125+
2126+ Q_INVOKABLE void removeForUrl(
2127+ const QUrl& url);
2128+
2129+ Q_INVOKABLE void removeClickName(
2130+ const QString& clickPackageNAme);
2131+
2132+
2133+Q_SIGNALS:
2134+ void dbPathChanged();
2135+
2136+
2137+private:
2138+ HomescreenInstalledWebappModelPrivate *d_ptr;
2139+ Q_DECLARE_PRIVATE(HomescreenInstalledWebappModel)
2140+};
2141+
2142+#endif // _HOMESCREEN_INSTALLED_WEBAPP_MODEL_H_
2143
2144=== added file 'src/app/webbrowser/homescreen-manifest-content.cpp'
2145--- src/app/webbrowser/homescreen-manifest-content.cpp 1970-01-01 00:00:00 +0000
2146+++ src/app/webbrowser/homescreen-manifest-content.cpp 2016-06-16 15:34:13 +0000
2147@@ -0,0 +1,180 @@
2148+/*
2149+ * Copyright 2015 Canonical Ltd.
2150+ *
2151+ * This file is part of webbrowser-app.
2152+ *
2153+ * webbrowser-app is free software; you can redistribute it and/or modify
2154+ * it under the terms of the GNU General Public License as published by
2155+ * the Free Software Foundation; version 3.
2156+ *
2157+ * webbrowser-app is distributed in the hope that it will be useful,
2158+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2159+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2160+ * GNU General Public License for more details.
2161+ *
2162+ * You should have received a copy of the GNU General Public License
2163+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2164+ */
2165+
2166+#include "homescreen-manifest-content.h"
2167+
2168+
2169+class WebappIconDescriptionPrivate
2170+{
2171+public:
2172+
2173+ WebappIconDescriptionPrivate(
2174+ const QString& src,
2175+ const QString& sizes,
2176+ const QString& type,
2177+ const QString& density)
2178+ : _src(src),
2179+ _sizes(sizes),
2180+ _type(type),
2181+ _density(density)
2182+ {}
2183+
2184+ QString _src;
2185+ QString _sizes;
2186+ QString _type;
2187+ QString _density;
2188+};
2189+
2190+WebappIconDescription::WebappIconDescription(
2191+ const QString& src,
2192+ const QString& sizes,
2193+ const QString& type,
2194+ const QString& density,
2195+ QObject* parent) :
2196+ QObject(parent),
2197+ d_ptr(new WebappIconDescriptionPrivate(
2198+ src, sizes, type, density))
2199+{}
2200+
2201+WebappIconDescription::~WebappIconDescription()
2202+{
2203+ delete d_ptr;
2204+}
2205+
2206+QString WebappIconDescription::src() const
2207+{
2208+ Q_D(const WebappIconDescription);
2209+ return d->_src;
2210+}
2211+
2212+QString WebappIconDescription::sizes() const
2213+{
2214+ Q_D(const WebappIconDescription);
2215+ return d->_sizes;
2216+}
2217+
2218+QString WebappIconDescription::type() const
2219+{
2220+ Q_D(const WebappIconDescription);
2221+ return d->_type;
2222+}
2223+
2224+QString WebappIconDescription::density() const
2225+{
2226+ Q_D(const WebappIconDescription);
2227+ return d->_density;
2228+}
2229+
2230+
2231+class HomescreenManifestContentPrivate
2232+{
2233+public:
2234+
2235+ HomescreenManifestContentPrivate(
2236+ const QString& name,
2237+ const QString& startUrl,
2238+ const QString& display,
2239+ HomescreenManifestContent::Orientation orientation,
2240+ const QList<WebappIconDescription*>& icons)
2241+ : _name(name),
2242+ _startUrl(startUrl),
2243+ _display(display),
2244+ _orientation(orientation),
2245+ _icons(icons)
2246+ {}
2247+
2248+ QString _name;
2249+ QString _startUrl;
2250+ QString _display;
2251+ HomescreenManifestContent::Orientation _orientation;
2252+ QList<WebappIconDescription*> _icons;
2253+};
2254+
2255+HomescreenManifestContent::HomescreenManifestContent(
2256+ const QString& name,
2257+ const QString& startUrl,
2258+ const QString& display,
2259+ Orientation orientation,
2260+ const QList<WebappIconDescription*>& icons,
2261+ QObject* parent) :
2262+ QObject(parent),
2263+ d_ptr(new HomescreenManifestContentPrivate(
2264+ name, startUrl, display, orientation, icons))
2265+{}
2266+
2267+HomescreenManifestContent::~HomescreenManifestContent()
2268+{
2269+ delete d_ptr;
2270+}
2271+
2272+QString HomescreenManifestContent::name() const
2273+{
2274+ Q_D(const HomescreenManifestContent);
2275+ return d->_name;
2276+}
2277+void HomescreenManifestContent::setName(const QString& name)
2278+{
2279+ Q_D(HomescreenManifestContent);
2280+ d->_name = name;
2281+}
2282+
2283+QString HomescreenManifestContent::startUrl() const
2284+{
2285+ Q_D(const HomescreenManifestContent);
2286+ return d->_startUrl;
2287+}
2288+void HomescreenManifestContent::setStartUrl(const QString& url)
2289+{
2290+ Q_D(HomescreenManifestContent);
2291+ d->_startUrl = url;
2292+}
2293+
2294+QString HomescreenManifestContent::display() const
2295+{
2296+ Q_D(const HomescreenManifestContent);
2297+ return d->_display;
2298+}
2299+void HomescreenManifestContent::setDisplay(const QString& display)
2300+{
2301+ Q_D(HomescreenManifestContent);
2302+ d->_display = display;
2303+}
2304+
2305+HomescreenManifestContent::Orientation HomescreenManifestContent::orientation() const
2306+{
2307+ Q_D(const HomescreenManifestContent);
2308+ return d->_orientation;
2309+}
2310+void HomescreenManifestContent::setOrientation(
2311+ HomescreenManifestContent::Orientation orientation)
2312+{
2313+ Q_D(HomescreenManifestContent);
2314+ d->_orientation = orientation;
2315+}
2316+
2317+QList<WebappIconDescription*> HomescreenManifestContent::icons() const
2318+{
2319+ Q_D(const HomescreenManifestContent);
2320+ return d->_icons;
2321+}
2322+void HomescreenManifestContent::setIcons(
2323+ const QList<WebappIconDescription*>& icons)
2324+{
2325+ Q_D(HomescreenManifestContent);
2326+ d->_icons = icons;
2327+}
2328
2329=== added file 'src/app/webbrowser/homescreen-manifest-content.h'
2330--- src/app/webbrowser/homescreen-manifest-content.h 1970-01-01 00:00:00 +0000
2331+++ src/app/webbrowser/homescreen-manifest-content.h 2016-06-16 15:34:13 +0000
2332@@ -0,0 +1,109 @@
2333+/*
2334+ * Copyright 2015 Canonical Ltd.
2335+ *
2336+ * This file is part of webbrowser-app.
2337+ *
2338+ * webbrowser-app is free software; you can redistribute it and/or modify
2339+ * it under the terms of the GNU General Public License as published by
2340+ * the Free Software Foundation; version 3.
2341+ *
2342+ * webbrowser-app is distributed in the hope that it will be useful,
2343+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2344+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2345+ * GNU General Public License for more details.
2346+ *
2347+ * You should have received a copy of the GNU General Public License
2348+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2349+ */
2350+
2351+#ifndef _HOMESCREEN_MANIFEST_CONTENT_H_
2352+#define _HOMESCREEN_MANIFEST_CONTENT_H_
2353+
2354+#include <QObject>
2355+#include <QString>
2356+#include <QList>
2357+#include <QScopedPointer>
2358+
2359+
2360+class WebappIconDescriptionPrivate;
2361+
2362+class WebappIconDescription: public QObject
2363+{
2364+public:
2365+ Q_OBJECT
2366+ Q_PROPERTY(QString src READ src)
2367+ Q_PROPERTY(QString sizes READ sizes)
2368+ Q_PROPERTY(QString type READ type)
2369+ Q_PROPERTY(QString density READ density)
2370+
2371+public:
2372+
2373+ WebappIconDescription(
2374+ const QString& src,
2375+ const QString& sizes,
2376+ const QString& type,
2377+ const QString& density,
2378+ QObject* parent = 0);
2379+ ~WebappIconDescription();
2380+
2381+ QString src() const;
2382+ QString sizes() const;
2383+ QString type() const;
2384+ QString density() const;
2385+
2386+private:
2387+ WebappIconDescriptionPrivate* d_ptr;
2388+ Q_DECLARE_PRIVATE(WebappIconDescription)
2389+};
2390+
2391+
2392+class HomescreenManifestContentPrivate;
2393+class HomescreenManifestContent : public QObject
2394+{
2395+ Q_OBJECT
2396+ Q_PROPERTY(QString name READ name)
2397+ Q_PROPERTY(QString startUrl READ startUrl)
2398+ Q_PROPERTY(QString display READ display)
2399+ Q_PROPERTY(Orientation orientation READ orientation)
2400+ Q_PROPERTY(QList<WebappIconDescription*> icons READ icons)
2401+ Q_ENUMS(Orientation)
2402+
2403+
2404+public:
2405+ enum Orientation
2406+ {
2407+ LANDSCAPE,
2408+ PORTRAIT
2409+ };
2410+
2411+ HomescreenManifestContent(
2412+ const QString& name,
2413+ const QString& startUrl,
2414+ const QString& display,
2415+ Orientation orientation,
2416+ const QList<WebappIconDescription*>& icons,
2417+ QObject* parent = 0);
2418+ HomescreenManifestContent(QObject* parent = 0);
2419+ ~HomescreenManifestContent();
2420+
2421+ QString name() const;
2422+ void setName(const QString&);
2423+
2424+ QString startUrl() const;
2425+ void setStartUrl(const QString&);
2426+
2427+ QString display() const;
2428+ void setDisplay(const QString&);
2429+
2430+ Orientation orientation() const;
2431+ void setOrientation(Orientation);
2432+
2433+ QList<WebappIconDescription*> icons() const;
2434+ void setIcons(const QList<WebappIconDescription*>&);
2435+
2436+private:
2437+ HomescreenManifestContentPrivate* d_ptr;
2438+ Q_DECLARE_PRIVATE(HomescreenManifestContent)
2439+};
2440+
2441+#endif // _HOMESCREEN_MANIFEST_CONTENT_H_
2442
2443=== added file 'src/app/webbrowser/homescreen-manifest-parser.cpp'
2444--- src/app/webbrowser/homescreen-manifest-parser.cpp 1970-01-01 00:00:00 +0000
2445+++ src/app/webbrowser/homescreen-manifest-parser.cpp 2016-06-16 15:34:13 +0000
2446@@ -0,0 +1,131 @@
2447+/*
2448+ * Copyright 2015 Canonical Ltd.
2449+ *
2450+ * This file is part of webbrowser-app.
2451+ *
2452+ * webbrowser-app is free software; you can redistribute it and/or modify
2453+ * it under the terms of the GNU General Public License as published by
2454+ * the Free Software Foundation; version 3.
2455+ *
2456+ * webbrowser-app is distributed in the hope that it will be useful,
2457+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2458+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2459+ * GNU General Public License for more details.
2460+ *
2461+ * You should have received a copy of the GNU General Public License
2462+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2463+ */
2464+
2465+#include "homescreen-manifest-parser.h"
2466+
2467+#include <QJsonDocument>
2468+#include <QJsonArray>
2469+#include <QJsonObject>
2470+#include <QJsonValue>
2471+
2472+#include "homescreen-manifest-content.h"
2473+
2474+
2475+namespace {
2476+
2477+const char WEBAPP_NAME_KEY[] = "name";
2478+const char WEBAPP_START_URL_KEY[] = "start_url";
2479+const char WEBAPP_DISPLAY_KEY[] = "display";
2480+const char WEBAPP_ORIENTATION_KEY[] = "orientation";
2481+
2482+// icons
2483+const char WEBAPP_ICONS_KEY[] = "icons";
2484+
2485+const char WEBAPP_ICON_SRC_KEY[] = "src";
2486+const char WEBAPP_ICON_SIZES_KEY[] = "sizes";
2487+const char WEBAPP_ICON_TYPE_KEY[] = "type";
2488+const char WEBAPP_ICON_DENSITY_KEY[] = "density";
2489+
2490+}
2491+
2492+
2493+HomescreenManifestParser::HomescreenManifestParser(
2494+ QObject *parent)
2495+ : QObject(parent)
2496+{}
2497+
2498+bool HomescreenManifestParser::parse(
2499+ const QString& content,
2500+ HomescreenManifestContent & out,
2501+ QString& errorMessage)
2502+{
2503+ if (content.isEmpty())
2504+ {
2505+ errorMessage = "Empty manifest content";
2506+ return false;
2507+ }
2508+
2509+ //
2510+ QJsonParseError error;
2511+ QJsonDocument doc = QJsonDocument::fromJson(content.toUtf8(), &error);
2512+ if (error.error != QJsonParseError::NoError)
2513+ {
2514+ errorMessage = "Could not parse manifest content";
2515+ return false;
2516+ }
2517+
2518+ if (doc.isNull() || !doc.isObject())
2519+ {
2520+ errorMessage = "Could not parse manifest content: invalid content";
2521+ return false;
2522+ }
2523+
2524+ QJsonObject root = doc.object();
2525+ if ( ! root.contains(WEBAPP_NAME_KEY))
2526+ {
2527+ errorMessage = "Invalid manifest content: no 'name' found";
2528+ return false;
2529+ }
2530+
2531+ QString name = root.value(WEBAPP_NAME_KEY).toString();
2532+
2533+ if ( ! root.contains(WEBAPP_ICONS_KEY))
2534+ {
2535+ errorMessage = "Invalid manifest content: no 'icons' found";
2536+ return false;
2537+ }
2538+
2539+ QList<WebappIconDescription*> iconDescriptions;
2540+
2541+ QJsonValue iconsValue = root.value(WEBAPP_ICONS_KEY);
2542+ if (iconsValue.isArray())
2543+ {
2544+ QJsonArray icons = iconsValue.toArray();
2545+ Q_FOREACH(QJsonValue iconValue, icons)
2546+ {
2547+ if (! iconValue.isObject())
2548+ {
2549+ continue;
2550+ }
2551+ QJsonObject icon = iconValue.toObject();
2552+ if (icon.contains(WEBAPP_ICON_SRC_KEY))
2553+ {
2554+ iconDescriptions.append(
2555+ new WebappIconDescription(
2556+ icon.value(WEBAPP_ICON_SRC_KEY).toString(),
2557+ icon.value(WEBAPP_ICON_SIZES_KEY).toString(),
2558+ icon.value(WEBAPP_ICON_TYPE_KEY).toString(),
2559+ icon.value(WEBAPP_ICON_DENSITY_KEY).toString()));
2560+ }
2561+ }
2562+ }
2563+
2564+ if (iconDescriptions.empty())
2565+ {
2566+ errorMessage = "Invalid manifest content: empty 'icons'";
2567+ return false;
2568+ }
2569+
2570+ out.setName(name);
2571+// out.setOrientation(root.value(WEBAPP_DISPLAY_KEY).toString());
2572+ out.setStartUrl(root.value(WEBAPP_START_URL_KEY).toString());
2573+ out.setIcons(iconDescriptions);
2574+ out.setDisplay(root.value(WEBAPP_DISPLAY_KEY).toString());
2575+
2576+ return true;
2577+}
2578
2579=== added file 'src/app/webbrowser/homescreen-manifest-parser.h'
2580--- src/app/webbrowser/homescreen-manifest-parser.h 1970-01-01 00:00:00 +0000
2581+++ src/app/webbrowser/homescreen-manifest-parser.h 2016-06-16 15:34:13 +0000
2582@@ -0,0 +1,41 @@
2583+/*
2584+ * Copyright 2015 Canonical Ltd.
2585+ *
2586+ * This file is part of webbrowser-app.
2587+ *
2588+ * webbrowser-app is free software; you can redistribute it and/or modify
2589+ * it under the terms of the GNU General Public License as published by
2590+ * the Free Software Foundation; version 3.
2591+ *
2592+ * webbrowser-app is distributed in the hope that it will be useful,
2593+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2594+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2595+ * GNU General Public License for more details.
2596+ *
2597+ * You should have received a copy of the GNU General Public License
2598+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2599+ */
2600+
2601+#ifndef _HOMESCREEN_MANIFEST_PARSER_H_
2602+#define _HOMESCREEN_MANIFEST_PARSER_H_
2603+
2604+#include <QObject>
2605+#include <QUrl>
2606+#include <QString>
2607+
2608+
2609+class HomescreenManifestContent;
2610+
2611+class HomescreenManifestParser : public QObject
2612+{
2613+ Q_OBJECT
2614+
2615+public:
2616+ explicit HomescreenManifestParser(QObject *parent = 0);
2617+
2618+ bool parse(const QString& content,
2619+ HomescreenManifestContent & out,
2620+ QString& errorMessage);
2621+};
2622+
2623+#endif // _HOMESCREEN_MANIFEST_PARSER_H_
2624
2625=== added file 'src/app/webbrowser/homescreen-manifest-request.cpp'
2626--- src/app/webbrowser/homescreen-manifest-request.cpp 1970-01-01 00:00:00 +0000
2627+++ src/app/webbrowser/homescreen-manifest-request.cpp 2016-06-16 15:34:13 +0000
2628@@ -0,0 +1,176 @@
2629+/*
2630+ * Copyright 2015 Canonical Ltd.
2631+ *
2632+ * This file is part of webbrowser-app.
2633+ *
2634+ * webbrowser-app is free software; you can redistribute it and/or modify
2635+ * it under the terms of the GNU General Public License as published by
2636+ * the Free Software Foundation; version 3.
2637+ *
2638+ * webbrowser-app is distributed in the hope that it will be useful,
2639+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2640+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2641+ * GNU General Public License for more details.
2642+ *
2643+ * You should have received a copy of the GNU General Public License
2644+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2645+ */
2646+
2647+#include "homescreen-manifest-request.h"
2648+
2649+#include <QNetworkAccessManager>
2650+#include <QNetworkReply>
2651+#include <QNetworkRequest>
2652+
2653+#include "homescreen-manifest-content.h"
2654+
2655+
2656+class HomescreenManifestRequestPrivate : public QObject
2657+{
2658+ Q_OBJECT
2659+
2660+public:
2661+ HomescreenManifestRequestPrivate(QObject *parent = 0);
2662+
2663+ void parse();
2664+
2665+
2666+Q_SIGNALS:
2667+ void parsed(HomescreenManifestContent *,
2668+ const QString& errorMEssage);
2669+
2670+private Q_SLOTS:
2671+ void requestFinished();
2672+
2673+private:
2674+ friend class HomescreenManifestRequest;
2675+
2676+ QString _manifestFilename;
2677+ QUrl _baseUrl;
2678+ QUrl _referrer;
2679+ QString _userAgent;
2680+};
2681+
2682+HomescreenManifestRequestPrivate::HomescreenManifestRequestPrivate(
2683+ QObject *parent)
2684+ : QObject(parent)
2685+{}
2686+
2687+void HomescreenManifestRequestPrivate::parse()
2688+{
2689+ if (_manifestFilename.isEmpty()
2690+ || _baseUrl.isEmpty())
2691+ {
2692+ Q_EMIT parsed(NULL, "No manifest to retrieve and parse");
2693+ return;
2694+ }
2695+ QNetworkRequest request(_baseUrl);
2696+ if (!_userAgent.isEmpty())
2697+ {
2698+ request.setRawHeader("User-Agent", _userAgent.toUtf8());
2699+ }
2700+ if (!_referrer.isEmpty())
2701+ {
2702+ request.setRawHeader("Referer", _referrer.toUtf8());
2703+ }
2704+
2705+ QNetworkAccessManager manager;
2706+
2707+ QNetworkReply * reply = manager.get(request);
2708+
2709+ QObject::connect(
2710+ reply, SIGNAL(finished()),
2711+ this, SLOT(requestFinished()));
2712+
2713+}
2714+
2715+void HomescreenManifestRequestPrivate::requestFinished()
2716+{
2717+ QNetworkReply *reply = qobject_cast(sender());
2718+ if (Q_UNLIKELY(! reply))
2719+ {
2720+ Q_EMIT parsed(NULL, "Invalid reply");
2721+ return;
2722+ }
2723+
2724+ QString manifestContent =
2725+ QString::fromUtf8(reply->readAll());
2726+}
2727+
2728+
2729+HomescreenManifestRequest::HomescreenManifestRequest(
2730+ QObject *parent) :
2731+ QObject(parent),
2732+ d_ptr(new HomescreenManifestRequestPrivate())
2733+{}
2734+
2735+HomescreenManifestRequest::HomescreenManifestRequest(
2736+ QObject *parent)
2737+ : QObject(parent)
2738+{}
2739+
2740+QString HomescreenManifestRequest::manifestFilename() const
2741+{
2742+ Q_D(const HomescreenManifestRequest);
2743+
2744+ return d->_manifestFilename;
2745+}
2746+
2747+void HomescreenManifestRequest::setManifestFilename(
2748+ const QString& manifest)
2749+{
2750+ Q_D(HomescreenManifestRequest);
2751+
2752+ if (d->_manifestFilename == manifest)
2753+ {
2754+ return;
2755+ }
2756+ d->_manifestFilename = manifest;
2757+ Q_EMIT manifestFilenameChanged();
2758+}
2759+
2760+QUrl HomescreenManifestRequest::baseUrl() const
2761+{
2762+ Q_D(HomescreenManifestRequest);
2763+
2764+}
2765+
2766+void HomescreenManifestRequest::setBaseUrl(
2767+ const QUrl& url)
2768+{
2769+ Q_D(HomescreenManifestRequest);
2770+
2771+ if (d->_baseUrl == url)
2772+ {
2773+ return;
2774+ }
2775+ d->_baseUrl = url;
2776+ Q_EMIT baseUrlChanged();
2777+}
2778+
2779+QUrl HomescreenManifestRequest::referrer() const
2780+{
2781+}
2782+
2783+void HomescreenManifestRequest::setReferrer(const QUrl& referrer)
2784+{
2785+
2786+}
2787+
2788+QString HomescreenManifestRequest::userAgent() const
2789+{
2790+
2791+}
2792+
2793+void HomescreenManifestRequest::setUserAgent(const QString& userAgent)
2794+{
2795+
2796+}
2797+
2798+void HomescreenManifestRequest::parse()
2799+{
2800+ HomescreenManifestContent content;
2801+
2802+ Q_NETWORK_EXPORT
2803+ Q_EMIT parsed(&content);
2804+}
2805
2806=== added file 'src/app/webbrowser/homescreen-manifest-request.h'
2807--- src/app/webbrowser/homescreen-manifest-request.h 1970-01-01 00:00:00 +0000
2808+++ src/app/webbrowser/homescreen-manifest-request.h 2016-06-16 15:34:13 +0000
2809@@ -0,0 +1,60 @@
2810+/*
2811+ * Copyright 2015 Canonical Ltd.
2812+ *
2813+ * This file is part of webbrowser-app.
2814+ *
2815+ * webbrowser-app is free software; you can redistribute it and/or modify
2816+ * it under the terms of the GNU General Public License as published by
2817+ * the Free Software Foundation; version 3.
2818+ *
2819+ * webbrowser-app is distributed in the hope that it will be useful,
2820+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2821+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2822+ * GNU General Public License for more details.
2823+ *
2824+ * You should have received a copy of the GNU General Public License
2825+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2826+ */
2827+
2828+#ifndef _HOMESCREEN_MANIFEST_PARSER_H_
2829+#define _HOMESCREEN_MANIFEST_PARSER_H_
2830+
2831+#include <QObject>
2832+#include <QUrl>
2833+#include <QString>
2834+#include <QScopedPointer>
2835+
2836+
2837+class QNetworkReply;
2838+
2839+class HomescreenManifestRequestPrivate;
2840+
2841+class HomescreenManifestRequest : public QObject
2842+{
2843+ Q_OBJECT
2844+
2845+public:
2846+ explicit HomescreenManifestRequest(QObject *parent = 0);
2847+
2848+ enum ResourceType
2849+ {
2850+ MAIN_MANIFEST_TYPE,
2851+ ICN_RESOURCE
2852+ };
2853+
2854+ void get(const QUrl& manifestUrl);
2855+ void getIcons(const QUrl& manifestUrl);
2856+
2857+
2858+Q_SIGNALS:
2859+ void failure(const QString& resourceName, ResourceType type);
2860+ void finished(const QString& content);
2861+
2862+
2863+private:
2864+
2865+ QScopedPointer<HomescreenManifestRequestPrivate>* d_ptr;
2866+ Q_DECLARE_PRIVATE(HomescreenManifestParser)
2867+};
2868+
2869+#endif // _HOMESCREEN_MANIFEST_PARSER_H_
2870
2871=== added file 'src/app/webbrowser/homescreen-webapp-model.cpp'
2872--- src/app/webbrowser/homescreen-webapp-model.cpp 1970-01-01 00:00:00 +0000
2873+++ src/app/webbrowser/homescreen-webapp-model.cpp 2016-06-16 15:34:13 +0000
2874@@ -0,0 +1,350 @@
2875+/*
2876+ * Copyright 2015 Canonical Ltd.
2877+ *
2878+ * This file is part of webbrowser-app.
2879+ *
2880+ * webbrowser-app is free software; you can redistribute it and/or modify
2881+ * it under the terms of the GNU General Public License as published by
2882+ * the Free Software Foundation; version 3.
2883+ *
2884+ * webbrowser-app is distributed in the hope that it will be useful,
2885+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2886+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2887+ * GNU General Public License for more details.
2888+ *
2889+ * You should have received a copy of the GNU General Public License
2890+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2891+ */
2892+
2893+#include "homescreen-webapp-model.h"
2894+
2895+#include <QtSql/QSqlQuery>
2896+
2897+
2898+HomescreenWebappModel::HomescreenWebappModel(QObject* parent)
2899+ : QAbstractListModel(parent)
2900+{
2901+ m_database = QSqlDatabase::addDatabase(
2902+ QLatin1String("QSQLITE"), "homescreen");
2903+}
2904+
2905+HomescreenWebappModel::~HomescreenWebappModel()
2906+{
2907+ m_database.close();
2908+ m_database = QSqlDatabase();
2909+ QSqlDatabase::removeDatabase(CONNECTION_NAME);
2910+}
2911+
2912+QString HomescreenWebappModel::dbPath() const
2913+{
2914+ return m_dbPath;
2915+}
2916+
2917+void HomescreenWebappModel::setDbPath(const QString& path)
2918+{
2919+ if (m_dbPath == path) {
2920+ return;
2921+ }
2922+ m_dbPath = path;
2923+ Q_EMIT dbPathChanged();
2924+}
2925+
2926+bool HomescreenWebappModel::contains(const QUrl& url)
2927+{
2928+ return false;
2929+}
2930+
2931+void HomescreenWebappModel::add(const QUrl& url)
2932+{
2933+}
2934+
2935+void HomescreenWebappModel::createOrAlterDatabaseSchema()
2936+{
2937+ QSqlQuery createQuery(m_database);
2938+ QString query = QLatin1String("CREATE TABLE IF NOT EXISTS history "
2939+ "(url VARCHAR, domain VARCHAR, title VARCHAR,"
2940+ " icon VARCHAR, visits INTEGER, lastVisit DATETIME);");
2941+ createQuery.prepare(query);
2942+ createQuery.exec();
2943+}
2944+
2945+void HomescreenWebappModel::populateFromDatabase()
2946+{
2947+ QSqlQuery populateQuery(m_database);
2948+ QString query = QLatin1String("SELECT url, domain, title, icon, visits, lastVisit "
2949+ "FROM history ORDER BY lastVisit DESC;");
2950+ populateQuery.prepare(query);
2951+ populateQuery.exec();
2952+
2953+ int count = 0;
2954+ while (populateQuery.next()) {
2955+ HistoryEntry entry;
2956+ entry.url = populateQuery.value(0).toUrl();
2957+ entry.domain = populateQuery.value(1).toString();
2958+ if (entry.domain.isEmpty()) {
2959+ entry.domain = DomainUtils::extractTopLevelDomainName(entry.url);
2960+ }
2961+ entry.title = populateQuery.value(2).toString();
2962+ entry.icon = populateQuery.value(3).toUrl();
2963+ entry.visits = populateQuery.value(4).toInt();
2964+ entry.lastVisit = QDateTime::fromTime_t(populateQuery.value(5).toInt());
2965+ beginInsertRows(QModelIndex(), count, count);
2966+ m_entries.append(entry);
2967+ endInsertRows();
2968+ ++count;
2969+ }
2970+}
2971+
2972+QHash<int, QByteArray> HomescreenWebappModel::roleNames() const
2973+{
2974+ static QHash<int, QByteArray> roles;
2975+ if (roles.isEmpty()) {
2976+ roles[Url] = "url";
2977+ roles[Domain] = "domain";
2978+ roles[Title] = "title";
2979+ roles[Icon] = "icon";
2980+ roles[Visits] = "visits";
2981+ roles[LastVisit] = "lastVisit";
2982+ roles[LastVisitDate] = "lastVisitDate";
2983+ }
2984+ return roles;
2985+}
2986+
2987+int HomescreenWebappModel::rowCount(const QModelIndex& parent) const
2988+{
2989+ Q_UNUSED(parent);
2990+ return m_entries.count();
2991+}
2992+
2993+QVariant HomescreenWebappModel::data(const QModelIndex& index, int role) const
2994+{
2995+ if (!index.isValid()) {
2996+ return QVariant();
2997+ }
2998+ const HistoryEntry& entry = m_entries.at(index.row());
2999+ switch (role) {
3000+ case Url:
3001+ return entry.url;
3002+ case Domain:
3003+ return entry.domain;
3004+ case Title:
3005+ return entry.title;
3006+ case Icon:
3007+ return entry.icon;
3008+ case Visits:
3009+ return entry.visits;
3010+ case LastVisit:
3011+ return entry.lastVisit;
3012+ case LastVisitDate:
3013+ return entry.lastVisit.toLocalTime().date();
3014+ default:
3015+ return QVariant();
3016+ }
3017+}
3018+
3019+const QString HomescreenWebappModel::databasePath() const
3020+{
3021+ return m_database.databaseName();
3022+}
3023+
3024+void HomescreenWebappModel::setDatabasePath(const QString& path)
3025+{
3026+ if (path != databasePath()) {
3027+ if (path.isEmpty()) {
3028+ resetDatabase(":memory:");
3029+ } else {
3030+ resetDatabase(path);
3031+ }
3032+ Q_EMIT databasePathChanged();
3033+ }
3034+}
3035+
3036+int HomescreenWebappModel::getEntryIndex(const QUrl& url) const
3037+{
3038+ for (int i = 0; i < m_entries.count(); ++i) {
3039+ if (m_entries.at(i).url == url) {
3040+ return i;
3041+ }
3042+ }
3043+ return -1;
3044+}
3045+
3046+/*!
3047+ Add an entry to the model.
3048+
3049+ If an entry with the same URL already exists, it is updated.
3050+ Otherwise a new entry is created and added to the model.
3051+
3052+ Return the total number of visits for the URL.
3053+*/
3054+int HomescreenWebappModel::add(const QUrl& url, const QString& title, const QUrl& icon)
3055+{
3056+ if (url.isEmpty()) {
3057+ return 0;
3058+ }
3059+ int count = 1;
3060+ QDateTime now = QDateTime::currentDateTimeUtc();
3061+ int index = getEntryIndex(url);
3062+ if (index == -1) {
3063+ HistoryEntry entry;
3064+ entry.url = url;
3065+ entry.domain = DomainUtils::extractTopLevelDomainName(url);
3066+ entry.title = title;
3067+ entry.icon = icon;
3068+ entry.visits = 1;
3069+ entry.lastVisit = now;
3070+ beginInsertRows(QModelIndex(), 0, 0);
3071+ m_entries.prepend(entry);
3072+ endInsertRows();
3073+ insertNewEntryInDatabase(entry);
3074+ } else {
3075+ QVector<int> roles;
3076+ roles << Visits;
3077+ if (index == 0) {
3078+ HistoryEntry& entry = m_entries.first();
3079+ if (title != entry.title) {
3080+ entry.title = title;
3081+ roles << Title;
3082+ }
3083+ if (icon != entry.icon) {
3084+ entry.icon = icon;
3085+ roles << Icon;
3086+ }
3087+ count = ++entry.visits;
3088+ if (now != entry.lastVisit) {
3089+ entry.lastVisit = now;
3090+ roles << LastVisit;
3091+ }
3092+ } else {
3093+ beginMoveRows(QModelIndex(), index, index, QModelIndex(), 0);
3094+ HistoryEntry entry = m_entries.takeAt(index);
3095+ if (title != entry.title) {
3096+ entry.title = title;
3097+ roles << Title;
3098+ }
3099+ if (icon != entry.icon) {
3100+ entry.icon = icon;
3101+ roles << Icon;
3102+ }
3103+ count = ++entry.visits;
3104+ if (now != entry.lastVisit) {
3105+ if (now.date() != entry.lastVisit.date()) {
3106+ roles << LastVisitDate;
3107+ }
3108+ entry.lastVisit = now;
3109+ roles << LastVisit;
3110+ }
3111+ m_entries.prepend(entry);
3112+ endMoveRows();
3113+ }
3114+ Q_EMIT dataChanged(this->index(0, 0), this->index(0, 0), roles);
3115+ updateExistingEntryInDatabase(m_entries.first());
3116+ }
3117+ return count;
3118+}
3119+
3120+/*!
3121+ Remove a given URL from the history model.
3122+
3123+ If the URL was not previously visited, do nothing.
3124+*/
3125+void HomescreenWebappModel::removeEntryByUrl(const QUrl& url)
3126+{
3127+ if (url.isEmpty()) {
3128+ return;
3129+ }
3130+
3131+ removeByIndex(getEntryIndex(url));
3132+ removeEntryFromDatabaseByUrl(url);
3133+}
3134+
3135+/*!
3136+ Remove all urls from a given DOMAIN from the history model.
3137+*/
3138+void HomescreenWebappModel::removeEntriesByDomain(const QString& domain)
3139+{
3140+ if (domain.isEmpty()) {
3141+ return;
3142+ }
3143+
3144+ for (int i = m_entries.count() - 1; i >= 0; --i) {
3145+ if (m_entries.at(i).domain == domain) {
3146+ removeByIndex(i);
3147+ }
3148+ }
3149+ removeEntriesFromDatabaseByDomain(domain);
3150+}
3151+
3152+void HomescreenWebappModel::removeByIndex(int index)
3153+{
3154+ if (index >= 0) {
3155+ beginRemoveRows(QModelIndex(), index, index);
3156+ m_entries.removeAt(index);
3157+ endRemoveRows();
3158+ }
3159+}
3160+
3161+void HomescreenWebappModel::insertNewEntryInDatabase(const HistoryEntry& entry)
3162+{
3163+ QSqlQuery query(m_database);
3164+ static QString insertStatement = QLatin1String("INSERT INTO history (url, domain, title, icon, "
3165+ "visits, lastVisit) VALUES (?, ?, ?, ?, 1, ?);");
3166+ query.prepare(insertStatement);
3167+ query.addBindValue(entry.url.toString());
3168+ query.addBindValue(entry.domain);
3169+ query.addBindValue(entry.title);
3170+ query.addBindValue(entry.icon.toString());
3171+ query.addBindValue(entry.lastVisit.toTime_t());
3172+ query.exec();
3173+}
3174+
3175+void HomescreenWebappModel::updateExistingEntryInDatabase(const HistoryEntry& entry)
3176+{
3177+ QSqlQuery query(m_database);
3178+ static QString updateStatement = QLatin1String("UPDATE history SET domain=?, title=?, icon=?, "
3179+ "visits=?, lastVisit=? WHERE url=?;");
3180+ query.prepare(updateStatement);
3181+ query.addBindValue(entry.domain);
3182+ query.addBindValue(entry.title);
3183+ query.addBindValue(entry.icon.toString());
3184+ query.addBindValue(entry.visits);
3185+ query.addBindValue(entry.lastVisit.toTime_t());
3186+ query.addBindValue(entry.url.toString());
3187+ query.exec();
3188+}
3189+
3190+void HomescreenWebappModel::removeEntryFromDatabaseByUrl(const QUrl& url)
3191+{
3192+ QSqlQuery query(m_database);
3193+ static QString deleteStatement = QLatin1String("DELETE FROM history WHERE url=?;");
3194+ query.prepare(deleteStatement);
3195+ query.addBindValue(url.toString());
3196+ query.exec();
3197+}
3198+
3199+void HomescreenWebappModel::removeEntriesFromDatabaseByDomain(const QString& domain)
3200+{
3201+ QSqlQuery query(m_database);
3202+ static QString deleteStatement = QLatin1String("DELETE FROM history WHERE domain=?;");
3203+ query.prepare(deleteStatement);
3204+ query.addBindValue(domain);
3205+ query.exec();
3206+}
3207+
3208+void HomescreenWebappModel::clearAll()
3209+{
3210+ if (!m_entries.isEmpty()) {
3211+ beginResetModel();
3212+ m_entries.clear();
3213+ endResetModel();
3214+ clearDatabase();
3215+ }
3216+}
3217+
3218+void HomescreenWebappModel::clearDatabase()
3219+{
3220+ QSqlQuery query(m_database);
3221+ QString deleteStatement = QLatin1String("DELETE FROM history;");
3222+ query.prepare(deleteStatement);
3223+ query.exec();
3224+}
3225
3226=== added file 'src/app/webbrowser/homescreen-webapp-model.h'
3227--- src/app/webbrowser/homescreen-webapp-model.h 1970-01-01 00:00:00 +0000
3228+++ src/app/webbrowser/homescreen-webapp-model.h 2016-06-16 15:34:13 +0000
3229@@ -0,0 +1,54 @@
3230+/*
3231+ * Copyright 2015 Canonical Ltd.
3232+ *
3233+ * This file is part of webbrowser-app.
3234+ *
3235+ * webbrowser-app is free software; you can redistribute it and/or modify
3236+ * it under the terms of the GNU General Public License as published by
3237+ * the Free Software Foundation; version 3.
3238+ *
3239+ * webbrowser-app is distributed in the hope that it will be useful,
3240+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3241+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3242+ * GNU General Public License for more details.
3243+ *
3244+ * You should have received a copy of the GNU General Public License
3245+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3246+ */
3247+
3248+#ifndef __HOMESCREEN_WEBAPP_MODEL_H__
3249+#define __HOMESCREEN_WEBAPP_MODEL_H__
3250+
3251+#include <QtCore/QAbstractListModel>
3252+#include <QtSql/QSqlDatabase>
3253+
3254+
3255+class HomescreenWebappModel : public QAbstractListModel
3256+{
3257+ Q_OBJECT
3258+ Q_PROPERTY(QString dbPath READ dbPath WRITE setDbPath NOTIFY dbPathChanged)
3259+
3260+public:
3261+ explicit HomescreenWebappModel(QObject *parent = 0);
3262+ ~HomescreenWebappModel();
3263+
3264+ // QAbstractListModel
3265+ QHash<int, QByteArray> roleNames() const;
3266+ int rowCount(const QModelIndex& parent=QModelIndex()) const;
3267+ QVariant data(const QModelIndex& index, int role) const;
3268+
3269+ QString dbPath() const;
3270+ void setDbPath(const QString& path);
3271+
3272+ Q_INVOKABLE bool contains(const QUrl& url);
3273+ Q_INVOKABLE void add(const QUrl& url);
3274+
3275+Q_SIGNALS:
3276+ void dbPathChanged() const;
3277+
3278+private:
3279+ QSqlDatabase m_database;
3280+ QString m_dbPath;
3281+};
3282+
3283+#endif // __HOMESCREEN_WEBAPP_MODEL_H__
3284
3285=== added file 'src/app/webbrowser/homescreen-webapp.cpp'
3286--- src/app/webbrowser/homescreen-webapp.cpp 1970-01-01 00:00:00 +0000
3287+++ src/app/webbrowser/homescreen-webapp.cpp 2016-06-16 15:34:13 +0000
3288@@ -0,0 +1,236 @@
3289+/*
3290+ * Copyright 2015 Canonical Ltd.
3291+ *
3292+ * This file is part of webbrowser-app.
3293+ *
3294+ * webbrowser-app is free software; you can redistribute it and/or modify
3295+ * it under the terms of the GNU General Public License as published by
3296+ * the Free Software Foundation; version 3.
3297+ *
3298+ * webbrowser-app is distributed in the hope that it will be useful,
3299+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3300+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3301+ * GNU General Public License for more details.
3302+ *
3303+ * You should have received a copy of the GNU General Public License
3304+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3305+ */
3306+
3307+#include "homescreen-webapp.h"
3308+
3309+#include <QtConcurrent/QtConcurrent>
3310+#include <QProcess>
3311+
3312+#include "homescreen-manifest-content.h"
3313+
3314+
3315+class HomescreenWebappPrivate : public QObject
3316+{
3317+ Q_OBJECT
3318+
3319+public:
3320+ HomescreenWebappPrivate(QObject *parent = 0);
3321+
3322+ void install(int requestId,
3323+ const QString& short_name,
3324+ const QString& name,
3325+ const QUrl& homepage,
3326+ const QStringList& icon_uris);
3327+
3328+Q_SIGNALS:
3329+ void installed(int requestId, bool status, const QString& errorMEssage);
3330+
3331+private Q_SLOTS:
3332+ void requestFinished();
3333+
3334+private:
3335+ friend class HomescreenWebapp;
3336+
3337+ int doInstall(int requestId,
3338+ const QString& short_name,
3339+ const QString& name,
3340+ const QUrl& homepage,
3341+ const QStringList& icon_uris);
3342+
3343+ QString _manifestFilename;
3344+ QUrl _baseUrl;
3345+ QUrl _referrer;
3346+ QString _userAgent;
3347+ QHash<int, QFutureWatcher<int>* > _requests;
3348+};
3349+
3350+
3351+HomescreenWebappPrivate::HomescreenWebappPrivate(
3352+ QObject *parent)
3353+ : QObject(parent)
3354+{}
3355+
3356+int HomescreenWebappPrivate::doInstall(
3357+ int requestId,
3358+ const QString& short_name,
3359+ const QString& ,
3360+ const QUrl& homepage,
3361+ const QStringList& )
3362+{
3363+ QProcess installerProcess;
3364+ qDebug() << "/usr/bin/homescreen-webapp-installer";
3365+ installerProcess.start(
3366+ "/usr/bin/homescreen-webapp-installer.py",
3367+ QStringList()
3368+ << QString("--name=%1").arg(short_name)
3369+ << QString("--homepage=%1").arg(homepage.toString())
3370+ << QString("--domain=%1").arg(homepage.host()));
3371+
3372+ installerProcess.waitForFinished();
3373+
3374+ return requestId;
3375+ // installerProcess.error()
3376+}
3377+
3378+void HomescreenWebappPrivate::install(int requestId,
3379+ const QString& short_name,
3380+ const QString& name,
3381+ const QUrl& homepage,
3382+ const QStringList& icon_uris)
3383+{
3384+ // https://developer.mozilla.org/en-US/Apps/Build/Manifest
3385+ // Internal paths also must be served from the same origin as the app
3386+
3387+ QFuture<int> future =
3388+ QtConcurrent::run(
3389+ this,
3390+ &HomescreenWebappPrivate::doInstall,
3391+ requestId, short_name, name, homepage, icon_uris);
3392+
3393+ QFutureWatcher<int>* watcher = new QFutureWatcher<int>();
3394+ connect(watcher,
3395+ SIGNAL(finished()),
3396+ this,
3397+ SLOT(requestFinished()));
3398+
3399+ _requests.insert(requestId, watcher);
3400+
3401+ watcher->setFuture(future);
3402+
3403+ future.begin();
3404+}
3405+
3406+void HomescreenWebappPrivate::requestFinished()
3407+{
3408+ QFutureWatcher<int> * watcher =
3409+ static_cast< QFutureWatcher<int>* >(sender());
3410+ if (watcher) {
3411+ QFuture<int> future = watcher->future();
3412+ Q_EMIT installed(future.result(), true, "");
3413+ }
3414+ // TODO no id?
3415+}
3416+
3417+
3418+HomescreenWebapp::HomescreenWebapp(
3419+ QObject *parent) :
3420+ QObject(parent),
3421+ d_ptr(new HomescreenWebappPrivate())
3422+{}
3423+
3424+HomescreenWebapp::~HomescreenWebapp()
3425+{
3426+ delete d_ptr;
3427+}
3428+
3429+QString HomescreenWebapp::manifestFilename() const
3430+{
3431+ Q_D(const HomescreenWebapp);
3432+
3433+ return d->_manifestFilename;
3434+}
3435+
3436+void HomescreenWebapp::setManifestFilename(
3437+ const QString& manifest)
3438+{
3439+ Q_D(HomescreenWebapp);
3440+
3441+ if (d->_manifestFilename == manifest)
3442+ {
3443+ return;
3444+ }
3445+ d->_manifestFilename = manifest;
3446+ Q_EMIT manifestFilenameChanged();
3447+}
3448+
3449+QUrl HomescreenWebapp::baseUrl() const
3450+{
3451+ Q_D(const HomescreenWebapp);
3452+ return d->_baseUrl;
3453+}
3454+
3455+void HomescreenWebapp::setBaseUrl(
3456+ const QUrl& url)
3457+{
3458+ Q_D(HomescreenWebapp);
3459+
3460+ if (d->_baseUrl == url)
3461+ {
3462+ return;
3463+ }
3464+ d->_baseUrl = url;
3465+ Q_EMIT baseUrlChanged();
3466+}
3467+
3468+QUrl HomescreenWebapp::referrer() const
3469+{
3470+ Q_D(const HomescreenWebapp);
3471+ return d->_referrer;
3472+}
3473+
3474+void HomescreenWebapp::setReferrer(const QUrl& referrer)
3475+{
3476+ Q_D(HomescreenWebapp);
3477+ if (d->_referrer == referrer)
3478+ {
3479+ return;
3480+ }
3481+ d->_referrer = referrer;
3482+}
3483+
3484+QString HomescreenWebapp::userAgent() const
3485+{
3486+ Q_D(const HomescreenWebapp);
3487+ return d->_userAgent;
3488+}
3489+
3490+void HomescreenWebapp::setUserAgent(const QString& userAgent)
3491+{
3492+ Q_D(HomescreenWebapp);
3493+ if (d->_userAgent == userAgent)
3494+ {
3495+ return;
3496+ }
3497+ d->_userAgent = userAgent;
3498+}
3499+
3500+void HomescreenWebapp::install(
3501+ int requestId,
3502+ const QString& short_name,
3503+ const QString& name,
3504+ const QUrl& homepage,
3505+ const QStringList& icon_uris)
3506+{
3507+ Q_D(HomescreenWebapp);
3508+
3509+ QObject::connect(
3510+ d, SIGNAL(installed(int,bool,QString)),
3511+ this, SLOT(installCompleted(int,bool,const QString&)));
3512+
3513+ return d->install(requestId, short_name, name, homepage, icon_uris);
3514+}
3515+
3516+void HomescreenWebapp::installCompleted(
3517+ int id,
3518+ bool status,
3519+ const QString& errorMessage)
3520+{
3521+ Q_EMIT installed(id, status, errorMessage);
3522+}
3523+
3524+#include "homescreen-webapp.moc"
3525
3526=== added file 'src/app/webbrowser/homescreen-webapp.h'
3527--- src/app/webbrowser/homescreen-webapp.h 1970-01-01 00:00:00 +0000
3528+++ src/app/webbrowser/homescreen-webapp.h 2016-06-16 15:34:13 +0000
3529@@ -0,0 +1,86 @@
3530+/*
3531+ * Copyright 2015 Canonical Ltd.
3532+ *
3533+ * This file is part of webbrowser-app.
3534+ *
3535+ * webbrowser-app is free software; you can redistribute it and/or modify
3536+ * it under the terms of the GNU General Public License as published by
3537+ * the Free Software Foundation; version 3.
3538+ *
3539+ * webbrowser-app is distributed in the hope that it will be useful,
3540+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3541+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3542+ * GNU General Public License for more details.
3543+ *
3544+ * You should have received a copy of the GNU General Public License
3545+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3546+ */
3547+
3548+#ifndef _HOMESCREEN_WEBAPP_H_
3549+#define _HOMESCREEN_WEBAPP_H_
3550+
3551+#include <QObject>
3552+#include <QUrl>
3553+#include <QString>
3554+#include <QScopedPointer>
3555+
3556+
3557+class HomescreenWebappPrivate;
3558+
3559+class HomescreenWebapp : public QObject
3560+{
3561+ Q_OBJECT
3562+ Q_PROPERTY(QString manifestFilename READ manifestFilename WRITE setManifestFilename NOTIFY manifestFilenameChanged)
3563+ Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl NOTIFY baseUrlChanged)
3564+ Q_PROPERTY(QUrl referrer READ referrer WRITE setReferrer NOTIFY referrerChanged)
3565+ Q_PROPERTY(QString userAgent READ userAgent WRITE setUserAgent NOTIFY userAgentChanged)
3566+
3567+public:
3568+ explicit HomescreenWebapp(QObject *parent = 0);
3569+ ~HomescreenWebapp();
3570+
3571+ QString manifestFilename() const;
3572+ void setManifestFilename(const QString& manifest);
3573+
3574+ QUrl baseUrl() const;
3575+ void setBaseUrl(const QUrl& url);
3576+
3577+ QUrl referrer() const;
3578+ void setReferrer(const QUrl& referrer);
3579+
3580+ QString userAgent() const;
3581+ void setUserAgent(const QString& userAgent);
3582+
3583+ Q_INVOKABLE void install(
3584+ int requestId,
3585+ const QString& short_name,
3586+ const QString& name,
3587+ const QUrl& homepage,
3588+ const QStringList& icon_uris);
3589+
3590+
3591+Q_SIGNALS:
3592+ void installed(
3593+ int id,
3594+ bool status,
3595+ const QString& errorMessage);
3596+
3597+ void manifestFilenameChanged();
3598+ void baseUrlChanged();
3599+ void referrerChanged();
3600+ void userAgentChanged();
3601+
3602+
3603+private Q_SLOTS:
3604+ void installCompleted(
3605+ int id,
3606+ bool status,
3607+ const QString& errorMessage);
3608+
3609+
3610+private:
3611+ HomescreenWebappPrivate* d_ptr;
3612+ Q_DECLARE_PRIVATE(HomescreenWebapp)
3613+};
3614+
3615+#endif // _HOMESCREEN_WEBAPP_H_
3616
3617=== added file 'src/app/webbrowser/page-metadata-gathering.js'
3618--- src/app/webbrowser/page-metadata-gathering.js 1970-01-01 00:00:00 +0000
3619+++ src/app/webbrowser/page-metadata-gathering.js 2016-06-16 15:34:13 +0000
3620@@ -0,0 +1,101 @@
3621+/*
3622+ * Copyright 2015 Canonical Ltd.
3623+ *
3624+ * This file is part of webbrowser-app.
3625+ *
3626+ * webbrowser-app is free software; you can redistribute it and/or modify
3627+ * it under the terms of the GNU General Public License as published by
3628+ * the Free Software Foundation; version 3.
3629+ *
3630+ * webbrowser-app is distributed in the hope that it will be useful,
3631+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3632+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3633+ * GNU General Public License for more details.
3634+ *
3635+ * You should have received a copy of the GNU General Public License
3636+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3637+ */
3638+
3639+// Runs at document-end by default
3640+(function() {
3641+ function detectWebappManifest() {
3642+ var webapp_capable_meta =
3643+ document.head.querySelector('meta[name="mobile-web-app-capable"]');
3644+ var manifest = document.head.querySelector('link[rel="manifest"]');
3645+ var theme_color_meta = document.head.querySelector('meta[name="theme-color"]');
3646+
3647+ if (webapp_capable_meta &&
3648+ manifest &&
3649+ webapp_capable_meta.getAttribute('content') === 'yes' &&
3650+ manifest.getAttribute('href')) {
3651+
3652+ oxide.sendMessage(
3653+ 'webapp-capable-website-detected', {
3654+ type: 'manifest',
3655+ manifest: manifest.href,
3656+ baseurl: document.location.href,
3657+ theme_color: theme_color_meta.getAttribute('content')
3658+ });
3659+ return true;
3660+ }
3661+ return false;
3662+ }
3663+
3664+ function detectAppleMetaWebappInfo() {
3665+ var webapp_capable_meta =
3666+ document.head.querySelector('meta[name="apple-mobile-web-app-capable"]');
3667+ var webapp_title_meta =
3668+ document.head.querySelector('meta[name="apple-mobile-web-app-title"]');
3669+ var webapp_color_meta =
3670+ document.head.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]');
3671+
3672+ function getAppleIcons() {
3673+ // by order of pref
3674+ var apple_icon = document.head.querySelector('link[rel="apple-touch-icon"]');
3675+ var apple_icon_pre = document.head.querySelector('link[rel="apple-touch-icon-precomposed"]');
3676+ var fallback_icon = document.head.querySelector('link[rel="icon"]');
3677+
3678+ if (apple_icon &&
3679+ apple_icon.getAttribute('href')) {
3680+ return [apple_icon.getAttribute('href')]
3681+ }
3682+ if (apple_icon_pre &&
3683+ apple_icon_pre.getAttribute('href')) {
3684+ return [apple_icon_pre.getAttribute('href')]
3685+ }
3686+
3687+ // TODO watch out for multiple defs (sizes)
3688+ if (fallback_icon &&
3689+ fallback_icon.getAttribute('href')) {
3690+ return [fallback_icon.getAttribute('href')]
3691+ }
3692+ }
3693+
3694+ if (webapp_capable_meta &&
3695+ webapp_title_meta &&
3696+ webapp_capable_meta.getAttribute('content') === 'yes' &&
3697+ webapp_title_meta.getAttribute('content')) {
3698+ oxide.sendMessage(
3699+ 'webapp-capable-website-detected', {
3700+ type: 'apple',
3701+ manifest: document.head.innerHTML,
3702+ baseurl: document.location.href,
3703+ title: webapp_title_meta.getAttribute('content'),
3704+ icons: getAppleIcons(),
3705+ theme_color: webapp_color_meta
3706+ ? webapp_color_meta.getAttribute('content')
3707+ : ''
3708+ });
3709+ return true;
3710+ }
3711+ return false;
3712+ };
3713+
3714+ var detectors = [detectAppleMetaWebappInfo, detectWebappManifest]
3715+ for (var i in detectors) {
3716+ if (detectors[i]()) {
3717+ console.log('Webapp capable website detected by ' + detectors[i].name)
3718+ break;
3719+ }
3720+ }
3721+})();
3722
3723=== modified file 'src/app/webbrowser/webbrowser-app.cpp'
3724--- src/app/webbrowser/webbrowser-app.cpp 2016-05-09 22:51:50 +0000
3725+++ src/app/webbrowser/webbrowser-app.cpp 2016-06-16 15:34:13 +0000
3726@@ -25,6 +25,8 @@
3727 #include "history-domainlist-model.h"
3728 #include "history-lastvisitdatelist-model.h"
3729 #include "history-model.h"
3730+#include "homescreen-webapp.h"
3731+#include "homescreen-installed-webapp-model.h"
3732 #include "limit-proxy-model.h"
3733 #include "searchengine.h"
3734 #include "text-search-filter-model.h"
3735@@ -70,6 +72,8 @@
3736 qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel");
3737 qmlRegisterSingletonType<BookmarksModel>(uri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory);
3738 qmlRegisterType<BookmarksFolderListModel>(uri, 0, 1, "BookmarksFolderListModel");
3739+ qmlRegisterType<HomescreenWebapp>(uri, 0, 1, "HomescreenWebapp");
3740+ qmlRegisterType<HomescreenInstalledWebappModel>(uri, 0, 1, "HomescreenInstalledWebappModel");
3741 qmlRegisterSingletonType<FileOperations>(uri, 0, 1, "FileOperations", FileOperations_singleton_factory);
3742 qmlRegisterType<SearchEngine>(uri, 0, 1, "SearchEngine");
3743 qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory);
3744
3745=== modified file 'src/app/webcontainer/WebApp.qml'
3746--- src/app/webcontainer/WebApp.qml 2016-04-29 20:19:23 +0000
3747+++ src/app/webcontainer/WebApp.qml 2016-06-16 15:34:13 +0000
3748@@ -127,6 +127,29 @@
3749 return result
3750 }
3751
3752+ Component {
3753+ id: webappCapableInstallComponent
3754+ Rectangle {
3755+ id: dialogue
3756+ title: "Install"
3757+ text: "Are you sure that you want to save this file?"
3758+ Button {
3759+ text: "cancel"
3760+ onClicked: PopupUtils.close(dialogue)
3761+ }
3762+ Button {
3763+ text: "overwrite previous version"
3764+ color: UbuntuColors.orange
3765+ onClicked: PopupUtils.close(dialogue)
3766+ }
3767+ Button {
3768+ text: "save a copy"
3769+ color: UbuntuColors.orange
3770+ onClicked: PopupUtils.close(dialogue)
3771+ }
3772+ }
3773+ }
3774+
3775 Item {
3776 id: webviewContainer
3777 anchors.fill: parent
3778@@ -144,6 +167,10 @@
3779 height: parent.height - osk.height
3780 developerExtrasEnabled: webapp.developerExtrasEnabled
3781
3782+ webappManifestDetected: {
3783+ PopupUtils.open(dialog)
3784+ }
3785+
3786 onThemeColorMetaInformationDetected: {
3787 var color = webappContainerHelper.rgbColorFromCSSColor(theme_color)
3788 if (!webapp.chromeless && chromeLoader.item && color.length) {
3789@@ -158,6 +185,7 @@
3790 onSamlRequestUrlPatternReceived: {
3791 addGeneratedUrlPattern(urlPattern)
3792 }
3793+
3794 webappUrlPatterns: mergeUrlPatternSets(urlPatternSettings.generatedUrlPatterns,
3795 webapp.webappUrlPatterns)
3796
3797
3798=== modified file 'src/app/webcontainer/WebappContainerWebview.qml'
3799--- src/app/webcontainer/WebappContainerWebview.qml 2016-04-22 13:52:07 +0000
3800+++ src/app/webcontainer/WebappContainerWebview.qml 2016-06-16 15:34:13 +0000
3801@@ -87,6 +87,9 @@
3802 }
3803 }
3804
3805+ // Just being trampolined to higher up from the oxide webview
3806+ signal webappManifestDetected(var metainfos)
3807+
3808 Connections {
3809 target: webappContainerWebViewLoader.item
3810 onThemeColorMetaInformationDetected: {
3811@@ -98,6 +101,13 @@
3812 id: webappContainerWebViewLoader
3813 objectName: "containerWebviewLoader"
3814 anchors.fill: parent
3815+ onLoaded: {
3816+ if (item && item.webappManifestDetected) {
3817+ item.webappManifestDetected.connect(function(metainfos) {
3818+ webappManifestDetected(metainfos)
3819+ });
3820+ }
3821+ }
3822 }
3823
3824 onUrlChanged: if (webappContainerWebViewLoader.item) webappContainerWebViewLoader.item.url = url

Subscribers

People subscribed via source and target branches

to status/vote changes: