browser/devtools/shared/AppCacheUtils.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /**
michael@0 6 * validateManifest() warns of the following errors:
michael@0 7 * - No manifest specified in page
michael@0 8 * - Manifest is not utf-8
michael@0 9 * - Manifest mimetype not text/cache-manifest
michael@0 10 * - Manifest does not begin with "CACHE MANIFEST"
michael@0 11 * - Page modified since appcache last changed
michael@0 12 * - Duplicate entries
michael@0 13 * - Conflicting entries e.g. in both CACHE and NETWORK sections or in cache
michael@0 14 * but blocked by FALLBACK namespace
michael@0 15 * - Detect referenced files that are not available
michael@0 16 * - Detect referenced files that have cache-control set to no-store
michael@0 17 * - Wildcards used in a section other than NETWORK
michael@0 18 * - Spaces in URI not replaced with %20
michael@0 19 * - Completely invalid URIs
michael@0 20 * - Too many dot dot slash operators
michael@0 21 * - SETTINGS section is valid
michael@0 22 * - Invalid section name
michael@0 23 * - etc.
michael@0 24 */
michael@0 25
michael@0 26 "use strict";
michael@0 27
michael@0 28 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
michael@0 29
michael@0 30 let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
michael@0 31 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
michael@0 32 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
michael@0 33
michael@0 34 this.EXPORTED_SYMBOLS = ["AppCacheUtils"];
michael@0 35
michael@0 36 function AppCacheUtils(documentOrUri) {
michael@0 37 this._parseManifest = this._parseManifest.bind(this);
michael@0 38
michael@0 39 if (documentOrUri) {
michael@0 40 if (typeof documentOrUri == "string") {
michael@0 41 this.uri = documentOrUri;
michael@0 42 }
michael@0 43 if (/HTMLDocument/.test(documentOrUri.toString())) {
michael@0 44 this.doc = documentOrUri;
michael@0 45 }
michael@0 46 }
michael@0 47 }
michael@0 48
michael@0 49 AppCacheUtils.prototype = {
michael@0 50 get cachePath() {
michael@0 51 return "";
michael@0 52 },
michael@0 53
michael@0 54 validateManifest: function ACU_validateManifest() {
michael@0 55 let deferred = promise.defer();
michael@0 56 this.errors = [];
michael@0 57 // Check for missing manifest.
michael@0 58 this._getManifestURI().then(manifestURI => {
michael@0 59 this.manifestURI = manifestURI;
michael@0 60
michael@0 61 if (!this.manifestURI) {
michael@0 62 this._addError(0, "noManifest");
michael@0 63 deferred.resolve(this.errors);
michael@0 64 }
michael@0 65
michael@0 66 this._getURIInfo(this.manifestURI).then(uriInfo => {
michael@0 67 this._parseManifest(uriInfo).then(() => {
michael@0 68 // Sort errors by line number.
michael@0 69 this.errors.sort(function(a, b) {
michael@0 70 return a.line - b.line;
michael@0 71 });
michael@0 72 deferred.resolve(this.errors);
michael@0 73 });
michael@0 74 });
michael@0 75 });
michael@0 76
michael@0 77 return deferred.promise;
michael@0 78 },
michael@0 79
michael@0 80 _parseManifest: function ACU__parseManifest(uriInfo) {
michael@0 81 let deferred = promise.defer();
michael@0 82 let manifestName = uriInfo.name;
michael@0 83 let manifestLastModified = new Date(uriInfo.responseHeaders["Last-Modified"]);
michael@0 84
michael@0 85 if (uriInfo.charset.toLowerCase() != "utf-8") {
michael@0 86 this._addError(0, "notUTF8", uriInfo.charset);
michael@0 87 }
michael@0 88
michael@0 89 if (uriInfo.mimeType != "text/cache-manifest") {
michael@0 90 this._addError(0, "badMimeType", uriInfo.mimeType);
michael@0 91 }
michael@0 92
michael@0 93 let parser = new ManifestParser(uriInfo.text, this.manifestURI);
michael@0 94 let parsed = parser.parse();
michael@0 95
michael@0 96 if (parsed.errors.length > 0) {
michael@0 97 this.errors.push.apply(this.errors, parsed.errors);
michael@0 98 }
michael@0 99
michael@0 100 // Check for duplicate entries.
michael@0 101 let dupes = {};
michael@0 102 for (let parsedUri of parsed.uris) {
michael@0 103 dupes[parsedUri.uri] = dupes[parsedUri.uri] || [];
michael@0 104 dupes[parsedUri.uri].push({
michael@0 105 line: parsedUri.line,
michael@0 106 section: parsedUri.section,
michael@0 107 original: parsedUri.original
michael@0 108 });
michael@0 109 }
michael@0 110 for (let [uri, value] of Iterator(dupes)) {
michael@0 111 if (value.length > 1) {
michael@0 112 this._addError(0, "duplicateURI", uri, JSON.stringify(value));
michael@0 113 }
michael@0 114 }
michael@0 115
michael@0 116 // Loop through network entries making sure that fallback and cache don't
michael@0 117 // contain uris starting with the network uri.
michael@0 118 for (let neturi of parsed.uris) {
michael@0 119 if (neturi.section == "NETWORK") {
michael@0 120 for (let parsedUri of parsed.uris) {
michael@0 121 if (parsedUri.uri.startsWith(neturi.uri)) {
michael@0 122 this._addError(neturi.line, "networkBlocksURI", neturi.line,
michael@0 123 neturi.original, parsedUri.line, parsedUri.original,
michael@0 124 parsedUri.section);
michael@0 125 }
michael@0 126 }
michael@0 127 }
michael@0 128 }
michael@0 129
michael@0 130 // Loop through fallback entries making sure that fallback and cache don't
michael@0 131 // contain uris starting with the network uri.
michael@0 132 for (let fb of parsed.fallbacks) {
michael@0 133 for (let parsedUri of parsed.uris) {
michael@0 134 if (parsedUri.uri.startsWith(fb.namespace)) {
michael@0 135 this._addError(fb.line, "fallbackBlocksURI", fb.line,
michael@0 136 fb.original, parsedUri.line, parsedUri.original,
michael@0 137 parsedUri.section);
michael@0 138 }
michael@0 139 }
michael@0 140 }
michael@0 141
michael@0 142 // Check that all resources exist and that their cach-control headers are
michael@0 143 // not set to no-store.
michael@0 144 let current = -1;
michael@0 145 for (let i = 0, len = parsed.uris.length; i < len; i++) {
michael@0 146 let parsedUri = parsed.uris[i];
michael@0 147 this._getURIInfo(parsedUri.uri).then(uriInfo => {
michael@0 148 current++;
michael@0 149
michael@0 150 if (uriInfo.success) {
michael@0 151 // Check that the resource was not modified after the manifest was last
michael@0 152 // modified. If it was then the manifest file should be refreshed.
michael@0 153 let resourceLastModified =
michael@0 154 new Date(uriInfo.responseHeaders["Last-Modified"]);
michael@0 155
michael@0 156 if (manifestLastModified < resourceLastModified) {
michael@0 157 this._addError(parsedUri.line, "fileChangedButNotManifest",
michael@0 158 uriInfo.name, manifestName, parsedUri.line);
michael@0 159 }
michael@0 160
michael@0 161 // If cache-control: no-store the file will not be added to the
michael@0 162 // appCache.
michael@0 163 if (uriInfo.nocache) {
michael@0 164 this._addError(parsedUri.line, "cacheControlNoStore",
michael@0 165 parsedUri.original, parsedUri.line);
michael@0 166 }
michael@0 167 } else {
michael@0 168 this._addError(parsedUri.line, "notAvailable",
michael@0 169 parsedUri.original, parsedUri.line);
michael@0 170 }
michael@0 171
michael@0 172 if (current == len - 1) {
michael@0 173 deferred.resolve();
michael@0 174 }
michael@0 175 });
michael@0 176 }
michael@0 177
michael@0 178 return deferred.promise;
michael@0 179 },
michael@0 180
michael@0 181 _getURIInfo: function ACU__getURIInfo(uri) {
michael@0 182 let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
michael@0 183 .createInstance(Ci.nsIScriptableInputStream);
michael@0 184 let deferred = promise.defer();
michael@0 185 let channelCharset = "";
michael@0 186 let buffer = "";
michael@0 187 let channel = Services.io.newChannel(uri, null, null);
michael@0 188
michael@0 189 // Avoid the cache:
michael@0 190 channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
michael@0 191 channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
michael@0 192
michael@0 193 channel.asyncOpen({
michael@0 194 onStartRequest: function (request, context) {
michael@0 195 // This empty method is needed in order for onDataAvailable to be
michael@0 196 // called.
michael@0 197 },
michael@0 198
michael@0 199 onDataAvailable: function (request, context, stream, offset, count) {
michael@0 200 request.QueryInterface(Ci.nsIHttpChannel);
michael@0 201 inputStream.init(stream);
michael@0 202 buffer = buffer.concat(inputStream.read(count));
michael@0 203 },
michael@0 204
michael@0 205 onStopRequest: function onStartRequest(request, context, statusCode) {
michael@0 206 if (statusCode == 0) {
michael@0 207 request.QueryInterface(Ci.nsIHttpChannel);
michael@0 208
michael@0 209 let result = {
michael@0 210 name: request.name,
michael@0 211 success: request.requestSucceeded,
michael@0 212 status: request.responseStatus + " - " + request.responseStatusText,
michael@0 213 charset: request.contentCharset || "utf-8",
michael@0 214 mimeType: request.contentType,
michael@0 215 contentLength: request.contentLength,
michael@0 216 nocache: request.isNoCacheResponse() || request.isNoStoreResponse(),
michael@0 217 prePath: request.URI.prePath + "/",
michael@0 218 text: buffer
michael@0 219 };
michael@0 220
michael@0 221 result.requestHeaders = {};
michael@0 222 request.visitRequestHeaders(function(header, value) {
michael@0 223 result.requestHeaders[header] = value;
michael@0 224 });
michael@0 225
michael@0 226 result.responseHeaders = {};
michael@0 227 request.visitResponseHeaders(function(header, value) {
michael@0 228 result.responseHeaders[header] = value;
michael@0 229 });
michael@0 230
michael@0 231 deferred.resolve(result);
michael@0 232 } else {
michael@0 233 deferred.resolve({
michael@0 234 name: request.name,
michael@0 235 success: false
michael@0 236 });
michael@0 237 }
michael@0 238 }
michael@0 239 }, null);
michael@0 240 return deferred.promise;
michael@0 241 },
michael@0 242
michael@0 243 listEntries: function ACU_show(searchTerm) {
michael@0 244 if (!Services.prefs.getBoolPref("browser.cache.disk.enable")) {
michael@0 245 throw new Error(l10n.GetStringFromName("cacheDisabled"));
michael@0 246 }
michael@0 247
michael@0 248 let entries = [];
michael@0 249
michael@0 250 Services.cache.visitEntries({
michael@0 251 visitDevice: function(deviceID, deviceInfo) {
michael@0 252 return true;
michael@0 253 },
michael@0 254
michael@0 255 visitEntry: function(deviceID, entryInfo) {
michael@0 256 if (entryInfo.deviceID == "offline") {
michael@0 257 let entry = {};
michael@0 258 let lowerKey = entryInfo.key.toLowerCase();
michael@0 259
michael@0 260 if (searchTerm && lowerKey.indexOf(searchTerm.toLowerCase()) == -1) {
michael@0 261 return true;
michael@0 262 }
michael@0 263
michael@0 264 for (let [key, value] of Iterator(entryInfo)) {
michael@0 265 if (key == "QueryInterface") {
michael@0 266 continue;
michael@0 267 }
michael@0 268 if (key == "clientID") {
michael@0 269 entry.key = entryInfo.key;
michael@0 270 }
michael@0 271 if (key == "expirationTime" || key == "lastFetched" || key == "lastModified") {
michael@0 272 value = new Date(value * 1000);
michael@0 273 }
michael@0 274 entry[key] = value;
michael@0 275 }
michael@0 276 entries.push(entry);
michael@0 277 }
michael@0 278 return true;
michael@0 279 }
michael@0 280 });
michael@0 281
michael@0 282 if (entries.length == 0) {
michael@0 283 throw new Error(l10n.GetStringFromName("noResults"));
michael@0 284 }
michael@0 285 return entries;
michael@0 286 },
michael@0 287
michael@0 288 viewEntry: function ACU_viewEntry(key) {
michael@0 289 let uri;
michael@0 290
michael@0 291 Services.cache.visitEntries({
michael@0 292 visitDevice: function(deviceID, deviceInfo) {
michael@0 293 return true;
michael@0 294 },
michael@0 295
michael@0 296 visitEntry: function(deviceID, entryInfo) {
michael@0 297 if (entryInfo.deviceID == "offline" && entryInfo.key == key) {
michael@0 298 uri = "about:cache-entry?client=" + entryInfo.clientID +
michael@0 299 "&sb=1&key=" + entryInfo.key;
michael@0 300 return false;
michael@0 301 }
michael@0 302 return true;
michael@0 303 }
michael@0 304 });
michael@0 305
michael@0 306 if (uri) {
michael@0 307 let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
michael@0 308 .getService(Ci.nsIWindowMediator);
michael@0 309 let win = wm.getMostRecentWindow("navigator:browser");
michael@0 310 win.gBrowser.selectedTab = win.gBrowser.addTab(uri);
michael@0 311 } else {
michael@0 312 return l10n.GetStringFromName("entryNotFound");
michael@0 313 }
michael@0 314 },
michael@0 315
michael@0 316 clearAll: function ACU_clearAll() {
michael@0 317 Services.cache.evictEntries(Ci.nsICache.STORE_OFFLINE);
michael@0 318 },
michael@0 319
michael@0 320 _getManifestURI: function ACU__getManifestURI() {
michael@0 321 let deferred = promise.defer();
michael@0 322
michael@0 323 let getURI = node => {
michael@0 324 let htmlNode = this.doc.querySelector("html[manifest]");
michael@0 325 if (htmlNode) {
michael@0 326 let pageUri = this.doc.location ? this.doc.location.href : this.uri;
michael@0 327 let origin = pageUri.substr(0, pageUri.lastIndexOf("/") + 1);
michael@0 328 return origin + htmlNode.getAttribute("manifest");
michael@0 329 }
michael@0 330 };
michael@0 331
michael@0 332 if (this.doc) {
michael@0 333 let uri = getURI(this.doc);
michael@0 334 return promise.resolve(uri);
michael@0 335 } else {
michael@0 336 this._getURIInfo(this.uri).then(uriInfo => {
michael@0 337 if (uriInfo.success) {
michael@0 338 let html = uriInfo.text;
michael@0 339 let parser = _DOMParser;
michael@0 340 this.doc = parser.parseFromString(html, "text/html");
michael@0 341 let uri = getURI(this.doc);
michael@0 342 deferred.resolve(uri);
michael@0 343 } else {
michael@0 344 this.errors.push({
michael@0 345 line: 0,
michael@0 346 msg: l10n.GetStringFromName("invalidURI")
michael@0 347 });
michael@0 348 }
michael@0 349 });
michael@0 350 }
michael@0 351 return deferred.promise;
michael@0 352 },
michael@0 353
michael@0 354 _addError: function ACU__addError(line, l10nString, ...params) {
michael@0 355 let msg;
michael@0 356
michael@0 357 if (params) {
michael@0 358 msg = l10n.formatStringFromName(l10nString, params, params.length);
michael@0 359 } else {
michael@0 360 msg = l10n.GetStringFromName(l10nString);
michael@0 361 }
michael@0 362
michael@0 363 this.errors.push({
michael@0 364 line: line,
michael@0 365 msg: msg
michael@0 366 });
michael@0 367 },
michael@0 368 };
michael@0 369
michael@0 370 /**
michael@0 371 * We use our own custom parser because we need far more detailed information
michael@0 372 * than the system manifest parser provides.
michael@0 373 *
michael@0 374 * @param {String} manifestText
michael@0 375 * The text content of the manifest file.
michael@0 376 * @param {String} manifestURI
michael@0 377 * The URI of the manifest file. This is used in calculating the path of
michael@0 378 * relative URIs.
michael@0 379 */
michael@0 380 function ManifestParser(manifestText, manifestURI) {
michael@0 381 this.manifestText = manifestText;
michael@0 382 this.origin = manifestURI.substr(0, manifestURI.lastIndexOf("/") + 1)
michael@0 383 .replace(" ", "%20");
michael@0 384 }
michael@0 385
michael@0 386 ManifestParser.prototype = {
michael@0 387 parse: function OCIMP_parse() {
michael@0 388 let lines = this.manifestText.split(/\r?\n/);
michael@0 389 let fallbacks = this.fallbacks = [];
michael@0 390 let settings = this.settings = [];
michael@0 391 let errors = this.errors = [];
michael@0 392 let uris = this.uris = [];
michael@0 393
michael@0 394 this.currSection = "CACHE";
michael@0 395
michael@0 396 for (let i = 0; i < lines.length; i++) {
michael@0 397 let text = this.text = lines[i].replace(/^\s+|\s+$/g);
michael@0 398 this.currentLine = i + 1;
michael@0 399
michael@0 400 if (i == 0 && text != "CACHE MANIFEST") {
michael@0 401 this._addError(1, "firstLineMustBeCacheManifest", 1);
michael@0 402 }
michael@0 403
michael@0 404 // Ignore comments
michael@0 405 if (/^#/.test(text) || !text.length) {
michael@0 406 continue;
michael@0 407 }
michael@0 408
michael@0 409 if (text == "CACHE MANIFEST") {
michael@0 410 if (this.currentLine != 1) {
michael@0 411 this._addError(this.currentLine, "cacheManifestOnlyFirstLine2",
michael@0 412 this.currentLine);
michael@0 413 }
michael@0 414 continue;
michael@0 415 }
michael@0 416
michael@0 417 if (this._maybeUpdateSectionName()) {
michael@0 418 continue;
michael@0 419 }
michael@0 420
michael@0 421 switch (this.currSection) {
michael@0 422 case "CACHE":
michael@0 423 case "NETWORK":
michael@0 424 this.parseLine();
michael@0 425 break;
michael@0 426 case "FALLBACK":
michael@0 427 this.parseFallbackLine();
michael@0 428 break;
michael@0 429 case "SETTINGS":
michael@0 430 this.parseSettingsLine();
michael@0 431 break;
michael@0 432 }
michael@0 433 }
michael@0 434
michael@0 435 return {
michael@0 436 uris: uris,
michael@0 437 fallbacks: fallbacks,
michael@0 438 settings: settings,
michael@0 439 errors: errors
michael@0 440 };
michael@0 441 },
michael@0 442
michael@0 443 parseLine: function OCIMP_parseLine() {
michael@0 444 let text = this.text;
michael@0 445
michael@0 446 if (text.indexOf("*") != -1) {
michael@0 447 if (this.currSection != "NETWORK" || text.length != 1) {
michael@0 448 this._addError(this.currentLine, "asteriskInWrongSection2",
michael@0 449 this.currSection, this.currentLine);
michael@0 450 return;
michael@0 451 }
michael@0 452 }
michael@0 453
michael@0 454 if (/\s/.test(text)) {
michael@0 455 this._addError(this.currentLine, "escapeSpaces", this.currentLine);
michael@0 456 text = text.replace(/\s/g, "%20")
michael@0 457 }
michael@0 458
michael@0 459 if (text[0] == "/") {
michael@0 460 if (text.substr(0, 4) == "/../") {
michael@0 461 this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
michael@0 462 } else {
michael@0 463 this.uris.push(this._wrapURI(this.origin + text.substring(1)));
michael@0 464 }
michael@0 465 } else if (text.substr(0, 2) == "./") {
michael@0 466 this.uris.push(this._wrapURI(this.origin + text.substring(2)));
michael@0 467 } else if (text.substr(0, 4) == "http") {
michael@0 468 this.uris.push(this._wrapURI(text));
michael@0 469 } else {
michael@0 470 let origin = this.origin;
michael@0 471 let path = text;
michael@0 472
michael@0 473 while (path.substr(0, 3) == "../" && /^https?:\/\/.*?\/.*?\//.test(origin)) {
michael@0 474 let trimIdx = origin.substr(0, origin.length - 1).lastIndexOf("/") + 1;
michael@0 475 origin = origin.substr(0, trimIdx);
michael@0 476 path = path.substr(3);
michael@0 477 }
michael@0 478
michael@0 479 if (path.substr(0, 3) == "../") {
michael@0 480 this._addError(this.currentLine, "tooManyDotDotSlashes", this.currentLine);
michael@0 481 return;
michael@0 482 }
michael@0 483
michael@0 484 if (/^https?:\/\//.test(path)) {
michael@0 485 this.uris.push(this._wrapURI(path));
michael@0 486 return;
michael@0 487 }
michael@0 488 this.uris.push(this._wrapURI(origin + path));
michael@0 489 }
michael@0 490 },
michael@0 491
michael@0 492 parseFallbackLine: function OCIMP_parseFallbackLine() {
michael@0 493 let split = this.text.split(/\s+/);
michael@0 494 let origURI = this.text;
michael@0 495
michael@0 496 if (split.length != 2) {
michael@0 497 this._addError(this.currentLine, "fallbackUseSpaces", this.currentLine);
michael@0 498 return;
michael@0 499 }
michael@0 500
michael@0 501 let [ namespace, fallback ] = split;
michael@0 502
michael@0 503 if (namespace.indexOf("*") != -1) {
michael@0 504 this._addError(this.currentLine, "fallbackAsterisk2", this.currentLine);
michael@0 505 }
michael@0 506
michael@0 507 if (/\s/.test(namespace)) {
michael@0 508 this._addError(this.currentLine, "escapeSpaces", this.currentLine);
michael@0 509 namespace = namespace.replace(/\s/g, "%20")
michael@0 510 }
michael@0 511
michael@0 512 if (namespace.substr(0, 4) == "/../") {
michael@0 513 this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
michael@0 514 }
michael@0 515
michael@0 516 if (namespace.substr(0, 2) == "./") {
michael@0 517 namespace = this.origin + namespace.substring(2);
michael@0 518 }
michael@0 519
michael@0 520 if (namespace.substr(0, 4) != "http") {
michael@0 521 let origin = this.origin;
michael@0 522 let path = namespace;
michael@0 523
michael@0 524 while (path.substr(0, 3) == "../" && /^https?:\/\/.*?\/.*?\//.test(origin)) {
michael@0 525 let trimIdx = origin.substr(0, origin.length - 1).lastIndexOf("/") + 1;
michael@0 526 origin = origin.substr(0, trimIdx);
michael@0 527 path = path.substr(3);
michael@0 528 }
michael@0 529
michael@0 530 if (path.substr(0, 3) == "../") {
michael@0 531 this._addError(this.currentLine, "tooManyDotDotSlashes", this.currentLine);
michael@0 532 }
michael@0 533
michael@0 534 if (/^https?:\/\//.test(path)) {
michael@0 535 namespace = path;
michael@0 536 } else {
michael@0 537 if (path[0] == "/") {
michael@0 538 path = path.substring(1);
michael@0 539 }
michael@0 540 namespace = origin + path;
michael@0 541 }
michael@0 542 }
michael@0 543
michael@0 544 this.text = fallback;
michael@0 545 this.parseLine();
michael@0 546
michael@0 547 this.fallbacks.push({
michael@0 548 line: this.currentLine,
michael@0 549 original: origURI,
michael@0 550 namespace: namespace,
michael@0 551 fallback: fallback
michael@0 552 });
michael@0 553 },
michael@0 554
michael@0 555 parseSettingsLine: function OCIMP_parseSettingsLine() {
michael@0 556 let text = this.text;
michael@0 557
michael@0 558 if (this.settings.length == 1 || !/prefer-online|fast/.test(text)) {
michael@0 559 this._addError(this.currentLine, "settingsBadValue", this.currentLine);
michael@0 560 return;
michael@0 561 }
michael@0 562
michael@0 563 switch (text) {
michael@0 564 case "prefer-online":
michael@0 565 this.settings.push(this._wrapURI(text));
michael@0 566 break;
michael@0 567 case "fast":
michael@0 568 this.settings.push(this._wrapURI(text));
michael@0 569 break;
michael@0 570 }
michael@0 571 },
michael@0 572
michael@0 573 _wrapURI: function OCIMP__wrapURI(uri) {
michael@0 574 return {
michael@0 575 section: this.currSection,
michael@0 576 line: this.currentLine,
michael@0 577 uri: uri,
michael@0 578 original: this.text
michael@0 579 };
michael@0 580 },
michael@0 581
michael@0 582 _addError: function OCIMP__addError(line, l10nString, ...params) {
michael@0 583 let msg;
michael@0 584
michael@0 585 if (params) {
michael@0 586 msg = l10n.formatStringFromName(l10nString, params, params.length);
michael@0 587 } else {
michael@0 588 msg = l10n.GetStringFromName(l10nString);
michael@0 589 }
michael@0 590
michael@0 591 this.errors.push({
michael@0 592 line: line,
michael@0 593 msg: msg
michael@0 594 });
michael@0 595 },
michael@0 596
michael@0 597 _maybeUpdateSectionName: function OCIMP__maybeUpdateSectionName() {
michael@0 598 let text = this.text;
michael@0 599
michael@0 600 if (text == text.toUpperCase() && text.charAt(text.length - 1) == ":") {
michael@0 601 text = text.substr(0, text.length - 1);
michael@0 602
michael@0 603 switch (text) {
michael@0 604 case "CACHE":
michael@0 605 case "NETWORK":
michael@0 606 case "FALLBACK":
michael@0 607 case "SETTINGS":
michael@0 608 this.currSection = text;
michael@0 609 return true;
michael@0 610 default:
michael@0 611 this._addError(this.currentLine,
michael@0 612 "invalidSectionName", text, this.currentLine);
michael@0 613 return false;
michael@0 614 }
michael@0 615 }
michael@0 616 },
michael@0 617 };
michael@0 618
michael@0 619 XPCOMUtils.defineLazyGetter(this, "l10n", function() Services.strings
michael@0 620 .createBundle("chrome://browser/locale/devtools/appcacheutils.properties"));
michael@0 621
michael@0 622 XPCOMUtils.defineLazyGetter(this, "appcacheservice", function() {
michael@0 623 return Cc["@mozilla.org/network/application-cache-service;1"]
michael@0 624 .getService(Ci.nsIApplicationCacheService);
michael@0 625
michael@0 626 });
michael@0 627
michael@0 628 XPCOMUtils.defineLazyGetter(this, "_DOMParser", function() {
michael@0 629 return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
michael@0 630 });

mercurial