Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/devtools/server/actors/resources/storage/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 19 kB image not shown  

Quelle  cookies.js   Sprache: JAVA

 
/* 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/. */


"use strict";

const {
  BaseStorageActor,
  DEFAULT_VALUE,
  SEPARATOR_GUID,
} = require("resource://devtools/server/actors/resources/storage/index.js");
const {
  LongStringActor,
} = require("resource://devtools/server/actors/string.js");

// "Lax", "Strict" and "None" are special values of the SameSite property
// that should not be translated.
const COOKIE_SAMESITE = {
  LAX: "Lax",
  STRICT: "Strict",
  NONE: "None",
};

// MAX_COOKIE_EXPIRY should be 2^63-1, but JavaScript can't handle that
// precision.
const MAX_COOKIE_EXPIRY = Math.pow(2, 62);

/**
 * General helpers
 */

function trimHttpHttpsPort(url) {
  const match = url.match(/(.+):\d+$/);

  if (match) {
    url = match[1];
  }
  if (url.startsWith("http://")) {
    return url.substr(7);
  }
  if (url.startsWith("https://")) {
    return url.substr(8);
  }
  return url;
}

class CookiesStorageActor extends BaseStorageActor {
  constructor(storageActor) {
    super(storageActor, "cookies");

    Services.obs.addObserver(this"cookie-changed");
    Services.obs.addObserver(this"private-cookie-changed");
  }

  destroy() {
    Services.obs.removeObserver(this"cookie-changed");
    Services.obs.removeObserver(this"private-cookie-changed");

    super.destroy();
  }

  static UNIQUE_KEY_INDEXES = { name: 0, host: 1, path: 2, partitionKey: 3 };

  #getCookieUniqueKey(cookie) {
    return (
      cookie.name +
      SEPARATOR_GUID +
      cookie.host +
      SEPARATOR_GUID +
      cookie.path +
      SEPARATOR_GUID +
      cookie.originAttributes.partitionKey
    );
  }

  populateStoresForHost(host) {
    this.hostVsStores.set(host, new Map());
    const cookies = this.getCookiesFromHost(host);
    for (const cookie of cookies) {
      if (this.isCookieAtHost(cookie, host)) {
        const uniqueKey = this.#getCookieUniqueKey(cookie);
        this.hostVsStores.get(host).set(uniqueKey, cookie);
      }
    }
  }

  getOriginAttributesFromHost(host) {
    const win = this.storageActor.getWindowFromHost(host);
    let originAttributes;
    if (win) {
      originAttributes =
        win.document.effectiveStoragePrincipal.originAttributes;
    } else {
      // If we can't find the window by host, fallback to the top window
      // origin attributes.
      originAttributes =
        this.storageActor.document?.effectiveStoragePrincipal.originAttributes;
    }

    return originAttributes;
  }

  getCookiesFromHost(host) {
    // Gather originAttributes list from host
    const hostBrowsingContexts =
      this.storageActor.getBrowsingContextsFromHost(host);
    const originAttributesList = [];
    if (hostBrowsingContexts.length) {
      // Since we need to get all browsing contexts to get their originAttributes,
      // we might get "duplicated" objects, which would translate into having the same
      // cookies multiple times.
      // To avoid that, we compute a unique key from originAttributes to only have unique ones.
      const uniqueOriginAttributes = new Set();
      for (const bc of hostBrowsingContexts) {
        const { originAttributes } =
          bc.currentWindowGlobal.documentStoragePrincipal;
        // The object is small, seems fine to stringify it to compute a unique key
        const oaKey = JSON.stringify(originAttributes);
        if (!uniqueOriginAttributes.has(oaKey)) {
          originAttributesList.push(originAttributes);
          uniqueOriginAttributes.add(oaKey);
        }

        // A document might have an empty partitionKey in browsingContext.currentWindowGlobal.documentStoragePrincipal.originAttributes,
        // (e.g. a top level document), but still have partitioned cookies, in a different jar
        // (in CHIPS, for top level document that's first-party partitioned cookies).
        // In order to retrieve those, we create a new originAttribute with the
        // partitionKey from the window global cookie jar partitionKey
        if (
          bc.currentWindowGlobal.cookieJarSettings.partitionKey !==
          originAttributes.partitionKey
        ) {
          const derivedOriginAttributes = {
            ...originAttributes,
            partitionKey: bc.currentWindowGlobal.cookieJarSettings.partitionKey,
          };
          const derivedOaKey = JSON.stringify(derivedOriginAttributes);
          if (!uniqueOriginAttributes.has(derivedOaKey)) {
            originAttributesList.push(derivedOriginAttributes);
            uniqueOriginAttributes.add(derivedOaKey);
          }
        }
      }
    } else {
      // In case of WebExtension or BrowserToolbox, we may pass privileged hosts
      // which don't relate to any particular window. getOriginAttributesFromHost will
      // fallback to the top window origin attributes.
      originAttributesList.push(this.getOriginAttributesFromHost(host));
    }

    // Local files have no host.
    if (host.startsWith("file:///")) {
      host = "";
    }

    host = trimHttpHttpsPort(host);

    // Retrieve cookies all the passed originAttributes so we can get cookies from all jars
    let cookies;
    for (const originAttributes of originAttributesList) {
      const oaCookies = Services.cookies.getCookiesFromHost(
        host,
        originAttributes
      );
      if (!cookies) {
        cookies = oaCookies;
      } else {
        cookies.push(...oaCookies);
      }
    }
    return cookies || [];
  }

  /**
   * Given a cookie object, figure out all the matching hosts from the page that
   * the cookie belong to.
   */

  getMatchingHosts(cookies) {
    if (!cookies) {
      return [];
    }
    if (!cookies.length) {
      cookies = [cookies];
    }
    const hosts = new Set();
    for (const host of this.hosts) {
      for (const cookie of cookies) {
        if (this.isCookieAtHost(cookie, host)) {
          hosts.add(host);
        }
      }
    }
    return [...hosts];
  }

  /**
   * Given a cookie object and a host, figure out if the cookie is valid for
   * that host.
   */

  isCookieAtHost(cookie, host) {
    if (cookie.host == null) {
      return host == null;
    }

    host = trimHttpHttpsPort(host);

    if (cookie.host.startsWith(".")) {
      return ("." + host).endsWith(cookie.host);
    }
    if (cookie.host === "") {
      return host.startsWith("file://" + cookie.path);
    }

    return cookie.host == host;
  }

  toStoreObject(cookie) {
    if (!cookie) {
      return null;
    }

    const obj = {
      uniqueKey: this.#getCookieUniqueKey(cookie),
      name: cookie.name,
      host: cookie.host || "",
      path: cookie.path || "",

      // because expires is in seconds
      expires: (cookie.expires || 0) * 1000,

      // because creationTime is in micro seconds
      creationTime: cookie.creationTime / 1000,

      size: cookie.name.length + (cookie.value || "").length,

      // - do -
      lastAccessed: cookie.lastAccessed / 1000,
      value: new LongStringActor(this.conn, cookie.value || ""),
      hostOnly: !cookie.isDomain,
      isSecure: cookie.isSecure,
      isHttpOnly: cookie.isHttpOnly,
      sameSite: this.getSameSiteStringFromCookie(cookie),
    };

    if (cookie.isPartitioned) {
      const rawPartitionKey = cookie.originAttributes.partitionKey;
      // We need to return the site derived from the partition key.
      // rawPartitionKey format should be like "(<scheme>,<baseDomain>,[port],[ancestorbit])"
      // see https://searchfox.org/mozilla-central/rev/23efe2c8c5b3a3182d449211ff9036fb34fe0219/caps/OriginAttributes.h#132-138
      // We can ignore the `ancestorbit` part.
      const [scheme, baseDomain, port] = rawPartitionKey
        .replace(/(?<openingparen>^\()|(?<closingparen>\)$)/g, "")
        .split(",");
      const partitionKey = `${scheme}://${baseDomain}${
        port !== undefined && /^\d+$/.test(port) ? ":" + port : ""
      }`;
      obj.partitionKey = partitionKey;
    }

    return obj;
  }

  getSameSiteStringFromCookie(cookie) {
    switch (cookie.sameSite) {
      case cookie.SAMESITE_LAX:
        return COOKIE_SAMESITE.LAX;
      case cookie.SAMESITE_STRICT:
        return COOKIE_SAMESITE.STRICT;
    }
    // cookie.SAMESITE_NONE
    return COOKIE_SAMESITE.NONE;
  }

  /**
   * Notification observer for "cookie-change".
   *
   * @param {(nsICookie|nsICookie[])} cookie - Cookie/s changed. Depending on the action
   * this is either null, a single cookie or an array of cookies.
   * @param {nsICookieNotification_Action} action - The cookie operation, see
   * nsICookieNotification for details.
   **/

  onCookieChanged(cookie, action) {
    const {
      COOKIE_ADDED,
      COOKIE_CHANGED,
      COOKIE_DELETED,
      COOKIES_BATCH_DELETED,
      ALL_COOKIES_CLEARED,
    } = Ci.nsICookieNotification;

    const hosts = this.getMatchingHosts(cookie);
    if (!hosts.length) {
      return;
    }

    const data = {};

    switch (action) {
      case COOKIE_ADDED:
      case COOKIE_CHANGED:
        if (hosts.length) {
          for (const host of hosts) {
            const uniqueKey = this.#getCookieUniqueKey(cookie);
            this.hostVsStores.get(host).set(uniqueKey, cookie);
            data[host] = [uniqueKey];
          }
          const actionStr = action == COOKIE_ADDED ? "added" : "changed";
          this.storageActor.update(actionStr, "cookies", data);
        }
        break;

      case COOKIE_DELETED:
        if (hosts.length) {
          for (const host of hosts) {
            const uniqueKey = this.#getCookieUniqueKey(cookie);
            this.hostVsStores.get(host).delete(uniqueKey);
            data[host] = [uniqueKey];
          }
          this.storageActor.update("deleted""cookies", data);
        }
        break;

      case COOKIES_BATCH_DELETED:
        if (hosts.length) {
          for (const host of hosts) {
            const stores = [];
            // For COOKIES_BATCH_DELETED cookie is an array.
            for (const batchCookie of cookie) {
              const uniqueKey = this.#getCookieUniqueKey(batchCookie);
              this.hostVsStores.get(host).delete(uniqueKey);
              stores.push(uniqueKey);
            }
            data[host] = stores;
          }
          this.storageActor.update("deleted""cookies", data);
        }
        break;

      case ALL_COOKIES_CLEARED:
        if (hosts.length) {
          for (const host of hosts) {
            data[host] = [];
          }
          this.storageActor.update("cleared""cookies", data);
        }
        break;
    }
  }

  async getFields() {
    const fields = [
      { name: "uniqueKey", editable: falseprivatetrue },
      { name: "name", editable: true, hidden: false },
      { name: "value", editable: true, hidden: false },
      { name: "host", editable: true, hidden: false },
      { name: "path", editable: true, hidden: false },
      { name: "expires", editable: true, hidden: false },
      { name: "size", editable: false, hidden: false },
      { name: "isHttpOnly", editable: true, hidden: false },
      { name: "isSecure", editable: true, hidden: false },
      { name: "sameSite", editable: false, hidden: false },
      { name: "lastAccessed", editable: false, hidden: false },
      { name: "creationTime", editable: false, hidden: true },
      { name: "hostOnly", editable: false, hidden: true },
    ];

    if (Services.prefs.getBoolPref("network.cookie.CHIPS.enabled"false)) {
      fields.push({ name: "partitionKey", editable: false, hidden: false });
    }

    return fields;
  }

  /**
   * Pass the editItem command from the content to the chrome process.
   *
   * @param {Object} data
   *        See editCookie() for format details.
   */

  async editItem(data) {
    this.editCookie(data);
  }

  async addItem(guid, host) {
    const window = this.storageActor.getWindowFromHost(host);
    const principal = window.document.effectiveStoragePrincipal;
    this.addCookie(guid, principal);
  }

  async removeItem(host, uniqueKey) {
    if (uniqueKey === undefined) {
      return;
    }
    this._removeCookies(host, { uniqueKey });
  }

  async removeAll(host, domain) {
    this._removeCookies(host, { domain });
  }

  async removeAllSessionCookies(host, domain) {
    this._removeCookies(host, { domain, session: true });
  }

  addCookie(guid, principal) {
    // Set expiry time for cookie 1 day into the future
    // NOTE: Services.cookies.add expects the time in seconds.
    const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
    const time = Math.floor(Date.now() / 1000);
    const expiry = time + ONE_DAY_IN_SECONDS;

    // principal throws an error when we try to access principal.host if it
    // does not exist (which happens at about: pages).
    // We check for asciiHost instead, which is always present, and has a
    // value of "" when the host is not available.
    const domain = principal.asciiHost ? principal.host : principal.baseDomain;

    Services.cookies.add(
      domain,
      "/",
      guid, // name
      DEFAULT_VALUE, // value
      false// isSecure
      false// isHttpOnly,
      false// isSession,
      expiry, // expires,
      principal.originAttributes, // originAttributes
      Ci.nsICookie.SAMESITE_LAX, // sameSite
      principal.scheme === "https" // schemeMap
        ? Ci.nsICookie.SCHEME_HTTPS
        : Ci.nsICookie.SCHEME_HTTP
    );
  }

  /**
   * Apply the results of a cookie edit.
   *
   * @param {Object} data
   *        An object in the following format:
   *        {
   *          host: "http://www.mozilla.org",
   *          field: "value",
   *          editCookie: "name",
   *          oldValue: "%7BHello%7D",
   *          newValue: "%7BHelloo%7D",
   *          items: {
   *            name: "optimizelyBuckets",
   *            path: "/",
   *            host: ".mozilla.org",
   *            expires: "Mon, 02 Jun 2025 12:37:37 GMT",
   *            creationTime: "Tue, 18 Nov 2014 16:21:18 GMT",
   *            lastAccessed: "Wed, 17 Feb 2016 10:06:23 GMT",
   *            value: "%7BHelloo%7D",
   *            isDomain: "true",
   *            isSecure: "false",
   *            isHttpOnly: "false"
   *          }
   *        }
   */

  // eslint-disable-next-line complexity
  editCookie(data) {
    let { field, oldValue, newValue } = data;
    const origName = field === "name" ? oldValue : data.items.name;
    const origHost = field === "host" ? oldValue : data.items.host;
    const origPath = field === "path" ? oldValue : data.items.path;
    // We can't use `data.items.partitionKey` as it's the formatted value and we need
    // to check against the "raw" one. Its value can't be modified, so we don't need to
    // look into oldValue.
    const partitionKey =
      data.items.uniqueKey.split(SEPARATOR_GUID)[
        CookiesStorageActor.UNIQUE_KEY_INDEXES.partitionKey
      ];
    let cookie = null;

    const cookies = this.getCookiesFromHost(data.host);
    for (const nsiCookie of cookies) {
      if (
        nsiCookie.name === origName &&
        nsiCookie.host === origHost &&
        nsiCookie.path === origPath &&
        nsiCookie.originAttributes.partitionKey === partitionKey
      ) {
        cookie = {
          host: nsiCookie.host,
          path: nsiCookie.path,
          name: nsiCookie.name,
          value: nsiCookie.value,
          isSecure: nsiCookie.isSecure,
          isHttpOnly: nsiCookie.isHttpOnly,
          isSession: nsiCookie.isSession,
          expires: nsiCookie.expires,
          originAttributes: nsiCookie.originAttributes,
          schemeMap: nsiCookie.schemeMap,
          isPartitioned: nsiCookie.isPartitioned,
        };
        break;
      }
    }

    if (!cookie) {
      return;
    }

    // If the date is expired set it for 10 seconds in the future.
    const now = new Date();
    if (!cookie.isSession && cookie.expires * 1000 <= now) {
      const tenSecondsFromNow = (now.getTime() + 10 * 1000) / 1000;

      cookie.expires = tenSecondsFromNow;
    }

    switch (field) {
      case "isSecure":
      case "isHttpOnly":
      case "isSession":
        newValue = newValue === "true";
        break;

      case "expires":
        newValue = Date.parse(newValue) / 1000;

        if (isNaN(newValue)) {
          newValue = MAX_COOKIE_EXPIRY;
        }
        break;

      case "host":
      case "name":
      case "path":
        // Remove the edited cookie.
        Services.cookies.remove(
          origHost,
          origName,
          origPath,
          cookie.originAttributes
        );
        break;
    }

    // Apply changes.
    cookie[field] = newValue;

    // cookie.isSession is not always set correctly on session cookies so we
    // need to trust cookie.expires instead.
    cookie.isSession = !cookie.expires;

    // Add the edited cookie.
    Services.cookies.add(
      cookie.host,
      cookie.path,
      cookie.name,
      cookie.value,
      cookie.isSecure,
      cookie.isHttpOnly,
      cookie.isSession,
      cookie.isSession ? MAX_COOKIE_EXPIRY : cookie.expires,
      cookie.originAttributes,
      cookie.sameSite,
      cookie.schemeMap,
      cookie.isPartitioned
    );
  }

  _removeCookies(host, opts = {}) {
    // We use a uniqueId to emulate compound keys for cookies. We need to
    // extract the cookie name to remove the correct cookie.
    if (opts.uniqueKey) {
      const uniqueKeyParts = opts.uniqueKey.split(SEPARATOR_GUID);

      opts.name = uniqueKeyParts[CookiesStorageActor.UNIQUE_KEY_INDEXES.name];
      opts.path = uniqueKeyParts[CookiesStorageActor.UNIQUE_KEY_INDEXES.path];
      opts.partitionKey =
        uniqueKeyParts[CookiesStorageActor.UNIQUE_KEY_INDEXES.partitionKey] ||
        "";
    }

    const cookies = this.getCookiesFromHost(host);
    for (const cookie of cookies) {
      if (
        this.isCookieAtHost(cookie, host) &&
        (!opts.name || cookie.name === opts.name) &&
        (!opts.domain || cookie.host === opts.domain) &&
        (!opts.path || cookie.path === opts.path) &&
        (!opts.uniqueKey ||
          // make sure to pick the cookie from the correct jar
          cookie.originAttributes.partitionKey === opts.partitionKey) &&
        // for session cookie removal
        (!opts.session || (!cookie.expires && !cookie.maxAge))
      ) {
        Services.cookies.remove(
          cookie.host,
          cookie.name,
          cookie.path,
          cookie.originAttributes
        );
      }
    }
  }

  removeCookie(host, name, originAttributes) {
    if (name !== undefined) {
      this._removeCookies(host, { name, originAttributes });
    }
  }

  removeAllCookies(host, domain, originAttributes) {
    this._removeCookies(host, { domain, originAttributes });
  }

  observe(subject, topic) {
    if (
      !subject ||
      (topic != "cookie-changed" && topic != "private-cookie-changed") ||
      !this.storageActor ||
      !this.storageActor.windows
    ) {
      return;
    }

    const notification = subject.QueryInterface(Ci.nsICookieNotification);
    let cookie;
    if (notification.action == Ci.nsICookieNotification.COOKIES_BATCH_DELETED) {
      // Extract the batch deleted cookies from nsIArray.
      const cookiesNoInterface =
        notification.batchDeletedCookies.QueryInterface(Ci.nsIArray);
      cookie = [];
      for (let i = 0; i < cookiesNoInterface.length; i++) {
        cookie.push(cookiesNoInterface.queryElementAt(i, Ci.nsICookie));
      }
    } else if (notification.cookie) {
      // Otherwise, get the single cookie affected by the operation.
      cookie = notification.cookie.QueryInterface(Ci.nsICookie);
    }

    this.onCookieChanged(cookie, notification.action);
  }
}
exports.CookiesStorageActor = CookiesStorageActor;

Messung V0.5
C=89 H=84 G=86

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.