forked from mirrors/gecko-dev
		
	When the array is too large, we should avoid using 'apply' as it may result in an 'too many arguments provided for a function call' error Differential Revision: https://phabricator.services.mozilla.com/D184200
		
			
				
	
	
		
			141 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
const lazy = {};
 | 
						|
ChromeUtils.defineESModuleGetters(lazy, {
 | 
						|
  RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
const COLLECTION_NAME = "tracking-protection-lists";
 | 
						|
 | 
						|
// SafeBrowsing protocol parameters.
 | 
						|
export const SBRS_UPDATE_MINIMUM_DELAY = 21600; // Minimum delay before polling again in seconds
 | 
						|
 | 
						|
export function UrlClassifierRemoteSettingsService() {}
 | 
						|
UrlClassifierRemoteSettingsService.prototype = {
 | 
						|
  classID: Components.ID("{1980624c-c50b-4b46-a91c-dfaba7792706}"),
 | 
						|
  QueryInterface: ChromeUtils.generateQI([
 | 
						|
    "nsIUrlClassifierRemoteSettingsService",
 | 
						|
  ]),
 | 
						|
 | 
						|
  _initialized: false,
 | 
						|
 | 
						|
  // Entries that are retrieved from RemoteSettings.get(). keyed by the table name.
 | 
						|
  _entries: {},
 | 
						|
 | 
						|
  async lazyInit() {
 | 
						|
    if (this._initialized) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let rs = lazy.RemoteSettings(COLLECTION_NAME);
 | 
						|
    // Bug 1750191: Notify listmanager to trigger an update instead
 | 
						|
    // of polling data periodically.
 | 
						|
    rs.on("sync", async event => {
 | 
						|
      let {
 | 
						|
        data: { current },
 | 
						|
      } = event;
 | 
						|
      this._onUpdateEntries(current);
 | 
						|
    });
 | 
						|
 | 
						|
    this._initialized = true;
 | 
						|
 | 
						|
    let entries;
 | 
						|
    try {
 | 
						|
      entries = await rs.get();
 | 
						|
    } catch (e) {}
 | 
						|
 | 
						|
    this._onUpdateEntries(entries || []);
 | 
						|
  },
 | 
						|
 | 
						|
  _onUpdateEntries(aEntries) {
 | 
						|
    aEntries.map(entry => {
 | 
						|
      this._entries[entry.Name] = entry;
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  // Parse the update request. See UrlClassifierListManager.jsm makeUpdateRequest
 | 
						|
  // for more details about how we build the update request.
 | 
						|
  //
 | 
						|
  // @param aRequest the request payload of the update request
 | 
						|
  // @return array The array of requested tables. Each item in the array is composed
 | 
						|
  //               with [table name, chunk numner]
 | 
						|
  _parseRequest(aRequest) {
 | 
						|
    let lines = aRequest.split("\n");
 | 
						|
    let requests = [];
 | 
						|
    for (let line of lines) {
 | 
						|
      let fields = line.split(";");
 | 
						|
      let chunkNum = fields[1]?.match(/(?<=a:).*/);
 | 
						|
      requests.push([fields[0], chunkNum]);
 | 
						|
    }
 | 
						|
    return requests;
 | 
						|
  },
 | 
						|
 | 
						|
  async _getLists(aRequest, aListener) {
 | 
						|
    await this.lazyInit();
 | 
						|
 | 
						|
    let rs = lazy.RemoteSettings(COLLECTION_NAME);
 | 
						|
    let payload = "n:" + SBRS_UPDATE_MINIMUM_DELAY + "\n";
 | 
						|
 | 
						|
    let requests = this._parseRequest(aRequest);
 | 
						|
    for (let request of requests) {
 | 
						|
      let [reqTableName, reqChunkNum] = request;
 | 
						|
      let entry = this._entries[reqTableName];
 | 
						|
      if (!entry?.attachment) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      // If the request version is the same as what we have in Remote Settings,
 | 
						|
      // we are up-to-date now.
 | 
						|
      if (entry.Version == reqChunkNum) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      let downloadError = false;
 | 
						|
      try {
 | 
						|
        // SafeBrowsing maintains its own files, so we can remove the downloaded
 | 
						|
        // files after SafeBrowsing processes the data.
 | 
						|
        let buffer = await rs.attachments.downloadAsBytes(entry);
 | 
						|
        let bytes = new Uint8Array(buffer);
 | 
						|
        let strData = "";
 | 
						|
        for (let i = 0; i < bytes.length; i++) {
 | 
						|
          strData += String.fromCharCode(bytes[i]);
 | 
						|
        }
 | 
						|
 | 
						|
        // Construct the payload
 | 
						|
        payload += "i:" + reqTableName + "\n";
 | 
						|
        payload += strData;
 | 
						|
      } catch (e) {
 | 
						|
        downloadError = true;
 | 
						|
      }
 | 
						|
 | 
						|
      if (downloadError) {
 | 
						|
        try {
 | 
						|
          aListener.onStartRequest(null);
 | 
						|
          aListener.onStopRequest(null, Cr.NS_ERROR_FAILURE);
 | 
						|
        } catch (e) {}
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Send the update response over stream listener interface
 | 
						|
    let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
 | 
						|
      Ci.nsIStringInputStream
 | 
						|
    );
 | 
						|
    stream.setData(payload, payload.length);
 | 
						|
 | 
						|
    aListener.onStartRequest(null);
 | 
						|
    aListener.onDataAvailable(null, stream, 0, payload.length);
 | 
						|
    aListener.onStopRequest(null, Cr.NS_OK);
 | 
						|
  },
 | 
						|
 | 
						|
  fetchList(aPayload, aListener) {
 | 
						|
    this._getLists(aPayload, aListener);
 | 
						|
  },
 | 
						|
 | 
						|
  clear() {
 | 
						|
    this._initialized = false;
 | 
						|
    this._entries = {};
 | 
						|
  },
 | 
						|
};
 |