Merge lp:~abreu-alexandre/webbrowser-app/add-to-homescreen into lp:webbrowser-app
- add-to-homescreen
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email:
|
Commit message
very much a WIP
Description of the change
very much a WIP
- 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.
maxCacheSizeHin t 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.2This 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
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 |