зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
42d8404c18
|
@ -287,7 +287,9 @@ uint32_t AccessibleWrap::GetFlags(role aRole, uint64_t aState,
|
|||
return flags;
|
||||
}
|
||||
|
||||
void AccessibleWrap::GetRoleDescription(role aRole, nsAString& aGeckoRole,
|
||||
void AccessibleWrap::GetRoleDescription(role aRole,
|
||||
nsIPersistentProperties* aAttributes,
|
||||
nsAString& aGeckoRole,
|
||||
nsAString& aRoleDescription) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
|
@ -305,6 +307,19 @@ void AccessibleWrap::GetRoleDescription(role aRole, nsAString& aGeckoRole,
|
|||
return;
|
||||
}
|
||||
|
||||
if (aRole == roles::HEADING) {
|
||||
nsString level;
|
||||
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("level"), level);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
const char16_t* formatString[] = {level.get()};
|
||||
rv = bundle->FormatStringFromName("headingLevel", formatString, 1,
|
||||
aRoleDescription);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetAccService()->GetStringRole(aRole, aGeckoRole);
|
||||
rv = bundle->GetStringFromName(NS_ConvertUTF16toUTF8(aGeckoRole).get(),
|
||||
aRoleDescription);
|
||||
|
@ -450,7 +465,7 @@ mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(
|
|||
nsAutoString geckoRole;
|
||||
nsAutoString roleDescription;
|
||||
if (VirtualViewID() != kNoID) {
|
||||
GetRoleDescription(role, geckoRole, roleDescription);
|
||||
GetRoleDescription(role, aAttributes, geckoRole, roleDescription);
|
||||
}
|
||||
|
||||
GECKOBUNDLE_PUT(nodeInfo, "roleDescription",
|
||||
|
|
|
@ -81,8 +81,11 @@ class AccessibleWrap : public Accessible {
|
|||
|
||||
virtual role WrapperRole() { return Role(); }
|
||||
|
||||
static void GetRoleDescription(role aRole, nsAString& aGeckoRole,
|
||||
static void GetRoleDescription(role aRole,
|
||||
nsIPersistentProperties* aAttributes,
|
||||
nsAString& aGeckoRole,
|
||||
nsAString& aRoleDescription);
|
||||
|
||||
static uint32_t GetFlags(role aRole, uint64_t aState, uint8_t aActionCount);
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 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/. */
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "DocAccessibleWrap.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
@ -229,6 +230,11 @@ void DocAccessibleWrap::UpdateFocusPathBounds() {
|
|||
nsTArray<BatchData> boundsData(mFocusPath.Count());
|
||||
for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) {
|
||||
Accessible* accessible = iter.Data();
|
||||
if (!accessible || accessible->IsDefunct()) {
|
||||
MOZ_ASSERT_UNREACHABLE("Focus path cached accessible is gone.");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc()
|
||||
? 0
|
||||
: reinterpret_cast<uint64_t>(accessible->UniqueID());
|
||||
|
|
|
@ -1473,9 +1473,9 @@ pref("media.gmp-widevinecdm.enabled", true);
|
|||
// Switch block autoplay logic to v2, and enable UI.
|
||||
pref("media.autoplay.enabled.user-gestures-needed", true);
|
||||
// Allow asking for permission to autoplay to appear in UI.
|
||||
pref("media.autoplay.ask-permission", true);
|
||||
pref("media.autoplay.ask-permission", false);
|
||||
// Set Firefox to block autoplay, asking for permission by default.
|
||||
pref("media.autoplay.default", 2); // 0=Allowed, 1=Blocked, 2=Prompt
|
||||
pref("media.autoplay.default", 1); // 0=Allowed, 1=Blocked, 2=Prompt
|
||||
#else
|
||||
pref("media.autoplay.default", 0); // 0=Allowed, 1=Blocked, 2=Prompt
|
||||
pref("media.autoplay.enabled.user-gestures-needed", false);
|
||||
|
@ -1539,6 +1539,8 @@ pref("dom.storage_access.max_concurrent_auto_grants", 5);
|
|||
pref("browser.contentblocking.trackingprotection.control-center.ui.enabled", true);
|
||||
pref("browser.contentblocking.rejecttrackers.control-center.ui.enabled", true);
|
||||
|
||||
pref("browser.contentblocking.control-center.ui.showAllowedLabels", false);
|
||||
|
||||
// Enable the Report Breakage UI on Nightly and Beta but not on Release yet.
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
pref("browser.contentblocking.reportBreakage.enabled", true);
|
||||
|
|
|
@ -80,9 +80,9 @@ var TrackingProtection = {
|
|||
if (this.enabled) {
|
||||
label = "contentBlocking.trackers.blocked.label";
|
||||
} else {
|
||||
label = "contentBlocking.trackers.allowed.label";
|
||||
label = ContentBlocking.showAllowedLabels ? "contentBlocking.trackers.allowed.label" : null;
|
||||
}
|
||||
this.categoryLabel.textContent = gNavigatorBundle.getString(label);
|
||||
this.categoryLabel.textContent = label ? gNavigatorBundle.getString(label) : "";
|
||||
},
|
||||
|
||||
isBlocking(state) {
|
||||
|
@ -266,10 +266,10 @@ var ThirdPartyCookies = {
|
|||
Cu.reportError(`Error: Unknown cookieBehavior pref observed: ${this.behaviorPref}`);
|
||||
// fall through
|
||||
case Ci.nsICookieService.BEHAVIOR_ACCEPT:
|
||||
label = "contentBlocking.cookies.allowed.label";
|
||||
label = ContentBlocking.showAllowedLabels ? "contentBlocking.cookies.allowed.label" : null;
|
||||
break;
|
||||
}
|
||||
this.categoryLabel.textContent = gNavigatorBundle.getString(label);
|
||||
this.categoryLabel.textContent = label ? gNavigatorBundle.getString(label) : "";
|
||||
},
|
||||
|
||||
init() {
|
||||
|
@ -488,6 +488,7 @@ var ContentBlocking = {
|
|||
PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url",
|
||||
PREF_INTRO_COUNT_CB: "browser.contentblocking.introCount",
|
||||
PREF_CB_CATEGORY: "browser.contentblocking.category",
|
||||
PREF_SHOW_ALLOWED_LABELS: "browser.contentblocking.control-center.ui.showAllowedLabels",
|
||||
content: null,
|
||||
icon: null,
|
||||
activeTooltipText: null,
|
||||
|
@ -575,6 +576,12 @@ var ContentBlocking = {
|
|||
|
||||
Services.prefs.addObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "showAllowedLabels",
|
||||
this.PREF_SHOW_ALLOWED_LABELS, false, () => {
|
||||
for (let blocker of this.blockers) {
|
||||
blocker.updateCategoryLabel();
|
||||
}
|
||||
});
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "reportBreakageEnabled",
|
||||
this.PREF_REPORT_BREAKAGE_ENABLED, false);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ const ROOT = "http://mochi.test:8888/browser/browser/base/content/test/favicons/
|
|||
|
||||
add_task(async () => {
|
||||
await BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, async (browser) => {
|
||||
let faviconPromise = waitForFaviconMessage();
|
||||
let faviconPromise = waitForFaviconMessage(true, `${ROOT}large.png`);
|
||||
|
||||
BrowserTestUtils.loadURI(browser, ROOT + "large_favicon.html");
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
|
|
@ -22,23 +22,6 @@ registerCleanupFunction(function() {
|
|||
Services.prefs.clearUserPref(ContentBlocking.prefIntroCount);
|
||||
});
|
||||
|
||||
function waitForSecurityChange(tabbrowser, numChanges = 1) {
|
||||
return new Promise(resolve => {
|
||||
let n = 0;
|
||||
let listener = {
|
||||
onSecurityChange() {
|
||||
n = n + 1;
|
||||
info("Received onSecurityChange event " + n + " of " + numChanges);
|
||||
if (n >= numChanges) {
|
||||
tabbrowser.removeProgressListener(listener);
|
||||
resolve(n);
|
||||
}
|
||||
},
|
||||
};
|
||||
tabbrowser.addProgressListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
async function testTrackingProtectionAnimation(tabbrowser) {
|
||||
info("Load a test page not containing tracking elements");
|
||||
let benignTab = await BrowserTestUtils.openNewForegroundTab(tabbrowser, BENIGN_PAGE);
|
||||
|
@ -62,7 +45,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
await BrowserTestUtils.waitForEvent(ContentBlocking.animatedIcon, "animationend");
|
||||
|
||||
info("Switch from tracking cookie -> benign tab");
|
||||
let securityChanged = waitForSecurityChange(tabbrowser);
|
||||
let securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
|
||||
tabbrowser.selectedTab = benignTab;
|
||||
await securityChanged;
|
||||
|
||||
|
@ -70,7 +53,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating");
|
||||
|
||||
info("Switch from benign -> tracking tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser);
|
||||
securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
|
||||
tabbrowser.selectedTab = trackingTab;
|
||||
await securityChanged;
|
||||
|
||||
|
@ -78,7 +61,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating");
|
||||
|
||||
info("Switch from tracking -> tracking cookies tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser);
|
||||
securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
|
||||
tabbrowser.selectedTab = trackingCookiesTab;
|
||||
await securityChanged;
|
||||
|
||||
|
@ -86,7 +69,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating");
|
||||
|
||||
info("Reload tracking cookies tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser, 3);
|
||||
securityChanged = waitForSecurityChange(3, tabbrowser.ownerGlobal);
|
||||
tabbrowser.reload();
|
||||
await securityChanged;
|
||||
|
||||
|
@ -95,7 +78,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
await BrowserTestUtils.waitForEvent(ContentBlocking.animatedIcon, "animationend");
|
||||
|
||||
info("Reload tracking tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser, 4);
|
||||
securityChanged = waitForSecurityChange(4, tabbrowser.ownerGlobal);
|
||||
tabbrowser.selectedTab = trackingTab;
|
||||
tabbrowser.reload();
|
||||
await securityChanged;
|
||||
|
@ -105,7 +88,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
await BrowserTestUtils.waitForEvent(ContentBlocking.animatedIcon, "animationend");
|
||||
|
||||
info("Inject tracking cookie inside tracking tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser);
|
||||
securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
|
||||
let timeoutPromise = new Promise(resolve => setTimeout(resolve, 500));
|
||||
await ContentTask.spawn(tabbrowser.selectedBrowser, {},
|
||||
function() {
|
||||
|
@ -118,7 +101,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating");
|
||||
|
||||
info("Inject tracking element inside tracking tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser);
|
||||
securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
|
||||
timeoutPromise = new Promise(resolve => setTimeout(resolve, 500));
|
||||
await ContentTask.spawn(tabbrowser.selectedBrowser, {},
|
||||
function() {
|
||||
|
@ -133,7 +116,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
tabbrowser.selectedTab = trackingCookiesTab;
|
||||
|
||||
info("Inject tracking cookie inside tracking cookies tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser);
|
||||
securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
|
||||
timeoutPromise = new Promise(resolve => setTimeout(resolve, 500));
|
||||
await ContentTask.spawn(tabbrowser.selectedBrowser, {},
|
||||
function() {
|
||||
|
@ -146,7 +129,7 @@ async function testTrackingProtectionAnimation(tabbrowser) {
|
|||
ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating");
|
||||
|
||||
info("Inject tracking element inside tracking cookies tab");
|
||||
securityChanged = waitForSecurityChange(tabbrowser);
|
||||
securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
|
||||
timeoutPromise = new Promise(resolve => setTimeout(resolve, 500));
|
||||
await ContentTask.spawn(tabbrowser.selectedBrowser, {},
|
||||
function() {
|
||||
|
|
|
@ -70,6 +70,8 @@ add_task(async function testCategoryLabelsInAppMenu() {
|
|||
});
|
||||
|
||||
add_task(async function testSubcategoryLabels() {
|
||||
SpecialPowers.pushPrefEnv({set: [["browser.contentblocking.control-center.ui.showAllowedLabels", true]]});
|
||||
|
||||
await BrowserTestUtils.withNewTab("http://www.example.com", async function() {
|
||||
let categoryLabel =
|
||||
document.getElementById("identity-popup-content-blocking-tracking-protection-state-label");
|
||||
|
|
|
@ -40,7 +40,10 @@ function openIdentityPopup() {
|
|||
return viewShown;
|
||||
}
|
||||
|
||||
function waitForSecurityChange(numChanges = 1) {
|
||||
function waitForSecurityChange(numChanges = 1, win = null) {
|
||||
if (!win) {
|
||||
win = window;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
let n = 0;
|
||||
let listener = {
|
||||
|
@ -48,11 +51,11 @@ function waitForSecurityChange(numChanges = 1) {
|
|||
n = n + 1;
|
||||
info("Received onSecurityChange event " + n + " of " + numChanges);
|
||||
if (n >= numChanges) {
|
||||
gBrowser.removeProgressListener(listener);
|
||||
win.gBrowser.removeProgressListener(listener);
|
||||
resolve(n);
|
||||
}
|
||||
},
|
||||
};
|
||||
gBrowser.addProgressListener(listener);
|
||||
win.gBrowser.addProgressListener(listener);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -55,8 +55,9 @@ this.pageAction = class extends ExtensionAPI {
|
|||
show = false;
|
||||
} else {
|
||||
// Might show or hide depending on the URL. Enable pattern matching.
|
||||
showMatches = new MatchPatternSet(show_matches);
|
||||
hideMatches = new MatchPatternSet(hide_matches);
|
||||
const {restrictSchemes} = extension;
|
||||
showMatches = new MatchPatternSet(show_matches, {restrictSchemes});
|
||||
hideMatches = new MatchPatternSet(hide_matches, {restrictSchemes});
|
||||
}
|
||||
|
||||
this.defaults = {
|
||||
|
|
|
@ -175,3 +175,63 @@ add_task(async function test_pageAction_all_urls() {
|
|||
let rejects = await extension.startup().then(() => false, () => true);
|
||||
is(rejects, true, "startup failed");
|
||||
});
|
||||
|
||||
add_task(async function test_pageAction_restrictScheme_false() {
|
||||
info("Check restricted origins are allowed in show_matches for privileged extensions");
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
isPrivileged: true,
|
||||
manifest: {
|
||||
permissions: ["mozillaAddons", "tabs"],
|
||||
page_action: {
|
||||
"show_matches": ["about:reader*"],
|
||||
"hide_matches": ["*://*/*"],
|
||||
},
|
||||
},
|
||||
background: function() {
|
||||
browser.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
|
||||
if (changeInfo.url && changeInfo.url.startsWith("about:reader")) {
|
||||
browser.test.sendMessage("readerModeEntered");
|
||||
}
|
||||
});
|
||||
|
||||
browser.test.onMessage.addListener(async (msg) => {
|
||||
if (msg !== "enterReaderMode") {
|
||||
browser.test.fail(`Received unexpected test message: ${msg}`);
|
||||
return;
|
||||
}
|
||||
|
||||
browser.tabs.toggleReaderMode();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
async function expectPageAction(extension, tab, isShown) {
|
||||
await promiseAnimationFrame();
|
||||
let widgetId = makeWidgetId(extension.id);
|
||||
let pageActionId = BrowserPageActions.urlbarButtonNodeIDForActionID(widgetId);
|
||||
let iconEl = document.getElementById(pageActionId);
|
||||
|
||||
if (isShown) {
|
||||
ok(iconEl && !iconEl.hasAttribute("disabled"), "pageAction is shown");
|
||||
} else {
|
||||
ok(iconEl == null || iconEl.getAttribute("disabled") == "true", "pageAction is hidden");
|
||||
}
|
||||
}
|
||||
|
||||
const baseUrl = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
|
||||
const url = `${baseUrl}/readerModeArticle.html`;
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true, true);
|
||||
|
||||
await extension.startup();
|
||||
|
||||
await expectPageAction(extension, tab, false);
|
||||
|
||||
extension.sendMessage("enterReaderMode");
|
||||
await extension.awaitMessage("readerModeEntered");
|
||||
|
||||
await expectPageAction(extension, tab, true);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
"32": "icons/icon-v2.svg"
|
||||
},
|
||||
"default_title": "__MSG_contextMenuLabel__",
|
||||
"show_matches": ["<all_urls>"],
|
||||
"show_matches": ["<all_urls>", "about:reader*"],
|
||||
"pinned": false
|
||||
},
|
||||
"icons": {
|
||||
|
|
|
@ -70,6 +70,7 @@ class HighlightersOverlay {
|
|||
this.shapesHighlighterShown = null;
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.onDisplayChange = this.onDisplayChange.bind(this);
|
||||
this.onMarkupMutation = this.onMarkupMutation.bind(this);
|
||||
this.onMouseMove = this.onMouseMove.bind(this);
|
||||
this.onMouseOut = this.onMouseOut.bind(this);
|
||||
|
@ -88,6 +89,7 @@ class HighlightersOverlay {
|
|||
|
||||
// Add inspector events, not specific to a given view.
|
||||
this.inspector.on("markupmutation", this.onMarkupMutation);
|
||||
this.inspector.walker.on("display-change", this.onDisplayChange);
|
||||
this.inspector.target.on("will-navigate", this.onWillNavigate);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
|
@ -1038,6 +1040,35 @@ class HighlightersOverlay {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for "display-change" events from the walker. Hides the flexbox or
|
||||
* grid highlighter if their respective node is no longer a flex container or
|
||||
* grid container.
|
||||
*
|
||||
* @param {Array} nodes
|
||||
* An array of nodeFronts
|
||||
*/
|
||||
async onDisplayChange(nodes) {
|
||||
for (const node of nodes) {
|
||||
const display = node.displayType;
|
||||
|
||||
// Hide the flexbox highlighter if the node is no longer a flexbox
|
||||
// container.
|
||||
if (display !== "flex" && display !== "inline-flex" &&
|
||||
node == this.flexboxHighlighterShown) {
|
||||
await this.hideFlexboxHighlighter(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the grid highlighter if the node is no longer a grid container.
|
||||
if (display !== "grid" && display !== "inline-grid" &&
|
||||
this.gridHighlighters.has(node)) {
|
||||
await this.hideGridHighlighter(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMouseMove(event) {
|
||||
// Bail out if the target is the same as for the last mousemove.
|
||||
if (event.target === this._lastHovered) {
|
||||
|
|
|
@ -277,12 +277,8 @@ pref("devtools.webconsole.sidebarToggle", false);
|
|||
// Enable CodeMirror in the JsTerm
|
||||
pref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
|
||||
// Enable console input reverse-search in Nightly builds
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
// Enable console input reverse-search everywhere
|
||||
pref("devtools.webconsole.jsterm.reverse-search", true);
|
||||
#else
|
||||
pref("devtools.webconsole.jsterm.reverse-search", false);
|
||||
#endif
|
||||
|
||||
// Disable the new performance recording panel by default
|
||||
pref("devtools.performance.new-panel-enabled", false);
|
||||
|
|
|
@ -757,18 +757,6 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
}
|
||||
|
||||
_update() {
|
||||
// If this.currentNode is not a flex container we have nothing to highlight.
|
||||
// We can't simply use getAsFlexContainer() here because this fails for
|
||||
// text fields. This will be removed by https://bugzil.la/1509460.
|
||||
if (!this.computedStyle) {
|
||||
this.computedStyle = getComputedStyle(this.currentNode);
|
||||
}
|
||||
|
||||
if (this.computedStyle.display !== "flex" &&
|
||||
this.computedStyle.display !== "inline-flex") {
|
||||
return false;
|
||||
}
|
||||
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
const root = this.getElement("root");
|
||||
|
|
|
@ -560,7 +560,7 @@ nsDOMWindowUtils::SetResolutionAndScaleTo(float aResolution) {
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
presShell->SetResolutionAndScaleTo(aResolution);
|
||||
presShell->SetResolutionAndScaleTo(aResolution, nsGkAtoms::other);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -10188,7 +10188,7 @@ void nsIDocument::CleanupFullscreenState() {
|
|||
// Restore the zoom level that was in place prior to entering fullscreen.
|
||||
if (nsIPresShell* shell = GetShell()) {
|
||||
if (shell->GetMobileViewportManager()) {
|
||||
shell->SetResolutionAndScaleTo(mSavedResolution);
|
||||
shell->SetResolutionAndScaleTo(mSavedResolution, nsGkAtoms::restore);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10585,7 +10585,8 @@ bool nsIDocument::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) {
|
|||
shell->GetMobileViewportManager()) {
|
||||
// Save the previous resolution so it can be restored.
|
||||
child->mSavedResolution = shell->GetResolution();
|
||||
shell->SetResolutionAndScaleTo(manager->ComputeIntrinsicResolution());
|
||||
shell->SetResolutionAndScaleTo(manager->ComputeIntrinsicResolution(),
|
||||
nsGkAtoms::other);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1123,8 +1123,14 @@ class GetUserMediaStreamRunnable : public Runnable {
|
|||
"TracksCreatedListener::mTrack", aTrack)) {}
|
||||
|
||||
~TracksCreatedListener() {
|
||||
mHolder.RejectIfExists(
|
||||
MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError), __func__);
|
||||
RejectIfExists(MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
|
||||
__func__);
|
||||
}
|
||||
|
||||
// TODO: The need for this should be removed by an upcoming refactor.
|
||||
void RejectIfExists(RefPtr<MediaMgrError>&& aError,
|
||||
const char* aMethodName) {
|
||||
mHolder.RejectIfExists(std::move(aError), aMethodName);
|
||||
}
|
||||
|
||||
void NotifyOutput(MediaStreamGraph* aGraph,
|
||||
|
@ -1384,7 +1390,7 @@ class GetUserMediaStreamRunnable : public Runnable {
|
|||
manager->SendPendingGUMRequest();
|
||||
},
|
||||
[manager = mManager, windowID = mWindowID,
|
||||
holder = std::move(mHolder)](RefPtr<MediaMgrError>&& aError) mutable {
|
||||
tracksCreatedListener](RefPtr<MediaMgrError>&& aError) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG(
|
||||
("GetUserMediaStreamRunnable::Run: starting failure callback "
|
||||
|
@ -1397,7 +1403,7 @@ class GetUserMediaStreamRunnable : public Runnable {
|
|||
}
|
||||
// This is safe since we're on main-thread, and the windowlist can
|
||||
// only be invalidated from the main-thread (see OnNavigation)
|
||||
holder.Reject(std::move(aError), __func__);
|
||||
tracksCreatedListener->RejectIfExists(std::move(aError), __func__);
|
||||
});
|
||||
|
||||
if (!IsPincipalInfoPrivate(mPrincipalInfo)) {
|
||||
|
|
|
@ -812,6 +812,7 @@ struct ScrollMetadata {
|
|||
mIsAutoDirRootContentRTL(false),
|
||||
mUsesContainerScrolling(false),
|
||||
mForceDisableApz(false),
|
||||
mResolutionUpdated(false),
|
||||
mOverscrollBehavior() {}
|
||||
|
||||
bool operator==(const ScrollMetadata& aOther) const {
|
||||
|
@ -827,6 +828,7 @@ struct ScrollMetadata {
|
|||
mIsAutoDirRootContentRTL == aOther.mIsAutoDirRootContentRTL &&
|
||||
mUsesContainerScrolling == aOther.mUsesContainerScrolling &&
|
||||
mForceDisableApz == aOther.mForceDisableApz &&
|
||||
mResolutionUpdated == aOther.mResolutionUpdated &&
|
||||
mDisregardedDirection == aOther.mDisregardedDirection &&
|
||||
mOverscrollBehavior == aOther.mOverscrollBehavior;
|
||||
}
|
||||
|
@ -907,6 +909,8 @@ struct ScrollMetadata {
|
|||
mForceDisableApz = aForceDisable;
|
||||
}
|
||||
bool IsApzForceDisabled() const { return mForceDisableApz; }
|
||||
void SetResolutionUpdated(bool aUpdated) { mResolutionUpdated = aUpdated; }
|
||||
bool IsResolutionUpdated() const { return mResolutionUpdated; }
|
||||
|
||||
// For more details about the concept of a disregarded direction, refer to the
|
||||
// code which defines mDisregardedDirection.
|
||||
|
@ -983,6 +987,11 @@ struct ScrollMetadata {
|
|||
// scrollframe.
|
||||
bool mForceDisableApz : 1;
|
||||
|
||||
// Whether the pres shell resolution stored in mMetrics reflects a change
|
||||
// originated by the main thread. Plays a similar role for the resolution as
|
||||
// FrameMetrics::mScrollUpdateType) does for the scroll offset.
|
||||
bool mResolutionUpdated : 1;
|
||||
|
||||
// The disregarded direction means the direction which is disregarded anyway,
|
||||
// even if the scroll frame overflows in that direction and the direction is
|
||||
// specified as scrollable. This could happen in some scenarios, for instance,
|
||||
|
|
|
@ -4418,11 +4418,13 @@ void AsyncPanZoomController::NotifyLayersUpdated(
|
|||
// in some things into our local Metrics() because these things are
|
||||
// determined by Gecko and our copy in Metrics() may be stale.
|
||||
|
||||
// TODO: Rely entirely on |aScrollMetadata.IsResolutionUpdated()| to
|
||||
// determine which branch to take, and drop the other conditions.
|
||||
if (FuzzyEqualsAdditive(Metrics().GetCompositionBounds().Width(),
|
||||
aLayerMetrics.GetCompositionBounds().Width()) &&
|
||||
Metrics().GetDevPixelsPerCSSPixel() ==
|
||||
aLayerMetrics.GetDevPixelsPerCSSPixel() &&
|
||||
!viewportUpdated) {
|
||||
!viewportUpdated && !aScrollMetadata.IsResolutionUpdated()) {
|
||||
// Any change to the pres shell resolution was requested by APZ and is
|
||||
// already included in our zoom; however, other components of the
|
||||
// cumulative resolution (a parent document's pres-shell resolution, or
|
||||
|
|
|
@ -317,7 +317,7 @@ void APZCCallbackHelper::UpdateRootFrame(const RepaintRequest& aRequest) {
|
|||
// last paint.
|
||||
presShellResolution =
|
||||
aRequest.GetPresShellResolution() * aRequest.GetAsyncZoom().scale;
|
||||
shell->SetResolutionAndScaleTo(presShellResolution);
|
||||
shell->SetResolutionAndScaleTo(presShellResolution, nsGkAtoms::apz);
|
||||
}
|
||||
|
||||
// Do this as late as possible since scrolling can flush layout. It also
|
||||
|
|
|
@ -339,6 +339,7 @@ struct ParamTraits<mozilla::layers::ScrollMetadata>
|
|||
WriteParam(aMsg, aParam.mIsAutoDirRootContentRTL);
|
||||
WriteParam(aMsg, aParam.mUsesContainerScrolling);
|
||||
WriteParam(aMsg, aParam.mForceDisableApz);
|
||||
WriteParam(aMsg, aParam.mResolutionUpdated);
|
||||
WriteParam(aMsg, aParam.mDisregardedDirection);
|
||||
WriteParam(aMsg, aParam.mOverscrollBehavior);
|
||||
}
|
||||
|
@ -373,6 +374,8 @@ struct ParamTraits<mozilla::layers::ScrollMetadata>
|
|||
¶mType::SetUsesContainerScrolling) &&
|
||||
ReadBoolForBitfield(aMsg, aIter, aResult,
|
||||
¶mType::SetForceDisableApz) &&
|
||||
ReadBoolForBitfield(aMsg, aIter, aResult,
|
||||
¶mType::SetResolutionUpdated) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mDisregardedDirection) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mOverscrollBehavior));
|
||||
}
|
||||
|
|
|
@ -329,7 +329,7 @@ void MobileViewportManager::UpdateResolution(
|
|||
if (newZoom) {
|
||||
LayoutDeviceToLayerScale resolution = ZoomToResolution(*newZoom, cssToDev);
|
||||
MVM_LOG("%p: setting resolution %f\n", this, resolution.scale);
|
||||
mPresShell->SetResolutionAndScaleTo(resolution.scale);
|
||||
mPresShell->SetResolutionAndScaleTo(resolution.scale, nsGkAtoms::other);
|
||||
|
||||
MVM_LOG("%p: New zoom is %f\n", this, newZoom->scale);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
|
@ -781,6 +780,7 @@ PresShell::PresShell()
|
|||
mHasReceivedPaintMessage(false),
|
||||
mIsLastKeyDownCanceled(false),
|
||||
mHasHandledUserInput(false),
|
||||
mResolutionUpdated(false),
|
||||
mForceDispatchKeyPressEventsForNonPrintableKeys(false),
|
||||
mForceUseLegacyKeyCodeAndCharCodeValues(false),
|
||||
mInitializedWithKeyPressEventDispatchingBlacklist(false) {
|
||||
|
@ -5106,7 +5106,8 @@ void PresShell::SetIgnoreViewportScrolling(bool aIgnore) {
|
|||
}
|
||||
|
||||
nsresult PresShell::SetResolutionImpl(float aResolution,
|
||||
bool aScaleToResolution) {
|
||||
bool aScaleToResolution,
|
||||
nsAtom* aOrigin) {
|
||||
if (!(aResolution > 0.0)) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
@ -5121,6 +5122,9 @@ nsresult PresShell::SetResolutionImpl(float aResolution,
|
|||
if (mMobileViewportManager) {
|
||||
mMobileViewportManager->ResolutionUpdated();
|
||||
}
|
||||
if (aOrigin != nsGkAtoms::apz) {
|
||||
mResolutionUpdated = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -195,12 +195,19 @@ class PresShell final : public nsIPresShell,
|
|||
void SetIgnoreViewportScrolling(bool aIgnore) override;
|
||||
|
||||
nsresult SetResolution(float aResolution) override {
|
||||
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false);
|
||||
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false,
|
||||
nsGkAtoms::other);
|
||||
}
|
||||
nsresult SetResolutionAndScaleTo(float aResolution) override {
|
||||
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true);
|
||||
nsresult SetResolutionAndScaleTo(float aResolution,
|
||||
nsAtom* aOrigin) override {
|
||||
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true,
|
||||
aOrigin);
|
||||
}
|
||||
bool ScaleToResolution() const override;
|
||||
bool IsResolutionUpdated() const override { return mResolutionUpdated; }
|
||||
void SetResolutionUpdated(bool aUpdated) override {
|
||||
mResolutionUpdated = aUpdated;
|
||||
}
|
||||
float GetCumulativeResolution() override;
|
||||
float GetCumulativeNonRootScaleResolution() override;
|
||||
void SetRestoreResolution(float aResolution,
|
||||
|
@ -711,7 +718,8 @@ class PresShell final : public nsIPresShell,
|
|||
// that we last did an approximate frame visibility update.
|
||||
VisibleFrames mApproximatelyVisibleFrames;
|
||||
|
||||
nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution);
|
||||
nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution,
|
||||
nsAtom* aOrigin);
|
||||
|
||||
nsIContent* GetOverrideClickTarget(WidgetGUIEvent* aEvent, nsIFrame* aFrame);
|
||||
#ifdef DEBUG
|
||||
|
@ -828,6 +836,10 @@ class PresShell final : public nsIPresShell,
|
|||
// Whether we have ever handled a user input event
|
||||
bool mHasHandledUserInput : 1;
|
||||
|
||||
// Whether the most recent change to the pres shell resolution was
|
||||
// originated by the main thread.
|
||||
bool mResolutionUpdated : 1;
|
||||
|
||||
// Whether we should dispatch keypress events even for non-printable keys
|
||||
// for keeping backward compatibility.
|
||||
bool mForceDispatchKeyPressEventsForNonPrintableKeys : 1;
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "mozilla/dom/HTMLBodyElement.h"
|
||||
|
||||
#include "Layers.h"
|
||||
#include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
|
||||
#include "nsAnimationManager.h"
|
||||
#include "nsBlockFrame.h"
|
||||
#include "nsBulletFrame.h"
|
||||
|
@ -44,7 +43,6 @@
|
|||
#include "StickyScrollContainer.h"
|
||||
#include "mozilla/EffectSet.h"
|
||||
#include "mozilla/IntegerRange.h"
|
||||
#include "mozilla/ViewportFrame.h"
|
||||
#include "SVGObserverUtils.h"
|
||||
#include "SVGTextFrame.h"
|
||||
#include "ActiveLayerTracker.h"
|
||||
|
|
|
@ -104,7 +104,6 @@
|
|||
#include "nsBackdropFrame.h"
|
||||
#include "nsTransitionManager.h"
|
||||
#include "DetailsFrame.h"
|
||||
#include "nsStyleConsts.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsIPopupContainer.h"
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
#include "nsIPageSequenceFrame.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIContentViewerEdit.h"
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
#include "mozilla/css/Loader.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
|
|
|
@ -1371,6 +1371,13 @@ class nsIPresShell : public nsStubDocumentObserver {
|
|||
float GetResolution() const { return mResolution.valueOr(1.0); }
|
||||
virtual float GetCumulativeResolution() = 0;
|
||||
|
||||
/**
|
||||
* Accessors for a flag that tracks whether the most recent change to
|
||||
* the pres shell's resolution was originated by the main thread.
|
||||
*/
|
||||
virtual bool IsResolutionUpdated() const = 0;
|
||||
virtual void SetResolutionUpdated(bool aUpdated) = 0;
|
||||
|
||||
/**
|
||||
* Calculate the cumulative scale resolution from this document up to
|
||||
* but not including the root document.
|
||||
|
@ -1385,8 +1392,13 @@ class nsIPresShell : public nsStubDocumentObserver {
|
|||
/**
|
||||
* Similar to SetResolution() but also increases the scale of the content
|
||||
* by the same amount.
|
||||
* |aOrigin| specifies who originated the resolution change. For changes
|
||||
* sent by APZ, pass nsGkAtoms::apz. For changes sent by the main thread,
|
||||
* use pass nsGkAtoms::other or nsGkAtoms::restore (similar to the |aOrigin|
|
||||
* parameter of nsIScrollableFrame::ScrollToCSSPixels()).
|
||||
*/
|
||||
virtual nsresult SetResolutionAndScaleTo(float aResolution) = 0;
|
||||
virtual nsresult SetResolutionAndScaleTo(float aResolution,
|
||||
nsAtom* aOrigin) = 0;
|
||||
|
||||
/**
|
||||
* Return whether we are scaling to the set resolution.
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
#include "ImageRegion.h"
|
||||
#include "gfxRect.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxContext.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsCSSRendering.h"
|
||||
#include "nsTextFragment.h"
|
||||
|
@ -117,7 +116,6 @@
|
|||
#include "mozilla/layers/APZUtils.h" // for apz::CalculatePendingDisplayPort
|
||||
#include "mozilla/layers/CompositorBridgeChild.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/WheelHandlingHelper.h" // for WheelHandlingUtils
|
||||
|
@ -8820,6 +8818,13 @@ static void MaybeReflowForInflationScreenSizeChange(
|
|||
} else {
|
||||
metrics.SetPresShellResolution(1.0f);
|
||||
}
|
||||
|
||||
if (presShell->IsResolutionUpdated()) {
|
||||
metadata.SetResolutionUpdated(true);
|
||||
// We only need to tell APZ about the resolution update once.
|
||||
presShell->SetResolutionUpdated(false);
|
||||
}
|
||||
|
||||
// The cumulative resolution is the resolution at which the scroll frame's
|
||||
// content is actually rendered. It includes the pres shell resolutions of
|
||||
// all the pres shells from here up to the root, as well as any css-driven
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "mozilla/ReflowOutput.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "SVGImageContext.h"
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "nsTObserverArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsCheckboxRadioFrame.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "ColumnSetWrapperFrame.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIFrameInlines.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "nsIPresShell.h"
|
||||
#include "nsHTMLParts.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "nsFloatManager.h"
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "nsStyleConsts.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "LayoutLogging.h"
|
||||
#include "mozilla/RestyleManager.h"
|
||||
|
@ -116,9 +115,6 @@
|
|||
#include "ActiveLayerTracker.h"
|
||||
|
||||
#include "nsITheme.h"
|
||||
#include "nsStyleConsts.h"
|
||||
|
||||
#include "ImageLoader.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::css;
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
|
||||
#include "nsTextFrame.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
|
|
|
@ -6145,7 +6145,8 @@ void ScrollFrameHelper::RestoreState(PresState* aState) {
|
|||
if (mIsRoot) {
|
||||
nsIPresShell* presShell = mOuter->PresShell();
|
||||
if (aState->scaleToResolution()) {
|
||||
presShell->SetResolutionAndScaleTo(aState->resolution());
|
||||
presShell->SetResolutionAndScaleTo(aState->resolution(),
|
||||
nsGkAtoms::restore);
|
||||
} else {
|
||||
presShell->SetResolution(aState->resolution());
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "nsLayoutUtils.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "FrameLayerBuilder.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "nsIURIMutator.h"
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "nsIPresShell.h"
|
||||
#include "nsPageContentFrame.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsLayoutUtils.h" // for function BinarySearchForPosition
|
||||
#include "nsSimplePageSequenceFrame.h" // for nsSharedPageData
|
||||
#include "nsTextFormatter.h" // for page number localization formatting
|
||||
#include "nsBidiUtils.h"
|
||||
|
|
|
@ -82,7 +82,6 @@
|
|||
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "mozilla/gfx/DrawTargetRecording.h"
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "inLayoutUtils.h"
|
||||
|
||||
#include "BindingStyleRule.h"
|
||||
#include "InspectorUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include "nsCSSRendering.h"
|
||||
#include "nsCSSColorUtils.h"
|
||||
#include "nsITheme.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsBlockFrame.h"
|
||||
#include "nsStyleStructInlines.h"
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "nsClassHashtable.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsStyleStruct.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxGradientCache.h"
|
||||
#include "mozilla/layers/StackingContextHelper.h"
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "gfxContext.h"
|
||||
#include "mozilla/ArenaAllocator.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Array.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
|
@ -37,7 +36,6 @@
|
|||
#include "ImgDrawResult.h"
|
||||
#include "mozilla/EffectCompositor.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/gfx/UserData.h"
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "nsSVGDisplayableFrame.h"
|
||||
#include "SVGObserverUtils.h"
|
||||
#include "nsSVGIntegrationUtils.h"
|
||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
|
|
|
@ -47,7 +47,6 @@ static const char sPrintSettingsServiceContractID[] =
|
|||
|
||||
// Print Preview
|
||||
#include "imgIContainer.h" // image animation mode constants
|
||||
#include "nsIWebBrowserPrint.h" // needed for PrintPreview Navigation constants
|
||||
|
||||
// Print Progress
|
||||
#include "nsIPrintProgress.h"
|
||||
|
@ -94,7 +93,6 @@ static const char kPrintingPromptService[] =
|
|||
#include "nsIDeviceContextSpec.h"
|
||||
#include "nsDeviceContextSpecProxy.h"
|
||||
#include "nsViewManager.h"
|
||||
#include "nsView.h"
|
||||
|
||||
#include "nsIPageSequenceFrame.h"
|
||||
#include "nsIURL.h"
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/URLPreloader.h"
|
||||
|
@ -65,7 +64,6 @@
|
|||
#include "mozilla/dom/SRICheck.h"
|
||||
|
||||
#include "mozilla/Encoding.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "MappedDeclarations.h"
|
||||
|
||||
#include "nsAttrValue.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/* list of user agent style sheets that nsLayoutStylesheetCache manages */
|
||||
|
||||
/*
|
||||
* STYLE_SHEET(identifier_, url_, lazy_)
|
||||
*
|
||||
* identifier_
|
||||
* An identifier for the style sheet, suitable for use as an enum class value.
|
||||
*
|
||||
* url_
|
||||
* The URL of the style sheet.
|
||||
*
|
||||
* lazy_
|
||||
* A boolean indicating whether the sheet is loaded lazily.
|
||||
*/
|
||||
|
||||
STYLE_SHEET(ContentEditable, "resource://gre/res/contenteditable.css", true)
|
||||
STYLE_SHEET(CounterStyles, "resource://gre-resources/counterstyles.css", false)
|
||||
STYLE_SHEET(DesignMode, "resource://gre/res/designmode.css", true)
|
||||
STYLE_SHEET(Forms, "resource://gre-resources/forms.css", true)
|
||||
STYLE_SHEET(HTML, "resource://gre-resources/html.css", false)
|
||||
STYLE_SHEET(MathML, "resource://gre-resources/mathml.css", true)
|
||||
STYLE_SHEET(MinimalXUL, "chrome://global/content/minimal-xul.css", false)
|
||||
STYLE_SHEET(NoFrames, "resource://gre-resources/noframes.css", true)
|
||||
STYLE_SHEET(NoScript, "resource://gre-resources/noscript.css", true)
|
||||
STYLE_SHEET(Quirk, "resource://gre-resources/quirk.css", false)
|
||||
STYLE_SHEET(Scrollbars, "chrome://global/skin/scrollbars.css", true)
|
||||
STYLE_SHEET(SVG, "resource://gre/res/svg.css", false)
|
||||
STYLE_SHEET(UA, "resource://gre-resources/ua.css", true)
|
||||
STYLE_SHEET(XUL, "chrome://global/content/xul.css", true)
|
|
@ -112,6 +112,7 @@ EXPORTS.mozilla += [
|
|||
'StyleSheetInfo.h',
|
||||
'StyleSheetInlines.h',
|
||||
'URLExtraData.h',
|
||||
'UserAgentStyleSheetList.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include "nsCSSKeywords.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsStyleConsts.h" // For system widget appearance types
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "nsLayoutStylesheetCache.h"
|
||||
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
@ -60,25 +59,15 @@ nsresult nsLayoutStylesheetCache::Observe(nsISupports* aSubject,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::ScrollbarsSheet() {
|
||||
if (!mScrollbarsSheet) {
|
||||
// Scrollbars don't need access to unsafe rules
|
||||
LoadSheetURL("chrome://global/skin/scrollbars.css", &mScrollbarsSheet,
|
||||
eSafeAgentSheetFeatures, eCrash);
|
||||
#define STYLE_SHEET(identifier_, url_, lazy_) \
|
||||
StyleSheet* nsLayoutStylesheetCache::identifier_##Sheet() { \
|
||||
if (lazy_ && !m##identifier_##Sheet) { \
|
||||
LoadSheetURL(url_, &m##identifier_##Sheet, eAgentSheetFeatures, eCrash); \
|
||||
} \
|
||||
return m##identifier_##Sheet; \
|
||||
}
|
||||
|
||||
return mScrollbarsSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::FormsSheet() {
|
||||
if (!mFormsSheet) {
|
||||
// forms.css needs access to unsafe rules
|
||||
LoadSheetURL("resource://gre-resources/forms.css", &mFormsSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mFormsSheet;
|
||||
}
|
||||
#include "mozilla/UserAgentStyleSheetList.h"
|
||||
#undef STYLE_SHEET
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::UserContentSheet() {
|
||||
return mUserContentSheet;
|
||||
|
@ -88,65 +77,6 @@ StyleSheet* nsLayoutStylesheetCache::UserChromeSheet() {
|
|||
return mUserChromeSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::UASheet() {
|
||||
if (!mUASheet) {
|
||||
LoadSheetURL("resource://gre-resources/ua.css", &mUASheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mUASheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::HTMLSheet() { return mHTMLSheet; }
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::MinimalXULSheet() {
|
||||
return mMinimalXULSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::XULSheet() {
|
||||
if (!mXULSheet) {
|
||||
LoadSheetURL("chrome://global/content/xul.css", &mXULSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mXULSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::QuirkSheet() { return mQuirkSheet; }
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::SVGSheet() { return mSVGSheet; }
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::MathMLSheet() {
|
||||
if (!mMathMLSheet) {
|
||||
LoadSheetURL("resource://gre-resources/mathml.css", &mMathMLSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mMathMLSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::CounterStylesSheet() {
|
||||
return mCounterStylesSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::NoScriptSheet() {
|
||||
if (!mNoScriptSheet) {
|
||||
LoadSheetURL("resource://gre-resources/noscript.css", &mNoScriptSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mNoScriptSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::NoFramesSheet() {
|
||||
if (!mNoFramesSheet) {
|
||||
LoadSheetURL("resource://gre-resources/noframes.css", &mNoFramesSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mNoFramesSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::ChromePreferenceSheet(
|
||||
nsPresContext* aPresContext) {
|
||||
if (!mChromePreferenceSheet) {
|
||||
|
@ -165,24 +95,6 @@ StyleSheet* nsLayoutStylesheetCache::ContentPreferenceSheet(
|
|||
return mContentPreferenceSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::ContentEditableSheet() {
|
||||
if (!mContentEditableSheet) {
|
||||
LoadSheetURL("resource://gre/res/contenteditable.css",
|
||||
&mContentEditableSheet, eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mContentEditableSheet;
|
||||
}
|
||||
|
||||
StyleSheet* nsLayoutStylesheetCache::DesignModeSheet() {
|
||||
if (!mDesignModeSheet) {
|
||||
LoadSheetURL("resource://gre/res/designmode.css", &mDesignModeSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
}
|
||||
|
||||
return mDesignModeSheet;
|
||||
}
|
||||
|
||||
void nsLayoutStylesheetCache::Shutdown() {
|
||||
gCSSLoader = nullptr;
|
||||
NS_WARNING_ASSERTION(!gStyleCache || !gUserContentSheetURL,
|
||||
|
@ -215,24 +127,14 @@ size_t nsLayoutStylesheetCache::SizeOfIncludingThis(
|
|||
|
||||
#define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0;
|
||||
|
||||
#define STYLE_SHEET(identifier_, url_, lazy_) MEASURE(m##identifier_##Sheet);
|
||||
#include "mozilla/UserAgentStyleSheetList.h"
|
||||
#undef STYLE_SHEET
|
||||
|
||||
MEASURE(mChromePreferenceSheet);
|
||||
MEASURE(mContentEditableSheet);
|
||||
MEASURE(mContentPreferenceSheet);
|
||||
MEASURE(mCounterStylesSheet);
|
||||
MEASURE(mDesignModeSheet);
|
||||
MEASURE(mFormsSheet);
|
||||
MEASURE(mHTMLSheet);
|
||||
MEASURE(mMathMLSheet);
|
||||
MEASURE(mMinimalXULSheet);
|
||||
MEASURE(mNoFramesSheet);
|
||||
MEASURE(mNoScriptSheet);
|
||||
MEASURE(mQuirkSheet);
|
||||
MEASURE(mSVGSheet);
|
||||
MEASURE(mScrollbarsSheet);
|
||||
MEASURE(mUASheet);
|
||||
MEASURE(mUserChromeSheet);
|
||||
MEASURE(mUserContentSheet);
|
||||
MEASURE(mXULSheet);
|
||||
|
||||
// Measurement of the following members may be added later if DMD finds it is
|
||||
// worthwhile:
|
||||
|
@ -256,16 +158,13 @@ nsLayoutStylesheetCache::nsLayoutStylesheetCache() {
|
|||
|
||||
// And make sure that we load our UA sheets. No need to do this
|
||||
// per-profile, since they're profile-invariant.
|
||||
LoadSheetURL("resource://gre-resources/counterstyles.css",
|
||||
&mCounterStylesSheet, eAgentSheetFeatures, eCrash);
|
||||
LoadSheetURL("resource://gre-resources/html.css", &mHTMLSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
LoadSheetURL("chrome://global/content/minimal-xul.css", &mMinimalXULSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
LoadSheetURL("resource://gre-resources/quirk.css", &mQuirkSheet,
|
||||
eAgentSheetFeatures, eCrash);
|
||||
LoadSheetURL("resource://gre/res/svg.css", &mSVGSheet, eAgentSheetFeatures,
|
||||
eCrash);
|
||||
#define STYLE_SHEET(identifier_, url_, lazy_) \
|
||||
if (!lazy_) { \
|
||||
LoadSheetURL(url_, &m##identifier_##Sheet, eAgentSheetFeatures, eCrash); \
|
||||
}
|
||||
#include "mozilla/UserAgentStyleSheetList.h"
|
||||
#undef STYLE_SHEET
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// We know we need xul.css for the UI, so load that now too:
|
||||
XULSheet();
|
||||
|
|
|
@ -38,24 +38,15 @@ class nsLayoutStylesheetCache final : public nsIObserver,
|
|||
|
||||
static nsLayoutStylesheetCache* Singleton();
|
||||
|
||||
mozilla::StyleSheet* ScrollbarsSheet();
|
||||
mozilla::StyleSheet* FormsSheet();
|
||||
#define STYLE_SHEET(identifier_, url_, lazy_) \
|
||||
mozilla::StyleSheet* identifier_##Sheet();
|
||||
#include "mozilla/UserAgentStyleSheetList.h"
|
||||
#undef STYLE_SHEET
|
||||
|
||||
mozilla::StyleSheet* UserContentSheet();
|
||||
mozilla::StyleSheet* UserChromeSheet();
|
||||
mozilla::StyleSheet* UASheet();
|
||||
mozilla::StyleSheet* HTMLSheet();
|
||||
mozilla::StyleSheet* MinimalXULSheet();
|
||||
mozilla::StyleSheet* XULSheet();
|
||||
mozilla::StyleSheet* QuirkSheet();
|
||||
mozilla::StyleSheet* SVGSheet();
|
||||
mozilla::StyleSheet* MathMLSheet();
|
||||
mozilla::StyleSheet* CounterStylesSheet();
|
||||
mozilla::StyleSheet* NoScriptSheet();
|
||||
mozilla::StyleSheet* NoFramesSheet();
|
||||
mozilla::StyleSheet* ChromePreferenceSheet(nsPresContext* aPresContext);
|
||||
mozilla::StyleSheet* ContentPreferenceSheet(nsPresContext* aPresContext);
|
||||
mozilla::StyleSheet* ContentEditableSheet();
|
||||
mozilla::StyleSheet* DesignModeSheet();
|
||||
|
||||
static void InvalidatePreferenceSheets();
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "nsStyleStruct.h"
|
||||
#include "nsStyleStructInlines.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsString.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIAppShellService.h"
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "nsIDocument.h"
|
||||
#include "nsSVGPaintServerFrame.h"
|
||||
#include "SVGObserverUtils.h"
|
||||
#include "nsSVGPaintServerFrame.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::image;
|
||||
|
|
|
@ -8,22 +8,24 @@
|
|||
#include "SVGObserverUtils.h"
|
||||
|
||||
// Keep others in (case-insensitive) order:
|
||||
#include "mozilla/css/ImageLoader.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "nsCSSFrameConstructor.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include "nsIReflowCallback.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsSVGClipPathFrame.h"
|
||||
#include "nsSVGMarkerFrame.h"
|
||||
#include "nsSVGPaintServerFrame.h"
|
||||
#include "nsSVGFilterFrame.h"
|
||||
#include "nsSVGMarkerFrame.h"
|
||||
#include "nsSVGMaskFrame.h"
|
||||
#include "nsIReflowCallback.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsSVGPaintServerFrame.h"
|
||||
#include "SVGGeometryElement.h"
|
||||
#include "SVGTextPathElement.h"
|
||||
#include "SVGUseElement.h"
|
||||
#include "ImageLoader.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "nsSVGUtils.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "FilterSupport.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "nscore.h"
|
||||
#include "celldata.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsRect.h"
|
||||
|
|
|
@ -437,6 +437,64 @@ class AccessibilityTest : BaseSessionTest() {
|
|||
waitUntilTextTraversed(0, 18) // "Lorem ipsum dolor "
|
||||
}
|
||||
|
||||
@Test fun testHeadings() {
|
||||
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID;
|
||||
sessionRule.session.loadString("""
|
||||
<a href=\"%23\">preamble</a>
|
||||
<h1>Fried cheese</h1><p>with club sauce.</p>
|
||||
<h2>Popcorn shrimp</h2><button>with club sauce.</button>
|
||||
<h3>Chicken fingers</h3><p>with spicy club sauce.</p>""".trimIndent(), "text/html")
|
||||
waitForInitialFocus()
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "HEADING")
|
||||
|
||||
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, bundle)
|
||||
sessionRule.waitUntilCalled(object : EventDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onAccessibilityFocused(event: AccessibilityEvent) {
|
||||
nodeId = getSourceId(event)
|
||||
val node = createNodeInfo(nodeId)
|
||||
assertThat("Accessibility focus on first heading", node.text as String, startsWith("Fried cheese"))
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
assertThat("First heading is level 1",
|
||||
node.extras.getCharSequence("AccessibilityNodeInfo.roleDescription").toString(),
|
||||
equalTo("heading level 1"))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, bundle)
|
||||
sessionRule.waitUntilCalled(object : EventDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onAccessibilityFocused(event: AccessibilityEvent) {
|
||||
nodeId = getSourceId(event)
|
||||
val node = createNodeInfo(nodeId)
|
||||
assertThat("Accessibility focus on second heading", node.text as String, startsWith("Popcorn shrimp"))
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
assertThat("Second heading is level 2",
|
||||
node.extras.getCharSequence("AccessibilityNodeInfo.roleDescription").toString(),
|
||||
equalTo("heading level 2"))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, bundle)
|
||||
sessionRule.waitUntilCalled(object : EventDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onAccessibilityFocused(event: AccessibilityEvent) {
|
||||
nodeId = getSourceId(event)
|
||||
val node = createNodeInfo(nodeId)
|
||||
assertThat("Accessibility focus on second heading", node.text as String, startsWith("Chicken fingers"))
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
assertThat("Third heading is level 3",
|
||||
node.extras.getCharSequence("AccessibilityNodeInfo.roleDescription").toString(),
|
||||
equalTo("heading level 3"))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Test fun testCheckbox() {
|
||||
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID;
|
||||
sessionRule.session.loadString("<label><input type='checkbox'>many option</label>", "text/html")
|
||||
|
|
|
@ -40,7 +40,7 @@ APPLICATION_CHOICE = '''
|
|||
Note on Artifact Mode:
|
||||
|
||||
Artifact builds download prebuilt C++ components rather than building
|
||||
them locally.
|
||||
them locally. Artifact builds are faster!
|
||||
|
||||
Artifact builds are recommended for people working on Firefox or
|
||||
Firefox for Android frontends. They are unsuitable for those working
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[DEFAULT]
|
||||
subsuite = mozlint, os == "linux"
|
||||
subsuite = mozlint
|
||||
skip-if = python == 3
|
||||
|
||||
[test_cli.py]
|
||||
|
|
|
@ -4,11 +4,14 @@ job-defaults:
|
|||
worker-type:
|
||||
by-platform:
|
||||
linux64.*: aws-provisioner-v1/gecko-t-linux-xlarge
|
||||
windows10-64.*: aws-provisioner-v1/gecko-t-win10-64
|
||||
worker:
|
||||
by-platform:
|
||||
linux64.*:
|
||||
docker-image: {in-tree: "lint"}
|
||||
max-run-time: 3600
|
||||
default:
|
||||
max-run-time: 3600
|
||||
treeherder:
|
||||
kind: test
|
||||
tier: 2
|
||||
|
@ -34,6 +37,9 @@ taskgraph-tests:
|
|||
|
||||
marionette-harness:
|
||||
description: testing/marionette/harness unit tests
|
||||
platform:
|
||||
- linux64/opt
|
||||
- windows10-64/opt
|
||||
python-version: [2]
|
||||
treeherder:
|
||||
symbol: mnh
|
||||
|
@ -89,6 +95,9 @@ mochitest-harness:
|
|||
|
||||
mozbase:
|
||||
description: testing/mozbase unit tests
|
||||
platform:
|
||||
- linux64/opt
|
||||
- windows10-64/opt
|
||||
python-version: [2, 3]
|
||||
treeherder:
|
||||
symbol: mb
|
||||
|
@ -115,6 +124,9 @@ mozharness:
|
|||
|
||||
mozlint:
|
||||
description: python/mozlint unit tests
|
||||
platform:
|
||||
- linux64/opt
|
||||
- windows10-64/opt
|
||||
python-version: [2]
|
||||
treeherder:
|
||||
symbol: ml
|
||||
|
@ -140,6 +152,9 @@ mozrelease:
|
|||
|
||||
mozterm:
|
||||
description: python/mozterm unit tests
|
||||
platform:
|
||||
- linux64/opt
|
||||
- windows10-64/opt
|
||||
python-version: [2, 3]
|
||||
treeherder:
|
||||
symbol: term
|
||||
|
@ -164,6 +179,9 @@ mozversioncontrol:
|
|||
|
||||
raptor:
|
||||
description: testing/raptor unit tests
|
||||
platform:
|
||||
- linux64/opt
|
||||
- windows10-64/opt
|
||||
python-version: [2]
|
||||
treeherder:
|
||||
symbol: rap
|
||||
|
@ -215,6 +233,9 @@ reftest-harness:
|
|||
|
||||
tryselect:
|
||||
description: tools/tryselect unit tests
|
||||
platform:
|
||||
- linux64/opt
|
||||
- windows10-64/opt
|
||||
python-version: [2]
|
||||
treeherder:
|
||||
symbol: try
|
||||
|
|
|
@ -448,13 +448,14 @@ def vcs_checkout(source_repo, dest, store_path,
|
|||
if sparse_profile:
|
||||
args.extend(['--sparseprofile', sparse_profile])
|
||||
|
||||
dest = os.path.abspath(dest)
|
||||
args.extend([
|
||||
revision_flag, revision_value,
|
||||
source_repo, dest,
|
||||
])
|
||||
|
||||
res = run_and_prefix_output(b'vcs', args,
|
||||
extra_env={b'PYTHONUNBUFFERED': b'1'})
|
||||
extra_env={'PYTHONUNBUFFERED': '1'})
|
||||
if res:
|
||||
sys.exit(res)
|
||||
|
||||
|
@ -741,6 +742,9 @@ def main(args):
|
|||
return 1
|
||||
|
||||
try:
|
||||
if 'GECKO_PATH' in os.environ:
|
||||
os.environ['GECKO_PATH'] = os.path.abspath(os.environ['GECKO_PATH'])
|
||||
|
||||
if 'MOZ_FETCHES' in os.environ:
|
||||
fetch_artifacts()
|
||||
|
||||
|
|
|
@ -102,8 +102,8 @@ CONFIG_PER_BOUNCER_PRODUCT = {
|
|||
'name_postfix': '-msi-SSL',
|
||||
'path_template': RELEASES_PATH_TEMPLATE,
|
||||
'file_names': {
|
||||
'win': '{pretty_product}%20Installer.msi',
|
||||
'win64': '{pretty_product}%20Installer.msi',
|
||||
'win': '{pretty_product}%20Setup%20{version}.msi',
|
||||
'win64': '{pretty_product}%20Setup%20{version}.msi',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,10 +64,20 @@ def support_vcs_checkout(config, job, taskdesc, sparse=False):
|
|||
This can only be used with ``run-task`` tasks, as the cache name is
|
||||
reserved for ``run-task`` tasks.
|
||||
"""
|
||||
level = config.params['level']
|
||||
is_win = job['worker']['os'] == 'windows'
|
||||
|
||||
# native-engine does not support caches (yet), so we just do a full clone
|
||||
# every time :(
|
||||
if is_win:
|
||||
checkoutdir = './build'
|
||||
geckodir = '{}/src'.format(checkoutdir)
|
||||
hgstore = 'y:/hg-shared'
|
||||
else:
|
||||
checkoutdir = '{workdir}/checkouts'.format(**job['run'])
|
||||
geckodir = '{}/gecko'.format(checkoutdir)
|
||||
hgstore = '{}/hg-store'.format(checkoutdir)
|
||||
|
||||
level = config.params['level']
|
||||
# native-engine and generic-worker do not support caches (yet), so we just
|
||||
# do a full clone every time :(
|
||||
if job['worker']['implementation'] in ('docker-worker', 'docker-engine'):
|
||||
name = 'level-%s-checkouts' % level
|
||||
|
||||
|
@ -84,15 +94,15 @@ def support_vcs_checkout(config, job, taskdesc, sparse=False):
|
|||
taskdesc['worker'].setdefault('caches', []).append({
|
||||
'type': 'persistent',
|
||||
'name': name,
|
||||
'mount-point': '{workdir}/checkouts'.format(**job['run']),
|
||||
'mount-point': checkoutdir,
|
||||
})
|
||||
|
||||
taskdesc['worker'].setdefault('env', {}).update({
|
||||
'GECKO_BASE_REPOSITORY': config.params['base_repository'],
|
||||
'GECKO_HEAD_REPOSITORY': config.params['head_repository'],
|
||||
'GECKO_HEAD_REV': config.params['head_rev'],
|
||||
'GECKO_PATH': '{workdir}/checkouts/gecko'.format(**job['run']),
|
||||
'HG_STORE_PATH': '{workdir}/checkouts/hg-store'.format(**job['run']),
|
||||
'GECKO_PATH': geckodir,
|
||||
'HG_STORE_PATH': hgstore,
|
||||
})
|
||||
|
||||
if 'comm_base_repository' in config.params:
|
||||
|
|
|
@ -30,14 +30,19 @@ mach_schema = Schema({
|
|||
})
|
||||
|
||||
|
||||
@run_job_using("docker-worker", "mach", schema=mach_schema, defaults={'comm-checkout': False})
|
||||
@run_job_using("native-engine", "mach", schema=mach_schema, defaults={'comm-checkout': False})
|
||||
@run_job_using("generic-worker", "mach", schema=mach_schema, defaults={'comm-checkout': False})
|
||||
def docker_worker_mach(config, job, taskdesc):
|
||||
defaults = {
|
||||
'comm-checkout': False,
|
||||
}
|
||||
|
||||
|
||||
@run_job_using("docker-worker", "mach", schema=mach_schema, defaults=defaults)
|
||||
@run_job_using("native-engine", "mach", schema=mach_schema, defaults=defaults)
|
||||
@run_job_using("generic-worker", "mach", schema=mach_schema, defaults=defaults)
|
||||
def configure_mach(config, job, taskdesc):
|
||||
run = job['run']
|
||||
|
||||
# defer to the run_task implementation
|
||||
run['command'] = 'cd {workdir}/checkouts/gecko && ./mach {mach}'.format(**run)
|
||||
run['command'] = 'cd $GECKO_PATH && ./mach {mach}'.format(**run)
|
||||
run['using'] = 'run-task'
|
||||
del run['mach']
|
||||
configure_taskdesc_for_run(config, job, taskdesc, job['worker']['implementation'])
|
||||
|
|
|
@ -25,12 +25,15 @@ python_test_schema = Schema({
|
|||
})
|
||||
|
||||
|
||||
@run_job_using(
|
||||
'docker-worker',
|
||||
'python-test',
|
||||
schema=python_test_schema,
|
||||
defaults={'python-version': 2, 'subsuite': 'default'})
|
||||
def docker_worker_python_test(config, job, taskdesc):
|
||||
defaults = {
|
||||
'python-version': 2,
|
||||
'subsuite': 'default',
|
||||
}
|
||||
|
||||
|
||||
@run_job_using('docker-worker', 'python-test', schema=python_test_schema, defaults=defaults)
|
||||
@run_job_using('generic-worker', 'python-test', schema=python_test_schema, defaults=defaults)
|
||||
def configure_python_test(config, job, taskdesc):
|
||||
run = job['run']
|
||||
|
||||
# defer to the mach implementation
|
||||
|
|
|
@ -41,12 +41,12 @@ run_task_schema = Schema({
|
|||
})
|
||||
|
||||
|
||||
def common_setup(config, job, taskdesc, command, checkoutdir):
|
||||
def common_setup(config, job, taskdesc, command, geckodir):
|
||||
run = job['run']
|
||||
if run['checkout']:
|
||||
support_vcs_checkout(config, job, taskdesc,
|
||||
sparse=bool(run['sparse-profile']))
|
||||
command.append('--vcs-checkout={}/gecko'.format(checkoutdir))
|
||||
command.append('--vcs-checkout={}'.format(geckodir))
|
||||
|
||||
if run['sparse-profile']:
|
||||
command.append('--sparse-profile=build/sparse-profiles/%s' %
|
||||
|
@ -73,7 +73,8 @@ def docker_worker_run_task(config, job, taskdesc):
|
|||
run = job['run']
|
||||
worker = taskdesc['worker'] = job['worker']
|
||||
command = ['/builds/worker/bin/run-task']
|
||||
common_setup(config, job, taskdesc, command, checkoutdir='{workdir}/checkouts'.format(**run))
|
||||
common_setup(config, job, taskdesc, command,
|
||||
geckodir='{workdir}/checkouts/gecko'.format(**run))
|
||||
|
||||
if run.get('cache-dotcache'):
|
||||
worker['caches'].append({
|
||||
|
@ -100,7 +101,8 @@ def native_engine_run_task(config, job, taskdesc):
|
|||
run = job['run']
|
||||
worker = taskdesc['worker'] = job['worker']
|
||||
command = ['./run-task']
|
||||
common_setup(config, job, taskdesc, command, checkoutdir='{workdir}/checkouts'.format(**run))
|
||||
common_setup(config, job, taskdesc, command,
|
||||
geckodir='{workdir}/checkouts/gecko'.format(**run))
|
||||
|
||||
worker['context'] = run_task_url(config)
|
||||
|
||||
|
@ -119,8 +121,16 @@ def native_engine_run_task(config, job, taskdesc):
|
|||
def generic_worker_run_task(config, job, taskdesc):
|
||||
run = job['run']
|
||||
worker = taskdesc['worker'] = job['worker']
|
||||
command = ['./run-task']
|
||||
common_setup(config, job, taskdesc, command, checkoutdir='{workdir}/checkouts'.format(**run))
|
||||
is_win = worker['os'] == 'windows'
|
||||
|
||||
if is_win:
|
||||
command = ['C:/mozilla-build/python3/python3.exe', 'run-task']
|
||||
geckodir = './build/src'
|
||||
else:
|
||||
command = ['./run-task']
|
||||
geckodir = '{workdir}/checkouts/gecko'.format(**run)
|
||||
|
||||
common_setup(config, job, taskdesc, command, geckodir=geckodir)
|
||||
|
||||
worker.setdefault('mounts', [])
|
||||
if run.get('cache-dotcache'):
|
||||
|
@ -137,7 +147,17 @@ def generic_worker_run_task(config, job, taskdesc):
|
|||
|
||||
run_command = run['command']
|
||||
if isinstance(run_command, basestring):
|
||||
if is_win:
|
||||
run_command = '"{}"'.format(run_command)
|
||||
run_command = ['bash', '-cx', run_command]
|
||||
|
||||
command.append('--')
|
||||
command.extend(run_command)
|
||||
worker['command'] = [['chmod', '+x', 'run-task'], command]
|
||||
|
||||
if is_win:
|
||||
worker['command'] = [' '.join(command)]
|
||||
else:
|
||||
worker['command'] = [
|
||||
['chmod', '+x', 'run-task'],
|
||||
command,
|
||||
]
|
||||
|
|
|
@ -1880,8 +1880,8 @@ def check_caches_are_volumes(task):
|
|||
return
|
||||
|
||||
raise Exception('task %s (image %s) has caches that are not declared as '
|
||||
'Docker volumes: %s'
|
||||
'Have you added them as VOLUMEs in the Dockerfile?'
|
||||
'Docker volumes: %s '
|
||||
'(have you added them as VOLUMEs in the Dockerfile?)'
|
||||
% (task['label'], task['worker']['docker-image'],
|
||||
', '.join(sorted(missing))))
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@ const {
|
|||
UnsupportedOperationError,
|
||||
} = ChromeUtils.import("chrome://marionette/content/error.js", {});
|
||||
const {
|
||||
MessageManagerDestroyedPromise,
|
||||
waitForEvent,
|
||||
waitForObserverTopic,
|
||||
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["browser", "Context", "WindowState"];
|
||||
|
@ -70,11 +71,11 @@ this.Context = Context;
|
|||
*/
|
||||
browser.getBrowserForTab = function(tab) {
|
||||
// Fennec
|
||||
if ("browser" in tab) {
|
||||
if (tab && "browser" in tab) {
|
||||
return tab.browser;
|
||||
|
||||
// Firefox
|
||||
} else if ("linkedBrowser" in tab) {
|
||||
} else if (tab && "linkedBrowser" in tab) {
|
||||
return tab.linkedBrowser;
|
||||
}
|
||||
|
||||
|
@ -287,17 +288,59 @@ browser.Context = class {
|
|||
* A promise which is resolved when the current window has been closed.
|
||||
*/
|
||||
closeWindow() {
|
||||
return new Promise(resolve => {
|
||||
// Wait for the window message manager to be destroyed
|
||||
let destroyed = new MessageManagerDestroyedPromise(
|
||||
this.window.messageManager);
|
||||
// Create a copy of the messageManager before it is disconnected
|
||||
let messageManager = this.window.messageManager;
|
||||
let disconnected = waitForObserverTopic("message-manager-disconnect",
|
||||
subject => subject === messageManager);
|
||||
let unloaded = waitForEvent(this.window, "unload");
|
||||
|
||||
this.window.addEventListener("unload", async () => {
|
||||
await destroyed;
|
||||
resolve();
|
||||
}, {once: true});
|
||||
this.window.close();
|
||||
});
|
||||
this.window.close();
|
||||
|
||||
return Promise.all([disconnected, unloaded]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new browser window.
|
||||
*
|
||||
* @return {Promise}
|
||||
* A promise resolving to the newly created chrome window.
|
||||
*/
|
||||
async openBrowserWindow(focus = false) {
|
||||
switch (this.driver.appName) {
|
||||
case "firefox":
|
||||
// Open new browser window, and wait until it is fully loaded.
|
||||
// Also wait for the window to be focused and activated to prevent a
|
||||
// race condition when promptly focusing to the original window again.
|
||||
let win = this.window.OpenBrowserWindow();
|
||||
|
||||
// Bug 1509380 - Missing focus/activate event when Firefox is not
|
||||
// the top-most application. As such wait for the next tick, and
|
||||
// manually focus the newly opened window.
|
||||
win.setTimeout(() => win.focus(), 0);
|
||||
|
||||
let activated = waitForEvent(win, "activate");
|
||||
let focused = waitForEvent(win, "focus", {capture: true});
|
||||
let startup = waitForObserverTopic("browser-delayed-startup-finished",
|
||||
subject => subject == win);
|
||||
await Promise.all([activated, focused, startup]);
|
||||
|
||||
if (!focus) {
|
||||
// The new window shouldn't get focused. As such set the
|
||||
// focus back to the currently selected window.
|
||||
activated = waitForEvent(this.window, "activate");
|
||||
focused = waitForEvent(this.window, "focus", {capture: true});
|
||||
|
||||
this.window.focus();
|
||||
|
||||
await Promise.all([activated, focused]);
|
||||
}
|
||||
|
||||
return win;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`openWindow() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -319,40 +362,65 @@ browser.Context = class {
|
|||
return this.closeWindow();
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Wait for the browser message manager to be destroyed
|
||||
let browserDetached = async () => {
|
||||
await new MessageManagerDestroyedPromise(this.messageManager);
|
||||
resolve();
|
||||
};
|
||||
// Create a copy of the messageManager before it is disconnected
|
||||
let messageManager = this.messageManager;
|
||||
let disconnected = waitForObserverTopic("message-manager-disconnect",
|
||||
subject => subject === messageManager);
|
||||
|
||||
if (this.tabBrowser.closeTab) {
|
||||
let tabClosed;
|
||||
|
||||
switch (this.driver.appName) {
|
||||
case "fennec":
|
||||
// Fennec
|
||||
this.tabBrowser.deck.addEventListener(
|
||||
"TabClose", browserDetached, {once: true});
|
||||
tabClosed = waitForEvent(this.tabBrowser.deck, "TabClose");
|
||||
this.tabBrowser.closeTab(this.tab);
|
||||
break;
|
||||
|
||||
} else if (this.tabBrowser.removeTab) {
|
||||
// Firefox
|
||||
this.tab.addEventListener(
|
||||
"TabClose", browserDetached, {once: true});
|
||||
case "firefox":
|
||||
tabClosed = waitForEvent(this.tab, "TabClose");
|
||||
this.tabBrowser.removeTab(this.tab);
|
||||
break;
|
||||
|
||||
} else {
|
||||
reject(new UnsupportedOperationError(
|
||||
`closeTab() not supported in ${this.driver.appName}`));
|
||||
}
|
||||
});
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`closeTab() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
|
||||
return Promise.all([disconnected, tabClosed]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a tab with given URI.
|
||||
*
|
||||
* @param {string} uri
|
||||
* URI to open.
|
||||
* Open a new tab in the currently selected chrome window.
|
||||
*/
|
||||
addTab(uri) {
|
||||
return this.tabBrowser.addTab(uri, true);
|
||||
async openTab(focus = false) {
|
||||
let tab = null;
|
||||
let tabOpened = waitForEvent(this.window, "TabOpen");
|
||||
|
||||
switch (this.driver.appName) {
|
||||
case "fennec":
|
||||
tab = this.tabBrowser.addTab(null, {selected: focus});
|
||||
break;
|
||||
|
||||
case "firefox":
|
||||
this.window.BrowserOpenTab();
|
||||
tab = this.tabBrowser.selectedTab;
|
||||
|
||||
// The new tab is always selected by default. If focus is not wanted,
|
||||
// the previously tab needs to be selected again.
|
||||
if (!focus) {
|
||||
this.tabBrowser.selectedTab = this.tab;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`openTab() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
|
||||
await tabOpened;
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -386,16 +454,18 @@ browser.Context = class {
|
|||
this.tab = this.tabBrowser.tabs[index];
|
||||
|
||||
if (focus) {
|
||||
if (this.tabBrowser.selectTab) {
|
||||
// Fennec
|
||||
this.tabBrowser.selectTab(this.tab);
|
||||
switch (this.driver.appName) {
|
||||
case "fennec":
|
||||
this.tabBrowser.selectTab(this.tab);
|
||||
break;
|
||||
|
||||
} else if ("selectedTab" in this.tabBrowser) {
|
||||
// Firefox
|
||||
this.tabBrowser.selectedTab = this.tab;
|
||||
case "firefox":
|
||||
this.tabBrowser.selectedTab = this.tab;
|
||||
break;
|
||||
|
||||
} else {
|
||||
throw new UnsupportedOperationError("switchToTab() not supported");
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`switchToTab() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1428,6 +1428,20 @@ class Marionette(object):
|
|||
return self._send_message("WebDriver:GetPageSource",
|
||||
key="value")
|
||||
|
||||
def open(self, type=None, focus=False):
|
||||
"""Open a new window, or tab based on the specified context type.
|
||||
|
||||
If no context type is given the application will choose the best
|
||||
option based on tab and window support.
|
||||
|
||||
:param type: Type of window to be opened. Can be one of "tab" or "window"
|
||||
:param focus: If true, the opened window will be focused
|
||||
|
||||
:returns: Dict with new window handle, and type of opened window
|
||||
"""
|
||||
body = {"type": type, "focus": focus}
|
||||
return self._send_message("WebDriver:NewWindow", body)
|
||||
|
||||
def close(self):
|
||||
"""Close the current window, ending the session if it's the last
|
||||
window currently open.
|
||||
|
|
|
@ -3,8 +3,7 @@ sync module
|
|||
|
||||
Provides an assortment of synchronisation primitives.
|
||||
|
||||
.. js:autoclass:: MessageManagerDestroyedPromise
|
||||
:members:
|
||||
.. js:autofunction:: executeSoon
|
||||
|
||||
.. js:autoclass:: PollPromise
|
||||
:members:
|
||||
|
@ -14,3 +13,9 @@ Provides an assortment of synchronisation primitives.
|
|||
|
||||
.. js:autoclass:: TimedPromise
|
||||
:members:
|
||||
|
||||
.. js:autofunction:: waitForEvent
|
||||
|
||||
.. js:autofunction:: waitForMessage
|
||||
|
||||
.. js:autofunction:: waitForObserverTopic
|
||||
|
|
|
@ -62,6 +62,8 @@ const {
|
|||
IdlePromise,
|
||||
PollPromise,
|
||||
TimedPromise,
|
||||
waitForEvent,
|
||||
waitForObserverTopic,
|
||||
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
|
||||
|
@ -106,13 +108,12 @@ const globalMessageManager = Services.mm;
|
|||
*
|
||||
* @class GeckoDriver
|
||||
*
|
||||
* @param {string} appId
|
||||
* Unique identifier of the application.
|
||||
* @param {MarionetteServer} server
|
||||
* The instance of Marionette server.
|
||||
*/
|
||||
this.GeckoDriver = function(appId, server) {
|
||||
this.appId = appId;
|
||||
this.GeckoDriver = function(server) {
|
||||
this.appId = Services.appinfo.ID;
|
||||
this.appName = Services.appinfo.name.toLowerCase();
|
||||
this._server = server;
|
||||
|
||||
this.sessionID = null;
|
||||
|
@ -1307,6 +1308,7 @@ GeckoDriver.prototype.getIdForBrowser = function(browser) {
|
|||
if (browser === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let permKey = browser.permanentKey;
|
||||
if (this._browserIds.has(permKey)) {
|
||||
return this._browserIds.get(permKey);
|
||||
|
@ -2722,6 +2724,73 @@ GeckoDriver.prototype.deleteCookie = async function(cmd) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a new top-level browsing context.
|
||||
*
|
||||
* @param {string=} type
|
||||
* Optional type of the new top-level browsing context. Can be one of
|
||||
* `tab` or `window`.
|
||||
* @param {boolean=} focus
|
||||
* Optional flag if the new top-level browsing context should be opened
|
||||
* in foreground (focused) or background (not focused).
|
||||
*
|
||||
* @return {Object.<string, string>}
|
||||
* Handle and type of the new browsing context.
|
||||
*/
|
||||
GeckoDriver.prototype.newWindow = async function(cmd) {
|
||||
assert.open(this.getCurrentWindow(Context.Content));
|
||||
await this._handleUserPrompts();
|
||||
|
||||
let focus = false;
|
||||
if (typeof cmd.parameters.focus != "undefined") {
|
||||
focus = assert.boolean(cmd.parameters.focus,
|
||||
pprint`Expected "focus" to be a boolean, got ${cmd.parameters.focus}`);
|
||||
}
|
||||
|
||||
let type;
|
||||
if (typeof cmd.parameters.type != "undefined") {
|
||||
type = assert.string(cmd.parameters.type,
|
||||
pprint`Expected "type" to be a string, got ${cmd.parameters.type}`);
|
||||
}
|
||||
|
||||
let types = ["tab", "window"];
|
||||
switch (this.appName) {
|
||||
case "firefox":
|
||||
if (typeof type == "undefined" || !types.includes(type)) {
|
||||
type = "window";
|
||||
}
|
||||
break;
|
||||
case "fennec":
|
||||
if (typeof type == "undefined" || !types.includes(type)) {
|
||||
type = "tab";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let contentBrowser;
|
||||
|
||||
switch (type) {
|
||||
case "tab":
|
||||
let tab = await this.curBrowser.openTab(focus);
|
||||
contentBrowser = browser.getBrowserForTab(tab);
|
||||
break;
|
||||
|
||||
default:
|
||||
let win = await this.curBrowser.openBrowserWindow(focus);
|
||||
contentBrowser = browser.getTabBrowser(win).selectedBrowser;
|
||||
}
|
||||
|
||||
// Even with the framescript registered, the browser might not be known to
|
||||
// the parent process yet. Wait until it is available.
|
||||
// TODO: Fix by using `Browser:Init` or equivalent on bug 1311041
|
||||
let windowId = await new PollPromise((resolve, reject) => {
|
||||
let id = this.getIdForBrowser(contentBrowser);
|
||||
this.windowHandles.includes(id) ? resolve(id) : reject();
|
||||
});
|
||||
|
||||
return {"handle": windowId.toString(), type};
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the currently selected tab/window.
|
||||
*
|
||||
|
@ -3101,16 +3170,14 @@ GeckoDriver.prototype.dismissDialog = async function() {
|
|||
let win = assert.open(this.getCurrentWindow());
|
||||
this._checkIfAlertIsPresent();
|
||||
|
||||
await new Promise(resolve => {
|
||||
win.addEventListener("DOMModalDialogClosed", async () => {
|
||||
await new IdlePromise(win);
|
||||
this.dialog = null;
|
||||
resolve();
|
||||
}, {once: true});
|
||||
let dialogClosed = waitForEvent(win, "DOMModalDialogClosed");
|
||||
|
||||
let {button0, button1} = this.dialog.ui;
|
||||
(button1 ? button1 : button0).click();
|
||||
});
|
||||
let {button0, button1} = this.dialog.ui;
|
||||
(button1 ? button1 : button0).click();
|
||||
|
||||
await dialogClosed;
|
||||
|
||||
this.dialog = null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3121,16 +3188,14 @@ GeckoDriver.prototype.acceptDialog = async function() {
|
|||
let win = assert.open(this.getCurrentWindow());
|
||||
this._checkIfAlertIsPresent();
|
||||
|
||||
await new Promise(resolve => {
|
||||
win.addEventListener("DOMModalDialogClosed", async () => {
|
||||
await new IdlePromise(win);
|
||||
this.dialog = null;
|
||||
resolve();
|
||||
}, {once: true});
|
||||
let dialogClosed = waitForEvent(win, "DOMModalDialogClosed");
|
||||
|
||||
let {button0} = this.dialog.ui;
|
||||
button0.click();
|
||||
});
|
||||
let {button0} = this.dialog.ui;
|
||||
button0.click();
|
||||
|
||||
await dialogClosed;
|
||||
|
||||
this.dialog = null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3301,15 +3366,10 @@ GeckoDriver.prototype.quit = async function(cmd) {
|
|||
this.deleteSession();
|
||||
|
||||
// delay response until the application is about to quit
|
||||
let quitApplication = new Promise(resolve => {
|
||||
Services.obs.addObserver(
|
||||
(subject, topic, data) => resolve(data),
|
||||
"quit-application");
|
||||
});
|
||||
|
||||
let quitApplication = waitForObserverTopic("quit-application");
|
||||
Services.startup.quit(mode);
|
||||
|
||||
return {cause: await quitApplication};
|
||||
return {cause: (await quitApplication).data};
|
||||
};
|
||||
|
||||
GeckoDriver.prototype.installAddon = function(cmd) {
|
||||
|
@ -3571,6 +3631,7 @@ GeckoDriver.prototype.commands = {
|
|||
"WebDriver:MaximizeWindow": GeckoDriver.prototype.maximizeWindow,
|
||||
"WebDriver:Navigate": GeckoDriver.prototype.get,
|
||||
"WebDriver:NewSession": GeckoDriver.prototype.newSession,
|
||||
"WebDriver:NewWindow": GeckoDriver.prototype.newWindow,
|
||||
"WebDriver:PerformActions": GeckoDriver.prototype.performActions,
|
||||
"WebDriver:Refresh": GeckoDriver.prototype.refresh,
|
||||
"WebDriver:ReleaseActions": GeckoDriver.prototype.releaseActions,
|
||||
|
|
|
@ -6,14 +6,12 @@ from __future__ import absolute_import
|
|||
|
||||
import sys
|
||||
|
||||
from marionette_driver import By, Wait
|
||||
from marionette_driver import Wait
|
||||
from six import reraise
|
||||
|
||||
|
||||
class WindowManagerMixin(object):
|
||||
|
||||
_menu_item_new_tab = (By.ID, "menu_newNavigatorTab")
|
||||
|
||||
def setUp(self):
|
||||
super(WindowManagerMixin, self).setUp()
|
||||
|
||||
|
@ -60,15 +58,18 @@ class WindowManagerMixin(object):
|
|||
|
||||
self.marionette.switch_to_window(self.start_window)
|
||||
|
||||
def open_tab(self, trigger="menu"):
|
||||
def open_tab(self, callback=None, focus=False):
|
||||
current_tabs = self.marionette.window_handles
|
||||
|
||||
try:
|
||||
if callable(trigger):
|
||||
trigger()
|
||||
elif trigger == 'menu':
|
||||
with self.marionette.using_context("chrome"):
|
||||
self.marionette.find_element(*self._menu_item_new_tab).click()
|
||||
if callable(callback):
|
||||
callback()
|
||||
else:
|
||||
result = self.marionette.open(type="tab", focus=focus)
|
||||
if result["type"] != "tab":
|
||||
raise Exception(
|
||||
"Newly opened browsing context is of type {} and not tab.".format(
|
||||
result["type"]))
|
||||
except Exception:
|
||||
exc, val, tb = sys.exc_info()
|
||||
reraise(exc, 'Failed to trigger opening a new tab: {}'.format(val), tb)
|
||||
|
@ -82,8 +83,9 @@ class WindowManagerMixin(object):
|
|||
|
||||
return new_tab
|
||||
|
||||
def open_window(self, trigger=None):
|
||||
def open_window(self, callback=None, focus=False):
|
||||
current_windows = self.marionette.chrome_window_handles
|
||||
current_tabs = self.marionette.window_handles
|
||||
|
||||
def loaded(handle):
|
||||
with self.marionette.using_context("chrome"):
|
||||
|
@ -95,11 +97,14 @@ class WindowManagerMixin(object):
|
|||
""", script_args=[handle])
|
||||
|
||||
try:
|
||||
if callable(trigger):
|
||||
trigger()
|
||||
if callable(callback):
|
||||
callback()
|
||||
else:
|
||||
with self.marionette.using_context("chrome"):
|
||||
self.marionette.execute_script("OpenBrowserWindow();")
|
||||
result = self.marionette.open(type="window", focus=focus)
|
||||
if result["type"] != "window":
|
||||
raise Exception(
|
||||
"Newly opened browsing context is of type {} and not window.".format(
|
||||
result["type"]))
|
||||
except Exception:
|
||||
exc, val, tb = sys.exc_info()
|
||||
reraise(exc, 'Failed to trigger opening a new window: {}'.format(val), tb)
|
||||
|
@ -116,9 +121,16 @@ class WindowManagerMixin(object):
|
|||
lambda _: loaded(new_window),
|
||||
message="Window with handle '{}'' did not finish loading".format(new_window))
|
||||
|
||||
return new_window
|
||||
# Bug 1507771 - Return the correct handle based on the currently selected context
|
||||
# as long as "WebDriver:NewWindow" is not handled separtely in chrome context
|
||||
context = self.marionette._send_message("Marionette:GetContext", key="value")
|
||||
if context == "chrome":
|
||||
return new_window
|
||||
elif context == "content":
|
||||
[new_tab] = list(set(self.marionette.window_handles) - set(current_tabs))
|
||||
return new_tab
|
||||
|
||||
def open_chrome_window(self, url):
|
||||
def open_chrome_window(self, url, focus=False):
|
||||
"""Open a new chrome window with the specified chrome URL.
|
||||
|
||||
Can be replaced with "WebDriver:NewWindow" once the command
|
||||
|
@ -166,4 +178,5 @@ class WindowManagerMixin(object):
|
|||
})();
|
||||
""", script_args=(url,))
|
||||
|
||||
return self.open_window(trigger=open_with_js)
|
||||
with self.marionette.using_context("chrome"):
|
||||
return self.open_window(callback=open_with_js, focus=focus)
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
import manifestparser
|
||||
import mozunit
|
||||
import pytest
|
||||
|
@ -296,7 +298,7 @@ def test_add_test_directory(runner):
|
|||
runner.add_test(test_dir)
|
||||
assert isdir.called and walk.called
|
||||
for test in runner.tests:
|
||||
assert test_dir in test['filepath']
|
||||
assert os.path.normpath(test_dir) in test['filepath']
|
||||
assert len(runner.tests) == 2
|
||||
|
||||
|
||||
|
|
|
@ -1,22 +1,5 @@
|
|||
#Copyright 2007-2009 WebDriver committers
|
||||
#Copyright 2007-2009 Google Inc.
|
||||
#
|
||||
#Licensed under the Apache License, Version 2.0 (the "License");
|
||||
#you may not use this file except in compliance with the License.
|
||||
#You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
#Unless required by applicable law or agreed to in writing, software
|
||||
#distributed under the License is distributed on an "AS IS" BASIS,
|
||||
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#See the License for the specific language governing permissions and
|
||||
#limitations under the License.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from marionette_driver import By
|
||||
|
||||
from marionette_harness import MarionetteTestCase, WindowManagerMixin
|
||||
|
||||
|
||||
|
@ -25,18 +8,13 @@ class ChromeTests(WindowManagerMixin, MarionetteTestCase):
|
|||
def setUp(self):
|
||||
super(ChromeTests, self).setUp()
|
||||
|
||||
self.marionette.set_context('chrome')
|
||||
|
||||
def tearDown(self):
|
||||
self.close_all_windows()
|
||||
super(ChromeTests, self).tearDown()
|
||||
|
||||
def test_hang_until_timeout(self):
|
||||
def open_with_menu():
|
||||
menu = self.marionette.find_element(By.ID, 'aboutName')
|
||||
menu.click()
|
||||
|
||||
new_window = self.open_window(trigger=open_with_menu)
|
||||
with self.marionette.using_context("chrome"):
|
||||
new_window = self.open_window()
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
try:
|
||||
|
@ -45,7 +23,8 @@ class ChromeTests(WindowManagerMixin, MarionetteTestCase):
|
|||
# while running this test. Otherwise it would mask eg. IOError as
|
||||
# thrown for a socket timeout.
|
||||
raise NotImplementedError('Exception should not cause a hang when '
|
||||
'closing the chrome window')
|
||||
'closing the chrome window in content '
|
||||
'context')
|
||||
finally:
|
||||
self.marionette.close_chrome_window()
|
||||
self.marionette.switch_to_window(self.start_window)
|
||||
|
|
|
@ -449,20 +449,16 @@ class TestClickCloseContext(WindowManagerMixin, MarionetteTestCase):
|
|||
super(TestClickCloseContext, self).tearDown()
|
||||
|
||||
def test_click_close_tab(self):
|
||||
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
|
||||
tab = self.open_tab(
|
||||
lambda: self.marionette.find_element(By.ID, "new-tab").click())
|
||||
self.marionette.switch_to_window(tab)
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
self.marionette.navigate(self.test_page)
|
||||
self.marionette.find_element(By.ID, "close-window").click()
|
||||
|
||||
@skip_if_mobile("Fennec doesn't support other chrome windows")
|
||||
def test_click_close_window(self):
|
||||
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
|
||||
win = self.open_window(
|
||||
lambda: self.marionette.find_element(By.ID, "new-window").click())
|
||||
self.marionette.switch_to_window(win)
|
||||
new_tab = self.open_window()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
self.marionette.navigate(self.test_page)
|
||||
self.marionette.find_element(By.ID, "close-window").click()
|
||||
|
|
|
@ -77,22 +77,3 @@ class TestKeyActions(WindowManagerMixin, MarionetteTestCase):
|
|||
.key_down("x")
|
||||
.perform())
|
||||
self.assertEqual(self.key_reporter_value, "")
|
||||
|
||||
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
|
||||
def test_open_in_new_window_shortcut(self):
|
||||
|
||||
def open_window_with_action():
|
||||
el = self.marionette.find_element(By.TAG_NAME, "a")
|
||||
(self.key_action.key_down(Keys.SHIFT)
|
||||
.press(el)
|
||||
.release()
|
||||
.key_up(Keys.SHIFT)
|
||||
.perform())
|
||||
|
||||
self.marionette.navigate(inline("<a href='#'>Click</a>"))
|
||||
new_window = self.open_window(trigger=open_window_with_action)
|
||||
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.marionette.close_chrome_window()
|
||||
|
||||
self.marionette.switch_to_window(self.start_window)
|
||||
|
|
|
@ -55,13 +55,8 @@ class BaseNavigationTestCase(WindowManagerMixin, MarionetteTestCase):
|
|||
else:
|
||||
self.mod_key = Keys.CONTROL
|
||||
|
||||
def open_with_link():
|
||||
link = self.marionette.find_element(By.ID, "new-blank-tab")
|
||||
link.click()
|
||||
|
||||
# Always use a blank new tab for an empty history
|
||||
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
|
||||
self.new_tab = self.open_tab(open_with_link)
|
||||
self.new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(self.new_tab)
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda _: self.history_length == 1,
|
||||
|
@ -298,7 +293,6 @@ class TestNavigate(BaseNavigationTestCase):
|
|||
focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus")
|
||||
self.assertEqual(self.marionette.get_active_element(), focus_el)
|
||||
|
||||
@skip_if_mobile("Needs application independent method to open a new tab")
|
||||
def test_no_hang_when_navigating_after_closing_original_tab(self):
|
||||
# Close the start tab
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
|
@ -340,22 +334,6 @@ class TestNavigate(BaseNavigationTestCase):
|
|||
message="'{}' hasn't been loaded".format(self.test_page_remote))
|
||||
self.assertTrue(self.is_remote_tab)
|
||||
|
||||
@skip_if_mobile("On Android no shortcuts are available")
|
||||
def test_navigate_shortcut_key(self):
|
||||
|
||||
def open_with_shortcut():
|
||||
self.marionette.navigate(self.test_page_remote)
|
||||
with self.marionette.using_context("chrome"):
|
||||
main_win = self.marionette.find_element(By.ID, "main-window")
|
||||
main_win.send_keys(self.mod_key, Keys.SHIFT, "a")
|
||||
|
||||
new_tab = self.open_tab(trigger=open_with_shortcut)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda mn: mn.get_url() == "about:addons",
|
||||
message="'about:addons' hasn't been loaded")
|
||||
|
||||
|
||||
class TestBackForwardNavigation(BaseNavigationTestCase):
|
||||
|
||||
|
@ -823,7 +801,7 @@ class TestPageLoadStrategy(BaseNavigationTestCase):
|
|||
@skip("Bug 1422741 - Causes following tests to fail in loading remote browser")
|
||||
@run_if_e10s("Requires e10s mode enabled")
|
||||
def test_strategy_after_remoteness_change(self):
|
||||
"""Bug 1378191 - Reset of capabilities after listener reload"""
|
||||
"""Bug 1378191 - Reset of capabilities after listener reload."""
|
||||
self.marionette.delete_session()
|
||||
self.marionette.start_session({"pageLoadStrategy": "eager"})
|
||||
|
||||
|
|
|
@ -253,7 +253,8 @@ class TestScreenCaptureChrome(WindowManagerMixin, ScreenCaptureTestCase):
|
|||
|
||||
chrome_document_element = self.document_element
|
||||
with self.marionette.using_context('content'):
|
||||
self.assertRaisesRegexp(NoSuchElementException, "Web element reference not seen before",
|
||||
self.assertRaisesRegexp(NoSuchElementException,
|
||||
"Web element reference not seen before",
|
||||
self.marionette.screenshot,
|
||||
highlights=[chrome_document_element])
|
||||
|
||||
|
@ -274,10 +275,9 @@ class TestScreenCaptureContent(WindowManagerMixin, ScreenCaptureTestCase):
|
|||
return [document.body.scrollWidth, document.body.scrollHeight]
|
||||
"""))
|
||||
|
||||
@skip_if_mobile("Needs application independent method to open a new tab")
|
||||
def test_capture_tab_already_closed(self):
|
||||
tab = self.open_tab()
|
||||
self.marionette.switch_to_window(tab)
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.close()
|
||||
|
||||
self.assertRaises(NoSuchWindowException, self.marionette.screenshot)
|
||||
|
|
|
@ -6,9 +6,8 @@ from __future__ import absolute_import
|
|||
|
||||
import os
|
||||
import sys
|
||||
from unittest import skipIf
|
||||
|
||||
from marionette_driver import By
|
||||
from unittest import skipIf
|
||||
|
||||
# add this directory to the path
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
|
@ -28,119 +27,70 @@ class TestSwitchWindowChrome(TestSwitchToWindowContent):
|
|||
|
||||
super(TestSwitchWindowChrome, self).tearDown()
|
||||
|
||||
def open_window_in_background(self):
|
||||
with self.marionette.using_context("chrome"):
|
||||
self.marionette.execute_async_script("""
|
||||
let callback = arguments[0];
|
||||
(async function() {
|
||||
function promiseEvent(target, type, args) {
|
||||
return new Promise(r => {
|
||||
let params = Object.assign({once: true}, args);
|
||||
target.addEventListener(type, r, params);
|
||||
});
|
||||
}
|
||||
function promiseWindowFocus(w) {
|
||||
return Promise.all([
|
||||
promiseEvent(w, "focus", {capture: true}),
|
||||
promiseEvent(w, "activate"),
|
||||
]);
|
||||
}
|
||||
// Open a window, wait for it to receive focus
|
||||
let win = OpenBrowserWindow();
|
||||
await promiseWindowFocus(win);
|
||||
|
||||
// Now refocus our original window and wait for that to happen.
|
||||
let windowFocusPromise = promiseWindowFocus(window);
|
||||
window.focus();
|
||||
return windowFocusPromise;
|
||||
})().then(() => {
|
||||
// can't just pass `callback`, as we can't JSON-ify the events it'd get passed.
|
||||
callback()
|
||||
});
|
||||
""")
|
||||
|
||||
def open_window_in_foreground(self):
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate(self.test_page)
|
||||
link = self.marionette.find_element(By.ID, "new-window")
|
||||
link.click()
|
||||
|
||||
@skipIf(sys.platform.startswith("linux"),
|
||||
"Bug 1511970 - New window isn't moved to the background on Linux")
|
||||
def test_switch_tabs_for_new_background_window_without_focus_change(self):
|
||||
# Open an addition tab in the original window so we can better check
|
||||
# Open an additional tab in the original window so we can better check
|
||||
# the selected index in thew new window to be opened.
|
||||
second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
|
||||
second_tab = self.open_tab(focus=True)
|
||||
self.marionette.switch_to_window(second_tab, focus=True)
|
||||
second_tab_index = self.get_selected_tab_index()
|
||||
self.assertNotEqual(second_tab_index, self.selected_tab_index)
|
||||
|
||||
# Opens a new background window, but we are interested in the tab
|
||||
tab_in_new_window = self.open_tab(trigger=self.open_window_in_background)
|
||||
# Open a new background window, but we are interested in the tab
|
||||
with self.marionette.using_context("content"):
|
||||
tab_in_new_window = self.open_window()
|
||||
self.assertEqual(self.marionette.current_window_handle, second_tab)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
self.assertEqual(self.get_selected_tab_index(), second_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.empty_page)
|
||||
|
||||
# Switch to the tab in the new window but don't focus it
|
||||
self.marionette.switch_to_window(tab_in_new_window, focus=False)
|
||||
self.assertEqual(self.marionette.current_window_handle, tab_in_new_window)
|
||||
self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
self.assertEqual(self.get_selected_tab_index(), second_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), "about:blank")
|
||||
|
||||
def test_switch_tabs_for_new_foreground_window_with_focus_change(self):
|
||||
# Open an addition tab in the original window so we can better check
|
||||
# the selected index in thew new window to be opened.
|
||||
second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
|
||||
second_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(second_tab, focus=True)
|
||||
second_tab_index = self.get_selected_tab_index()
|
||||
self.assertNotEqual(second_tab_index, self.selected_tab_index)
|
||||
|
||||
# Opens a new window, but we are interested in the tab
|
||||
tab_in_new_window = self.open_tab(trigger=self.open_window_in_foreground)
|
||||
with self.marionette.using_context("content"):
|
||||
tab_in_new_window = self.open_window(focus=True)
|
||||
self.assertEqual(self.marionette.current_window_handle, second_tab)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
self.marionette.switch_to_window(tab_in_new_window)
|
||||
self.assertEqual(self.marionette.current_window_handle, tab_in_new_window)
|
||||
self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.empty_page)
|
||||
|
||||
self.marionette.switch_to_window(second_tab, focus=True)
|
||||
self.assertEqual(self.marionette.current_window_handle, second_tab)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
# Bug 1335085 - The focus doesn't change even as requested so.
|
||||
# self.assertEqual(self.get_selected_tab_index(), second_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
def test_switch_tabs_for_new_foreground_window_without_focus_change(self):
|
||||
# Open an addition tab in the original window so we can better check
|
||||
# the selected index in thew new window to be opened.
|
||||
second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
|
||||
second_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(second_tab, focus=True)
|
||||
second_tab_index = self.get_selected_tab_index()
|
||||
self.assertNotEqual(second_tab_index, self.selected_tab_index)
|
||||
|
||||
# Opens a new window, but we are interested in the tab which automatically
|
||||
# gets the focus.
|
||||
self.open_tab(trigger=self.open_window_in_foreground)
|
||||
self.open_window(focus=True)
|
||||
self.assertEqual(self.marionette.current_window_handle, second_tab)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
# Switch to the second tab in the first window, but don't focus it.
|
||||
self.marionette.switch_to_window(second_tab, focus=False)
|
||||
self.assertEqual(self.marionette.current_window_handle, second_tab)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from marionette_driver import Actions, By, Wait
|
||||
from marionette_driver import By
|
||||
from marionette_driver.keys import Keys
|
||||
|
||||
from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
|
||||
from marionette_harness import MarionetteTestCase, WindowManagerMixin
|
||||
|
||||
|
||||
class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
|
||||
|
@ -20,14 +20,8 @@ class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
else:
|
||||
self.mod_key = Keys.CONTROL
|
||||
|
||||
self.empty_page = self.marionette.absolute_url("empty.html")
|
||||
self.test_page = self.marionette.absolute_url("windowHandles.html")
|
||||
|
||||
self.selected_tab_index = self.get_selected_tab_index()
|
||||
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate(self.test_page)
|
||||
|
||||
def tearDown(self):
|
||||
self.close_all_tabs()
|
||||
|
||||
|
@ -69,78 +63,51 @@ class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
}
|
||||
""")
|
||||
|
||||
def open_tab_in_background(self):
|
||||
with self.marionette.using_context("content"):
|
||||
link = self.marionette.find_element(By.ID, "new-tab")
|
||||
|
||||
action = Actions(self.marionette)
|
||||
action.key_down(self.mod_key).click(link).perform()
|
||||
|
||||
def open_tab_in_foreground(self):
|
||||
with self.marionette.using_context("content"):
|
||||
link = self.marionette.find_element(By.ID, "new-tab")
|
||||
link.click()
|
||||
|
||||
def test_switch_tabs_with_focus_change(self):
|
||||
new_tab = self.open_tab(self.open_tab_in_foreground)
|
||||
new_tab = self.open_tab(focus=True)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
# Switch to new tab first because it is already selected
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
|
||||
|
||||
with self.marionette.using_context("content"):
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda _: self.marionette.get_url() == self.empty_page,
|
||||
message="{} has been loaded in the newly opened tab.".format(self.empty_page))
|
||||
|
||||
# Switch to original tab by explicitely setting the focus
|
||||
self.marionette.switch_to_window(self.start_tab, focus=True)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.close()
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
def test_switch_tabs_without_focus_change(self):
|
||||
new_tab = self.open_tab(self.open_tab_in_foreground)
|
||||
new_tab = self.open_tab(focus=True)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
# Switch to new tab first because it is already selected
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
|
||||
# Switch to original tab by explicitely not setting the focus
|
||||
self.marionette.switch_to_window(self.start_tab, focus=False)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
|
||||
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.close()
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
def test_switch_from_content_to_chrome_window_should_not_change_selected_tab(self):
|
||||
new_tab = self.open_tab(self.open_tab_in_foreground)
|
||||
new_tab = self.open_tab(focus=True)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
|
@ -150,24 +117,31 @@ class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
self.assertEqual(self.get_selected_tab_index(), new_tab_index)
|
||||
|
||||
@skip_if_mobile("New windows not supported in Fennec")
|
||||
def test_switch_to_new_private_browsing_window_has_to_register_browsers(self):
|
||||
def test_switch_to_new_private_browsing_tab(self):
|
||||
# Test that tabs (browsers) are correctly registered for a newly opened
|
||||
# private browsing window. This has to also happen without explicitely
|
||||
# private browsing window/tab. This has to also happen without explicitely
|
||||
# switching to the tab itself before using any commands in content scope.
|
||||
#
|
||||
# Note: Not sure why this only affects private browsing windows only.
|
||||
new_tab = self.open_tab(focus=True)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
def open_private_browsing_window():
|
||||
def open_private_browsing_window_firefox():
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate("about:privatebrowsing")
|
||||
button = self.marionette.find_element(By.ID, "startPrivateBrowsing")
|
||||
button.click()
|
||||
self.marionette.find_element(By.ID, "startPrivateBrowsing").click()
|
||||
|
||||
new_window = self.open_window(open_private_browsing_window)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
|
||||
self.assertNotEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
def open_private_browsing_tab_fennec():
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.find_element(By.ID, "newPrivateTabLink").click()
|
||||
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.execute_script(" return true; ")
|
||||
self.marionette.navigate("about:privatebrowsing")
|
||||
if self.marionette.session_capabilities["browserName"] == "fennec":
|
||||
new_pb_tab = self.open_tab(open_private_browsing_tab_fennec)
|
||||
else:
|
||||
new_pb_tab = self.open_tab(open_private_browsing_window_firefox)
|
||||
|
||||
self.marionette.switch_to_window(new_pb_tab)
|
||||
self.assertEqual(self.marionette.current_window_handle, new_pb_tab)
|
||||
|
||||
self.marionette.execute_script(" return true; ")
|
||||
|
|
|
@ -21,14 +21,14 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
|
|||
super(TestCloseWindow, self).tearDown()
|
||||
|
||||
def test_close_chrome_window_for_browser_window(self):
|
||||
win = self.open_window()
|
||||
self.marionette.switch_to_window(win)
|
||||
new_window = self.open_window()
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
self.assertNotIn(win, self.marionette.window_handles)
|
||||
self.assertNotIn(new_window, self.marionette.window_handles)
|
||||
chrome_window_handles = self.marionette.close_chrome_window()
|
||||
self.assertNotIn(win, chrome_window_handles)
|
||||
self.assertNotIn(new_window, chrome_window_handles)
|
||||
self.assertListEqual(self.start_windows, chrome_window_handles)
|
||||
self.assertNotIn(win, self.marionette.window_handles)
|
||||
self.assertNotIn(new_window, self.marionette.window_handles)
|
||||
|
||||
def test_close_chrome_window_for_non_browser_window(self):
|
||||
win = self.open_chrome_window("chrome://marionette/content/test.xul")
|
||||
|
@ -50,20 +50,20 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assertIsNotNone(self.marionette.session)
|
||||
|
||||
def test_close_window_for_browser_tab(self):
|
||||
tab = self.open_tab()
|
||||
self.marionette.switch_to_window(tab)
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
window_handles = self.marionette.close()
|
||||
self.assertNotIn(tab, window_handles)
|
||||
self.assertNotIn(new_tab, window_handles)
|
||||
self.assertListEqual(self.start_tabs, window_handles)
|
||||
|
||||
def test_close_window_for_browser_window_with_single_tab(self):
|
||||
win = self.open_window()
|
||||
self.marionette.switch_to_window(win)
|
||||
new_window = self.open_window()
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles))
|
||||
window_handles = self.marionette.close()
|
||||
self.assertNotIn(win, window_handles)
|
||||
self.assertNotIn(new_window, window_handles)
|
||||
self.assertListEqual(self.start_tabs, window_handles)
|
||||
self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles)
|
||||
|
||||
|
|
|
@ -24,26 +24,27 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
|
|||
|
||||
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
|
||||
def test_close_chrome_window_for_browser_window(self):
|
||||
win = self.open_window()
|
||||
self.marionette.switch_to_window(win)
|
||||
with self.marionette.using_context("chrome"):
|
||||
new_window = self.open_window()
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
self.assertNotIn(win, self.marionette.window_handles)
|
||||
self.assertIn(new_window, self.marionette.chrome_window_handles)
|
||||
chrome_window_handles = self.marionette.close_chrome_window()
|
||||
self.assertNotIn(win, chrome_window_handles)
|
||||
self.assertNotIn(new_window, chrome_window_handles)
|
||||
self.assertListEqual(self.start_windows, chrome_window_handles)
|
||||
self.assertNotIn(win, self.marionette.window_handles)
|
||||
self.assertNotIn(new_window, self.marionette.window_handles)
|
||||
|
||||
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
|
||||
def test_close_chrome_window_for_non_browser_window(self):
|
||||
win = self.open_chrome_window("chrome://marionette/content/test.xul")
|
||||
self.marionette.switch_to_window(win)
|
||||
new_window = self.open_chrome_window("chrome://marionette/content/test.xul")
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
self.assertIn(win, self.marionette.chrome_window_handles)
|
||||
self.assertNotIn(win, self.marionette.window_handles)
|
||||
self.assertIn(new_window, self.marionette.chrome_window_handles)
|
||||
self.assertNotIn(new_window, self.marionette.window_handles)
|
||||
chrome_window_handles = self.marionette.close_chrome_window()
|
||||
self.assertNotIn(win, chrome_window_handles)
|
||||
self.assertNotIn(new_window, chrome_window_handles)
|
||||
self.assertListEqual(self.start_windows, chrome_window_handles)
|
||||
self.assertNotIn(win, self.marionette.window_handles)
|
||||
self.assertNotIn(new_window, self.marionette.window_handles)
|
||||
|
||||
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
|
||||
def test_close_chrome_window_for_last_open_window(self):
|
||||
|
@ -54,19 +55,17 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assertListEqual([self.start_window], self.marionette.chrome_window_handles)
|
||||
self.assertIsNotNone(self.marionette.session)
|
||||
|
||||
@skip_if_mobile("Needs application independent method to open a new tab")
|
||||
def test_close_window_for_browser_tab(self):
|
||||
tab = self.open_tab()
|
||||
self.marionette.switch_to_window(tab)
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
window_handles = self.marionette.close()
|
||||
self.assertNotIn(tab, window_handles)
|
||||
self.assertNotIn(new_tab, window_handles)
|
||||
self.assertListEqual(self.start_tabs, window_handles)
|
||||
|
||||
@skip_if_mobile("Needs application independent method to open a new tab")
|
||||
def test_close_window_with_dismissed_beforeunload_prompt(self):
|
||||
tab = self.open_tab()
|
||||
self.marionette.switch_to_window(tab)
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
self.marionette.navigate(inline("""
|
||||
<input type="text">
|
||||
|
@ -82,12 +81,12 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
|
|||
|
||||
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
|
||||
def test_close_window_for_browser_window_with_single_tab(self):
|
||||
win = self.open_window()
|
||||
self.marionette.switch_to_window(win)
|
||||
new_tab = self.open_window()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles))
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
window_handles = self.marionette.close()
|
||||
self.assertNotIn(win, window_handles)
|
||||
self.assertNotIn(new_tab, window_handles)
|
||||
self.assertListEqual(self.start_tabs, window_handles)
|
||||
self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles)
|
||||
|
||||
|
@ -104,8 +103,8 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
|
|||
self.close_all_tabs()
|
||||
|
||||
test_page = self.marionette.absolute_url("windowHandles.html")
|
||||
tab = self.open_tab()
|
||||
self.marionette.switch_to_window(tab)
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.navigate(test_page)
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from __future__ import absolute_import
|
|||
|
||||
import types
|
||||
|
||||
from marionette_driver import By, errors, Wait
|
||||
from marionette_driver import errors
|
||||
|
||||
from marionette_harness import MarionetteTestCase, WindowManagerMixin
|
||||
|
||||
|
@ -16,9 +16,7 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
def setUp(self):
|
||||
super(TestWindowHandles, self).setUp()
|
||||
|
||||
self.empty_page = self.marionette.absolute_url("empty.html")
|
||||
self.test_page = self.marionette.absolute_url("windowHandles.html")
|
||||
self.marionette.navigate(self.test_page)
|
||||
self.xul_dialog = "chrome://marionette/content/test_dialog.xul"
|
||||
|
||||
self.marionette.set_context("chrome")
|
||||
|
||||
|
@ -42,17 +40,16 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assertIsInstance(handle, types.StringTypes)
|
||||
|
||||
def test_chrome_window_handles_with_scopes(self):
|
||||
# Open a browser and a non-browser (about window) chrome window
|
||||
self.open_window(
|
||||
trigger=lambda: self.marionette.execute_script("OpenBrowserWindow();"))
|
||||
new_browser = self.open_window()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
|
||||
self.assertIn(new_browser, self.marionette.chrome_window_handles)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
|
||||
self.open_window(
|
||||
trigger=lambda: self.marionette.find_element(By.ID, "aboutName").click())
|
||||
new_dialog = self.open_chrome_window(self.xul_dialog)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 2)
|
||||
self.assertIn(new_dialog, self.marionette.chrome_window_handles)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
|
||||
chrome_window_handles_in_chrome_scope = self.marionette.chrome_window_handles
|
||||
|
@ -64,117 +61,112 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assertEqual(self.marionette.window_handles,
|
||||
window_handles_in_chrome_scope)
|
||||
|
||||
def test_chrome_window_handles_after_opening_new_dialog(self):
|
||||
xul_dialog = "chrome://marionette/content/test_dialog.xul"
|
||||
new_win = self.open_chrome_window(xul_dialog)
|
||||
def test_chrome_window_handles_after_opening_new_chrome_window(self):
|
||||
new_window = self.open_chrome_window(self.xul_dialog)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
|
||||
self.assertIn(new_window, self.marionette.chrome_window_handles)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
|
||||
# Check that the new tab has the correct page loaded
|
||||
self.marionette.switch_to_window(new_win)
|
||||
# Check that the new chrome window has the correct URL loaded
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, new_win)
|
||||
self.assertEqual(self.marionette.get_url(), xul_dialog)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
|
||||
self.assertEqual(self.marionette.get_url(), self.xul_dialog)
|
||||
|
||||
# Close the opened dialog and carry on in our original tab.
|
||||
# Close the chrome window, and carry on in our original window.
|
||||
self.marionette.close_chrome_window()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
|
||||
self.assertNotIn(new_window, self.marionette.chrome_window_handles)
|
||||
|
||||
self.marionette.switch_to_window(self.start_window)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
def test_chrome_window_handles_after_opening_new_window(self):
|
||||
def open_with_link():
|
||||
with self.marionette.using_context("content"):
|
||||
link = self.marionette.find_element(By.ID, "new-window")
|
||||
link.click()
|
||||
|
||||
# We open a new window but are actually interested in the new tab
|
||||
new_win = self.open_window(trigger=open_with_link)
|
||||
new_window = self.open_window()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
|
||||
self.assertIn(new_window, self.marionette.chrome_window_handles)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
|
||||
# Check that the new tab has the correct page loaded
|
||||
self.marionette.switch_to_window(new_win)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, new_win)
|
||||
with self.marionette.using_context("content"):
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda mn: mn.get_url() == self.empty_page,
|
||||
message="{} did not load after opening a new tab".format(self.empty_page))
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
|
||||
|
||||
# Ensure navigate works in our current window
|
||||
other_page = self.marionette.absolute_url("test.html")
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate(other_page)
|
||||
self.assertEqual(self.marionette.get_url(), other_page)
|
||||
|
||||
# Close the opened window and carry on in our original tab.
|
||||
# Close the opened window and carry on in our original window.
|
||||
self.marionette.close()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
|
||||
self.assertNotIn(new_window, self.marionette.chrome_window_handles)
|
||||
|
||||
self.marionette.switch_to_window(self.start_window)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
def test_window_handles_after_opening_new_tab(self):
|
||||
def open_with_link():
|
||||
with self.marionette.using_context("content"):
|
||||
link = self.marionette.find_element(By.ID, "new-tab")
|
||||
link.click()
|
||||
|
||||
new_tab = self.open_tab(trigger=open_with_link)
|
||||
with self.marionette.using_context("content"):
|
||||
new_tab = self.open_tab()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertIn(new_tab, self.marionette.window_handles)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
with self.marionette.using_context("content"):
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda mn: mn.get_url() == self.empty_page,
|
||||
message="{} did not load after opening a new tab".format(self.empty_page))
|
||||
|
||||
# Ensure navigate works in our current tab
|
||||
other_page = self.marionette.absolute_url("test.html")
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate(other_page)
|
||||
self.assertEqual(self.marionette.get_url(), other_page)
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.close()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
self.assertNotIn(new_tab, self.marionette.window_handles)
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
def test_window_handles_after_opening_new_dialog(self):
|
||||
xul_dialog = "chrome://marionette/content/test_dialog.xul"
|
||||
new_win = self.open_chrome_window(xul_dialog)
|
||||
def test_window_handles_after_opening_new_foreground_tab(self):
|
||||
with self.marionette.using_context("content"):
|
||||
new_tab = self.open_tab(focus=True)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertIn(new_tab, self.marionette.window_handles)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
# We still have the default tab set as our window handle. This
|
||||
# get_url command should be sent immediately, and not be forever-queued.
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.get_url()
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
|
||||
self.marionette.close()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
self.assertNotIn(new_tab, self.marionette.window_handles)
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
self.marionette.switch_to_window(new_win)
|
||||
def test_window_handles_after_opening_new_chrome_window(self):
|
||||
new_window = self.open_chrome_window(self.xul_dialog)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.get_url(), xul_dialog)
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
self.assertNotIn(new_window, self.marionette.window_handles)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.get_url(), self.xul_dialog)
|
||||
|
||||
# Check that the opened dialog is not accessible via window handles
|
||||
with self.assertRaises(errors.NoSuchWindowException):
|
||||
|
@ -190,111 +182,23 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
def test_window_handles_after_opening_new_window(self):
|
||||
def open_with_link():
|
||||
with self.marionette.using_context("content"):
|
||||
link = self.marionette.find_element(By.ID, "new-window")
|
||||
link.click()
|
||||
|
||||
# We open a new window but are actually interested in the new tab
|
||||
new_tab = self.open_tab(trigger=open_with_link)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
# Check that the new tab has the correct page loaded
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
with self.marionette.using_context("content"):
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda mn: mn.get_url() == self.empty_page,
|
||||
message="{} did not load after opening a new tab".format(self.empty_page))
|
||||
|
||||
# Ensure navigate works in our current window
|
||||
other_page = self.marionette.absolute_url("test.html")
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate(other_page)
|
||||
self.assertEqual(self.marionette.get_url(), other_page)
|
||||
|
||||
# Close the opened window and carry on in our original tab.
|
||||
self.marionette.close()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
def test_window_handles_after_closing_original_tab(self):
|
||||
def open_with_link():
|
||||
with self.marionette.using_context("content"):
|
||||
link = self.marionette.find_element(By.ID, "new-tab")
|
||||
link.click()
|
||||
|
||||
new_tab = self.open_tab(trigger=open_with_link)
|
||||
with self.marionette.using_context("content"):
|
||||
new_tab = self.open_tab()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertIn(new_tab, self.marionette.window_handles)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
self.marionette.close()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
self.assertIn(new_tab, self.marionette.window_handles)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
with self.marionette.using_context("content"):
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda mn: mn.get_url() == self.empty_page,
|
||||
message="{} did not load after opening a new tab".format(self.empty_page))
|
||||
|
||||
def test_window_handles_no_switch(self):
|
||||
"""Regression test for bug 1294456.
|
||||
This test is testing the case where Marionette attempts to send a
|
||||
command to a window handle when the browser has opened and selected
|
||||
a new tab. Before bug 1294456 landed, the Marionette driver was getting
|
||||
confused about which window handle the client cared about, and assumed
|
||||
it was the window handle for the newly opened and selected tab.
|
||||
|
||||
This caused Marionette to think that the browser needed to do a remoteness
|
||||
flip in the e10s case, since the tab opened by menu_newNavigatorTab is
|
||||
about:newtab (which is currently non-remote). This meant that commands
|
||||
sent to what should have been the original window handle would be
|
||||
queued and never sent, since the remoteness flip in the new tab was
|
||||
never going to happen.
|
||||
"""
|
||||
def open_with_menu():
|
||||
menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
|
||||
menu_new_tab.click()
|
||||
|
||||
new_tab = self.open_tab(trigger=open_with_menu)
|
||||
self.assert_window_handles()
|
||||
|
||||
# We still have the default tab set as our window handle. This
|
||||
# get_url command should be sent immediately, and not be forever-queued.
|
||||
with self.marionette.using_context("content"):
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
|
||||
self.marionette.close()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
def test_window_handles_after_closing_last_window(self):
|
||||
self.close_all_windows()
|
||||
|
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import
|
|||
import types
|
||||
import urllib
|
||||
|
||||
from marionette_driver import By, errors, Wait
|
||||
from marionette_driver import errors
|
||||
|
||||
from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
|
||||
|
||||
|
@ -21,9 +21,7 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
def setUp(self):
|
||||
super(TestWindowHandles, self).setUp()
|
||||
|
||||
self.empty_page = self.marionette.absolute_url("empty.html")
|
||||
self.test_page = self.marionette.absolute_url("windowHandles.html")
|
||||
self.marionette.navigate(self.test_page)
|
||||
self.xul_dialog = "chrome://marionette/content/test_dialog.xul"
|
||||
|
||||
def tearDown(self):
|
||||
self.close_all_tabs()
|
||||
|
@ -39,12 +37,8 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
for handle in self.marionette.window_handles:
|
||||
self.assertIsInstance(handle, types.StringTypes)
|
||||
|
||||
def test_window_handles_after_opening_new_tab(self):
|
||||
def open_with_link():
|
||||
link = self.marionette.find_element(By.ID, "new-tab")
|
||||
link.click()
|
||||
|
||||
new_tab = self.open_tab(trigger=open_with_link)
|
||||
def tst_window_handles_after_opening_new_tab(self):
|
||||
new_tab = self.open_tab()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
@ -52,13 +46,9 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
lambda mn: mn.get_url() == self.empty_page,
|
||||
message="{} did not load after opening a new tab".format(self.empty_page))
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.close()
|
||||
|
@ -69,29 +59,15 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
def test_window_handles_after_opening_new_browser_window(self):
|
||||
def open_with_link():
|
||||
link = self.marionette.find_element(By.ID, "new-window")
|
||||
link.click()
|
||||
|
||||
# We open a new window but are actually interested in the new tab
|
||||
new_tab = self.open_tab(trigger=open_with_link)
|
||||
def tst_window_handles_after_opening_new_browser_window(self):
|
||||
new_tab = self.open_window()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
# Check that the new tab has the correct page loaded
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
Wait(self.marionette, self.marionette.timeout.page_load).until(
|
||||
lambda _: self.marionette.get_url() == self.empty_page,
|
||||
message="The expected page '{}' has not been loaded".format(self.empty_page))
|
||||
|
||||
# Ensure navigate works in our current window
|
||||
other_page = self.marionette.absolute_url("test.html")
|
||||
self.marionette.navigate(other_page)
|
||||
self.assertEqual(self.marionette.get_url(), other_page)
|
||||
|
||||
# Close the opened window and carry on in our original tab.
|
||||
self.marionette.close()
|
||||
|
@ -101,31 +77,16 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertEqual(self.marionette.get_url(), self.test_page)
|
||||
|
||||
@skip_if_mobile("Fennec doesn't support other chrome windows")
|
||||
def test_window_handles_after_opening_new_non_browser_window(self):
|
||||
def open_with_link():
|
||||
self.marionette.navigate(inline("""
|
||||
<a id="blob-download" download="foo.html">Download</a>
|
||||
|
||||
<script>
|
||||
const string = "test";
|
||||
const blob = new Blob([string], { type: "text/html" });
|
||||
|
||||
const link = document.getElementById("blob-download");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
</script>
|
||||
"""))
|
||||
link = self.marionette.find_element(By.ID, "blob-download")
|
||||
link.click()
|
||||
|
||||
new_win = self.open_window(trigger=open_with_link)
|
||||
def tst_window_handles_after_opening_new_non_browser_window(self):
|
||||
new_window = self.open_chrome_window(self.xul_dialog)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertNotIn(new_window, self.marionette.window_handles)
|
||||
|
||||
self.marionette.switch_to_window(new_win)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.assert_window_handles()
|
||||
|
||||
# Check that the opened window is not accessible via window handles
|
||||
|
@ -144,26 +105,21 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
|
||||
def test_window_handles_after_closing_original_tab(self):
|
||||
def open_with_link():
|
||||
link = self.marionette.find_element(By.ID, "new-tab")
|
||||
link.click()
|
||||
|
||||
new_tab = self.open_tab(trigger=open_with_link)
|
||||
new_tab = self.open_tab()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
|
||||
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
|
||||
self.assertIn(new_tab, self.marionette.window_handles)
|
||||
|
||||
self.marionette.close()
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
|
||||
self.assertNotIn(self.start_tab, self.marionette.window_handles)
|
||||
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assert_window_handles()
|
||||
self.assertEqual(self.marionette.current_window_handle, new_tab)
|
||||
Wait(self.marionette, self.marionette.timeout.page_load).until(
|
||||
lambda _: self.marionette.get_url() == self.empty_page,
|
||||
message="The expected page '{}' has not been loaded".format(self.empty_page))
|
||||
|
||||
def test_window_handles_after_closing_last_tab(self):
|
||||
def tst_window_handles_after_closing_last_tab(self):
|
||||
self.close_all_tabs()
|
||||
self.assertEqual(self.marionette.close(), [])
|
||||
|
|
|
@ -21,15 +21,9 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
|
||||
@skip_if_mobile("Fennec doesn't support other chrome windows")
|
||||
def test_closed_chrome_window(self):
|
||||
|
||||
def open_with_link():
|
||||
with self.marionette.using_context("content"):
|
||||
test_page = self.marionette.absolute_url("windowHandles.html")
|
||||
self.marionette.navigate(test_page)
|
||||
self.marionette.find_element(By.ID, "new-window").click()
|
||||
|
||||
win = self.open_window(open_with_link)
|
||||
self.marionette.switch_to_window(win)
|
||||
with self.marionette.using_context("chrome"):
|
||||
new_window = self.open_window()
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.marionette.close_chrome_window()
|
||||
|
||||
# When closing a browser window both handles are not available
|
||||
|
@ -43,12 +37,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_window)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(win)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
@skip_if_mobile("Fennec doesn't support other chrome windows")
|
||||
def test_closed_chrome_window_while_in_frame(self):
|
||||
win = self.open_chrome_window("chrome://marionette/content/test.xul")
|
||||
self.marionette.switch_to_window(win)
|
||||
new_window = self.open_chrome_window("chrome://marionette/content/test.xul")
|
||||
self.marionette.switch_to_window(new_window)
|
||||
with self.marionette.using_context("chrome"):
|
||||
self.marionette.switch_to_frame("iframe")
|
||||
self.marionette.close_chrome_window()
|
||||
|
@ -61,13 +55,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_window)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(win)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
def test_closed_tab(self):
|
||||
with self.marionette.using_context("content"):
|
||||
tab = self.open_tab()
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.close()
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.close()
|
||||
|
||||
# Check that only the content window is not available in both contexts
|
||||
for context in ("chrome", "content"):
|
||||
|
@ -79,25 +72,26 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_tab)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
def test_closed_tab_while_in_frame(self):
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
with self.marionette.using_context("content"):
|
||||
tab = self.open_tab()
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.navigate(self.marionette.absolute_url("test_iframe.html"))
|
||||
frame = self.marionette.find_element(By.ID, "test_iframe")
|
||||
self.marionette.switch_to_frame(frame)
|
||||
self.marionette.close()
|
||||
self.marionette.close()
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.current_window_handle
|
||||
self.marionette.current_chrome_window_handle
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.current_window_handle
|
||||
self.marionette.current_chrome_window_handle
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
|
||||
class TestNoSuchWindowChrome(TestNoSuchWindowContent):
|
||||
|
@ -121,42 +115,22 @@ class TestSwitchWindow(WindowManagerMixin, MarionetteTestCase):
|
|||
self.close_all_windows()
|
||||
super(TestSwitchWindow, self).tearDown()
|
||||
|
||||
def test_windows(self):
|
||||
def open_browser_with_js():
|
||||
self.marionette.execute_script(" window.open(); ")
|
||||
|
||||
new_window = self.open_window(trigger=open_browser_with_js)
|
||||
def test_switch_window_after_open_and_close(self):
|
||||
with self.marionette.using_context("chrome"):
|
||||
new_window = self.open_window()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
|
||||
self.assertIn(new_window, self.marionette.chrome_window_handles)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
|
||||
# switch to the other window
|
||||
# switch to the new chrome window and close it
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
|
||||
self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
|
||||
# switch back and close original window
|
||||
self.marionette.close_chrome_window()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
|
||||
self.assertNotIn(new_window, self.marionette.chrome_window_handles)
|
||||
|
||||
# switch back to the original chrome window
|
||||
self.marionette.switch_to_window(self.start_window)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
|
||||
self.marionette.close_chrome_window()
|
||||
|
||||
self.assertNotIn(self.start_window, self.marionette.chrome_window_handles)
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
|
||||
|
||||
def test_should_load_and_close_a_window(self):
|
||||
def open_window_with_link():
|
||||
test_html = self.marionette.absolute_url("test_windows.html")
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate(test_html)
|
||||
self.marionette.find_element(By.LINK_TEXT, "Open new window").click()
|
||||
|
||||
new_window = self.open_window(trigger=open_window_with_link)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), 2)
|
||||
|
||||
with self.marionette.using_context('content'):
|
||||
self.assertEqual(self.marionette.title, "We Arrive Here")
|
||||
|
||||
# Let's close and check
|
||||
self.marionette.close_chrome_window()
|
||||
self.marionette.switch_to_window(self.start_window)
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), 1)
|
||||
|
|
|
@ -15,30 +15,15 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
def setUp(self):
|
||||
super(TestNoSuchWindowContent, self).setUp()
|
||||
|
||||
self.test_page = self.marionette.absolute_url("windowHandles.html")
|
||||
with self.marionette.using_context("content"):
|
||||
self.marionette.navigate(self.test_page)
|
||||
|
||||
def tearDown(self):
|
||||
self.close_all_windows()
|
||||
super(TestNoSuchWindowContent, self).tearDown()
|
||||
|
||||
def open_tab_in_foreground(self):
|
||||
with self.marionette.using_context("content"):
|
||||
link = self.marionette.find_element(By.ID, "new-tab")
|
||||
link.click()
|
||||
|
||||
@skip_if_mobile("Fennec doesn't support other chrome windows")
|
||||
def test_closed_chrome_window(self):
|
||||
|
||||
def open_with_link():
|
||||
with self.marionette.using_context("content"):
|
||||
test_page = self.marionette.absolute_url("windowHandles.html")
|
||||
self.marionette.navigate(test_page)
|
||||
self.marionette.find_element(By.ID, "new-window").click()
|
||||
|
||||
win = self.open_window(open_with_link)
|
||||
self.marionette.switch_to_window(win)
|
||||
with self.marionette.using_context("chrome"):
|
||||
new_window = self.open_window()
|
||||
self.marionette.switch_to_window(new_window)
|
||||
self.marionette.close_chrome_window()
|
||||
|
||||
# When closing a browser window both handles are not available
|
||||
|
@ -52,12 +37,13 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_window)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(win)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
@skip_if_mobile("Fennec doesn't support other chrome windows")
|
||||
def test_closed_chrome_window_while_in_frame(self):
|
||||
win = self.open_chrome_window("chrome://marionette/content/test.xul")
|
||||
self.marionette.switch_to_window(win)
|
||||
new_window = self.open_chrome_window("chrome://marionette/content/test.xul")
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
with self.marionette.using_context("chrome"):
|
||||
self.marionette.switch_to_frame("iframe")
|
||||
self.marionette.close_chrome_window()
|
||||
|
@ -70,13 +56,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_window)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(win)
|
||||
self.marionette.switch_to_window(new_window)
|
||||
|
||||
def test_closed_tab(self):
|
||||
with self.marionette.using_context("content"):
|
||||
tab = self.open_tab(self.open_tab_in_foreground)
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.close()
|
||||
new_tab = self.open_tab(focus=True)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.marionette.close()
|
||||
|
||||
# Check that only the content window is not available in both contexts
|
||||
for context in ("chrome", "content"):
|
||||
|
@ -88,22 +73,24 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
|
|||
self.marionette.switch_to_window(self.start_tab)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
def test_closed_tab_while_in_frame(self):
|
||||
new_tab = self.open_tab()
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
||||
with self.marionette.using_context("content"):
|
||||
tab = self.open_tab(self.open_tab_in_foreground)
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.navigate(self.marionette.absolute_url("test_iframe.html"))
|
||||
frame = self.marionette.find_element(By.ID, "test_iframe")
|
||||
self.marionette.switch_to_frame(frame)
|
||||
self.marionette.close()
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.current_window_handle
|
||||
self.marionette.current_chrome_window_handle
|
||||
self.marionette.close()
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.current_window_handle
|
||||
self.marionette.current_chrome_window_handle
|
||||
|
||||
self.marionette.switch_to_window(self.start_tab)
|
||||
|
||||
with self.assertRaises(NoSuchWindowException):
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
|
|
|
@ -15,7 +15,7 @@ ChromeUtils.import("chrome://marionette/content/evaluate.js");
|
|||
const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
|
||||
ChromeUtils.import("chrome://marionette/content/modal.js");
|
||||
const {
|
||||
MessageManagerDestroyedPromise,
|
||||
waitForObserverTopic,
|
||||
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["proxy"];
|
||||
|
@ -156,7 +156,9 @@ proxy.AsyncMessageChannel = class {
|
|||
break;
|
||||
}
|
||||
|
||||
await new MessageManagerDestroyedPromise(messageManager);
|
||||
await waitForObserverTopic("message-manager-disconnect",
|
||||
subject => subject === messageManager);
|
||||
|
||||
this.removeHandlers();
|
||||
resolve();
|
||||
};
|
||||
|
|
|
@ -11,7 +11,6 @@ const ServerSocket = CC(
|
|||
"nsIServerSocket",
|
||||
"initSpecialConnection");
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.import("chrome://marionette/content/assert.js");
|
||||
|
@ -74,7 +73,7 @@ class TCPListener {
|
|||
*/
|
||||
driverFactory() {
|
||||
MarionettePrefs.contentListener = false;
|
||||
return new GeckoDriver(Services.appinfo.ID, this);
|
||||
return new GeckoDriver(this);
|
||||
}
|
||||
|
||||
set acceptConnections(value) {
|
||||
|
|
|
@ -13,23 +13,42 @@ const {
|
|||
stack,
|
||||
TimeoutError,
|
||||
} = ChromeUtils.import("chrome://marionette/content/error.js", {});
|
||||
const {truncate} = ChromeUtils.import("chrome://marionette/content/format.js", {});
|
||||
const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"executeSoon",
|
||||
"DebounceCallback",
|
||||
"IdlePromise",
|
||||
"MessageManagerDestroyedPromise",
|
||||
"PollPromise",
|
||||
"Sleep",
|
||||
"TimedPromise",
|
||||
"waitForEvent",
|
||||
"waitForMessage",
|
||||
"waitForObserverTopic",
|
||||
];
|
||||
|
||||
const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer;
|
||||
|
||||
const PROMISE_TIMEOUT = AppConstants.DEBUG ? 4500 : 1500;
|
||||
|
||||
|
||||
/**
|
||||
* Dispatch a function to be executed on the main thread.
|
||||
*
|
||||
* @param {function} func
|
||||
* Function to be executed.
|
||||
*/
|
||||
function executeSoon(func) {
|
||||
if (typeof func != "function") {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
Services.tm.dispatchToMainThread(func);
|
||||
}
|
||||
|
||||
/**
|
||||
* @callback Condition
|
||||
*
|
||||
|
@ -236,46 +255,6 @@ function Sleep(timeout) {
|
|||
return new TimedPromise(() => {}, {timeout, throws: null});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects when the specified message manager has been destroyed.
|
||||
*
|
||||
* One can observe the removal and detachment of a content browser
|
||||
* (`<xul:browser>`) or a chrome window by its message manager
|
||||
* disconnecting.
|
||||
*
|
||||
* When a browser is associated with a tab, this is safer than only
|
||||
* relying on the event `TabClose` which signalises the _intent to_
|
||||
* remove a tab and consequently would lead to the destruction of
|
||||
* the content browser and its browser message manager.
|
||||
*
|
||||
* When closing a chrome window it is safer than only relying on
|
||||
* the event 'unload' which signalises the _intent to_ close the
|
||||
* chrome window and consequently would lead to the destruction of
|
||||
* the window and its window message manager.
|
||||
*
|
||||
* @param {MessageListenerManager} messageManager
|
||||
* The message manager to observe for its disconnect state.
|
||||
* Use the browser message manager when closing a content browser,
|
||||
* and the window message manager when closing a chrome window.
|
||||
*
|
||||
* @return {Promise}
|
||||
* A promise that resolves when the message manager has been destroyed.
|
||||
*/
|
||||
function MessageManagerDestroyedPromise(messageManager) {
|
||||
return new Promise(resolve => {
|
||||
function observe(subject, topic) {
|
||||
log.trace(`Received observer notification ${topic}`);
|
||||
|
||||
if (subject == messageManager) {
|
||||
Services.obs.removeObserver(this, "message-manager-disconnect");
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
Services.obs.addObserver(observe, "message-manager-disconnect");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle until the main thread is idle and `window` has performed
|
||||
* an animation frame (in that order).
|
||||
|
@ -351,3 +330,192 @@ class DebounceCallback {
|
|||
}
|
||||
}
|
||||
this.DebounceCallback = DebounceCallback;
|
||||
|
||||
/**
|
||||
* Wait for an event to be fired on a specified element.
|
||||
*
|
||||
* This method has been duplicated from BrowserTestUtils.jsm.
|
||||
*
|
||||
* Because this function is intended for testing, any error in checkFn
|
||||
* will cause the returned promise to be rejected instead of waiting for
|
||||
* the next event, since this is probably a bug in the test.
|
||||
*
|
||||
* Usage::
|
||||
*
|
||||
* let promiseEvent = waitForEvent(element, "eventName");
|
||||
* // Do some processing here that will cause the event to be fired
|
||||
* // ...
|
||||
* // Now wait until the Promise is fulfilled
|
||||
* let receivedEvent = await promiseEvent;
|
||||
*
|
||||
* The promise resolution/rejection handler for the returned promise is
|
||||
* guaranteed not to be called until the next event tick after the event
|
||||
* listener gets called, so that all other event listeners for the element
|
||||
* are executed before the handler is executed::
|
||||
*
|
||||
* let promiseEvent = waitForEvent(element, "eventName");
|
||||
* // Same event tick here.
|
||||
* await promiseEvent;
|
||||
* // Next event tick here.
|
||||
*
|
||||
* If some code, such like adding yet another event listener, needs to be
|
||||
* executed in the same event tick, use raw addEventListener instead and
|
||||
* place the code inside the event listener::
|
||||
*
|
||||
* element.addEventListener("load", () => {
|
||||
* // Add yet another event listener in the same event tick as the load
|
||||
* // event listener.
|
||||
* p = waitForEvent(element, "ready");
|
||||
* }, { once: true });
|
||||
*
|
||||
* @param {Element} subject
|
||||
* The element that should receive the event.
|
||||
* @param {string} eventName
|
||||
* Name of the event to listen to.
|
||||
* @param {Object=} options
|
||||
* Extra options.
|
||||
* @param {boolean=} options.capture
|
||||
* True to use a capturing listener.
|
||||
* @param {function(Event)=} options.checkFn
|
||||
* Called with the ``Event`` object as argument, should return ``true``
|
||||
* if the event is the expected one, or ``false`` if it should be
|
||||
* ignored and listening should continue. If not specified, the first
|
||||
* event with the specified name resolves the returned promise.
|
||||
* @param {boolean=} options.wantsUntrusted
|
||||
* True to receive synthetic events dispatched by web content.
|
||||
*
|
||||
* @return {Promise.<Event>}
|
||||
* Promise which resolves to the received ``Event`` object, or rejects
|
||||
* in case of a failure.
|
||||
*/
|
||||
function waitForEvent(subject, eventName,
|
||||
{capture = false, checkFn = null, wantsUntrusted = false} = {}) {
|
||||
if (subject == null || !("addEventListener" in subject)) {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (typeof eventName != "string") {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (capture != null && typeof capture != "boolean") {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (checkFn != null && typeof checkFn != "function") {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (wantsUntrusted != null && typeof wantsUntrusted != "boolean") {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
subject.addEventListener(eventName, function listener(event) {
|
||||
log.trace(`Received DOM event ${event.type} for ${event.target}`);
|
||||
try {
|
||||
if (checkFn && !checkFn(event)) {
|
||||
return;
|
||||
}
|
||||
subject.removeEventListener(eventName, listener, capture);
|
||||
executeSoon(() => resolve(event));
|
||||
} catch (ex) {
|
||||
try {
|
||||
subject.removeEventListener(eventName, listener, capture);
|
||||
} catch (ex2) {
|
||||
// Maybe the provided object does not support removeEventListener.
|
||||
}
|
||||
executeSoon(() => reject(ex));
|
||||
}
|
||||
}, capture, wantsUntrusted);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a message to be fired from a particular message manager.
|
||||
*
|
||||
* This method has been duplicated from BrowserTestUtils.jsm.
|
||||
*
|
||||
* @param {nsIMessageManager} messageManager
|
||||
* The message manager that should be used.
|
||||
* @param {string} messageName
|
||||
* The message to wait for.
|
||||
* @param {Object=} options
|
||||
* Extra options.
|
||||
* @param {function(Message)=} options.checkFn
|
||||
* Called with the ``Message`` object as argument, should return ``true``
|
||||
* if the message is the expected one, or ``false`` if it should be
|
||||
* ignored and listening should continue. If not specified, the first
|
||||
* message with the specified name resolves the returned promise.
|
||||
*
|
||||
* @return {Promise.<Object>}
|
||||
* Promise which resolves to the data property of the received
|
||||
* ``Message``.
|
||||
*/
|
||||
function waitForMessage(messageManager, messageName,
|
||||
{checkFn = undefined} = {}) {
|
||||
if (messageManager == null || !("addMessageListener" in messageManager)) {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (typeof messageName != "string") {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (checkFn && typeof checkFn != "function") {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
messageManager.addMessageListener(messageName, function onMessage(msg) {
|
||||
log.trace(`Received ${messageName} for ${msg.target}`);
|
||||
if (checkFn && !checkFn(msg)) {
|
||||
return;
|
||||
}
|
||||
messageManager.removeMessageListener(messageName, onMessage);
|
||||
resolve(msg.data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the specified observer topic to be observed.
|
||||
*
|
||||
* This method has been duplicated from TestUtils.jsm.
|
||||
*
|
||||
* Because this function is intended for testing, any error in checkFn
|
||||
* will cause the returned promise to be rejected instead of waiting for
|
||||
* the next notification, since this is probably a bug in the test.
|
||||
*
|
||||
* @param {string} topic
|
||||
* The topic to observe.
|
||||
* @param {Object=} options
|
||||
* Extra options.
|
||||
* @param {function(String,Object)=} options.checkFn
|
||||
* Called with ``subject``, and ``data`` as arguments, should return true
|
||||
* if the notification is the expected one, or false if it should be
|
||||
* ignored and listening should continue. If not specified, the first
|
||||
* notification for the specified topic resolves the returned promise.
|
||||
*
|
||||
* @return {Promise.<Array<String, Object>>}
|
||||
* Promise which resolves to an array of ``subject``, and ``data`` from
|
||||
* the observed notification.
|
||||
*/
|
||||
function waitForObserverTopic(topic, {checkFn = null} = {}) {
|
||||
if (typeof topic != "string") {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (checkFn != null && typeof checkFn != "function") {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Services.obs.addObserver(function observer(subject, topic, data) {
|
||||
log.trace(`Received observer notification ${topic}`);
|
||||
try {
|
||||
if (checkFn && !checkFn(subject, data)) {
|
||||
return;
|
||||
}
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
resolve({subject, data});
|
||||
} catch (ex) {
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
reject(ex);
|
||||
}
|
||||
}, topic);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,16 +2,93 @@
|
|||
* 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/. */
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {
|
||||
DebounceCallback,
|
||||
IdlePromise,
|
||||
PollPromise,
|
||||
Sleep,
|
||||
TimedPromise,
|
||||
waitForEvent,
|
||||
waitForMessage,
|
||||
waitForObserverTopic,
|
||||
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
|
||||
|
||||
const DEFAULT_TIMEOUT = 2000;
|
||||
|
||||
/**
|
||||
* Mimic a DOM node for listening for events.
|
||||
*/
|
||||
class MockElement {
|
||||
constructor() {
|
||||
this.capture = false;
|
||||
this.func = null;
|
||||
this.eventName = null;
|
||||
this.untrusted = false;
|
||||
}
|
||||
|
||||
addEventListener(name, func, capture, untrusted) {
|
||||
this.eventName = name;
|
||||
this.func = func;
|
||||
if (capture != null) {
|
||||
this.capture = capture;
|
||||
}
|
||||
if (untrusted != null) {
|
||||
this.untrusted = untrusted;
|
||||
}
|
||||
}
|
||||
|
||||
click() {
|
||||
if (this.func) {
|
||||
let details = {
|
||||
capture: this.capture,
|
||||
target: this,
|
||||
type: this.eventName,
|
||||
untrusted: this.untrusted,
|
||||
};
|
||||
this.func(details);
|
||||
}
|
||||
}
|
||||
|
||||
removeEventListener(name, func) {
|
||||
this.capture = false;
|
||||
this.func = null;
|
||||
this.eventName = null;
|
||||
this.untrusted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimic a message manager for sending messages.
|
||||
*/
|
||||
class MessageManager {
|
||||
constructor() {
|
||||
this.func = null;
|
||||
this.message = null;
|
||||
}
|
||||
|
||||
addMessageListener(message, func) {
|
||||
this.func = func;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
removeMessageListener(message) {
|
||||
this.func = null;
|
||||
this.message = null;
|
||||
}
|
||||
|
||||
send(message, data) {
|
||||
if (this.func) {
|
||||
this.func({
|
||||
data,
|
||||
message,
|
||||
target: this,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimics nsITimer, but instead of using a system clock you can
|
||||
* preprogram it to invoke the callback after a given number of ticks.
|
||||
|
@ -35,6 +112,23 @@ class MockTimer {
|
|||
}
|
||||
}
|
||||
|
||||
add_test(function test_executeSoon_callback() {
|
||||
// executeSoon() is already defined for xpcshell in head.js. As such import
|
||||
// our implementation into a custom namespace.
|
||||
let sync = {};
|
||||
ChromeUtils.import("chrome://marionette/content/sync.js", sync);
|
||||
|
||||
for (let func of ["foo", null, true, [], {}]) {
|
||||
Assert.throws(() => sync.executeSoon(func), /TypeError/);
|
||||
}
|
||||
|
||||
let a;
|
||||
sync.executeSoon(() => { a = 1; });
|
||||
executeSoon(() => equal(1, a));
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_PollPromise_funcTypes() {
|
||||
for (let type of ["foo", 42, null, undefined, true, [], {}]) {
|
||||
Assert.throws(() => new PollPromise(type), /TypeError/);
|
||||
|
@ -213,3 +307,155 @@ add_task(async function test_DebounceCallback_repeatedCallback() {
|
|||
equal(ncalls, 1);
|
||||
ok(debouncer.timer.cancelled);
|
||||
});
|
||||
|
||||
add_task(async function test_waitForEvent_subjectAndEventNameTypes() {
|
||||
let element = new MockElement();
|
||||
|
||||
for (let subject of ["foo", 42, null, undefined, true, [], {}]) {
|
||||
Assert.throws(() => waitForEvent(subject, "click"), /TypeError/);
|
||||
}
|
||||
|
||||
for (let eventName of [42, null, undefined, true, [], {}]) {
|
||||
Assert.throws(() => waitForEvent(element, eventName), /TypeError/);
|
||||
}
|
||||
|
||||
let clicked = waitForEvent(element, "click");
|
||||
element.click();
|
||||
let event = await clicked;
|
||||
equal(element, event.target);
|
||||
});
|
||||
|
||||
add_task(async function test_waitForEvent_captureTypes() {
|
||||
let element = new MockElement();
|
||||
|
||||
for (let capture of ["foo", 42, [], {}]) {
|
||||
Assert.throws(() => waitForEvent(
|
||||
element, "click", {capture}), /TypeError/);
|
||||
}
|
||||
|
||||
for (let capture of [null, undefined, false, true]) {
|
||||
let expected_capture = (capture == null) ? false : capture;
|
||||
|
||||
element = new MockElement();
|
||||
let clicked = waitForEvent(element, "click", {capture});
|
||||
element.click();
|
||||
let event = await clicked;
|
||||
equal(element, event.target);
|
||||
equal(expected_capture, event.capture);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_waitForEvent_checkFnTypes() {
|
||||
let element = new MockElement();
|
||||
|
||||
for (let checkFn of ["foo", 42, true, [], {}]) {
|
||||
Assert.throws(() => waitForEvent(
|
||||
element, "click", {checkFn}), /TypeError/);
|
||||
}
|
||||
|
||||
let count;
|
||||
for (let checkFn of [null, undefined, event => count++ > 0]) {
|
||||
let expected_count = (checkFn == null) ? 0 : 2;
|
||||
count = 0;
|
||||
|
||||
element = new MockElement();
|
||||
let clicked = waitForEvent(element, "click", {checkFn});
|
||||
element.click();
|
||||
element.click();
|
||||
let event = await clicked;
|
||||
equal(element, event.target);
|
||||
equal(expected_count, count);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_waitForEvent_wantsUntrustedTypes() {
|
||||
let element = new MockElement();
|
||||
|
||||
for (let wantsUntrusted of ["foo", 42, [], {}]) {
|
||||
Assert.throws(() => waitForEvent(
|
||||
element, "click", {wantsUntrusted}), /TypeError/);
|
||||
}
|
||||
|
||||
for (let wantsUntrusted of [null, undefined, false, true]) {
|
||||
let expected_untrusted = (wantsUntrusted == null) ? false : wantsUntrusted;
|
||||
|
||||
element = new MockElement();
|
||||
let clicked = waitForEvent(element, "click", {wantsUntrusted});
|
||||
element.click();
|
||||
let event = await clicked;
|
||||
equal(element, event.target);
|
||||
equal(expected_untrusted, event.untrusted);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_waitForMessage_messageManagerAndMessageTypes() {
|
||||
let messageManager = new MessageManager();
|
||||
|
||||
for (let manager of ["foo", 42, null, undefined, true, [], {}]) {
|
||||
Assert.throws(() => waitForMessage(manager, "message"), /TypeError/);
|
||||
}
|
||||
|
||||
for (let message of [42, null, undefined, true, [], {}]) {
|
||||
Assert.throws(() => waitForEvent(messageManager, message), /TypeError/);
|
||||
}
|
||||
|
||||
let data = {"foo": "bar"};
|
||||
let sent = waitForMessage(messageManager, "message");
|
||||
messageManager.send("message", data);
|
||||
equal(data, await sent);
|
||||
});
|
||||
|
||||
add_task(async function test_waitForMessage_checkFnTypes() {
|
||||
let messageManager = new MessageManager();
|
||||
|
||||
for (let checkFn of ["foo", 42, true, [], {}]) {
|
||||
Assert.throws(() => waitForMessage(
|
||||
messageManager, "message", {checkFn}), /TypeError/);
|
||||
}
|
||||
|
||||
let data1 = {"fo": "bar"};
|
||||
let data2 = {"foo": "bar"};
|
||||
|
||||
for (let checkFn of [null, undefined, msg => "foo" in msg.data]) {
|
||||
let expected_data = (checkFn == null) ? data1 : data2;
|
||||
|
||||
messageManager = new MessageManager();
|
||||
let sent = waitForMessage(messageManager, "message", {checkFn});
|
||||
messageManager.send("message", data1);
|
||||
messageManager.send("message", data2);
|
||||
equal(expected_data, await sent);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_waitForObserverTopic_topicTypes() {
|
||||
for (let topic of [42, null, undefined, true, [], {}]) {
|
||||
Assert.throws(() => waitForObserverTopic(topic), /TypeError/);
|
||||
}
|
||||
|
||||
let data = {"foo": "bar"};
|
||||
let sent = waitForObserverTopic("message");
|
||||
Services.obs.notifyObservers(this, "message", data);
|
||||
let result = await sent;
|
||||
equal(this, result.subject);
|
||||
equal(data, result.data);
|
||||
});
|
||||
|
||||
add_task(async function test_waitForObserverTopic_checkFnTypes() {
|
||||
for (let checkFn of ["foo", 42, true, [], {}]) {
|
||||
Assert.throws(() => waitForObserverTopic(
|
||||
"message", {checkFn}), /TypeError/);
|
||||
}
|
||||
|
||||
let data1 = {"fo": "bar"};
|
||||
let data2 = {"foo": "bar"};
|
||||
|
||||
for (let checkFn of [null, undefined, (subject, data) => data == data2]) {
|
||||
let expected_data = (checkFn == null) ? data1 : data2;
|
||||
|
||||
let sent = waitForObserverTopic("message");
|
||||
Services.obs.notifyObservers(this, "message", data1);
|
||||
Services.obs.notifyObservers(this, "message", data2);
|
||||
let result = await sent;
|
||||
equal(expected_data, result.data);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,14 +10,17 @@ const CC = Components.Constructor;
|
|||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
|
||||
const {StreamUtils} =
|
||||
ChromeUtils.import("chrome://marionette/content/stream-utils.js", {});
|
||||
const {Packet, JSONPacket, BulkPacket} =
|
||||
ChromeUtils.import("chrome://marionette/content/packets.js", {});
|
||||
|
||||
const executeSoon = function(func) {
|
||||
Services.tm.dispatchToMainThread(func);
|
||||
};
|
||||
const {
|
||||
StreamUtils,
|
||||
} = ChromeUtils.import("chrome://marionette/content/stream-utils.js", {});
|
||||
const {
|
||||
BulkPacket,
|
||||
JSONPacket,
|
||||
Packet,
|
||||
} = ChromeUtils.import("chrome://marionette/content/packets.js", {});
|
||||
const {
|
||||
executeSoon,
|
||||
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
|
||||
|
||||
const flags = {wantVerbose: false, wantLogging: false};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[DEFAULT]
|
||||
subsuite = mozbase, os == "linux"
|
||||
subsuite = mozbase
|
||||
skip-if = python == 3
|
||||
[test_expressionparser.py]
|
||||
[test_manifestparser.py]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[DEFAULT]
|
||||
subsuite = mozbase, os == "linux"
|
||||
subsuite = mozbase
|
||||
skip-if = python == 3
|
||||
[test_basic.py]
|
||||
[test_java_exception.py]
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[DEFAULT]
|
||||
subsuite = mozbase, os == "linux"
|
||||
subsuite = mozbase
|
||||
[test.py]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[DEFAULT]
|
||||
subsuite = mozbase, os == "linux"
|
||||
subsuite = mozbase
|
||||
skip-if = python == 3
|
||||
[test_socket_connection.py]
|
||||
[test_is_app_installed.py]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[DEFAULT]
|
||||
subsuite = mozbase, os == "linux"
|
||||
subsuite = mozbase
|
||||
[test_extract.py]
|
||||
[test_load.py]
|
||||
[test_move_remove.py]
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче