mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
b6b164ec6d
@ -83,12 +83,6 @@ devtools/client/commandline/**
|
||||
devtools/client/debugger/**
|
||||
devtools/client/eyedropper/**
|
||||
devtools/client/framework/**
|
||||
# devtools/client/inspector/shared/*.js files are eslint-clean, so they aren't
|
||||
# included in the ignore list.
|
||||
devtools/client/inspector/fonts/**
|
||||
devtools/client/inspector/test/**
|
||||
devtools/client/inspector/*.js
|
||||
!devtools/client/inspector/breadcrumbs.js
|
||||
devtools/client/jsonview/lib/**
|
||||
devtools/client/memory/**
|
||||
devtools/client/netmonitor/test/**
|
||||
|
@ -28,7 +28,7 @@
|
||||
"extensions.blocklist.url": "http://localhost/extensions-dummy/blocklistURL",
|
||||
"extensions.webservice.discoverURL": "http://localhost/extensions-dummy/discoveryURL",
|
||||
"extensions.getAddons.maxResults": 0,
|
||||
"services.kinto.base": "http://localhost/dummy-kinto/v1",
|
||||
"services.blocklist.base": "http://localhost/dummy-kinto/v1",
|
||||
"geo.wifi.uri": "http://localhost/location-dummy/locationURL",
|
||||
"browser.search.geoip.url": "http://localhost/location-dummy/locationURL",
|
||||
"browser.search.isUS": true,
|
||||
|
0
autom4te.cache/output.0t
Normal file
0
autom4te.cache/output.0t
Normal file
0
autom4te.cache/requests
Normal file
0
autom4te.cache/requests
Normal file
0
autom4te.cache/traces.0t
Normal file
0
autom4te.cache/traces.0t
Normal file
@ -1087,11 +1087,6 @@ pref("dom.performance.enable_notify_performance_timing", true);
|
||||
pref("b2g.multiscreen.chrome_remote_url", "chrome://b2g/content/shell_remote.html");
|
||||
pref("b2g.multiscreen.system_remote_url", "index_remote.html");
|
||||
|
||||
// Blocklist service
|
||||
pref("extensions.blocklist.enabled", true);
|
||||
pref("extensions.blocklist.interval", 86400);
|
||||
pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
|
||||
pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
|
||||
|
||||
// Because we can't have nice things.
|
||||
#ifdef MOZ_GRAPHENE
|
||||
|
@ -50,38 +50,6 @@ pref("extensions.webservice.discoverURL", "https://discovery.addons.mozilla.org/
|
||||
pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/list/recommended/all/%MAX_RESULTS%/%OS%/%VERSION%?src=firefox");
|
||||
pref("extensions.getAddons.link.url", "https://addons.mozilla.org/%LOCALE%/firefox/");
|
||||
|
||||
// Blocklist preferences
|
||||
pref("extensions.blocklist.enabled", true);
|
||||
// OneCRL freshness checking depends on this value, so if you change it,
|
||||
// please also update security.onecrl.maximum_staleness_in_seconds.
|
||||
pref("extensions.blocklist.interval", 86400);
|
||||
// Controls what level the blocklist switches from warning about items to forcibly
|
||||
// blocking them.
|
||||
pref("extensions.blocklist.level", 2);
|
||||
pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
|
||||
pref("extensions.blocklist.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/");
|
||||
pref("extensions.blocklist.itemURL", "https://blocklist.addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%");
|
||||
|
||||
// Kinto blocklist preferences
|
||||
pref("services.kinto.base", "https://firefox.settings.services.mozilla.com/v1");
|
||||
pref("services.kinto.changes.path", "/buckets/monitor/collections/changes/records");
|
||||
pref("services.kinto.bucket", "blocklists");
|
||||
pref("services.kinto.onecrl.collection", "certificates");
|
||||
pref("services.kinto.onecrl.checked", 0);
|
||||
pref("services.kinto.addons.collection", "addons");
|
||||
pref("services.kinto.addons.checked", 0);
|
||||
pref("services.kinto.plugins.collection", "plugins");
|
||||
pref("services.kinto.plugins.checked", 0);
|
||||
pref("services.kinto.gfx.collection", "gfx");
|
||||
pref("services.kinto.gfx.checked", 0);
|
||||
|
||||
// for now, let's keep kinto update out of the release channel
|
||||
#ifdef RELEASE_BUILD
|
||||
pref("services.kinto.update_enabled", false);
|
||||
#else
|
||||
pref("services.kinto.update_enabled", true);
|
||||
#endif
|
||||
|
||||
pref("extensions.update.autoUpdateDefault", true);
|
||||
|
||||
pref("extensions.hotfix.id", "firefox-hotfix@mozilla.org");
|
||||
@ -1211,9 +1179,6 @@ pref("security.insecure_password.ui.enabled", false);
|
||||
// 1 = allow MITM for certificate pinning checks.
|
||||
pref("security.cert_pinning.enforcement_level", 1);
|
||||
|
||||
// Required blocklist freshness for OneCRL OCSP bypass
|
||||
// (default is 1.25x extensions.blocklist.interval, or 30 hours)
|
||||
pref("security.onecrl.maximum_staleness_in_seconds", 108000);
|
||||
|
||||
// Override the Gecko-default value of false for Firefox.
|
||||
pref("plain_text.wrap_long_lines", true);
|
||||
|
@ -1946,3 +1946,27 @@ var BookmarkingUI = {
|
||||
Ci.nsINavBookmarkObserver
|
||||
])
|
||||
};
|
||||
|
||||
var AutoShowBookmarksToolbar = {
|
||||
init() {
|
||||
Services.obs.addObserver(this, "autoshow-bookmarks-toolbar", false);
|
||||
},
|
||||
|
||||
uninit() {
|
||||
Services.obs.removeObserver(this, "autoshow-bookmarks-toolbar");
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
let toolbar = document.getElementById("PersonalToolbar");
|
||||
if (!toolbar.collapsed)
|
||||
return;
|
||||
|
||||
let placement = CustomizableUI.getPlacementOfWidget("personal-bookmarks");
|
||||
let area = placement && placement.area;
|
||||
if (area != CustomizableUI.AREA_BOOKMARKS)
|
||||
return;
|
||||
|
||||
setToolbarVisibility(toolbar, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1206,6 +1206,7 @@ var gBrowserInit = {
|
||||
gBrowser.tabContainer.updateVisibility();
|
||||
|
||||
BookmarkingUI.init();
|
||||
AutoShowBookmarksToolbar.init();
|
||||
|
||||
gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
|
||||
|
||||
@ -1500,6 +1501,7 @@ var gBrowserInit = {
|
||||
IndexedDBPromptHelper.uninit();
|
||||
LightweightThemeListener.uninit();
|
||||
PanelUI.uninit();
|
||||
AutoShowBookmarksToolbar.uninit();
|
||||
}
|
||||
|
||||
// Final window teardown, do this last.
|
||||
@ -7859,11 +7861,17 @@ TabModalPromptBox.prototype = {
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
|
||||
let browser = this.browser;
|
||||
browser.parentNode.appendChild(newPrompt);
|
||||
browser.parentNode.insertBefore(newPrompt, browser.nextSibling);
|
||||
browser.setAttribute("tabmodalPromptShowing", true);
|
||||
|
||||
newPrompt.clientTop; // style flush to assure binding is attached
|
||||
|
||||
let prompts = this.listPrompts();
|
||||
if (prompts.length > 1) {
|
||||
// Let's hide ourself behind the current prompt.
|
||||
newPrompt.hidden = true;
|
||||
}
|
||||
|
||||
let principalToAllowFocusFor = this._allowTabFocusByPromptPrincipal;
|
||||
delete this._allowTabFocusByPromptPrincipal;
|
||||
|
||||
@ -7898,6 +7906,7 @@ TabModalPromptBox.prototype = {
|
||||
let prompts = this.listPrompts();
|
||||
if (prompts.length) {
|
||||
let prompt = prompts[prompts.length - 1];
|
||||
prompt.hidden = false;
|
||||
prompt.Dialog.setDefaultFocus();
|
||||
} else {
|
||||
browser.removeAttribute("tabmodalPromptShowing");
|
||||
|
@ -231,7 +231,7 @@ const SEC_ERROR_OCSP_FUTURE_RESPONSE = SEC_ERROR_BASE + 131;
|
||||
const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132;
|
||||
const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
|
||||
|
||||
const PREF_KINTO_CLOCK_SKEW_SECONDS = "services.kinto.clock_skew_seconds";
|
||||
const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
|
||||
|
||||
const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.min", "security.tls.version.max", "security.ssl3."];
|
||||
|
||||
@ -280,16 +280,16 @@ var AboutNetAndCertErrorListener = {
|
||||
break;
|
||||
|
||||
// in case the certificate expired we make sure the system clock
|
||||
// matches kinto server time
|
||||
// matches settings server (kinto) time
|
||||
case SEC_ERROR_EXPIRED_CERTIFICATE:
|
||||
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
|
||||
case SEC_ERROR_OCSP_FUTURE_RESPONSE:
|
||||
case SEC_ERROR_OCSP_OLD_RESPONSE:
|
||||
case MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE:
|
||||
|
||||
// use Kinto stats if available
|
||||
if (Services.prefs.getPrefType(PREF_KINTO_CLOCK_SKEW_SECONDS)) {
|
||||
let difference = Services.prefs.getIntPref(PREF_KINTO_CLOCK_SKEW_SECONDS);
|
||||
// use blocklist stats if available
|
||||
if (Services.prefs.getPrefType(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS)) {
|
||||
let difference = Services.prefs.getIntPref(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS);
|
||||
|
||||
// if the difference is more than a day
|
||||
if (Math.abs(difference) > 60 * 60 * 24) {
|
||||
|
@ -343,8 +343,6 @@ support-files = feed_discovery.html
|
||||
[browser_gZipOfflineChild.js]
|
||||
skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
|
||||
support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^
|
||||
[browser_openPromptInBackgroundTab.js]
|
||||
support-files = openPromptOffTimeout.html
|
||||
[browser_overflowScroll.js]
|
||||
[browser_pageInfo.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
|
@ -105,7 +105,7 @@ add_task(function* checkBadStsCert() {
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
const PREF_KINTO_CLOCK_SKEW_SECONDS = "services.kinto.clock_skew_seconds";
|
||||
const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
|
||||
|
||||
add_task(function* checkWrongSystemTimeWarning() {
|
||||
function* setUpPage() {
|
||||
@ -144,7 +144,7 @@ add_task(function* checkWrongSystemTimeWarning() {
|
||||
|
||||
let skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
|
||||
yield new Promise(r => SpecialPowers.pushPrefEnv({set:
|
||||
[[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
[[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
|
||||
info("Loading a bad cert page with a skewed clock");
|
||||
let message = yield Task.spawn(setUpPage);
|
||||
@ -165,7 +165,7 @@ add_task(function* checkWrongSystemTimeWarning() {
|
||||
|
||||
skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
|
||||
yield new Promise(r => SpecialPowers.pushPrefEnv({set:
|
||||
[[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
[[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
|
||||
info("Loading a bad cert page with a skewed clock");
|
||||
message = yield Task.spawn(setUpPage);
|
||||
@ -182,7 +182,7 @@ add_task(function* checkWrongSystemTimeWarning() {
|
||||
// pretend we only have a slightly skewed system time, four hours
|
||||
skew = 60 * 60 * 4;
|
||||
yield new Promise(r => SpecialPowers.pushPrefEnv({set:
|
||||
[[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
[[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
|
||||
info("Loading a bad cert page with an only slightly skewed clock");
|
||||
message = yield Task.spawn(setUpPage);
|
||||
@ -194,7 +194,7 @@ add_task(function* checkWrongSystemTimeWarning() {
|
||||
// now pretend we have no skewed system time
|
||||
skew = 0;
|
||||
yield new Promise(r => SpecialPowers.pushPrefEnv({set:
|
||||
[[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
[[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
|
||||
|
||||
info("Loading a bad cert page with no skewed clock");
|
||||
message = yield Task.spawn(setUpPage);
|
||||
|
3
browser/base/content/test/tabPrompts/browser.ini
Normal file
3
browser/base/content/test/tabPrompts/browser.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[browser_multiplePrompts.js]
|
||||
[browser_openPromptInBackgroundTab.js]
|
||||
support-files = openPromptOffTimeout.html
|
@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* This test triggers multiple alerts on one single tab, because it"s possible
|
||||
* for web content to do so. The behavior is described in bug 1266353.
|
||||
*
|
||||
* We assert the presentation of the multiple alerts, ensuring we show only
|
||||
* the oldest one.
|
||||
*/
|
||||
add_task(function*() {
|
||||
const PROMPTCOUNT = 5;
|
||||
|
||||
let contentScript = function() {
|
||||
var i = 5; // contentScript has no access to PROMPTCOUNT.
|
||||
window.addEventListener("message", function() {
|
||||
i--;
|
||||
if (i) {
|
||||
window.postMessage("ping", "*");
|
||||
}
|
||||
alert("Alert countdown #" + i);
|
||||
});
|
||||
window.postMessage("ping", "*");
|
||||
};
|
||||
let url = "data:text/html,<script>(" + encodeURIComponent(contentScript.toSource()) + ")();</script>"
|
||||
|
||||
let promptsOpenedPromise = new Promise(function(resolve) {
|
||||
let unopenedPromptCount = PROMPTCOUNT;
|
||||
Services.obs.addObserver(function observer() {
|
||||
unopenedPromptCount--;
|
||||
if (!unopenedPromptCount) {
|
||||
Services.obs.removeObserver(observer, "tabmodal-dialog-loaded");
|
||||
info("Prompts opened.");
|
||||
resolve();
|
||||
}
|
||||
}, "tabmodal-dialog-loaded", false);
|
||||
});
|
||||
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
|
||||
info("Tab loaded");
|
||||
|
||||
yield promptsOpenedPromise;
|
||||
|
||||
let promptsCount = PROMPTCOUNT;
|
||||
while (promptsCount--) {
|
||||
let prompts = tab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt");
|
||||
is(prompts.length, promptsCount + 1, "There should be " + (promptsCount + 1) + " prompt(s).");
|
||||
// The oldest should be the first.
|
||||
let i = 0;
|
||||
for (let prompt of prompts) {
|
||||
is(prompt.Dialog.args.text, "Alert countdown #" + i, "The #" + i + " alert should be labelled as such.");
|
||||
if (i !== promptsCount) {
|
||||
is(prompt.hidden, true, "This prompt should be hidden.");
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
is(prompt.hidden, false, "The last prompt should not be hidden.");
|
||||
prompt.onButtonClick(0);
|
||||
}
|
||||
}
|
||||
|
||||
let prompts = tab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt");
|
||||
is(prompts.length, 0, "Prompts should all be dismissed.");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
@ -15,7 +15,7 @@ registerCleanupFunction(function() {
|
||||
* checking the checkbox does actually enable that behaviour.
|
||||
*/
|
||||
add_task(function*() {
|
||||
yield pushPrefs(["browser.tabs.dontfocusfordialogs", true]);
|
||||
yield SpecialPowers.pushPrefEnv({"set": [["browser.tabs.dontfocusfordialogs", true]]});
|
||||
let firstTab = gBrowser.selectedTab;
|
||||
// load page that opens prompt when page is hidden
|
||||
let openedTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageWithAlert, true);
|
||||
@ -60,4 +60,3 @@ add_task(function*() {
|
||||
|
||||
yield BrowserTestUtils.removeTab(openedTab);
|
||||
});
|
||||
|
@ -23,6 +23,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
'content/test/popupNotifications/browser.ini',
|
||||
'content/test/referrer/browser.ini',
|
||||
'content/test/social/browser.ini',
|
||||
'content/test/tabPrompts/browser.ini',
|
||||
'content/test/urlbar/browser.ini',
|
||||
]
|
||||
|
||||
|
@ -27,6 +27,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
|
||||
"resource://gre/modules/ShortcutUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gELS",
|
||||
"@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
@ -151,6 +153,7 @@ var gListeners = new Set();
|
||||
var gUIStateBeforeReset = {
|
||||
uiCustomizationState: null,
|
||||
drawInTitlebar: null,
|
||||
currentTheme: null,
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
@ -2527,12 +2530,14 @@ var CustomizableUIInternal = {
|
||||
try {
|
||||
gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar);
|
||||
gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
|
||||
gUIStateBeforeReset.currentTheme = LightweightThemeManager.currentTheme;
|
||||
} catch(e) { }
|
||||
|
||||
this._resetExtraToolbars();
|
||||
|
||||
Services.prefs.clearUserPref(kPrefCustomizationState);
|
||||
Services.prefs.clearUserPref(kPrefDrawInTitlebar);
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
log.debug("State reset");
|
||||
|
||||
// Reset placements to make restoring default placements possible.
|
||||
@ -2601,6 +2606,7 @@ var CustomizableUIInternal = {
|
||||
|
||||
let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState;
|
||||
let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar;
|
||||
let currentTheme = gUIStateBeforeReset.currentTheme;
|
||||
|
||||
// Need to clear the previous state before setting the prefs
|
||||
// because pref observers may check if there is a previous UI state.
|
||||
@ -2608,6 +2614,7 @@ var CustomizableUIInternal = {
|
||||
|
||||
Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
|
||||
Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar);
|
||||
LightweightThemeManager.currentTheme = currentTheme;
|
||||
this.loadSavedState();
|
||||
// If the user just customizes toolbar/titlebar visibility, gSavedState will be null
|
||||
// and we don't need to do anything else here:
|
||||
@ -2786,6 +2793,11 @@ var CustomizableUIInternal = {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(LightweightThemeManager.currentTheme) {
|
||||
log.debug(LightweightThemeManager.currentTheme + " theme is non-default");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -3490,7 +3502,8 @@ this.CustomizableUI = {
|
||||
*/
|
||||
get canUndoReset() {
|
||||
return gUIStateBeforeReset.uiCustomizationState != null ||
|
||||
gUIStateBeforeReset.drawInTitlebar != null;
|
||||
gUIStateBeforeReset.drawInTitlebar != null ||
|
||||
gUIStateBeforeReset.currentTheme != null;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1177,6 +1177,8 @@ CustomizeMode.prototype = {
|
||||
|
||||
CustomizableUI.reset();
|
||||
|
||||
this.swatchForTheme(this.document);
|
||||
|
||||
yield this._wrapToolbarItems();
|
||||
this.populatePalette();
|
||||
|
||||
@ -1203,6 +1205,8 @@ CustomizeMode.prototype = {
|
||||
|
||||
CustomizableUI.undoReset();
|
||||
|
||||
this.swatchForTheme(this.document);
|
||||
|
||||
yield this._wrapToolbarItems();
|
||||
this.populatePalette();
|
||||
|
||||
@ -1451,6 +1455,10 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
}
|
||||
target.removeAttribute("height");
|
||||
|
||||
if (LightweightThemeManager.currentTheme) {
|
||||
this._onUIChange();
|
||||
}
|
||||
},
|
||||
|
||||
_onUIChange: function() {
|
||||
|
@ -63,6 +63,22 @@ add_task(function* () {
|
||||
defaultTheme.doCommand();
|
||||
is(Services.prefs.getCharPref("lightweightThemes.selectedThemeID"), "", "No lwtheme should be selected");
|
||||
|
||||
// ensure current theme isn't set to "Default"
|
||||
popupShownPromise = popupShown(popup);
|
||||
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
||||
info("Clicked on themes button a second time");
|
||||
yield popupShownPromise;
|
||||
|
||||
firstLWTheme = recommendedHeader.nextSibling;
|
||||
themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
||||
firstLWTheme.doCommand();
|
||||
info("Clicked on first theme again");
|
||||
yield themeChangedPromise;
|
||||
|
||||
// check that "Restore Defaults" button resets theme
|
||||
yield gCustomizeMode.reset();
|
||||
is(LightweightThemeManager.currentTheme, null, "Current theme reset to default");
|
||||
|
||||
yield endCustomizing();
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", "[]");
|
||||
Services.prefs.setCharPref("lightweightThemes.recommendedThemes", "[]");
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Restoring default should show an "undo" option which undoes the restoring operation.
|
||||
// Restoring default should reset theme and show an "undo" option which undoes the restoring operation.
|
||||
add_task(function*() {
|
||||
let homeButtonId = "home-button";
|
||||
CustomizableUI.removeWidgetFromArea(homeButtonId);
|
||||
@ -16,13 +16,32 @@ add_task(function*() {
|
||||
let undoResetButton = document.getElementById("customization-undo-reset-button");
|
||||
is(undoResetButton.hidden, true, "The undo button is hidden before reset");
|
||||
|
||||
let themesButton = document.getElementById("customization-lwtheme-button");
|
||||
let popup = document.getElementById("customization-lwtheme-menu");
|
||||
let popupShownPromise = popupShown(popup);
|
||||
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
||||
info("Clicked on themes button");
|
||||
yield popupShownPromise;
|
||||
|
||||
let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
|
||||
let firstLWTheme = recommendedHeader.nextSibling;
|
||||
let firstLWThemeId = firstLWTheme.theme.id;
|
||||
let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
||||
firstLWTheme.doCommand();
|
||||
info("Clicked on first theme");
|
||||
yield themeChangedPromise;
|
||||
|
||||
is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme changed to first option");
|
||||
|
||||
yield gCustomizeMode.reset();
|
||||
|
||||
ok(CustomizableUI.inDefaultState, "In default state after reset");
|
||||
is(undoResetButton.hidden, false, "The undo button is visible after reset");
|
||||
is(LightweightThemeManager.currentTheme, null, "Theme reset to default");
|
||||
|
||||
yield gCustomizeMode.undoReset()
|
||||
|
||||
is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme has been reset from default to original choice");
|
||||
ok(!CustomizableUI.inDefaultState, "Not in default state after undo-reset");
|
||||
is(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button");
|
||||
is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
|
||||
|
@ -31,7 +31,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
|
||||
"resource://gre/modules/OSCrypto.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
|
||||
"resource://gre/modules/Sqlite.jsm");
|
||||
/**
|
||||
* Get an nsIFile instance representing the expected location of user data
|
||||
* for this copy of Chrome/Chromium/Canary on different OSes.
|
||||
@ -290,54 +291,63 @@ function GetHistoryResource(aProfileFolder) {
|
||||
return {
|
||||
type: MigrationUtils.resourceTypes.HISTORY,
|
||||
|
||||
migrate: function(aCallback) {
|
||||
let dbConn = Services.storage.openUnsharedDatabase(historyFile);
|
||||
let stmt = dbConn.createAsyncStatement(
|
||||
"SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0");
|
||||
migrate(aCallback) {
|
||||
Task.spawn(function* () {
|
||||
let db = yield Sqlite.openConnection({
|
||||
path: historyFile.path
|
||||
});
|
||||
|
||||
stmt.executeAsync({
|
||||
handleResult : function(aResults) {
|
||||
let places = [];
|
||||
for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
|
||||
try {
|
||||
// if having typed_count, we changes transition type to typed.
|
||||
let transType = PlacesUtils.history.TRANSITION_LINK;
|
||||
if (row.getResultByName("typed_count") > 0)
|
||||
transType = PlacesUtils.history.TRANSITION_TYPED;
|
||||
|
||||
places.push({
|
||||
uri: NetUtil.newURI(row.getResultByName("url")),
|
||||
title: row.getResultByName("title"),
|
||||
visits: [{
|
||||
transitionType: transType,
|
||||
visitDate: chromeTimeToDate(
|
||||
row.getResultByName(
|
||||
"last_visit_time")) * 1000,
|
||||
}],
|
||||
});
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
let rows = yield db.execute(`SELECT url, title, last_visit_time, typed_count
|
||||
FROM urls WHERE hidden = 0`);
|
||||
yield db.close();
|
||||
|
||||
let places = [];
|
||||
for (let row of rows) {
|
||||
try {
|
||||
PlacesUtils.asyncHistory.updatePlaces(places);
|
||||
// if having typed_count, we changes transition type to typed.
|
||||
let transType = PlacesUtils.history.TRANSITION_LINK;
|
||||
if (row.getResultByName("typed_count") > 0)
|
||||
transType = PlacesUtils.history.TRANSITION_TYPED;
|
||||
|
||||
places.push({
|
||||
uri: NetUtil.newURI(row.getResultByName("url")),
|
||||
title: row.getResultByName("title"),
|
||||
visits: [{
|
||||
transitionType: transType,
|
||||
visitDate: chromeTimeToDate(
|
||||
row.getResultByName(
|
||||
"last_visit_time")) * 1000,
|
||||
}],
|
||||
});
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
},
|
||||
|
||||
handleError : function(aError) {
|
||||
Cu.reportError("Async statement execution returned with '" +
|
||||
aError.result + "', '" + aError.message + "'");
|
||||
},
|
||||
|
||||
handleCompletion : function(aReason) {
|
||||
dbConn.asyncClose();
|
||||
aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
|
||||
}
|
||||
});
|
||||
stmt.finalize();
|
||||
|
||||
if (places.length > 0) {
|
||||
yield new Promise((resolve, reject) => {
|
||||
PlacesUtils.asyncHistory.updatePlaces(places, {
|
||||
_success: false,
|
||||
handleResult: function() {
|
||||
// Importing any entry is considered a successful import.
|
||||
this._success = true;
|
||||
},
|
||||
handleError: function() {},
|
||||
handleCompletion: function() {
|
||||
if (this._success) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error("Couldn't add visits"));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}).then(() => { aCallback(true); },
|
||||
ex => {
|
||||
Cu.reportError(ex);
|
||||
aCallback(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
|
||||
"resource://gre/modules/BookmarkHTMLUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
|
||||
"resource://gre/modules/PromiseUtils.jsm");
|
||||
|
||||
var gMigrators = null;
|
||||
var gProfileStartup = null;
|
||||
@ -227,16 +229,21 @@ this.MigratorPrototype = {
|
||||
if (aItems != Ci.nsIBrowserProfileMigrator.ALL)
|
||||
resources = resources.filter(r => aItems & r.type);
|
||||
|
||||
// Used to periodically give back control to the main-thread loop.
|
||||
let unblockMainThread = function () {
|
||||
return new Promise(resolve => {
|
||||
Services.tm.mainThread.dispatch(resolve, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
});
|
||||
};
|
||||
|
||||
// Called either directly or through the bookmarks import callback.
|
||||
function doMigrate() {
|
||||
// TODO: use Map (for the items) and Set (for the resources)
|
||||
// once they are iterable.
|
||||
let doMigrate = Task.async(function*() {
|
||||
let resourcesGroupedByItems = new Map();
|
||||
resources.forEach(function(resource) {
|
||||
if (resourcesGroupedByItems.has(resource.type))
|
||||
resourcesGroupedByItems.get(resource.type).push(resource);
|
||||
else
|
||||
resourcesGroupedByItems.set(resource.type, [resource]);
|
||||
if (!resourcesGroupedByItems.has(resource.type)) {
|
||||
resourcesGroupedByItems.set(resource.type, new Set());
|
||||
}
|
||||
resourcesGroupedByItems.get(resource.type).add(resource)
|
||||
});
|
||||
|
||||
if (resourcesGroupedByItems.size == 0)
|
||||
@ -248,44 +255,52 @@ this.MigratorPrototype = {
|
||||
|
||||
notify("Migration:Started");
|
||||
for (let [key, value] of resourcesGroupedByItems) {
|
||||
// TODO: (bug 449811).
|
||||
// Workaround bug 449811.
|
||||
let migrationType = key, itemResources = value;
|
||||
|
||||
notify("Migration:ItemBeforeMigrate", migrationType);
|
||||
|
||||
let itemSuccess = false;
|
||||
for (let res of itemResources) {
|
||||
// Workaround bug 449811.
|
||||
let resource = res;
|
||||
let completeDeferred = PromiseUtils.defer();
|
||||
let resourceDone = function(aSuccess) {
|
||||
let resourceIndex = itemResources.indexOf(resource);
|
||||
if (resourceIndex != -1) {
|
||||
itemResources.splice(resourceIndex, 1);
|
||||
itemSuccess |= aSuccess;
|
||||
if (itemResources.length == 0) {
|
||||
resourcesGroupedByItems.delete(migrationType);
|
||||
notify(itemSuccess ?
|
||||
"Migration:ItemAfterMigrate" : "Migration:ItemError",
|
||||
migrationType);
|
||||
if (resourcesGroupedByItems.size == 0)
|
||||
notify("Migration:Ended");
|
||||
itemResources.delete(resource);
|
||||
itemSuccess |= aSuccess;
|
||||
if (itemResources.size == 0) {
|
||||
notify(itemSuccess ?
|
||||
"Migration:ItemAfterMigrate" : "Migration:ItemError",
|
||||
migrationType);
|
||||
resourcesGroupedByItems.delete(migrationType);
|
||||
if (resourcesGroupedByItems.size == 0) {
|
||||
notify("Migration:Ended");
|
||||
}
|
||||
}
|
||||
completeDeferred.resolve();
|
||||
}
|
||||
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
// If migrate throws, an error occurred, and the callback
|
||||
// (itemMayBeDone) might haven't been called.
|
||||
try {
|
||||
resource.migrate(resourceDone);
|
||||
}
|
||||
catch(ex) {
|
||||
Cu.reportError(ex);
|
||||
resourceDone(false);
|
||||
}
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
// If migrate throws, an error occurred, and the callback
|
||||
// (itemMayBeDone) might haven't been called.
|
||||
try {
|
||||
resource.migrate(resourceDone);
|
||||
}
|
||||
catch(ex) {
|
||||
Cu.reportError(ex);
|
||||
resourceDone(false);
|
||||
}
|
||||
|
||||
// Certain resources must be ran sequentially or they could fail,
|
||||
// for example bookmarks and history (See bug 1272652).
|
||||
if (migrationType == MigrationUtils.resourceTypes.BOOKMARKS ||
|
||||
migrationType == MigrationUtils.resourceTypes.HISTORY) {
|
||||
yield completeDeferred.promise;
|
||||
}
|
||||
|
||||
yield unblockMainThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator) {
|
||||
MigrationUtils.profileStartup.doStartup();
|
||||
|
@ -230,17 +230,24 @@ class TestFirefoxRefresh(MarionetteTestCase):
|
||||
let cookieEnum = Services.cookies.getCookiesFromHost(arguments[0]);
|
||||
let cookie = null;
|
||||
while (cookieEnum.hasMoreElements()) {
|
||||
let hostCookie = cookieEnum.getNext();
|
||||
hostCookie.QueryInterface(Ci.nsICookie2);
|
||||
// getCookiesFromHost returns any cookie from the BASE host.
|
||||
if (hostCookie.rawHost != arguments[0])
|
||||
continue;
|
||||
if (cookie != null) {
|
||||
return "more than 1 cookie! That shouldn't happen!";
|
||||
}
|
||||
cookie = cookieEnum.getNext();
|
||||
cookie.QueryInterface(Ci.nsICookie2);
|
||||
cookie = hostCookie;
|
||||
}
|
||||
return {path: cookie.path, name: cookie.name, value: cookie.value};
|
||||
} catch (ex) {
|
||||
return "got exception trying to fetch cookie: " + ex;
|
||||
}
|
||||
""", script_args=[self._cookieHost])
|
||||
if not isinstance(cookieInfo, dict):
|
||||
self.fail(cookieInfo)
|
||||
return
|
||||
self.assertEqual(cookieInfo['path'], self._cookiePath)
|
||||
self.assertEqual(cookieInfo['value'], self._cookieValue)
|
||||
self.assertEqual(cookieInfo['name'], self._cookieName)
|
||||
|
@ -761,6 +761,11 @@ var gEditItemOverlay = {
|
||||
this._markFolderAsRecentlyUsed(containerId)
|
||||
.catch(Components.utils.reportError);
|
||||
}
|
||||
|
||||
// Auto-show the bookmarks toolbar when adding / moving an item there.
|
||||
if (containerId == PlacesUtils.toolbarFolderId) {
|
||||
Services.obs.notifyObservers(null, "autoshow-bookmarks-toolbar", null);
|
||||
}
|
||||
}
|
||||
|
||||
// Update folder-tree selection
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(2);
|
||||
requestLongerTimeout(4);
|
||||
|
||||
/**
|
||||
* Test that when restoring an 'initial page' with session restore, it
|
||||
|
@ -136,10 +136,15 @@ button {
|
||||
margin: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
.page-not-found {
|
||||
.error-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.error-page .error-page-details {
|
||||
color: gray;
|
||||
}
|
||||
|
@ -83,10 +83,12 @@ module.exports = createClass({
|
||||
panel = selectedPanel.component({ client, id: selectedPanel.id });
|
||||
} else {
|
||||
panel = (
|
||||
dom.div({ className: "page-not-found" },
|
||||
dom.div({ className: "error-page" },
|
||||
dom.h1({ className: "header-name" },
|
||||
Strings.GetStringFromName("pageNotFound")
|
||||
)
|
||||
),
|
||||
dom.h4({ className: "error-page-details" },
|
||||
Strings.formatStringFromName("doesNotExist", [selectedPanelId], 1))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
/* global sendAsyncMessage */
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -40,29 +39,12 @@ add_task(function* () {
|
||||
let aboutDebuggingUpdate = waitForMutation(serviceWorkersElement,
|
||||
{ childList: true });
|
||||
|
||||
// Use message manager to work with e10s
|
||||
let frameScript = function () {
|
||||
// Retrieve the `sw` promise created in the html page
|
||||
let { sw } = content.wrappedJSObject;
|
||||
sw.then(function (registration) {
|
||||
registration.unregister().then(function () {
|
||||
sendAsyncMessage("sw-unregistered");
|
||||
},
|
||||
function (e) {
|
||||
dump("SW not unregistered; " + e + "\n");
|
||||
});
|
||||
});
|
||||
};
|
||||
let mm = swTab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
yield new Promise(done => {
|
||||
mm.addMessageListener("sw-unregistered", function listener() {
|
||||
mm.removeMessageListener("sw-unregistered", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
ok(true, "Service worker registration unregistered");
|
||||
try {
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
} catch (e) {
|
||||
ok(false, "SW not unregistered; " + e);
|
||||
}
|
||||
|
||||
yield aboutDebuggingUpdate;
|
||||
|
||||
|
@ -37,16 +37,16 @@ add_task(function* () {
|
||||
let swTab = yield addTab(TAB_URL);
|
||||
|
||||
info("Make the test page notify us when the service worker sends a message.");
|
||||
let frameScript = function () {
|
||||
|
||||
yield ContentTask.spawn(swTab.linkedBrowser, {}, function () {
|
||||
let win = content.wrappedJSObject;
|
||||
win.navigator.serviceWorker.addEventListener("message", function (event) {
|
||||
sendAsyncMessage(event.data);
|
||||
}, false);
|
||||
};
|
||||
let mm = swTab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
});
|
||||
|
||||
// Expect the service worker to claim the test window when activating.
|
||||
let mm = swTab.linkedBrowser.messageManager;
|
||||
let onClaimed = new Promise(done => {
|
||||
mm.addMessageListener("sw-claimed", function listener() {
|
||||
mm.removeMessageListener("sw-claimed", listener);
|
||||
@ -90,8 +90,12 @@ add_task(function* () {
|
||||
ok(true, "Service worker received a push notification");
|
||||
|
||||
// Finally, unregister the service worker itself.
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
try {
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
} catch (e) {
|
||||
ok(false, "SW not unregistered; " + e);
|
||||
}
|
||||
|
||||
yield removeTab(swTab);
|
||||
yield closeAboutDebugging(tab);
|
||||
|
@ -77,8 +77,12 @@ add_task(function* () {
|
||||
ok(!targetElement.querySelector(".start-button"), "No start button");
|
||||
|
||||
// Finally, unregister the service worker itself.
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
try {
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
} catch (e) {
|
||||
ok(false, "SW not unregistered; " + e);
|
||||
}
|
||||
|
||||
yield removeTab(swTab);
|
||||
yield closeAboutDebugging(tab);
|
||||
|
@ -78,8 +78,12 @@ add_task(function* () {
|
||||
"The debug button was removed when the worker was killed");
|
||||
|
||||
// Finally, unregister the service worker itself.
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
try {
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
} catch (e) {
|
||||
ok(false, "SW not unregistered; " + e);
|
||||
}
|
||||
|
||||
// Now ensure that the worker registration is correctly removed.
|
||||
// The list should update once the registration is destroyed.
|
||||
|
@ -7,7 +7,6 @@
|
||||
installAddon, uninstallAddon, waitForMutation, assertHasTarget,
|
||||
getServiceWorkerList, getTabList, openPanel, waitForInitialAddonList,
|
||||
waitForServiceWorkerRegistered, unregisterServiceWorker */
|
||||
/* global sendAsyncMessage */
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -273,24 +272,13 @@ function assertHasTarget(expected, document, type, name) {
|
||||
* Returns a promise that will resolve after the service worker in the page
|
||||
* has successfully registered itself.
|
||||
* @param {Tab} tab
|
||||
* @return {Promise} Resolves when the service worker is registered.
|
||||
*/
|
||||
function waitForServiceWorkerRegistered(tab) {
|
||||
// Make the test page notify us when the service worker is registered.
|
||||
let frameScript = function () {
|
||||
return ContentTask.spawn(tab.linkedBrowser, {}, function* () {
|
||||
// Retrieve the `sw` promise created in the html page.
|
||||
let { sw } = content.wrappedJSObject;
|
||||
sw.then(function (registration) {
|
||||
sendAsyncMessage("sw-registered");
|
||||
});
|
||||
};
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
return new Promise(done => {
|
||||
mm.addMessageListener("sw-registered", function listener() {
|
||||
mm.removeMessageListener("sw-registered", listener);
|
||||
done();
|
||||
});
|
||||
yield sw;
|
||||
});
|
||||
}
|
||||
|
||||
@ -298,28 +286,13 @@ function waitForServiceWorkerRegistered(tab) {
|
||||
* Asks the service worker within the test page to unregister, and returns a
|
||||
* promise that will resolve when it has successfully unregistered itself.
|
||||
* @param {Tab} tab
|
||||
* @return {Promise} Resolves when the service worker is unregistered.
|
||||
*/
|
||||
function unregisterServiceWorker(tab) {
|
||||
// Use message manager to work with e10s.
|
||||
let frameScript = function () {
|
||||
// Retrieve the `sw` promise created in the html page.
|
||||
return ContentTask.spawn(tab.linkedBrowser, {}, function* () {
|
||||
// Retrieve the `sw` promise created in the html page
|
||||
let { sw } = content.wrappedJSObject;
|
||||
sw.then(function (registration) {
|
||||
registration.unregister().then(function () {
|
||||
sendAsyncMessage("sw-unregistered");
|
||||
},
|
||||
function (e) {
|
||||
dump("SW not unregistered; " + e + "\n");
|
||||
});
|
||||
});
|
||||
};
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
return new Promise(done => {
|
||||
mm.addMessageListener("sw-unregistered", function listener() {
|
||||
mm.removeMessageListener("sw-unregistered", listener);
|
||||
done();
|
||||
});
|
||||
let registration = yield sw;
|
||||
yield registration.unregister();
|
||||
});
|
||||
}
|
||||
|
@ -96,11 +96,8 @@ registerCleanupFunction(() => {
|
||||
});
|
||||
|
||||
registerCleanupFunction(function* cleanup() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
yield gDevTools.closeToolbox(target);
|
||||
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
yield closeTabAndToolbox(gBrowser.selectedTab);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -6,16 +6,31 @@
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
|
||||
const FONTS = [
|
||||
{name: "Ostrich Sans Medium", remote: true, url: URL_ROOT + "ostrich-regular.ttf",
|
||||
format: "truetype", cssName: "bar"},
|
||||
{name: "Ostrich Sans Black", remote: true, url: URL_ROOT + "ostrich-black.ttf",
|
||||
format: "", cssName: "bar"},
|
||||
{name: "Ostrich Sans Black", remote: true, url: URL_ROOT + "ostrich-black.ttf",
|
||||
format: "", cssName: "bar"},
|
||||
{name: "Ostrich Sans Medium", remote: true, url: URL_ROOT + "ostrich-regular.ttf",
|
||||
format: "", cssName: "barnormal"},
|
||||
];
|
||||
const FONTS = [{
|
||||
name: "Ostrich Sans Medium",
|
||||
remote: true,
|
||||
url: URL_ROOT + "ostrich-regular.ttf",
|
||||
format: "truetype",
|
||||
cssName: "bar"
|
||||
}, {
|
||||
name: "Ostrich Sans Black",
|
||||
remote: true,
|
||||
url: URL_ROOT + "ostrich-black.ttf",
|
||||
format: "",
|
||||
cssName: "bar"
|
||||
}, {
|
||||
name: "Ostrich Sans Black",
|
||||
remote: true,
|
||||
url: URL_ROOT + "ostrich-black.ttf",
|
||||
format: "",
|
||||
cssName: "bar"
|
||||
}, {
|
||||
name: "Ostrich Sans Medium",
|
||||
remote: true,
|
||||
url: URL_ROOT + "ostrich-regular.ttf",
|
||||
format: "",
|
||||
cssName: "barnormal"
|
||||
}];
|
||||
|
||||
add_task(function* () {
|
||||
let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
|
||||
@ -74,8 +89,9 @@ function* testDivFonts(inspector, viewDoc) {
|
||||
|
||||
let sections1 = viewDoc.querySelectorAll("#all-fonts > section");
|
||||
is(sections1.length, 1, "Found 1 font on DIV");
|
||||
is(sections1[0].querySelector(".font-name").textContent, "Ostrich Sans Medium",
|
||||
"The DIV font has the right name");
|
||||
is(sections1[0].querySelector(".font-name").textContent,
|
||||
"Ostrich Sans Medium",
|
||||
"The DIV font has the right name");
|
||||
}
|
||||
|
||||
function* testShowAllFonts(inspector, viewDoc) {
|
||||
|
@ -2,6 +2,8 @@
|
||||
/* 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/. */
|
||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||
/* import-globals-from ../../test/head.js */
|
||||
"use strict";
|
||||
|
||||
// Import the inspector's head.js first (which itself imports shared-head.js).
|
||||
|
@ -3,16 +3,17 @@
|
||||
/* 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/. */
|
||||
/* Experimenting with 100 char long lines */
|
||||
/* eslint max-len: [2, 100, 2, {ignoreUrls: true, "ignorePattern": "\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */ // eslint-disable-line
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cc, Ci, Cu} = require("chrome");
|
||||
const {Cc, Ci} = require("chrome");
|
||||
|
||||
var Services = require("Services");
|
||||
var promise = require("promise");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
var clipboard = require("sdk/clipboard");
|
||||
var {HostType} = require("devtools/client/framework/toolbox").Toolbox;
|
||||
const {executeSoon} = require("devtools/shared/DevToolsUtils");
|
||||
var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
|
||||
var {Task} = require("devtools/shared/task");
|
||||
@ -40,7 +41,6 @@ loader.lazyGetter(this, "clipboardHelper", () => {
|
||||
return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Represents an open instance of the Inspector for a tab.
|
||||
* The inspector controls the breadcrumbs, the markup view, and the sidebar
|
||||
@ -191,7 +191,6 @@ InspectorPanel.prototype = {
|
||||
if (notification && !this._toolbox.threadClient.paused) {
|
||||
notificationBox.removeNotification(notification);
|
||||
}
|
||||
|
||||
};
|
||||
this.target.on("thread-paused", this.updateDebuggerPausedWarning);
|
||||
this.target.on("thread-resumed", this.updateDebuggerPausedWarning);
|
||||
@ -251,15 +250,16 @@ InspectorPanel.prototype = {
|
||||
|
||||
// If available, set either the previously selected node or the body
|
||||
// as default selected, else set documentElement
|
||||
return walker.getRootNode().then(aRootNode => {
|
||||
return walker.getRootNode().then(node => {
|
||||
if (hasNavigated()) {
|
||||
return promise.reject("navigated; resolution of _defaultNode aborted");
|
||||
}
|
||||
|
||||
rootNode = aRootNode;
|
||||
rootNode = node;
|
||||
if (this.selectionCssSelector) {
|
||||
return walker.querySelector(rootNode, this.selectionCssSelector);
|
||||
}
|
||||
return null;
|
||||
}).then(front => {
|
||||
if (hasNavigated()) {
|
||||
return promise.reject("navigated; resolution of _defaultNode aborted");
|
||||
@ -391,7 +391,7 @@ InspectorPanel.prototype = {
|
||||
if (this.target.form.animationsActor) {
|
||||
this.sidebar.addTab("animationinspector",
|
||||
"chrome://devtools/content/animationinspector/animation-inspector.xhtml",
|
||||
"animationinspector" == defaultTab);
|
||||
defaultTab == "animationinspector");
|
||||
}
|
||||
|
||||
this.sidebar.show(defaultTab);
|
||||
@ -467,9 +467,8 @@ InspectorPanel.prototype = {
|
||||
if (this._selectionCssSelector &&
|
||||
this._selectionCssSelector.url === this._target.url) {
|
||||
return this._selectionCssSelector.selector;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -688,13 +687,13 @@ InspectorPanel.prototype = {
|
||||
/**
|
||||
* Show the node menu.
|
||||
*/
|
||||
showNodeMenu: function (aButton, aPosition, aExtraItems) {
|
||||
if (aExtraItems) {
|
||||
for (let item of aExtraItems) {
|
||||
showNodeMenu: function (button, position, extraItems) {
|
||||
if (extraItems) {
|
||||
for (let item of extraItems) {
|
||||
this.nodemenu.appendChild(item);
|
||||
}
|
||||
}
|
||||
this.nodemenu.openPopup(aButton, aPosition, 0, 0, true, false);
|
||||
this.nodemenu.openPopup(button, position, 0, 0, true, false);
|
||||
},
|
||||
|
||||
hideNodeMenu: function () {
|
||||
@ -796,8 +795,7 @@ InspectorPanel.prototype = {
|
||||
|
||||
if (isDuplicatableElement) {
|
||||
duplicateNode.removeAttribute("disabled");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
duplicateNode.setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
@ -997,7 +995,8 @@ InspectorPanel.prototype = {
|
||||
this._markupBox.setAttribute("collapsed", true);
|
||||
this._markupBox.appendChild(this._markupFrame);
|
||||
this._markupFrame.setAttribute("src", "chrome://devtools/content/inspector/markup/markup.xhtml");
|
||||
this._markupFrame.setAttribute("aria-label", strings.GetStringFromName("inspector.panelLabel.markupView"));
|
||||
this._markupFrame.setAttribute("aria-label",
|
||||
strings.GetStringFromName("inspector.panelLabel.markupView"));
|
||||
},
|
||||
|
||||
_onMarkupFrameLoad: function () {
|
||||
@ -1096,16 +1095,17 @@ InspectorPanel.prototype = {
|
||||
/**
|
||||
* Toggle a pseudo class.
|
||||
*/
|
||||
togglePseudoClass: function (aPseudo) {
|
||||
togglePseudoClass: function (pseudo) {
|
||||
if (this.selection.isElementNode()) {
|
||||
let node = this.selection.nodeFront;
|
||||
if (node.hasPseudoClassLock(aPseudo)) {
|
||||
return this.walker.removePseudoClassLock(node, aPseudo, {parents: true});
|
||||
if (node.hasPseudoClassLock(pseudo)) {
|
||||
return this.walker.removePseudoClassLock(node, pseudo, {parents: true});
|
||||
}
|
||||
|
||||
let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
|
||||
return this.walker.addPseudoClassLock(node, aPseudo, {parents: hierarchical});
|
||||
let hierarchical = pseudo == ":hover" || pseudo == ":active";
|
||||
return this.walker.addPseudoClassLock(node, pseudo, {parents: hierarchical});
|
||||
}
|
||||
return promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1156,7 +1156,7 @@ InspectorPanel.prototype = {
|
||||
*/
|
||||
clearPseudoClasses: function () {
|
||||
if (!this.walker) {
|
||||
return;
|
||||
return promise.resolve();
|
||||
}
|
||||
return this.walker.clearPseudoClassLocks().then(null, console.error);
|
||||
},
|
||||
@ -1178,8 +1178,9 @@ InspectorPanel.prototype = {
|
||||
*/
|
||||
pasteOuterHTML: function () {
|
||||
let content = this._getClipboardContentForPaste();
|
||||
if (!content)
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
let node = this.selection.nodeFront;
|
||||
return this.markup.getNodeOuterHTML(node).then(oldContent => {
|
||||
@ -1192,8 +1193,9 @@ InspectorPanel.prototype = {
|
||||
*/
|
||||
pasteInnerHTML: function () {
|
||||
let content = this._getClipboardContentForPaste();
|
||||
if (!content)
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
let node = this.selection.nodeFront;
|
||||
return this.markup.getNodeInnerHTML(node).then(oldContent => {
|
||||
@ -1208,8 +1210,9 @@ InspectorPanel.prototype = {
|
||||
*/
|
||||
pasteAdjacentHTML: function (position) {
|
||||
let content = this._getClipboardContentForPaste();
|
||||
if (!content)
|
||||
if (!content) {
|
||||
return promise.reject("No clipboard content for paste");
|
||||
}
|
||||
|
||||
let node = this.selection.nodeFront;
|
||||
return this.markup.insertAdjacentHTMLToNode(node, position, content);
|
||||
@ -1415,16 +1418,18 @@ InspectorPanel.prototype = {
|
||||
// Open link in a new tab.
|
||||
// When the inspector menu was setup on click (see _setupNodeLinkMenu), we
|
||||
// already checked that resolveRelativeURL existed.
|
||||
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
|
||||
if (type === "uri") {
|
||||
let browserWin = this.target.tab.ownerDocument.defaultView;
|
||||
browserWin.openUILinkIn(url, "tab");
|
||||
} else if (type === "cssresource") {
|
||||
return this.toolbox.viewSourceInStyleEditor(url);
|
||||
} else if (type === "jsresource") {
|
||||
return this.toolbox.viewSourceInDebugger(url);
|
||||
}
|
||||
}).catch(e => console.error(e));
|
||||
this.inspector.resolveRelativeURL(
|
||||
link, this.selection.nodeFront).then(url => {
|
||||
if (type === "uri") {
|
||||
let browserWin = this.target.tab.ownerDocument.defaultView;
|
||||
browserWin.openUILinkIn(url, "tab");
|
||||
} else if (type === "cssresource") {
|
||||
return this.toolbox.viewSourceInStyleEditor(url);
|
||||
} else if (type === "jsresource") {
|
||||
return this.toolbox.viewSourceInDebugger(url);
|
||||
}
|
||||
return null;
|
||||
}).catch(e => console.error(e));
|
||||
} else if (type == "idref") {
|
||||
// Select the node in the same document.
|
||||
this.walker.document(this.selection.nodeFront).then(doc => {
|
||||
|
@ -4,8 +4,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cu, Ci} = require("chrome");
|
||||
|
||||
const {Ci} = require("chrome");
|
||||
const promise = require("promise");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
|
||||
@ -112,8 +111,8 @@ InspectorSearch.prototype = {
|
||||
this._onSearch(event.shiftKey);
|
||||
}
|
||||
|
||||
const modifierKey = system.constants.platform === "macosx" ? event.metaKey :
|
||||
event.ctrlKey;
|
||||
const modifierKey = system.constants.platform === "macosx"
|
||||
? event.metaKey : event.ctrlKey;
|
||||
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_G && modifierKey) {
|
||||
this._onSearch(event.shiftKey);
|
||||
event.preventDefault();
|
||||
@ -214,7 +213,8 @@ SelectorAutocompleter.prototype = {
|
||||
|
||||
this._state = null;
|
||||
let subQuery = "";
|
||||
// Now we iterate over the query and decide the state character by character.
|
||||
// Now we iterate over the query and decide the state character by
|
||||
// character.
|
||||
// The logic here is that while iterating, the state can go from one to
|
||||
// another with some restrictions. Like, if the state is Class, then it can
|
||||
// never go to Tag state without a space or '>' character; Or like, a Class
|
||||
@ -229,7 +229,8 @@ SelectorAutocompleter.prototype = {
|
||||
case null:
|
||||
// This will happen only in the first iteration of the for loop.
|
||||
lastChar = secondLastChar;
|
||||
case this.States.TAG:
|
||||
|
||||
case this.States.TAG: // eslint-disable-line
|
||||
if (lastChar == ".") {
|
||||
this._state = this.States.CLASS;
|
||||
} else if (lastChar == "#") {
|
||||
@ -243,7 +244,8 @@ SelectorAutocompleter.prototype = {
|
||||
|
||||
case this.States.CLASS:
|
||||
if (subQuery.match(/[\.]+[^\.]*$/)[0].length > 2) {
|
||||
// Checks whether the subQuery has atleast one [a-zA-Z] after the '.'.
|
||||
// Checks whether the subQuery has atleast one [a-zA-Z] after the
|
||||
// '.'.
|
||||
if (lastChar == " " || lastChar == ">") {
|
||||
this._state = this.States.TAG;
|
||||
} else if (lastChar == "#") {
|
||||
@ -258,7 +260,8 @@ SelectorAutocompleter.prototype = {
|
||||
|
||||
case this.States.ID:
|
||||
if (subQuery.match(/[#]+[^#]*$/)[0].length > 2) {
|
||||
// Checks whether the subQuery has atleast one [a-zA-Z] after the '#'.
|
||||
// Checks whether the subQuery has atleast one [a-zA-Z] after the
|
||||
// '#'.
|
||||
if (lastChar == " " || lastChar == ">") {
|
||||
this._state = this.States.TAG;
|
||||
} else if (lastChar == ".") {
|
||||
@ -295,7 +298,8 @@ SelectorAutocompleter.prototype = {
|
||||
*/
|
||||
destroy: function () {
|
||||
this.searchBox.removeEventListener("input", this.showSuggestions, true);
|
||||
this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true);
|
||||
this.searchBox.removeEventListener("keypress",
|
||||
this._onSearchKeypress, true);
|
||||
this.inspector.off("markupmutation", this._onMarkupMutation);
|
||||
this.searchPopup.destroy();
|
||||
this.searchPopup = null;
|
||||
@ -308,16 +312,19 @@ SelectorAutocompleter.prototype = {
|
||||
*/
|
||||
_onSearchKeypress: function (event) {
|
||||
let query = this.searchBox.value;
|
||||
let popup = this.searchPopup;
|
||||
|
||||
switch (event.keyCode) {
|
||||
case event.DOM_VK_RETURN:
|
||||
case event.DOM_VK_TAB:
|
||||
if (this.searchPopup.isOpen &&
|
||||
this.searchPopup.getItemAtIndex(this.searchPopup.itemCount - 1)
|
||||
if (popup.isOpen &&
|
||||
popup.getItemAtIndex(popup.itemCount - 1)
|
||||
.preLabel == query) {
|
||||
this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
popup.selectedIndex = popup.itemCount - 1;
|
||||
this.searchBox.value = popup.selectedItem.label;
|
||||
this.hidePopup();
|
||||
} else if (!this.searchPopup.isOpen && event.keyCode === event.DOM_VK_TAB) {
|
||||
} else if (!popup.isOpen &&
|
||||
event.keyCode === event.DOM_VK_TAB) {
|
||||
// When tab is pressed with focus on searchbox and closed popup,
|
||||
// do not prevent the default to avoid a keyboard trap and move focus
|
||||
// to next/previous element.
|
||||
@ -327,24 +334,23 @@ SelectorAutocompleter.prototype = {
|
||||
break;
|
||||
|
||||
case event.DOM_VK_UP:
|
||||
if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
|
||||
this.searchPopup.focus();
|
||||
if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
|
||||
this.searchPopup.selectedIndex =
|
||||
Math.max(0, this.searchPopup.itemCount - 2);
|
||||
if (popup.isOpen && popup.itemCount > 0) {
|
||||
popup.focus();
|
||||
if (popup.selectedIndex == popup.itemCount - 1) {
|
||||
popup.selectedIndex =
|
||||
Math.max(0, popup.itemCount - 2);
|
||||
} else {
|
||||
popup.selectedIndex = popup.itemCount - 1;
|
||||
}
|
||||
else {
|
||||
this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
|
||||
}
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
this.searchBox.value = popup.selectedItem.label;
|
||||
}
|
||||
break;
|
||||
|
||||
case event.DOM_VK_DOWN:
|
||||
if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
|
||||
this.searchPopup.focus();
|
||||
this.searchPopup.selectedIndex = 0;
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
if (popup.isOpen && popup.itemCount > 0) {
|
||||
popup.focus();
|
||||
popup.selectedIndex = 0;
|
||||
this.searchBox.value = popup.selectedItem.label;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -361,40 +367,41 @@ SelectorAutocompleter.prototype = {
|
||||
* Handles keypress and mouse click on the suggestions richlistbox.
|
||||
*/
|
||||
_onListBoxKeypress: function (event) {
|
||||
let popup = this.searchPopup;
|
||||
|
||||
switch (event.keyCode || event.button) {
|
||||
case event.DOM_VK_RETURN:
|
||||
case event.DOM_VK_TAB:
|
||||
case 0: // left mouse button
|
||||
case 0:
|
||||
// left mouse button
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
this.searchBox.value = popup.selectedItem.label;
|
||||
this.searchBox.focus();
|
||||
this.hidePopup();
|
||||
break;
|
||||
|
||||
case event.DOM_VK_UP:
|
||||
if (this.searchPopup.selectedIndex == 0) {
|
||||
this.searchPopup.selectedIndex = -1;
|
||||
if (popup.selectedIndex == 0) {
|
||||
popup.selectedIndex = -1;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.searchBox.focus();
|
||||
}
|
||||
else {
|
||||
let index = this.searchPopup.selectedIndex;
|
||||
this.searchBox.value = this.searchPopup.getItemAtIndex(index - 1).label;
|
||||
} else {
|
||||
let index = popup.selectedIndex;
|
||||
this.searchBox.value = popup.getItemAtIndex(index - 1).label;
|
||||
}
|
||||
break;
|
||||
|
||||
case event.DOM_VK_DOWN:
|
||||
if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
|
||||
this.searchPopup.selectedIndex = -1;
|
||||
if (popup.selectedIndex == popup.itemCount - 1) {
|
||||
popup.selectedIndex = -1;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.searchBox.focus();
|
||||
}
|
||||
else {
|
||||
let index = this.searchPopup.selectedIndex;
|
||||
this.searchBox.value = this.searchPopup.getItemAtIndex(index + 1).label;
|
||||
} else {
|
||||
let index = popup.selectedIndex;
|
||||
this.searchBox.value = popup.getItemAtIndex(index + 1).label;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -403,8 +410,8 @@ SelectorAutocompleter.prototype = {
|
||||
event.preventDefault();
|
||||
this.searchBox.focus();
|
||||
if (this.searchBox.selectionStart > 0) {
|
||||
this.searchBox.value =
|
||||
this.searchBox.value.substring(0, this.searchBox.selectionStart - 1);
|
||||
this.searchBox.value = this.searchBox.value.substring(0,
|
||||
this.searchBox.selectionStart - 1);
|
||||
}
|
||||
this.hidePopup();
|
||||
break;
|
||||
@ -424,12 +431,12 @@ SelectorAutocompleter.prototype = {
|
||||
/**
|
||||
* Populates the suggestions list and show the suggestion popup.
|
||||
*/
|
||||
_showPopup: function (list, firstPart, aState) {
|
||||
_showPopup: function (list, firstPart, popupState) {
|
||||
let total = 0;
|
||||
let query = this.searchBox.value;
|
||||
let items = [];
|
||||
|
||||
for (let [value, /* count*/, state] of list) {
|
||||
for (let [value, , state] of list) {
|
||||
if (query.match(/[\s>+]$/)) {
|
||||
// for cases like 'div ' or 'div >' or 'div+'
|
||||
value = query + value;
|
||||
@ -454,10 +461,10 @@ SelectorAutocompleter.prototype = {
|
||||
|
||||
// In case the query's state is tag and the item's state is id or class
|
||||
// adjust the preLabel
|
||||
if (aState === this.States.TAG && state === this.States.CLASS) {
|
||||
if (popupState === this.States.TAG && state === this.States.CLASS) {
|
||||
item.preLabel = "." + item.preLabel;
|
||||
}
|
||||
if (aState === this.States.TAG && state === this.States.ID) {
|
||||
if (popupState === this.States.TAG && state === this.States.ID) {
|
||||
item.preLabel = "#" + item.preLabel;
|
||||
}
|
||||
|
||||
@ -469,13 +476,11 @@ SelectorAutocompleter.prototype = {
|
||||
if (total > 0) {
|
||||
this.searchPopup.setItems(items);
|
||||
this.searchPopup.openPopup(this.searchBox);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.hidePopup();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Hide the suggestion popup if necessary.
|
||||
*/
|
||||
@ -503,17 +508,15 @@ SelectorAutocompleter.prototype = {
|
||||
}
|
||||
|
||||
if (state === this.States.TAG) {
|
||||
// gets the tag that is being completed. For ex. 'div.foo > s' returns 's',
|
||||
// 'di' returns 'di' and likewise.
|
||||
// gets the tag that is being completed. For ex. 'div.foo > s' returns
|
||||
// 's', 'di' returns 'di' and likewise.
|
||||
firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1];
|
||||
query = query.slice(0, query.length - firstPart.length);
|
||||
}
|
||||
else if (state === this.States.CLASS) {
|
||||
} else if (state === this.States.CLASS) {
|
||||
// gets the class that is being completed. For ex. '.foo.b' returns 'b'
|
||||
firstPart = query.match(/\.([^\.]*)$/)[1];
|
||||
query = query.slice(0, query.length - firstPart.length - 1);
|
||||
}
|
||||
else if (state === this.States.ID) {
|
||||
} else if (state === this.States.ID) {
|
||||
// gets the id that is being completed. For ex. '.foo#b' returns 'b'
|
||||
firstPart = query.match(/#([^#]*)$/)[1];
|
||||
query = query.slice(0, query.length - firstPart.length - 1);
|
||||
@ -524,7 +527,9 @@ SelectorAutocompleter.prototype = {
|
||||
query += "*";
|
||||
}
|
||||
|
||||
this._lastQuery = this.walker.getSuggestionsForQuery(query, firstPart, state).then(result => {
|
||||
let suggestionsPromise = this.walker.getSuggestionsForQuery(
|
||||
query, firstPart, state);
|
||||
this._lastQuery = suggestionsPromise.then(result => {
|
||||
this.emit("processing-done");
|
||||
if (result.query !== query) {
|
||||
// This means that this response is for a previous request and the user
|
||||
@ -545,10 +550,9 @@ SelectorAutocompleter.prototype = {
|
||||
result.suggestions = [];
|
||||
}
|
||||
|
||||
|
||||
this._showPopup(result.suggestions, firstPart, state);
|
||||
});
|
||||
|
||||
return this._lastQuery;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -4,32 +4,37 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that hovering over nodes on the breadcrumb buttons in the inspector shows the highlighter over
|
||||
// those nodes
|
||||
// Test that hovering over nodes on the breadcrumb buttons in the inspector
|
||||
// shows the highlighter over those nodes
|
||||
add_task(function* () {
|
||||
info("Loading the test document and opening the inspector");
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL("data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(
|
||||
"data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
|
||||
info("Selecting the test node");
|
||||
yield selectNode("span", inspector);
|
||||
let bcButtons = inspector.breadcrumbs["container"];
|
||||
let bcButtons = inspector.breadcrumbs.container;
|
||||
|
||||
let onNodeHighlighted = toolbox.once("node-highlight");
|
||||
let button = bcButtons.childNodes[1];
|
||||
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"}, button.ownerDocument.defaultView);
|
||||
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"},
|
||||
button.ownerDocument.defaultView);
|
||||
yield onNodeHighlighted;
|
||||
|
||||
let isVisible = yield testActor.isHighlighting();
|
||||
ok(isVisible, "The highlighter is shown on a markup container hover");
|
||||
|
||||
ok((yield testActor.assertHighlightedNode("body")), "The highlighter highlights the right node");
|
||||
ok((yield testActor.assertHighlightedNode("body")),
|
||||
"The highlighter highlights the right node");
|
||||
|
||||
onNodeHighlighted = toolbox.once("node-highlight");
|
||||
button = bcButtons.childNodes[2];
|
||||
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"}, button.ownerDocument.defaultView);
|
||||
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"},
|
||||
button.ownerDocument.defaultView);
|
||||
yield onNodeHighlighted;
|
||||
|
||||
isVisible = yield testActor.isHighlighting();
|
||||
ok(isVisible, "The highlighter is shown on a markup container hover");
|
||||
|
||||
ok((yield testActor.assertHighlightedNode("span")), "The highlighter highlights the right node");
|
||||
ok((yield testActor.assertHighlightedNode("span")),
|
||||
"The highlighter highlights the right node");
|
||||
});
|
||||
|
@ -33,7 +33,8 @@ add_task(function* () {
|
||||
{type: "contextmenu", button: 2}, inspector.panelWin);
|
||||
|
||||
info("Waiting for the context menu to open.");
|
||||
yield once(inspector.panelDoc.getElementById("inspectorPopupSet"), "popupshown");
|
||||
yield once(inspector.panelDoc.getElementById("inspectorPopupSet"),
|
||||
"popupshown");
|
||||
|
||||
info("Clicking 'Delete Node' in the context menu.");
|
||||
inspector.panelDoc.getElementById("node-menu-delete").click();
|
||||
@ -42,7 +43,8 @@ add_task(function* () {
|
||||
yield inspector.once("inspector-updated");
|
||||
|
||||
info("Inspector updated, performing checks.");
|
||||
yield assertNodeSelectedAndPanelsUpdated("#selectedAfterDelete", "li#selectedAfterDelete");
|
||||
yield assertNodeSelectedAndPanelsUpdated("#selectedAfterDelete",
|
||||
"li#selectedAfterDelete");
|
||||
}
|
||||
|
||||
function* testAutomaticallyDeleteSelectedNode() {
|
||||
@ -59,7 +61,8 @@ add_task(function* () {
|
||||
yield inspector.once("inspector-updated");
|
||||
|
||||
info("Inspector updated, performing checks.");
|
||||
yield assertNodeSelectedAndPanelsUpdated("#deleteChildren", "ul#deleteChildren");
|
||||
yield assertNodeSelectedAndPanelsUpdated("#deleteChildren",
|
||||
"ul#deleteChildren");
|
||||
}
|
||||
|
||||
function* testDeleteSelectedNodeContainerFrame() {
|
||||
@ -86,8 +89,10 @@ add_task(function* () {
|
||||
let nodeFront = yield getNodeFront(selector, inspector);
|
||||
is(inspector.selection.nodeFront, nodeFront, "The right node is selected");
|
||||
|
||||
let breadcrumbs = inspector.panelDoc.getElementById("inspector-breadcrumbs");
|
||||
is(breadcrumbs.querySelector("button[checked=true]").textContent, crumbLabel,
|
||||
"The right breadcrumb is selected");
|
||||
let breadcrumbs = inspector.panelDoc.getElementById(
|
||||
"inspector-breadcrumbs");
|
||||
is(breadcrumbs.querySelector("button[checked=true]").textContent,
|
||||
crumbLabel,
|
||||
"The right breadcrumb is selected");
|
||||
}
|
||||
});
|
||||
|
@ -22,5 +22,5 @@ add_task(function* () {
|
||||
info("Switch to the inspector panel and immediately end the test");
|
||||
let onInspectorSelected = toolbox.once("inspector-selected");
|
||||
toolbox.selectTool("inspector");
|
||||
let inspector = yield onInspectorSelected;
|
||||
yield onInspectorSelected;
|
||||
});
|
||||
|
@ -6,17 +6,19 @@
|
||||
|
||||
// Tests that context menu items exapnd all and collapse are shown properly.
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8,<div id='parent-node'><div id='child-node'></div></div>";
|
||||
const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
"<div id='parent-node'><div id='child-node'></div></div>";
|
||||
|
||||
add_task(function* () {
|
||||
|
||||
// Test is often exceeding time-out threshold, similar to Bug 1137765
|
||||
// Test is often exceeding time-out threshold, similar to Bug 1137765
|
||||
requestLongerTimeout(2);
|
||||
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
let nodeMenuCollapseElement = inspector.panelDoc.getElementById("node-menu-collapse");
|
||||
let nodeMenuExpandElement = inspector.panelDoc.getElementById("node-menu-expand");
|
||||
let nodeMenuCollapseElement = inspector.panelDoc.getElementById(
|
||||
"node-menu-collapse");
|
||||
let nodeMenuExpandElement = inspector.panelDoc.getElementById(
|
||||
"node-menu-expand");
|
||||
|
||||
info("Selecting the parent node");
|
||||
|
||||
@ -27,9 +29,10 @@ add_task(function* () {
|
||||
info("Simulating context menu click on the selected node container.");
|
||||
contextMenuClick(getContainerForNodeFront(front, inspector).tagLine);
|
||||
|
||||
ok(nodeMenuCollapseElement.hasAttribute("disabled"), "Collapse option is disabled");
|
||||
|
||||
ok(!nodeMenuExpandElement.hasAttribute("disabled"), "ExpandAll option is enabled");
|
||||
ok(nodeMenuCollapseElement.hasAttribute("disabled"),
|
||||
"Collapse option is disabled");
|
||||
ok(!nodeMenuExpandElement.hasAttribute("disabled"),
|
||||
"ExpandAll option is enabled");
|
||||
|
||||
info("Testing whether expansion works properly");
|
||||
dispatchCommandEvent(nodeMenuExpandElement);
|
||||
@ -45,7 +48,8 @@ add_task(function* () {
|
||||
info("Simulating context menu click on the selected node container.");
|
||||
contextMenuClick(getContainerForNodeFront(front, inspector).tagLine);
|
||||
|
||||
ok(!nodeMenuCollapseElement.hasAttribute("disabled"), "Collapse option is enabled");
|
||||
ok(!nodeMenuCollapseElement.hasAttribute("disabled"),
|
||||
"Collapse option is enabled");
|
||||
|
||||
dispatchCommandEvent(nodeMenuCollapseElement);
|
||||
info("Waiting for collapse to occur");
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint key-spacing: 0 */
|
||||
"use strict";
|
||||
|
||||
// Testing that the gcli 'inspect' command works as it should.
|
||||
@ -129,6 +130,6 @@ add_task(function* () {
|
||||
status: "VALID"
|
||||
},
|
||||
},
|
||||
]); // helpers.audit
|
||||
}); // helpers.addTabWithToolbar
|
||||
}); // add_task
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -8,7 +8,8 @@
|
||||
// those nodes
|
||||
add_task(function* () {
|
||||
info("Loading the test document and opening the inspector");
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL("data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(
|
||||
"data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
|
||||
|
||||
let isVisible = yield testActor.isHighlighting(toolbox);
|
||||
ok(!isVisible, "The highlighter is hidden by default");
|
||||
@ -25,5 +26,6 @@ add_task(function* () {
|
||||
isVisible = yield testActor.isHighlighting();
|
||||
ok(isVisible, "The highlighter is shown on a markup container hover");
|
||||
|
||||
ok((yield testActor.assertHighlightedNode("h1")), "The highlighter highlights the right node");
|
||||
ok((yield testActor.assertHighlightedNode("h1")),
|
||||
"The highlighter highlights the right node");
|
||||
});
|
||||
|
@ -11,7 +11,7 @@
|
||||
const TEST_URI = URL_ROOT + "doc_inspector_highlighter.html";
|
||||
|
||||
add_task(function* () {
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_URI);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URI);
|
||||
|
||||
info("Selecting the simple, non-transformed DIV");
|
||||
yield selectAndHighlightNode("#simple-div", inspector);
|
||||
@ -34,5 +34,6 @@ add_task(function* () {
|
||||
|
||||
isVisible = yield testActor.isHighlighting();
|
||||
ok(isVisible, "The highlighter is shown");
|
||||
yield testActor.isNodeCorrectlyHighlighted("#widthHeightZero-div", is, "zero width height");
|
||||
yield testActor.isNodeCorrectlyHighlighted("#widthHeightZero-div", is,
|
||||
"zero width height");
|
||||
});
|
||||
|
@ -55,7 +55,8 @@ add_task(function* () {
|
||||
info("Moving mouse over iframe body");
|
||||
yield moveMouseOver("iframe", 40, 40);
|
||||
|
||||
ok((yield testActor.assertHighlightedNode(iframeBodySelector)), "highlighter shows the right node");
|
||||
ok((yield testActor.assertHighlightedNode(iframeBodySelector)),
|
||||
"highlighter shows the right node");
|
||||
yield testActor.isNodeCorrectlyHighlighted(iframeBodySelector, is);
|
||||
|
||||
info("Waiting for the element picker to deactivate.");
|
||||
|
@ -10,7 +10,7 @@
|
||||
const TEST_URL = "data:text/html;charset=utf-8,custom highlighters";
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, toolbox} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
yield onlyOneInstanceOfMainHighlighter(inspector);
|
||||
yield manyInstancesOfCustomHighlighters(inspector);
|
||||
|
@ -19,37 +19,37 @@ const TEST_PAGE = URL_ROOT +
|
||||
"doc_inspector_highlighter-comments.html";
|
||||
|
||||
add_task(function* () {
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_PAGE);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_PAGE);
|
||||
let markupView = inspector.markup;
|
||||
yield selectNode("p", inspector);
|
||||
|
||||
info("Hovering over #id1 and waiting for highlighter to appear.");
|
||||
yield hoverElement("#id1");
|
||||
yield assertHighlighterShownOn(testActor, "#id1");
|
||||
yield assertHighlighterShownOn("#id1");
|
||||
|
||||
info("Hovering over comment node and ensuring highlighter doesn't appear.");
|
||||
yield hoverComment();
|
||||
yield assertHighlighterHidden(testActor);
|
||||
yield assertHighlighterHidden();
|
||||
|
||||
info("Hovering over #id1 again and waiting for highlighter to appear.");
|
||||
yield hoverElement("#id1");
|
||||
yield assertHighlighterShownOn(testActor, "#id1");
|
||||
yield assertHighlighterShownOn("#id1");
|
||||
|
||||
info("Hovering over #id2 and waiting for highlighter to appear.");
|
||||
yield hoverElement("#id2");
|
||||
yield assertHighlighterShownOn(testActor, "#id2");
|
||||
yield assertHighlighterShownOn("#id2");
|
||||
|
||||
info("Hovering over <script> and ensuring highlighter doesn't appear.");
|
||||
yield hoverElement("script");
|
||||
yield assertHighlighterHidden(testActor);
|
||||
yield assertHighlighterHidden();
|
||||
|
||||
info("Hovering over #id3 and waiting for highlighter to appear.");
|
||||
yield hoverElement("#id3");
|
||||
yield assertHighlighterShownOn(testActor, "#id3");
|
||||
yield assertHighlighterShownOn("#id3");
|
||||
|
||||
info("Hovering over hidden #id4 and ensuring highlighter doesn't appear.");
|
||||
yield hoverElement("#id4");
|
||||
yield assertHighlighterHidden(testActor);
|
||||
yield assertHighlighterHidden();
|
||||
|
||||
function hoverContainer(container) {
|
||||
let promise = inspector.toolbox.once("node-highlight");
|
||||
@ -72,13 +72,15 @@ add_task(function* () {
|
||||
return hoverContainer(container);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function* assertHighlighterShownOn(testActor, selector) {
|
||||
ok((yield testActor.assertHighlightedNode(selector)), "Highlighter is shown on the right node: " + selector);
|
||||
function* assertHighlighterShownOn(selector) {
|
||||
ok((yield testActor.assertHighlightedNode(selector)),
|
||||
"Highlighter is shown on the right node: " + selector);
|
||||
}
|
||||
|
||||
function* assertHighlighterHidden(testActor) {
|
||||
function* assertHighlighterHidden() {
|
||||
let isVisible = yield testActor.isHighlighting();
|
||||
ok(!isVisible, "Highlighter is hidden");
|
||||
}
|
||||
|
@ -7,13 +7,21 @@
|
||||
// Test the creation of the SVG highlighter elements of the css transform
|
||||
// highlighter.
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
"<div id='transformed' style='border:1px solid red;width:100px;height:100px;transform:skew(13deg);'></div>" +
|
||||
"<div id='untransformed' style='border:1px solid blue;width:100px;height:100px;'></div>" +
|
||||
"<span id='inline' style='transform:rotate(90deg);'>this is an inline transformed element</span>";
|
||||
const TEST_URL = `
|
||||
<div id="transformed"
|
||||
style="border:1px solid red;width:100px;height:100px;transform:skew(13deg);">
|
||||
</div>
|
||||
<div id="untransformed"
|
||||
style="border:1px solid blue;width:100px;height:100px;">
|
||||
</div>
|
||||
<span id="inline"
|
||||
style="transform:rotate(90deg);">this is an inline transformed element
|
||||
</span>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector, testActor} = yield openInspectorForURL(
|
||||
"data:text/html;charset=utf-8," + encodeURI(TEST_URL));
|
||||
let front = inspector.inspector;
|
||||
|
||||
let highlighter = yield front.getHighlighterByType("CssTransformHighlighter");
|
||||
@ -31,21 +39,25 @@ add_task(function* () {
|
||||
function* isHiddenByDefault(testActor, highlighterFront) {
|
||||
info("Checking that the highlighter is hidden by default");
|
||||
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("css-transform-elements", "hidden", highlighterFront);
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-elements", "hidden", highlighterFront);
|
||||
ok(hidden, "The highlighter is hidden by default");
|
||||
}
|
||||
|
||||
function* has2PolygonsAnd4Lines(testActor, highlighterFront) {
|
||||
info("Checking that the highlighter is made up of 4 lines and 2 polygons");
|
||||
|
||||
let value = yield testActor.getHighlighterNodeAttribute("css-transform-untransformed", "class", highlighterFront);
|
||||
let value = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-untransformed", "class", highlighterFront);
|
||||
is(value, "css-transform-untransformed", "The untransformed polygon exists");
|
||||
|
||||
value = yield testActor.getHighlighterNodeAttribute("css-transform-transformed", "class", highlighterFront);
|
||||
value = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-transformed", "class", highlighterFront);
|
||||
is(value, "css-transform-transformed", "The transformed polygon exists");
|
||||
|
||||
for (let nb of ["1", "2", "3", "4"]) {
|
||||
value = yield testActor.getHighlighterNodeAttribute("css-transform-line" + nb, "class", highlighterFront);
|
||||
value = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-line" + nb, "class", highlighterFront);
|
||||
is(value, "css-transform-line", "The line " + nb + " exists");
|
||||
}
|
||||
}
|
||||
@ -56,7 +68,8 @@ function* isNotShownForUntransformed(testActor, inspector, highlighterFront) {
|
||||
let node = yield getNodeFront("#untransformed", inspector);
|
||||
yield highlighterFront.show(node);
|
||||
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("css-transform-elements", "hidden", highlighterFront);
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-elements", "hidden", highlighterFront);
|
||||
ok(hidden, "The highlighter is still hidden");
|
||||
}
|
||||
|
||||
@ -66,7 +79,8 @@ function* isNotShownForInline(testActor, inspector, highlighterFront) {
|
||||
let node = yield getNodeFront("#inline", inspector);
|
||||
yield highlighterFront.show(node);
|
||||
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("css-transform-elements", "hidden", highlighterFront);
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-elements", "hidden", highlighterFront);
|
||||
ok(hidden, "The highlighter is still hidden");
|
||||
}
|
||||
|
||||
@ -76,13 +90,15 @@ function* isVisibleWhenShown(testActor, inspector, highlighterFront) {
|
||||
let node = yield getNodeFront("#transformed", inspector);
|
||||
yield highlighterFront.show(node);
|
||||
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("css-transform-elements", "hidden", highlighterFront);
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-elements", "hidden", highlighterFront);
|
||||
ok(!hidden, "The highlighter is visible");
|
||||
|
||||
info("Hiding the highlighter");
|
||||
yield highlighterFront.hide();
|
||||
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("css-transform-elements", "hidden", highlighterFront);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-elements", "hidden", highlighterFront);
|
||||
ok(hidden, "The highlighter is hidden");
|
||||
}
|
||||
|
||||
@ -96,17 +112,23 @@ function* linesLinkThePolygons(testActor, inspector, highlighterFront) {
|
||||
|
||||
let lines = [];
|
||||
for (let nb of ["1", "2", "3", "4"]) {
|
||||
let x1 = yield testActor.getHighlighterNodeAttribute("css-transform-line" + nb, "x1", highlighterFront);
|
||||
let y1 = yield testActor.getHighlighterNodeAttribute("css-transform-line" + nb, "y1", highlighterFront);
|
||||
let x2 = yield testActor.getHighlighterNodeAttribute("css-transform-line" + nb, "x2", highlighterFront);
|
||||
let y2 = yield testActor.getHighlighterNodeAttribute("css-transform-line" + nb, "y2", highlighterFront);
|
||||
let x1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-line" + nb, "x1", highlighterFront);
|
||||
let y1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-line" + nb, "y1", highlighterFront);
|
||||
let x2 = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-line" + nb, "x2", highlighterFront);
|
||||
let y2 = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-line" + nb, "y2", highlighterFront);
|
||||
lines.push({x1, y1, x2, y2});
|
||||
}
|
||||
|
||||
let points1 = yield testActor.getHighlighterNodeAttribute("css-transform-untransformed", "points", highlighterFront);
|
||||
let points1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-untransformed", "points", highlighterFront);
|
||||
points1 = points1.split(" ");
|
||||
|
||||
let points2 = yield testActor.getHighlighterNodeAttribute("css-transform-transformed", "points", highlighterFront);
|
||||
let points2 = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-transformed", "points", highlighterFront);
|
||||
points2 = points2.split(" ");
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
@ -114,12 +136,16 @@ function* linesLinkThePolygons(testActor, inspector, highlighterFront) {
|
||||
let line = lines[i];
|
||||
|
||||
let p1 = points1[i].split(",");
|
||||
is(p1[0], line.x1, "line " + i + "'s first point matches the untransformed x coordinate");
|
||||
is(p1[1], line.y1, "line " + i + "'s first point matches the untransformed y coordinate");
|
||||
is(p1[0], line.x1,
|
||||
"line " + i + "'s first point matches the untransformed x coordinate");
|
||||
is(p1[1], line.y1,
|
||||
"line " + i + "'s first point matches the untransformed y coordinate");
|
||||
|
||||
let p2 = points2[i].split(",");
|
||||
is(p2[0], line.x2, "line " + i + "'s first point matches the transformed x coordinate");
|
||||
is(p2[1], line.y2, "line " + i + "'s first point matches the transformed y coordinate");
|
||||
is(p2[0], line.x2,
|
||||
"line " + i + "'s first point matches the transformed x coordinate");
|
||||
is(p2[1], line.y2,
|
||||
"line " + i + "'s first point matches the transformed y coordinate");
|
||||
}
|
||||
|
||||
yield highlighterFront.hide();
|
||||
|
@ -21,7 +21,7 @@ transform highlighter applies those values correctly to the SVG elements
|
||||
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_csstransform.html";
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let front = inspector.inspector;
|
||||
|
||||
let highlighter = yield front.getHighlighterByType("CssTransformHighlighter");
|
||||
@ -34,7 +34,8 @@ add_task(function* () {
|
||||
let data = yield testActor.getAllAdjustedQuads("#test-node");
|
||||
let [expected] = data.border;
|
||||
|
||||
let points = yield testActor.getHighlighterNodeAttribute("css-transform-transformed", "points", highlighter);
|
||||
let points = yield testActor.getHighlighterNodeAttribute(
|
||||
"css-transform-transformed", "points", highlighter);
|
||||
let polygonPoints = points.split(" ").map(p => {
|
||||
return {
|
||||
x: +p.substring(0, p.indexOf(",")),
|
||||
|
@ -8,10 +8,11 @@
|
||||
// by clicking on it leaves the highlighter visible for as long as the mouse is
|
||||
// over the node
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8,<p>It's going to be legen....</p>";
|
||||
const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
"<p>It's going to be legen....</p>";
|
||||
|
||||
add_task(function* () {
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("hovering over the <p> line in the markup-view");
|
||||
yield hoverContainer("p", inspector);
|
||||
@ -22,7 +23,8 @@ add_task(function* () {
|
||||
yield clickContainer("p", inspector);
|
||||
|
||||
yield testActor.setProperty("p", "textContent", "wait for it ....");
|
||||
info("wait and see if the highlighter stays visible even after the node was selected");
|
||||
info("wait and see if the highlighter stays visible even after the node " +
|
||||
"was selected");
|
||||
yield waitForTheBrieflyShowBoxModelTimeout();
|
||||
|
||||
yield testActor.setProperty("p", "textContent", "dary!!!!");
|
||||
|
@ -13,7 +13,8 @@ const TEST_URL = "data:text/html;charset=utf-8,<p>Select me!</p>";
|
||||
add_task(function* () {
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("hover over the <p> line in the markup-view so that it's the currently hovered node");
|
||||
info("hover over the <p> line in the markup-view so that it's the " +
|
||||
"currently hovered node");
|
||||
yield hoverContainer("p", inspector);
|
||||
|
||||
info("select the <p> markup-container line by clicking");
|
||||
@ -22,7 +23,8 @@ add_task(function* () {
|
||||
ok(isVisible, "the highlighter is shown");
|
||||
|
||||
info("listen to the highlighter's hidden event");
|
||||
let onHidden = testActor.waitForHighlighterEvent("hidden", toolbox.highlighter);
|
||||
let onHidden = testActor.waitForHighlighterEvent("hidden",
|
||||
toolbox.highlighter);
|
||||
info("mouse-leave the markup-view");
|
||||
yield mouseLeaveMarkupView(inspector);
|
||||
yield onHidden;
|
||||
|
@ -21,36 +21,39 @@ const TEST_URI = "data:text/html;charset=utf-8," +
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URI);
|
||||
let outerFrame = "iframe";
|
||||
let outerFrameDiv = ["iframe", "div"];
|
||||
let innerFrame = ["iframe", "iframe"];
|
||||
let innerFrameDiv = ["iframe", "iframe", "div"];
|
||||
|
||||
info("Waiting for element picker to activate.");
|
||||
yield startPicker(inspector.toolbox);
|
||||
|
||||
info("Moving mouse over outerFrameDiv");
|
||||
yield moveMouseOver(testActor, outerFrameDiv);
|
||||
ok((yield testActor.assertHighlightedNode(outerFrameDiv)), "outerFrameDiv is highlighted.");
|
||||
yield moveMouseOver(outerFrameDiv);
|
||||
ok((yield testActor.assertHighlightedNode(outerFrameDiv)),
|
||||
"outerFrameDiv is highlighted.");
|
||||
|
||||
info("Moving mouse over innerFrameDiv");
|
||||
yield moveMouseOver(testActor, innerFrameDiv);
|
||||
ok((yield testActor.assertHighlightedNode(innerFrameDiv)), "innerFrameDiv is highlighted.");
|
||||
yield moveMouseOver(innerFrameDiv);
|
||||
ok((yield testActor.assertHighlightedNode(innerFrameDiv)),
|
||||
"innerFrameDiv is highlighted.");
|
||||
|
||||
info("Selecting root node");
|
||||
yield selectNode(inspector.walker.rootNode, inspector);
|
||||
|
||||
info("Selecting an element from the nested iframe directly");
|
||||
let innerFrameFront = yield getNodeFrontInFrame("iframe", "iframe", inspector);
|
||||
let innerFrameDivFront = yield getNodeFrontInFrame("div", innerFrameFront, inspector);
|
||||
let innerFrameFront = yield getNodeFrontInFrame("iframe", "iframe",
|
||||
inspector);
|
||||
let innerFrameDivFront = yield getNodeFrontInFrame("div", innerFrameFront,
|
||||
inspector);
|
||||
yield selectNode(innerFrameDivFront, inspector);
|
||||
|
||||
is(inspector.breadcrumbs.nodeHierarchy.length, 9, "Breadcrumbs have 9 items.");
|
||||
is(inspector.breadcrumbs.nodeHierarchy.length, 9,
|
||||
"Breadcrumbs have 9 items.");
|
||||
|
||||
info("Waiting for element picker to deactivate.");
|
||||
yield inspector.toolbox.highlighterUtils.stopPicker();
|
||||
|
||||
function moveMouseOver(testActor, selector) {
|
||||
function moveMouseOver(selector) {
|
||||
info("Waiting for element " + selector + " to be highlighted");
|
||||
testActor.synthesizeMouse({
|
||||
selector: selector,
|
||||
|
@ -23,7 +23,7 @@ const TEST_DATA = [
|
||||
|
||||
add_task(function* () {
|
||||
info("Loading the test document and opening the inspector");
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
for (let selector of TEST_DATA) {
|
||||
info("Selecting and highlighting node " + selector);
|
||||
@ -32,14 +32,16 @@ add_task(function* () {
|
||||
info("Get all quads for this node");
|
||||
let data = yield testActor.getAllAdjustedQuads(selector);
|
||||
|
||||
info("Iterate over the box-model regions and verify that the highlighter is correct");
|
||||
info("Iterate over the box-model regions and verify that the highlighter " +
|
||||
"is correct");
|
||||
for (let region of ["margin", "border", "padding", "content"]) {
|
||||
let {points} = yield testActor.getHighlighterRegionPath(region);
|
||||
is(points.length, data[region].length,
|
||||
"The highlighter's " + region + " path defines the correct number of boxes");
|
||||
is(points.length, data[region].length, "The highlighter's " + region +
|
||||
" path defines the correct number of boxes");
|
||||
}
|
||||
|
||||
info("Verify that the guides define a rectangle that contains all content boxes");
|
||||
info("Verify that the guides define a rectangle that contains all " +
|
||||
"content boxes");
|
||||
|
||||
let expectedContentRect = {
|
||||
p1: {x: Infinity, y: Infinity},
|
||||
|
@ -16,25 +16,30 @@ add_task(function* () {
|
||||
info("Selecting the simple-div1 DIV");
|
||||
yield moveMouseOver("#simple-div1");
|
||||
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div1")), "The highlighter shows #simple-div1. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div1")),
|
||||
"The highlighter shows #simple-div1. OK.");
|
||||
|
||||
// First Child selection
|
||||
info("Testing first-child selection.");
|
||||
|
||||
yield doKeyHover({key: "VK_RIGHT", options: {}});
|
||||
ok((yield testActor.assertHighlightedNode("#useless-para")), "The highlighter shows #useless-para. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#useless-para")),
|
||||
"The highlighter shows #useless-para. OK.");
|
||||
|
||||
info("Selecting the useful-para paragraph DIV");
|
||||
yield moveMouseOver("#useful-para");
|
||||
ok((yield testActor.assertHighlightedNode("#useful-para")), "The highlighter shows #useful-para. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#useful-para")),
|
||||
"The highlighter shows #useful-para. OK.");
|
||||
|
||||
yield doKeyHover({key: "VK_RIGHT", options: {}});
|
||||
ok((yield testActor.assertHighlightedNode("#bold")), "The highlighter shows #bold. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#bold")),
|
||||
"The highlighter shows #bold. OK.");
|
||||
|
||||
info("Going back up to the simple-div1 DIV");
|
||||
yield doKeyHover({key: "VK_LEFT", options: {}});
|
||||
yield doKeyHover({key: "VK_LEFT", options: {}});
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div1")), "The highlighter shows #simple-div1. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div1")),
|
||||
"The highlighter shows #simple-div1. OK.");
|
||||
|
||||
info("First child selection test Passed.");
|
||||
|
||||
@ -56,5 +61,4 @@ add_task(function* () {
|
||||
});
|
||||
return inspector.toolbox.once("picker-node-hovered");
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -20,18 +20,22 @@ add_task(function* () {
|
||||
yield moveMouseOver("#ahoy");
|
||||
|
||||
yield doKeyHover({key: "VK_LEFT", options: {}});
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div2")), "The highlighter shows #simple-div2. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div2")),
|
||||
"The highlighter shows #simple-div2. OK.");
|
||||
|
||||
yield doKeyHover({key: "VK_RIGHT", options: {}});
|
||||
ok((yield testActor.assertHighlightedNode("#ahoy")), "The highlighter shows #ahoy. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#ahoy")),
|
||||
"The highlighter shows #ahoy. OK.");
|
||||
|
||||
info("Going back up to the complex-div DIV");
|
||||
yield doKeyHover({key: "VK_LEFT", options: {}});
|
||||
yield doKeyHover({key: "VK_LEFT", options: {}});
|
||||
ok((yield testActor.assertHighlightedNode("#complex-div")), "The highlighter shows #complex-div. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#complex-div")),
|
||||
"The highlighter shows #complex-div. OK.");
|
||||
|
||||
yield doKeyHover({key: "VK_RIGHT", options: {}});
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div2")), "The highlighter shows #simple-div2. OK.");
|
||||
ok((yield testActor.assertHighlightedNode("#simple-div2")),
|
||||
"The highlighter shows #simple-div2. OK.");
|
||||
|
||||
info("Previously chosen child is remembered. Passed.");
|
||||
|
||||
@ -57,5 +61,4 @@ add_task(function* () {
|
||||
});
|
||||
return promise.all([onHighlighterReady, onPickerNodeHovered]);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -19,7 +19,8 @@ add_task(function* () {
|
||||
// Testing pick-node shortcut
|
||||
info("Testing enter/return key as pick-node command");
|
||||
yield doKeyPick({key: "VK_RETURN", options: {}});
|
||||
is(inspector.selection.nodeFront.id, "another", "The #another node was selected. Passed.");
|
||||
is(inspector.selection.nodeFront.id, "another",
|
||||
"The #another node was selected. Passed.");
|
||||
|
||||
// Testing cancel-picker command
|
||||
yield startPicker(toolbox);
|
||||
@ -29,7 +30,8 @@ add_task(function* () {
|
||||
|
||||
info("Testing escape key as cancel-picker command");
|
||||
yield doKeyStop({key: "VK_ESCAPE", options: {}});
|
||||
is(inspector.selection.nodeFront.id, "another", "The #another DIV is still selected. Passed.");
|
||||
is(inspector.selection.nodeFront.id, "another",
|
||||
"The #another DIV is still selected. Passed.");
|
||||
|
||||
function doKeyPick(args) {
|
||||
info("Key pressed. Waiting for element to be picked");
|
||||
@ -57,5 +59,4 @@ add_task(function* () {
|
||||
});
|
||||
return promise.all([onHighlighterReady, onPickerNodeHovered]);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -6,10 +6,12 @@
|
||||
|
||||
// Check that the box-model highlighter supports configuration options
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
"<body style='padding:2em;'>" +
|
||||
"<div style='width:100px;height:100px;padding:2em;border:.5em solid black;margin:1em;'>test</div>" +
|
||||
"</body>";
|
||||
const TEST_URL = `
|
||||
<body style="padding:2em;">
|
||||
<div style="width:100px;height:100px;padding:2em;
|
||||
border:.5em solid black;margin:1em;">test</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
// Test data format:
|
||||
// - desc: a string that will be output to the console.
|
||||
@ -21,14 +23,17 @@ const TEST_DATA = [
|
||||
desc: "Guides and infobar should be shown by default",
|
||||
options: {},
|
||||
checkHighlighter: function* (testActor) {
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("box-model-nodeinfobar-container", "hidden");
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-nodeinfobar-container", "hidden");
|
||||
ok(!hidden, "Node infobar is visible");
|
||||
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("box-model-elements", "hidden");
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-elements", "hidden");
|
||||
ok(!hidden, "SVG container is visible");
|
||||
|
||||
for (let side of ["top", "right", "bottom", "left"]) {
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("box-model-guide-" + side, "hidden");
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-" + side, "hidden");
|
||||
ok(!hidden, side + " guide is visible");
|
||||
}
|
||||
}
|
||||
@ -48,7 +53,8 @@ const TEST_DATA = [
|
||||
options: {hideGuides: true},
|
||||
checkHighlighter: function* (testActor) {
|
||||
for (let side of ["top", "right", "bottom", "left"]) {
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("box-model-guide-" + side, "hidden");
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-" + side, "hidden");
|
||||
is(hidden, "true", side + " guide has been hidden");
|
||||
}
|
||||
}
|
||||
@ -57,7 +63,8 @@ const TEST_DATA = [
|
||||
desc: "Infobar can be hidden",
|
||||
options: {hideInfoBar: true},
|
||||
checkHighlighter: function* (testActor) {
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("box-model-nodeinfobar-container", "hidden");
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-nodeinfobar-container", "hidden");
|
||||
is(hidden, "true", "nodeinfobar has been hidden");
|
||||
}
|
||||
},
|
||||
@ -99,10 +106,14 @@ const TEST_DATA = [
|
||||
desc: "Guides can be drawn around a given region (1)",
|
||||
options: {region: "padding"},
|
||||
checkHighlighter: function* (testActor) {
|
||||
let topY1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-top", "y1");
|
||||
let rightX1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-right", "x1");
|
||||
let bottomY1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-bottom", "y1");
|
||||
let leftX1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-left", "x1");
|
||||
let topY1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-top", "y1");
|
||||
let rightX1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-right", "x1");
|
||||
let bottomY1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-bottom", "y1");
|
||||
let leftX1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-left", "x1");
|
||||
|
||||
let {points} = yield testActor.getHighlighterRegionPath("padding");
|
||||
points = points[0];
|
||||
@ -117,10 +128,14 @@ const TEST_DATA = [
|
||||
desc: "Guides can be drawn around a given region (2)",
|
||||
options: {region: "margin"},
|
||||
checkHighlighter: function* (testActor) {
|
||||
let topY1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-top", "y1");
|
||||
let rightX1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-right", "x1");
|
||||
let bottomY1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-bottom", "y1");
|
||||
let leftX1 = yield testActor.getHighlighterNodeAttribute("box-model-guide-left", "x1");
|
||||
let topY1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-top", "y1");
|
||||
let rightX1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-right", "x1");
|
||||
let bottomY1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-bottom", "y1");
|
||||
let leftX1 = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-guide-left", "x1");
|
||||
|
||||
let {points} = yield testActor.getHighlighterRegionPath("margin");
|
||||
points = points[0];
|
||||
@ -170,7 +185,8 @@ const TEST_DATA = [
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(
|
||||
"data:text/html;charset=utf-8," + encodeURI(TEST_URL));
|
||||
|
||||
let divFront = yield getNodeFront("div", inspector);
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
const TEST_URL = "data:text/html;charset=utf-8,Rect Highlighter Test";
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let front = inspector.inspector;
|
||||
let highlighter = yield front.getHighlighterByType("RectHighlighter");
|
||||
let body = yield getNodeFront("body", inspector);
|
||||
@ -26,13 +26,15 @@ add_task(function* () {
|
||||
|
||||
info("Check that the highlighter is hidden by default");
|
||||
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
let hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
is(hidden, "true", "The highlighter is hidden by default");
|
||||
|
||||
info("Check that nothing is shown if no rect is passed");
|
||||
|
||||
yield highlighter.show(body);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
is(hidden, "true", "The highlighter is hidden when no rect is passed");
|
||||
|
||||
info("Check that nothing is shown if rect is incomplete or invalid");
|
||||
@ -40,25 +42,29 @@ add_task(function* () {
|
||||
yield highlighter.show(body, {
|
||||
rect: {x: 0, y: 0}
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
is(hidden, "true", "The highlighter is hidden when the rect is incomplete");
|
||||
|
||||
yield highlighter.show(body, {
|
||||
rect: {x: 0, y: 0, width: -Infinity, height: 0}
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
is(hidden, "true", "The highlighter is hidden when the rect is invalid (1)");
|
||||
|
||||
yield highlighter.show(body, {
|
||||
rect: {x: 0, y: 0, width: 5, height: -45}
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
is(hidden, "true", "The highlighter is hidden when the rect is invalid (2)");
|
||||
|
||||
yield highlighter.show(body, {
|
||||
rect: {x: "test", y: 0, width: 5, height: 5}
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
is(hidden, "true", "The highlighter is hidden when the rect is invalid (3)");
|
||||
|
||||
info("Check that the highlighter is displayed when valid options are passed");
|
||||
@ -66,9 +72,11 @@ add_task(function* () {
|
||||
yield highlighter.show(body, {
|
||||
rect: {x: 5, y: 5, width: 50, height: 50}
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
ok(!hidden, "The highlighter is displayed");
|
||||
let style = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "style", highlighter);
|
||||
let style = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "style", highlighter);
|
||||
is(style, "left:5px;top:5px;width:50px;height:50px;",
|
||||
"The highlighter is positioned correctly");
|
||||
|
||||
@ -77,9 +85,11 @@ add_task(function* () {
|
||||
yield highlighter.show(body, {
|
||||
rect: {x: 0, y: 0, width: 50, height: 50}
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
ok(!hidden, "The highlighter is displayed when x=0 and y=0");
|
||||
style = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "style", highlighter);
|
||||
style = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "style", highlighter);
|
||||
is(style, "left:0px;top:0px;width:50px;height:50px;",
|
||||
"The highlighter is positioned correctly");
|
||||
|
||||
@ -88,7 +98,8 @@ add_task(function* () {
|
||||
yield highlighter.show(body, {
|
||||
rect: {x: 0, y: 0, width: 0, height: 0}
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
is(hidden, "true", "The highlighter is hidden width and height are 0");
|
||||
|
||||
info("Check that a fill color can be passed");
|
||||
@ -97,9 +108,11 @@ add_task(function* () {
|
||||
rect: {x: 100, y: 200, width: 500, height: 200},
|
||||
fill: "red"
|
||||
});
|
||||
hidden = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "hidden", highlighter);
|
||||
hidden = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "hidden", highlighter);
|
||||
ok(!hidden, "The highlighter is displayed");
|
||||
style = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "style", highlighter);
|
||||
style = yield testActor.getHighlighterNodeAttribute(
|
||||
"highlighted-rect", "style", highlighter);
|
||||
is(style, "left:100px;top:200px;width:500px;height:200px;background:red;",
|
||||
"The highlighter has the right background color");
|
||||
|
||||
|
@ -22,7 +22,8 @@ add_task(function* () {
|
||||
rect: {x: 50, y: 50, width: 100, height: 100}
|
||||
});
|
||||
|
||||
let style = yield testActor.getHighlighterNodeAttribute("highlighted-rect", "style", highlighter);
|
||||
let style = yield testActor.getHighlighterNodeAttribute("highlighted-rect",
|
||||
"style", highlighter);
|
||||
|
||||
// The parent body has margin=50px and border=10px
|
||||
// The parent iframe also has margin=50px and border=10px
|
||||
|
@ -7,7 +7,8 @@
|
||||
// Test the creation of the geometry highlighter elements.
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
"<div style='position:absolute;left: 0; top: 0; width: 40000px; height: 8000px'></div>";
|
||||
"<div style='position:absolute;left: 0; top: 0; " +
|
||||
"width: 40000px; height: 8000px'></div>";
|
||||
|
||||
const ID = "rulers-highlighter-";
|
||||
|
||||
@ -20,7 +21,7 @@ const RULERS_MAX_Y_AXIS = 15000;
|
||||
const RULERS_TEXT_STEP = 100;
|
||||
|
||||
add_task(function* () {
|
||||
let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URL);
|
||||
let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
|
||||
let front = inspector.inspector;
|
||||
|
||||
let highlighter = yield front.getHighlighterByType("RulersHighlighter");
|
||||
@ -54,18 +55,22 @@ function* isHiddenByDefault(highlighterFront, inspector, testActor) {
|
||||
function* hasRightLabelsContent(highlighterFront, inspector, testActor) {
|
||||
info("Checking the rulers have the proper text, based on rulers' size");
|
||||
|
||||
let contentX = yield testActor.getHighlighterNodeTextContent(`${ID}x-axis-text`, highlighterFront);
|
||||
let contentY = yield testActor.getHighlighterNodeTextContent(`${ID}y-axis-text`, highlighterFront);
|
||||
let contentX = yield testActor.getHighlighterNodeTextContent(
|
||||
`${ID}x-axis-text`, highlighterFront);
|
||||
let contentY = yield testActor.getHighlighterNodeTextContent(
|
||||
`${ID}y-axis-text`, highlighterFront);
|
||||
|
||||
let expectedX = "";
|
||||
for (let i = RULERS_TEXT_STEP; i < RULERS_MAX_X_AXIS; i += RULERS_TEXT_STEP)
|
||||
for (let i = RULERS_TEXT_STEP; i < RULERS_MAX_X_AXIS; i += RULERS_TEXT_STEP) {
|
||||
expectedX += i;
|
||||
}
|
||||
|
||||
is(contentX, expectedX, "x axis text content is correct");
|
||||
|
||||
let expectedY = "";
|
||||
for (let i = RULERS_TEXT_STEP; i < RULERS_MAX_Y_AXIS; i += RULERS_TEXT_STEP)
|
||||
for (let i = RULERS_TEXT_STEP; i < RULERS_MAX_Y_AXIS; i += RULERS_TEXT_STEP) {
|
||||
expectedY += i;
|
||||
}
|
||||
|
||||
is(contentY, expectedY, "y axis text content is correct");
|
||||
}
|
||||
|
@ -7,12 +7,13 @@
|
||||
// Test the creation of the geometry highlighter elements.
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
"<div style='position:absolute;left: 0; top: 0; width: 40000px; height: 8000px'></div>";
|
||||
"<div style='position:absolute;left: 0; top: 0; " +
|
||||
"width: 40000px; height: 8000px'></div>";
|
||||
|
||||
const ID = "rulers-highlighter-";
|
||||
|
||||
add_task(function* () {
|
||||
let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URL);
|
||||
let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
|
||||
let front = inspector.inspector;
|
||||
|
||||
let highlighter = yield front.getHighlighterByType("RulersHighlighter");
|
||||
@ -28,7 +29,7 @@ add_task(function* () {
|
||||
});
|
||||
|
||||
function* isUpdatedAfterScroll(highlighterFront, inspector, testActor) {
|
||||
info("Checking the rulers' position by default");
|
||||
info("Check the rulers' position by default");
|
||||
|
||||
let xAxisRulerTransform = yield testActor.getHighlighterNodeAttribute(
|
||||
`${ID}x-axis-ruler`, "transform", highlighterFront);
|
||||
@ -42,9 +43,9 @@ function* isUpdatedAfterScroll(highlighterFront, inspector, testActor) {
|
||||
is(xAxisRulerTransform, null, "x axis ruler is positioned properly");
|
||||
is(xAxisTextTransform, null, "x axis text are positioned properly");
|
||||
is(yAxisRulerTransform, null, "y axis ruler is positioned properly");
|
||||
is(yAxisRulerTransform, null, "y axis text are positioned properly");
|
||||
is(yAxisTextTransform, null, "y axis text are positioned properly");
|
||||
|
||||
info("Asking the content window to scroll to specific coords");
|
||||
info("Ask the content window to scroll to specific coords");
|
||||
|
||||
let x = 200, y = 300;
|
||||
|
||||
@ -53,7 +54,7 @@ function* isUpdatedAfterScroll(highlighterFront, inspector, testActor) {
|
||||
is(data.x, x, "window scrolled properly horizontally");
|
||||
is(data.y, y, "window scrolled properly vertically");
|
||||
|
||||
info("Checking the rulers are properly positioned after the scrolling");
|
||||
info("Check the rulers are properly positioned after the scrolling");
|
||||
|
||||
xAxisRulerTransform = yield testActor.getHighlighterNodeAttribute(
|
||||
`${ID}x-axis-ruler`, "transform", highlighterFront);
|
||||
@ -64,19 +65,23 @@ function* isUpdatedAfterScroll(highlighterFront, inspector, testActor) {
|
||||
yAxisTextTransform = yield testActor.getHighlighterNodeAttribute(
|
||||
`${ID}y-axis-text`, "transform", highlighterFront);
|
||||
|
||||
is(xAxisRulerTransform, `translate(-${x})`, "x axis ruler is positioned properly");
|
||||
is(xAxisTextTransform, `translate(-${x})`, "x axis text are positioned properly");
|
||||
is(yAxisRulerTransform, `translate(0, -${y})`, "y axis ruler is positioned properly");
|
||||
is(yAxisRulerTransform, `translate(0, -${y})`, "y axis text are positioned properly");
|
||||
is(xAxisRulerTransform, `translate(-${x})`,
|
||||
"x axis ruler is positioned properly");
|
||||
is(xAxisTextTransform, `translate(-${x})`,
|
||||
"x axis text are positioned properly");
|
||||
is(yAxisRulerTransform, `translate(0, -${y})`,
|
||||
"y axis ruler is positioned properly");
|
||||
is(yAxisTextTransform, `translate(0, -${y})`,
|
||||
"y axis text are positioned properly");
|
||||
|
||||
info("Asking the content window to scroll relative to the current position");
|
||||
info("Ask the content window to scroll relative to the current position");
|
||||
|
||||
data = yield testActor.scrollWindow(-50, -60, true);
|
||||
|
||||
is(data.x, x - 50, "window scrolled properly horizontally");
|
||||
is(data.y, y - 60, "window scrolled properly vertically");
|
||||
|
||||
info("Checking the rulers are properly positioned after the relative scrolling");
|
||||
info("Check the rulers are properly positioned after the relative scrolling");
|
||||
|
||||
xAxisRulerTransform = yield testActor.getHighlighterNodeAttribute(
|
||||
`${ID}x-axis-ruler`, "transform", highlighterFront);
|
||||
@ -87,8 +92,12 @@ function* isUpdatedAfterScroll(highlighterFront, inspector, testActor) {
|
||||
yAxisTextTransform = yield testActor.getHighlighterNodeAttribute(
|
||||
`${ID}y-axis-text`, "transform", highlighterFront);
|
||||
|
||||
is(xAxisRulerTransform, `translate(-${x - 50})`, "x axis ruler is positioned properly");
|
||||
is(xAxisTextTransform, `translate(-${x - 50})`, "x axis text are positioned properly");
|
||||
is(yAxisRulerTransform, `translate(0, -${y - 60})`, "y axis ruler is positioned properly");
|
||||
is(yAxisRulerTransform, `translate(0, -${y - 60})`, "y axis text are positioned properly");
|
||||
is(xAxisRulerTransform, `translate(-${x - 50})`,
|
||||
"x axis ruler is positioned properly");
|
||||
is(xAxisTextTransform, `translate(-${x - 50})`,
|
||||
"x axis text are positioned properly");
|
||||
is(yAxisRulerTransform, `translate(0, -${y - 60})`,
|
||||
"y axis ruler is positioned properly");
|
||||
is(yAxisTextTransform, `translate(0, -${y - 60})`,
|
||||
"y axis text are positioned properly");
|
||||
}
|
||||
|
@ -36,5 +36,4 @@ add_task(function* () {
|
||||
});
|
||||
return inspector.toolbox.once("picker-node-hovered");
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -15,13 +15,15 @@ const TEST_URL = "data:text/html;charset=utf-8,<div>zoom me</div>";
|
||||
// element.
|
||||
const TEST_LEVELS = [{
|
||||
level: 2,
|
||||
expected: "position:absolute;transform-origin:top left;transform:scale(0.5);width:200%;height:200%;"
|
||||
expected: "position:absolute;transform-origin:top left;" +
|
||||
"transform:scale(0.5);width:200%;height:200%;"
|
||||
}, {
|
||||
level: 1,
|
||||
expected: "position:absolute;width:100%;height:100%;"
|
||||
}, {
|
||||
level: .5,
|
||||
expected: "position:absolute;transform-origin:top left;transform:scale(2);width:50%;height:50%;"
|
||||
expected: "position:absolute;transform-origin:top left;" +
|
||||
"transform:scale(2);width:50%;height:50%;"
|
||||
}];
|
||||
|
||||
add_task(function* () {
|
||||
@ -34,7 +36,8 @@ add_task(function* () {
|
||||
ok(isVisible, "The highlighter is visible");
|
||||
|
||||
for (let {level, expected} of TEST_LEVELS) {
|
||||
info("Zoom to level " + level + " and check that the highlighter is correct");
|
||||
info("Zoom to level " + level +
|
||||
" and check that the highlighter is correct");
|
||||
|
||||
yield testActor.zoomPageTo(level);
|
||||
isVisible = yield testActor.isHighlighting();
|
||||
@ -63,6 +66,7 @@ function* hoverContainer(container, inspector) {
|
||||
}
|
||||
|
||||
function* getRootNodeStyle(testActor) {
|
||||
let value = yield testActor.getHighlighterNodeAttribute("box-model-root", "style");
|
||||
let value = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-root", "style");
|
||||
return value;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ const TEST_URI = "data:text/html;charset=utf-8," +
|
||||
"<iframe src='data:text/html;charset=utf-8,hello world'></iframe>";
|
||||
|
||||
add_task(function* () {
|
||||
let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URI);
|
||||
let { toolbox, testActor } = yield openInspectorForURL(TEST_URI);
|
||||
|
||||
info("Starting element picker.");
|
||||
yield startPicker(toolbox);
|
||||
|
@ -63,22 +63,27 @@ function* testPosition(test, inspector, testActor) {
|
||||
|
||||
yield selectAndHighlightNode(test.selector, inspector);
|
||||
|
||||
let position = yield testActor.getHighlighterNodeAttribute("box-model-nodeinfobar-container", "position");
|
||||
let position = yield testActor.getHighlighterNodeAttribute(
|
||||
"box-model-nodeinfobar-container", "position");
|
||||
is(position, test.position, "Node " + test.selector + ": position matches");
|
||||
|
||||
let tag = yield testActor.getHighlighterNodeTextContent("box-model-nodeinfobar-tagname");
|
||||
let tag = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-nodeinfobar-tagname");
|
||||
is(tag, test.tag, "node " + test.selector + ": tagName matches.");
|
||||
|
||||
if (test.id) {
|
||||
let id = yield testActor.getHighlighterNodeTextContent("box-model-nodeinfobar-id");
|
||||
let id = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-nodeinfobar-id");
|
||||
is(id, "#" + test.id, "node " + test.selector + ": id matches.");
|
||||
}
|
||||
|
||||
let classes = yield testActor.getHighlighterNodeTextContent("box-model-nodeinfobar-classes");
|
||||
let classes = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-nodeinfobar-classes");
|
||||
is(classes, test.classes, "node " + test.selector + ": classes match.");
|
||||
|
||||
if (test.dims) {
|
||||
let dims = yield testActor.getHighlighterNodeTextContent("box-model-nodeinfobar-dimensions");
|
||||
let dims = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-nodeinfobar-dimensions");
|
||||
is(dims, test.dims, "node " + test.selector + ": dims match.");
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
/* 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/. */
|
||||
|
||||
/* globals getTestActorWithoutToolbox */
|
||||
"use strict";
|
||||
|
||||
// Tests for different ways to initialize the inspector.
|
||||
|
@ -11,7 +11,7 @@ const TEST_URI = "data:text/html;charset=utf-8," +
|
||||
"<div style=\"width: 100px; height: 100px; background:yellow;\"></div>";
|
||||
|
||||
add_task(function* () {
|
||||
let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_URI);
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URI);
|
||||
let divFront = yield getNodeFront("div", inspector);
|
||||
|
||||
info("Waiting for highlighter to activate");
|
||||
@ -20,7 +20,8 @@ add_task(function* () {
|
||||
let rect = yield testActor.getSimpleBorderRect();
|
||||
is(rect.width, 100, "The highlighter has the right width.");
|
||||
|
||||
info("Changing the test element's size and waiting for the highlighter to update");
|
||||
info("Changing the test element's size and waiting for the highlighter " +
|
||||
"to update");
|
||||
yield testActor.changeHighlightedNodeWaitForUpdate(
|
||||
"style",
|
||||
"width: 200px; height: 100px; background:yellow;"
|
||||
|
@ -48,4 +48,5 @@ function getElementByType(inspector, type) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -231,7 +231,8 @@ add_task(function* () {
|
||||
info("Simulating context menu click on the selected node container.");
|
||||
let nodeFrontContainer = getContainerForNodeFront(front, inspector);
|
||||
let contextMenuTrigger = attributeTrigger
|
||||
? nodeFrontContainer.tagLine.querySelector(`[data-attr="${attributeTrigger}"]`)
|
||||
? nodeFrontContainer.tagLine.querySelector(
|
||||
`[data-attr="${attributeTrigger}"]`)
|
||||
: nodeFrontContainer.tagLine;
|
||||
contextMenuClick(contextMenuTrigger);
|
||||
|
||||
@ -254,11 +255,11 @@ function* getNodeFrontForSelector(selector, inspector) {
|
||||
if (selector) {
|
||||
info("Retrieving front for selector " + selector);
|
||||
return getNodeFront(selector, inspector);
|
||||
} else {
|
||||
info("Retrieving front for doctype node");
|
||||
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
|
||||
return nodes[0];
|
||||
}
|
||||
|
||||
info("Retrieving front for doctype node");
|
||||
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
|
||||
return nodes[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,7 +281,7 @@ function setupClipboard(data, type) {
|
||||
*/
|
||||
function contextMenuClick(element) {
|
||||
let evt = element.ownerDocument.createEvent("MouseEvents");
|
||||
let button = 2; // right click
|
||||
let button = 2;
|
||||
|
||||
evt.initMouseEvent("contextmenu", true, true,
|
||||
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
|
||||
|
@ -5,7 +5,6 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that different paste items work in the context menu
|
||||
|
||||
|
||||
const TEST_URL = URL_ROOT + "doc_inspector_menu.html";
|
||||
const PASTE_ADJACENT_HTML_DATA = [
|
||||
{
|
||||
@ -30,7 +29,6 @@ const PASTE_ADJACENT_HTML_DATA = [
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
var clipboard = require("sdk/clipboard");
|
||||
registerCleanupFunction(() => {
|
||||
clipboard = null;
|
||||
@ -71,7 +69,8 @@ add_task(function* () {
|
||||
info("Testing that 'Paste Inner HTML' menu item works.");
|
||||
clipboard.set("this was pasted (innerHTML)");
|
||||
let innerHTMLSelector = "#paste-area .inner";
|
||||
let getInnerHTML = () => testActor.getProperty(innerHTMLSelector, "innerHTML");
|
||||
let getInnerHTML = () => testActor.getProperty(innerHTMLSelector,
|
||||
"innerHTML");
|
||||
let origInnerHTML = yield getInnerHTML();
|
||||
|
||||
let nodeFront = yield getNodeFront(innerHTMLSelector, inspector);
|
||||
@ -88,10 +87,11 @@ add_task(function* () {
|
||||
|
||||
ok((yield getInnerHTML()) === clipboard.get(),
|
||||
"Clipboard content was pasted into the node's inner HTML.");
|
||||
ok((yield testActor.hasNode(innerHTMLSelector)), "The original node has been preserved.");
|
||||
ok((yield testActor.hasNode(innerHTMLSelector)),
|
||||
"The original node has been preserved.");
|
||||
yield undoChange(inspector);
|
||||
ok((yield getInnerHTML()) === origInnerHTML, "Previous innerHTML has been " +
|
||||
"restored after undo");
|
||||
ok((yield getInnerHTML()) === origInnerHTML,
|
||||
"Previous innerHTML has been restored after undo");
|
||||
}
|
||||
|
||||
function* testPasteAdjacentHTMLMenu() {
|
||||
@ -114,28 +114,29 @@ add_task(function* () {
|
||||
yield onMutation;
|
||||
}
|
||||
|
||||
ok((yield testActor.getProperty(adjacentNodeSelector, "innerHTML")).trim() ===
|
||||
"1<span class=\"ref\">234</span>" +
|
||||
"<span>5</span>", "The Paste as Last Child / as First Child / Before " +
|
||||
"/ After worked as expected");
|
||||
let html = yield testActor.getProperty(adjacentNodeSelector, "innerHTML");
|
||||
ok(html.trim() === "1<span class=\"ref\">234</span><span>5</span>",
|
||||
"The Paste as Last Child / as First Child / Before / After worked as " +
|
||||
"expected");
|
||||
yield undoChange(inspector);
|
||||
ok((yield testActor.getProperty(adjacentNodeSelector, "innerHTML")).trim() ===
|
||||
"1<span class=\"ref\">234</span>",
|
||||
"Undo works for paste adjacent HTML");
|
||||
|
||||
html = yield testActor.getProperty(adjacentNodeSelector, "innerHTML");
|
||||
ok(html.trim() === "1<span class=\"ref\">234</span>",
|
||||
"Undo works for paste adjacent HTML");
|
||||
}
|
||||
|
||||
function dispatchCommandEvent(node) {
|
||||
info("Dispatching command event on " + node);
|
||||
let commandEvent = document.createEvent("XULCommandEvent");
|
||||
commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
|
||||
false, false, null);
|
||||
commandEvent.initCommandEvent("command", true, true, window, 0, false,
|
||||
false, false, false, null);
|
||||
node.dispatchEvent(commandEvent);
|
||||
}
|
||||
|
||||
function contextMenuClick(element) {
|
||||
info("Simulating contextmenu event on " + element);
|
||||
let evt = element.ownerDocument.createEvent("MouseEvents");
|
||||
let button = 2; // right click
|
||||
let button = 2;
|
||||
|
||||
evt.initMouseEvent("contextmenu", true, true,
|
||||
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
|
||||
@ -145,8 +146,9 @@ add_task(function* () {
|
||||
}
|
||||
|
||||
function getLabelFor(elt) {
|
||||
if (typeof elt === "string")
|
||||
if (typeof elt === "string") {
|
||||
elt = inspector.panelDoc.querySelector(elt);
|
||||
}
|
||||
let isInPasteSubMenu = elt.matches("#node-menu-paste-extra-submenu *");
|
||||
return `"${isInPasteSubMenu ? "Paste > " : ""}${elt.label}"`;
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ add_task(function* () {
|
||||
|
||||
function* testUseInConsole() {
|
||||
info("Testing 'Use in Console' menu item.");
|
||||
let useInConsoleNode = inspector.panelDoc.getElementById("node-menu-useinconsole");
|
||||
let useInConsoleNode = inspector.panelDoc.getElementById(
|
||||
"node-menu-useinconsole");
|
||||
|
||||
yield selectNode("#console-var", inspector);
|
||||
dispatchCommandEvent(useInConsoleNode);
|
||||
@ -31,7 +32,8 @@ add_task(function* () {
|
||||
is(jstermInput.value, "temp0", "first console variable is named temp0");
|
||||
|
||||
let result = yield jsterm.execute();
|
||||
isnot(result.textContent.indexOf('<p id="console-var">'), -1, "variable temp0 references correct node");
|
||||
isnot(result.textContent.indexOf('<p id="console-var">'), -1,
|
||||
"variable temp0 references correct node");
|
||||
|
||||
yield selectNode("#console-var-multi", inspector);
|
||||
dispatchCommandEvent(useInConsoleNode);
|
||||
@ -40,7 +42,8 @@ add_task(function* () {
|
||||
is(jstermInput.value, "temp1", "second console variable is named temp1");
|
||||
|
||||
result = yield jsterm.execute();
|
||||
isnot(result.textContent.indexOf('<p id="console-var-multi">'), -1, "variable temp1 references correct node");
|
||||
isnot(result.textContent.indexOf('<p id="console-var-multi">'), -1,
|
||||
"variable temp1 references correct node");
|
||||
|
||||
jsterm.clearHistory();
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
const TEST_URL = URL_ROOT + "doc_inspector_menu.html";
|
||||
|
||||
add_task(function* () {
|
||||
let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URL);
|
||||
let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
|
||||
yield selectNode("#attributes", inspector);
|
||||
|
||||
yield testAddAttribute();
|
||||
|
@ -14,7 +14,8 @@ add_task(function* () {
|
||||
yield testScrollIntoView();
|
||||
function* testShowDOMProperties() {
|
||||
info("Testing 'Show DOM Properties' menu item.");
|
||||
let showDOMPropertiesNode = inspector.panelDoc.getElementById("node-menu-showdomproperties");
|
||||
let showDOMPropertiesNode = inspector.panelDoc.getElementById(
|
||||
"node-menu-showdomproperties");
|
||||
ok(showDOMPropertiesNode, "the popup menu has a show dom properties item");
|
||||
|
||||
let consoleOpened = toolbox.once("webconsole-ready");
|
||||
|
@ -12,7 +12,8 @@ add_task(function* () {
|
||||
|
||||
let button = inspector.panelDoc.getElementById("inspector-pane-toggle");
|
||||
ok(button, "The toggle button exists in the DOM");
|
||||
is(button.parentNode.id, "inspector-toolbar", "The toggle button is in the toolbar");
|
||||
is(button.parentNode.id, "inspector-toolbar",
|
||||
"The toggle button is in the toolbar");
|
||||
ok(button.getAttribute("tooltiptext"), "The tool tip has initial state");
|
||||
ok(!button.hasAttribute("pane-collapsed"), "The button is in expanded state");
|
||||
ok(!!button.getClientRects().length, "The button is visible");
|
||||
|
@ -26,5 +26,5 @@ add_task(function* () {
|
||||
"toolbox is destroyed.");
|
||||
yield pickerStopped;
|
||||
|
||||
ok(true, "picker-stopped event fired after switch tools, so picker is closed.");
|
||||
ok(true, "picker-stopped event fired after switch tools so picker is closed");
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* globals getTestActorWithoutToolbox */
|
||||
"use strict";
|
||||
|
||||
// Test that locking the pseudoclass displays correctly in the ruleview
|
||||
@ -49,7 +50,6 @@ add_task(function* () {
|
||||
yield assertPseudoRemovedFromNode(testActor);
|
||||
});
|
||||
|
||||
|
||||
function* togglePseudoClass(inspector) {
|
||||
info("Toggle the pseudoclass, wait for it to be applied");
|
||||
|
||||
@ -72,14 +72,17 @@ function* testNavigate(inspector, testActor, ruleview) {
|
||||
|
||||
info("Make sure the pseudoclass is still on after navigating to a parent");
|
||||
|
||||
ok((yield testActor.hasPseudoClassLock("#div-1", PSEUDO)), "pseudo-class lock is still applied after inspecting ancestor");
|
||||
ok((yield testActor.hasPseudoClassLock("#div-1", PSEUDO)),
|
||||
"pseudo-class lock is still applied after inspecting ancestor");
|
||||
|
||||
let onPseudo = inspector.selection.once("pseudoclass");
|
||||
yield selectNode("#div-2", inspector);
|
||||
yield onPseudo;
|
||||
|
||||
info("Make sure the pseudoclass is removed after navigating to a non-hierarchy node");
|
||||
ok(!(yield testActor.hasPseudoClassLock("#div-1", PSEUDO)), "pseudo-class lock is removed after inspecting sibling node");
|
||||
info("Make sure the pseudoclass is removed after navigating to a " +
|
||||
"non-hierarchy node");
|
||||
ok(!(yield testActor.hasPseudoClassLock("#div-1", PSEUDO)),
|
||||
"pseudo-class lock is removed after inspecting sibling node");
|
||||
|
||||
yield selectNode("#div-1", inspector);
|
||||
yield togglePseudoClass(inspector);
|
||||
@ -102,21 +105,26 @@ function* assertPseudoAddedToNode(inspector, testActor, ruleview) {
|
||||
ok(hasLock, "pseudo-class lock has been applied");
|
||||
|
||||
info("Check that the ruleview contains the pseudo-class rule");
|
||||
let rules = ruleview.element.querySelectorAll(".ruleview-rule.theme-separator");
|
||||
is(rules.length, 3, "rule view is showing 3 rules for pseudo-class locked div");
|
||||
is(rules[1]._ruleEditor.rule.selectorText, "div:hover", "rule view is showing " + PSEUDO + " rule");
|
||||
let rules = ruleview.element.querySelectorAll(
|
||||
".ruleview-rule.theme-separator");
|
||||
is(rules.length, 3,
|
||||
"rule view is showing 3 rules for pseudo-class locked div");
|
||||
is(rules[1]._ruleEditor.rule.selectorText, "div:hover",
|
||||
"rule view is showing " + PSEUDO + " rule");
|
||||
|
||||
info("Show the highlighter on #div-1");
|
||||
yield showPickerOn("#div-1", inspector);
|
||||
|
||||
info("Check that the infobar selector contains the pseudo-class");
|
||||
let value = yield testActor.getHighlighterNodeTextContent("box-model-nodeinfobar-pseudo-classes");
|
||||
let value = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-nodeinfobar-pseudo-classes");
|
||||
is(value, PSEUDO, "pseudo-class in infobar selector");
|
||||
yield inspector.toolbox.highlighter.hideBoxModel();
|
||||
}
|
||||
|
||||
function* assertPseudoRemovedFromNode(testActor) {
|
||||
info("Make sure the pseudoclass lock is removed from #div-1 and its ancestors");
|
||||
info("Make sure the pseudoclass lock is removed from #div-1 and its " +
|
||||
"ancestors");
|
||||
|
||||
let hasLock = yield testActor.hasPseudoClassLock("#div-1", PSEUDO);
|
||||
ok(!hasLock, "pseudo-class lock has been removed");
|
||||
@ -128,12 +136,14 @@ function* assertPseudoRemovedFromNode(testActor) {
|
||||
|
||||
function* assertPseudoRemovedFromView(inspector, testActor, ruleview) {
|
||||
info("Check that the ruleview no longer contains the pseudo-class rule");
|
||||
let rules = ruleview.element.querySelectorAll(".ruleview-rule.theme-separator");
|
||||
let rules = ruleview.element.querySelectorAll(
|
||||
".ruleview-rule.theme-separator");
|
||||
is(rules.length, 2, "rule view is showing 2 rules after removing lock");
|
||||
|
||||
yield showPickerOn("#div-1", inspector);
|
||||
|
||||
let value = yield testActor.getHighlighterNodeTextContent("box-model-nodeinfobar-pseudo-classes");
|
||||
let value = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-nodeinfobar-pseudo-classes");
|
||||
is(value, "", "pseudo-class removed from infobar selector");
|
||||
yield inspector.toolbox.highlighter.hideBoxModel();
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ function openMenu(menu) {
|
||||
|
||||
function* testMenuItems(testActor, menu, inspector) {
|
||||
for (let pseudo of PSEUDOS) {
|
||||
let menuitem = inspector.panelDoc.getElementById("node-menu-pseudo-" + pseudo);
|
||||
let menuitem = inspector.panelDoc.getElementById(
|
||||
"node-menu-pseudo-" + pseudo);
|
||||
ok(menuitem, ":" + pseudo + " menuitem exists");
|
||||
|
||||
// Give the inspector panels a chance to update when the pseudoclass changes
|
||||
|
@ -9,11 +9,11 @@
|
||||
const TEST_URL = URL_ROOT + "doc_inspector_remove-iframe-during-load.html";
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL("about:blank");
|
||||
let {inspector, testActor} = yield openInspectorForURL("about:blank");
|
||||
yield selectNode("body", inspector);
|
||||
|
||||
// We do not want to wait for the inspector to be fully ready before testing
|
||||
// so we load TEST_URL and just wait for the content window to be done loading.
|
||||
// so we load TEST_URL and just wait for the content window to be done loading
|
||||
yield testActor.loadAndWaitForCustomEvent(TEST_URL);
|
||||
|
||||
// The content doc contains a script that creates iframes and deletes them
|
||||
@ -28,7 +28,7 @@ add_task(function* () {
|
||||
info("Creating and removing an iframe.");
|
||||
let onMarkupLoaded = inspector.once("markuploaded");
|
||||
testActor.eval("new " + function () {
|
||||
var iframe = document.createElement("iframe");
|
||||
let iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
iframe.remove();
|
||||
});
|
||||
@ -41,7 +41,8 @@ add_task(function* () {
|
||||
|
||||
// Assert that the markup-view is displayed and works
|
||||
ok(!(yield testActor.hasNode("iframe")), "Iframe has been removed.");
|
||||
is((yield testActor.getProperty("#yay", "textContent")), "load", "Load event fired.");
|
||||
is((yield testActor.getProperty("#yay", "textContent")), "load",
|
||||
"Load event fired.");
|
||||
|
||||
yield selectNode("#yay", inspector);
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint no-inline-comments: 0 */
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
@ -229,7 +229,8 @@ add_task(function* () {
|
||||
info("Waiting for search query to complete");
|
||||
yield inspector.searchSuggestions._lastQuery;
|
||||
|
||||
info("Query completed. Performing checks for input '" + searchBox.value + "'");
|
||||
info("Query completed. Performing checks for input '" +
|
||||
searchBox.value + "'");
|
||||
let actualSuggestions = popup.getItems().reverse();
|
||||
|
||||
is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
|
||||
|
@ -91,7 +91,8 @@ add_task(function* () {
|
||||
info("Waiting for search query to complete");
|
||||
yield inspector.searchSuggestions._lastQuery;
|
||||
|
||||
info("Query completed. Performing checks for input '" + searchBox.value + "'");
|
||||
info("Query completed. Performing checks for input '" +
|
||||
searchBox.value + "'");
|
||||
let actualSuggestions = popup.getItems().reverse();
|
||||
|
||||
is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
|
||||
|
@ -7,21 +7,21 @@
|
||||
// frames, selecting these suggestions actually selects the right nodes.
|
||||
|
||||
const IFRAME_SRC = "doc_inspector_search.html";
|
||||
const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
"<iframe id=\"iframe-1\" src=\"" +
|
||||
URL_ROOT + IFRAME_SRC + "\"></iframe>" +
|
||||
"<iframe id=\"iframe-2\" src=\"" +
|
||||
URL_ROOT + IFRAME_SRC + "\"></iframe>" +
|
||||
"<iframe id='iframe-3' src='data:text/html," +
|
||||
"<button id=\"b1\">Nested button</button>" +
|
||||
"<iframe id=\"iframe-4\" src=" + URL_ROOT + IFRAME_SRC + "></iframe>'>" +
|
||||
"</iframe>";
|
||||
const NESTED_IFRAME_SRC = `
|
||||
<button id="b1">Nested button</button>
|
||||
<iframe id="iframe-4" src="${URL_ROOT + IFRAME_SRC}"></iframe>
|
||||
`;
|
||||
const TEST_URL = `
|
||||
<iframe id="iframe-1" src="${URL_ROOT + IFRAME_SRC}"></iframe>
|
||||
<iframe id="iframe-2" src="${URL_ROOT + IFRAME_SRC}"></iframe>
|
||||
<iframe id="iframe-3"
|
||||
src="data:text/html;charset=utf-8,${encodeURI(NESTED_IFRAME_SRC)}">
|
||||
</iframe>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
let searchBox = inspector.searchBox;
|
||||
let popup = inspector.searchSuggestions.searchPopup;
|
||||
let {inspector} = yield openInspectorForURL(
|
||||
"data:text/html;charset=utf-8," + encodeURI(TEST_URL));
|
||||
|
||||
info("Focus the search box");
|
||||
yield focusSearchBoxUsingShortcut(inspector.panelWin);
|
||||
|
@ -53,7 +53,7 @@ const TEST_DATA = [
|
||||
suggestions: []
|
||||
},
|
||||
{
|
||||
key:"c",
|
||||
key: "c",
|
||||
suggestions: [{label: ".c1\\.c2"}]
|
||||
},
|
||||
{
|
||||
@ -111,7 +111,8 @@ add_task(function* () {
|
||||
info("Waiting for search query to complete");
|
||||
yield inspector.searchSuggestions._lastQuery;
|
||||
|
||||
info("Query completed. Performing checks for input '" + searchBox.value + "'");
|
||||
info("Query completed. Performing checks for input '" +
|
||||
searchBox.value + "'");
|
||||
let actualSuggestions = popup.getItems().reverse();
|
||||
|
||||
is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
|
||||
|
@ -36,17 +36,20 @@ add_task(function* () {
|
||||
yield sendKeyAndCheck(inspector, msg, "VK_G", { metaKey: true }, "#p2");
|
||||
|
||||
msg = "Press shift+meta-g to select the previous node";
|
||||
yield sendKeyAndCheck(inspector, msg, "VK_G", { metaKey: true, shiftKey: true }, "#p1");
|
||||
yield sendKeyAndCheck(inspector, msg, "VK_G",
|
||||
{ metaKey: true, shiftKey: true }, "#p1");
|
||||
} else {
|
||||
msg = "Press ctrl-g to cycle through multiple nodes";
|
||||
yield sendKeyAndCheck(inspector, msg, "VK_G", { ctrlKey: true }, "#p2");
|
||||
|
||||
msg = "Press shift+ctrl-g to select the previous node";
|
||||
yield sendKeyAndCheck(inspector, msg, "VK_G", { ctrlKey: true, shiftKey: true }, "#p1");
|
||||
yield sendKeyAndCheck(inspector, msg, "VK_G",
|
||||
{ ctrlKey: true, shiftKey: true }, "#p1");
|
||||
}
|
||||
});
|
||||
|
||||
let sendKeyAndCheck = Task.async(function* (inspector, description, key, modifiers, expectedId) {
|
||||
let sendKeyAndCheck = Task.async(function* (inspector, description, key,
|
||||
modifiers, expectedId) {
|
||||
info(description);
|
||||
let onSelect = inspector.once("inspector-updated");
|
||||
EventUtils.synthesizeKey(key, modifiers, inspector.panelWin);
|
||||
|
@ -1,125 +1,84 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that the selector-search input proposes ids and classes even when . and
|
||||
// # is missing, but that this only occurs when the query is one word (no
|
||||
// selector combination)
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
// The various states of the inspector: [key, suggestions array]
|
||||
// [
|
||||
// what key to press,
|
||||
// suggestions array with count [
|
||||
// [suggestion1, count1], [suggestion2] ...
|
||||
// ] count can be left to represent 1
|
||||
// ]
|
||||
const KEY_STATES = [
|
||||
["s", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["p", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["a", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["n", []],
|
||||
[" ", [["span div", 1]]],
|
||||
// mixed tag/class/id suggestions only work for the first word
|
||||
["d", [["span div", 1]]],
|
||||
["VK_BACK_SPACE", [["span div", 1]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
// Test that mixed tags, classes and ids are grouped by types, sorted by
|
||||
// count and alphabetical order
|
||||
["b", [
|
||||
["button", 3],
|
||||
["body", 1],
|
||||
[".bc", 3],
|
||||
[".ba", 1],
|
||||
[".bb", 1],
|
||||
["#ba", 1],
|
||||
["#bb", 1],
|
||||
["#bc", 1]
|
||||
]],
|
||||
];
|
||||
|
||||
let inspector, searchBox, state, popup;
|
||||
const TEST_URL = `<span class="span" id="span">
|
||||
<div class="div" id="div"></div>
|
||||
</span>
|
||||
<button class="ba bc" id="bc"></button>
|
||||
<button class="bb bc" id="bb"></button>
|
||||
<button class="bc" id="ba"></button>`;
|
||||
|
||||
// The various states of the inspector: [key, suggestions array]
|
||||
// [
|
||||
// what key to press,
|
||||
// suggestions array with count [
|
||||
// [suggestion1, count1], [suggestion2] ...
|
||||
// ] count can be left to represent 1
|
||||
// ]
|
||||
let keyStates = [
|
||||
["s", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["p", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["a", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["n", []],
|
||||
[" ", [["span div", 1]]],
|
||||
["d", [["span div", 1]]], // mixed tag/class/id suggestions only work for the first word
|
||||
["VK_BACK_SPACE", [["span div", 1]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
// Test that mixed tags, classes and ids are grouped by types, sorted by
|
||||
// count and alphabetical order
|
||||
["b", [
|
||||
["button", 3],
|
||||
["body", 1],
|
||||
[".bc", 3],
|
||||
[".ba", 1],
|
||||
[".bb", 1],
|
||||
["#ba", 1],
|
||||
["#bb", 1],
|
||||
["#bc", 1]
|
||||
]],
|
||||
];
|
||||
add_task(function* () {
|
||||
let {inspector} = yield openInspectorForURL("data:text/html;charset=utf-8," +
|
||||
encodeURI(TEST_URL));
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
let searchBox = inspector.panelWin.document.getElementById(
|
||||
"inspector-searchbox");
|
||||
let popup = inspector.searchSuggestions.searchPopup;
|
||||
|
||||
content.location = "data:text/html," +
|
||||
"<span class='span' id='span'>" +
|
||||
" <div class='div' id='div'></div>" +
|
||||
"</span>" +
|
||||
"<button class='ba bc' id='bc'></button>" +
|
||||
"<button class='bb bc' id='bb'></button>" +
|
||||
"<button class='bc' id='ba'></button>";
|
||||
|
||||
function $(id) {
|
||||
if (id == null) return null;
|
||||
return content.document.getElementById(id);
|
||||
}
|
||||
|
||||
function setupTest()
|
||||
{
|
||||
openInspector().then(startTest);
|
||||
}
|
||||
|
||||
function startTest(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
|
||||
searchBox =
|
||||
inspector.panelWin.document.getElementById("inspector-searchbox");
|
||||
popup = inspector.searchSuggestions.searchPopup;
|
||||
|
||||
focusSearchBoxUsingShortcut(inspector.panelWin, function () {
|
||||
searchBox.addEventListener("command", checkState, true);
|
||||
checkStateAndMoveOn(0);
|
||||
});
|
||||
}
|
||||
|
||||
function checkStateAndMoveOn(index) {
|
||||
if (index == keyStates.length) {
|
||||
finishUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let [key, suggestions] = keyStates[index];
|
||||
state = index;
|
||||
yield focusSearchBoxUsingShortcut(inspector.panelWin);
|
||||
|
||||
for (let [key, expectedSuggestions] of KEY_STATES) {
|
||||
info("pressing key " + key + " to get suggestions " +
|
||||
JSON.stringify(suggestions));
|
||||
JSON.stringify(expectedSuggestions));
|
||||
|
||||
let onCommand = once(searchBox, "command", true);
|
||||
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
||||
}
|
||||
yield onCommand;
|
||||
|
||||
function checkState(event) {
|
||||
inspector.searchSuggestions._lastQuery.then(() => {
|
||||
let [key, suggestions] = keyStates[state];
|
||||
let actualSuggestions = popup.getItems();
|
||||
is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
|
||||
"There are expected number of suggestions at " + state + "th step.");
|
||||
actualSuggestions.reverse();
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
is(suggestions[i][0], actualSuggestions[i].label,
|
||||
"The suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.");
|
||||
is(suggestions[i][1] || 1, actualSuggestions[i].count,
|
||||
"The count for suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.");
|
||||
}
|
||||
checkStateAndMoveOn(state + 1);
|
||||
});
|
||||
}
|
||||
info("Waiting for the suggestions to be retrieved");
|
||||
yield inspector.searchSuggestions._lastQuery;
|
||||
|
||||
function finishUp() {
|
||||
searchBox = null;
|
||||
popup = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
let actualSuggestions = popup.getItems();
|
||||
is(popup.isOpen ? actualSuggestions.length : 0, expectedSuggestions.length,
|
||||
"There are expected number of suggestions");
|
||||
actualSuggestions.reverse();
|
||||
|
||||
for (let i = 0; i < expectedSuggestions.length; i++) {
|
||||
is(expectedSuggestions[i][0], actualSuggestions[i].label,
|
||||
"The suggestion at " + i + "th index is correct.");
|
||||
is(expectedSuggestions[i][1] || 1, actualSuggestions[i].count,
|
||||
"The count for suggestion at " + i + "th index is correct.");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -40,7 +40,8 @@ const TEST_DATA = [
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: "Open popup and then tab away (2 times) to the a next focusable element",
|
||||
desc: "Open popup and then tab away (2 times) to the a next focusable " +
|
||||
"element",
|
||||
focused: false,
|
||||
keys: [
|
||||
{
|
||||
|
@ -10,7 +10,8 @@
|
||||
const FrameURL = "data:text/html;charset=UTF-8," +
|
||||
encodeURI("<div id=\"frame\">frame</div>");
|
||||
const URL = "data:text/html;charset=UTF-8," +
|
||||
encodeURI("<iframe src=\"" + FrameURL + "\"></iframe><div id=\"top\">top</div>");
|
||||
encodeURI('<iframe src="' + FrameURL +
|
||||
'"></iframe><div id="top">top</div>');
|
||||
|
||||
add_task(function* () {
|
||||
Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
|
||||
@ -18,20 +19,25 @@ add_task(function* () {
|
||||
let {inspector, toolbox, testActor} = yield openInspectorForURL(URL);
|
||||
|
||||
// Verify we are on the top level document
|
||||
ok((yield testActor.hasNode("#top")), "We have the test node on the top level document");
|
||||
ok((yield testActor.hasNode("#top")),
|
||||
"We have the test node on the top level document");
|
||||
|
||||
assertMarkupViewIsLoaded(inspector);
|
||||
|
||||
// Verify that the frame list button is visible and populated
|
||||
let btn = toolbox.doc.getElementById("command-button-frames");
|
||||
ok(!btn.firstChild.getAttribute("hidden"), "The frame list button is visible");
|
||||
let frameBtns = Array.slice(btn.firstChild.querySelectorAll("[data-window-id]"));
|
||||
ok(!btn.firstChild.getAttribute("hidden"),
|
||||
"The frame list button is visible");
|
||||
let frameBtns = Array.slice(
|
||||
btn.firstChild.querySelectorAll("[data-window-id]"));
|
||||
is(frameBtns.length, 2, "We have both frames in the list");
|
||||
frameBtns.sort(function (a, b) {
|
||||
return a.getAttribute("label").localeCompare(b.getAttribute("label"));
|
||||
});
|
||||
is(frameBtns[0].getAttribute("label"), FrameURL, "Got top level document in the list");
|
||||
is(frameBtns[1].getAttribute("label"), URL, "Got iframe document in the list");
|
||||
is(frameBtns[0].getAttribute("label"), FrameURL,
|
||||
"Got top level document in the list");
|
||||
is(frameBtns[1].getAttribute("label"), URL,
|
||||
"Got iframe document in the list");
|
||||
|
||||
// Listen to will-navigate to check if the view is empty
|
||||
let willNavigate = toolbox.target.once("will-navigate").then(() => {
|
||||
@ -52,8 +58,10 @@ add_task(function* () {
|
||||
info("Navigation to the iframe is done, the inspector should be back up");
|
||||
|
||||
// Verify we are on page one
|
||||
ok(!(yield testActor.hasNode("iframe")), "We not longer have access to the top frame elements");
|
||||
ok((yield testActor.hasNode("#frame")), "But now have direct access to the iframe elements");
|
||||
ok(!(yield testActor.hasNode("iframe")),
|
||||
"We not longer have access to the top frame elements");
|
||||
ok((yield testActor.hasNode("#frame")),
|
||||
"But now have direct access to the iframe elements");
|
||||
|
||||
// On page 2 load, verify we have the right content
|
||||
assertMarkupViewIsLoaded(inspector);
|
||||
|
@ -3,6 +3,7 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// Checks that the expected default node is selected after a page navigation or
|
||||
// a reload.
|
||||
@ -62,7 +63,7 @@ add_task(function* () {
|
||||
}
|
||||
|
||||
let onNewRoot = inspector.once("new-root");
|
||||
yield navigateToAndWaitForNewRoot(toolbox, testActor, url);
|
||||
yield navigateToAndWaitForNewRoot(url);
|
||||
|
||||
info("Waiting for new root.");
|
||||
yield onNewRoot;
|
||||
@ -76,7 +77,7 @@ add_task(function* () {
|
||||
selectedNode + " is selected after navigation.");
|
||||
}
|
||||
|
||||
function navigateToAndWaitForNewRoot(toolbox, testActor, url) {
|
||||
function navigateToAndWaitForNewRoot(url) {
|
||||
info("Navigating and waiting for new-root event after navigation.");
|
||||
|
||||
let newRoot = inspector.once("new-root");
|
||||
@ -87,11 +88,11 @@ add_task(function* () {
|
||||
info("Reloading page.");
|
||||
let activeTab = toolbox.target.activeTab;
|
||||
return activeTab.reload();
|
||||
} else {
|
||||
info("Navigating to " + url);
|
||||
navigateTo(toolbox, url);
|
||||
}
|
||||
|
||||
info("Navigating to " + url);
|
||||
navigateTo(toolbox, url);
|
||||
|
||||
return newRoot;
|
||||
});
|
||||
}
|
||||
|
@ -9,7 +9,9 @@
|
||||
<div id="id1">Visible div 1</div>
|
||||
<!-- Invisible comment node -->
|
||||
<div id="id2">Visible div 2</div>
|
||||
<script type="text/javascript">/*Invisible script node*/</script>
|
||||
<script type="text/javascript">
|
||||
/* Invisible script node */
|
||||
</script>
|
||||
<div id="id3">Visible div 3</div>
|
||||
<div id="id4" style="display:none;">Invisible div node</div>
|
||||
</body>
|
||||
|
@ -7,6 +7,8 @@
|
||||
<body>
|
||||
<div id="yay"></div>
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
var yay = document.querySelector("#yay");
|
||||
yay.textContent = "nothing";
|
||||
|
||||
@ -21,18 +23,18 @@
|
||||
yay.textContent = "before events";
|
||||
|
||||
// Create/remove an iframe on DOMContentLoaded.
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
iframe.remove();
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
let newIframe = document.createElement("iframe");
|
||||
document.body.appendChild(newIframe);
|
||||
newIframe.remove();
|
||||
yay.textContent = "DOMContentLoaded";
|
||||
});
|
||||
|
||||
// Create/remove an iframe on window load.
|
||||
window.addEventListener("load", function() {
|
||||
var iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
iframe.remove();
|
||||
window.addEventListener("load", function () {
|
||||
let newIframe = document.createElement("iframe");
|
||||
document.body.appendChild(newIframe);
|
||||
newIframe.remove();
|
||||
yay.textContent = "load";
|
||||
|
||||
// Dispatch the done event.
|
||||
|
@ -6,6 +6,7 @@
|
||||
/* import-globals-from ../../framework/test/shared-head.js */
|
||||
/* import-globals-from ../../commandline/test/helpers.js */
|
||||
/* import-globals-from ../../shared/test/test-actor-registry.js */
|
||||
/* globals registerTestActor, getTestActor */
|
||||
"use strict";
|
||||
|
||||
// Load the shared-head file first.
|
||||
@ -81,8 +82,7 @@ function getNode(nodeOrSelector, options = {}) {
|
||||
let node = document.querySelector(nodeOrSelector);
|
||||
if (noMatches) {
|
||||
ok(!node, "Selector " + nodeOrSelector + " didn't match any nodes.");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ok(node, "Selector " + nodeOrSelector + " matched a node.");
|
||||
}
|
||||
|
||||
@ -190,7 +190,8 @@ var openInspectorForURL = Task.async(function* (url, hostType) {
|
||||
var openInspector = Task.async(function* (hostType) {
|
||||
info("Opening the inspector");
|
||||
|
||||
let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector", hostType);
|
||||
let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
|
||||
hostType);
|
||||
let inspector = toolbox.getPanel("inspector");
|
||||
|
||||
info("Waiting for the inspector to update");
|
||||
@ -217,7 +218,8 @@ function getActiveInspector() {
|
||||
*/
|
||||
var clickOnInspectMenuItem = Task.async(function* (testActor, selector) {
|
||||
info("Showing the contextual menu on node " + selector);
|
||||
let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu");
|
||||
let contentAreaContextMenu = document.querySelector(
|
||||
"#contentAreaContextMenu");
|
||||
let contextOpened = once(contentAreaContextMenu, "popupshown");
|
||||
|
||||
yield testActor.synthesizeMouse({
|
||||
@ -344,12 +346,10 @@ function getNodeFront(selector, {walker}) {
|
||||
* the node is in
|
||||
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
|
||||
* loaded in the toolbox
|
||||
* @param {String} reason Defaults to "test" which instructs the inspector not
|
||||
* to highlight the node upon selection
|
||||
* @return {Promise} Resolves when the inspector is updated with the new node
|
||||
*/
|
||||
var getNodeFrontInFrame = Task.async(function* (selector, frameSelector,
|
||||
inspector, reason = "test") {
|
||||
inspector) {
|
||||
let iframe = yield getNodeFront(frameSelector, inspector);
|
||||
let {nodes} = yield inspector.walker.children(iframe);
|
||||
return inspector.walker.querySelector(nodes[0], selector);
|
||||
@ -445,7 +445,8 @@ var clickContainer = Task.async(function* (selector, inspector) {
|
||||
|
||||
/**
|
||||
* Simulate the mouse leaving the markup-view area
|
||||
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
|
||||
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
|
||||
* loaded in the toolbox
|
||||
* @return a promise when done
|
||||
*/
|
||||
function mouseLeaveMarkupView(inspector) {
|
||||
@ -528,7 +529,7 @@ function dispatchCommandEvent(node) {
|
||||
*/
|
||||
function contextMenuClick(element) {
|
||||
let evt = element.ownerDocument.createEvent("MouseEvents");
|
||||
let button = 2; // right click
|
||||
let button = 2;
|
||||
|
||||
evt.initMouseEvent("contextmenu", true, true,
|
||||
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
|
||||
@ -545,11 +546,11 @@ function* getNodeFrontForSelector(selector, inspector) {
|
||||
if (selector) {
|
||||
info("Retrieving front for selector " + selector);
|
||||
return getNodeFront(selector, inspector);
|
||||
} else {
|
||||
info("Retrieving front for doctype node");
|
||||
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
|
||||
return nodes[0];
|
||||
}
|
||||
|
||||
info("Retrieving front for doctype node");
|
||||
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
|
||||
return nodes[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -654,16 +655,17 @@ const getHighlighterHelperFor = (type) => Task.async(
|
||||
);
|
||||
|
||||
// The expand all operation of the markup-view calls itself recursively and
|
||||
// there's not one event we can wait for to know when it's done
|
||||
// so use this helper function to wait until all recursive children updates are done.
|
||||
// there's not one event we can wait for to know when it's done so use this
|
||||
// helper function to wait until all recursive children updates are done.
|
||||
function* waitForMultipleChildrenUpdates(inspector) {
|
||||
// As long as child updates are queued up while we wait for an update already
|
||||
// wait again
|
||||
// As long as child updates are queued up while we wait for an update already
|
||||
// wait again
|
||||
if (inspector.markup._queuedChildUpdates &&
|
||||
inspector.markup._queuedChildUpdates.size) {
|
||||
yield waitForChildrenUpdated(inspector);
|
||||
return yield waitForMultipleChildrenUpdates(inspector);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,5 +27,6 @@ otherWorkers = Other Workers
|
||||
tabs = Tabs
|
||||
|
||||
pageNotFound = Page not found
|
||||
doesNotExist = #%S does not exist!
|
||||
|
||||
nothing = Nothing yet.
|
||||
|
@ -271,7 +271,6 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_737873_mixedcontent.js]
|
||||
tags = mcb
|
||||
[browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js]
|
||||
[browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js]
|
||||
skip-if = true # Bug 1110500 - mouse event failure in test
|
||||
|
@ -8,8 +8,9 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI_WARNING = "http://example.com/browser/devtools/client/" +
|
||||
"webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html";
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const TEST_URI_WARNING = "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html";
|
||||
const TEST_URI_NOWARNING = [
|
||||
"http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html",
|
||||
"http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html",
|
||||
@ -23,56 +24,60 @@ const INEFFECTIVE_IFRAME_SANDBOXING_MSG = "An iframe which has both " +
|
||||
"its sandboxing.";
|
||||
const SENTINEL_MSG = "testing ineffective sandboxing message";
|
||||
|
||||
function test() {
|
||||
loadTab(TEST_URI_WARNING).then(() => {
|
||||
openConsole().then((hud) => {
|
||||
content.console.log(SENTINEL_MSG);
|
||||
waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [
|
||||
{
|
||||
name: "Ineffective iframe sandboxing warning displayed successfully",
|
||||
text: INEFFECTIVE_IFRAME_SANDBOXING_MSG,
|
||||
category: CATEGORY_SECURITY,
|
||||
severity: SEVERITY_WARNING
|
||||
},
|
||||
{
|
||||
text: SENTINEL_MSG,
|
||||
severity: SEVERITY_LOG
|
||||
}
|
||||
]
|
||||
}).then(() => {
|
||||
let msgs = hud.outputNode.querySelectorAll(".message[category=security]");
|
||||
is(msgs.length, 1, "one security message");
|
||||
testNoWarning(0);
|
||||
});
|
||||
});
|
||||
add_task(function* () {
|
||||
yield testYesWarning();
|
||||
|
||||
for (let id = 0; id < TEST_URI_NOWARNING.length; id++) {
|
||||
yield testNoWarning(id);
|
||||
}
|
||||
});
|
||||
|
||||
function* testYesWarning() {
|
||||
yield loadTab(TEST_URI_WARNING);
|
||||
let hud = yield openConsole();
|
||||
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, SENTINEL_MSG, function* (msg) {
|
||||
content.console.log(msg);
|
||||
});
|
||||
|
||||
yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [
|
||||
{
|
||||
name: "Ineffective iframe sandboxing warning displayed successfully",
|
||||
text: INEFFECTIVE_IFRAME_SANDBOXING_MSG,
|
||||
category: CATEGORY_SECURITY,
|
||||
severity: SEVERITY_WARNING
|
||||
},
|
||||
{
|
||||
text: SENTINEL_MSG,
|
||||
severity: SEVERITY_LOG
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
let msgs = hud.outputNode.querySelectorAll(".message[category=security]");
|
||||
is(msgs.length, 1, "one security message");
|
||||
}
|
||||
|
||||
function testNoWarning(id) {
|
||||
loadTab(TEST_URI_NOWARNING[id]).then(() => {
|
||||
openConsole().then((hud) => {
|
||||
content.console.log(SENTINEL_MSG);
|
||||
waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [
|
||||
{
|
||||
text: SENTINEL_MSG,
|
||||
severity: SEVERITY_LOG
|
||||
}
|
||||
]
|
||||
}).then(() => {
|
||||
let msgs = hud.outputNode.querySelectorAll(".message[category=security]");
|
||||
is(msgs.length, 0, "no security messages (case " + id + ")");
|
||||
function* testNoWarning(id) {
|
||||
yield loadTab(TEST_URI_NOWARNING[id]);
|
||||
let hud = yield openConsole();
|
||||
|
||||
id += 1;
|
||||
if (id < TEST_URI_NOWARNING.length) {
|
||||
testNoWarning(id);
|
||||
} else {
|
||||
finishTest();
|
||||
}
|
||||
});
|
||||
});
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, SENTINEL_MSG, function* (msg) {
|
||||
content.console.log(msg);
|
||||
});
|
||||
|
||||
yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [
|
||||
{
|
||||
text: SENTINEL_MSG,
|
||||
severity: SEVERITY_LOG
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
let msgs = hud.outputNode.querySelectorAll(".message[category=security]");
|
||||
is(msgs.length, 0, "no security messages (case " + id + ")");
|
||||
}
|
||||
|
41
devtools/server/actors/addons.js
Normal file
41
devtools/server/actors/addons.js
Normal file
@ -0,0 +1,41 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {AddonManager} = require("resource://gre/modules/AddonManager.jsm");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const {addonsSpec} = require("devtools/shared/specs/addons");
|
||||
|
||||
const AddonsActor = protocol.ActorClassWithSpec(addonsSpec, {
|
||||
|
||||
initialize: function (conn) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
},
|
||||
|
||||
installTemporaryAddon: Task.async(function* (addonPath) {
|
||||
let addonFile;
|
||||
let addon;
|
||||
try {
|
||||
addonFile = new FileUtils.File(addonPath);
|
||||
addon = yield AddonManager.installTemporaryAddon(addonFile);
|
||||
} catch (error) {
|
||||
throw new Error(`Could not install add-on at '${addonPath}': ${error}`);
|
||||
}
|
||||
|
||||
// TODO: once the add-on actor has been refactored to use
|
||||
// protocol.js, we could return it directly.
|
||||
// return new BrowserAddonActor(this.conn, addon);
|
||||
|
||||
// Return a pseudo add-on object that a calling client can work
|
||||
// with. Provide a flag that the client can use to detect when it
|
||||
// gets upgraded to a real actor object.
|
||||
return { id: addon.id, actor: false };
|
||||
|
||||
}),
|
||||
});
|
||||
|
||||
exports.AddonsActor = AddonsActor;
|
@ -33,6 +33,8 @@ const {ActorClass, Actor, FrontClass, Front,
|
||||
Arg, method, RetVal, types} = protocol;
|
||||
// Make sure the nodeActor type is know here.
|
||||
const {NodeActor} = require("devtools/server/actors/inspector");
|
||||
const {AnimationPlayerFront} = require("devtools/shared/fronts/animation");
|
||||
const {animationPlayerSpec} = require("devtools/shared/specs/animation");
|
||||
const events = require("sdk/event/core");
|
||||
|
||||
// Types of animations.
|
||||
@ -53,16 +55,7 @@ exports.ANIMATION_TYPES = ANIMATION_TYPES;
|
||||
*
|
||||
* This actor also allows playing, pausing and seeking the animation.
|
||||
*/
|
||||
var AnimationPlayerActor = ActorClass({
|
||||
typeName: "animationplayer",
|
||||
|
||||
events: {
|
||||
"changed": {
|
||||
type: "changed",
|
||||
state: Arg(0, "json")
|
||||
}
|
||||
},
|
||||
|
||||
var AnimationPlayerActor = protocol.ActorClassWithSpec(animationPlayerSpec, {
|
||||
/**
|
||||
* @param {AnimationsActor} The main AnimationsActor instance
|
||||
* @param {AnimationPlayer} The player object returned by getAnimationPlayers
|
||||
@ -141,7 +134,7 @@ var AnimationPlayerActor = ActorClass({
|
||||
* Release the actor, when it isn't needed anymore.
|
||||
* Protocol.js uses this release method to call the destroy method.
|
||||
*/
|
||||
release: method(function () {}, {release: true}),
|
||||
release: function () {},
|
||||
|
||||
form: function (detail) {
|
||||
if (detail === "actorid") {
|
||||
@ -313,7 +306,7 @@ var AnimationPlayerActor = ActorClass({
|
||||
* reconstruct those). If you want the full state, use the getState method.
|
||||
* @return {Object}
|
||||
*/
|
||||
getCurrentState: method(function () {
|
||||
getCurrentState: function () {
|
||||
let newState = this.getState();
|
||||
|
||||
// If we've saved a state before, compare and only send what has changed.
|
||||
@ -334,12 +327,7 @@ var AnimationPlayerActor = ActorClass({
|
||||
this.currentState = newState;
|
||||
|
||||
return sentState;
|
||||
}, {
|
||||
request: {},
|
||||
response: {
|
||||
data: RetVal("json")
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Executed when the current animation changes, used to emit the new state
|
||||
@ -380,25 +368,19 @@ var AnimationPlayerActor = ActorClass({
|
||||
/**
|
||||
* Pause the player.
|
||||
*/
|
||||
pause: method(function () {
|
||||
pause: function () {
|
||||
this.player.pause();
|
||||
return this.player.ready;
|
||||
}, {
|
||||
request: {},
|
||||
response: {}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Play the player.
|
||||
* This method only returns when the animation has left its pending state.
|
||||
*/
|
||||
play: method(function () {
|
||||
play: function () {
|
||||
this.player.play();
|
||||
return this.player.ready;
|
||||
}, {
|
||||
request: {},
|
||||
response: {}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Simply exposes the player ready promise.
|
||||
@ -411,177 +393,48 @@ var AnimationPlayerActor = ActorClass({
|
||||
* might be interested to call this method.
|
||||
* This is especially important for tests.
|
||||
*/
|
||||
ready: method(function () {
|
||||
ready: function () {
|
||||
return this.player.ready;
|
||||
}, {
|
||||
request: {},
|
||||
response: {}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the current time of the animation player.
|
||||
*/
|
||||
setCurrentTime: method(function (currentTime) {
|
||||
setCurrentTime: function (currentTime) {
|
||||
this.player.currentTime = currentTime * this.player.playbackRate;
|
||||
}, {
|
||||
request: {
|
||||
currentTime: Arg(0, "number")
|
||||
},
|
||||
response: {}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the playback rate of the animation player.
|
||||
*/
|
||||
setPlaybackRate: method(function (playbackRate) {
|
||||
setPlaybackRate: function (playbackRate) {
|
||||
this.player.playbackRate = playbackRate;
|
||||
}, {
|
||||
request: {
|
||||
currentTime: Arg(0, "number")
|
||||
},
|
||||
response: {}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Get data about the keyframes of this animation player.
|
||||
* @return {Object} Returns a list of frames, each frame containing the list
|
||||
* animated properties as well as the frame's offset.
|
||||
*/
|
||||
getFrames: method(function () {
|
||||
getFrames: function () {
|
||||
return this.player.effect.getKeyframes();
|
||||
}, {
|
||||
request: {},
|
||||
response: {
|
||||
frames: RetVal("json")
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Get data about the animated properties of this animation player.
|
||||
* @return {Array} Returns a list of animated properties.
|
||||
* Each property contains a list of values and their offsets
|
||||
*/
|
||||
getProperties: method(function () {
|
||||
getProperties: function () {
|
||||
return this.player.effect.getProperties().map(property => {
|
||||
return {name: property.property, values: property.values};
|
||||
});
|
||||
}, {
|
||||
request: {},
|
||||
response: {
|
||||
properties: RetVal("array:json")
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
exports.AnimationPlayerActor = AnimationPlayerActor;
|
||||
|
||||
var AnimationPlayerFront = FrontClass(AnimationPlayerActor, {
|
||||
initialize: function (conn, form, detail, ctx) {
|
||||
Front.prototype.initialize.call(this, conn, form, detail, ctx);
|
||||
|
||||
this.state = {};
|
||||
},
|
||||
|
||||
form: function (form, detail) {
|
||||
if (detail === "actorid") {
|
||||
this.actorID = form;
|
||||
return;
|
||||
}
|
||||
this._form = form;
|
||||
this.state = this.initialState;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
Front.prototype.destroy.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* If the AnimationsActor was given a reference to the WalkerActor previously
|
||||
* then calling this getter will return the animation target NodeFront.
|
||||
*/
|
||||
get animationTargetNodeFront() {
|
||||
if (!this._form.animationTargetNodeActorID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.conn.getActor(this._form.animationTargetNodeActorID);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the initial state of the player. Up to date states can be
|
||||
* retrieved by calling the getCurrentState method.
|
||||
*/
|
||||
get initialState() {
|
||||
return {
|
||||
type: this._form.type,
|
||||
startTime: this._form.startTime,
|
||||
previousStartTime: this._form.previousStartTime,
|
||||
currentTime: this._form.currentTime,
|
||||
playState: this._form.playState,
|
||||
playbackRate: this._form.playbackRate,
|
||||
name: this._form.name,
|
||||
duration: this._form.duration,
|
||||
delay: this._form.delay,
|
||||
endDelay: this._form.endDelay,
|
||||
iterationCount: this._form.iterationCount,
|
||||
iterationStart: this._form.iterationStart,
|
||||
isRunningOnCompositor: this._form.isRunningOnCompositor,
|
||||
propertyState: this._form.propertyState,
|
||||
documentCurrentTime: this._form.documentCurrentTime
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Executed when the AnimationPlayerActor emits a "changed" event. Used to
|
||||
* update the local knowledge of the state.
|
||||
*/
|
||||
onChanged: protocol.preEvent("changed", function (partialState) {
|
||||
let {state} = this.reconstructState(partialState);
|
||||
this.state = state;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Refresh the current state of this animation on the client from information
|
||||
* found on the server. Doesn't return anything, just stores the new state.
|
||||
*/
|
||||
refreshState: Task.async(function* () {
|
||||
let data = yield this.getCurrentState();
|
||||
if (this.currentStateHasChanged) {
|
||||
this.state = data;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* getCurrentState interceptor re-constructs incomplete states since the actor
|
||||
* only sends the values that have changed.
|
||||
*/
|
||||
getCurrentState: protocol.custom(function () {
|
||||
this.currentStateHasChanged = false;
|
||||
return this._getCurrentState().then(partialData => {
|
||||
let {state, hasChanged} = this.reconstructState(partialData);
|
||||
this.currentStateHasChanged = hasChanged;
|
||||
return state;
|
||||
});
|
||||
}, {
|
||||
impl: "_getCurrentState"
|
||||
}),
|
||||
|
||||
reconstructState: function (data) {
|
||||
let hasChanged = false;
|
||||
|
||||
for (let key in this.state) {
|
||||
if (typeof data[key] === "undefined") {
|
||||
data[key] = this.state[key];
|
||||
} else if (data[key] !== this.state[key]) {
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {state: data, hasChanged};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sent with the 'mutations' event as part of an array of changes, used to
|
||||
* inform fronts of the type of change that occured.
|
||||
*/
|
||||
|
@ -12,6 +12,7 @@ DIRS += [
|
||||
DevToolsModules(
|
||||
'actor-registry.js',
|
||||
'addon.js',
|
||||
'addons.js',
|
||||
'animation.js',
|
||||
'breakpoint.js',
|
||||
'call-watcher.js',
|
||||
|
@ -5,35 +5,16 @@ A Simple Hello World
|
||||
--------------------
|
||||
|
||||
Here's a simple Hello World actor. It is a global actor (not associated with a given browser tab).
|
||||
It has two parts: a spec and an implementation. The spec would go somewhere like
|
||||
`devtools/shared/specs/hello-world.js` and would look like:
|
||||
|
||||
let protocol = require("devtools/shared/protocol");
|
||||
let {method, Arg, Option, RetVal} = protocol;
|
||||
const {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");
|
||||
|
||||
// This will be called by the framework when you call DebuggerServer.
|
||||
// registerModule(), and adds the actor as a 'helloActor' property
|
||||
// on the root actor.
|
||||
exports.register = function(handle) {
|
||||
handle.addGlobalActor(HelloActor, "helloActor");
|
||||
}
|
||||
const helloWorldSpec = generateActorSpec({
|
||||
typeName: "helloWorld", // I'll explain types later, I promise.
|
||||
|
||||
// This will be called by the framework during shutdown/unload.
|
||||
exports.unregister = function(handle) {
|
||||
handle.removeGlobalActor(HelloActor); // This is optional, all registered actors will be removed automatically
|
||||
}
|
||||
|
||||
// Create the hello actor. This uses addon-sdk's heritage module
|
||||
// behind the scenes in case you want to understand the class stuff
|
||||
// a bit better.
|
||||
|
||||
let HelloActor = protocol.ActorClass({
|
||||
typeName: "helloWorld", // I'll explain types later, I promise.
|
||||
initialize: function(conn) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn); // This is the worst part of heritage.
|
||||
},
|
||||
|
||||
sayHello: method(function() {
|
||||
return "hello";
|
||||
}, {
|
||||
methods: {
|
||||
sayHello: {
|
||||
// The request packet template. There are no arguments, so
|
||||
// it is empty. The framework will add the "type" and "to"
|
||||
// request properties.
|
||||
@ -44,21 +25,58 @@ Here's a simple Hello World actor. It is a global actor (not associated with a
|
||||
response: {
|
||||
greeting: RetVal("string") // "string" is the return value type.
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
This actor now supports a `sayHello` request. A request/reply will look like this:
|
||||
// Expose the spec so it can be imported by the implementation.
|
||||
exports.helloWorldSpec = helloWorldSpec;
|
||||
|
||||
The actor implementation would go somewhere like
|
||||
`devtools/server/actors/hello-world.js` and would look like:
|
||||
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const {helloWorldSpec} = require("devtools/shared/specs/hello-world");
|
||||
|
||||
const HelloActor = protocol.ActorClassWithSpec(helloWorldSpec, {
|
||||
initialize: function (conn) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn); // This is the worst part of heritage.
|
||||
},
|
||||
|
||||
sayHello: function () {
|
||||
return "hello";
|
||||
},
|
||||
});
|
||||
|
||||
// You also need to export the actor class in your module for discovery.
|
||||
exports.HelloActor = HelloActor;
|
||||
|
||||
To activate your actor, register it in the `addBrowserActors` method in `server/main.js`.
|
||||
The registration code would look something like this:
|
||||
|
||||
this.registerModule("devtools/server/actors/hello-world", {
|
||||
prefix: "hello",
|
||||
constructor: "HelloActor",
|
||||
type: { global: true }
|
||||
});
|
||||
|
||||
Your spec allows the actor to support a `sayHello` request.
|
||||
A request/reply will look like this:
|
||||
|
||||
-> { to: <actorID>, type: "sayHello" }
|
||||
<- { from: <actorID>, greeting: "hello" }
|
||||
|
||||
Now we can create a client side object. We call these *front* objects.
|
||||
Now we can create a client side object. We call these *front* objects and
|
||||
they typically go in `devtools/shared/fronts/`.
|
||||
|
||||
Here's the front for the HelloActor:
|
||||
|
||||
let HelloFront = protocol.FrontClass(HelloActor, {
|
||||
initialize: function(client, form) {
|
||||
const HelloFront = protocol.FrontClassWithSpec(helloWorldSpec, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
// This call may not be required but it's a good idea. It will
|
||||
// guarantee that your instance is managed in the pool.
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
|
||||
@ -86,14 +104,21 @@ Magically - Once you have an initial reference to a protocol.js object, it can r
|
||||
Arguments
|
||||
---------
|
||||
|
||||
`sayHello` has no arguments, so let's add a method that does take arguments:
|
||||
`sayHello` has no arguments, so let's add a method that does take arguments.
|
||||
Here's an adjustment to the spec:
|
||||
|
||||
echo: method(function(str) {
|
||||
return str + "... " + str + "...";
|
||||
}, {
|
||||
request: { echo: Arg(0, "string") },
|
||||
response: { echoed: RetVal("string") }
|
||||
})
|
||||
methods: {
|
||||
echo: {
|
||||
request: { echo: Arg(0, "string") },
|
||||
response: { echoed: RetVal("string") }
|
||||
}
|
||||
}
|
||||
|
||||
Here's an adjustment to the implementation:
|
||||
|
||||
echo: function (str) {
|
||||
return str + "... " + str + "...";
|
||||
}
|
||||
|
||||
This tells the library to place the 0th argument, which should be a string, in the `echo` property of the request packet.
|
||||
|
||||
@ -112,14 +137,20 @@ The library tries hard to make using fronts feel like natural javascript (or as
|
||||
Returning JSON
|
||||
--------------
|
||||
|
||||
Maybe your response is an object:
|
||||
Maybe your response is an object. Here's an example of a spec:
|
||||
|
||||
addOneTwice: method(function(a, b) {
|
||||
return { a: a + 1, b: b + 1 };
|
||||
}, {
|
||||
methods: {
|
||||
addOneTwice: {
|
||||
request: { a: Arg(0, "number"), b: Arg(1, "number") },
|
||||
response: { ret: RetVal("json") }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Here's an example implementation:
|
||||
|
||||
addOneTwice: function (a, b) {
|
||||
return { a: a + 1, b: b + 1 };
|
||||
}
|
||||
|
||||
This will generate a response packet that looks like:
|
||||
|
||||
@ -140,23 +171,29 @@ Things have been pretty simple up to this point - all the arguments we've passed
|
||||
|
||||
Again, the protocol lib tries hard to provide a natural API to actors and clients, and sometime that natural API might involve object APIs. I'm going to use a wickedly contrived example, bear with me. Let's say I have a small object that contains a number and has a few methods associated with it:
|
||||
|
||||
let Incrementor = function(i) {
|
||||
let Incrementor = function (i) {
|
||||
this.value = value;
|
||||
}
|
||||
Incrementor.prototype = {
|
||||
increment: function() { this.value++ },
|
||||
decrement: function() { this.value-- }
|
||||
increment: function () { this.value++ },
|
||||
decrement: function () { this.value-- }
|
||||
};
|
||||
|
||||
|
||||
and I want to return it from a backend function:
|
||||
|
||||
getIncrementor: method(function(i) {
|
||||
return new Incrementor(i)
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
getIncrementor: {
|
||||
request: { number: Arg(0, "number") },
|
||||
response: { value: RetVal("incrementor") } // We'll define "incrementor" below.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
getIncrementor: function (i) {
|
||||
return new Incrementor(i)
|
||||
}
|
||||
|
||||
I want that response to look like `{ from: <actorID>, value: <number> }`, but the client side needs to know to return an Incrementor, not a primitive number. So let's tell the protocol lib about Incrementors:
|
||||
|
||||
@ -178,12 +215,18 @@ And now our client can use the API as expected:
|
||||
|
||||
You can do the same thing with arguments:
|
||||
|
||||
passIncrementor: method(function(inc) {
|
||||
w.increment();
|
||||
assert(incrementor.value === 6);
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
passIncrementor: {
|
||||
request: { Arg(0, "incrementor") },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
passIncrementor: function (inc) {
|
||||
w.increment();
|
||||
assert(incrementor.value === 6);
|
||||
}
|
||||
|
||||
front.passIncrementor(new Incrementor(5));
|
||||
|
||||
@ -191,41 +234,54 @@ The library provides primitiive `boolean`, `number`, `string`, and `json` types.
|
||||
|
||||
Moving right along, let's say you want to pass/return an array of Incrementors. You can just prepend `array:` to the type name:
|
||||
|
||||
incrementAll: method(function(incrementors) {
|
||||
incrementors.forEach(incrementor => {
|
||||
incrementor.increment();
|
||||
}
|
||||
return incrementors;
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
incrementAll: {
|
||||
request: { incrementors: Arg(0, "array:incrementor") },
|
||||
response: { incrementors: RetVal("array:incrementor") }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
incrementAll: function (incrementors) {
|
||||
incrementors.forEach(incrementor => {
|
||||
incrementor.increment();
|
||||
}
|
||||
return incrementors;
|
||||
}
|
||||
|
||||
You can use an iterator in place of an array as an argument or return value, and the library will handle the conversion automatically.
|
||||
|
||||
Or maybe you want to return a dictionary where one item is a incrementor. To do this you need to tell the type system which members of the dictionary need custom marshallers:
|
||||
|
||||
protocol.types.addDictType("contrivedObject", {
|
||||
incrementor: "incrementor",
|
||||
incrementorArray: "array:incrementor"
|
||||
incrementor: "incrementor",
|
||||
incrementorArray: "array:incrementor"
|
||||
});
|
||||
|
||||
reallyContrivedExample: method(function() {
|
||||
return {
|
||||
/* a and b are primitives and so don't need to be called out specifically in addDictType */
|
||||
a: "hello", b: "world",
|
||||
incrementor: new Incrementor(1), incrementorArray: [new Incrementor(2), new Incrementor(3)]
|
||||
}
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
reallyContrivedExample: {
|
||||
response: RetVal("contrivedObject")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementations:
|
||||
reallyContrivedExample: function () {
|
||||
return {
|
||||
/* a and b are primitives and so don't need to be called out specifically in addDictType */
|
||||
a: "hello", b: "world",
|
||||
incrementor: new Incrementor(1),
|
||||
incrementorArray: [new Incrementor(2), new Incrementor(3)]
|
||||
}
|
||||
}
|
||||
|
||||
front.reallyContrivedExample().then(obj => {
|
||||
assert(obj.a == "hello");
|
||||
assert(obj.b == "world");
|
||||
assert(incrementor.i == 1);
|
||||
assert(incrementorArray[0].i == 2);
|
||||
assert(incrementorArray[1].i == 3);
|
||||
assert(obj.a == "hello");
|
||||
assert(obj.b == "world");
|
||||
assert(incrementor.i == 1);
|
||||
assert(incrementorArray[0].i == 2);
|
||||
assert(incrementorArray[1].i == 3);
|
||||
});
|
||||
|
||||
Nullables
|
||||
@ -243,40 +299,56 @@ Actors
|
||||
|
||||
Probably the most common objects that need custom martialing are actors themselves. These are more interesting than the Incrementor object, but by default they're somewhat easy to work with. Let's add a ChildActor implementation that will be returned by the HelloActor (which is rapidly becoming the OverwhelminglyComplexActor):
|
||||
|
||||
let ChildActor = protocol.ActorClass({
|
||||
actorType: "childActor",
|
||||
initialize: function(conn, id) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.greeting = "hello from " + id;
|
||||
},
|
||||
getGreeting: method(function() {
|
||||
return this.greeting;
|
||||
}, {
|
||||
response: { greeting: RetVal("string") },
|
||||
// spec:
|
||||
const childActorSpec = generateActorSpec({
|
||||
actorType: "childActor",
|
||||
methods: {
|
||||
getGreeting: {
|
||||
response: { greeting: RetVal("string") },
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let ChildFront = protocol.FrontClass(ChildActor, {
|
||||
initialize: function(client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
},
|
||||
// implementation:
|
||||
const ChildActor = protocol.ActorClassWithSpec(childActorSpec, {
|
||||
initialize: function (conn, id) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.greeting = "hello from " + id;
|
||||
},
|
||||
getGreeting: function () {
|
||||
return this.greeting;
|
||||
},
|
||||
});
|
||||
|
||||
exports.ChildActor = ChildActor;
|
||||
|
||||
const ChildFront = protocol.FrontClassWithSpec(childActorSpec, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
},
|
||||
});
|
||||
|
||||
The library will register a marshaller for the actor type itself, using typeName as its tag.
|
||||
|
||||
So we can now add the following code to HelloActor:
|
||||
|
||||
getChild: method(function(id) {
|
||||
return ChildActor(this.conn, id);
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
getChild: {
|
||||
request: { id: Arg(0, "string") },
|
||||
response: { child: RetVal("childActor") }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
getChild: function (id) {
|
||||
return ChildActor(this.conn, id);
|
||||
}
|
||||
|
||||
front.getChild("child1").then(childFront => {
|
||||
return childFront.getGreeting();
|
||||
return childFront.getGreeting();
|
||||
}).then(greeting => {
|
||||
assert(id === "hello from child1");
|
||||
assert(id === "hello from child1");
|
||||
});
|
||||
|
||||
The conversation will look like this:
|
||||
@ -290,7 +362,7 @@ But the ID is the only interesting part of this made-up example. You're never g
|
||||
|
||||
You can customize the marshalling of an actor by providing a `form` method in the `ChildActor` class:
|
||||
|
||||
form: function() {
|
||||
form: function () {
|
||||
return {
|
||||
actor: this.actorID,
|
||||
greeting: this.greeting
|
||||
@ -299,7 +371,7 @@ You can customize the marshalling of an actor by providing a `form` method in th
|
||||
|
||||
And you can demarshal in the `ChildFront` class by implementing a matching `form` method:
|
||||
|
||||
form: function(form) {
|
||||
form: function (form) {
|
||||
this.actorID = form.actor;
|
||||
this.greeting = form.greeting;
|
||||
}
|
||||
@ -311,7 +383,7 @@ Now you can use the id immediately:
|
||||
You may come across a situation where you want to customize the output of a `form` method depending on the operation being performed. For example, imagine that ChildActor is a bit more complex, with a, b, c, and d members:
|
||||
|
||||
ChildActor:
|
||||
form: function() {
|
||||
form: function () {
|
||||
return {
|
||||
actor: this.actorID,
|
||||
greeting: this.greeting,
|
||||
@ -322,7 +394,7 @@ You may come across a situation where you want to customize the output of a `for
|
||||
}
|
||||
}
|
||||
ChildFront:
|
||||
form: function(form) {
|
||||
form: function (form) {
|
||||
this.actorID = form.actorID;
|
||||
this.id = form.id;
|
||||
this.a = form.a;
|
||||
@ -339,13 +411,19 @@ And imagine you want to change 'c' and return the object:
|
||||
|
||||
...
|
||||
|
||||
changeC: method(function(newC) {
|
||||
c = newC;
|
||||
return this;
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
changeC: {
|
||||
request: { newC: Arg(0) },
|
||||
response: { self: RetVal("childActor") }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
changeC: function (newC) {
|
||||
c = newC;
|
||||
return this;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
@ -361,22 +439,22 @@ But that's wasteful. Only c changed. So we can provide a *detail* to the type
|
||||
|
||||
and update our form methods to make use of that data:
|
||||
|
||||
ChildActor:
|
||||
form: function(detail) {
|
||||
// In ChildActor:
|
||||
form: function (detail) {
|
||||
if (detail === "changec") {
|
||||
return { actor: this.actorID, c: this.c }
|
||||
}
|
||||
... // the rest of the form method stays the same.
|
||||
// ... the rest of the form method stays the same.
|
||||
}
|
||||
|
||||
ChildFront:
|
||||
form: function(form, detail) {
|
||||
// In ChildFront:
|
||||
form: function (form, detail) {
|
||||
if (detail === "changec") {
|
||||
this.actorID = form.actor;
|
||||
this.c = form.c;
|
||||
return;
|
||||
}
|
||||
... // the rest of the form method stays the same.
|
||||
// ... the rest of the form method stays the same.
|
||||
}
|
||||
|
||||
Now the packet looks like a much more reasonable `{ from: <childActorID>, self: { actor: <childActorID>, c: "hello" } }`
|
||||
@ -391,52 +469,64 @@ Events
|
||||
|
||||
Your actor has great news!
|
||||
|
||||
Actors are subclasses of jetpack `EventTarget`, so you can just emit:
|
||||
|
||||
let event = require("sdk/event/core");
|
||||
|
||||
giveGoodNews: method(function(news) {
|
||||
event.emit(this, "good-news", news);
|
||||
}, {
|
||||
request: { news: Arg(0) }
|
||||
});
|
||||
|
||||
... but nobody will really care, because that's not going over the protocol. But you can describe the packet in an `events` member, the same way you would specify a request:
|
||||
Actors are subclasses of jetpack `EventTarget`, so you can just emit events.
|
||||
Here's how you'd set it up in a spec:
|
||||
|
||||
events: {
|
||||
"good-news": {
|
||||
type: "goodNews", // event target naming and packet naming are at odds, and we want both to be natural!
|
||||
news: Arg(0)
|
||||
}
|
||||
"good-news": {
|
||||
type: "goodNews", // event target naming and packet naming are at odds, and we want both to be natural!
|
||||
news: Arg(0)
|
||||
}
|
||||
}
|
||||
|
||||
And now you can listen to events on a front:
|
||||
methods: {
|
||||
giveGoodNews: {
|
||||
request: { news: Arg(0) }
|
||||
}
|
||||
}
|
||||
|
||||
Here's how the implementation would look:
|
||||
|
||||
const event = require("sdk/event/core");
|
||||
|
||||
// In your protocol.ActorClassWithSpec definition:
|
||||
giveGoodNews: function (news) {
|
||||
event.emit(this, "good-news", news);
|
||||
}
|
||||
|
||||
Now you can listen to events on a front:
|
||||
|
||||
front.on("good-news", news => {
|
||||
console.log("Got good news: " + news + "\n");
|
||||
console.log(`Got some good news: ${news}\n`);
|
||||
});
|
||||
front.giveGoodNews().then(() => { console.log("request returned.") });
|
||||
|
||||
You might want to update your front's state when an event is fired, before emitting it against the front. You can use `preEvent` in the front definition for that:
|
||||
|
||||
countGoodNews: protocol.preEvent("good-news", function(news) {
|
||||
countGoodNews: protocol.preEvent("good-news", function (news) {
|
||||
this.amountOfGoodNews++;
|
||||
});
|
||||
|
||||
You can have events wait until an asynchronous action completes before firing by returning a promise. If you have multiple preEvents defined for a specific event, and atleast one fires asynchronously, then all preEvents most resolve before all events are fired.
|
||||
|
||||
countGoodNews: protocol.preEvent("good-news", function(news) {
|
||||
countGoodNews: protocol.preEvent("good-news", function (news) {
|
||||
return this.updateGoodNews().then(() => this.amountOfGoodNews++);
|
||||
});
|
||||
|
||||
On a somewhat related note, not every method needs to be request/response. Just like an actor can emit a one-way event, a method can be marked as a one-way request. Maybe we don't care about giveGoodNews returning anything:
|
||||
|
||||
giveGoodNews: method(function(news) {
|
||||
emit(this, "good-news", news);
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
giveGoodNews: {
|
||||
request: { news: Arg(0, "string") },
|
||||
oneway: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
giveGoodNews: function (news) {
|
||||
emit(this, "good-news", news);
|
||||
}
|
||||
|
||||
Lifetimes
|
||||
---------
|
||||
@ -448,7 +538,7 @@ Custom Front Methods
|
||||
|
||||
You might have some bookkeeping to do before issuing a request. Let's say you're calling `echo`, but you want to count the number of times you issue that request. Just use the `custom` tag in your front implementation:
|
||||
|
||||
echo: custom(function(str) {
|
||||
echo: custom(function (str) {
|
||||
this.numEchos++;
|
||||
return this._echo(str);
|
||||
}, {
|
||||
@ -468,23 +558,35 @@ The protocol library will maintain the child/parent relationships for you, but i
|
||||
|
||||
The default parent of an object is the first object that returns it after it is created. So to revisit our earlier HelloActor `getChild` implementation:
|
||||
|
||||
getChild: method(function(id) {
|
||||
return new ChildActor(this.conn, id);
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
getChild: {
|
||||
request: { id: Arg(0) },
|
||||
response: { child: RetVal("childActor") }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
getChild: function (id) {
|
||||
return new ChildActor(this.conn, id);
|
||||
}
|
||||
|
||||
The ChildActor's parent is the HelloActor, because it's the one that created it.
|
||||
|
||||
You can customize this behavior in two ways. The first is by defining a `marshallPool` property in your actor. Imagine a new ChildActor method:
|
||||
|
||||
getSibling: method(function(id) {
|
||||
return new ChildActor(this.conn, id);
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
getSibling: {
|
||||
request: { id: Arg(0) },
|
||||
response: { child: RetVal("childActor") }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
getSibling: function (id) {
|
||||
return new ChildActor(this.conn, id);
|
||||
}
|
||||
|
||||
This creates a new child actor owned by the current child actor. But in this example we want all actors created by the child to be owned by the HelloActor. So we can define a `defaultParent` property that makes use of the `parent` proeprty provided by the Actor class:
|
||||
|
||||
@ -497,30 +599,39 @@ For more complex situations, you can define your own lifetime properties. Take
|
||||
// When the "temp" lifetime is specified, look for the _temporaryParent attribute as the owner.
|
||||
types.addLifetime("temp", "_temporaryParent");
|
||||
|
||||
getTemporaryChild: method(function(id) {
|
||||
if (!this._temporaryParent) {
|
||||
// Create an actor to serve as the parent for all temporary children and explicitly
|
||||
// add it as a child of this actor.
|
||||
this._temporaryParent = this.manage(new Actor(this.conn));
|
||||
}
|
||||
return new ChildActor(this.conn, id);
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
getTemporaryChild: {
|
||||
request: { id: Arg(0) },
|
||||
response: {
|
||||
child: RetVal("temp:childActor") // use the lifetime name here to specify the expected lifetime.
|
||||
child: RetVal("temp:childActor") // use the lifetime name here to specify the expected lifetime.
|
||||
}
|
||||
});
|
||||
},
|
||||
clearTemporaryChildren: {
|
||||
oneway: true
|
||||
}
|
||||
}
|
||||
|
||||
clearTemporaryChildren: method(function(id) {
|
||||
if (this._temporaryParent) {
|
||||
this._temporaryParent.destroy();
|
||||
delete this._temporaryParent;
|
||||
}
|
||||
});
|
||||
// implementation:
|
||||
getTemporaryChild: function (id) {
|
||||
if (!this._temporaryParent) {
|
||||
// Create an actor to serve as the parent for all temporary children and explicitly
|
||||
// add it as a child of this actor.
|
||||
this._temporaryParent = this.manage(new Actor(this.conn));
|
||||
}
|
||||
return new ChildActor(this.conn, id);
|
||||
}
|
||||
|
||||
clearTemporaryChildren: function () {
|
||||
if (this._temporaryParent) {
|
||||
this._temporaryParent.destroy();
|
||||
delete this._temporaryParent;
|
||||
}
|
||||
}
|
||||
|
||||
This will require some matching work on the front:
|
||||
|
||||
getTemporaryChild: protocol.custom(function(id) {
|
||||
getTemporaryChild: protocol.custom(function (id) {
|
||||
if (!this._temporaryParent) {
|
||||
this._temporaryParent = this.manage(new Front(this.client));
|
||||
}
|
||||
@ -529,7 +640,7 @@ This will require some matching work on the front:
|
||||
impl: "_getTemporaryChild"
|
||||
}),
|
||||
|
||||
clearTemporaryChildren: protocol.custom(function(id) {
|
||||
clearTemporaryChildren: protocol.custom(function (id) {
|
||||
if (this._temporaryParent) {
|
||||
this._temporaryParent.destroy();
|
||||
delete this._temporaryParent;
|
||||
@ -544,12 +655,18 @@ Telemetry
|
||||
|
||||
You can specify a telemetry probe id in your method spec:
|
||||
|
||||
echo: method(function(str) {
|
||||
return str;
|
||||
}, {
|
||||
// spec:
|
||||
methods: {
|
||||
echo: {
|
||||
request: { str: Arg(0) },
|
||||
response: { str: RetVal() },
|
||||
telemetry: "ECHO"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// implementation:
|
||||
echo: function (str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
... and the time to execute that request will be included as a telemetry probe.
|
||||
|
@ -400,6 +400,11 @@ var DebuggerServer = {
|
||||
type: { global: true }
|
||||
});
|
||||
}
|
||||
this.registerModule("devtools/server/actors/addons", {
|
||||
prefix: "addons",
|
||||
constructor: "AddonsActor",
|
||||
type: { global: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/webapps", {
|
||||
prefix: "webapps",
|
||||
constructor: "WebappsActor",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user