forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			381 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			381 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* Any copyright is dedicated to the Public Domain.
 | 
						|
   http://creativecommons.org/publicdomain/zero/1.0/ */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
requestLongerTimeout(2);
 | 
						|
 | 
						|
const { PermissionUI } = ChromeUtils.importESModule(
 | 
						|
  "resource:///modules/PermissionUI.sys.mjs"
 | 
						|
);
 | 
						|
 | 
						|
const { PermissionTestUtils } = ChromeUtils.importESModule(
 | 
						|
  "resource://testing-common/PermissionTestUtils.sys.mjs"
 | 
						|
);
 | 
						|
 | 
						|
const CP = Cc["@mozilla.org/content-pref/service;1"].getService(
 | 
						|
  Ci.nsIContentPrefService2
 | 
						|
);
 | 
						|
 | 
						|
const EXAMPLE_PAGE_URL = "https://example.com";
 | 
						|
const EXAMPLE_PAGE_URI = Services.io.newURI(EXAMPLE_PAGE_URL);
 | 
						|
const EXAMPLE_PAGE_PRINCIPAL =
 | 
						|
  Services.scriptSecurityManager.createContentPrincipal(EXAMPLE_PAGE_URI, {});
 | 
						|
const GEO_CONTENT_PREF_KEY = "permissions.geoLocation.lastAccess";
 | 
						|
const POLL_INTERVAL_FALSE_STATE = 50;
 | 
						|
 | 
						|
async function testGeoSharingIconVisible(state = true) {
 | 
						|
  let sharingIcon = document.getElementById("geo-sharing-icon");
 | 
						|
  ok(sharingIcon, "Geo sharing icon exists");
 | 
						|
 | 
						|
  try {
 | 
						|
    await TestUtils.waitForCondition(
 | 
						|
      () => sharingIcon.hasAttribute("sharing") === true,
 | 
						|
      "Waiting for geo sharing icon visibility state",
 | 
						|
      // If we wait for sharing icon to *not* show, waitForCondition will always timeout on correct state.
 | 
						|
      // In these cases we want to reduce the wait time from 5 seconds to 2.5 seconds to prevent test duration timeouts
 | 
						|
      !state ? POLL_INTERVAL_FALSE_STATE : undefined
 | 
						|
    );
 | 
						|
  } catch (e) {
 | 
						|
    ok(!state, "Geo sharing icon not showing");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  ok(state, "Geo sharing icon showing");
 | 
						|
}
 | 
						|
 | 
						|
async function checkForDOMElement(state, id) {
 | 
						|
  info(`Testing state ${state} of element  ${id}`);
 | 
						|
  let el;
 | 
						|
  try {
 | 
						|
    await TestUtils.waitForCondition(
 | 
						|
      () => {
 | 
						|
        el = document.getElementById(id);
 | 
						|
        return el != null;
 | 
						|
      },
 | 
						|
      `Waiting for ${id}`,
 | 
						|
      !state ? POLL_INTERVAL_FALSE_STATE : undefined
 | 
						|
    );
 | 
						|
  } catch (e) {
 | 
						|
    ok(!state, `${id} has correct state`);
 | 
						|
    return el;
 | 
						|
  }
 | 
						|
  ok(state, `${id} has correct state`);
 | 
						|
 | 
						|
  return el;
 | 
						|
}
 | 
						|
 | 
						|
async function testPermissionPopupGeoContainer(
 | 
						|
  containerVisible,
 | 
						|
  timestampVisible
 | 
						|
) {
 | 
						|
  // The container holds the timestamp element, therefore we can't have a
 | 
						|
  // visible timestamp without the container.
 | 
						|
  if (timestampVisible && !containerVisible) {
 | 
						|
    ok(false, "Can't have timestamp without container");
 | 
						|
  }
 | 
						|
 | 
						|
  // Only call openPermissionPopup if popup is closed, otherwise it does not resolve
 | 
						|
  if (!gPermissionPanel._identityPermissionBox.hasAttribute("open")) {
 | 
						|
    await openPermissionPopup();
 | 
						|
  }
 | 
						|
 | 
						|
  let checkContainer = checkForDOMElement(
 | 
						|
    containerVisible,
 | 
						|
    "permission-popup-geo-container"
 | 
						|
  );
 | 
						|
 | 
						|
  if (containerVisible && timestampVisible) {
 | 
						|
    // Wait for the geo container to be fully populated.
 | 
						|
    // The time label is computed async.
 | 
						|
    let container = await checkContainer;
 | 
						|
    await TestUtils.waitForCondition(
 | 
						|
      () => container.childElementCount == 2,
 | 
						|
      "permission-popup-geo-container should have two elements."
 | 
						|
    );
 | 
						|
    is(
 | 
						|
      container.childNodes[0].classList[0],
 | 
						|
      "permission-popup-permission-item",
 | 
						|
      "Geo container should have permission item."
 | 
						|
    );
 | 
						|
    is(
 | 
						|
      container.childNodes[1].id,
 | 
						|
      "geo-access-indicator-item",
 | 
						|
      "Geo container should have indicator item."
 | 
						|
    );
 | 
						|
  }
 | 
						|
  let checkAccessIndicator = checkForDOMElement(
 | 
						|
    timestampVisible,
 | 
						|
    "geo-access-indicator-item"
 | 
						|
  );
 | 
						|
 | 
						|
  return Promise.all([checkContainer, checkAccessIndicator]);
 | 
						|
}
 | 
						|
 | 
						|
function openExamplePage(tabbrowser = gBrowser) {
 | 
						|
  return BrowserTestUtils.openNewForegroundTab(tabbrowser, EXAMPLE_PAGE_URL);
 | 
						|
}
 | 
						|
 | 
						|
function requestGeoLocation(browser) {
 | 
						|
  return SpecialPowers.spawn(browser, [], () => {
 | 
						|
    return new Promise(resolve => {
 | 
						|
      content.navigator.geolocation.getCurrentPosition(
 | 
						|
        () => resolve(true),
 | 
						|
        error => resolve(error.code !== 1) // PERMISSION_DENIED = 1
 | 
						|
      );
 | 
						|
    });
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function answerGeoLocationPopup(allow, remember = false) {
 | 
						|
  let notification = PopupNotifications.getNotification("geolocation");
 | 
						|
  ok(
 | 
						|
    PopupNotifications.isPanelOpen && notification,
 | 
						|
    "Geolocation notification is open"
 | 
						|
  );
 | 
						|
 | 
						|
  let rememberCheck = PopupNotifications.panel.querySelector(
 | 
						|
    ".popup-notification-checkbox"
 | 
						|
  );
 | 
						|
  rememberCheck.checked = remember;
 | 
						|
 | 
						|
  let popupHidden = BrowserTestUtils.waitForEvent(
 | 
						|
    PopupNotifications.panel,
 | 
						|
    "popuphidden"
 | 
						|
  );
 | 
						|
  if (allow) {
 | 
						|
    let allowBtn = PopupNotifications.panel.querySelector(
 | 
						|
      ".popup-notification-primary-button"
 | 
						|
    );
 | 
						|
    allowBtn.click();
 | 
						|
  } else {
 | 
						|
    let denyBtn = PopupNotifications.panel.querySelector(
 | 
						|
      ".popup-notification-secondary-button"
 | 
						|
    );
 | 
						|
    denyBtn.click();
 | 
						|
  }
 | 
						|
  return popupHidden;
 | 
						|
}
 | 
						|
 | 
						|
function setGeoLastAccess(browser, state) {
 | 
						|
  return new Promise(resolve => {
 | 
						|
    let host = browser.currentURI.host;
 | 
						|
    let handler = {
 | 
						|
      handleCompletion: () => resolve(),
 | 
						|
    };
 | 
						|
 | 
						|
    if (!state) {
 | 
						|
      CP.removeByDomainAndName(
 | 
						|
        host,
 | 
						|
        GEO_CONTENT_PREF_KEY,
 | 
						|
        browser.loadContext,
 | 
						|
        handler
 | 
						|
      );
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    CP.set(
 | 
						|
      host,
 | 
						|
      GEO_CONTENT_PREF_KEY,
 | 
						|
      new Date().toString(),
 | 
						|
      browser.loadContext,
 | 
						|
      handler
 | 
						|
    );
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
async function testGeoLocationLastAccessSet(browser) {
 | 
						|
  let timestamp = await new Promise(resolve => {
 | 
						|
    let lastAccess = null;
 | 
						|
    CP.getByDomainAndName(
 | 
						|
      gBrowser.currentURI.spec,
 | 
						|
      GEO_CONTENT_PREF_KEY,
 | 
						|
      browser.loadContext,
 | 
						|
      {
 | 
						|
        handleResult(pref) {
 | 
						|
          lastAccess = pref.value;
 | 
						|
        },
 | 
						|
        handleCompletion() {
 | 
						|
          resolve(lastAccess);
 | 
						|
        },
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  ok(timestamp != null, "Geo last access timestamp set");
 | 
						|
 | 
						|
  let parseSuccess = true;
 | 
						|
  try {
 | 
						|
    timestamp = new Date(timestamp);
 | 
						|
  } catch (e) {
 | 
						|
    parseSuccess = false;
 | 
						|
  }
 | 
						|
  ok(
 | 
						|
    parseSuccess && !isNaN(timestamp),
 | 
						|
    "Geo last access timestamp is valid Date"
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
async function cleanup(tab) {
 | 
						|
  await setGeoLastAccess(tab.linkedBrowser, false);
 | 
						|
  SitePermissions.removeFromPrincipal(
 | 
						|
    tab.linkedBrowser.contentPrincipal,
 | 
						|
    "geo",
 | 
						|
    tab.linkedBrowser
 | 
						|
  );
 | 
						|
  gBrowser.resetBrowserSharing(tab.linkedBrowser);
 | 
						|
  BrowserTestUtils.removeTab(tab);
 | 
						|
}
 | 
						|
 | 
						|
async function testIndicatorGeoSharingState(active) {
 | 
						|
  let tab = await openExamplePage();
 | 
						|
  gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: active });
 | 
						|
  await testGeoSharingIconVisible(active);
 | 
						|
 | 
						|
  await cleanup(tab);
 | 
						|
}
 | 
						|
 | 
						|
async function testIndicatorExplicitAllow(persistent) {
 | 
						|
  let tab = await openExamplePage();
 | 
						|
 | 
						|
  let popupShown = BrowserTestUtils.waitForEvent(
 | 
						|
    PopupNotifications.panel,
 | 
						|
    "popupshown"
 | 
						|
  );
 | 
						|
  info("Requesting geolocation");
 | 
						|
  let request = requestGeoLocation(tab.linkedBrowser);
 | 
						|
  await popupShown;
 | 
						|
  info("Allowing geolocation via popup");
 | 
						|
  answerGeoLocationPopup(true, persistent);
 | 
						|
  await request;
 | 
						|
 | 
						|
  await Promise.all([
 | 
						|
    testGeoSharingIconVisible(true),
 | 
						|
    testPermissionPopupGeoContainer(true, true),
 | 
						|
    testGeoLocationLastAccessSet(tab.linkedBrowser),
 | 
						|
  ]);
 | 
						|
 | 
						|
  await cleanup(tab);
 | 
						|
}
 | 
						|
 | 
						|
// Indicator and permission popup entry shown after explicit PermissionUI geolocation allow
 | 
						|
add_task(function test_indicator_and_timestamp_after_explicit_allow() {
 | 
						|
  return testIndicatorExplicitAllow(false);
 | 
						|
});
 | 
						|
add_task(function test_indicator_and_timestamp_after_explicit_allow_remember() {
 | 
						|
  return testIndicatorExplicitAllow(true);
 | 
						|
});
 | 
						|
 | 
						|
// Indicator and permission popup entry shown after auto PermissionUI geolocation allow
 | 
						|
add_task(async function test_indicator_and_timestamp_after_implicit_allow() {
 | 
						|
  PermissionTestUtils.add(
 | 
						|
    EXAMPLE_PAGE_URI,
 | 
						|
    "geo",
 | 
						|
    Services.perms.ALLOW_ACTION,
 | 
						|
    Services.perms.EXPIRE_NEVER
 | 
						|
  );
 | 
						|
  let tab = await openExamplePage();
 | 
						|
  let result = await requestGeoLocation(tab.linkedBrowser);
 | 
						|
  ok(result, "Request should be allowed");
 | 
						|
 | 
						|
  await Promise.all([
 | 
						|
    testGeoSharingIconVisible(true),
 | 
						|
    testPermissionPopupGeoContainer(true, true),
 | 
						|
    testGeoLocationLastAccessSet(tab.linkedBrowser),
 | 
						|
  ]);
 | 
						|
 | 
						|
  await cleanup(tab);
 | 
						|
});
 | 
						|
 | 
						|
// Indicator shown when manually setting sharing state to true
 | 
						|
add_task(function test_indicator_sharing_state_active() {
 | 
						|
  return testIndicatorGeoSharingState(true);
 | 
						|
});
 | 
						|
 | 
						|
// Indicator not shown when manually setting sharing state to false
 | 
						|
add_task(function test_indicator_sharing_state_inactive() {
 | 
						|
  return testIndicatorGeoSharingState(false);
 | 
						|
});
 | 
						|
 | 
						|
// Permission popup shows permission if geo permission is set to persistent allow
 | 
						|
add_task(async function test_permission_popup_permission_scope_permanent() {
 | 
						|
  PermissionTestUtils.add(
 | 
						|
    EXAMPLE_PAGE_URI,
 | 
						|
    "geo",
 | 
						|
    Services.perms.ALLOW_ACTION,
 | 
						|
    Services.perms.EXPIRE_NEVER
 | 
						|
  );
 | 
						|
  let tab = await openExamplePage();
 | 
						|
 | 
						|
  await testPermissionPopupGeoContainer(true, false); // Expect permission to be visible, but not lastAccess indicator
 | 
						|
 | 
						|
  await cleanup(tab);
 | 
						|
});
 | 
						|
 | 
						|
// Sharing state set, but no permission
 | 
						|
add_task(async function test_permission_popup_permission_sharing_state() {
 | 
						|
  let tab = await openExamplePage();
 | 
						|
  gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
 | 
						|
  await testPermissionPopupGeoContainer(true, false);
 | 
						|
 | 
						|
  await cleanup(tab);
 | 
						|
});
 | 
						|
 | 
						|
// Permission popup has correct state if sharing state and last geo access timestamp are set
 | 
						|
add_task(
 | 
						|
  async function test_permission_popup_permission_sharing_state_timestamp() {
 | 
						|
    let tab = await openExamplePage();
 | 
						|
    gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
 | 
						|
    await setGeoLastAccess(tab.linkedBrowser, true);
 | 
						|
 | 
						|
    await testPermissionPopupGeoContainer(true, true);
 | 
						|
 | 
						|
    await cleanup(tab);
 | 
						|
  }
 | 
						|
);
 | 
						|
 | 
						|
// Clicking permission clear button clears permission and resets geo sharing state
 | 
						|
add_task(async function test_permission_popup_permission_clear() {
 | 
						|
  PermissionTestUtils.add(
 | 
						|
    EXAMPLE_PAGE_URI,
 | 
						|
    "geo",
 | 
						|
    Services.perms.ALLOW_ACTION,
 | 
						|
    Services.perms.EXPIRE_NEVER
 | 
						|
  );
 | 
						|
  let tab = await openExamplePage();
 | 
						|
  gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
 | 
						|
 | 
						|
  await openPermissionPopup();
 | 
						|
 | 
						|
  let clearButton = document.querySelector(
 | 
						|
    "#permission-popup-geo-container button"
 | 
						|
  );
 | 
						|
  ok(clearButton, "Clear button is visible");
 | 
						|
  clearButton.click();
 | 
						|
 | 
						|
  await Promise.all([
 | 
						|
    testGeoSharingIconVisible(false),
 | 
						|
    testPermissionPopupGeoContainer(false, false),
 | 
						|
    TestUtils.waitForCondition(() => {
 | 
						|
      let sharingState = tab._sharingState;
 | 
						|
      return (
 | 
						|
        sharingState == null ||
 | 
						|
        sharingState.geo == null ||
 | 
						|
        sharingState.geo === false
 | 
						|
      );
 | 
						|
    }, "Waiting for geo sharing state to reset"),
 | 
						|
  ]);
 | 
						|
  await cleanup(tab);
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * Tests that we only show the last access label once when the sharing
 | 
						|
 * state is updated multiple times while the popup is open.
 | 
						|
 */
 | 
						|
add_task(async function test_permission_no_duplicate_last_access_label() {
 | 
						|
  let tab = await openExamplePage();
 | 
						|
  await setGeoLastAccess(tab.linkedBrowser, true);
 | 
						|
  await openPermissionPopup();
 | 
						|
  gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
 | 
						|
  gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
 | 
						|
  await testPermissionPopupGeoContainer(true, true);
 | 
						|
  await cleanup(tab);
 | 
						|
});
 |