Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /** |
michael@0 | 8 | * This component implements the XPCOM interfaces required for integration with |
michael@0 | 9 | * the legacy download components. |
michael@0 | 10 | * |
michael@0 | 11 | * New code is expected to use the "Downloads.jsm" module directly, without |
michael@0 | 12 | * going through the interfaces implemented in this XPCOM component. These |
michael@0 | 13 | * interfaces are only maintained for backwards compatibility with components |
michael@0 | 14 | * that still work synchronously on the main thread. |
michael@0 | 15 | */ |
michael@0 | 16 | |
michael@0 | 17 | "use strict"; |
michael@0 | 18 | |
michael@0 | 19 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 20 | //// Globals |
michael@0 | 21 | |
michael@0 | 22 | const Cc = Components.classes; |
michael@0 | 23 | const Ci = Components.interfaces; |
michael@0 | 24 | const Cu = Components.utils; |
michael@0 | 25 | const Cr = Components.results; |
michael@0 | 26 | |
michael@0 | 27 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 28 | |
michael@0 | 29 | XPCOMUtils.defineLazyModuleGetter(this, "Downloads", |
michael@0 | 30 | "resource://gre/modules/Downloads.jsm"); |
michael@0 | 31 | XPCOMUtils.defineLazyModuleGetter(this, "Promise", |
michael@0 | 32 | "resource://gre/modules/Promise.jsm"); |
michael@0 | 33 | |
michael@0 | 34 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 35 | //// DownloadLegacyTransfer |
michael@0 | 36 | |
michael@0 | 37 | /** |
michael@0 | 38 | * nsITransfer implementation that provides a bridge to a Download object. |
michael@0 | 39 | * |
michael@0 | 40 | * Legacy downloads work differently than the JavaScript implementation. In the |
michael@0 | 41 | * latter, the caller only provides the properties for the Download object and |
michael@0 | 42 | * the entire process is handled by the "start" method. In the legacy |
michael@0 | 43 | * implementation, the caller must create a separate object to execute the |
michael@0 | 44 | * download, and then make the download visible to the user by hooking it up to |
michael@0 | 45 | * an nsITransfer instance. |
michael@0 | 46 | * |
michael@0 | 47 | * Since nsITransfer instances may be created before the download system is |
michael@0 | 48 | * initialized, and initialization as well as other operations are asynchronous, |
michael@0 | 49 | * this implementation is able to delay all progress and status notifications it |
michael@0 | 50 | * receives until the associated Download object is finally created. |
michael@0 | 51 | * |
michael@0 | 52 | * Conversely, the DownloadLegacySaver object can also receive execution and |
michael@0 | 53 | * cancellation requests asynchronously, before or after it is connected to |
michael@0 | 54 | * this nsITransfer instance. For that reason, those requests are communicated |
michael@0 | 55 | * in a potentially deferred way, using promise objects. |
michael@0 | 56 | * |
michael@0 | 57 | * The component that executes the download implements nsICancelable to receive |
michael@0 | 58 | * cancellation requests, but after cancellation it cannot be reused again. |
michael@0 | 59 | * |
michael@0 | 60 | * Since the components that execute the download may be different and they |
michael@0 | 61 | * don't always give consistent results, this bridge takes care of enforcing the |
michael@0 | 62 | * expectations, for example by ensuring the target file exists when the |
michael@0 | 63 | * download is successful, even if the source has a size of zero bytes. |
michael@0 | 64 | */ |
michael@0 | 65 | function DownloadLegacyTransfer() |
michael@0 | 66 | { |
michael@0 | 67 | this._deferDownload = Promise.defer(); |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | DownloadLegacyTransfer.prototype = { |
michael@0 | 71 | classID: Components.ID("{1b4c85df-cbdd-4bb6-b04e-613caece083c}"), |
michael@0 | 72 | |
michael@0 | 73 | ////////////////////////////////////////////////////////////////////////////// |
michael@0 | 74 | //// nsISupports |
michael@0 | 75 | |
michael@0 | 76 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, |
michael@0 | 77 | Ci.nsIWebProgressListener2, |
michael@0 | 78 | Ci.nsITransfer]), |
michael@0 | 79 | |
michael@0 | 80 | ////////////////////////////////////////////////////////////////////////////// |
michael@0 | 81 | //// nsIWebProgressListener |
michael@0 | 82 | |
michael@0 | 83 | onStateChange: function DLT_onStateChange(aWebProgress, aRequest, aStateFlags, |
michael@0 | 84 | aStatus) |
michael@0 | 85 | { |
michael@0 | 86 | if (!Components.isSuccessCode(aStatus)) { |
michael@0 | 87 | this._componentFailed = true; |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | if ((aStateFlags & Ci.nsIWebProgressListener.STATE_START) && |
michael@0 | 91 | (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) { |
michael@0 | 92 | |
michael@0 | 93 | // If the request's response has been blocked by Windows Parental Controls |
michael@0 | 94 | // with an HTTP 450 error code, we must cancel the request synchronously. |
michael@0 | 95 | let blockedByParentalControls = aRequest instanceof Ci.nsIHttpChannel && |
michael@0 | 96 | aRequest.responseStatus == 450; |
michael@0 | 97 | if (blockedByParentalControls) { |
michael@0 | 98 | aRequest.cancel(Cr.NS_BINDING_ABORTED); |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | // The main request has just started. Wait for the associated Download |
michael@0 | 102 | // object to be available before notifying. |
michael@0 | 103 | this._deferDownload.promise.then(download => { |
michael@0 | 104 | // If the request was blocked, now that we have the download object we |
michael@0 | 105 | // should set a flag that can be retrieved later when handling the |
michael@0 | 106 | // cancellation so that the proper error can be thrown. |
michael@0 | 107 | if (blockedByParentalControls) { |
michael@0 | 108 | download._blockedByParentalControls = true; |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | download.saver.onTransferStarted( |
michael@0 | 112 | aRequest, |
michael@0 | 113 | this._cancelable instanceof Ci.nsIHelperAppLauncher); |
michael@0 | 114 | |
michael@0 | 115 | // To handle asynchronous cancellation properly, we should hook up the |
michael@0 | 116 | // handler only after we have been notified that the main request |
michael@0 | 117 | // started. We will wait until the main request stopped before |
michael@0 | 118 | // notifying that the download has been canceled. Since the request has |
michael@0 | 119 | // not completed yet, deferCanceled is guaranteed to be set. |
michael@0 | 120 | return download.saver.deferCanceled.promise.then(() => { |
michael@0 | 121 | // Only cancel if the object executing the download is still running. |
michael@0 | 122 | if (this._cancelable && !this._componentFailed) { |
michael@0 | 123 | this._cancelable.cancel(Cr.NS_ERROR_ABORT); |
michael@0 | 124 | if (this._cancelable instanceof Ci.nsIWebBrowserPersist) { |
michael@0 | 125 | // This component will not send the STATE_STOP notification. |
michael@0 | 126 | download.saver.onTransferFinished(aRequest, Cr.NS_ERROR_ABORT); |
michael@0 | 127 | this._cancelable = null; |
michael@0 | 128 | } |
michael@0 | 129 | } |
michael@0 | 130 | }); |
michael@0 | 131 | }).then(null, Cu.reportError); |
michael@0 | 132 | } else if ((aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) && |
michael@0 | 133 | (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) { |
michael@0 | 134 | // The last file has been received, or the download failed. Wait for the |
michael@0 | 135 | // associated Download object to be available before notifying. |
michael@0 | 136 | this._deferDownload.promise.then(download => { |
michael@0 | 137 | // At this point, the hash has been set and we need to copy it to the |
michael@0 | 138 | // DownloadSaver. |
michael@0 | 139 | if (Components.isSuccessCode(aStatus)) { |
michael@0 | 140 | download.saver.setSha256Hash(this._sha256Hash); |
michael@0 | 141 | download.saver.setSignatureInfo(this._signatureInfo); |
michael@0 | 142 | } |
michael@0 | 143 | download.saver.onTransferFinished(aRequest, aStatus); |
michael@0 | 144 | }).then(null, Cu.reportError); |
michael@0 | 145 | |
michael@0 | 146 | // Release the reference to the component executing the download. |
michael@0 | 147 | this._cancelable = null; |
michael@0 | 148 | } |
michael@0 | 149 | }, |
michael@0 | 150 | |
michael@0 | 151 | onProgressChange: function DLT_onProgressChange(aWebProgress, aRequest, |
michael@0 | 152 | aCurSelfProgress, |
michael@0 | 153 | aMaxSelfProgress, |
michael@0 | 154 | aCurTotalProgress, |
michael@0 | 155 | aMaxTotalProgress) |
michael@0 | 156 | { |
michael@0 | 157 | this.onProgressChange64(aWebProgress, aRequest, aCurSelfProgress, |
michael@0 | 158 | aMaxSelfProgress, aCurTotalProgress, |
michael@0 | 159 | aMaxTotalProgress); |
michael@0 | 160 | }, |
michael@0 | 161 | |
michael@0 | 162 | onLocationChange: function () { }, |
michael@0 | 163 | |
michael@0 | 164 | onStatusChange: function DLT_onStatusChange(aWebProgress, aRequest, aStatus, |
michael@0 | 165 | aMessage) |
michael@0 | 166 | { |
michael@0 | 167 | // The status change may optionally be received in addition to the state |
michael@0 | 168 | // change, but if no network request actually started, it is possible that |
michael@0 | 169 | // we only receive a status change with an error status code. |
michael@0 | 170 | if (!Components.isSuccessCode(aStatus)) { |
michael@0 | 171 | this._componentFailed = true; |
michael@0 | 172 | |
michael@0 | 173 | // Wait for the associated Download object to be available. |
michael@0 | 174 | this._deferDownload.promise.then(function DLT_OSC_onDownload(aDownload) { |
michael@0 | 175 | aDownload.saver.onTransferFinished(aRequest, aStatus); |
michael@0 | 176 | }).then(null, Cu.reportError); |
michael@0 | 177 | } |
michael@0 | 178 | }, |
michael@0 | 179 | |
michael@0 | 180 | onSecurityChange: function () { }, |
michael@0 | 181 | |
michael@0 | 182 | ////////////////////////////////////////////////////////////////////////////// |
michael@0 | 183 | //// nsIWebProgressListener2 |
michael@0 | 184 | |
michael@0 | 185 | onProgressChange64: function DLT_onProgressChange64(aWebProgress, aRequest, |
michael@0 | 186 | aCurSelfProgress, |
michael@0 | 187 | aMaxSelfProgress, |
michael@0 | 188 | aCurTotalProgress, |
michael@0 | 189 | aMaxTotalProgress) |
michael@0 | 190 | { |
michael@0 | 191 | // Wait for the associated Download object to be available. |
michael@0 | 192 | this._deferDownload.promise.then(function DLT_OPC64_onDownload(aDownload) { |
michael@0 | 193 | aDownload.saver.onProgressBytes(aCurTotalProgress, aMaxTotalProgress); |
michael@0 | 194 | }).then(null, Cu.reportError); |
michael@0 | 195 | }, |
michael@0 | 196 | |
michael@0 | 197 | onRefreshAttempted: function DLT_onRefreshAttempted(aWebProgress, aRefreshURI, |
michael@0 | 198 | aMillis, aSameURI) |
michael@0 | 199 | { |
michael@0 | 200 | // Indicate that refreshes and redirects are allowed by default. However, |
michael@0 | 201 | // note that download components don't usually call this method at all. |
michael@0 | 202 | return true; |
michael@0 | 203 | }, |
michael@0 | 204 | |
michael@0 | 205 | ////////////////////////////////////////////////////////////////////////////// |
michael@0 | 206 | //// nsITransfer |
michael@0 | 207 | |
michael@0 | 208 | init: function DLT_init(aSource, aTarget, aDisplayName, aMIMEInfo, aStartTime, |
michael@0 | 209 | aTempFile, aCancelable, aIsPrivate) |
michael@0 | 210 | { |
michael@0 | 211 | this._cancelable = aCancelable; |
michael@0 | 212 | |
michael@0 | 213 | let launchWhenSucceeded = false, contentType = null, launcherPath = null; |
michael@0 | 214 | |
michael@0 | 215 | if (aMIMEInfo instanceof Ci.nsIMIMEInfo) { |
michael@0 | 216 | launchWhenSucceeded = |
michael@0 | 217 | aMIMEInfo.preferredAction != Ci.nsIMIMEInfo.saveToDisk; |
michael@0 | 218 | contentType = aMIMEInfo.type; |
michael@0 | 219 | |
michael@0 | 220 | let appHandler = aMIMEInfo.preferredApplicationHandler; |
michael@0 | 221 | if (aMIMEInfo.preferredAction == Ci.nsIMIMEInfo.useHelperApp && |
michael@0 | 222 | appHandler instanceof Ci.nsILocalHandlerApp) { |
michael@0 | 223 | launcherPath = appHandler.executable.path; |
michael@0 | 224 | } |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | // Create a new Download object associated to a DownloadLegacySaver, and |
michael@0 | 228 | // wait for it to be available. This operation may cause the entire |
michael@0 | 229 | // download system to initialize before the object is created. |
michael@0 | 230 | Downloads.createDownload({ |
michael@0 | 231 | source: { url: aSource.spec, isPrivate: aIsPrivate }, |
michael@0 | 232 | target: { path: aTarget.QueryInterface(Ci.nsIFileURL).file.path, |
michael@0 | 233 | partFilePath: aTempFile && aTempFile.path }, |
michael@0 | 234 | saver: "legacy", |
michael@0 | 235 | launchWhenSucceeded: launchWhenSucceeded, |
michael@0 | 236 | contentType: contentType, |
michael@0 | 237 | launcherPath: launcherPath |
michael@0 | 238 | }).then(function DLT_I_onDownload(aDownload) { |
michael@0 | 239 | // Legacy components keep partial data when they use a ".part" file. |
michael@0 | 240 | if (aTempFile) { |
michael@0 | 241 | aDownload.tryToKeepPartialData = true; |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | // Start the download before allowing it to be controlled. Ignore errors. |
michael@0 | 245 | aDownload.start().then(null, () => {}); |
michael@0 | 246 | |
michael@0 | 247 | // Start processing all the other events received through nsITransfer. |
michael@0 | 248 | this._deferDownload.resolve(aDownload); |
michael@0 | 249 | |
michael@0 | 250 | // Add the download to the list, allowing it to be seen and canceled. |
michael@0 | 251 | return Downloads.getList(Downloads.ALL).then(list => list.add(aDownload)); |
michael@0 | 252 | }.bind(this)).then(null, Cu.reportError); |
michael@0 | 253 | }, |
michael@0 | 254 | |
michael@0 | 255 | setSha256Hash: function (hash) |
michael@0 | 256 | { |
michael@0 | 257 | this._sha256Hash = hash; |
michael@0 | 258 | }, |
michael@0 | 259 | |
michael@0 | 260 | setSignatureInfo: function (signatureInfo) |
michael@0 | 261 | { |
michael@0 | 262 | this._signatureInfo = signatureInfo; |
michael@0 | 263 | }, |
michael@0 | 264 | |
michael@0 | 265 | ////////////////////////////////////////////////////////////////////////////// |
michael@0 | 266 | //// Private methods and properties |
michael@0 | 267 | |
michael@0 | 268 | /** |
michael@0 | 269 | * This deferred object contains a promise that is resolved with the Download |
michael@0 | 270 | * object associated with this nsITransfer instance, when it is available. |
michael@0 | 271 | */ |
michael@0 | 272 | _deferDownload: null, |
michael@0 | 273 | |
michael@0 | 274 | /** |
michael@0 | 275 | * Reference to the component that is executing the download. This component |
michael@0 | 276 | * allows cancellation through its nsICancelable interface. |
michael@0 | 277 | */ |
michael@0 | 278 | _cancelable: null, |
michael@0 | 279 | |
michael@0 | 280 | /** |
michael@0 | 281 | * Indicates that the component that executes the download has notified a |
michael@0 | 282 | * failure condition. In this case, we should never use the component methods |
michael@0 | 283 | * that cancel the download. |
michael@0 | 284 | */ |
michael@0 | 285 | _componentFailed: false, |
michael@0 | 286 | |
michael@0 | 287 | /** |
michael@0 | 288 | * Save the SHA-256 hash in raw bytes of the downloaded file. |
michael@0 | 289 | */ |
michael@0 | 290 | _sha256Hash: null, |
michael@0 | 291 | |
michael@0 | 292 | /** |
michael@0 | 293 | * Save the signature info in a serialized protobuf of the downloaded file. |
michael@0 | 294 | */ |
michael@0 | 295 | _signatureInfo: null, |
michael@0 | 296 | }; |
michael@0 | 297 | |
michael@0 | 298 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 299 | //// Module |
michael@0 | 300 | |
michael@0 | 301 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadLegacyTransfer]); |