|
1 #filter substitution |
|
2 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 "use strict"; |
|
8 |
|
9 const { classes: Cc, interfaces: Ci, utils: Cu } = Components; |
|
10 |
|
11 Cu.import("resource://gre/modules/Services.jsm"); |
|
12 Cu.import("resource://gre/modules/Messaging.jsm"); |
|
13 Cu.import("resource://gre/modules/SharedPreferences.jsm"); |
|
14 |
|
15 // Name of Android SharedPreference controlling whether to upload |
|
16 // health reports. |
|
17 const PREF_UPLOAD_ENABLED = "android.not_a_preference.healthreport.uploadEnabled"; |
|
18 |
|
19 // Name of Gecko Pref specifying report content location. |
|
20 const PREF_REPORTURL = "datareporting.healthreport.about.reportUrl"; |
|
21 |
|
22 // Monotonically increasing wrapper API version number. |
|
23 const WRAPPER_VERSION = 1; |
|
24 |
|
25 const EVENT_HEALTH_REQUEST = "HealthReport:Request"; |
|
26 const EVENT_HEALTH_RESPONSE = "HealthReport:Response"; |
|
27 |
|
28 // about:healthreport prefs are stored in Firefox's default Android |
|
29 // SharedPreferences. |
|
30 let sharedPrefs = new SharedPreferences(); |
|
31 |
|
32 let healthReportWrapper = { |
|
33 init: function () { |
|
34 let iframe = document.getElementById("remote-report"); |
|
35 iframe.addEventListener("load", healthReportWrapper.initRemotePage, false); |
|
36 let report = this._getReportURI(); |
|
37 iframe.src = report.spec; |
|
38 console.log("AboutHealthReport: loading content from " + report.spec); |
|
39 |
|
40 sharedPrefs.addObserver(PREF_UPLOAD_ENABLED, this, false); |
|
41 Services.obs.addObserver(this, EVENT_HEALTH_RESPONSE, false); |
|
42 }, |
|
43 |
|
44 observe: function (subject, topic, data) { |
|
45 if (topic == PREF_UPLOAD_ENABLED) { |
|
46 this.updatePrefState(); |
|
47 } else if (topic == EVENT_HEALTH_RESPONSE) { |
|
48 this.updatePayload(data); |
|
49 } |
|
50 }, |
|
51 |
|
52 uninit: function () { |
|
53 sharedPrefs.removeObserver(PREF_UPLOAD_ENABLED, this); |
|
54 Services.obs.removeObserver(this, EVENT_HEALTH_RESPONSE); |
|
55 }, |
|
56 |
|
57 _getReportURI: function () { |
|
58 let url = Services.urlFormatter.formatURLPref(PREF_REPORTURL); |
|
59 // This handles URLs that already have query parameters. |
|
60 let uri = Services.io.newURI(url, null, null).QueryInterface(Ci.nsIURL); |
|
61 uri.query += ((uri.query != "") ? "&v=" : "v=") + WRAPPER_VERSION; |
|
62 return uri; |
|
63 }, |
|
64 |
|
65 onOptIn: function () { |
|
66 console.log("AboutHealthReport: page sent opt-in command."); |
|
67 sharedPrefs.setBoolPref(PREF_UPLOAD_ENABLED, true); |
|
68 this.updatePrefState(); |
|
69 }, |
|
70 |
|
71 onOptOut: function () { |
|
72 console.log("AboutHealthReport: page sent opt-out command."); |
|
73 sharedPrefs.setBoolPref(PREF_UPLOAD_ENABLED, false); |
|
74 this.updatePrefState(); |
|
75 }, |
|
76 |
|
77 updatePrefState: function () { |
|
78 console.log("AboutHealthReport: sending pref state to page."); |
|
79 try { |
|
80 let prefs = { |
|
81 enabled: sharedPrefs.getBoolPref(PREF_UPLOAD_ENABLED), |
|
82 }; |
|
83 this.injectData("prefs", prefs); |
|
84 } catch (e) { |
|
85 this.reportFailure(this.ERROR_PREFS_FAILED); |
|
86 } |
|
87 }, |
|
88 |
|
89 refreshPayload: function () { |
|
90 console.log("AboutHealthReport: page requested fresh payload."); |
|
91 sendMessageToJava({ |
|
92 type: EVENT_HEALTH_REQUEST, |
|
93 }); |
|
94 }, |
|
95 |
|
96 updatePayload: function (data) { |
|
97 healthReportWrapper.injectData("payload", data); |
|
98 // Data is supposed to be a string, so the length should be |
|
99 // defined. Just in case, we do this after injecting the data. |
|
100 console.log("AboutHealthReport: sending payload to page " + |
|
101 "(" + typeof(data) + " of length " + data.length + ")."); |
|
102 }, |
|
103 |
|
104 injectData: function (type, content) { |
|
105 let report = this._getReportURI(); |
|
106 |
|
107 // file: URIs can't be used for targetOrigin, so we use "*" for |
|
108 // this special case. In all other cases, pass in the URL to the |
|
109 // report so we properly restrict the message dispatch. |
|
110 let reportUrl = (report.scheme == "file") ? "*" : report.spec; |
|
111 |
|
112 let data = { |
|
113 type: type, |
|
114 content: content, |
|
115 }; |
|
116 |
|
117 let iframe = document.getElementById("remote-report"); |
|
118 iframe.contentWindow.postMessage(data, reportUrl); |
|
119 }, |
|
120 |
|
121 showSettings: function () { |
|
122 console.log("AboutHealthReport: showing settings."); |
|
123 sendMessageToJava({ |
|
124 type: "Settings:Show", |
|
125 resource: "preferences_vendor", |
|
126 }); |
|
127 }, |
|
128 |
|
129 launchUpdater: function () { |
|
130 console.log("AboutHealthReport: launching updater."); |
|
131 sendMessageToJava({ |
|
132 type: "Updater:Launch", |
|
133 }); |
|
134 }, |
|
135 |
|
136 handleRemoteCommand: function (evt) { |
|
137 switch (evt.detail.command) { |
|
138 case "DisableDataSubmission": |
|
139 this.onOptOut(); |
|
140 break; |
|
141 case "EnableDataSubmission": |
|
142 this.onOptIn(); |
|
143 break; |
|
144 case "RequestCurrentPrefs": |
|
145 this.updatePrefState(); |
|
146 break; |
|
147 case "RequestCurrentPayload": |
|
148 this.refreshPayload(); |
|
149 break; |
|
150 case "ShowSettings": |
|
151 this.showSettings(); |
|
152 break; |
|
153 case "LaunchUpdater": |
|
154 this.launchUpdater(); |
|
155 break; |
|
156 default: |
|
157 Cu.reportError("Unexpected remote command received: " + evt.detail.command + |
|
158 ". Ignoring command."); |
|
159 break; |
|
160 } |
|
161 }, |
|
162 |
|
163 initRemotePage: function () { |
|
164 let iframe = document.getElementById("remote-report").contentDocument; |
|
165 iframe.addEventListener("RemoteHealthReportCommand", |
|
166 function onCommand(e) {healthReportWrapper.handleRemoteCommand(e);}, |
|
167 false); |
|
168 healthReportWrapper.injectData("begin", null); |
|
169 }, |
|
170 |
|
171 // error handling |
|
172 ERROR_INIT_FAILED: 1, |
|
173 ERROR_PAYLOAD_FAILED: 2, |
|
174 ERROR_PREFS_FAILED: 3, |
|
175 |
|
176 reportFailure: function (error) { |
|
177 let details = { |
|
178 errorType: error, |
|
179 }; |
|
180 healthReportWrapper.injectData("error", details); |
|
181 }, |
|
182 |
|
183 handleInitFailure: function () { |
|
184 healthReportWrapper.reportFailure(healthReportWrapper.ERROR_INIT_FAILED); |
|
185 }, |
|
186 |
|
187 handlePayloadFailure: function () { |
|
188 healthReportWrapper.reportFailure(healthReportWrapper.ERROR_PAYLOAD_FAILED); |
|
189 }, |
|
190 }; |
|
191 |
|
192 window.addEventListener("load", healthReportWrapper.init.bind(healthReportWrapper), false); |
|
193 window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false); |