/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */

requestLongerTimeout(2);

const gHttpTestRoot = "http://example.com/browser/dom/base/test/";

/**
 * Enable local telemetry recording for the duration of the tests.
 */
var gOldContentCanRecord = false;
var gOldParentCanRecord = false;
add_task(async function test_initialize() {
  let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
    Ci.nsITelemetry
  );
  gOldParentCanRecord = Telemetry.canRecordExtended;
  Telemetry.canRecordExtended = true;

  await SpecialPowers.pushPrefEnv({
    set: [
      // Because canRecordExtended is a per-process variable, we need to make sure
      // that all of the pages load in the same content process. Limit the number
      // of content processes to at most 1 (or 0 if e10s is off entirely).
      ["dom.ipc.processCount", 1],
      ["layout.css.use-counters.enabled", true],
      ["layout.css.use-counters-unimplemented.enabled", true],
    ],
  });

  gOldContentCanRecord = await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [],
    function() {
      let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
        Ci.nsITelemetry
      );
      let old = telemetry.canRecordExtended;
      telemetry.canRecordExtended = true;
      return old;
    }
  );
  info("canRecord for content: " + gOldContentCanRecord);
});

add_task(async function() {
  // Check that use counters are incremented by SVGs loaded directly in iframes.
  await check_use_counter_iframe(
    "file_use_counter_svg_getElementById.svg",
    "SVGSVGELEMENT_GETELEMENTBYID"
  );
  await check_use_counter_iframe(
    "file_use_counter_svg_currentScale.svg",
    "SVGSVGELEMENT_CURRENTSCALE_getter"
  );
  await check_use_counter_iframe(
    "file_use_counter_svg_currentScale.svg",
    "SVGSVGELEMENT_CURRENTSCALE_setter"
  );

  // Check for longhands.
  await check_use_counter_iframe(
    "file_use_counter_style.html",
    "CSS_PROPERTY_BackgroundImage"
  );

  // Check for shorthands.
  await check_use_counter_iframe(
    "file_use_counter_style.html",
    "CSS_PROPERTY_Padding"
  );

  // Check for aliases.
  await check_use_counter_iframe(
    "file_use_counter_style.html",
    "CSS_PROPERTY_MozTransform"
  );

  // Check for counted unknown properties.
  await check_use_counter_iframe(
    "file_use_counter_style.html",
    "CSS_PROPERTY_WebkitPaddingStart"
  );

  // Check that even loads from the imglib cache update use counters.  The
  // images should still be there, because we just loaded them in the last
  // set of tests.  But we won't get updated counts for the document
  // counters, because we won't be re-parsing the SVG documents.
  await check_use_counter_iframe(
    "file_use_counter_svg_getElementById.svg",
    "SVGSVGELEMENT_GETELEMENTBYID",
    false
  );
  await check_use_counter_iframe(
    "file_use_counter_svg_currentScale.svg",
    "SVGSVGELEMENT_CURRENTSCALE_getter",
    false
  );
  await check_use_counter_iframe(
    "file_use_counter_svg_currentScale.svg",
    "SVGSVGELEMENT_CURRENTSCALE_setter",
    false
  );

  // Check that use counters are incremented by SVGs loaded as images.
  // Note that SVG images are not permitted to execute script, so we can only
  // check for properties here.
  await check_use_counter_img(
    "file_use_counter_svg_getElementById.svg",
    "CSS_PROPERTY_Fill"
  );
  await check_use_counter_img(
    "file_use_counter_svg_currentScale.svg",
    "CSS_PROPERTY_Fill"
  );

  // Check that use counters are incremented by directly loading SVGs
  // that reference patterns defined in another SVG file.
  await check_use_counter_direct(
    "file_use_counter_svg_fill_pattern.svg",
    "CSS_PROPERTY_FillOpacity",
    /*xfail=*/ true
  );

  // Check that use counters are incremented by directly loading SVGs
  // that reference patterns defined in the same file or in data: URLs.
  await check_use_counter_direct(
    "file_use_counter_svg_fill_pattern_internal.svg",
    "CSS_PROPERTY_FillOpacity"
  );

  // data: URLs don't correctly propagate to their referring document yet.
  //yield check_use_counter_direct("file_use_counter_svg_fill_pattern_data.svg",
  //                               "PROPERTY_FILL_OPACITY");
});

add_task(async function() {
  let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
    Ci.nsITelemetry
  );
  Telemetry.canRecordExtended = gOldParentCanRecord;

  await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [{ oldCanRecord: gOldContentCanRecord }],
    async function(arg) {
      await new Promise(resolve => {
        let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
          Ci.nsITelemetry
        );
        telemetry.canRecordExtended = arg.oldCanRecord;
        resolve();
      });
    }
  );
});

function grabHistogramsFromContent(use_counter_middlefix, page_before = null) {
  let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
    Ci.nsITelemetry
  );
  let gather = () => {
    let snapshots;
    if (Services.appinfo.browserTabsRemoteAutostart) {
      snapshots = telemetry.getSnapshotForHistograms("main", false).content;
    } else {
      snapshots = telemetry.getSnapshotForHistograms("main", false).parent;
    }
    let checkGet = probe => {
      return snapshots[probe] ? snapshots[probe].sum : 0;
    };
    return [
      checkGet("USE_COUNTER2_" + use_counter_middlefix + "_PAGE"),
      checkGet("USE_COUNTER2_" + use_counter_middlefix + "_DOCUMENT"),
      checkGet("CONTENT_DOCUMENTS_DESTROYED"),
      checkGet("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED"),
    ];
  };
  return BrowserTestUtils.waitForCondition(() => {
    return page_before != gather()[0];
  }).then(gather, gather);
}

var check_use_counter_iframe = async function(
  file,
  use_counter_middlefix,
  check_documents = true
) {
  info("checking " + file + " with histogram " + use_counter_middlefix);

  let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
  gBrowser.selectedTab = newTab;
  newTab.linkedBrowser.stop();

  // Hold on to the current values of the telemetry histograms we're
  // interested in.
  let [
    histogram_page_before,
    histogram_document_before,
    histogram_docs_before,
    histogram_toplevel_docs_before,
  ] = await grabHistogramsFromContent(use_counter_middlefix);

  BrowserTestUtils.loadURI(
    gBrowser.selectedBrowser,
    gHttpTestRoot + "file_use_counter_outer.html"
  );
  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);

  // Inject our desired file into the iframe of the newly-loaded page.
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [{ file }], function(
    opts
  ) {
    let iframe = content.document.getElementById("content");
    iframe.src = opts.file;

    return new Promise(resolve => {
      let listener = event => {
        event.target.removeEventListener("load", listener, true);
        resolve();
      };
      iframe.addEventListener("load", listener, true);
    });
  });

  // Tear down the page.
  let tabClosed = BrowserTestUtils.waitForTabClosing(newTab);
  gBrowser.removeTab(newTab);
  await tabClosed;

  // Grab histograms again and compare.
  let [
    histogram_page_after,
    histogram_document_after,
    histogram_docs_after,
    histogram_toplevel_docs_after,
  ] = await grabHistogramsFromContent(
    use_counter_middlefix,
    histogram_page_before
  );

  is(
    histogram_page_after,
    histogram_page_before + 1,
    "page counts for " + use_counter_middlefix + " after are correct"
  );
  ok(
    histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
    "top level document counts are correct"
  );
  if (check_documents) {
    is(
      histogram_document_after,
      histogram_document_before + 1,
      "document counts for " + use_counter_middlefix + " after are correct"
    );
  }
};

var check_use_counter_img = async function(file, use_counter_middlefix) {
  info(
    "checking " + file + " as image with histogram " + use_counter_middlefix
  );

  let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
  gBrowser.selectedTab = newTab;
  newTab.linkedBrowser.stop();

  // Hold on to the current values of the telemetry histograms we're
  // interested in.
  let [
    histogram_page_before,
    histogram_document_before,
    histogram_docs_before,
    histogram_toplevel_docs_before,
  ] = await grabHistogramsFromContent(use_counter_middlefix);

  BrowserTestUtils.loadURI(
    gBrowser.selectedBrowser,
    gHttpTestRoot + "file_use_counter_outer.html"
  );
  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);

  // Inject our desired file into the img of the newly-loaded page.
  await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [{ file }],
    async function(opts) {
      let img = content.document.getElementById("display");
      img.src = opts.file;

      return new Promise(resolve => {
        let listener = event => {
          img.removeEventListener("load", listener, true);
          resolve();
        };
        img.addEventListener("load", listener, true);
      });
    }
  );

  // Tear down the page.
  let tabClosed = BrowserTestUtils.waitForTabClosing(newTab);
  gBrowser.removeTab(newTab);
  await tabClosed;

  // Grab histograms again and compare.
  let [
    histogram_page_after,
    histogram_document_after,
    histogram_docs_after,
    histogram_toplevel_docs_after,
  ] = await grabHistogramsFromContent(
    use_counter_middlefix,
    histogram_page_before
  );
  is(
    histogram_page_after,
    histogram_page_before + 1,
    "page counts for " + use_counter_middlefix + " after are correct"
  );
  is(
    histogram_document_after,
    histogram_document_before + 1,
    "document counts for " + use_counter_middlefix + " after are correct"
  );
  ok(
    histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
    "top level document counts are correct"
  );
  // 2 documents: one for the outer html page containing the <img> element, and
  // one for the SVG image itself.
  ok(
    histogram_docs_after >= histogram_docs_before + 2,
    "document counts are correct"
  );
};

var check_use_counter_direct = async function(
  file,
  use_counter_middlefix,
  xfail = false
) {
  info("checking " + file + " with histogram " + use_counter_middlefix);

  let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
  gBrowser.selectedTab = newTab;
  newTab.linkedBrowser.stop();

  // Hold on to the current values of the telemetry histograms we're
  // interested in.
  let [
    histogram_page_before,
    histogram_document_before,
    histogram_docs_before,
    histogram_toplevel_docs_before,
  ] = await grabHistogramsFromContent(use_counter_middlefix);

  BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gHttpTestRoot + file);
  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);

  // Tear down the page.
  let tabClosed = BrowserTestUtils.waitForTabClosing(newTab);
  gBrowser.removeTab(newTab);
  await tabClosed;

  // Grab histograms again and compare.
  let [
    histogram_page_after,
    histogram_document_after,
    histogram_docs_after,
    histogram_toplevel_docs_after,
  ] = await grabHistogramsFromContent(
    use_counter_middlefix,
    histogram_page_before
  );
  if (!xfail) {
    is(
      histogram_page_after,
      histogram_page_before + 1,
      "page counts for " + use_counter_middlefix + " after are correct"
    );
    is(
      histogram_document_after,
      histogram_document_before + 1,
      "document counts for " + use_counter_middlefix + " after are correct"
    );
  }
  ok(
    histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
    "top level document counts are correct"
  );
  ok(
    histogram_docs_after >= histogram_docs_before + 1,
    "document counts are correct"
  );
};
