mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Merge mozilla-central to mozilla-inbound
--HG-- rename : toolkit/components/prompts/test/test_bug625187.html => toolkit/components/prompts/test/test_subresources_prompts.html
This commit is contained in:
commit
06a7fd0491
@ -110,6 +110,7 @@ devtools/client/webide/**
|
||||
devtools/server/**
|
||||
!devtools/server/actors/webbrowser.js
|
||||
!devtools/server/actors/styles.js
|
||||
!devtools/server/actors/string.js
|
||||
devtools/shared/*.js
|
||||
!devtools/shared/css-lexer.js
|
||||
!devtools/shared/defer.js
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1464091834000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1464967719000">
|
||||
<emItems>
|
||||
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
@ -363,6 +363,15 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i1227" id="{A34CAF42-A3E3-11E5-945F-18C31D5D46B0}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
<pref>security.csp.enable</pref>
|
||||
<pref>security.fileuri.strict_origin_policy</pref>
|
||||
<pref>security.mixed_content.block_active_content</pref>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i1129" id="youtubeunblocker__web@unblocker.yt">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
@ -484,6 +493,12 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i1229" id="/^.*@unblocker\.yt$/">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i404" id="{a9bb9fa0-4122-4c75-bd9a-bc27db3f9155}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
@ -978,7 +993,9 @@
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i1222" id="tmbepff@trendmicro.com">
|
||||
<versionRange minVersion="0" maxVersion="9.2.0.1024" severity="1">
|
||||
<versionRange minVersion="0" maxVersion="9.1.0.1035" severity="1">
|
||||
</versionRange>
|
||||
<versionRange minVersion="9.2" maxVersion="9.2.0.1023" severity="1">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
@ -1652,6 +1669,12 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i1228" id="unblocker30__web@unblocker.yt">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i692" id="/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
@ -3478,6 +3501,18 @@
|
||||
<match name="filename" exp="npqtplugin\.dll" /> <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="2"></versionRange>
|
||||
<infoURL>https://support.apple.com/en-us/HT205771</infoURL>
|
||||
</pluginItem>
|
||||
<pluginItem os="Linux" blockID="p1224">
|
||||
<match name="filename" exp="libflashplayer\.so" /> <versionRange minVersion="11.2.202.577" maxVersion="11.2.202.616" severity="0" vulnerabilitystatus="1"></versionRange>
|
||||
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
|
||||
</pluginItem>
|
||||
<pluginItem blockID="p1225">
|
||||
<match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="18.0.0.333" maxVersion="18.0.0.343" severity="0" vulnerabilitystatus="1"></versionRange>
|
||||
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
|
||||
</pluginItem>
|
||||
<pluginItem blockID="p1226">
|
||||
<match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="21.0.0.197" maxVersion="21.0.0.226" severity="0" vulnerabilitystatus="1"></versionRange>
|
||||
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
|
||||
</pluginItem>
|
||||
</pluginItems>
|
||||
|
||||
<gfxItems>
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
var gFxAccounts = {
|
||||
|
||||
PREF_SYNC_START_DOORHANGER: "services.sync.ui.showSyncStartDoorhanger",
|
||||
DOORHANGER_ACTIVATE_DELAY_MS: 5000,
|
||||
SYNC_MIGRATION_NOTIFICATION_TITLE: "fxa-migration",
|
||||
|
||||
_initialized: false,
|
||||
@ -23,13 +21,11 @@ var gFxAccounts = {
|
||||
delete this.topics;
|
||||
return this.topics = [
|
||||
"weave:service:ready",
|
||||
"weave:service:sync:start",
|
||||
"weave:service:login:error",
|
||||
"weave:service:setup-complete",
|
||||
"weave:ui:login:error",
|
||||
"fxa-migration:state-changed",
|
||||
this.FxAccountsCommon.ONLOGIN_NOTIFICATION,
|
||||
this.FxAccountsCommon.ONVERIFIED_NOTIFICATION,
|
||||
this.FxAccountsCommon.ONLOGOUT_NOTIFICATION,
|
||||
this.FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
|
||||
];
|
||||
@ -82,11 +78,6 @@ var gFxAccounts = {
|
||||
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
|
||||
},
|
||||
|
||||
get isActiveWindow() {
|
||||
let fm = Services.focus;
|
||||
return fm.activeWindow == window;
|
||||
},
|
||||
|
||||
init: function () {
|
||||
// Bail out if we're already initialized and for pop-up windows.
|
||||
if (this._initialized || !window.toolbar.visible) {
|
||||
@ -97,7 +88,6 @@ var gFxAccounts = {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
}
|
||||
|
||||
addEventListener("activate", this);
|
||||
gNavToolbox.addEventListener("customizationstarting", this);
|
||||
gNavToolbox.addEventListener("customizationending", this);
|
||||
|
||||
@ -121,12 +111,6 @@ var gFxAccounts = {
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
switch (topic) {
|
||||
case this.FxAccountsCommon.ONVERIFIED_NOTIFICATION:
|
||||
Services.prefs.setBoolPref(this.PREF_SYNC_START_DOORHANGER, true);
|
||||
break;
|
||||
case "weave:service:sync:start":
|
||||
this.onSyncStart();
|
||||
break;
|
||||
case "fxa-migration:state-changed":
|
||||
this.onMigrationStateChanged(data, subject);
|
||||
break;
|
||||
@ -139,23 +123,6 @@ var gFxAccounts = {
|
||||
}
|
||||
},
|
||||
|
||||
onSyncStart: function () {
|
||||
if (!this.isActiveWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
let showDoorhanger = false;
|
||||
|
||||
try {
|
||||
showDoorhanger = Services.prefs.getBoolPref(this.PREF_SYNC_START_DOORHANGER);
|
||||
} catch (e) { /* The pref might not exist. */ }
|
||||
|
||||
if (showDoorhanger) {
|
||||
Services.prefs.clearUserPref(this.PREF_SYNC_START_DOORHANGER);
|
||||
this.showSyncStartedDoorhanger();
|
||||
}
|
||||
},
|
||||
|
||||
onMigrationStateChanged: function () {
|
||||
// Since we nuked most of the migration code, this notification will fire
|
||||
// once after legacy Sync has been disconnected (and should never fire
|
||||
@ -199,33 +166,8 @@ var gFxAccounts = {
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
if (event.type == "activate") {
|
||||
// Our window might have been in the background while we received the
|
||||
// sync:start notification. If still needed, show the doorhanger after
|
||||
// a short delay. Without this delay the doorhanger would not show up
|
||||
// or with a too small delay show up while we're still animating the
|
||||
// window.
|
||||
setTimeout(() => this.onSyncStart(), this.DOORHANGER_ACTIVATE_DELAY_MS);
|
||||
} else {
|
||||
this._inCustomizationMode = event.type == "customizationstarting";
|
||||
this.updateAppMenuItem();
|
||||
}
|
||||
},
|
||||
|
||||
showDoorhanger: function (id) {
|
||||
let panel = document.getElementById(id);
|
||||
let anchor = document.getElementById("PanelUI-menu-button");
|
||||
|
||||
let iconAnchor =
|
||||
document.getAnonymousElementByAttribute(anchor, "class",
|
||||
"toolbarbutton-icon");
|
||||
|
||||
panel.hidden = false;
|
||||
panel.openPopup(iconAnchor || anchor, "bottomcenter topright");
|
||||
},
|
||||
|
||||
showSyncStartedDoorhanger: function () {
|
||||
this.showDoorhanger("sync-start-panel");
|
||||
this._inCustomizationMode = event.type == "customizationstarting";
|
||||
this.updateAppMenuItem();
|
||||
},
|
||||
|
||||
updateUI: function () {
|
||||
|
@ -3228,6 +3228,7 @@ function getPEMString(cert)
|
||||
var PrintPreviewListener = {
|
||||
_printPreviewTab: null,
|
||||
_tabBeforePrintPreview: null,
|
||||
_simplifyPageTab: null,
|
||||
|
||||
getPrintPreviewBrowser: function () {
|
||||
if (!this._printPreviewTab) {
|
||||
@ -3241,10 +3242,19 @@ var PrintPreviewListener = {
|
||||
}
|
||||
return gBrowser.getBrowserForTab(this._printPreviewTab);
|
||||
},
|
||||
createSimplifiedBrowser: function () {
|
||||
this._simplifyPageTab = gBrowser.loadOneTab("about:blank",
|
||||
{ inBackground: true });
|
||||
return this.getSimplifiedSourceBrowser();
|
||||
},
|
||||
getSourceBrowser: function () {
|
||||
return this._tabBeforePrintPreview ?
|
||||
this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
|
||||
},
|
||||
getSimplifiedSourceBrowser: function () {
|
||||
return this._simplifyPageTab ?
|
||||
gBrowser.getBrowserForTab(this._simplifyPageTab) : null;
|
||||
},
|
||||
getNavToolbox: function () {
|
||||
return gNavToolbox;
|
||||
},
|
||||
@ -3257,6 +3267,10 @@ var PrintPreviewListener = {
|
||||
this._tabBeforePrintPreview = null;
|
||||
gInPrintPreviewMode = false;
|
||||
this._toggleAffectedChrome();
|
||||
if (this._simplifyPageTab) {
|
||||
gBrowser.removeTab(this._simplifyPageTab);
|
||||
this._simplifyPageTab = null;
|
||||
}
|
||||
gBrowser.removeTab(this._printPreviewTab);
|
||||
this._printPreviewTab = null;
|
||||
},
|
||||
@ -7162,13 +7176,8 @@ var gIdentityHandler = {
|
||||
|
||||
let uri = gBrowser.currentURI;
|
||||
|
||||
for (let permission of SitePermissions.listPermissions()) {
|
||||
let state = SitePermissions.get(uri, permission);
|
||||
|
||||
if (state == SitePermissions.UNKNOWN)
|
||||
continue;
|
||||
|
||||
let item = this._createPermissionItem(permission, state);
|
||||
for (let permission of SitePermissions.getPermissionsByURI(uri)) {
|
||||
let item = this._createPermissionItem(permission);
|
||||
this._permissionList.appendChild(item);
|
||||
}
|
||||
},
|
||||
@ -7180,26 +7189,26 @@ var gIdentityHandler = {
|
||||
SitePermissions.set(gBrowser.currentURI, aPermission, aState);
|
||||
},
|
||||
|
||||
_createPermissionItem: function (aPermission, aState) {
|
||||
_createPermissionItem: function (aPermission) {
|
||||
let menulist = document.createElement("menulist");
|
||||
let menupopup = document.createElement("menupopup");
|
||||
for (let state of SitePermissions.getAvailableStates(aPermission)) {
|
||||
for (let state of aPermission.availableStates) {
|
||||
let menuitem = document.createElement("menuitem");
|
||||
menuitem.setAttribute("value", state);
|
||||
menuitem.setAttribute("label", SitePermissions.getStateLabel(aPermission, state));
|
||||
menuitem.setAttribute("value", state.id);
|
||||
menuitem.setAttribute("label", state.label);
|
||||
menupopup.appendChild(menuitem);
|
||||
}
|
||||
menulist.appendChild(menupopup);
|
||||
menulist.setAttribute("value", aState);
|
||||
menulist.setAttribute("value", aPermission.state);
|
||||
menulist.setAttribute("oncommand", "gIdentityHandler.setPermission('" +
|
||||
aPermission + "', this.value)");
|
||||
menulist.setAttribute("id", "identity-popup-permission:" + aPermission);
|
||||
aPermission.id + "', this.value)");
|
||||
menulist.setAttribute("id", "identity-popup-permission:" + aPermission.id);
|
||||
|
||||
let label = document.createElement("label");
|
||||
label.setAttribute("flex", "1");
|
||||
label.setAttribute("class", "identity-popup-permission-label");
|
||||
label.setAttribute("control", menulist.getAttribute("id"));
|
||||
label.textContent = SitePermissions.getPermissionLabel(aPermission);
|
||||
label.textContent = aPermission.label;
|
||||
|
||||
let container = document.createElement("hbox");
|
||||
container.setAttribute("align", "center");
|
||||
|
@ -421,21 +421,6 @@
|
||||
</hbox>
|
||||
</panel>
|
||||
|
||||
<!-- Sync Panel -->
|
||||
<panel id="sync-start-panel" class="sync-panel" type="arrow" hidden="true"
|
||||
noautofocus="true" onclick="this.hidePopup();"
|
||||
flip="slide">
|
||||
<hbox class="sync-panel-outer">
|
||||
<image class="sync-panel-icon"/>
|
||||
<vbox class="sync-panel-inner">
|
||||
<description id="sync-start-panel-title"
|
||||
value="&syncStartPanel2.heading;"/>
|
||||
<description id="sync-start-panel-subtitle"
|
||||
value="&syncStartPanel2.subTitle;"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</panel>
|
||||
|
||||
<!-- Bookmarks and history tooltip -->
|
||||
<tooltip id="bhTooltip"/>
|
||||
|
||||
|
@ -42,7 +42,6 @@ function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
|
||||
yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm));
|
||||
is(bookmarks.length, 1, "Only one bookmark should exist");
|
||||
is(bookmarkStar.getAttribute("starred"), "true", "Page is starred");
|
||||
is(bookmarkPanel.state, "open", "Check that panel state is 'open'");
|
||||
is(bookmarkPanelTitle.value,
|
||||
isNewBookmark ?
|
||||
gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
|
||||
|
@ -230,6 +230,36 @@ let tabListener = {
|
||||
|
||||
this.emit("tab-removed", {tab, tabId, windowId, isWindowClosing});
|
||||
},
|
||||
|
||||
tabReadyInitialized: false,
|
||||
tabReadyPromises: new WeakMap(),
|
||||
|
||||
initTabReady() {
|
||||
if (!this.tabReadyInitialized) {
|
||||
AllWindowEvents.addListener("progress", this);
|
||||
|
||||
this.tabReadyInitialized = true;
|
||||
}
|
||||
},
|
||||
|
||||
onLocationChange(browser, webProgress, request, locationURI, flags) {
|
||||
if (webProgress.isTopLevel) {
|
||||
let gBrowser = browser.ownerDocument.defaultView.gBrowser;
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
|
||||
let deferred = this.tabReadyPromises.get(tab);
|
||||
if (deferred) {
|
||||
deferred.resolve(tab);
|
||||
this.tabReadyPromises.delete(tab);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
awaitTabReady(tab) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.tabReadyPromises.set(tab, {resolve, reject});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
extensions.registerSchemaAPI("tabs", null, (extension, context) => {
|
||||
@ -453,39 +483,6 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
|
||||
|
||||
create: function(createProperties) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function createInWindow(window) {
|
||||
let url;
|
||||
|
||||
if (createProperties.url !== null) {
|
||||
url = context.uri.resolve(createProperties.url);
|
||||
|
||||
if (!context.checkLoadURL(url, {dontReportErrors: true})) {
|
||||
reject({message: `URL not allowed: ${url}`});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL);
|
||||
|
||||
let active = true;
|
||||
if (createProperties.active !== null) {
|
||||
active = createProperties.active;
|
||||
}
|
||||
if (active) {
|
||||
window.gBrowser.selectedTab = tab;
|
||||
}
|
||||
|
||||
if (createProperties.index !== null) {
|
||||
window.gBrowser.moveTabTo(tab, createProperties.index);
|
||||
}
|
||||
|
||||
if (createProperties.pinned) {
|
||||
window.gBrowser.pinTab(tab);
|
||||
}
|
||||
|
||||
resolve(TabManager.convert(extension, tab));
|
||||
}
|
||||
|
||||
let window = createProperties.windowId !== null ?
|
||||
WindowManager.getWindow(createProperties.windowId, context) :
|
||||
WindowManager.topWindow;
|
||||
@ -495,12 +492,55 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
|
||||
return;
|
||||
}
|
||||
Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
|
||||
createInWindow(window);
|
||||
resolve(window);
|
||||
};
|
||||
Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
|
||||
} else {
|
||||
createInWindow(window);
|
||||
resolve(window);
|
||||
}
|
||||
}).then(window => {
|
||||
let url;
|
||||
|
||||
if (createProperties.url !== null) {
|
||||
url = context.uri.resolve(createProperties.url);
|
||||
|
||||
if (!context.checkLoadURL(url, {dontReportErrors: true})) {
|
||||
return Promise.reject({message: `Illegal URL: ${url}`});
|
||||
}
|
||||
}
|
||||
|
||||
tabListener.initTabReady();
|
||||
let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL);
|
||||
|
||||
let active = true;
|
||||
if (createProperties.active !== null) {
|
||||
active = createProperties.active;
|
||||
}
|
||||
if (active) {
|
||||
window.gBrowser.selectedTab = tab;
|
||||
}
|
||||
|
||||
if (createProperties.index !== null) {
|
||||
window.gBrowser.moveTabTo(tab, createProperties.index);
|
||||
}
|
||||
|
||||
if (createProperties.pinned) {
|
||||
window.gBrowser.pinTab(tab);
|
||||
}
|
||||
|
||||
if (!createProperties.url || createProperties.url.startsWith("about:")) {
|
||||
// We can't wait for a location change event for about:newtab,
|
||||
// since it may be pre-rendered, in which case its initial
|
||||
// location change event has already fired.
|
||||
return tab;
|
||||
}
|
||||
|
||||
// Wait for the first location change event, so that operations
|
||||
// like `executeScript` are dispatched to the inner window that
|
||||
// contains the URL we're attempting to load.
|
||||
return tabListener.awaitTabReady(tab);
|
||||
}).then(tab => {
|
||||
return TabManager.convert(extension, tab);
|
||||
});
|
||||
},
|
||||
|
||||
@ -530,7 +570,7 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
|
||||
let url = context.uri.resolve(updateProperties.url);
|
||||
|
||||
if (!context.checkLoadURL(url, {dontReportErrors: true})) {
|
||||
return Promise.reject({message: `URL not allowed: ${url}`});
|
||||
return Promise.reject({message: `Illegal URL: ${url}`});
|
||||
}
|
||||
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
|
@ -558,7 +558,7 @@ global.TabManager = {
|
||||
},
|
||||
|
||||
handleWindowOpen(window) {
|
||||
if (window.arguments[0] instanceof window.XULElement) {
|
||||
if (window.arguments && window.arguments[0] instanceof window.XULElement) {
|
||||
// If the first window argument is a XUL element, it means the
|
||||
// window is about to adopt a tab from another window to replace its
|
||||
// initial tab.
|
||||
|
@ -11,7 +11,7 @@
|
||||
"choices": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^\\s*(Alt|Ctrl|Command|MacCtr)\\s*\\+\\s*(Shift\\s*\\+\\s*)?([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right)\\s*$"
|
||||
"pattern": "^\\s*(Alt|Ctrl|Command|MacCtrl)\\s*\\+\\s*(Shift\\s*\\+\\s*)?([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right)\\s*$"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -205,8 +205,6 @@ add_task(function* testTabSwitchContext() {
|
||||
});
|
||||
});
|
||||
};
|
||||
let tabLoadPromise;
|
||||
|
||||
return [
|
||||
expect => {
|
||||
browser.test.log("Initial state. No icon visible.");
|
||||
@ -225,16 +223,13 @@ add_task(function* testTabSwitchContext() {
|
||||
expect => {
|
||||
browser.test.log("Create a new tab. No icon visible.");
|
||||
browser.tabs.create({active: true, url: "about:blank?0"}, tab => {
|
||||
tabLoadPromise = promiseTabLoad({url: "about:blank?0", id: tab.id});
|
||||
tabs.push(tab.id);
|
||||
expect(null);
|
||||
});
|
||||
},
|
||||
expect => {
|
||||
browser.test.log("Await tab load. No icon visible.");
|
||||
tabLoadPromise.then(() => {
|
||||
expect(null);
|
||||
});
|
||||
expect(null);
|
||||
},
|
||||
expect => {
|
||||
browser.test.log("Change properties. Expect new properties.");
|
||||
|
@ -101,12 +101,11 @@ add_task(function* () {
|
||||
|
||||
browser.test.log(`Testing tabs.create(${JSON.stringify(test.create)}), expecting ${JSON.stringify(test.result)}`);
|
||||
|
||||
let tabId;
|
||||
let updatedPromise = new Promise(resolve => {
|
||||
let onUpdated = (changedTabId, changed) => {
|
||||
if (changedTabId === tabId && changed.url) {
|
||||
if (changed.url) {
|
||||
browser.tabs.onUpdated.removeListener(onUpdated);
|
||||
resolve(changed.url);
|
||||
resolve({tabId: changedTabId, url: changed.url});
|
||||
}
|
||||
};
|
||||
browser.tabs.onUpdated.addListener(onUpdated);
|
||||
@ -120,6 +119,7 @@ add_task(function* () {
|
||||
browser.tabs.onCreated.addListener(onCreated);
|
||||
});
|
||||
|
||||
let tabId;
|
||||
Promise.all([
|
||||
browser.tabs.create(test.create),
|
||||
createdPromise,
|
||||
@ -136,8 +136,9 @@ add_task(function* () {
|
||||
}
|
||||
|
||||
return updatedPromise;
|
||||
}).then(url => {
|
||||
browser.test.assertEq(expected.url, url, `Expected value for tab.url`);
|
||||
}).then(updated => {
|
||||
browser.test.assertEq(tabId, updated.tabId, `Expected value for tab.id`);
|
||||
browser.test.assertEq(expected.url, updated.url, `Expected value for tab.url`);
|
||||
|
||||
return browser.tabs.remove(tabId);
|
||||
}).then(() => {
|
||||
|
@ -13,7 +13,7 @@ function* testTabsCreateInvalidURL(tabsCreateURL) {
|
||||
browser.test.onMessage.addListener((msg, tabsCreateURL) => {
|
||||
browser.tabs.create({url: tabsCreateURL}, (tab) => {
|
||||
browser.test.assertEq(undefined, tab, "on error tab should be undefined");
|
||||
browser.test.assertTrue(/URL not allowed/.test(browser.runtime.lastError.message),
|
||||
browser.test.assertTrue(/Illegal URL/.test(browser.runtime.lastError.message),
|
||||
"runtime.lastError should report the expected error message");
|
||||
|
||||
// Remove the opened tab is any.
|
||||
|
@ -12,20 +12,7 @@ add_task(function* testDetectLanguage() {
|
||||
const BASE_PATH = "browser/browser/components/extensions/test/browser";
|
||||
|
||||
function loadTab(url) {
|
||||
let tabId;
|
||||
let awaitUpdated = new Promise(resolve => {
|
||||
browser.tabs.onUpdated.addListener(function onUpdated(changedTabId, changed, tab) {
|
||||
if (changedTabId === tabId && changed.url) {
|
||||
browser.tabs.onUpdated.removeListener(onUpdated);
|
||||
resolve(tab);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return browser.tabs.create({url}).then(tab => {
|
||||
tabId = tab.id;
|
||||
return awaitUpdated;
|
||||
});
|
||||
return browser.tabs.create({url});
|
||||
}
|
||||
|
||||
loadTab(`http://example.co.jp/${BASE_PATH}/file_language_ja.html`).then(tab => {
|
||||
|
@ -118,6 +118,14 @@ add_task(function* testExecuteScript() {
|
||||
browser.test.assertEq("http://mochi.test:8888/", result, "Result for frameId[1] is correct");
|
||||
}),
|
||||
|
||||
browser.tabs.create({url: "http://example.com/"}).then(tab => {
|
||||
return browser.tabs.executeScript(tab.id, {code: "location.href"}).then(result => {
|
||||
browser.test.assertEq("http://example.com/", result, "Script executed correctly in new tab");
|
||||
|
||||
return browser.tabs.remove(tab.id);
|
||||
});
|
||||
}),
|
||||
|
||||
new Promise(resolve => {
|
||||
browser.runtime.onMessage.addListener(message => {
|
||||
browser.test.assertEq("script ran", message, "Expected runtime message");
|
||||
@ -135,7 +143,7 @@ add_task(function* testExecuteScript() {
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["http://mochi.test/", "webNavigation"],
|
||||
"permissions": ["http://mochi.test/", "http://example.com/", "webNavigation"],
|
||||
},
|
||||
|
||||
background,
|
||||
|
@ -37,7 +37,7 @@ function* testTabsUpdateURL(existentTabURL, tabsUpdateURL, isErrorExpected) {
|
||||
if (!isErrorExpected) {
|
||||
browser.test.fails(`tabs.update with URL ${tabsUpdateURL} should not be rejected`);
|
||||
} else {
|
||||
browser.test.assertTrue(/^URL not allowed/.test(error.message),
|
||||
browser.test.assertTrue(/^Illegal URL/.test(error.message),
|
||||
"tabs.update should be rejected with the expected error message");
|
||||
}
|
||||
};
|
||||
|
@ -2,9 +2,13 @@
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
SimpleTest.requestCompleteLog();
|
||||
|
||||
add_task(function* testWindowsEvents() {
|
||||
function background() {
|
||||
browser.windows.onCreated.addListener(function listener(window) {
|
||||
browser.test.log(`onCreated: windowId=${window.id}`);
|
||||
|
||||
browser.test.assertTrue(Number.isInteger(window.id),
|
||||
"Window object's id is an integer");
|
||||
browser.test.assertEq("normal", window.type,
|
||||
@ -14,22 +18,28 @@ add_task(function* testWindowsEvents() {
|
||||
|
||||
let lastWindowId;
|
||||
browser.windows.onFocusChanged.addListener(function listener(windowId) {
|
||||
browser.test.log(`onFocusChange: windowId=${windowId} lastWindowId=${lastWindowId}`);
|
||||
|
||||
browser.test.assertTrue(lastWindowId !== windowId,
|
||||
"onFocusChanged fired once for the given window");
|
||||
lastWindowId = windowId;
|
||||
|
||||
browser.test.assertTrue(Number.isInteger(windowId),
|
||||
"windowId is an integer");
|
||||
|
||||
browser.windows.getLastFocused().then(window => {
|
||||
browser.test.assertEq(windowId, window.id,
|
||||
"Last focused window has the correct id");
|
||||
browser.test.sendMessage(`window-focus-changed-${windowId}`);
|
||||
browser.test.sendMessage(`window-focus-changed`, window.id);
|
||||
});
|
||||
});
|
||||
|
||||
browser.windows.onRemoved.addListener(function listener(windowId) {
|
||||
browser.test.log(`onRemoved: windowId=${windowId}`);
|
||||
|
||||
browser.test.assertTrue(Number.isInteger(windowId),
|
||||
"windowId is an integer");
|
||||
browser.test.sendMessage(`window-removed-${windowId}`);
|
||||
browser.test.sendMessage(`window-removed`, windowId);
|
||||
browser.test.notifyPass("windows.events");
|
||||
});
|
||||
|
||||
@ -44,27 +54,48 @@ add_task(function* testWindowsEvents() {
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let currentWindow = window;
|
||||
let currentWindowId = WindowManager.getId(currentWindow);
|
||||
info(`Current window ID: ${currentWindowId}`);
|
||||
|
||||
info(`Create browser window 1`);
|
||||
let win1 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let win1Id = yield extension.awaitMessage("window-created");
|
||||
yield extension.awaitMessage(`window-focus-changed-${win1Id}`);
|
||||
info(`Window 1 ID: ${win1Id}`);
|
||||
|
||||
let winId = yield extension.awaitMessage(`window-focus-changed`);
|
||||
is(winId, win1Id, "Got focus change event for the correct window ID.");
|
||||
|
||||
info(`Create browser window 2`);
|
||||
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let win2Id = yield extension.awaitMessage("window-created");
|
||||
yield extension.awaitMessage(`window-focus-changed-${win2Id}`);
|
||||
info(`Window 2 ID: ${win2Id}`);
|
||||
|
||||
winId = yield extension.awaitMessage(`window-focus-changed`);
|
||||
is(winId, win2Id, "Got focus change event for the correct window ID.");
|
||||
|
||||
info(`Focus browser window 1`);
|
||||
yield focusWindow(win1);
|
||||
yield extension.awaitMessage(`window-focus-changed-${win1Id}`);
|
||||
|
||||
winId = yield extension.awaitMessage(`window-focus-changed`);
|
||||
is(winId, win1Id, "Got focus change event for the correct window ID.");
|
||||
|
||||
info(`Close browser window 2`);
|
||||
yield BrowserTestUtils.closeWindow(win2);
|
||||
yield extension.awaitMessage(`window-removed-${win2Id}`);
|
||||
|
||||
winId = yield extension.awaitMessage(`window-removed`);
|
||||
is(winId, win2Id, "Got removed event for the correct window ID.");
|
||||
|
||||
info(`Close browser window 1`);
|
||||
yield BrowserTestUtils.closeWindow(win1);
|
||||
yield extension.awaitMessage(`window-removed-${win1Id}`);
|
||||
|
||||
yield extension.awaitMessage(`window-focus-changed-${currentWindowId}`);
|
||||
winId = yield extension.awaitMessage(`window-removed`);
|
||||
is(winId, win1Id, "Got removed event for the correct window ID.");
|
||||
|
||||
winId = yield extension.awaitMessage(`window-focus-changed`);
|
||||
is(winId, currentWindowId, "Got focus change event for the correct window ID.");
|
||||
|
||||
yield extension.awaitFinish("windows.events");
|
||||
yield extension.unload();
|
||||
});
|
||||
|
@ -309,6 +309,9 @@ BrowserGlue.prototype = {
|
||||
case "weave:service:ready":
|
||||
this._setSyncAutoconnectDelay();
|
||||
break;
|
||||
case "fxaccounts:onverified":
|
||||
this._showSyncStartedDoorhanger();
|
||||
break;
|
||||
case "weave:engine:clients:display-uri":
|
||||
this._onDisplaySyncURI(subject);
|
||||
break;
|
||||
@ -526,6 +529,7 @@ BrowserGlue.prototype = {
|
||||
os.addObserver(this, "browser-lastwindow-close-granted", false);
|
||||
}
|
||||
os.addObserver(this, "weave:service:ready", false);
|
||||
os.addObserver(this, "fxaccounts:onverified", false);
|
||||
os.addObserver(this, "weave:engine:clients:display-uri", false);
|
||||
os.addObserver(this, "session-save", false);
|
||||
os.addObserver(this, "places-init-complete", false);
|
||||
@ -591,6 +595,7 @@ BrowserGlue.prototype = {
|
||||
os.removeObserver(this, "browser-lastwindow-close-granted");
|
||||
}
|
||||
os.removeObserver(this, "weave:service:ready");
|
||||
os.removeObserver(this, "fxaccounts:onverified");
|
||||
os.removeObserver(this, "weave:engine:clients:display-uri");
|
||||
os.removeObserver(this, "session-save");
|
||||
if (this._bookmarksBackupIdleTime) {
|
||||
@ -1895,6 +1900,19 @@ BrowserGlue.prototype = {
|
||||
notification.persistence = -1; // Until user closes it
|
||||
},
|
||||
|
||||
_showSyncStartedDoorhanger: function () {
|
||||
let bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
|
||||
let title = bundle.GetStringFromName("syncStartNotification.title");
|
||||
let body = bundle.GetStringFromName("syncStartNotification.body");
|
||||
|
||||
let clickCallback = (subject, topic, data) => {
|
||||
if (topic != "alertclickcallback")
|
||||
return;
|
||||
this._openPreferences("sync");
|
||||
}
|
||||
AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
|
||||
},
|
||||
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 38;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||
|
@ -1,3 +1,3 @@
|
||||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.5.276
|
||||
Current extension version is: 1.5.281
|
||||
|
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
|
||||
// Use strict in our context only - users might not want it
|
||||
'use strict';
|
||||
|
||||
var pdfjsVersion = '1.5.276';
|
||||
var pdfjsBuild = '41f978c';
|
||||
var pdfjsVersion = '1.5.281';
|
||||
var pdfjsBuild = '5a5bb99';
|
||||
|
||||
var pdfjsFilePath =
|
||||
typeof document !== 'undefined' && document.currentScript ?
|
||||
|
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {}));
|
||||
// Use strict in our context only - users might not want it
|
||||
'use strict';
|
||||
|
||||
var pdfjsVersion = '1.5.276';
|
||||
var pdfjsBuild = '41f978c';
|
||||
var pdfjsVersion = '1.5.281';
|
||||
var pdfjsBuild = '5a5bb99';
|
||||
|
||||
var pdfjsFilePath =
|
||||
typeof document !== 'undefined' && document.currentScript ?
|
||||
@ -37210,7 +37210,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
case OPS.setFont:
|
||||
flushTextContentItem();
|
||||
textState.fontSize = args[1];
|
||||
next(handleSetFont(args[0].name));
|
||||
next(handleSetFont(args[0].name, null));
|
||||
return;
|
||||
case OPS.setTextRise:
|
||||
flushTextContentItem();
|
||||
@ -37416,21 +37416,17 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
var dictName = args[0];
|
||||
var extGState = resources.get('ExtGState');
|
||||
|
||||
if (!isDict(extGState) || !extGState.has(dictName.name)) {
|
||||
if (!isDict(extGState) || !isName(dictName)) {
|
||||
break;
|
||||
}
|
||||
|
||||
var gsStateMap = extGState.get(dictName.name);
|
||||
var gsStateFont = null;
|
||||
for (var key in gsStateMap) {
|
||||
if (key === 'Font') {
|
||||
assert(!gsStateFont);
|
||||
gsStateFont = gsStateMap[key];
|
||||
}
|
||||
var gState = extGState.get(dictName.name);
|
||||
if (!isDict(gState)) {
|
||||
break;
|
||||
}
|
||||
if (gsStateFont) {
|
||||
textState.fontSize = gsStateFont[1];
|
||||
next(handleSetFont(gsStateFont[0]));
|
||||
var gStateFont = gState.get('Font');
|
||||
if (gStateFont) {
|
||||
textState.fontSize = gStateFont[1];
|
||||
next(handleSetFont(null, gStateFont[0]));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -2560,6 +2560,7 @@ exports.binarySearchFirstItem = binarySearchFirstItem;
|
||||
var event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent('find' + e.type, true, true, {
|
||||
query: e.query,
|
||||
phraseSearch: e.phraseSearch,
|
||||
caseSensitive: e.caseSensitive,
|
||||
highlightAll: e.highlightAll,
|
||||
findPrevious: e.findPrevious
|
||||
@ -3010,6 +3011,7 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
this.active = false; // If active, find results will be highlighted.
|
||||
this.pageContents = []; // Stores the text for each page.
|
||||
this.pageMatches = [];
|
||||
this.pageMatchesLength = null;
|
||||
this.matchCount = 0;
|
||||
this.selected = { // Currently selected match.
|
||||
pageIdx: -1,
|
||||
@ -3036,10 +3038,114 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
});
|
||||
},
|
||||
|
||||
// Helper for multiple search - fills matchesWithLength array
|
||||
// and takes into account cases when one search term
|
||||
// include another search term (for example, "tamed tame" or "this is").
|
||||
// Looking for intersecting terms in the 'matches' and
|
||||
// leave elements with a longer match-length.
|
||||
|
||||
_prepareMatches: function PDFFindController_prepareMatches(
|
||||
matchesWithLength, matches, matchesLength) {
|
||||
|
||||
function isSubTerm(matchesWithLength, currentIndex) {
|
||||
var currentElem, prevElem, nextElem;
|
||||
currentElem = matchesWithLength[currentIndex];
|
||||
nextElem = matchesWithLength[currentIndex + 1];
|
||||
// checking for cases like "TAMEd TAME"
|
||||
if (currentIndex < matchesWithLength.length - 1 &&
|
||||
currentElem.match === nextElem.match) {
|
||||
currentElem.skipped = true;
|
||||
return true;
|
||||
}
|
||||
// checking for cases like "thIS IS"
|
||||
for (var i = currentIndex - 1; i >= 0; i--) {
|
||||
prevElem = matchesWithLength[i];
|
||||
if (prevElem.skipped) {
|
||||
continue;
|
||||
}
|
||||
if (prevElem.match + prevElem.matchLength < currentElem.match) {
|
||||
break;
|
||||
}
|
||||
if (prevElem.match + prevElem.matchLength >=
|
||||
currentElem.match + currentElem.matchLength) {
|
||||
currentElem.skipped = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var i, len;
|
||||
// Sorting array of objects { match: <match>, matchLength: <matchLength> }
|
||||
// in increasing index first and then the lengths.
|
||||
matchesWithLength.sort(function(a, b) {
|
||||
return a.match === b.match ?
|
||||
a.matchLength - b.matchLength : a.match - b.match;
|
||||
});
|
||||
for (i = 0, len = matchesWithLength.length; i < len; i++) {
|
||||
if (isSubTerm(matchesWithLength, i)) {
|
||||
continue;
|
||||
}
|
||||
matches.push(matchesWithLength[i].match);
|
||||
matchesLength.push(matchesWithLength[i].matchLength);
|
||||
}
|
||||
},
|
||||
|
||||
calcFindPhraseMatch: function PDFFindController_calcFindPhraseMatch(
|
||||
query, pageIndex, pageContent) {
|
||||
var matches = [];
|
||||
var queryLen = query.length;
|
||||
var matchIdx = -queryLen;
|
||||
while (true) {
|
||||
matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
|
||||
if (matchIdx === -1) {
|
||||
break;
|
||||
}
|
||||
matches.push(matchIdx);
|
||||
}
|
||||
this.pageMatches[pageIndex] = matches;
|
||||
},
|
||||
|
||||
calcFindWordMatch: function PDFFindController_calcFindWordMatch(
|
||||
query, pageIndex, pageContent) {
|
||||
var matchesWithLength = [];
|
||||
// Divide the query into pieces and search for text on each piece.
|
||||
var queryArray = query.match(/\S+/g);
|
||||
var subquery, subqueryLen, matchIdx;
|
||||
for (var i = 0, len = queryArray.length; i < len; i++) {
|
||||
subquery = queryArray[i];
|
||||
subqueryLen = subquery.length;
|
||||
matchIdx = -subqueryLen;
|
||||
while (true) {
|
||||
matchIdx = pageContent.indexOf(subquery, matchIdx + subqueryLen);
|
||||
if (matchIdx === -1) {
|
||||
break;
|
||||
}
|
||||
// Other searches do not, so we store the length.
|
||||
matchesWithLength.push({
|
||||
match: matchIdx,
|
||||
matchLength: subqueryLen,
|
||||
skipped: false
|
||||
});
|
||||
}
|
||||
}
|
||||
// Prepare arrays for store the matches.
|
||||
if (!this.pageMatchesLength) {
|
||||
this.pageMatchesLength = [];
|
||||
}
|
||||
this.pageMatchesLength[pageIndex] = [];
|
||||
this.pageMatches[pageIndex] = [];
|
||||
// Sort matchesWithLength, clean up intersecting terms
|
||||
// and put the result into the two arrays.
|
||||
this._prepareMatches(matchesWithLength, this.pageMatches[pageIndex],
|
||||
this.pageMatchesLength[pageIndex]);
|
||||
},
|
||||
|
||||
calcFindMatch: function PDFFindController_calcFindMatch(pageIndex) {
|
||||
var pageContent = this.normalize(this.pageContents[pageIndex]);
|
||||
var query = this.normalize(this.state.query);
|
||||
var caseSensitive = this.state.caseSensitive;
|
||||
var phraseSearch = this.state.phraseSearch;
|
||||
var queryLen = query.length;
|
||||
|
||||
if (queryLen === 0) {
|
||||
@ -3052,16 +3158,12 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
query = query.toLowerCase();
|
||||
}
|
||||
|
||||
var matches = [];
|
||||
var matchIdx = -queryLen;
|
||||
while (true) {
|
||||
matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
|
||||
if (matchIdx === -1) {
|
||||
break;
|
||||
}
|
||||
matches.push(matchIdx);
|
||||
if (phraseSearch) {
|
||||
this.calcFindPhraseMatch(query, pageIndex, pageContent);
|
||||
} else {
|
||||
this.calcFindWordMatch(query, pageIndex, pageContent);
|
||||
}
|
||||
this.pageMatches[pageIndex] = matches;
|
||||
|
||||
this.updatePage(pageIndex);
|
||||
if (this.resumePageIdx === pageIndex) {
|
||||
this.resumePageIdx = null;
|
||||
@ -3069,8 +3171,8 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
}
|
||||
|
||||
// Update the matches count
|
||||
if (matches.length > 0) {
|
||||
this.matchCount += matches.length;
|
||||
if (this.pageMatches[pageIndex].length > 0) {
|
||||
this.matchCount += this.pageMatches[pageIndex].length;
|
||||
this.updateUIResultsCount();
|
||||
}
|
||||
},
|
||||
@ -3165,6 +3267,7 @@ var PDFFindController = (function PDFFindControllerClosure() {
|
||||
this.resumePageIdx = null;
|
||||
this.pageMatches = [];
|
||||
this.matchCount = 0;
|
||||
this.pageMatchesLength = null;
|
||||
var self = this;
|
||||
|
||||
for (var i = 0; i < numPages; i++) {
|
||||
@ -3980,6 +4083,7 @@ var PDFFindBar = (function PDFFindBarClosure() {
|
||||
type: type,
|
||||
query: this.findField.value,
|
||||
caseSensitive: this.caseSensitive.checked,
|
||||
phraseSearch: true,
|
||||
highlightAll: this.highlightAll.checked,
|
||||
findPrevious: findPrev
|
||||
});
|
||||
@ -4651,6 +4755,13 @@ var PDFLinkService = (function () {
|
||||
setHash: function PDFLinkService_setHash(hash) {
|
||||
if (hash.indexOf('=') >= 0) {
|
||||
var params = parseQueryString(hash);
|
||||
if ('search' in params) {
|
||||
this.eventBus.dispatch('findfromurlhash', {
|
||||
source: this,
|
||||
query: params['search'].replace(/"/g, ''),
|
||||
phraseSearch: (params['phrase'] === 'true')
|
||||
});
|
||||
}
|
||||
// borrowing syntax from "Parameters for Opening PDF Files"
|
||||
if ('nameddest' in params) {
|
||||
if (this.pdfHistory) {
|
||||
@ -5690,7 +5801,8 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
this.divContentDone = true;
|
||||
},
|
||||
|
||||
convertMatches: function TextLayerBuilder_convertMatches(matches) {
|
||||
convertMatches: function TextLayerBuilder_convertMatches(matches,
|
||||
matchesLength) {
|
||||
var i = 0;
|
||||
var iIndex = 0;
|
||||
var bidiTexts = this.textContent.items;
|
||||
@ -5698,7 +5810,9 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
var queryLen = (this.findController === null ?
|
||||
0 : this.findController.state.query.length);
|
||||
var ret = [];
|
||||
|
||||
if (!matches) {
|
||||
return ret;
|
||||
}
|
||||
for (var m = 0, len = matches.length; m < len; m++) {
|
||||
// Calculate the start position.
|
||||
var matchIdx = matches[m];
|
||||
@ -5721,7 +5835,11 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
};
|
||||
|
||||
// Calculate the end position.
|
||||
matchIdx += queryLen;
|
||||
if (matchesLength) { // multiterm search
|
||||
matchIdx += matchesLength[m];
|
||||
} else { // phrase search
|
||||
matchIdx += queryLen;
|
||||
}
|
||||
|
||||
// Somewhat the same array as above, but use > instead of >= to get
|
||||
// the end position right.
|
||||
@ -5863,8 +5981,14 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
|
||||
// Convert the matches on the page controller into the match format
|
||||
// used for the textLayer.
|
||||
this.matches = this.convertMatches(this.findController === null ?
|
||||
[] : (this.findController.pageMatches[this.pageIdx] || []));
|
||||
var pageMatches, pageMatchesLength;
|
||||
if (this.findController !== null) {
|
||||
pageMatches = this.findController.pageMatches[this.pageIdx] || null;
|
||||
pageMatchesLength = (this.findController.pageMatchesLength) ?
|
||||
this.findController.pageMatchesLength[this.pageIdx] || null : null;
|
||||
}
|
||||
|
||||
this.matches = this.convertMatches(pageMatches, pageMatchesLength);
|
||||
this.renderMatches(this.matches);
|
||||
},
|
||||
|
||||
@ -7947,6 +8071,7 @@ var PDFViewerApplication = {
|
||||
eventBus.on('rotateccw', webViewerRotateCcw);
|
||||
eventBus.on('documentproperties', webViewerDocumentProperties);
|
||||
eventBus.on('find', webViewerFind);
|
||||
eventBus.on('findfromurlhash', webViewerFindFromUrlHash);
|
||||
}
|
||||
};
|
||||
|
||||
@ -8436,12 +8561,23 @@ function webViewerDocumentProperties() {
|
||||
function webViewerFind(e) {
|
||||
PDFViewerApplication.findController.executeCommand('find' + e.type, {
|
||||
query: e.query,
|
||||
phraseSearch: e.phraseSearch,
|
||||
caseSensitive: e.caseSensitive,
|
||||
highlightAll: e.highlightAll,
|
||||
findPrevious: e.findPrevious
|
||||
});
|
||||
}
|
||||
|
||||
function webViewerFindFromUrlHash(e) {
|
||||
PDFViewerApplication.findController.executeCommand('find', {
|
||||
query: e.query,
|
||||
phraseSearch: e.phraseSearch,
|
||||
caseSensitive: false,
|
||||
highlightAll: true,
|
||||
findPrevious: false
|
||||
});
|
||||
}
|
||||
|
||||
function webViewerScaleChanging(e) {
|
||||
var appConfig = PDFViewerApplication.appConfig;
|
||||
appConfig.toolbar.zoomOut.disabled = (e.scale === MIN_SCALE);
|
||||
@ -8585,6 +8721,7 @@ window.addEventListener('keydown', function keydown(evt) {
|
||||
if (findState) {
|
||||
PDFViewerApplication.findController.executeCommand('findagain', {
|
||||
query: findState.query,
|
||||
phraseSearch: findState.phraseSearch,
|
||||
caseSensitive: findState.caseSensitive,
|
||||
highlightAll: findState.highlightAll,
|
||||
findPrevious: cmd === 5 || cmd === 12
|
||||
@ -8958,6 +9095,7 @@ Preferences._readFromStorage = function (prefObj) {
|
||||
source: window,
|
||||
type: evt.type.substring('find'.length),
|
||||
query: evt.detail.query,
|
||||
phraseSearch: true,
|
||||
caseSensitive: !!evt.detail.caseSensitive,
|
||||
highlightAll: !!evt.detail.highlightAll,
|
||||
findPrevious: !!evt.detail.findPrevious
|
||||
|
@ -23,3 +23,8 @@ verificationSentTitle = Verification Sent
|
||||
verificationSentBody = A verification link has been sent to %S.
|
||||
verificationNotSentTitle = Unable to Send Verification
|
||||
verificationNotSentBody = We are unable to send a verification mail at this time, please try again later.
|
||||
|
||||
# LOCALIZATION NOTE (syncStartNotification.title, syncStartNotification.body)
|
||||
# These strings are used in a notification shown after Sync is connected.
|
||||
syncStartNotification.title = Sync enabled
|
||||
syncStartNotification.body = Firefox will begin syncing momentarily.
|
||||
|
@ -110,8 +110,6 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||
<!ENTITY fxaSignedIn.tooltip "Open &syncBrand.shortName.label; preferences">
|
||||
<!ENTITY fxaSignInError.label "Reconnect to &syncBrand.shortName.label;">
|
||||
<!ENTITY fxaUnverified.label "Verify Your Account">
|
||||
<!ENTITY syncStartPanel2.heading "&syncBrand.shortName.label; enabled">
|
||||
<!ENTITY syncStartPanel2.subTitle "&brandShortName; will begin syncing momentarily.">
|
||||
|
||||
|
||||
<!ENTITY fullScreenMinimize.tooltip "Minimize">
|
||||
|
@ -16,6 +16,45 @@ this.SitePermissions = {
|
||||
BLOCK: Services.perms.DENY_ACTION,
|
||||
SESSION: Components.interfaces.nsICookiePermission.ACCESS_SESSION,
|
||||
|
||||
/* Returns a list of objects representing all permissions that are currently
|
||||
* set for the given URI. Each object contains the following keys:
|
||||
* - id: the permissionID of the permission
|
||||
* - label: the localized label
|
||||
* - state: a constant representing the current permission state
|
||||
* (e.g. SitePermissions.ALLOW)
|
||||
* - availableStates: an array of all available states for that permission,
|
||||
* represented as objects with the keys:
|
||||
* - id: the state constant
|
||||
* - label: the translated label of that state
|
||||
*/
|
||||
getPermissionsByURI: function (aURI) {
|
||||
if (!this.isSupportedURI(aURI)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let permissions = [];
|
||||
for (let permission of this.listPermissions()) {
|
||||
let state = this.get(aURI, permission);
|
||||
if (state === this.UNKNOWN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let availableStates = this.getAvailableStates(permission).map( state => {
|
||||
return { id: state, label: this.getStateLabel(permission, state) };
|
||||
});
|
||||
let label = this.getPermissionLabel(permission);
|
||||
|
||||
permissions.push({
|
||||
id: permission,
|
||||
label: label,
|
||||
state: state,
|
||||
availableStates: availableStates,
|
||||
});
|
||||
}
|
||||
|
||||
return permissions;
|
||||
},
|
||||
|
||||
/* Returns a boolean indicating whether there are any granted
|
||||
* (meaning allowed or session-allowed) permissions for the given URI.
|
||||
* Will return false for invalid URIs (such as file:// URLs).
|
||||
|
@ -14,6 +14,11 @@ add_task(function* testPermissionsListing() {
|
||||
});
|
||||
|
||||
add_task(function* testHasGrantedPermissions() {
|
||||
// check that it returns false on an invalid URI
|
||||
// like a file URI, which doesn't support site permissions
|
||||
let wrongURI = Services.io.newURI("file:///example.js", null, null)
|
||||
Assert.equal(SitePermissions.hasGrantedPermissions(wrongURI), false);
|
||||
|
||||
let uri = Services.io.newURI("https://example.com", null, null)
|
||||
Assert.equal(SitePermissions.hasGrantedPermissions(uri), false);
|
||||
|
||||
@ -49,3 +54,65 @@ add_task(function* testHasGrantedPermissions() {
|
||||
|
||||
SitePermissions.remove(uri, "pointerLock");
|
||||
});
|
||||
|
||||
add_task(function* testGetPermissionsByURI() {
|
||||
// check that it returns an empty array on an invalid URI
|
||||
// like a file URI, which doesn't support site permissions
|
||||
let wrongURI = Services.io.newURI("file:///example.js", null, null)
|
||||
Assert.deepEqual(SitePermissions.getPermissionsByURI(wrongURI), []);
|
||||
|
||||
let uri = Services.io.newURI("https://example.com", null, null)
|
||||
|
||||
SitePermissions.set(uri, "camera", SitePermissions.ALLOW);
|
||||
SitePermissions.set(uri, "cookie", SitePermissions.SESSION);
|
||||
SitePermissions.set(uri, "popup", SitePermissions.BLOCK);
|
||||
|
||||
let permissions = SitePermissions.getPermissionsByURI(uri);
|
||||
|
||||
let camera = permissions.find(({id}) => id === "camera");
|
||||
Assert.deepEqual(camera, {
|
||||
id: "camera",
|
||||
label: "Use the Camera",
|
||||
state: SitePermissions.ALLOW,
|
||||
availableStates: [
|
||||
{ id: SitePermissions.UNKNOWN, label: "Always Ask" },
|
||||
{ id: SitePermissions.ALLOW, label: "Allow" },
|
||||
{ id: SitePermissions.BLOCK, label: "Block" },
|
||||
]
|
||||
});
|
||||
|
||||
// check that removed permissions (State.UNKNOWN) are skipped
|
||||
SitePermissions.remove(uri, "camera");
|
||||
permissions = SitePermissions.getPermissionsByURI(uri);
|
||||
|
||||
camera = permissions.find(({id}) => id === "camera");
|
||||
Assert.equal(camera, undefined);
|
||||
|
||||
// check that different available state values are represented
|
||||
|
||||
let cookie = permissions.find(({id}) => id === "cookie");
|
||||
Assert.deepEqual(cookie, {
|
||||
id: "cookie",
|
||||
label: "Set Cookies",
|
||||
state: SitePermissions.SESSION,
|
||||
availableStates: [
|
||||
{ id: SitePermissions.ALLOW, label: "Allow" },
|
||||
{ id: SitePermissions.SESSION, label: "Allow for Session" },
|
||||
{ id: SitePermissions.BLOCK, label: "Block" },
|
||||
]
|
||||
});
|
||||
|
||||
let popup = permissions.find(({id}) => id === "popup");
|
||||
Assert.deepEqual(popup, {
|
||||
id: "popup",
|
||||
label: "Open Pop-up Windows",
|
||||
state: SitePermissions.BLOCK,
|
||||
availableStates: [
|
||||
{ id: SitePermissions.ALLOW, label: "Allow" },
|
||||
{ id: SitePermissions.BLOCK, label: "Block" },
|
||||
]
|
||||
});
|
||||
|
||||
SitePermissions.remove(uri, "cookie");
|
||||
SitePermissions.remove(uri, "popup");
|
||||
});
|
||||
|
@ -1747,33 +1747,6 @@ toolbarbutton.chevron > .toolbarbutton-icon {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Sync Panel */
|
||||
|
||||
.sync-panel-icon {
|
||||
height:32px;
|
||||
width: 32px;
|
||||
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
|
||||
}
|
||||
|
||||
.sync-panel-inner {
|
||||
width: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.sync-panel-button-box {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#sync-start-panel-title {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#sync-start-panel-subtitle {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Status panel */
|
||||
|
||||
.statuspanel-label {
|
||||
|
@ -3260,55 +3260,6 @@ menulist.translate-infobar-element > .menulist-dropmarker {
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
/* Sync Panels */
|
||||
|
||||
.sync-panel-icon {
|
||||
height:32px;
|
||||
width: 32px;
|
||||
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.sync-panel-icon {
|
||||
background: url("chrome://browser/content/abouthome/sync@2x.png") top left no-repeat;
|
||||
background-size: 32px 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.sync-panel-inner {
|
||||
width: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.sync-panel-button-box {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.sync-panel-button {
|
||||
@hudButton@
|
||||
margin: 0;
|
||||
min-width: 72px;
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
.sync-panel-button:hover:active {
|
||||
@hudButtonPressed@
|
||||
}
|
||||
|
||||
.sync-panel-button:-moz-focusring {
|
||||
@hudButtonFocused@
|
||||
}
|
||||
|
||||
#sync-start-panel-title {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#sync-start-panel-subtitle {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Status panel */
|
||||
|
||||
.statuspanel-label {
|
||||
|
@ -2425,33 +2425,6 @@ notification[value="translation"] {
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
/* Sync Panel */
|
||||
|
||||
.sync-panel-icon {
|
||||
height:32px;
|
||||
width: 32px;
|
||||
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
|
||||
}
|
||||
|
||||
.sync-panel-inner {
|
||||
width: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.sync-panel-button-box {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#sync-start-panel-title {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#sync-start-panel-subtitle {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Status panel */
|
||||
|
||||
.statuspanel-label {
|
||||
|
@ -12,8 +12,8 @@ const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const promise = require("promise");
|
||||
const Services = require("Services");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { CallWatcherFront } = require("devtools/server/actors/call-watcher");
|
||||
const { CanvasFront } = require("devtools/server/actors/canvas");
|
||||
const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
|
||||
const { CanvasFront } = require("devtools/shared/fronts/canvas");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const { LocalizationHelper } = require("devtools/client/shared/l10n");
|
||||
const { Heritage, WidgetMethods, setNamedTimeout, clearNamedTimeout,
|
||||
|
@ -8,7 +8,7 @@
|
||||
const { Cc, Ci, Cu, Cr } = require("chrome");
|
||||
const promise = require("promise");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { CanvasFront } = require("devtools/server/actors/canvas");
|
||||
const { CanvasFront } = require("devtools/shared/fronts/canvas");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
|
||||
function CanvasDebuggerPanel(iframeWindow, toolbox) {
|
||||
|
@ -12,8 +12,8 @@ var promise = require("promise");
|
||||
var { gDevTools } = require("devtools/client/framework/devtools");
|
||||
var { DebuggerClient } = require("devtools/shared/client/main");
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { CallWatcherFront } = require("devtools/server/actors/call-watcher");
|
||||
var { CanvasFront } = require("devtools/server/actors/canvas");
|
||||
var { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
|
||||
var { CanvasFront } = require("devtools/shared/fronts/canvas");
|
||||
var { setTimeout } = require("sdk/timers");
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
var { TargetFactory } = require("devtools/client/framework/target");
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { Cu, Ci } = require("chrome");
|
||||
const { Cu } = require("chrome");
|
||||
const nodeConstants = require("devtools/shared/dom-node-constants.js")
|
||||
const { getRootBindingParent } = require("devtools/shared/layout/utils");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
@ -162,9 +163,9 @@ Selection.prototype = {
|
||||
setNodeFront: function (value, reason = "unknown") {
|
||||
this.reason = reason;
|
||||
|
||||
// If a singleTextChild text node is being set, then set it's parent instead.
|
||||
// If an inlineTextChild text node is being set, then set it's parent instead.
|
||||
let parentNode = value && value.parentNode();
|
||||
if (value && parentNode && parentNode.singleTextChild === value) {
|
||||
if (value && parentNode && parentNode.inlineTextChild === value) {
|
||||
value = parentNode;
|
||||
}
|
||||
|
||||
@ -262,8 +263,8 @@ Selection.prototype = {
|
||||
|
||||
// Node type
|
||||
|
||||
isElementNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ELEMENT_NODE;
|
||||
isElementNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.ELEMENT_NODE;
|
||||
},
|
||||
|
||||
isPseudoElementNode: function () {
|
||||
@ -274,36 +275,36 @@ Selection.prototype = {
|
||||
return this.isNode() && this.nodeFront.isAnonymous;
|
||||
},
|
||||
|
||||
isAttributeNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ATTRIBUTE_NODE;
|
||||
isAttributeNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.ATTRIBUTE_NODE;
|
||||
},
|
||||
|
||||
isTextNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.TEXT_NODE;
|
||||
isTextNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.TEXT_NODE;
|
||||
},
|
||||
|
||||
isCDATANode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.CDATA_SECTION_NODE;
|
||||
isCDATANode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.CDATA_SECTION_NODE;
|
||||
},
|
||||
|
||||
isEntityRefNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ENTITY_REFERENCE_NODE;
|
||||
isEntityRefNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.ENTITY_REFERENCE_NODE;
|
||||
},
|
||||
|
||||
isEntityNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ENTITY_NODE;
|
||||
isEntityNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.ENTITY_NODE;
|
||||
},
|
||||
|
||||
isProcessingInstructionNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.PROCESSING_INSTRUCTION_NODE;
|
||||
isProcessingInstructionNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.PROCESSING_INSTRUCTION_NODE;
|
||||
},
|
||||
|
||||
isCommentNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.PROCESSING_INSTRUCTION_NODE;
|
||||
isCommentNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.PROCESSING_INSTRUCTION_NODE;
|
||||
},
|
||||
|
||||
isDocumentNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE;
|
||||
isDocumentNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_NODE;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -324,15 +325,15 @@ Selection.prototype = {
|
||||
this.nodeFront.nodeName === "HEAD";
|
||||
},
|
||||
|
||||
isDocumentTypeNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE;
|
||||
isDocumentTypeNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_TYPE_NODE;
|
||||
},
|
||||
|
||||
isDocumentFragmentNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE;
|
||||
isDocumentFragmentNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_FRAGMENT_NODE;
|
||||
},
|
||||
|
||||
isNotationNode: function () {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.NOTATION_NODE;
|
||||
isNotationNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == nodeConstants.NOTATION_NODE;
|
||||
},
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
// `actorHasMethod` and `getTrait`.
|
||||
|
||||
var { WebAudioFront } =
|
||||
require("devtools/server/actors/webaudio");
|
||||
require("devtools/shared/fronts/webaudio");
|
||||
|
||||
function* testTarget(client, target) {
|
||||
yield target.makeRemote();
|
||||
|
@ -30,13 +30,17 @@ add_task(function* () {
|
||||
is(getTitle(), `Developer Tools - Page title - ${URL}`,
|
||||
"Devtools title correct after switching to detached window host");
|
||||
|
||||
// Wait for tick to avoid unexpected 'popuphidden' event, which
|
||||
// blocks the frame popup menu opened below. See also bug 1276873
|
||||
yield waitForTick();
|
||||
|
||||
// Open frame menu and wait till it's available on the screen.
|
||||
let btn = toolbox.doc.getElementById("command-button-frames");
|
||||
let menu = toolbox.showFramesMenu({target: btn});
|
||||
yield once(menu, "open");
|
||||
|
||||
// Verify that the frame list menu is populated
|
||||
let frames = menu.menuitems;
|
||||
let frames = menu.items;
|
||||
is(frames.length, 2, "We have both frames in the list");
|
||||
|
||||
let topFrameBtn = frames.filter(b => b.label == URL)[0];
|
||||
|
@ -67,7 +67,7 @@ loader.lazyRequireGetter(this, "createPerformanceFront",
|
||||
loader.lazyRequireGetter(this, "system",
|
||||
"devtools/shared/system");
|
||||
loader.lazyRequireGetter(this, "getPreferenceFront",
|
||||
"devtools/server/actors/preference", true);
|
||||
"devtools/shared/fronts/preference", true);
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts",
|
||||
"devtools/client/shared/key-shortcuts", true);
|
||||
loader.lazyRequireGetter(this, "ZoomKeys",
|
||||
|
@ -24,6 +24,8 @@ loader.lazyRequireGetter(this, "overlays",
|
||||
"devtools/client/inspector/shared/style-inspector-overlays");
|
||||
loader.lazyRequireGetter(this, "StyleInspectorMenu",
|
||||
"devtools/client/inspector/shared/style-inspector-menu");
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts",
|
||||
"devtools/client/shared/key-shortcuts", true);
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
@ -155,12 +157,10 @@ function CssComputedView(inspector, document, pageStyle) {
|
||||
|
||||
// Create bound methods.
|
||||
this.focusWindow = this.focusWindow.bind(this);
|
||||
this._onKeypress = this._onKeypress.bind(this);
|
||||
this._onContextMenu = this._onContextMenu.bind(this);
|
||||
this._onClick = this._onClick.bind(this);
|
||||
this._onCopy = this._onCopy.bind(this);
|
||||
this._onFilterStyles = this._onFilterStyles.bind(this);
|
||||
this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
|
||||
this._onClearSearch = this._onClearSearch.bind(this);
|
||||
this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);
|
||||
this._onFilterTextboxContextMenu =
|
||||
@ -174,13 +174,15 @@ function CssComputedView(inspector, document, pageStyle) {
|
||||
this.includeBrowserStylesCheckbox =
|
||||
doc.getElementById("browser-style-checkbox");
|
||||
|
||||
this.styleDocument.addEventListener("keypress", this._onKeypress);
|
||||
this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
|
||||
this._onShortcut = this._onShortcut.bind(this);
|
||||
this.shortcuts.on("CmdOrCtrl+F", this._onShortcut);
|
||||
this.shortcuts.on("Escape", this._onShortcut);
|
||||
this.styleDocument.addEventListener("mousedown", this.focusWindow);
|
||||
this.element.addEventListener("click", this._onClick);
|
||||
this.element.addEventListener("copy", this._onCopy);
|
||||
this.element.addEventListener("contextmenu", this._onContextMenu);
|
||||
this.searchField.addEventListener("input", this._onFilterStyles);
|
||||
this.searchField.addEventListener("keypress", this._onFilterKeyPress);
|
||||
this.searchField.addEventListener("contextmenu",
|
||||
this._onFilterTextboxContextMenu);
|
||||
this.searchClearButton.addEventListener("click", this._onClearSearch);
|
||||
@ -502,17 +504,19 @@ CssComputedView.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the keypress event in the computed view.
|
||||
* Handle the shortcut events in the computed view.
|
||||
*/
|
||||
_onKeypress: function (event) {
|
||||
_onShortcut: function (name, event) {
|
||||
if (!event.target.closest("#sidebar-panel-computedview")) {
|
||||
return;
|
||||
}
|
||||
let isOSX = Services.appinfo.OS === "Darwin";
|
||||
|
||||
if (((isOSX && event.metaKey && !event.ctrlKey && !event.altKey) ||
|
||||
(!isOSX && event.ctrlKey && !event.metaKey && !event.altKey)) &&
|
||||
event.key === "f") {
|
||||
// Handle the search box's keypress event. If the escape key is pressed,
|
||||
// clear the search box field.
|
||||
if (name === "Escape" && event.target === this.searchField &&
|
||||
this._onClearSearch()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
} else if (name === "CmdOrCtrl+F") {
|
||||
this.searchField.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
@ -553,18 +557,6 @@ CssComputedView.prototype = {
|
||||
}, filterTimeout);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the search box's keypress event. If the escape key is pressed,
|
||||
* clear the search box field.
|
||||
*/
|
||||
_onFilterKeyPress: function (event) {
|
||||
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE &&
|
||||
this._onClearSearch()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Context menu handler for filter style search box.
|
||||
*/
|
||||
@ -776,7 +768,6 @@ CssComputedView.prototype = {
|
||||
this.element.removeEventListener("copy", this._onCopy);
|
||||
this.element.removeEventListener("contextmenu", this._onContextMenu);
|
||||
this.searchField.removeEventListener("input", this._onFilterStyles);
|
||||
this.searchField.removeEventListener("keypress", this._onFilterKeyPress);
|
||||
this.searchField.removeEventListener("contextmenu",
|
||||
this._onFilterTextboxContextMenu);
|
||||
this.searchClearButton.removeEventListener("click", this._onClearSearch);
|
||||
@ -957,18 +948,15 @@ PropertyView.prototype = {
|
||||
|
||||
// Make it keyboard navigable
|
||||
this.element.setAttribute("tabindex", "0");
|
||||
this.onKeyDown = (event) => {
|
||||
let keyEvent = Ci.nsIDOMKeyEvent;
|
||||
if (event.keyCode === keyEvent.DOM_VK_F1) {
|
||||
this.mdnLinkClick();
|
||||
event.preventDefault();
|
||||
}
|
||||
if (event.keyCode === keyEvent.DOM_VK_RETURN ||
|
||||
event.keyCode === keyEvent.DOM_VK_SPACE) {
|
||||
this.onMatchedToggle(event);
|
||||
}
|
||||
};
|
||||
this.element.addEventListener("keydown", this.onKeyDown, false);
|
||||
this.shortcuts = new KeyShortcuts({
|
||||
window: this.tree.styleWindow,
|
||||
target: this.element
|
||||
});
|
||||
this.shortcuts.on("F1", (name, event) => {
|
||||
this.mdnLinkClick(event);
|
||||
});
|
||||
this.shortcuts.on("Return", (name, event) => this.onMatchedToggle(event));
|
||||
this.shortcuts.on("Space", (name, event) => this.onMatchedToggle(event));
|
||||
|
||||
let nameContainer = doc.createElementNS(HTML_NS, "div");
|
||||
nameContainer.className = "property-name-container";
|
||||
@ -1116,7 +1104,11 @@ PropertyView.prototype = {
|
||||
textContent: selector.source
|
||||
});
|
||||
link.addEventListener("click", selector.openStyleEditor, false);
|
||||
link.addEventListener("keydown", selector.maybeOpenStyleEditor, false);
|
||||
let shortcuts = new KeyShortcuts({
|
||||
window: this.tree.styleWindow,
|
||||
target: link
|
||||
});
|
||||
shortcuts.on("Return", () => selector.openStyleEditor());
|
||||
|
||||
let status = createChild(p, "span", {
|
||||
dir: "ltr",
|
||||
@ -1191,6 +1183,7 @@ PropertyView.prototype = {
|
||||
browserWin.openUILinkIn(this.link, "tab");
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1198,7 +1191,7 @@ PropertyView.prototype = {
|
||||
*/
|
||||
destroy: function () {
|
||||
this.element.removeEventListener("dblclick", this.onMatchedToggle, false);
|
||||
this.element.removeEventListener("keydown", this.onKeyDown, false);
|
||||
this.shortcuts.destroy();
|
||||
this.element = null;
|
||||
|
||||
this.matchedExpander.removeEventListener("click", this.onMatchedToggle,
|
||||
@ -1226,7 +1219,6 @@ function SelectorView(tree, selectorInfo) {
|
||||
this._cacheStatusNames();
|
||||
|
||||
this.openStyleEditor = this.openStyleEditor.bind(this);
|
||||
this.maybeOpenStyleEditor = this.maybeOpenStyleEditor.bind(this);
|
||||
|
||||
this.ready = this.updateSourceLink();
|
||||
}
|
||||
@ -1368,16 +1360,6 @@ SelectorView.prototype = {
|
||||
return promise.resolve(oldSource);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the style editor if the RETURN key was pressed.
|
||||
*/
|
||||
maybeOpenStyleEditor: function (event) {
|
||||
let keyEvent = Ci.nsIDOMKeyEvent;
|
||||
if (event.keyCode === keyEvent.DOM_VK_RETURN) {
|
||||
this.openStyleEditor();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When a css link is clicked this method is called in order to either:
|
||||
* 1. Open the link in view source (for chrome stylesheets).
|
||||
|
@ -18,6 +18,7 @@ const {executeSoon} = require("devtools/shared/DevToolsUtils");
|
||||
var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
|
||||
var {Task} = require("devtools/shared/task");
|
||||
const {initCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
const nodeConstants = require("devtools/shared/dom-node-constants");
|
||||
|
||||
loader.lazyRequireGetter(this, "CSS", "CSS");
|
||||
|
||||
@ -1254,15 +1255,15 @@ InspectorPanel.prototype = {
|
||||
let node = this.selection.nodeFront;
|
||||
|
||||
switch (node.nodeType) {
|
||||
case Ci.nsIDOMNode.ELEMENT_NODE :
|
||||
case nodeConstants.ELEMENT_NODE :
|
||||
this._copyLongString(this.walker.outerHTML(node));
|
||||
break;
|
||||
case Ci.nsIDOMNode.COMMENT_NODE :
|
||||
case nodeConstants.COMMENT_NODE :
|
||||
this._getLongString(node.getNodeValue()).then(comment => {
|
||||
clipboardHelper.copyString("<!--" + comment + "-->");
|
||||
});
|
||||
break;
|
||||
case Ci.nsIDOMNode.DOCUMENT_TYPE_NODE :
|
||||
case nodeConstants.DOCUMENT_TYPE_NODE :
|
||||
clipboardHelper.copyString(node.doctypeString);
|
||||
break;
|
||||
}
|
||||
|
@ -44,13 +44,12 @@ const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
const {parseAttribute} =
|
||||
require("devtools/client/shared/node-attribute-parser");
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
|
||||
Ci.nsIPrefLocalizedString).data;
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const {scrollIntoViewIfNeeded} = require("devtools/shared/layout/utils");
|
||||
const {PrefObserver} = require("devtools/client/styleeditor/utils");
|
||||
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
|
||||
const {template} = require("devtools/shared/gcli/templater");
|
||||
const nodeConstants = require("devtools/shared/dom-node-constants");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
@ -219,7 +218,7 @@ MarkupView.prototype = {
|
||||
|
||||
let container = target.container;
|
||||
if (this._hoveredNode !== container.node) {
|
||||
if (container.node.nodeType !== Ci.nsIDOMNode.TEXT_NODE) {
|
||||
if (container.node.nodeType !== nodeConstants.TEXT_NODE) {
|
||||
this._showBoxModel(container.node);
|
||||
} else {
|
||||
this._hideBoxModel();
|
||||
@ -843,7 +842,7 @@ MarkupView.prototype = {
|
||||
*/
|
||||
deleteNode: function (node, moveBackward) {
|
||||
if (node.isDocumentElement ||
|
||||
node.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE ||
|
||||
node.nodeType == nodeConstants.DOCUMENT_TYPE_NODE ||
|
||||
node.isAnonymous) {
|
||||
return;
|
||||
}
|
||||
@ -870,6 +869,8 @@ MarkupView.prototype = {
|
||||
}
|
||||
});
|
||||
}, () => {
|
||||
let isValidSibling = nextSibling && !nextSibling.isPseudoElement;
|
||||
nextSibling = isValidSibling ? nextSibling : null;
|
||||
this.walker.insertBefore(node, parent, nextSibling);
|
||||
});
|
||||
}).then(null, console.error);
|
||||
@ -928,10 +929,10 @@ MarkupView.prototype = {
|
||||
container = new RootContainer(this, node);
|
||||
this._elt.appendChild(container.elt);
|
||||
this._rootNode = node;
|
||||
} else if (nodeType == Ci.nsIDOMNode.ELEMENT_NODE && !isPseudoElement) {
|
||||
} else if (nodeType == nodeConstants.ELEMENT_NODE && !isPseudoElement) {
|
||||
container = new MarkupElementContainer(this, node, this._inspector);
|
||||
} else if (nodeType == Ci.nsIDOMNode.COMMENT_NODE ||
|
||||
nodeType == Ci.nsIDOMNode.TEXT_NODE) {
|
||||
} else if (nodeType == nodeConstants.COMMENT_NODE ||
|
||||
nodeType == nodeConstants.TEXT_NODE) {
|
||||
container = new MarkupTextContainer(this, node, this._inspector);
|
||||
} else {
|
||||
container = new MarkupReadOnlyContainer(this, node, this._inspector);
|
||||
@ -989,6 +990,10 @@ MarkupView.prototype = {
|
||||
// accessibility where necessary.
|
||||
this._updateChildren(container, {flash: true}).then(() =>
|
||||
container.updateLevel());
|
||||
} else if (type === "inlineTextChild") {
|
||||
container.childrenDirty = true;
|
||||
this._updateChildren(container, {flash: true});
|
||||
container.update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1549,19 +1554,19 @@ MarkupView.prototype = {
|
||||
return promise.resolve(container);
|
||||
}
|
||||
|
||||
if (container.singleTextChild
|
||||
&& container.singleTextChild != container.node.singleTextChild) {
|
||||
if (container.inlineTextChild
|
||||
&& container.inlineTextChild != container.node.inlineTextChild) {
|
||||
// This container was doing double duty as a container for a single
|
||||
// text child, back that out.
|
||||
this._containers.delete(container.singleTextChild);
|
||||
container.clearSingleTextChild();
|
||||
this._containers.delete(container.inlineTextChild);
|
||||
container.clearInlineTextChild();
|
||||
|
||||
if (container.hasChildren && container.selected) {
|
||||
container.setExpanded(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (container.node.singleTextChild) {
|
||||
if (container.node.inlineTextChild) {
|
||||
container.setExpanded(false);
|
||||
// this container will do double duty as the container for the single
|
||||
// text child.
|
||||
@ -1569,9 +1574,9 @@ MarkupView.prototype = {
|
||||
container.children.removeChild(container.children.firstChild);
|
||||
}
|
||||
|
||||
container.setSingleTextChild(container.node.singleTextChild);
|
||||
container.setInlineTextChild(container.node.inlineTextChild);
|
||||
|
||||
this._containers.set(container.node.singleTextChild, container);
|
||||
this._containers.set(container.node.inlineTextChild, container);
|
||||
container.childrenDirty = false;
|
||||
return promise.resolve(container);
|
||||
}
|
||||
@ -1824,7 +1829,7 @@ MarkupView.prototype = {
|
||||
nextSibling = null;
|
||||
}
|
||||
|
||||
if (parent.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
if (parent.nodeType !== nodeConstants.ELEMENT_NODE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -2008,7 +2013,7 @@ MarkupContainer.prototype = {
|
||||
* True if the current node can be expanded.
|
||||
*/
|
||||
get canExpand() {
|
||||
return this._hasChildren && !this.node.singleTextChild;
|
||||
return this._hasChildren && !this.node.inlineTextChild;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2568,9 +2573,9 @@ function MarkupTextContainer(markupView, node) {
|
||||
MarkupContainer.prototype.initialize.call(this, markupView, node,
|
||||
"textcontainer");
|
||||
|
||||
if (node.nodeType == Ci.nsIDOMNode.TEXT_NODE) {
|
||||
if (node.nodeType == nodeConstants.TEXT_NODE) {
|
||||
this.editor = new TextEditor(this, node, "text");
|
||||
} else if (node.nodeType == Ci.nsIDOMNode.COMMENT_NODE) {
|
||||
} else if (node.nodeType == nodeConstants.COMMENT_NODE) {
|
||||
this.editor = new TextEditor(this, node, "comment");
|
||||
} else {
|
||||
throw new Error("Invalid node for MarkupTextContainer");
|
||||
@ -2595,7 +2600,7 @@ function MarkupElementContainer(markupView, node) {
|
||||
MarkupContainer.prototype.initialize.call(this, markupView, node,
|
||||
"elementcontainer");
|
||||
|
||||
if (node.nodeType === Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
if (node.nodeType === nodeConstants.ELEMENT_NODE) {
|
||||
this.editor = new ElementEditor(this, node);
|
||||
} else {
|
||||
throw new Error("Invalid node for MarkupElementContainer");
|
||||
@ -2717,13 +2722,13 @@ MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
|
||||
});
|
||||
},
|
||||
|
||||
setSingleTextChild: function (singleTextChild) {
|
||||
this.singleTextChild = singleTextChild;
|
||||
setInlineTextChild: function (inlineTextChild) {
|
||||
this.inlineTextChild = inlineTextChild;
|
||||
this.editor.updateTextEditor();
|
||||
},
|
||||
|
||||
clearSingleTextChild: function () {
|
||||
this.singleTextChild = undefined;
|
||||
clearInlineTextChild: function () {
|
||||
this.inlineTextChild = undefined;
|
||||
this.editor.updateTextEditor();
|
||||
},
|
||||
|
||||
@ -2818,7 +2823,7 @@ function GenericEditor(container, node) {
|
||||
if (node.isPseudoElement) {
|
||||
this.tag.classList.add("theme-fg-color5");
|
||||
this.tag.textContent = node.isBeforePseudoElement ? "::before" : "::after";
|
||||
} else if (node.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
|
||||
} else if (node.nodeType == nodeConstants.DOCUMENT_TYPE_NODE) {
|
||||
this.elt.classList.add("comment");
|
||||
this.tag.textContent = node.doctypeString;
|
||||
} else {
|
||||
@ -2905,25 +2910,14 @@ TextEditor.prototype = {
|
||||
},
|
||||
|
||||
update: function () {
|
||||
if (!this.selected || !this.node.incompleteValue) {
|
||||
let text = this.node.shortValue;
|
||||
if (this.node.incompleteValue) {
|
||||
text += ELLIPSIS;
|
||||
}
|
||||
this.value.textContent = text;
|
||||
} else {
|
||||
let longstr = null;
|
||||
this.node.getNodeValue().then(ret => {
|
||||
longstr = ret;
|
||||
return longstr.string();
|
||||
}).then(str => {
|
||||
longstr.release().then(null, console.error);
|
||||
if (this.selected) {
|
||||
this.value.textContent = str;
|
||||
this.markup.emit("text-expand");
|
||||
}
|
||||
}).then(null, console.error);
|
||||
}
|
||||
let longstr = null;
|
||||
this.node.getNodeValue().then(ret => {
|
||||
longstr = ret;
|
||||
return longstr.string();
|
||||
}).then(str => {
|
||||
longstr.release().then(null, console.error);
|
||||
this.value.textContent = str;
|
||||
}).then(null, console.error);
|
||||
},
|
||||
|
||||
destroy: function () {},
|
||||
@ -3114,7 +3108,7 @@ ElementEditor.prototype = {
|
||||
* Update the inline text editor in case of a single text child node.
|
||||
*/
|
||||
updateTextEditor: function () {
|
||||
let node = this.node.singleTextChild;
|
||||
let node = this.node.inlineTextChild;
|
||||
|
||||
if (this.textEditor && this.textEditor.node != node) {
|
||||
this.elt.removeChild(this.textEditor.elt);
|
||||
|
@ -132,6 +132,7 @@ skip-if = e10s # Bug 1036409 - The last selected node isn't reselected
|
||||
[browser_markup_tag_edit_11.js]
|
||||
[browser_markup_tag_edit_12.js]
|
||||
[browser_markup_tag_edit_13-other.js]
|
||||
[browser_markup_textcontent_display.js]
|
||||
[browser_markup_textcontent_edit_01.js]
|
||||
[browser_markup_textcontent_edit_02.js]
|
||||
[browser_markup_toggle_01.js]
|
||||
|
@ -89,7 +89,7 @@ const TEST_DATA = [
|
||||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -99,9 +99,9 @@ const TEST_DATA = [
|
||||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with singleTextChild.");
|
||||
ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with inlineTextChild.");
|
||||
ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
|
||||
is(container.editor.elt.querySelector(".text").textContent.trim(),
|
||||
"newtext", "Single text child editor updated.");
|
||||
}
|
||||
@ -117,7 +117,7 @@ const TEST_DATA = [
|
||||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(!container.singleTextChild, "Does not have single text child.");
|
||||
ok(!container.inlineTextChild, "Does not have single text child.");
|
||||
ok(container.canExpand, "Can expand container with child nodes.");
|
||||
ok(container.editor.elt.querySelector(".text") == null,
|
||||
"Single text child editor removed.");
|
||||
@ -130,9 +130,9 @@ const TEST_DATA = [
|
||||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with singleTextChild.");
|
||||
ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with inlineTextChild.");
|
||||
ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
|
||||
ok(container.editor.elt.querySelector(".text").textContent.trim(),
|
||||
"newtext", "Single text child editor updated.");
|
||||
},
|
||||
@ -144,7 +144,7 @@ const TEST_DATA = [
|
||||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(!container.singleTextChild, "Does not have single text child.");
|
||||
ok(!container.inlineTextChild, "Does not have single text child.");
|
||||
ok(!container.canExpand, "Can't expand empty container.");
|
||||
ok(container.editor.elt.querySelector(".text") == null,
|
||||
"Single text child editor removed.");
|
||||
@ -157,9 +157,9 @@ const TEST_DATA = [
|
||||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with singleTextChild.");
|
||||
ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with inlineTextChild.");
|
||||
ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
|
||||
ok(container.editor.elt.querySelector(".text").textContent.trim(),
|
||||
"newtext", "Single text child editor updated.");
|
||||
},
|
||||
|
@ -8,10 +8,20 @@
|
||||
// Also checks that after deletion the correct element is highlighted.
|
||||
// The next sibling is preferred, but the parent is a fallback.
|
||||
|
||||
const HTML = `<div id="parent">
|
||||
const HTML = `<style type="text/css">
|
||||
#pseudo::before { content: 'before'; }
|
||||
#pseudo::after { content: 'after'; }
|
||||
</style>
|
||||
<div id="parent">
|
||||
<div id="first"></div>
|
||||
<div id="second"></div>
|
||||
<div id="third"></div>
|
||||
</div>
|
||||
<div id="only-child">
|
||||
<div id="fourth"></div>
|
||||
</div>
|
||||
<div id="pseudo">
|
||||
<div id="fifth"></div>
|
||||
</div>`;
|
||||
const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
|
||||
|
||||
@ -20,8 +30,8 @@ const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
|
||||
// - key: the key to press to delete the node (delete or back_space)
|
||||
// - focusedSelector: the css selector of the node we expect to be selected as
|
||||
// a result of the deletion
|
||||
// - setup: an optional function that will be run before selecting and deleting
|
||||
// the node
|
||||
// - pseudo: (optional) if the focused node is actually supposed to be a pseudo element
|
||||
// of the specified selector.
|
||||
// Note that after each test case, undo is called.
|
||||
const TEST_DATA = [{
|
||||
selector: "#first",
|
||||
@ -35,6 +45,15 @@ const TEST_DATA = [{
|
||||
selector: "#third",
|
||||
key: "delete",
|
||||
focusedSelector: "#second"
|
||||
}, {
|
||||
selector: "#fourth",
|
||||
key: "delete",
|
||||
focusedSelector: "#only-child"
|
||||
}, {
|
||||
selector: "#fifth",
|
||||
key: "delete",
|
||||
focusedSelector: "#pseudo",
|
||||
pseudo: "after"
|
||||
}, {
|
||||
selector: "#first",
|
||||
key: "back_space",
|
||||
@ -48,38 +67,25 @@ const TEST_DATA = [{
|
||||
key: "back_space",
|
||||
focusedSelector: "#second"
|
||||
}, {
|
||||
setup: function* (inspector, testActor) {
|
||||
// Removing the siblings of #first in order to test with an only child.
|
||||
let mutated = inspector.once("markupmutation");
|
||||
yield testActor.eval(`
|
||||
for (let node of content.document.querySelectorAll("#second, #third")) {
|
||||
node.remove();
|
||||
}
|
||||
`);
|
||||
yield mutated;
|
||||
},
|
||||
selector: "#first",
|
||||
key: "delete",
|
||||
focusedSelector: "#parent"
|
||||
}, {
|
||||
selector: "#first",
|
||||
selector: "#fourth",
|
||||
key: "back_space",
|
||||
focusedSelector: "#parent"
|
||||
focusedSelector: "#only-child"
|
||||
}, {
|
||||
selector: "#fifth",
|
||||
key: "back_space",
|
||||
focusedSelector: "#pseudo",
|
||||
pseudo: "before"
|
||||
}];
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
for (let {setup, selector, key, focusedSelector} of TEST_DATA) {
|
||||
if (setup) {
|
||||
yield setup(inspector, testActor);
|
||||
}
|
||||
|
||||
yield checkDeleteAndSelection(inspector, key, selector, focusedSelector);
|
||||
for (let data of TEST_DATA) {
|
||||
yield checkDeleteAndSelection(inspector, data);
|
||||
}
|
||||
});
|
||||
|
||||
function* checkDeleteAndSelection(inspector, key, selector, focusedSelector) {
|
||||
function* checkDeleteAndSelection(inspector, {key, selector, focusedSelector, pseudo}) {
|
||||
info("Test deleting node " + selector + " with " + key + ", " +
|
||||
"expecting " + focusedSelector + " to be focused");
|
||||
|
||||
@ -93,6 +99,14 @@ function* checkDeleteAndSelection(inspector, key, selector, focusedSelector) {
|
||||
yield Promise.all([mutated, inspector.once("inspector-updated")]);
|
||||
|
||||
let nodeFront = yield getNodeFront(focusedSelector, inspector);
|
||||
if (pseudo) {
|
||||
// Update the selector for logging in case of failure.
|
||||
focusedSelector = focusedSelector + "::" + pseudo;
|
||||
// Retrieve the :before or :after pseudo element of the nodeFront.
|
||||
let {nodes} = yield inspector.walker.children(nodeFront);
|
||||
nodeFront = pseudo === "before" ? nodes[0] : nodes[nodes.length - 1];
|
||||
}
|
||||
|
||||
is(inspector.selection.nodeFront, nodeFront,
|
||||
focusedSelector + " is selected after deletion");
|
||||
|
||||
|
@ -0,0 +1,89 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test the rendering of text nodes in the markup view.
|
||||
|
||||
const LONG_VALUE = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " +
|
||||
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.";
|
||||
const SCHEMA = "data:text/html;charset=UTF-8,";
|
||||
const TEST_URL = `${SCHEMA}<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div id="shorttext">Short text</div>
|
||||
<div id="longtext">${LONG_VALUE}</div>
|
||||
<div id="shortcomment"><!--Short comment--></div>
|
||||
<div id="longcomment"><!--${LONG_VALUE}--></div>
|
||||
<div id="shorttext-and-node">Short text<span>Other element</span></div>
|
||||
<div id="longtext-and-node">${LONG_VALUE}<span>Other element</span></div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const TEST_DATA = [{
|
||||
desc: "Test node containing a short text, short text nodes can be inlined.",
|
||||
selector: "#shorttext",
|
||||
inline: true,
|
||||
value: "Short text",
|
||||
}, {
|
||||
desc: "Test node containing a long text, long text nodes are not inlined.",
|
||||
selector: "#longtext",
|
||||
inline: false,
|
||||
value: LONG_VALUE,
|
||||
}, {
|
||||
desc: "Test node containing a short comment, comments are not inlined.",
|
||||
selector: "#shortcomment",
|
||||
inline: false,
|
||||
value: "Short comment",
|
||||
}, {
|
||||
desc: "Test node containing a long comment, comments are not inlined.",
|
||||
selector: "#longcomment",
|
||||
inline: false,
|
||||
value: LONG_VALUE,
|
||||
}, {
|
||||
desc: "Test node containing a short text and a span.",
|
||||
selector: "#shorttext-and-node",
|
||||
inline: false,
|
||||
value: "Short text",
|
||||
}, {
|
||||
desc: "Test node containing a long text and a span.",
|
||||
selector: "#longtext-and-node",
|
||||
inline: false,
|
||||
value: LONG_VALUE,
|
||||
}];
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
for (let data of TEST_DATA) {
|
||||
yield checkNode(inspector, testActor, data);
|
||||
}
|
||||
});
|
||||
|
||||
function* checkNode(inspector, testActor, {desc, selector, inline, value}) {
|
||||
info(desc);
|
||||
|
||||
let container = yield getContainerForSelector(selector, inspector);
|
||||
let nodeValue = yield getFirstChildNodeValue(selector, testActor);
|
||||
is(nodeValue, value, "The test node's text content is correct");
|
||||
|
||||
is(!!container.inlineTextChild, inline, "Container inlineTextChild is as expected");
|
||||
is(!container.canExpand, inline, "Container canExpand property is as expected");
|
||||
|
||||
let textContainer;
|
||||
if (inline) {
|
||||
textContainer = container.elt.querySelector("pre");
|
||||
ok(!!textContainer, "Text container is already rendered for inline text elements");
|
||||
} else {
|
||||
textContainer = container.elt.querySelector("pre");
|
||||
ok(!textContainer, "Text container is not rendered for collapsed text nodes");
|
||||
yield inspector.markup.expandNode(container.node);
|
||||
yield waitForMultipleChildrenUpdates(inspector);
|
||||
|
||||
textContainer = container.elt.querySelector("pre");
|
||||
ok(!!textContainer, "Text container is rendered after expanding the container");
|
||||
}
|
||||
|
||||
is(textContainer.textContent, value, "The complete text node is rendered.");
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
// Test editing a node's text content
|
||||
|
||||
const TEST_URL = URL_ROOT + "doc_markup_edit.html";
|
||||
const {DEFAULT_VALUE_SUMMARY_LENGTH} = require("devtools/server/actors/inspector");
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
@ -26,55 +27,38 @@ add_task(function* () {
|
||||
newValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
|
||||
"DONEC POSUERE PLACERAT MAGNA ET IMPERDIET.",
|
||||
oldValue: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
|
||||
"Donec posuere placerat magna et imperdiet.",
|
||||
shortValue: true
|
||||
"Donec posuere placerat magna et imperdiet."
|
||||
});
|
||||
|
||||
yield editContainer(inspector, testActor, {
|
||||
selector: "#node17",
|
||||
newValue: "New value",
|
||||
oldValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
|
||||
"DONEC POSUERE PLACERAT MAGNA ET IMPERDIET."
|
||||
});
|
||||
|
||||
yield editContainer(inspector, testActor, {
|
||||
selector: "#node17",
|
||||
newValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
|
||||
"DONEC POSUERE PLACERAT MAGNA ET IMPERDIET.",
|
||||
shortValue: true
|
||||
oldValue: "New value"
|
||||
});
|
||||
});
|
||||
|
||||
function* getNodeValue(selector, testActor) {
|
||||
let nodeValue = yield testActor.eval(`
|
||||
content.document.querySelector("${selector}").firstChild.nodeValue;
|
||||
`);
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
function* editContainer(inspector, testActor,
|
||||
{selector, newValue, oldValue, shortValue}) {
|
||||
let nodeValue = yield getNodeValue(selector, testActor);
|
||||
{selector, newValue, oldValue}) {
|
||||
let nodeValue = yield getFirstChildNodeValue(selector, testActor);
|
||||
is(nodeValue, oldValue, "The test node's text content is correct");
|
||||
|
||||
info("Changing the text content");
|
||||
let onMutated = inspector.once("markupmutation");
|
||||
let container = yield focusNode(selector, inspector);
|
||||
|
||||
let isOldValueInline = oldValue.length <= DEFAULT_VALUE_SUMMARY_LENGTH;
|
||||
is(!!container.inlineTextChild, isOldValueInline, "inlineTextChild is as expected");
|
||||
is(!container.canExpand, isOldValueInline, "canExpand property is as expected");
|
||||
|
||||
let field = container.elt.querySelector("pre");
|
||||
|
||||
if (shortValue) {
|
||||
is(oldValue.indexOf(
|
||||
field.textContent.substring(0, field.textContent.length - 1)),
|
||||
0,
|
||||
"The shortened value starts with the full value " + field.textContent);
|
||||
ok(oldValue.length > field.textContent.length,
|
||||
"The shortened value is short");
|
||||
} else {
|
||||
is(field.textContent, oldValue,
|
||||
"The text node has the correct original value");
|
||||
}
|
||||
|
||||
inspector.markup.markNodeAsSelected(container.node);
|
||||
|
||||
if (shortValue) {
|
||||
info("Waiting for the text to be updated");
|
||||
yield inspector.markup.once("text-expand");
|
||||
}
|
||||
|
||||
is(field.textContent, oldValue,
|
||||
"The text node has the correct original value after selecting");
|
||||
setEditableFieldValue(field, newValue, inspector);
|
||||
@ -82,9 +66,18 @@ function* editContainer(inspector, testActor,
|
||||
info("Listening to the markupmutation event");
|
||||
yield onMutated;
|
||||
|
||||
nodeValue = yield getNodeValue(selector, testActor);
|
||||
nodeValue = yield getFirstChildNodeValue(selector, testActor);
|
||||
is(nodeValue, newValue, "The test node's text content has changed");
|
||||
|
||||
let isNewValueInline = newValue.length <= DEFAULT_VALUE_SUMMARY_LENGTH;
|
||||
is(!!container.inlineTextChild, isNewValueInline, "inlineTextChild is as expected");
|
||||
is(!container.canExpand, isNewValueInline, "canExpand property is as expected");
|
||||
|
||||
if (isOldValueInline != isNewValueInline) {
|
||||
is(container.expanded, !isNewValueInline,
|
||||
"Container was automatically expanded/collapsed");
|
||||
}
|
||||
|
||||
info("Selecting the <body> to reset the selection");
|
||||
let bodyContainer = yield getContainerForSelector("body", inspector);
|
||||
inspector.markup.markNodeAsSelected(bodyContainer.node);
|
||||
|
@ -17,7 +17,7 @@ add_task(function* () {
|
||||
yield inspector.markup.expandAll();
|
||||
yield waitForMultipleChildrenUpdates(inspector);
|
||||
|
||||
let nodeValue = yield getNodeValue(SELECTOR, testActor);
|
||||
let nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
|
||||
let expectedValue = "line6";
|
||||
is(nodeValue, expectedValue, "The test node's text content is correct");
|
||||
|
||||
@ -84,17 +84,10 @@ add_task(function* () {
|
||||
yield sendKey("VK_RETURN", {}, editor, inspector.panelWin);
|
||||
yield onMutated;
|
||||
|
||||
nodeValue = yield getNodeValue(SELECTOR, testActor);
|
||||
nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
|
||||
is(nodeValue, expectedValue, "The test node's text content is correct");
|
||||
});
|
||||
|
||||
function* getNodeValue(selector, testActor) {
|
||||
let nodeValue = yield testActor.eval(`
|
||||
content.document.querySelector("${selector}").firstChild.nodeValue;
|
||||
`);
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the editor selection is at the expected positions.
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@ Services.scriptloader.loadSubScript(
|
||||
|
||||
var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor");
|
||||
var clipboard = require("sdk/clipboard");
|
||||
var {ActorRegistryFront} = require("devtools/server/actors/actor-registry");
|
||||
var {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
|
||||
|
||||
// If a test times out we want to see the complete log and not just the last few
|
||||
// lines.
|
||||
@ -89,6 +89,20 @@ var getContainerForSelector = Task.async(function* (selector, inspector) {
|
||||
return container;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve the nodeValue for the firstChild of a provided selector on the content page.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @param {TestActorFront} testActor The current TestActorFront instance.
|
||||
* @return {String} the nodeValue of the first
|
||||
*/
|
||||
function* getFirstChildNodeValue(selector, testActor) {
|
||||
let nodeValue = yield testActor.eval(`
|
||||
content.document.querySelector("${selector}").firstChild.nodeValue;
|
||||
`);
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the markupview's _waitForChildren function, wait for all queued
|
||||
* children updates to be handled.
|
||||
|
@ -22,10 +22,6 @@ XPCOMUtils.defineLazyGetter(this, "osString", function () {
|
||||
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "domUtils", function () {
|
||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
});
|
||||
|
||||
/**
|
||||
* Rule is responsible for the following:
|
||||
* Manages a single style declaration or rule.
|
||||
@ -449,7 +445,7 @@ Rule.prototype = {
|
||||
|
||||
// Starting with FF49, StyleRuleActors provide parsed declarations.
|
||||
let props = this.style.declarations;
|
||||
if (!props) {
|
||||
if (!props.length) {
|
||||
props = parseDeclarations(this.cssProperties.isKnown,
|
||||
this.style.authoredText, true);
|
||||
}
|
||||
@ -466,7 +462,7 @@ Rule.prototype = {
|
||||
// However, we must keep all properties in order for rule
|
||||
// rewriting to work properly. So, compute the "invisible"
|
||||
// property here.
|
||||
let invisible = this.inherited && !domUtils.isInheritedProperty(name);
|
||||
let invisible = this.inherited && !this.cssProperties.isInherited(name);
|
||||
let value = store.userProperties.getProperty(this.style, name,
|
||||
prop.value);
|
||||
let textProp = new TextProperty(this, name, value, prop.priority,
|
||||
|
@ -33,6 +33,8 @@ loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/shared/event-emitter");
|
||||
loader.lazyRequireGetter(this, "StyleInspectorMenu",
|
||||
"devtools/client/inspector/shared/style-inspector-menu");
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts",
|
||||
"devtools/client/shared/key-shortcuts", true);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function () {
|
||||
return Cc["@mozilla.org/widget/clipboardhelper;1"]
|
||||
@ -161,13 +163,10 @@ function CssRuleView(inspector, document, store, pageStyle) {
|
||||
|
||||
this._outputParser = new OutputParser(document);
|
||||
|
||||
this._onKeydown = this._onKeydown.bind(this);
|
||||
this._onKeypress = this._onKeypress.bind(this);
|
||||
this._onAddRule = this._onAddRule.bind(this);
|
||||
this._onContextMenu = this._onContextMenu.bind(this);
|
||||
this._onCopy = this._onCopy.bind(this);
|
||||
this._onFilterStyles = this._onFilterStyles.bind(this);
|
||||
this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
|
||||
this._onClearSearch = this._onClearSearch.bind(this);
|
||||
this._onFilterTextboxContextMenu =
|
||||
this._onFilterTextboxContextMenu.bind(this);
|
||||
@ -187,13 +186,16 @@ function CssRuleView(inspector, document, store, pageStyle) {
|
||||
|
||||
this.searchClearButton.hidden = true;
|
||||
|
||||
this.styleDocument.addEventListener("keydown", this._onKeydown);
|
||||
this.styleDocument.addEventListener("keypress", this._onKeypress);
|
||||
this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
|
||||
this._onShortcut = this._onShortcut.bind(this);
|
||||
this.shortcuts.on("Escape", this._onShortcut);
|
||||
this.shortcuts.on("Return", this._onShortcut);
|
||||
this.shortcuts.on("Space", this._onShortcut);
|
||||
this.shortcuts.on("CmdOrCtrl+F", this._onShortcut);
|
||||
this.element.addEventListener("copy", this._onCopy);
|
||||
this.element.addEventListener("contextmenu", this._onContextMenu);
|
||||
this.addRuleButton.addEventListener("click", this._onAddRule);
|
||||
this.searchField.addEventListener("input", this._onFilterStyles);
|
||||
this.searchField.addEventListener("keypress", this._onFilterKeyPress);
|
||||
this.searchField.addEventListener("contextmenu",
|
||||
this._onFilterTextboxContextMenu);
|
||||
this.searchClearButton.addEventListener("click", this._onClearSearch);
|
||||
@ -703,18 +705,6 @@ CssRuleView.prototype = {
|
||||
}, filterTimeout);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the search box's keypress event. If the escape key is pressed,
|
||||
* clear the search box field.
|
||||
*/
|
||||
_onFilterKeyPress: function (event) {
|
||||
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE &&
|
||||
this._onClearSearch()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Context menu handler for filter style search box.
|
||||
*/
|
||||
@ -766,13 +756,11 @@ CssRuleView.prototype = {
|
||||
this.highlighters.destroy();
|
||||
|
||||
// Remove bound listeners
|
||||
this.styleDocument.removeEventListener("keydown", this._onKeydown);
|
||||
this.styleDocument.removeEventListener("keypress", this._onKeypress);
|
||||
this.shortcuts.destroy();
|
||||
this.element.removeEventListener("copy", this._onCopy);
|
||||
this.element.removeEventListener("contextmenu", this._onContextMenu);
|
||||
this.addRuleButton.removeEventListener("click", this._onAddRule);
|
||||
this.searchField.removeEventListener("input", this._onFilterStyles);
|
||||
this.searchField.removeEventListener("keypress", this._onFilterKeyPress);
|
||||
this.searchField.removeEventListener("contextmenu",
|
||||
this._onFilterTextboxContextMenu);
|
||||
this.searchClearButton.removeEventListener("click", this._onClearSearch);
|
||||
@ -1489,33 +1477,30 @@ CssRuleView.prototype = {
|
||||
this.inspector.togglePseudoClass(target.value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the keydown event in the rule view.
|
||||
*/
|
||||
_onKeydown: function (event) {
|
||||
if (this.element.classList.contains("non-interactive") &&
|
||||
(event.code === "Enter" || event.code === " ")) {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the keypress event in the rule view.
|
||||
*/
|
||||
_onKeypress: function (event) {
|
||||
_onShortcut: function (name, event) {
|
||||
if (!event.target.closest("#sidebar-panel-ruleview")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let isOSX = Services.appinfo.OS === "Darwin";
|
||||
|
||||
if (((isOSX && event.metaKey && !event.ctrlKey && !event.altKey) ||
|
||||
(!isOSX && event.ctrlKey && !event.metaKey && !event.altKey)) &&
|
||||
event.key === "f") {
|
||||
if (name === "CmdOrCtrl+F") {
|
||||
this.searchField.focus();
|
||||
event.preventDefault();
|
||||
} else if ((name === "Return" || name === "Space") &&
|
||||
this.element.classList.contains("non-interactive")) {
|
||||
event.preventDefault();
|
||||
} else if (name === "Escape" &&
|
||||
event.target === this.searchField &&
|
||||
this._onClearSearch()) {
|
||||
// Handle the search box's keypress event. If the escape key is pressed,
|
||||
// clear the search box field.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -127,6 +127,10 @@ skip-if = os == "mac" # Bug 1245996 : click on scrollbar not working on OSX
|
||||
[browser_rules_edit-selector_07.js]
|
||||
[browser_rules_edit-selector_08.js]
|
||||
[browser_rules_edit-selector_09.js]
|
||||
[browser_rules_edit-value-after-name_01.js]
|
||||
[browser_rules_edit-value-after-name_02.js]
|
||||
[browser_rules_edit-value-after-name_03.js]
|
||||
[browser_rules_edit-value-after-name_04.js]
|
||||
[browser_rules_editable-field-focus_01.js]
|
||||
[browser_rules_editable-field-focus_02.js]
|
||||
[browser_rules_eyedropper.js]
|
||||
|
@ -43,7 +43,8 @@ function* testColorChangeIsntRevertedWhenOtherTooltipIsShown(ruleView) {
|
||||
});
|
||||
|
||||
let spectrum = yield picker.spectrum;
|
||||
let onModifications = ruleView.once("ruleview-changed");
|
||||
|
||||
let onModifications = waitForNEvents(ruleView, "ruleview-changed", 2);
|
||||
let onHidden = picker.tooltip.once("hidden");
|
||||
EventUtils.sendKey("RETURN", spectrum.element.ownerDocument.defaultView);
|
||||
yield onHidden;
|
||||
@ -53,8 +54,7 @@ function* testColorChangeIsntRevertedWhenOtherTooltipIsShown(ruleView) {
|
||||
let value = getRuleViewProperty(ruleView, "body", "background").valueSpan;
|
||||
let url = value.querySelector(".theme-link");
|
||||
onShown = ruleView.tooltips.previewTooltip.once("shown");
|
||||
let anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip,
|
||||
url);
|
||||
let anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip, url);
|
||||
ruleView.tooltips.previewTooltip.show(anchor);
|
||||
yield onShown;
|
||||
|
||||
|
@ -0,0 +1,107 @@
|
||||
/* 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/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that clicking on swatch-preceeded value while editing the property name
|
||||
// will result in editing the property value. Also tests that the value span is updated
|
||||
// only if the property name has changed. See also Bug 1248274.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type="text/css">
|
||||
#testid {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
<div id="testid">Styled Node</div>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
yield selectNode("#testid", inspector);
|
||||
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
|
||||
yield testColorValueSpanClickWithoutNameChange(propEditor, view);
|
||||
yield testColorValueSpanClickAfterNameChange(propEditor, view);
|
||||
});
|
||||
|
||||
function* testColorValueSpanClickWithoutNameChange(propEditor, view) {
|
||||
info("Test click on color span while focusing property name editor");
|
||||
let colorSpan = propEditor.valueSpan.querySelector(".ruleview-color");
|
||||
|
||||
info("Focus the color name span");
|
||||
yield focusEditableField(view, propEditor.nameSpan);
|
||||
let editor = inplaceEditor(propEditor.doc.activeElement);
|
||||
|
||||
// We add a click event to make sure the color span won't be cleared
|
||||
// on nameSpan blur (which would lead to the click event not being triggered)
|
||||
let onColorSpanClick = once(colorSpan, "click");
|
||||
|
||||
// The property-value-updated is emitted when the valueSpan markup is being
|
||||
// re-populated, which should not be the case when not modifying the property name
|
||||
let onPropertyValueUpdated = function () {
|
||||
ok(false, "The \"property-value-updated\" should not be emitted");
|
||||
};
|
||||
view.on("property-value-updated", onPropertyValueUpdated);
|
||||
|
||||
info("blur propEditor.nameSpan by clicking on the color span");
|
||||
EventUtils.synthesizeMouse(colorSpan, 1, 1, {}, propEditor.doc.defaultView);
|
||||
|
||||
info("wait for the click event on the color span");
|
||||
yield onColorSpanClick;
|
||||
ok(true, "Expected click event was emitted");
|
||||
|
||||
editor = inplaceEditor(propEditor.doc.activeElement);
|
||||
is(inplaceEditor(propEditor.valueSpan), editor,
|
||||
"The property value editor got focused");
|
||||
|
||||
// We remove this listener in order to not cause unwanted conflict in the next test
|
||||
view.off("property-value-updated", onPropertyValueUpdated);
|
||||
|
||||
info("blur valueSpan editor to trigger ruleview-changed event and prevent " +
|
||||
"having pending request");
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
editor.input.blur();
|
||||
yield onRuleViewChanged;
|
||||
}
|
||||
|
||||
function* testColorValueSpanClickAfterNameChange(propEditor, view) {
|
||||
info("Test click on color span after property name change");
|
||||
let colorSpan = propEditor.valueSpan.querySelector(".ruleview-color");
|
||||
|
||||
info("Focus the color name span");
|
||||
yield focusEditableField(view, propEditor.nameSpan);
|
||||
let editor = inplaceEditor(propEditor.doc.activeElement);
|
||||
|
||||
info("Modify the property to border-color to trigger the " +
|
||||
"property-value-updated event");
|
||||
editor.input.value = "border-color";
|
||||
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
let onPropertyValueUpdate = view.once("property-value-updated");
|
||||
|
||||
info("blur propEditor.nameSpan by clicking on the color span");
|
||||
EventUtils.synthesizeMouse(colorSpan, 1, 1, {}, propEditor.doc.defaultView);
|
||||
|
||||
info("wait for ruleview-changed event to be triggered to prevent pending requests");
|
||||
yield onRuleViewChanged;
|
||||
|
||||
info("wait for the property value to be updated");
|
||||
yield onPropertyValueUpdate;
|
||||
ok(true, "Expected \"property-value-updated\" event was emitted");
|
||||
|
||||
editor = inplaceEditor(propEditor.doc.activeElement);
|
||||
is(inplaceEditor(propEditor.valueSpan), editor,
|
||||
"The property value editor got focused");
|
||||
|
||||
info("blur valueSpan editor to trigger ruleview-changed event and prevent " +
|
||||
"having pending request");
|
||||
onRuleViewChanged = view.once("ruleview-changed");
|
||||
editor.input.blur();
|
||||
yield onRuleViewChanged;
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/* 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/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that hitting shift + click on color swatch while editing the property
|
||||
// name will only change the color unit and not lead to edit the property value.
|
||||
// See also Bug 1248274.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type="text/css">
|
||||
#testid {
|
||||
color: red;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgb(183,222,237),
|
||||
rgb(33,180,226),
|
||||
rgb(31,170,217),
|
||||
rgba(200,170,140,0.5));
|
||||
}
|
||||
</style>
|
||||
<div id="testid">Styled Node</div>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
info("Test shift + click on color swatch while editing property name");
|
||||
|
||||
yield selectNode("#testid", inspector);
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[1].editor;
|
||||
let swatchSpan = propEditor.valueSpan.querySelectorAll(".ruleview-colorswatch")[2];
|
||||
|
||||
info("Focus the background name span");
|
||||
yield focusEditableField(view, propEditor.nameSpan);
|
||||
let editor = inplaceEditor(propEditor.doc.activeElement);
|
||||
|
||||
info("Modify the property to background-image to trigger the " +
|
||||
"property-value-updated event");
|
||||
editor.input.value = "background-image";
|
||||
|
||||
let onPropertyValueUpdate = view.once("property-value-updated");
|
||||
let onSwatchUnitChange = swatchSpan.once("unit-change");
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
|
||||
info("blur propEditor.nameSpan by clicking on the color swatch");
|
||||
EventUtils.synthesizeMouseAtCenter(swatchSpan, {shiftKey: true},
|
||||
propEditor.doc.defaultView);
|
||||
|
||||
info("wait for ruleview-changed event to be triggered to prevent pending requests");
|
||||
yield onRuleViewChanged;
|
||||
|
||||
info("wait for the color unit to change");
|
||||
yield onSwatchUnitChange;
|
||||
ok(true, "the color unit was changed");
|
||||
|
||||
info("wait for the property value to be updated");
|
||||
yield onPropertyValueUpdate;
|
||||
|
||||
ok(!inplaceEditor(propEditor.valueSpan), "The inplace editor wasn't shown " +
|
||||
"as a result of the color swatch shift + click");
|
||||
});
|
@ -0,0 +1,69 @@
|
||||
/* 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/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that clicking on color swatch while editing the property name
|
||||
// will show the color tooltip with the correct value. See also Bug 1248274.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type="text/css">
|
||||
#testid {
|
||||
color: red;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgb(183,222,237),
|
||||
rgb(33,180,226),
|
||||
rgb(31,170,217),
|
||||
rgba(200,170,140,0.5));
|
||||
}
|
||||
</style>
|
||||
<div id="testid">Styled Node</div>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
info("Test click on color swatch while editing property name");
|
||||
|
||||
yield selectNode("#testid", inspector);
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[1].editor;
|
||||
let swatchSpan = propEditor.valueSpan.querySelectorAll(
|
||||
".ruleview-colorswatch")[3];
|
||||
let colorPicker = view.tooltips.colorPicker;
|
||||
|
||||
info("Focus the background name span");
|
||||
yield focusEditableField(view, propEditor.nameSpan);
|
||||
let editor = inplaceEditor(propEditor.doc.activeElement);
|
||||
|
||||
info("Modify the background property to background-image to trigger the " +
|
||||
"property-value-updated event");
|
||||
editor.input.value = "background-image";
|
||||
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
let onPropertyValueUpdate = view.once("property-value-updated");
|
||||
let onShown = colorPicker.tooltip.once("shown");
|
||||
|
||||
info("blur propEditor.nameSpan by clicking on the color swatch");
|
||||
EventUtils.synthesizeMouseAtCenter(swatchSpan, {},
|
||||
propEditor.doc.defaultView);
|
||||
|
||||
info("wait for ruleview-changed event to be triggered to prevent pending requests");
|
||||
yield onRuleViewChanged;
|
||||
|
||||
info("wait for the property value to be updated");
|
||||
yield onPropertyValueUpdate;
|
||||
|
||||
info("wait for the color picker to be shown");
|
||||
yield onShown;
|
||||
|
||||
ok(true, "The color picker was shown on click of the color swatch");
|
||||
ok(!inplaceEditor(propEditor.valueSpan),
|
||||
"The inplace editor wasn't shown as a result of the color swatch click");
|
||||
|
||||
let spectrum = yield colorPicker.spectrum;
|
||||
is(spectrum.rgb, "200,170,140,0.5", "The correct color picker was shown");
|
||||
});
|
@ -0,0 +1,62 @@
|
||||
/* 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/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that clicking on a property's value URL while editing the property name
|
||||
// will open the link in a new tab. See also Bug 1248274.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type="text/css">
|
||||
#testid {
|
||||
background: url("chrome://global/skin/icons/warning-64.png"), linear-gradient(white, #F06 400px);
|
||||
}
|
||||
</style>
|
||||
<div id="testid">Styled Node</div>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
info("Test click on background-image url while editing property name");
|
||||
|
||||
yield selectNode("#testid", inspector);
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
let anchor = propEditor.valueSpan.querySelector(".ruleview-propertyvalue .theme-link");
|
||||
|
||||
info("Focus the background name span");
|
||||
yield focusEditableField(view, propEditor.nameSpan);
|
||||
let editor = inplaceEditor(propEditor.doc.activeElement);
|
||||
|
||||
info("Modify the property to background to trigger the " +
|
||||
"property-value-updated event");
|
||||
editor.input.value = "background-image";
|
||||
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
let onPropertyValueUpdate = view.once("property-value-updated");
|
||||
let onTabOpened = waitForTab();
|
||||
|
||||
info("blur propEditor.nameSpan by clicking on the link");
|
||||
// The url can be wrapped across multiple lines, and so we click the lower left corner
|
||||
// of the anchor to make sure to target the link.
|
||||
let rect = anchor.getBoundingClientRect();
|
||||
EventUtils.synthesizeMouse(anchor, 2, rect.height - 2, {}, propEditor.doc.defaultView);
|
||||
|
||||
info("wait for ruleview-changed event to be triggered to prevent pending requests");
|
||||
yield onRuleViewChanged;
|
||||
|
||||
info("wait for the property value to be updated");
|
||||
yield onPropertyValueUpdate;
|
||||
|
||||
info("wait for the image to be open in a new tab");
|
||||
let tab = yield onTabOpened;
|
||||
ok(true, "A new tab opened");
|
||||
|
||||
is(tab.linkedBrowser.currentURI.spec, anchor.href,
|
||||
"The URL for the new tab is correct");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
@ -23,6 +23,24 @@ const {
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const SHARED_SWATCH_CLASS = "ruleview-swatch";
|
||||
const COLOR_SWATCH_CLASS = "ruleview-colorswatch";
|
||||
const BEZIER_SWATCH_CLASS = "ruleview-bezierswatch";
|
||||
const FILTER_SWATCH_CLASS = "ruleview-filterswatch";
|
||||
const ANGLE_SWATCH_CLASS = "ruleview-angleswatch";
|
||||
|
||||
/*
|
||||
* An actionable element is an element which on click triggers a specific action
|
||||
* (e.g. shows a color tooltip, opens a link, …).
|
||||
*/
|
||||
const ACTIONABLE_ELEMENTS_SELECTORS = [
|
||||
`.${COLOR_SWATCH_CLASS}`,
|
||||
`.${BEZIER_SWATCH_CLASS}`,
|
||||
`.${FILTER_SWATCH_CLASS}`,
|
||||
`.${ANGLE_SWATCH_CLASS}`,
|
||||
"a"
|
||||
];
|
||||
|
||||
/**
|
||||
* TextPropertyEditor is responsible for the following:
|
||||
* Owns a TextProperty object.
|
||||
@ -44,6 +62,8 @@ function TextPropertyEditor(ruleEditor, property) {
|
||||
this.prop.editor = this;
|
||||
this.browserWindow = this.doc.defaultView.top;
|
||||
this._populatedComputed = false;
|
||||
this._hasPendingClick = false;
|
||||
this._clickedElementOptions = null;
|
||||
|
||||
const toolbox = this.ruleView.inspector.toolbox;
|
||||
this.cssProperties = getCssProperties(toolbox);
|
||||
@ -58,6 +78,7 @@ function TextPropertyEditor(ruleEditor, property) {
|
||||
this._onSwatchRevert = this._onSwatchRevert.bind(this);
|
||||
this._onValidate = throttle(this._previewValue, 10, this);
|
||||
this.update = this.update.bind(this);
|
||||
this.updatePropertyState = this.updatePropertyState.bind(this);
|
||||
|
||||
this._create();
|
||||
this.update();
|
||||
@ -195,7 +216,7 @@ TextPropertyEditor.prototype = {
|
||||
start: this._onStartEditing,
|
||||
element: this.nameSpan,
|
||||
done: this._onNameDone,
|
||||
destroy: this.update,
|
||||
destroy: this.updatePropertyState,
|
||||
advanceChars: ":",
|
||||
contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY,
|
||||
popup: this.popup
|
||||
@ -215,6 +236,37 @@ TextPropertyEditor.prototype = {
|
||||
}
|
||||
}, false);
|
||||
|
||||
// The mousedown event could trigger a blur event on nameContainer, which
|
||||
// will trigger a call to the update function. The update function clears
|
||||
// valueSpan's markup. Thus the regular click event does not bubble up, and
|
||||
// listener's callbacks are not called.
|
||||
// So we need to remember where the user clicks in order to re-trigger the click
|
||||
// after the valueSpan's markup is re-populated. We only need to track this for
|
||||
// valueSpan's child elements, because direct click on valueSpan will always
|
||||
// trigger a click event.
|
||||
this.valueSpan.addEventListener("mousedown", (event) => {
|
||||
let clickedEl = event.target;
|
||||
if (clickedEl === this.valueSpan) {
|
||||
return;
|
||||
}
|
||||
this._hasPendingClick = true;
|
||||
|
||||
let matchedSelector = ACTIONABLE_ELEMENTS_SELECTORS.find(
|
||||
(selector) => clickedEl.matches(selector));
|
||||
if (matchedSelector) {
|
||||
let similarElements = [...this.valueSpan.querySelectorAll(matchedSelector)];
|
||||
this._clickedElementOptions = {
|
||||
selector: matchedSelector,
|
||||
index: similarElements.indexOf(clickedEl)
|
||||
};
|
||||
}
|
||||
}, false);
|
||||
|
||||
this.valueSpan.addEventListener("mouseup", (event) => {
|
||||
this._clickedElementOptions = null;
|
||||
this._hasPendingClick = false;
|
||||
}, false);
|
||||
|
||||
this.valueSpan.addEventListener("click", (event) => {
|
||||
let target = event.target;
|
||||
|
||||
@ -263,27 +315,7 @@ TextPropertyEditor.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.prop.enabled) {
|
||||
this.enable.style.removeProperty("visibility");
|
||||
this.enable.setAttribute("checked", "");
|
||||
} else {
|
||||
this.enable.style.visibility = "visible";
|
||||
this.enable.removeAttribute("checked");
|
||||
}
|
||||
|
||||
this.warning.hidden = this.editing || this.isValid();
|
||||
this.filterProperty.hidden = this.editing ||
|
||||
!this.isValid() ||
|
||||
!this.prop.overridden ||
|
||||
this.ruleEditor.rule.isUnmatched;
|
||||
|
||||
if (!this.editing &&
|
||||
(this.prop.overridden || !this.prop.enabled ||
|
||||
!this.prop.isKnownProperty())) {
|
||||
this.element.classList.add("ruleview-overridden");
|
||||
} else {
|
||||
this.element.classList.remove("ruleview-overridden");
|
||||
}
|
||||
this.updatePropertyState();
|
||||
|
||||
let name = this.prop.name;
|
||||
this.nameSpan.textContent = name;
|
||||
@ -305,21 +337,15 @@ TextPropertyEditor.prototype = {
|
||||
this.element.removeAttribute("dirty");
|
||||
}
|
||||
|
||||
const sharedSwatchClass = "ruleview-swatch ";
|
||||
const colorSwatchClass = "ruleview-colorswatch";
|
||||
const bezierSwatchClass = "ruleview-bezierswatch";
|
||||
const filterSwatchClass = "ruleview-filterswatch";
|
||||
const angleSwatchClass = "ruleview-angleswatch";
|
||||
|
||||
let outputParser = this.ruleView._outputParser;
|
||||
let parserOptions = {
|
||||
colorSwatchClass: sharedSwatchClass + colorSwatchClass,
|
||||
colorSwatchClass: SHARED_SWATCH_CLASS + " " + COLOR_SWATCH_CLASS,
|
||||
colorClass: "ruleview-color",
|
||||
bezierSwatchClass: sharedSwatchClass + bezierSwatchClass,
|
||||
bezierSwatchClass: SHARED_SWATCH_CLASS + " " + BEZIER_SWATCH_CLASS,
|
||||
bezierClass: "ruleview-bezier",
|
||||
filterSwatchClass: sharedSwatchClass + filterSwatchClass,
|
||||
filterSwatchClass: SHARED_SWATCH_CLASS + " " + FILTER_SWATCH_CLASS,
|
||||
filterClass: "ruleview-filter",
|
||||
angleSwatchClass: sharedSwatchClass + angleSwatchClass,
|
||||
angleSwatchClass: SHARED_SWATCH_CLASS + " " + ANGLE_SWATCH_CLASS,
|
||||
angleClass: "ruleview-angle",
|
||||
defaultColorType: !propDirty,
|
||||
urlClass: "theme-link",
|
||||
@ -329,9 +355,11 @@ TextPropertyEditor.prototype = {
|
||||
this.valueSpan.innerHTML = "";
|
||||
this.valueSpan.appendChild(frag);
|
||||
|
||||
this.ruleView.emit("property-value-updated", this.valueSpan);
|
||||
|
||||
// Attach the color picker tooltip to the color swatches
|
||||
this._colorSwatchSpans =
|
||||
this.valueSpan.querySelectorAll("." + colorSwatchClass);
|
||||
this.valueSpan.querySelectorAll("." + COLOR_SWATCH_CLASS);
|
||||
if (this.ruleEditor.isEditable) {
|
||||
for (let span of this._colorSwatchSpans) {
|
||||
// Adding this swatch to the list of swatches our colorpicker
|
||||
@ -350,7 +378,7 @@ TextPropertyEditor.prototype = {
|
||||
|
||||
// Attach the cubic-bezier tooltip to the bezier swatches
|
||||
this._bezierSwatchSpans =
|
||||
this.valueSpan.querySelectorAll("." + bezierSwatchClass);
|
||||
this.valueSpan.querySelectorAll("." + BEZIER_SWATCH_CLASS);
|
||||
if (this.ruleEditor.isEditable) {
|
||||
for (let span of this._bezierSwatchSpans) {
|
||||
// Adding this swatch to the list of swatches our colorpicker
|
||||
@ -367,7 +395,7 @@ TextPropertyEditor.prototype = {
|
||||
}
|
||||
|
||||
// Attach the filter editor tooltip to the filter swatch
|
||||
let span = this.valueSpan.querySelector("." + filterSwatchClass);
|
||||
let span = this.valueSpan.querySelector("." + FILTER_SWATCH_CLASS);
|
||||
if (this.ruleEditor.isEditable) {
|
||||
if (span) {
|
||||
parserOptions.filterSwatch = true;
|
||||
@ -384,7 +412,7 @@ TextPropertyEditor.prototype = {
|
||||
}
|
||||
|
||||
this.angleSwatchSpans =
|
||||
this.valueSpan.querySelectorAll("." + angleSwatchClass);
|
||||
this.valueSpan.querySelectorAll("." + ANGLE_SWATCH_CLASS);
|
||||
if (this.ruleEditor.isEditable) {
|
||||
for (let angleSpan of this.angleSwatchSpans) {
|
||||
angleSpan.on("unit-change", this._onSwatchCommit);
|
||||
@ -393,6 +421,26 @@ TextPropertyEditor.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have updated the property's value, we might have a pending
|
||||
// click on the value container. If we do, we have to trigger a click event
|
||||
// on the right element.
|
||||
if (this._hasPendingClick) {
|
||||
this._hasPendingClick = false;
|
||||
let elToClick;
|
||||
|
||||
if (this._clickedElementOptions !== null) {
|
||||
let {selector, index} = this._clickedElementOptions;
|
||||
elToClick = this.valueSpan.querySelectorAll(selector)[index];
|
||||
|
||||
this._clickedElementOptions = null;
|
||||
}
|
||||
|
||||
if (!elToClick) {
|
||||
elToClick = this.valueSpan;
|
||||
}
|
||||
elToClick.click();
|
||||
}
|
||||
|
||||
// Populate the computed styles.
|
||||
this._updateComputed();
|
||||
|
||||
@ -405,6 +453,34 @@ TextPropertyEditor.prototype = {
|
||||
this.enable.style.visibility = "hidden";
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the visibility of the enable checkbox, the warning indicator and
|
||||
* the filter property, as well as the overriden state of the property.
|
||||
*/
|
||||
updatePropertyState: function () {
|
||||
if (this.prop.enabled) {
|
||||
this.enable.style.removeProperty("visibility");
|
||||
this.enable.setAttribute("checked", "");
|
||||
} else {
|
||||
this.enable.style.visibility = "visible";
|
||||
this.enable.removeAttribute("checked");
|
||||
}
|
||||
|
||||
this.warning.hidden = this.editing || this.isValid();
|
||||
this.filterProperty.hidden = this.editing ||
|
||||
!this.isValid() ||
|
||||
!this.prop.overridden ||
|
||||
this.ruleEditor.rule.isUnmatched;
|
||||
|
||||
if (!this.editing &&
|
||||
(this.prop.overridden || !this.prop.enabled ||
|
||||
!this.prop.isKnownProperty())) {
|
||||
this.element.classList.add("ruleview-overridden");
|
||||
} else {
|
||||
this.element.classList.remove("ruleview-overridden");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the indicator for computed styles. The computed styles themselves
|
||||
* are populated on demand, when they become visible.
|
||||
|
@ -12,8 +12,14 @@
|
||||
// - in-content highlighters that appear when hovering over property values
|
||||
// - etc.
|
||||
|
||||
const {getTheme} = require("devtools/client/shared/theme");
|
||||
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
|
||||
const {
|
||||
getImageDimensions,
|
||||
setImageTooltip,
|
||||
setBrokenImageTooltip,
|
||||
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
|
||||
const {
|
||||
Tooltip,
|
||||
SwatchColorPickerTooltip,
|
||||
SwatchCubicBezierTooltip,
|
||||
CssDocsTooltip,
|
||||
@ -273,7 +279,9 @@ TooltipsOverlay.prototype = {
|
||||
let panelDoc = this.view.inspector.panelDoc;
|
||||
|
||||
// Image, fonts, ... preview tooltip
|
||||
this.previewTooltip = new Tooltip(panelDoc);
|
||||
this.previewTooltip = new HTMLTooltip(this.view.inspector.toolbox, {
|
||||
type: "arrow"
|
||||
});
|
||||
this.previewTooltip.startTogglingOnHover(this.view.element,
|
||||
this._onPreviewTooltipTargetHover.bind(this));
|
||||
|
||||
@ -395,23 +403,85 @@ TooltipsOverlay.prototype = {
|
||||
let inspector = this.view.inspector;
|
||||
|
||||
if (type === TOOLTIP_IMAGE_TYPE) {
|
||||
let dim = Services.prefs.getIntPref(PREF_IMAGE_TOOLTIP_SIZE);
|
||||
// nodeInfo contains an absolute uri
|
||||
let uri = nodeInfo.value.url;
|
||||
yield this.previewTooltip.setRelativeImageContent(uri,
|
||||
inspector.inspector, dim);
|
||||
try {
|
||||
yield this._setImagePreviewTooltip(nodeInfo.value.url);
|
||||
} catch (e) {
|
||||
yield setBrokenImageTooltip(this.previewTooltip, this.view.inspector.panelDoc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type === TOOLTIP_FONTFAMILY_TYPE) {
|
||||
yield this.previewTooltip.setFontFamilyContent(nodeInfo.value.value,
|
||||
inspector.selection.nodeFront);
|
||||
let font = nodeInfo.value.value;
|
||||
let nodeFront = inspector.selection.nodeFront;
|
||||
yield this._setFontPreviewTooltip(font, nodeFront);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Set the content of the preview tooltip to display an image preview. The image URL can
|
||||
* be relative, a call will be made to the debuggee to retrieve the image content as an
|
||||
* imageData URI.
|
||||
*
|
||||
* @param {String} imageUrl
|
||||
* The image url value (may be relative or absolute).
|
||||
* @return {Promise} A promise that resolves when the preview tooltip content is ready
|
||||
*/
|
||||
_setImagePreviewTooltip: Task.async(function* (imageUrl) {
|
||||
let doc = this.view.inspector.panelDoc;
|
||||
let maxDim = Services.prefs.getIntPref(PREF_IMAGE_TOOLTIP_SIZE);
|
||||
|
||||
let naturalWidth, naturalHeight;
|
||||
if (imageUrl.startsWith("data:")) {
|
||||
// If the imageUrl already is a data-url, save ourselves a round-trip
|
||||
let size = yield getImageDimensions(doc, imageUrl);
|
||||
naturalWidth = size.naturalWidth;
|
||||
naturalHeight = size.naturalHeight;
|
||||
} else {
|
||||
let inspectorFront = this.view.inspector.inspector;
|
||||
let {data, size} = yield inspectorFront.getImageDataFromURL(imageUrl, maxDim);
|
||||
imageUrl = yield data.string();
|
||||
naturalWidth = size.naturalWidth;
|
||||
naturalHeight = size.naturalHeight;
|
||||
}
|
||||
|
||||
yield setImageTooltip(this.previewTooltip, doc, imageUrl,
|
||||
{maxDim, naturalWidth, naturalHeight});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Set the content of the preview tooltip to display a font family preview.
|
||||
*
|
||||
* @param {String} font
|
||||
* The font family value.
|
||||
* @param {object} nodeFront
|
||||
* The NodeActor that will used to retrieve the dataURL for the font
|
||||
* family tooltip contents.
|
||||
* @return {Promise} A promise that resolves when the preview tooltip content is ready
|
||||
*/
|
||||
_setFontPreviewTooltip: Task.async(function* (font, nodeFront) {
|
||||
if (!font || !nodeFront || typeof nodeFront.getFontFamilyDataURL !== "function") {
|
||||
throw new Error("Unable to create font preview tooltip content.");
|
||||
}
|
||||
|
||||
font = font.replace(/"/g, "'");
|
||||
font = font.replace("!important", "");
|
||||
font = font.trim();
|
||||
|
||||
let fillStyle = getTheme() === "light" ? "black" : "white";
|
||||
let {data, size: maxDim} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
|
||||
|
||||
let imageUrl = yield data.string();
|
||||
let doc = this.view.inspector.panelDoc;
|
||||
let {naturalWidth, naturalHeight} = yield getImageDimensions(doc, imageUrl);
|
||||
|
||||
yield setImageTooltip(this.previewTooltip, doc, imageUrl,
|
||||
{hideDimensionLabel: true, maxDim, naturalWidth, naturalHeight});
|
||||
}),
|
||||
|
||||
_onNewSelection: function () {
|
||||
if (this.previewTooltip) {
|
||||
this.previewTooltip.hide();
|
||||
|
@ -65,7 +65,7 @@ function* testBodyRuleView(view) {
|
||||
|
||||
yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
|
||||
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].getAttribute("src")
|
||||
.indexOf("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHe") !== -1,
|
||||
@ -81,7 +81,7 @@ function* testDivRuleView(view) {
|
||||
|
||||
yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
|
||||
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].getAttribute("src").startsWith("data:"),
|
||||
"Tooltip contains a data-uri image as expected");
|
||||
@ -117,7 +117,7 @@ function* testComputedView(view) {
|
||||
|
||||
yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
|
||||
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
|
||||
ok(images[0].getAttribute("src").startsWith("data:"),
|
||||
|
@ -7,6 +7,7 @@
|
||||
// Test that if a tooltip is visible when a new selection is made, it closes
|
||||
|
||||
const TEST_URI = "<div class='one'>el 1</div><div class='two'>el 2</div>";
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
@ -25,7 +26,9 @@ function* testRuleView(ruleView, inspector) {
|
||||
info("Showing the tooltip");
|
||||
|
||||
let tooltip = ruleView.tooltips.previewTooltip;
|
||||
tooltip.setTextContent({messages: ["rule-view tooltip"]});
|
||||
let tooltipContent = ruleView.styleDocument.createElementNS(XHTML_NS, "div");
|
||||
yield tooltip.setContent(tooltipContent, 100, 30);
|
||||
|
||||
// Stop listening for mouse movements because it's not needed for this test,
|
||||
// and causes intermittent failures on Linux. When this test runs in the suite
|
||||
// sometimes a mouseleave event is dispatched at the start, which causes the
|
||||
@ -48,7 +51,8 @@ function* testComputedView(computedView, inspector) {
|
||||
info("Showing the tooltip");
|
||||
|
||||
let tooltip = computedView.tooltips.previewTooltip;
|
||||
tooltip.setTextContent({messages: ["computed-view tooltip"]});
|
||||
let tooltipContent = computedView.styleDocument.createElementNS(XHTML_NS, "div");
|
||||
yield tooltip.setContent(tooltipContent, 100, 30);
|
||||
// Stop listening for mouse movements because it's not needed for this test,
|
||||
// and causes intermittent failures on Linux. When this test runs in the suite
|
||||
// sometimes a mouseleave event is dispatched at the start, which causes the
|
||||
|
@ -51,7 +51,7 @@ function* testRuleView(ruleView, nodeFront) {
|
||||
// And verify that the tooltip gets shown on this property
|
||||
yield assertHoverTooltipOn(tooltip, valueSpan);
|
||||
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].getAttribute("src").startsWith("data:"),
|
||||
"Tooltip contains a data-uri image as expected");
|
||||
@ -70,7 +70,7 @@ function* testComputedView(computedView, nodeFront) {
|
||||
|
||||
yield assertHoverTooltipOn(tooltip, valueSpan);
|
||||
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].getAttribute("src").startsWith("data:"),
|
||||
"Tooltip contains a data-uri image as expected");
|
||||
@ -97,7 +97,7 @@ function* testExpandedComputedViewProperty(computedView, nodeFront) {
|
||||
|
||||
yield assertHoverTooltipOn(tooltip, valueSpan);
|
||||
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].getAttribute("src").startsWith("data:"),
|
||||
"Tooltip contains a data-uri image as expected");
|
||||
|
@ -45,7 +45,7 @@ function* testComputedViewUrls(inspector) {
|
||||
*/
|
||||
function* performChecks(view, propertyValue) {
|
||||
function checkTooltip(panel, imageSrc) {
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
is(images[0].getAttribute("src"), imageSrc, "The image URL is correct");
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ function* testRuleView(ruleView, nodeFront) {
|
||||
// And verify that the tooltip gets shown on this property
|
||||
yield assertHoverTooltipOn(tooltip, valueSpan);
|
||||
|
||||
let images = panel.getElementsByTagName("image");
|
||||
let images = panel.getElementsByTagName("img");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].getAttribute("src")
|
||||
.startsWith("data:"), "Tooltip contains a data-uri image as expected");
|
||||
|
@ -39,12 +39,12 @@ function* testImageDimension(ruleView) {
|
||||
|
||||
info("Showing the tooltip");
|
||||
let onShown = tooltip.once("shown");
|
||||
tooltip.show();
|
||||
tooltip.show(uriSpan);
|
||||
yield onShown;
|
||||
|
||||
// Let's not test for a specific size, but instead let's make sure it's at
|
||||
// least as big as the image
|
||||
let imageRect = panel.querySelector("image").getBoundingClientRect();
|
||||
let imageRect = panel.querySelector("img").getBoundingClientRect();
|
||||
let panelRect = panel.getBoundingClientRect();
|
||||
|
||||
ok(panelRect.width >= imageRect.width,
|
||||
|
@ -50,7 +50,7 @@ function* switchToFrameContext(frameIndex, toolbox, inspector) {
|
||||
info("Select the iframe in the frame list.");
|
||||
let newRoot = inspector.once("new-root");
|
||||
|
||||
menu.menuitems[frameIndex].click();
|
||||
menu.items[frameIndex].click();
|
||||
|
||||
yield newRoot;
|
||||
yield inspector.once("inspector-updated");
|
||||
|
@ -33,7 +33,7 @@ add_task(function* () {
|
||||
yield once(menu, "open");
|
||||
|
||||
// Verify that the menu is popuplated.
|
||||
let frames = menu.menuitems.slice();
|
||||
let frames = menu.items.slice();
|
||||
is(frames.length, 2, "We have both frames in the menu");
|
||||
|
||||
frames.sort(function (a, b) {
|
||||
|
@ -83,6 +83,12 @@ module.exports = createClass({
|
||||
modalClass += " hidden";
|
||||
}
|
||||
|
||||
const sortedDevices = {};
|
||||
for (let type of devices.types) {
|
||||
sortedDevices[type] = Object.assign([], devices[type])
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: modalClass,
|
||||
@ -108,7 +114,7 @@ module.exports = createClass({
|
||||
},
|
||||
type
|
||||
),
|
||||
devices[type].map(device => {
|
||||
sortedDevices[type].map(device => {
|
||||
return dom.label(
|
||||
{
|
||||
className: "device-label",
|
||||
|
@ -8,7 +8,7 @@
|
||||
const { Cc, Ci, Cu, Cr } = require("chrome");
|
||||
const promise = require("promise");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { WebGLFront } = require("devtools/server/actors/webgl");
|
||||
const { WebGLFront } = require("devtools/shared/fronts/webgl");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
|
||||
function ShaderEditorPanel(iframeWindow, toolbox) {
|
||||
|
@ -12,7 +12,7 @@ var promise = require("promise");
|
||||
var { gDevTools } = require("devtools/client/framework/devtools");
|
||||
var { DebuggerClient } = require("devtools/shared/client/main");
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { WebGLFront } = require("devtools/server/actors/webgl");
|
||||
var { WebGLFront } = require("devtools/shared/fronts/webgl");
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
var { TargetFactory } = require("devtools/client/framework/target");
|
||||
var { Toolbox } = require("devtools/client/framework/toolbox");
|
||||
|
@ -1,422 +0,0 @@
|
||||
/* 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";
|
||||
|
||||
/**
|
||||
* This list is generated from the output of the CssPropertiesActor. If a server
|
||||
* does not support the actor, this is loaded as a backup. This list does not
|
||||
* guarantee that the server actually supports these CSS properties.
|
||||
*/
|
||||
exports.propertiesList = [
|
||||
"align-content",
|
||||
"align-items",
|
||||
"align-self",
|
||||
"animation-delay",
|
||||
"animation-direction",
|
||||
"animation-duration",
|
||||
"animation-fill-mode",
|
||||
"animation-iteration-count",
|
||||
"animation-name",
|
||||
"animation-play-state",
|
||||
"animation-timing-function",
|
||||
"-moz-appearance",
|
||||
"backface-visibility",
|
||||
"background-attachment",
|
||||
"background-blend-mode",
|
||||
"background-clip",
|
||||
"background-color",
|
||||
"background-image",
|
||||
"background-origin",
|
||||
"background-position-x",
|
||||
"background-position-y",
|
||||
"background-repeat",
|
||||
"background-size",
|
||||
"-moz-binding",
|
||||
"block-size",
|
||||
"border-block-end-color",
|
||||
"border-block-end-style",
|
||||
"border-block-end-width",
|
||||
"border-block-start-color",
|
||||
"border-block-start-style",
|
||||
"border-block-start-width",
|
||||
"border-bottom-color",
|
||||
"-moz-border-bottom-colors",
|
||||
"border-bottom-left-radius",
|
||||
"border-bottom-right-radius",
|
||||
"border-bottom-style",
|
||||
"border-bottom-width",
|
||||
"border-collapse",
|
||||
"border-image-outset",
|
||||
"border-image-repeat",
|
||||
"border-image-slice",
|
||||
"border-image-source",
|
||||
"border-image-width",
|
||||
"border-inline-end-color",
|
||||
"border-inline-end-style",
|
||||
"border-inline-end-width",
|
||||
"border-inline-start-color",
|
||||
"border-inline-start-style",
|
||||
"border-inline-start-width",
|
||||
"border-left-color",
|
||||
"-moz-border-left-colors",
|
||||
"border-left-style",
|
||||
"border-left-width",
|
||||
"border-right-color",
|
||||
"-moz-border-right-colors",
|
||||
"border-right-style",
|
||||
"border-right-width",
|
||||
"border-spacing",
|
||||
"border-top-color",
|
||||
"-moz-border-top-colors",
|
||||
"border-top-left-radius",
|
||||
"border-top-right-radius",
|
||||
"border-top-style",
|
||||
"border-top-width",
|
||||
"bottom",
|
||||
"-moz-box-align",
|
||||
"box-decoration-break",
|
||||
"-moz-box-direction",
|
||||
"-moz-box-flex",
|
||||
"-moz-box-ordinal-group",
|
||||
"-moz-box-orient",
|
||||
"-moz-box-pack",
|
||||
"box-shadow",
|
||||
"box-sizing",
|
||||
"caption-side",
|
||||
"clear",
|
||||
"clip",
|
||||
"clip-path",
|
||||
"clip-rule",
|
||||
"color",
|
||||
"color-adjust",
|
||||
"color-interpolation",
|
||||
"color-interpolation-filters",
|
||||
"-moz-column-count",
|
||||
"-moz-column-fill",
|
||||
"-moz-column-gap",
|
||||
"-moz-column-rule-color",
|
||||
"-moz-column-rule-style",
|
||||
"-moz-column-rule-width",
|
||||
"-moz-column-width",
|
||||
"content",
|
||||
"-moz-control-character-visibility",
|
||||
"counter-increment",
|
||||
"counter-reset",
|
||||
"cursor",
|
||||
"direction",
|
||||
"display",
|
||||
"dominant-baseline",
|
||||
"empty-cells",
|
||||
"fill",
|
||||
"fill-opacity",
|
||||
"fill-rule",
|
||||
"filter",
|
||||
"flex-basis",
|
||||
"flex-direction",
|
||||
"flex-grow",
|
||||
"flex-shrink",
|
||||
"flex-wrap",
|
||||
"float",
|
||||
"-moz-float-edge",
|
||||
"flood-color",
|
||||
"flood-opacity",
|
||||
"font-family",
|
||||
"font-feature-settings",
|
||||
"font-kerning",
|
||||
"font-language-override",
|
||||
"font-size",
|
||||
"font-size-adjust",
|
||||
"font-stretch",
|
||||
"font-style",
|
||||
"font-synthesis",
|
||||
"font-variant-alternates",
|
||||
"font-variant-caps",
|
||||
"font-variant-east-asian",
|
||||
"font-variant-ligatures",
|
||||
"font-variant-numeric",
|
||||
"font-variant-position",
|
||||
"font-weight",
|
||||
"-moz-force-broken-image-icon",
|
||||
"grid-auto-columns",
|
||||
"grid-auto-flow",
|
||||
"grid-auto-rows",
|
||||
"grid-column-end",
|
||||
"grid-column-gap",
|
||||
"grid-column-start",
|
||||
"grid-row-end",
|
||||
"grid-row-gap",
|
||||
"grid-row-start",
|
||||
"grid-template-areas",
|
||||
"grid-template-columns",
|
||||
"grid-template-rows",
|
||||
"height",
|
||||
"hyphens",
|
||||
"image-orientation",
|
||||
"-moz-image-region",
|
||||
"image-rendering",
|
||||
"ime-mode",
|
||||
"inline-size",
|
||||
"isolation",
|
||||
"justify-content",
|
||||
"justify-items",
|
||||
"justify-self",
|
||||
"left",
|
||||
"letter-spacing",
|
||||
"lighting-color",
|
||||
"line-height",
|
||||
"list-style-image",
|
||||
"list-style-position",
|
||||
"list-style-type",
|
||||
"margin-block-end",
|
||||
"margin-block-start",
|
||||
"margin-bottom",
|
||||
"margin-inline-end",
|
||||
"margin-inline-start",
|
||||
"margin-left",
|
||||
"margin-right",
|
||||
"margin-top",
|
||||
"marker-end",
|
||||
"marker-mid",
|
||||
"marker-offset",
|
||||
"marker-start",
|
||||
"mask",
|
||||
"mask-type",
|
||||
"max-block-size",
|
||||
"max-height",
|
||||
"max-inline-size",
|
||||
"max-width",
|
||||
"min-block-size",
|
||||
"min-height",
|
||||
"min-inline-size",
|
||||
"min-width",
|
||||
"mix-blend-mode",
|
||||
"object-fit",
|
||||
"object-position",
|
||||
"offset-block-end",
|
||||
"offset-block-start",
|
||||
"offset-inline-end",
|
||||
"offset-inline-start",
|
||||
"opacity",
|
||||
"order",
|
||||
"-moz-orient",
|
||||
"-moz-osx-font-smoothing",
|
||||
"outline-color",
|
||||
"outline-offset",
|
||||
"-moz-outline-radius-bottomleft",
|
||||
"-moz-outline-radius-bottomright",
|
||||
"-moz-outline-radius-topleft",
|
||||
"-moz-outline-radius-topright",
|
||||
"outline-style",
|
||||
"outline-width",
|
||||
"overflow-x",
|
||||
"overflow-y",
|
||||
"padding-block-end",
|
||||
"padding-block-start",
|
||||
"padding-bottom",
|
||||
"padding-inline-end",
|
||||
"padding-inline-start",
|
||||
"padding-left",
|
||||
"padding-right",
|
||||
"padding-top",
|
||||
"page-break-after",
|
||||
"page-break-before",
|
||||
"page-break-inside",
|
||||
"paint-order",
|
||||
"perspective",
|
||||
"perspective-origin",
|
||||
"pointer-events",
|
||||
"position",
|
||||
"quotes",
|
||||
"resize",
|
||||
"right",
|
||||
"ruby-align",
|
||||
"ruby-position",
|
||||
"scroll-behavior",
|
||||
"scroll-snap-coordinate",
|
||||
"scroll-snap-destination",
|
||||
"scroll-snap-points-x",
|
||||
"scroll-snap-points-y",
|
||||
"scroll-snap-type-x",
|
||||
"scroll-snap-type-y",
|
||||
"shape-rendering",
|
||||
"-moz-stack-sizing",
|
||||
"stop-color",
|
||||
"stop-opacity",
|
||||
"stroke",
|
||||
"stroke-dasharray",
|
||||
"stroke-dashoffset",
|
||||
"stroke-linecap",
|
||||
"stroke-linejoin",
|
||||
"stroke-miterlimit",
|
||||
"stroke-opacity",
|
||||
"stroke-width",
|
||||
"-moz-tab-size",
|
||||
"table-layout",
|
||||
"text-align",
|
||||
"-moz-text-align-last",
|
||||
"text-anchor",
|
||||
"text-combine-upright",
|
||||
"text-decoration-color",
|
||||
"text-decoration-line",
|
||||
"text-decoration-style",
|
||||
"text-emphasis-color",
|
||||
"text-emphasis-position",
|
||||
"text-emphasis-style",
|
||||
"-webkit-text-fill-color",
|
||||
"text-indent",
|
||||
"text-orientation",
|
||||
"text-overflow",
|
||||
"text-rendering",
|
||||
"text-shadow",
|
||||
"-moz-text-size-adjust",
|
||||
"-webkit-text-stroke-color",
|
||||
"-webkit-text-stroke-width",
|
||||
"text-transform",
|
||||
"top",
|
||||
"transform",
|
||||
"transform-box",
|
||||
"transform-origin",
|
||||
"transform-style",
|
||||
"transition-delay",
|
||||
"transition-duration",
|
||||
"transition-property",
|
||||
"transition-timing-function",
|
||||
"unicode-bidi",
|
||||
"-moz-user-focus",
|
||||
"-moz-user-input",
|
||||
"-moz-user-modify",
|
||||
"-moz-user-select",
|
||||
"vector-effect",
|
||||
"vertical-align",
|
||||
"visibility",
|
||||
"white-space",
|
||||
"width",
|
||||
"will-change",
|
||||
"-moz-window-dragging",
|
||||
"word-break",
|
||||
"word-spacing",
|
||||
"word-wrap",
|
||||
"writing-mode",
|
||||
"z-index",
|
||||
"all",
|
||||
"animation",
|
||||
"background",
|
||||
"background-position",
|
||||
"border",
|
||||
"border-block-end",
|
||||
"border-block-start",
|
||||
"border-bottom",
|
||||
"border-color",
|
||||
"border-image",
|
||||
"border-inline-end",
|
||||
"border-inline-start",
|
||||
"border-left",
|
||||
"border-radius",
|
||||
"border-right",
|
||||
"border-style",
|
||||
"border-top",
|
||||
"border-width",
|
||||
"-moz-column-rule",
|
||||
"-moz-columns",
|
||||
"flex",
|
||||
"flex-flow",
|
||||
"font",
|
||||
"font-variant",
|
||||
"grid",
|
||||
"grid-area",
|
||||
"grid-column",
|
||||
"grid-gap",
|
||||
"grid-row",
|
||||
"grid-template",
|
||||
"list-style",
|
||||
"margin",
|
||||
"marker",
|
||||
"outline",
|
||||
"-moz-outline-radius",
|
||||
"overflow",
|
||||
"padding",
|
||||
"scroll-snap-type",
|
||||
"text-decoration",
|
||||
"text-emphasis",
|
||||
"-webkit-text-stroke",
|
||||
"-moz-transform",
|
||||
"transition",
|
||||
"-moz-transform-origin",
|
||||
"-moz-perspective-origin",
|
||||
"-moz-perspective",
|
||||
"-moz-transform-style",
|
||||
"-moz-backface-visibility",
|
||||
"-moz-border-image",
|
||||
"-moz-transition",
|
||||
"-moz-transition-delay",
|
||||
"-moz-transition-duration",
|
||||
"-moz-transition-property",
|
||||
"-moz-transition-timing-function",
|
||||
"-moz-animation",
|
||||
"-moz-animation-delay",
|
||||
"-moz-animation-direction",
|
||||
"-moz-animation-duration",
|
||||
"-moz-animation-fill-mode",
|
||||
"-moz-animation-iteration-count",
|
||||
"-moz-animation-name",
|
||||
"-moz-animation-play-state",
|
||||
"-moz-animation-timing-function",
|
||||
"-moz-box-sizing",
|
||||
"-moz-font-feature-settings",
|
||||
"-moz-font-language-override",
|
||||
"-moz-padding-end",
|
||||
"-moz-padding-start",
|
||||
"-moz-margin-end",
|
||||
"-moz-margin-start",
|
||||
"-moz-border-end",
|
||||
"-moz-border-end-color",
|
||||
"-moz-border-end-style",
|
||||
"-moz-border-end-width",
|
||||
"-moz-border-start",
|
||||
"-moz-border-start-color",
|
||||
"-moz-border-start-style",
|
||||
"-moz-border-start-width",
|
||||
"-moz-hyphens",
|
||||
"-webkit-animation",
|
||||
"-webkit-animation-delay",
|
||||
"-webkit-animation-direction",
|
||||
"-webkit-animation-duration",
|
||||
"-webkit-animation-fill-mode",
|
||||
"-webkit-animation-iteration-count",
|
||||
"-webkit-animation-name",
|
||||
"-webkit-animation-play-state",
|
||||
"-webkit-animation-timing-function",
|
||||
"-webkit-filter",
|
||||
"-webkit-text-size-adjust",
|
||||
"-webkit-transform",
|
||||
"-webkit-transform-origin",
|
||||
"-webkit-transform-style",
|
||||
"-webkit-backface-visibility",
|
||||
"-webkit-perspective",
|
||||
"-webkit-perspective-origin",
|
||||
"-webkit-transition",
|
||||
"-webkit-transition-delay",
|
||||
"-webkit-transition-duration",
|
||||
"-webkit-transition-property",
|
||||
"-webkit-transition-timing-function",
|
||||
"-webkit-border-radius",
|
||||
"-webkit-border-top-left-radius",
|
||||
"-webkit-border-top-right-radius",
|
||||
"-webkit-border-bottom-left-radius",
|
||||
"-webkit-border-bottom-right-radius",
|
||||
"-webkit-background-clip",
|
||||
"-webkit-background-origin",
|
||||
"-webkit-background-size",
|
||||
"-webkit-border-image",
|
||||
"-webkit-box-shadow",
|
||||
"-webkit-box-sizing",
|
||||
"-webkit-box-flex",
|
||||
"-webkit-box-ordinal-group",
|
||||
"-webkit-box-orient",
|
||||
"-webkit-box-direction",
|
||||
"-webkit-box-align",
|
||||
"-webkit-box-pack",
|
||||
"-webkit-user-select"
|
||||
];
|
@ -12,7 +12,6 @@ const Telemetry = require("devtools/client/shared/telemetry");
|
||||
|
||||
const NS_XHTML = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const Node = Ci.nsIDOMNode;
|
||||
|
||||
loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
|
||||
loader.lazyImporter(this, "EventEmitter", "resource://devtools/shared/event-emitter.js");
|
||||
@ -30,6 +29,7 @@ loader.lazyRequireGetter(this, "util", "gcli/util/util");
|
||||
loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/shared/webconsole/utils", true);
|
||||
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
|
||||
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
|
||||
loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants", true);
|
||||
|
||||
/**
|
||||
* A collection of utilities to help working with commands
|
||||
@ -392,7 +392,7 @@ DeveloperToolbar.prototype.focusToggle = function () {
|
||||
// inside the xul input element
|
||||
let active = this._chromeWindow.document.activeElement;
|
||||
let position = this._input.compareDocumentPosition(active);
|
||||
if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
||||
if (position & nodeConstants.DOCUMENT_POSITION_CONTAINED_BY) {
|
||||
this.hide();
|
||||
}
|
||||
else {
|
||||
|
@ -68,12 +68,16 @@ const ElectronKeysMapping = {
|
||||
*
|
||||
* @param DOMWindow window
|
||||
* The window object of the document to listen events from.
|
||||
* @param DOMElement target
|
||||
* Optional DOM Element on which we should listen events from.
|
||||
* If omitted, we listen for all events fired on `window`.
|
||||
*/
|
||||
function KeyShortcuts({ window }) {
|
||||
function KeyShortcuts({ window, target }) {
|
||||
this.window = window;
|
||||
this.target = target || window;
|
||||
this.keys = new Map();
|
||||
this.eventEmitter = new EventEmitter();
|
||||
this.window.addEventListener("keydown", this);
|
||||
this.target.addEventListener("keydown", this);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -169,7 +173,7 @@ KeyShortcuts.stringify = function (shortcut) {
|
||||
|
||||
KeyShortcuts.prototype = {
|
||||
destroy() {
|
||||
this.window.removeEventListener("keydown", this);
|
||||
this.target.removeEventListener("keydown", this);
|
||||
this.keys.clear();
|
||||
},
|
||||
|
||||
|
@ -22,7 +22,6 @@ DevToolsModules(
|
||||
'css-angle.js',
|
||||
'css-color-db.js',
|
||||
'css-color.js',
|
||||
'css-properties-db.js',
|
||||
'css-reload.js',
|
||||
'Curl.jsm',
|
||||
'demangle.js',
|
||||
|
@ -454,13 +454,13 @@ OutputParser.prototype = {
|
||||
},
|
||||
|
||||
_onColorSwatchMouseDown: function (event) {
|
||||
// Prevent text selection in the case of shift-click or double-click.
|
||||
event.preventDefault();
|
||||
|
||||
if (!event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent click event to be fired to not show the tooltip
|
||||
event.stopPropagation();
|
||||
|
||||
let swatch = event.target;
|
||||
let color = this.colorSwatches.get(swatch);
|
||||
let val = color.nextColorUnit();
|
||||
@ -470,13 +470,12 @@ OutputParser.prototype = {
|
||||
},
|
||||
|
||||
_onAngleSwatchMouseDown: function (event) {
|
||||
// Prevent text selection in the case of shift-click or double-click.
|
||||
event.preventDefault();
|
||||
|
||||
if (!event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
let swatch = event.target;
|
||||
let angle = this.angleSwatches.get(swatch);
|
||||
let val = angle.nextAngleUnit();
|
||||
|
@ -17,7 +17,9 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
|
||||
<vbox flex="1">
|
||||
<hbox id="box1" flex="1">test1</hbox>
|
||||
<hbox id="box2" flex="1">test2</hbox>
|
||||
<hbox id="box3" flex="1">test3</hbox>
|
||||
<hbox id="box3" flex="1">
|
||||
<textbox id="box3-input"></textbox>
|
||||
</hbox>
|
||||
<hbox id="box4" flex="1">
|
||||
<textbox id="box4-input"></textbox>
|
||||
</hbox>
|
||||
@ -31,51 +33,70 @@ add_task(function* () {
|
||||
yield addTab("about:blank");
|
||||
let [,, doc] = yield createHost("bottom", TEST_URI);
|
||||
|
||||
yield testTooltipWithAutoFocus(doc);
|
||||
yield testTooltipWithoutAutoFocus(doc);
|
||||
yield testNoAutoFocus(doc);
|
||||
yield testAutoFocus(doc);
|
||||
yield testAutoFocusPreservesFocusChange(doc);
|
||||
});
|
||||
|
||||
function* testTooltipWithAutoFocus(doc) {
|
||||
info("Test a tooltip with autofocus takes focus when displayed");
|
||||
let textbox = doc.querySelector("textbox");
|
||||
|
||||
info("Focus a XUL textbox");
|
||||
let onInputFocus = once(textbox, "focus");
|
||||
EventUtils.synthesizeMouseAtCenter(textbox, {}, doc.defaultView);
|
||||
yield onInputFocus;
|
||||
|
||||
function* testNoAutoFocus(doc) {
|
||||
yield focusNode(doc, "#box4-input");
|
||||
is(getFocusedDocument(doc), doc, "Focus is in the XUL document");
|
||||
|
||||
let tooltip = new HTMLTooltip({doc}, {autofocus: true});
|
||||
let tooltipNode = getTooltipContent(doc);
|
||||
yield tooltip.setContent(tooltipNode, 150, 50);
|
||||
|
||||
yield showTooltip(tooltip, doc.getElementById("box1"));
|
||||
is(getFocusedDocument(doc), tooltipNode.ownerDocument,
|
||||
"Focus is in the tooltip document");
|
||||
|
||||
yield hideTooltip(tooltip);
|
||||
}
|
||||
|
||||
function* testTooltipWithoutAutoFocus(doc) {
|
||||
info("Test a tooltip can be closed by clicking outside");
|
||||
let textbox = doc.querySelector("textbox");
|
||||
|
||||
info("Focus a XUL textbox");
|
||||
let onInputFocus = once(textbox, "focus");
|
||||
EventUtils.synthesizeMouseAtCenter(textbox, {}, doc.defaultView);
|
||||
yield onInputFocus;
|
||||
|
||||
is(getFocusedDocument(doc), doc, "Focus is in the XUL document");
|
||||
|
||||
let tooltip = new HTMLTooltip({doc}, {autofocus: false});
|
||||
let tooltipNode = getTooltipContent(doc);
|
||||
yield tooltip.setContent(tooltipNode, 150, 50);
|
||||
info("Test a tooltip without autofocus will not take focus");
|
||||
let tooltip = yield createTooltip(doc, false);
|
||||
|
||||
yield showTooltip(tooltip, doc.getElementById("box1"));
|
||||
is(getFocusedDocument(doc), doc, "Focus is still in the XUL document");
|
||||
ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
|
||||
|
||||
yield hideTooltip(tooltip);
|
||||
yield blurNode(doc, "#box4-input");
|
||||
}
|
||||
|
||||
function* testAutoFocus(doc) {
|
||||
yield focusNode(doc, "#box4-input");
|
||||
is(getFocusedDocument(doc), doc, "Focus is in the XUL document");
|
||||
|
||||
info("Test autofocus tooltip takes focus when displayed, " +
|
||||
"and restores the focus when hidden");
|
||||
let tooltip = yield createTooltip(doc, true);
|
||||
|
||||
yield showTooltip(tooltip, doc.getElementById("box1"));
|
||||
is(getFocusedDocument(doc), tooltip.panel.ownerDocument,
|
||||
"Focus is in the tooltip document");
|
||||
|
||||
yield hideTooltip(tooltip);
|
||||
is(getFocusedDocument(doc), doc, "Focus is back in the XUL document");
|
||||
ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
|
||||
|
||||
info("Blur the textbox before moving to the next test to reset the state.");
|
||||
yield blurNode(doc, "#box4-input");
|
||||
}
|
||||
|
||||
function* testAutoFocusPreservesFocusChange(doc) {
|
||||
yield focusNode(doc, "#box4-input");
|
||||
is(getFocusedDocument(doc), doc, "Focus is in the XUL document");
|
||||
|
||||
info("Test autofocus tooltip takes focus when displayed, " +
|
||||
"but does not try to restore the active element if it is not focused when hidden");
|
||||
let tooltip = yield createTooltip(doc, true);
|
||||
|
||||
yield showTooltip(tooltip, doc.getElementById("box1"));
|
||||
is(getFocusedDocument(doc), tooltip.panel.ownerDocument,
|
||||
"Focus is in the tooltip document");
|
||||
|
||||
info("Move the focus to #box3-input while the tooltip is displayed");
|
||||
yield focusNode(doc, "#box3-input");
|
||||
is(getFocusedDocument(doc), doc, "Focus is back in the XUL document");
|
||||
ok(doc.activeElement.closest("#box3-input"), "Focus is in the #box3-input");
|
||||
|
||||
yield hideTooltip(tooltip);
|
||||
is(getFocusedDocument(doc), doc, "Focus is still in the XUL document");
|
||||
|
||||
ok(doc.activeElement.closest("#box3-input"), "Focus is still in the #box3-input");
|
||||
|
||||
info("Blur the textbox before moving to the next test to reset the state.");
|
||||
yield blurNode(doc, "#box3-input");
|
||||
}
|
||||
|
||||
function getFocusedDocument(doc) {
|
||||
@ -86,9 +107,41 @@ function getFocusedDocument(doc) {
|
||||
return activeElement.ownerDocument;
|
||||
}
|
||||
|
||||
function getTooltipContent(doc) {
|
||||
/**
|
||||
* Fpcus the node corresponding to the provided selector in the provided document. Returns
|
||||
* a promise that will resolve when receiving the focus event on the node.
|
||||
*/
|
||||
function focusNode(doc, selector) {
|
||||
let node = doc.querySelector(selector);
|
||||
let onFocus = once(node, "focus");
|
||||
node.focus();
|
||||
return onFocus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur the node corresponding to the provided selector in the provided document. Returns
|
||||
* a promise that will resolve when receiving the blur event on the node.
|
||||
*/
|
||||
function blurNode(doc, selector) {
|
||||
let node = doc.querySelector(selector);
|
||||
let onBlur = once(node, "blur");
|
||||
node.blur();
|
||||
return onBlur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTMLTooltip instance with the provided autofocus setting.
|
||||
*
|
||||
* @param {Document} doc
|
||||
* Document in which the tooltip should be created
|
||||
* @param {Boolean} autofocus
|
||||
* @return {Promise} promise that will resolve the HTMLTooltip instance created when the
|
||||
* tooltip content will be ready.
|
||||
*/
|
||||
function* createTooltip(doc, autofocus) {
|
||||
let tooltip = new HTMLTooltip({doc}, {autofocus});
|
||||
let div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.height = "50px";
|
||||
div.style.boxSizing = "border-box";
|
||||
return div;
|
||||
yield tooltip.setContent(div, 150, 50);
|
||||
return tooltip;
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ add_task(function* () {
|
||||
yield testCommandOrControlModifier(shortcuts);
|
||||
yield testCtrlModifier(shortcuts);
|
||||
shortcuts.destroy();
|
||||
|
||||
yield testTarget();
|
||||
});
|
||||
|
||||
// Test helper to listen to the next key press for a given key,
|
||||
@ -336,3 +338,27 @@ function testCtrlModifier(shortcuts) {
|
||||
yield onKey;
|
||||
yield onKeyAlias;
|
||||
}
|
||||
|
||||
function testTarget() {
|
||||
info("Test KeyShortcuts with target argument");
|
||||
|
||||
let target = document.createElementNS("http://www.w3.org/1999/xhtml",
|
||||
"input");
|
||||
document.documentElement.appendChild(target);
|
||||
target.focus();
|
||||
|
||||
let shortcuts = new KeyShortcuts({
|
||||
window,
|
||||
target
|
||||
});
|
||||
let onKey = once(shortcuts, "0", (key, event) => {
|
||||
is(event.key, "0");
|
||||
is(event.target, target);
|
||||
});
|
||||
EventUtils.synthesizeKey("0", {}, window);
|
||||
yield onKey;
|
||||
|
||||
target.remove();
|
||||
|
||||
shortcuts.destroy();
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
let deferred = promise.defer();
|
||||
client.listTabs(deferred.resolve);
|
||||
let response = yield deferred.promise;
|
||||
let { ActorRegistryFront } = require("devtools/server/actors/actor-registry");
|
||||
let { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
|
||||
let registryFront = ActorRegistryFront(client, response);
|
||||
|
||||
// Then ask to register our test-actor to retrieve its front
|
||||
|
@ -54,13 +54,13 @@ const EXTRA_BORDER = {
|
||||
* - {String} type
|
||||
* Display type of the tooltip. Possible values: "normal", "arrow"
|
||||
* - {Boolean} autofocus
|
||||
* Defaults to true. Should the tooltip be focused when opening it.
|
||||
* Defaults to false. Should the tooltip be focused when opening it.
|
||||
* - {Boolean} consumeOutsideClicks
|
||||
* Defaults to true. The tooltip is closed when clicking outside.
|
||||
* Should this event be stopped and consumed or not.
|
||||
*/
|
||||
function HTMLTooltip(toolbox,
|
||||
{type = "normal", autofocus = true, consumeOutsideClicks = true} = {}) {
|
||||
{type = "normal", autofocus = false, consumeOutsideClicks = true} = {}) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this.doc = toolbox.doc;
|
||||
@ -191,6 +191,7 @@ HTMLTooltip.prototype = {
|
||||
this.container.classList.add("tooltip-visible");
|
||||
|
||||
this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
|
||||
this._focusedElement = this.doc.activeElement;
|
||||
if (this.autofocus) {
|
||||
this.frame.focus();
|
||||
}
|
||||
@ -211,6 +212,11 @@ HTMLTooltip.prototype = {
|
||||
this.topWindow.removeEventListener("click", this._onClick, true);
|
||||
this.container.classList.remove("tooltip-visible");
|
||||
this.emit("hidden");
|
||||
|
||||
if (this.container.contains(this.doc.activeElement) && this._focusedElement) {
|
||||
this._focusedElement.focus();
|
||||
this._focusedElement = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -18,7 +18,6 @@ const Heritage = require("sdk/core/heritage");
|
||||
const {Eyedropper} = require("devtools/client/eyedropper/eyedropper");
|
||||
const Editor = require("devtools/client/sourceeditor/editor");
|
||||
const Services = require("Services");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
|
||||
loader.lazyRequireGetter(this, "beautify", "devtools/shared/jsbeautify/beautify");
|
||||
loader.lazyRequireGetter(this, "setNamedTimeout", "devtools/client/shared/widgets/view-helpers", true);
|
||||
@ -520,31 +519,6 @@ Tooltip.prototype = {
|
||||
this.panel.setAttribute("clamped-dimensions", "");
|
||||
},
|
||||
|
||||
/**
|
||||
* Uses the provided inspectorFront's getImageDataFromURL method to resolve
|
||||
* the relative URL on the server-side, in the page context, and then sets the
|
||||
* tooltip content with the resulting image just like |setImageContent| does.
|
||||
* @return a promise that resolves when the image is shown in the tooltip or
|
||||
* resolves when the broken image tooltip content is ready, but never rejects.
|
||||
*/
|
||||
setRelativeImageContent: Task.async(function* (imageUrl, inspectorFront,
|
||||
maxDim) {
|
||||
if (imageUrl.startsWith("data:")) {
|
||||
// If the imageUrl already is a data-url, save ourselves a round-trip
|
||||
this.setImageContent(imageUrl, {maxDim: maxDim});
|
||||
} else if (inspectorFront) {
|
||||
try {
|
||||
let {data, size} = yield inspectorFront.getImageDataFromURL(imageUrl,
|
||||
maxDim);
|
||||
size.maxDim = maxDim;
|
||||
let str = yield data.string();
|
||||
this.setImageContent(str, size);
|
||||
} catch (e) {
|
||||
this.setBrokenImageContent();
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Fill the tooltip with a message explaining the the image is missing
|
||||
*/
|
||||
@ -560,7 +534,7 @@ Tooltip.prototype = {
|
||||
* Fill the tooltip with an image and add the image dimension at the bottom.
|
||||
*
|
||||
* Only use this for absolute URLs that can be queried from the devtools
|
||||
* client-side. For relative URLs, use |setRelativeImageContent|.
|
||||
* client-side.
|
||||
*
|
||||
* @param {string} imageUrl
|
||||
* The url to load the image from
|
||||
@ -768,38 +742,6 @@ Tooltip.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the content of the tooltip to display a font family preview.
|
||||
* This is based on Lea Verou's Dablet.
|
||||
* See https://github.com/LeaVerou/dabblet
|
||||
* for more info.
|
||||
* @param {String} font The font family value.
|
||||
* @param {object} nodeFront
|
||||
* The NodeActor that will used to retrieve the dataURL for the font
|
||||
* family tooltip contents.
|
||||
* @return A promise that resolves when the font tooltip content is ready, or
|
||||
* rejects if no font is provided
|
||||
*/
|
||||
setFontFamilyContent: Task.async(function* (font, nodeFront) {
|
||||
if (!font || !nodeFront) {
|
||||
throw new Error("Missing font");
|
||||
}
|
||||
|
||||
if (typeof nodeFront.getFontFamilyDataURL === "function") {
|
||||
font = font.replace(/"/g, "'");
|
||||
font = font.replace("!important", "");
|
||||
font = font.trim();
|
||||
|
||||
let fillStyle =
|
||||
(Services.prefs.getCharPref("devtools.theme") === "light") ?
|
||||
"black" : "white";
|
||||
|
||||
let {data, size} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
|
||||
let str = yield data.string();
|
||||
this.setImageContent(str, { hideDimensionLabel: true, maxDim: size });
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Set the content of this tooltip to the MDN docs widget.
|
||||
*
|
||||
|
@ -27,6 +27,7 @@ const promise = require("promise");
|
||||
const { Heritage, ViewHelpers, setNamedTimeout } =
|
||||
require("devtools/client/shared/widgets/view-helpers");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
const nodeConstants = require("devtools/shared/dom-node-constants");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
@ -3758,7 +3759,7 @@ VariablesView.stringifiers.byObjectKind = {
|
||||
let {preview} = aGrip;
|
||||
|
||||
switch (preview.nodeType) {
|
||||
case Ci.nsIDOMNode.DOCUMENT_NODE: {
|
||||
case nodeConstants.DOCUMENT_NODE: {
|
||||
let result = aGrip.class;
|
||||
if (preview.location) {
|
||||
result += ` \u2192 ${getSourceNames(preview.location)[concise ? "short" : "long"]}`;
|
||||
@ -3767,22 +3768,22 @@ VariablesView.stringifiers.byObjectKind = {
|
||||
return result;
|
||||
}
|
||||
|
||||
case Ci.nsIDOMNode.ATTRIBUTE_NODE: {
|
||||
case nodeConstants.ATTRIBUTE_NODE: {
|
||||
let value = VariablesView.getString(preview.value, { noStringQuotes: true });
|
||||
return preview.nodeName + '="' + escapeHTML(value) + '"';
|
||||
}
|
||||
|
||||
case Ci.nsIDOMNode.TEXT_NODE:
|
||||
case nodeConstants.TEXT_NODE:
|
||||
return preview.nodeName + " " +
|
||||
VariablesView.getString(preview.textContent);
|
||||
|
||||
case Ci.nsIDOMNode.COMMENT_NODE: {
|
||||
case nodeConstants.COMMENT_NODE: {
|
||||
let comment = VariablesView.getString(preview.textContent,
|
||||
{ noStringQuotes: true });
|
||||
return "<!--" + comment + "-->";
|
||||
}
|
||||
|
||||
case Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE: {
|
||||
case nodeConstants.DOCUMENT_FRAGMENT_NODE: {
|
||||
if (concise || !preview.childNodes) {
|
||||
return aGrip.class + "[" + preview.childNodesLength + "]";
|
||||
}
|
||||
@ -3797,7 +3798,7 @@ VariablesView.stringifiers.byObjectKind = {
|
||||
return aGrip.class + " [" + nodes.join(", ") + "]";
|
||||
}
|
||||
|
||||
case Ci.nsIDOMNode.ELEMENT_NODE: {
|
||||
case nodeConstants.ELEMENT_NODE: {
|
||||
let attrs = preview.attributes;
|
||||
if (!concise) {
|
||||
let n = 0, result = "<" + preview.nodeName;
|
||||
|
@ -5,7 +5,7 @@ code, and optionally help with indentation.
|
||||
|
||||
# Upgrade
|
||||
|
||||
Currently used version is 5.14.2. To upgrade, download a new version of
|
||||
Currently used version is 5.15.2. To upgrade, download a new version of
|
||||
CodeMirror from the project's page [1] and replace all JavaScript and
|
||||
CSS files inside the codemirror directory [2].
|
||||
|
||||
|
@ -109,7 +109,7 @@
|
||||
var ranges = cm.listSelections();
|
||||
var opening = pos % 2 == 0;
|
||||
|
||||
var type, next;
|
||||
var type;
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], cur = range.head, curType;
|
||||
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
CodeMirror.registerHelper("fold", "brace", function(cm, start) {
|
||||
var line = start.line, lineText = cm.getLine(line);
|
||||
var startCh, tokenType;
|
||||
var tokenType;
|
||||
|
||||
function findOpening(openCh) {
|
||||
for (var at = start.ch, pass = 0;;) {
|
||||
@ -72,15 +72,15 @@ CodeMirror.registerHelper("fold", "import", function(cm, start) {
|
||||
}
|
||||
}
|
||||
|
||||
var start = start.line, has = hasImport(start), prev;
|
||||
if (!has || hasImport(start - 1) || ((prev = hasImport(start - 2)) && prev.end.line == start - 1))
|
||||
var startLine = start.line, has = hasImport(startLine), prev;
|
||||
if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
|
||||
return null;
|
||||
for (var end = has.end;;) {
|
||||
var next = hasImport(end.line + 1);
|
||||
if (next == null) break;
|
||||
end = next.end;
|
||||
}
|
||||
return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end};
|
||||
return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
|
||||
});
|
||||
|
||||
CodeMirror.registerHelper("fold", "include", function(cm, start) {
|
||||
@ -91,14 +91,14 @@ CodeMirror.registerHelper("fold", "include", function(cm, start) {
|
||||
if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
|
||||
}
|
||||
|
||||
var start = start.line, has = hasInclude(start);
|
||||
if (has == null || hasInclude(start - 1) != null) return null;
|
||||
for (var end = start;;) {
|
||||
var startLine = start.line, has = hasInclude(startLine);
|
||||
if (has == null || hasInclude(startLine - 1) != null) return null;
|
||||
for (var end = startLine;;) {
|
||||
var next = hasInclude(end + 1);
|
||||
if (next == null) break;
|
||||
++end;
|
||||
}
|
||||
return {from: CodeMirror.Pos(start, has + 1),
|
||||
return {from: CodeMirror.Pos(startLine, has + 1),
|
||||
to: cm.clipPos(CodeMirror.Pos(end))};
|
||||
});
|
||||
|
||||
|
@ -140,9 +140,9 @@
|
||||
var openTag = toNextTag(iter), end;
|
||||
if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
|
||||
if (!openTag[1] && end != "selfClose") {
|
||||
var start = Pos(iter.line, iter.ch);
|
||||
var close = findMatchingClose(iter, openTag[2]);
|
||||
return close && {from: start, to: close.from};
|
||||
var startPos = Pos(iter.line, iter.ch);
|
||||
var endPos = findMatchingClose(iter, openTag[2]);
|
||||
return endPos && {from: startPos, to: endPos.from};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -124,6 +124,7 @@
|
||||
}
|
||||
cm.setSelections(newSelection);
|
||||
});
|
||||
cm.execCommand("indentAuto");
|
||||
}
|
||||
|
||||
cmds[map[ctrl + "Enter"] = "insertLineAfter"] = function(cm) { return insertLine(cm, false); };
|
||||
@ -419,27 +420,6 @@
|
||||
|
||||
map[cK + ctrl + "Backspace"] = "delLineLeft";
|
||||
|
||||
cmds[map["Backspace"] = "smartBackspace"] = function(cm) {
|
||||
if (cm.somethingSelected()) return CodeMirror.Pass;
|
||||
|
||||
var cursor = cm.getCursor();
|
||||
var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
|
||||
var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
|
||||
var indentUnit = cm.getOption("indentUnit");
|
||||
|
||||
if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
|
||||
var prevIndent = new Pos(cursor.line,
|
||||
CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
|
||||
|
||||
// If no smart delete is happening (due to tab sizing) just do a regular delete
|
||||
if (prevIndent.ch == cursor.ch) return CodeMirror.Pass;
|
||||
|
||||
return cm.replaceRange("", prevIndent, cursor, "+delete");
|
||||
} else {
|
||||
return CodeMirror.Pass;
|
||||
}
|
||||
};
|
||||
|
||||
cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
|
@ -3782,17 +3782,10 @@
|
||||
}
|
||||
}
|
||||
function makePrompt(prefix, desc) {
|
||||
var raw = '';
|
||||
if (prefix) {
|
||||
raw += '<span style="font-family: monospace">' + prefix + '</span>';
|
||||
}
|
||||
raw += '<input type="text"/> ' +
|
||||
'<span style="color: #888">';
|
||||
if (desc) {
|
||||
raw += '<span style="color: #888">';
|
||||
raw += desc;
|
||||
raw += '</span>';
|
||||
}
|
||||
var raw = '<span style="font-family: monospace; white-space: pre">' +
|
||||
(prefix || "") + '<input type="text"></span>';
|
||||
if (desc)
|
||||
raw += ' <span style="color: #888">' + desc + '</span>';
|
||||
return raw;
|
||||
}
|
||||
var searchPromptDesc = '(Javascript regexp)';
|
||||
|
@ -52,7 +52,7 @@
|
||||
}
|
||||
.cm-fat-cursor .CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
border: 0 !important;
|
||||
background: #7e7;
|
||||
}
|
||||
.cm-fat-cursor div.CodeMirror-cursors {
|
||||
|
@ -1096,9 +1096,9 @@
|
||||
if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
|
||||
}
|
||||
|
||||
// This will be set to an array of strings when copying, so that,
|
||||
// when pasting, we know what kind of selections the copied text
|
||||
// was made out of.
|
||||
// This will be set to a {lineWise: bool, text: [string]} object, so
|
||||
// that, when pasting, we know what kind of selections the copied
|
||||
// text was made out of.
|
||||
var lastCopied = null;
|
||||
|
||||
function applyTextInput(cm, inserted, deleted, sel, origin) {
|
||||
@ -1107,14 +1107,14 @@
|
||||
if (!sel) sel = doc.sel;
|
||||
|
||||
var paste = cm.state.pasteIncoming || origin == "paste";
|
||||
var textLines = doc.splitLines(inserted), multiPaste = null;
|
||||
var textLines = doc.splitLines(inserted), multiPaste = null
|
||||
// When pasing N lines into N selections, insert one line per selection
|
||||
if (paste && sel.ranges.length > 1) {
|
||||
if (lastCopied && lastCopied.join("\n") == inserted) {
|
||||
if (sel.ranges.length % lastCopied.length == 0) {
|
||||
if (lastCopied && lastCopied.text.join("\n") == inserted) {
|
||||
if (sel.ranges.length % lastCopied.text.length == 0) {
|
||||
multiPaste = [];
|
||||
for (var i = 0; i < lastCopied.length; i++)
|
||||
multiPaste.push(doc.splitLines(lastCopied[i]));
|
||||
for (var i = 0; i < lastCopied.text.length; i++)
|
||||
multiPaste.push(doc.splitLines(lastCopied.text[i]));
|
||||
}
|
||||
} else if (textLines.length == sel.ranges.length) {
|
||||
multiPaste = map(textLines, function(l) { return [l]; });
|
||||
@ -1130,6 +1130,8 @@
|
||||
from = Pos(from.line, from.ch - deleted);
|
||||
else if (cm.state.overwrite && !paste) // Handle overwrite
|
||||
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
|
||||
else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
|
||||
from = to = Pos(from.line, 0)
|
||||
}
|
||||
var updateInput = cm.curOp.updateInput;
|
||||
var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
|
||||
@ -1262,18 +1264,18 @@
|
||||
function prepareCopyCut(e) {
|
||||
if (signalDOMEvent(cm, e)) return
|
||||
if (cm.somethingSelected()) {
|
||||
lastCopied = cm.getSelections();
|
||||
lastCopied = {lineWise: false, text: cm.getSelections()};
|
||||
if (input.inaccurateSelection) {
|
||||
input.prevInput = "";
|
||||
input.inaccurateSelection = false;
|
||||
te.value = lastCopied.join("\n");
|
||||
te.value = lastCopied.text.join("\n");
|
||||
selectInput(te);
|
||||
}
|
||||
} else if (!cm.options.lineWiseCopyCut) {
|
||||
return;
|
||||
} else {
|
||||
var ranges = copyableRanges(cm);
|
||||
lastCopied = ranges.text;
|
||||
lastCopied = {lineWise: true, text: ranges.text};
|
||||
if (e.type == "cut") {
|
||||
cm.setSelections(ranges.ranges, null, sel_dontScroll);
|
||||
} else {
|
||||
@ -1621,13 +1623,13 @@
|
||||
function onCopyCut(e) {
|
||||
if (signalDOMEvent(cm, e)) return
|
||||
if (cm.somethingSelected()) {
|
||||
lastCopied = cm.getSelections();
|
||||
lastCopied = {lineWise: false, text: cm.getSelections()};
|
||||
if (e.type == "cut") cm.replaceSelection("", null, "cut");
|
||||
} else if (!cm.options.lineWiseCopyCut) {
|
||||
return;
|
||||
} else {
|
||||
var ranges = copyableRanges(cm);
|
||||
lastCopied = ranges.text;
|
||||
lastCopied = {lineWise: true, text: ranges.text};
|
||||
if (e.type == "cut") {
|
||||
cm.operation(function() {
|
||||
cm.setSelections(ranges.ranges, 0, sel_dontScroll);
|
||||
@ -1639,12 +1641,12 @@
|
||||
if (e.clipboardData && !ios) {
|
||||
e.preventDefault();
|
||||
e.clipboardData.clearData();
|
||||
e.clipboardData.setData("text/plain", lastCopied.join("\n"));
|
||||
e.clipboardData.setData("text/plain", lastCopied.text.join("\n"));
|
||||
} else {
|
||||
// Old-fashioned briefly-focus-a-textarea hack
|
||||
var kludge = hiddenTextarea(), te = kludge.firstChild;
|
||||
cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
|
||||
te.value = lastCopied.join("\n");
|
||||
te.value = lastCopied.text.join("\n");
|
||||
var hadFocus = document.activeElement;
|
||||
selectInput(te);
|
||||
setTimeout(function() {
|
||||
@ -1663,9 +1665,9 @@
|
||||
return result;
|
||||
},
|
||||
|
||||
showSelection: function(info) {
|
||||
showSelection: function(info, takeFocus) {
|
||||
if (!info || !this.cm.display.view.length) return;
|
||||
if (info.focus) this.showPrimarySelection();
|
||||
if (info.focus || takeFocus) this.showPrimarySelection();
|
||||
this.showMultipleSelections(info);
|
||||
},
|
||||
|
||||
@ -3101,7 +3103,7 @@
|
||||
}
|
||||
|
||||
if (op.updatedDisplay || op.selectionChanged)
|
||||
op.preparedSelection = display.input.prepareSelection();
|
||||
op.preparedSelection = display.input.prepareSelection(op.focus);
|
||||
}
|
||||
|
||||
function endOperation_W2(op) {
|
||||
@ -3114,8 +3116,9 @@
|
||||
cm.display.maxLineChanged = false;
|
||||
}
|
||||
|
||||
var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
|
||||
if (op.preparedSelection)
|
||||
cm.display.input.showSelection(op.preparedSelection);
|
||||
cm.display.input.showSelection(op.preparedSelection, takeFocus);
|
||||
if (op.updatedDisplay || op.startHeight != cm.doc.height)
|
||||
updateScrollbars(cm, op.barMeasure);
|
||||
if (op.updatedDisplay)
|
||||
@ -3125,8 +3128,7 @@
|
||||
|
||||
if (cm.state.focused && op.updateInput)
|
||||
cm.display.input.reset(op.typing);
|
||||
if (op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()))
|
||||
ensureFocus(op.cm);
|
||||
if (takeFocus) ensureFocus(op.cm);
|
||||
}
|
||||
|
||||
function endOperation_finish(op) {
|
||||
@ -5392,7 +5394,7 @@
|
||||
for (var i = newBreaks.length - 1; i >= 0; i--)
|
||||
replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length))
|
||||
});
|
||||
option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
|
||||
option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
|
||||
cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
|
||||
if (old != CodeMirror.Init) cm.refresh();
|
||||
});
|
||||
@ -5721,7 +5723,7 @@
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var pos = ranges[i].from();
|
||||
var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
|
||||
spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
|
||||
spaces.push(spaceStr(tabSize - col % tabSize));
|
||||
}
|
||||
cm.replaceSelections(spaces);
|
||||
},
|
||||
@ -5764,6 +5766,7 @@
|
||||
ensureCursorVisible(cm);
|
||||
});
|
||||
},
|
||||
openLine: function(cm) {cm.replaceSelection("\n", "start")},
|
||||
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
|
||||
};
|
||||
|
||||
@ -5798,7 +5801,8 @@
|
||||
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
|
||||
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
|
||||
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
|
||||
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
|
||||
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
|
||||
"Ctrl-O": "openLine"
|
||||
};
|
||||
keyMap.macDefault = {
|
||||
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
||||
@ -6560,8 +6564,8 @@
|
||||
var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
|
||||
var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
|
||||
if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
|
||||
if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
|
||||
fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
|
||||
if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
|
||||
fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -6963,8 +6967,11 @@
|
||||
}
|
||||
|
||||
// See issue #2901
|
||||
if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
|
||||
builder.content.className = "cm-tab-wrap-hack";
|
||||
if (webkit) {
|
||||
var last = builder.content.lastChild
|
||||
if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
|
||||
builder.content.className = "cm-tab-wrap-hack";
|
||||
}
|
||||
|
||||
signal(cm, "renderLine", cm, lineView.line, builder.pre);
|
||||
if (builder.pre.className)
|
||||
@ -7316,13 +7323,16 @@
|
||||
if (at <= sz) {
|
||||
child.insertInner(at, lines, height);
|
||||
if (child.lines && child.lines.length > 50) {
|
||||
while (child.lines.length > 50) {
|
||||
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
||||
var newleaf = new LeafChunk(spilled);
|
||||
child.height -= newleaf.height;
|
||||
this.children.splice(i + 1, 0, newleaf);
|
||||
newleaf.parent = this;
|
||||
// To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
|
||||
// Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
|
||||
var remaining = child.lines.length % 25 + 25
|
||||
for (var pos = remaining; pos < child.lines.length;) {
|
||||
var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
|
||||
child.height -= leaf.height;
|
||||
this.children.splice(++i, 0, leaf);
|
||||
leaf.parent = this;
|
||||
}
|
||||
child.lines = child.lines.slice(0, remaining);
|
||||
this.maybeSpill();
|
||||
}
|
||||
break;
|
||||
@ -7342,7 +7352,7 @@
|
||||
copy.parent = me;
|
||||
me.children = [copy, sibling];
|
||||
me = copy;
|
||||
} else {
|
||||
} else {
|
||||
me.size -= sibling.size;
|
||||
me.height -= sibling.height;
|
||||
var myIndex = indexOf(me.parent.children, me);
|
||||
@ -8892,7 +8902,7 @@
|
||||
|
||||
// THE END
|
||||
|
||||
CodeMirror.version = "5.14.2";
|
||||
CodeMirror.version = "5.15.2";
|
||||
|
||||
return CodeMirror;
|
||||
});
|
||||
|
@ -11,21 +11,19 @@
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
function Context(indented, column, type, align, prev) {
|
||||
function Context(indented, column, type, info, align, prev) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.info = info;
|
||||
this.align = align;
|
||||
this.prev = prev;
|
||||
}
|
||||
function isStatement(type) {
|
||||
return type == "statement" || type == "switchstatement" || type == "namespace";
|
||||
}
|
||||
function pushContext(state, col, type) {
|
||||
function pushContext(state, col, type, info) {
|
||||
var indent = state.indented;
|
||||
if (state.context && isStatement(state.context.type) && !isStatement(type))
|
||||
if (state.context && state.context.type != "statement" && type != "statement")
|
||||
indent = state.context.indented;
|
||||
return state.context = new Context(indent, col, type, null, state.context);
|
||||
return state.context = new Context(indent, col, type, info, null, state.context);
|
||||
}
|
||||
function popContext(state) {
|
||||
var t = state.context.type;
|
||||
@ -34,15 +32,16 @@ function popContext(state) {
|
||||
return state.context = state.context.prev;
|
||||
}
|
||||
|
||||
function typeBefore(stream, state) {
|
||||
function typeBefore(stream, state, pos) {
|
||||
if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
|
||||
if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
|
||||
if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true;
|
||||
if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;
|
||||
}
|
||||
|
||||
function isTopScope(context) {
|
||||
for (;;) {
|
||||
if (!context || context.type == "top") return true;
|
||||
if (context.type == "}" && context.prev.type != "namespace") return false;
|
||||
if (context.type == "}" && context.prev.info != "namespace") return false;
|
||||
context = context.prev;
|
||||
}
|
||||
}
|
||||
@ -147,13 +146,18 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
return "comment";
|
||||
}
|
||||
|
||||
function maybeEOL(stream, state) {
|
||||
if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))
|
||||
state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
return {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: null,
|
||||
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
||||
context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false),
|
||||
indented: 0,
|
||||
startOfLine: true,
|
||||
prevToken: null
|
||||
@ -167,36 +171,31 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
state.indented = stream.indentation();
|
||||
state.startOfLine = true;
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
if (stream.eatSpace()) { maybeEOL(stream, state); return null; }
|
||||
curPunc = isDefKeyword = null;
|
||||
var style = (state.tokenize || tokenBase)(stream, state);
|
||||
if (style == "comment" || style == "meta") return style;
|
||||
if (ctx.align == null) ctx.align = true;
|
||||
|
||||
if (endStatement.test(curPunc)) while (isStatement(state.context.type)) popContext(state);
|
||||
if (endStatement.test(curPunc)) while (state.context.type == "statement") popContext(state);
|
||||
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
||||
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
||||
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
||||
else if (curPunc == "}") {
|
||||
while (isStatement(ctx.type)) ctx = popContext(state);
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
if (ctx.type == "}") ctx = popContext(state);
|
||||
while (isStatement(ctx.type)) ctx = popContext(state);
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
}
|
||||
else if (curPunc == ctx.type) popContext(state);
|
||||
else if (indentStatements &&
|
||||
(((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
|
||||
(isStatement(ctx.type) && curPunc == "newstatement"))) {
|
||||
var type = "statement";
|
||||
if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch")
|
||||
type = "switchstatement";
|
||||
else if (style == "keyword" && stream.current() == "namespace")
|
||||
type = "namespace";
|
||||
pushContext(state, stream.column(), type);
|
||||
(ctx.type == "statement" && curPunc == "newstatement"))) {
|
||||
pushContext(state, stream.column(), "statement", stream.current());
|
||||
}
|
||||
|
||||
if (style == "variable" &&
|
||||
((state.prevToken == "def" ||
|
||||
(parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
|
||||
(parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&
|
||||
isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
|
||||
style = "def";
|
||||
|
||||
@ -209,24 +208,28 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
|
||||
state.startOfLine = false;
|
||||
state.prevToken = isDefKeyword ? "def" : style || curPunc;
|
||||
maybeEOL(stream, state);
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
|
||||
if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
|
||||
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
||||
if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
|
||||
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
||||
if (parserConfig.dontIndentStatements)
|
||||
while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
|
||||
ctx = ctx.prev
|
||||
if (hooks.indent) {
|
||||
var hook = hooks.indent(state, ctx, textAfter);
|
||||
if (typeof hook == "number") return hook
|
||||
}
|
||||
var closing = firstChar == ctx.type;
|
||||
var switchBlock = ctx.prev && ctx.prev.type == "switchstatement";
|
||||
var switchBlock = ctx.prev && ctx.prev.info == "switch";
|
||||
if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
|
||||
while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
|
||||
return ctx.indented
|
||||
}
|
||||
if (isStatement(ctx.type))
|
||||
if (ctx.type == "statement")
|
||||
return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
|
||||
if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
|
||||
return ctx.column + (closing ? 0 : 1);
|
||||
@ -386,6 +389,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
defKeywords: words("class namespace struct enum union"),
|
||||
typeFirstDefinitions: true,
|
||||
atoms: words("true false null"),
|
||||
dontIndentStatements: /^template$/,
|
||||
hooks: {
|
||||
"#": cppHook,
|
||||
"*": pointerHook,
|
||||
@ -429,6 +433,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
typeFirstDefinitions: true,
|
||||
atoms: words("true false null"),
|
||||
endStatement: /^[;:]$/,
|
||||
number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
|
||||
hooks: {
|
||||
"@": function(stream) {
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
@ -531,7 +536,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
"=": function(stream, state) {
|
||||
var cx = state.context
|
||||
if (cx.type == "}" && cx.align && stream.eat(">")) {
|
||||
state.context = new Context(cx.indented, cx.column, cx.type, null, cx.prev)
|
||||
state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
|
||||
return "operator"
|
||||
} else {
|
||||
return false
|
||||
|
@ -484,9 +484,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
"font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
|
||||
"font-variant-ligatures", "font-variant-numeric", "font-variant-position",
|
||||
"font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
|
||||
"grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end",
|
||||
"grid-column-start", "grid-row", "grid-row-end", "grid-row-start",
|
||||
"grid-template", "grid-template-areas", "grid-template-columns",
|
||||
"grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
|
||||
"grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
|
||||
"grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
|
||||
"grid-template-rows", "hanging-punctuation", "height", "hyphens",
|
||||
"icon", "image-orientation", "image-rendering", "image-resolution",
|
||||
"inline-box-align", "justify-content", "left", "letter-spacing",
|
||||
@ -601,7 +601,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
"compact", "condensed", "contain", "content",
|
||||
"content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
|
||||
"cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
|
||||
"decimal-leading-zero", "default", "default-button", "destination-atop",
|
||||
"decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
|
||||
"destination-in", "destination-out", "destination-over", "devanagari", "difference",
|
||||
"disc", "discard", "disclosure-closed", "disclosure-open", "document",
|
||||
"dot-dash", "dot-dot-dash",
|
||||
@ -615,13 +615,13 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
||||
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
|
||||
"ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
|
||||
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
|
||||
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
|
||||
"forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
|
||||
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
|
||||
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
|
||||
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
|
||||
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
|
||||
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
|
||||
"inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
|
||||
"inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
|
||||
"italic", "japanese-formal", "japanese-informal", "justify", "kannada",
|
||||
"katakana", "katakana-iroha", "keep-all", "khmer",
|
||||
"korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
|
||||
|
@ -115,7 +115,7 @@
|
||||
|
||||
return {
|
||||
startState: function () {
|
||||
var state = htmlMode.startState();
|
||||
var state = CodeMirror.startState(htmlMode);
|
||||
return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
|
||||
},
|
||||
|
||||
|
@ -42,7 +42,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
"in": operator, "typeof": operator, "instanceof": operator,
|
||||
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
|
||||
"this": kw("this"), "class": kw("class"), "super": kw("atom"),
|
||||
"yield": C, "export": kw("export"), "import": kw("import"), "extends": C
|
||||
"yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
|
||||
"await": C, "async": kw("async")
|
||||
};
|
||||
|
||||
// Extend the 'normal' keywords with the TypeScript language extensions
|
||||
@ -366,6 +367,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
|
||||
if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
|
||||
if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
|
||||
if (type == "async") return cont(statement)
|
||||
return pass(pushlex("stat"), expression, expect(";"), poplex);
|
||||
}
|
||||
function expression(type) {
|
||||
@ -488,17 +490,17 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == "(") return pass(functiondef);
|
||||
}
|
||||
function commasep(what, end) {
|
||||
function proceed(type) {
|
||||
function proceed(type, value) {
|
||||
if (type == ",") {
|
||||
var lex = cx.state.lexical;
|
||||
if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
|
||||
return cont(what, proceed);
|
||||
}
|
||||
if (type == end) return cont();
|
||||
if (type == end || value == end) return cont();
|
||||
return cont(expect(end));
|
||||
}
|
||||
return function(type) {
|
||||
if (type == end) return cont();
|
||||
return function(type, value) {
|
||||
if (type == end || value == end) return cont();
|
||||
return pass(what, proceed);
|
||||
};
|
||||
}
|
||||
@ -512,13 +514,17 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
return pass(statement, block);
|
||||
}
|
||||
function maybetype(type) {
|
||||
if (isTS && type == ":") return cont(typedef);
|
||||
if (isTS && type == ":") return cont(typeexpr);
|
||||
}
|
||||
function maybedefault(_, value) {
|
||||
if (value == "=") return cont(expressionNoComma);
|
||||
}
|
||||
function typedef(type) {
|
||||
if (type == "variable") {cx.marked = "variable-3"; return cont();}
|
||||
function typeexpr(type) {
|
||||
if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
|
||||
}
|
||||
function afterType(type, value) {
|
||||
if (value == "<") return cont(commasep(typeexpr, ">"), afterType)
|
||||
if (type == "[") return cont(expect("]"), afterType)
|
||||
}
|
||||
function vardef() {
|
||||
return pass(pattern, maybetype, maybeAssign, vardefCont);
|
||||
@ -573,7 +579,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
function functiondef(type, value) {
|
||||
if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
|
||||
if (type == "variable") {register(value); return cont(functiondef);}
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
|
||||
}
|
||||
function funarg(type) {
|
||||
if (type == "spread") return cont(funarg);
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
const { Cc, Ci, Cu, Cr } = require("chrome");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { WebAudioFront } = require("devtools/server/actors/webaudio");
|
||||
const { WebAudioFront } = require("devtools/shared/fronts/webaudio");
|
||||
var Promise = require("promise");
|
||||
|
||||
function WebAudioEditorPanel(iframeWindow, toolbox) {
|
||||
|
@ -32,7 +32,7 @@ add_task(function* () {
|
||||
let menu = toolbox.showFramesMenu({target: btn});
|
||||
yield once(menu, "open");
|
||||
|
||||
let frames = menu.menuitems;
|
||||
let frames = menu.items;
|
||||
is(frames.length, 2, "We have both frames in the list");
|
||||
|
||||
// Select the iframe
|
||||
|
@ -14,7 +14,7 @@ var { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUID
|
||||
|
||||
var Promise = require("promise");
|
||||
var Services = require("Services");
|
||||
var { WebAudioFront } = require("devtools/server/actors/webaudio");
|
||||
var { WebAudioFront } = require("devtools/shared/fronts/webaudio");
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
var audioNodes = require("devtools/server/actors/utils/audionodes.json");
|
||||
var mm = null;
|
||||
|
@ -28,6 +28,7 @@ const WebConsoleUtils = require("devtools/shared/webconsole/utils").Utils;
|
||||
const { getSourceNames } = require("devtools/client/shared/source-utils");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
|
||||
const nodeConstants = require("devtools/shared/dom-node-constants");
|
||||
|
||||
const MAX_STRING_GRIP_LENGTH = 36;
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
@ -1506,7 +1507,7 @@ Messages.ConsoleGeneric.prototype = extend(Messages.Extended.prototype, {
|
||||
let result = elem;
|
||||
|
||||
if (style) {
|
||||
if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
if (elem.nodeType == nodeConstants.ELEMENT_NODE) {
|
||||
elem.style = style;
|
||||
} else {
|
||||
let span = this.document.createElementNS(XHTML_NS, "span");
|
||||
@ -3054,12 +3055,12 @@ Widgets.ObjectRenderers.add({
|
||||
}
|
||||
|
||||
switch (preview.nodeType) {
|
||||
case Ci.nsIDOMNode.DOCUMENT_NODE:
|
||||
case Ci.nsIDOMNode.ATTRIBUTE_NODE:
|
||||
case Ci.nsIDOMNode.TEXT_NODE:
|
||||
case Ci.nsIDOMNode.COMMENT_NODE:
|
||||
case Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE:
|
||||
case Ci.nsIDOMNode.ELEMENT_NODE:
|
||||
case nodeConstants.DOCUMENT_NODE:
|
||||
case nodeConstants.ATTRIBUTE_NODE:
|
||||
case nodeConstants.TEXT_NODE:
|
||||
case nodeConstants.COMMENT_NODE:
|
||||
case nodeConstants.DOCUMENT_FRAGMENT_NODE:
|
||||
case nodeConstants.ELEMENT_NODE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -3069,26 +3070,26 @@ Widgets.ObjectRenderers.add({
|
||||
render: function ()
|
||||
{
|
||||
switch (this.objectActor.preview.nodeType) {
|
||||
case Ci.nsIDOMNode.DOCUMENT_NODE:
|
||||
case nodeConstants.DOCUMENT_NODE:
|
||||
this._renderDocumentNode();
|
||||
break;
|
||||
case Ci.nsIDOMNode.ATTRIBUTE_NODE: {
|
||||
case nodeConstants.ATTRIBUTE_NODE: {
|
||||
let {preview} = this.objectActor;
|
||||
this.element = this.el("span.attributeNode.kind-" + preview.kind);
|
||||
let attr = this._renderAttributeNode(preview.nodeName, preview.value, true);
|
||||
this.element.appendChild(attr);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIDOMNode.TEXT_NODE:
|
||||
case nodeConstants.TEXT_NODE:
|
||||
this._renderTextNode();
|
||||
break;
|
||||
case Ci.nsIDOMNode.COMMENT_NODE:
|
||||
case nodeConstants.COMMENT_NODE:
|
||||
this._renderCommentNode();
|
||||
break;
|
||||
case Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE:
|
||||
case nodeConstants.DOCUMENT_FRAGMENT_NODE:
|
||||
this._renderDocumentFragmentNode();
|
||||
break;
|
||||
case Ci.nsIDOMNode.ELEMENT_NODE:
|
||||
case nodeConstants.ELEMENT_NODE:
|
||||
this._renderElementNode();
|
||||
break;
|
||||
default:
|
||||
@ -3234,7 +3235,7 @@ Widgets.ObjectRenderers.add({
|
||||
* inspector, or rejects if it wasn't (either if no toolbox could be found to
|
||||
* access the inspector, or if the node isn't present in the inspector, i.e.
|
||||
* if the node is in a DocumentFragment or not part of the tree, or not of
|
||||
* type Ci.nsIDOMNode.ELEMENT_NODE).
|
||||
* type nodeConstants.ELEMENT_NODE).
|
||||
*/
|
||||
linkToInspector: Task.async(function* ()
|
||||
{
|
||||
@ -3243,7 +3244,7 @@ Widgets.ObjectRenderers.add({
|
||||
}
|
||||
|
||||
// Checking the node type
|
||||
if (this.objectActor.preview.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
if (this.objectActor.preview.nodeType !== nodeConstants.ELEMENT_NODE) {
|
||||
throw new Error("The object cannot be linked to the inspector as it " +
|
||||
"isn't an element node");
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ var listener = {
|
||||
observe: function (subject) {
|
||||
if (subject instanceof Ci.nsIScriptError &&
|
||||
subject.category === "XPConnect JavaScript" &&
|
||||
subject.sourceName.contains("webconsole")) {
|
||||
subject.sourceName.includes("webconsole")) {
|
||||
good = false;
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ const {AppValidator} = require("devtools/client/webide/modules/app-validator");
|
||||
const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
|
||||
const {AppActorFront} = require("devtools/shared/apps/app-actor-front");
|
||||
const {getDeviceFront} = require("devtools/server/actors/device");
|
||||
const {getPreferenceFront} = require("devtools/server/actors/preference");
|
||||
const {getSettingsFront} = require("devtools/server/actors/settings");
|
||||
const {getPreferenceFront} = require("devtools/shared/fronts/preference");
|
||||
const {getSettingsFront} = require("devtools/shared/fronts/settings");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const {RuntimeScanners, RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
|
||||
const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
|
||||
|
@ -11,50 +11,34 @@ const { Cu, CC, components } = require("chrome");
|
||||
const Services = require("Services");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const { registerActor, unregisterActor } = require("devtools/server/actors/utils/actor-registry-utils");
|
||||
|
||||
loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
|
||||
const { actorActorSpec, actorRegistrySpec } = require("devtools/shared/specs/actor-registry");
|
||||
|
||||
/**
|
||||
* The ActorActor gives you a handle to an actor you've dynamically
|
||||
* registered and allows you to unregister it.
|
||||
*/
|
||||
const ActorActor = protocol.ActorClass({
|
||||
typeName: "actorActor",
|
||||
|
||||
const ActorActor = protocol.ActorClassWithSpec(actorActorSpec, {
|
||||
initialize: function (conn, options) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
|
||||
this.options = options;
|
||||
},
|
||||
|
||||
unregister: method(function () {
|
||||
unregister: function () {
|
||||
unregisterActor(this.options);
|
||||
}, {
|
||||
request: {},
|
||||
response: {}
|
||||
})
|
||||
});
|
||||
|
||||
const ActorActorFront = protocol.FrontClass(ActorActor, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
}
|
||||
});
|
||||
|
||||
exports.ActorActorFront = ActorActorFront;
|
||||
|
||||
/*
|
||||
* The ActorRegistryActor allows clients to define new actors on the
|
||||
* server. This is particularly useful for addons.
|
||||
*/
|
||||
const ActorRegistryActor = protocol.ActorClass({
|
||||
typeName: "actorRegistry",
|
||||
|
||||
const ActorRegistryActor = protocol.ActorClassWithSpec(actorRegistrySpec, {
|
||||
initialize: function (conn) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
},
|
||||
|
||||
registerActor: method(function (sourceText, fileName, options) {
|
||||
registerActor: function (sourceText, fileName, options) {
|
||||
return registerActor(sourceText, fileName, options).then(() => {
|
||||
let { constructor, type } = options;
|
||||
|
||||
@ -64,63 +48,7 @@ const ActorRegistryActor = protocol.ActorClass({
|
||||
global: type.global
|
||||
});
|
||||
});
|
||||
}, {
|
||||
request: {
|
||||
sourceText: Arg(0, "string"),
|
||||
filename: Arg(1, "string"),
|
||||
options: Arg(2, "json")
|
||||
},
|
||||
|
||||
response: {
|
||||
actorActor: RetVal("actorActor")
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
exports.ActorRegistryActor = ActorRegistryActor;
|
||||
|
||||
function request(uri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
uri = Services.io.newURI(uri, null, null);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
|
||||
NetUtil.asyncFetch({
|
||||
uri,
|
||||
loadUsingSystemPrincipal: true,
|
||||
}, (stream, status, req) => {
|
||||
if (!components.isSuccessCode(status)) {
|
||||
reject(new Error("Request failed with status code = "
|
||||
+ status
|
||||
+ " after NetUtil.asyncFetch for url = "
|
||||
+ uri));
|
||||
return;
|
||||
}
|
||||
|
||||
let source = NetUtil.readInputStreamToString(stream, stream.available());
|
||||
stream.close();
|
||||
resolve(source);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const ActorRegistryFront = protocol.FrontClass(ActorRegistryActor, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client,
|
||||
{ actor: form.actorRegistryActor });
|
||||
|
||||
this.manage(this);
|
||||
},
|
||||
|
||||
registerActor: custom(function (uri, options) {
|
||||
return request(uri, options)
|
||||
.then(sourceText => {
|
||||
return this._registerActor(sourceText, uri, options);
|
||||
});
|
||||
}, {
|
||||
impl: "_registerActor"
|
||||
})
|
||||
});
|
||||
exports.ActorRegistryFront = ActorRegistryFront;
|
||||
|
@ -11,31 +11,14 @@ const {serializeStack, parseStack} = require("toolkit/loader");
|
||||
const {on, once, off, emit} = events;
|
||||
const {method, Arg, Option, RetVal} = protocol;
|
||||
|
||||
/**
|
||||
* Type describing a single function call in a stack trace.
|
||||
*/
|
||||
protocol.types.addDictType("call-stack-item", {
|
||||
name: "string",
|
||||
file: "string",
|
||||
line: "number"
|
||||
});
|
||||
|
||||
/**
|
||||
* Type describing an overview of a function call.
|
||||
*/
|
||||
protocol.types.addDictType("call-details", {
|
||||
type: "number",
|
||||
name: "string",
|
||||
stack: "array:call-stack-item"
|
||||
});
|
||||
const { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher");
|
||||
const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
|
||||
|
||||
/**
|
||||
* This actor contains information about a function call, like the function
|
||||
* type, name, stack, arguments, returned value etc.
|
||||
*/
|
||||
var FunctionCallActor = protocol.ActorClass({
|
||||
typeName: "function-call",
|
||||
|
||||
var FunctionCallActor = protocol.ActorClassWithSpec(functionCallSpec, {
|
||||
/**
|
||||
* Creates the function call actor.
|
||||
*
|
||||
@ -135,7 +118,7 @@ var FunctionCallActor = protocol.ActorClass({
|
||||
* Gets more information about this function call, which is not necessarily
|
||||
* available on the Front instance.
|
||||
*/
|
||||
getDetails: method(function () {
|
||||
getDetails: function () {
|
||||
let { type, name, stack, timestamp } = this.details;
|
||||
|
||||
// Since not all calls on the stack have corresponding owner files (e.g.
|
||||
@ -157,9 +140,7 @@ var FunctionCallActor = protocol.ActorClass({
|
||||
stack: stack,
|
||||
timestamp: timestamp
|
||||
};
|
||||
}, {
|
||||
response: { info: RetVal("call-details") }
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes the arguments so that they can be easily be transferred
|
||||
@ -242,36 +223,10 @@ var FunctionCallActor = protocol.ActorClass({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the FunctionCallActor.
|
||||
*/
|
||||
var FunctionCallFront = protocol.FrontClass(FunctionCallActor, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds some generic information directly to this instance,
|
||||
* to avoid extra roundtrips.
|
||||
*/
|
||||
form: function (form) {
|
||||
this.actorID = form.actor;
|
||||
this.type = form.type;
|
||||
this.name = form.name;
|
||||
this.file = form.file;
|
||||
this.line = form.line;
|
||||
this.timestamp = form.timestamp;
|
||||
this.callerPreview = form.callerPreview;
|
||||
this.argsPreview = form.argsPreview;
|
||||
this.resultPreview = form.resultPreview;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This actor observes function calls on certain objects or globals.
|
||||
*/
|
||||
var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
|
||||
typeName: "call-watcher",
|
||||
var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClassWithSpec(callWatcherSpec, {
|
||||
initialize: function (conn, tabActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.tabActor = tabActor;
|
||||
@ -284,16 +239,6 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
|
||||
this.finalize();
|
||||
},
|
||||
|
||||
events: {
|
||||
/**
|
||||
* Events emitted when the `onCall` function isn't provided.
|
||||
*/
|
||||
"call": {
|
||||
type: "call",
|
||||
function: Arg(0, "function-call")
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Lightweight listener invoked whenever an instrumented function is called
|
||||
* while recording. We're doing this to avoid the event emitter overhead,
|
||||
@ -306,7 +251,7 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
|
||||
* created, in order to instrument the specified objects and become
|
||||
* aware of everything the content does with them.
|
||||
*/
|
||||
setup: method(function ({ tracedGlobals, tracedFunctions, startRecording, performReload, holdWeak, storeCalls }) {
|
||||
setup: function ({ tracedGlobals, tracedFunctions, startRecording, performReload, holdWeak, storeCalls }) {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
@ -328,24 +273,14 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
|
||||
if (performReload) {
|
||||
this.tabActor.window.location.reload();
|
||||
}
|
||||
}, {
|
||||
request: {
|
||||
tracedGlobals: Option(0, "nullable:array:string"),
|
||||
tracedFunctions: Option(0, "nullable:array:string"),
|
||||
startRecording: Option(0, "boolean"),
|
||||
performReload: Option(0, "boolean"),
|
||||
holdWeak: Option(0, "boolean"),
|
||||
storeCalls: Option(0, "boolean")
|
||||
},
|
||||
oneway: true
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops listening for document global changes and puts this actor
|
||||
* to hibernation. This method is called automatically just before the
|
||||
* actor is destroyed.
|
||||
*/
|
||||
finalize: method(function () {
|
||||
finalize: function () {
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
@ -357,50 +292,44 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
|
||||
|
||||
this._tracedGlobals = null;
|
||||
this._tracedFunctions = null;
|
||||
}, {
|
||||
oneway: true
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether the instrumented function calls are currently recorded.
|
||||
*/
|
||||
isRecording: method(function () {
|
||||
isRecording: function () {
|
||||
return this._recording;
|
||||
}, {
|
||||
response: RetVal("boolean")
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the timestamp epoch used to offset function call timestamps.
|
||||
*/
|
||||
initTimestampEpoch: method(function () {
|
||||
initTimestampEpoch: function () {
|
||||
this._timestampEpoch = this.tabActor.window.performance.now();
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts recording function calls.
|
||||
*/
|
||||
resumeRecording: method(function () {
|
||||
resumeRecording: function () {
|
||||
this._recording = true;
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops recording function calls.
|
||||
*/
|
||||
pauseRecording: method(function () {
|
||||
pauseRecording: function () {
|
||||
this._recording = false;
|
||||
return this._functionCalls;
|
||||
}, {
|
||||
response: { calls: RetVal("array:function-call") }
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases all the recorded function calls.
|
||||
* Calling `resumeRecording` or `pauseRecording` does not erase history.
|
||||
*/
|
||||
eraseRecording: method(function () {
|
||||
eraseRecording: function () {
|
||||
this._functionCalls = [];
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever the current tab actor's document global is created.
|
||||
@ -604,196 +533,6 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the CallWatcherActor.
|
||||
*/
|
||||
var CallWatcherFront = exports.CallWatcherFront = protocol.FrontClass(CallWatcherActor, {
|
||||
initialize: function (client, { callWatcherActor }) {
|
||||
protocol.Front.prototype.initialize.call(this, client, { actor: callWatcherActor });
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
CallWatcherFront.METHOD_FUNCTION = 0;
|
||||
CallWatcherFront.GETTER_FUNCTION = 1;
|
||||
CallWatcherFront.SETTER_FUNCTION = 2;
|
||||
|
||||
CallWatcherFront.KNOWN_METHODS = {};
|
||||
|
||||
CallWatcherFront.KNOWN_METHODS["CanvasRenderingContext2D"] = {
|
||||
asyncDrawXULElement: {
|
||||
enums: new Set([6]),
|
||||
},
|
||||
drawWindow: {
|
||||
enums: new Set([6])
|
||||
},
|
||||
};
|
||||
|
||||
CallWatcherFront.KNOWN_METHODS["WebGLRenderingContext"] = {
|
||||
activeTexture: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
bindBuffer: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
bindFramebuffer: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
bindRenderbuffer: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
bindTexture: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
blendEquation: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
blendEquationSeparate: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
blendFunc: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
blendFuncSeparate: {
|
||||
enums: new Set([0, 1, 2, 3]),
|
||||
},
|
||||
bufferData: {
|
||||
enums: new Set([0, 1, 2]),
|
||||
},
|
||||
bufferSubData: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
checkFramebufferStatus: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
clear: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
compressedTexImage2D: {
|
||||
enums: new Set([0, 2]),
|
||||
},
|
||||
compressedTexSubImage2D: {
|
||||
enums: new Set([0, 6]),
|
||||
},
|
||||
copyTexImage2D: {
|
||||
enums: new Set([0, 2]),
|
||||
},
|
||||
copyTexSubImage2D: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
createShader: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
cullFace: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
depthFunc: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
disable: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
drawArrays: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
drawElements: {
|
||||
enums: new Set([0, 2]),
|
||||
},
|
||||
enable: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
framebufferRenderbuffer: {
|
||||
enums: new Set([0, 1, 2]),
|
||||
},
|
||||
framebufferTexture2D: {
|
||||
enums: new Set([0, 1, 2]),
|
||||
},
|
||||
frontFace: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
generateMipmap: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
getBufferParameter: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
getParameter: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
getFramebufferAttachmentParameter: {
|
||||
enums: new Set([0, 1, 2]),
|
||||
},
|
||||
getProgramParameter: {
|
||||
enums: new Set([1]),
|
||||
},
|
||||
getRenderbufferParameter: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
getShaderParameter: {
|
||||
enums: new Set([1]),
|
||||
},
|
||||
getShaderPrecisionFormat: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
getTexParameter: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
getVertexAttrib: {
|
||||
enums: new Set([1]),
|
||||
},
|
||||
getVertexAttribOffset: {
|
||||
enums: new Set([1]),
|
||||
},
|
||||
hint: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
isEnabled: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
pixelStorei: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
readPixels: {
|
||||
enums: new Set([4, 5]),
|
||||
},
|
||||
renderbufferStorage: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
stencilFunc: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
stencilFuncSeparate: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
stencilMaskSeparate: {
|
||||
enums: new Set([0]),
|
||||
},
|
||||
stencilOp: {
|
||||
enums: new Set([0, 1, 2]),
|
||||
},
|
||||
stencilOpSeparate: {
|
||||
enums: new Set([0, 1, 2, 3]),
|
||||
},
|
||||
texImage2D: {
|
||||
enums: args => args.length > 6 ? new Set([0, 2, 6, 7]) : new Set([0, 2, 3, 4]),
|
||||
},
|
||||
texParameterf: {
|
||||
enums: new Set([0, 1]),
|
||||
},
|
||||
texParameteri: {
|
||||
enums: new Set([0, 1, 2]),
|
||||
},
|
||||
texSubImage2D: {
|
||||
enums: args => args.length === 9 ? new Set([0, 6, 7]) : new Set([0, 4, 5]),
|
||||
},
|
||||
vertexAttribPointer: {
|
||||
enums: new Set([2])
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A lookup table for cross-referencing flags or properties with their name
|
||||
* assuming they look LIKE_THIS most of the time.
|
||||
|
@ -7,101 +7,30 @@ const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
const events = require("sdk/event/core");
|
||||
const promise = require("promise");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const {CallWatcherActor, CallWatcherFront} = require("devtools/server/actors/call-watcher");
|
||||
const {CallWatcherActor} = require("devtools/server/actors/call-watcher");
|
||||
const {CallWatcherFront} = require("devtools/shared/fronts/call-watcher");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const {WebGLPrimitiveCounter} = require("devtools/server/primitive");
|
||||
const {
|
||||
frameSnapshotSpec,
|
||||
canvasSpec,
|
||||
CANVAS_CONTEXTS,
|
||||
ANIMATION_GENERATORS,
|
||||
LOOP_GENERATORS,
|
||||
DRAW_CALLS,
|
||||
INTERESTING_CALLS,
|
||||
} = require("devtools/shared/specs/canvas");
|
||||
const {CanvasFront} = require("devtools/shared/fronts/canvas");
|
||||
|
||||
const {on, once, off, emit} = events;
|
||||
const {method, custom, Arg, Option, RetVal} = protocol;
|
||||
|
||||
const CANVAS_CONTEXTS = [
|
||||
"CanvasRenderingContext2D",
|
||||
"WebGLRenderingContext"
|
||||
];
|
||||
|
||||
const ANIMATION_GENERATORS = [
|
||||
"requestAnimationFrame"
|
||||
];
|
||||
|
||||
const LOOP_GENERATORS = [
|
||||
"setTimeout"
|
||||
];
|
||||
|
||||
const DRAW_CALLS = [
|
||||
// 2D canvas
|
||||
"fill",
|
||||
"stroke",
|
||||
"clearRect",
|
||||
"fillRect",
|
||||
"strokeRect",
|
||||
"fillText",
|
||||
"strokeText",
|
||||
"drawImage",
|
||||
|
||||
// WebGL
|
||||
"clear",
|
||||
"drawArrays",
|
||||
"drawElements",
|
||||
"finish",
|
||||
"flush"
|
||||
];
|
||||
|
||||
const INTERESTING_CALLS = [
|
||||
// 2D canvas
|
||||
"save",
|
||||
"restore",
|
||||
|
||||
// WebGL
|
||||
"useProgram"
|
||||
];
|
||||
|
||||
/**
|
||||
* Type representing an ArrayBufferView, serialized fast(er).
|
||||
*
|
||||
* Don't create a new array buffer view from the parsed array on the frontend.
|
||||
* Consumers may copy the data into an existing buffer, or create a new one if
|
||||
* necesasry. For example, this avoids the need for a redundant copy when
|
||||
* populating ImageData objects, at the expense of transferring char views
|
||||
* of a pixel buffer over the protocol instead of a packed int view.
|
||||
*
|
||||
* XXX: It would be nice if on local connections (only), we could just *give*
|
||||
* the buffer directly to the front, instead of going through all this
|
||||
* serialization redundancy.
|
||||
*/
|
||||
protocol.types.addType("array-buffer-view", {
|
||||
write: (v) => "[" + Array.join(v, ",") + "]",
|
||||
read: (v) => JSON.parse(v)
|
||||
});
|
||||
|
||||
/**
|
||||
* Type describing a thumbnail or screenshot in a recorded animation frame.
|
||||
*/
|
||||
protocol.types.addDictType("snapshot-image", {
|
||||
index: "number",
|
||||
width: "number",
|
||||
height: "number",
|
||||
scaling: "number",
|
||||
flipped: "boolean",
|
||||
pixels: "array-buffer-view"
|
||||
});
|
||||
|
||||
/**
|
||||
* Type describing an overview of a recorded animation frame.
|
||||
*/
|
||||
protocol.types.addDictType("snapshot-overview", {
|
||||
calls: "array:function-call",
|
||||
thumbnails: "array:snapshot-image",
|
||||
screenshot: "snapshot-image"
|
||||
});
|
||||
|
||||
/**
|
||||
* This actor represents a recorded animation frame snapshot, along with
|
||||
* all the corresponding canvas' context methods invoked in that frame,
|
||||
* thumbnails for each draw call and a screenshot of the end result.
|
||||
*/
|
||||
var FrameSnapshotActor = protocol.ActorClass({
|
||||
typeName: "frame-snapshot",
|
||||
|
||||
var FrameSnapshotActor = protocol.ActorClassWithSpec(frameSnapshotSpec, {
|
||||
/**
|
||||
* Creates the frame snapshot call actor.
|
||||
*
|
||||
@ -125,7 +54,7 @@ var FrameSnapshotActor = protocol.ActorClass({
|
||||
/**
|
||||
* Gets as much data about this snapshot without computing anything costly.
|
||||
*/
|
||||
getOverview: method(function () {
|
||||
getOverview: function () {
|
||||
return {
|
||||
calls: this._functionCalls,
|
||||
thumbnails: this._functionCalls.map(e => e._thumbnail).filter(e => !!e),
|
||||
@ -137,15 +66,13 @@ var FrameSnapshotActor = protocol.ActorClass({
|
||||
lines: this._primitive.lines
|
||||
}
|
||||
};
|
||||
}, {
|
||||
response: { overview: RetVal("snapshot-overview") }
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a screenshot of the canvas's contents after the specified
|
||||
* function was called.
|
||||
*/
|
||||
generateScreenshotFor: method(function (functionCall) {
|
||||
generateScreenshotFor: function (functionCall) {
|
||||
let caller = functionCall.details.caller;
|
||||
let global = functionCall.details.global;
|
||||
|
||||
@ -185,54 +112,7 @@ var FrameSnapshotActor = protocol.ActorClass({
|
||||
screenshot.scaling = replayContextScaling;
|
||||
screenshot.index = lastDrawCallIndex;
|
||||
return screenshot;
|
||||
}, {
|
||||
request: { call: Arg(0, "function-call") },
|
||||
response: { screenshot: RetVal("snapshot-image") }
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the FrameSnapshotActor.
|
||||
*/
|
||||
var FrameSnapshotFront = protocol.FrontClass(FrameSnapshotActor, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
this._animationFrameEndScreenshot = null;
|
||||
this._cachedScreenshots = new WeakMap();
|
||||
},
|
||||
|
||||
/**
|
||||
* This implementation caches the animation frame end screenshot to optimize
|
||||
* frontend requests to `generateScreenshotFor`.
|
||||
*/
|
||||
getOverview: custom(function () {
|
||||
return this._getOverview().then(data => {
|
||||
this._animationFrameEndScreenshot = data.screenshot;
|
||||
return data;
|
||||
});
|
||||
}, {
|
||||
impl: "_getOverview"
|
||||
}),
|
||||
|
||||
/**
|
||||
* This implementation saves a roundtrip to the backend if the screenshot
|
||||
* was already generated and retrieved once.
|
||||
*/
|
||||
generateScreenshotFor: custom(function (functionCall) {
|
||||
if (CanvasFront.ANIMATION_GENERATORS.has(functionCall.name) ||
|
||||
CanvasFront.LOOP_GENERATORS.has(functionCall.name)) {
|
||||
return promise.resolve(this._animationFrameEndScreenshot);
|
||||
}
|
||||
let cachedScreenshot = this._cachedScreenshots.get(functionCall);
|
||||
if (cachedScreenshot) {
|
||||
return cachedScreenshot;
|
||||
}
|
||||
let screenshot = this._generateScreenshotFor(functionCall);
|
||||
this._cachedScreenshots.set(functionCall, screenshot);
|
||||
return screenshot;
|
||||
}, {
|
||||
impl: "_generateScreenshotFor"
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@ -240,12 +120,11 @@ var FrameSnapshotFront = protocol.FrontClass(FrameSnapshotActor, {
|
||||
* of a 2D or WebGL context, to provide information regarding all the calls
|
||||
* made when drawing frame inside an animation loop.
|
||||
*/
|
||||
var CanvasActor = exports.CanvasActor = protocol.ActorClass({
|
||||
var CanvasActor = exports.CanvasActor = protocol.ActorClassWithSpec(canvasSpec, {
|
||||
// Reset for each recording, boolean indicating whether or not
|
||||
// any draw calls were called for a recording.
|
||||
_animationContainsDrawCall: false,
|
||||
|
||||
typeName: "canvas",
|
||||
initialize: function (conn, tabActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.tabActor = tabActor;
|
||||
@ -261,7 +140,7 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
|
||||
/**
|
||||
* Starts listening for function calls.
|
||||
*/
|
||||
setup: method(function ({ reload }) {
|
||||
setup: function ({ reload }) {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
@ -275,15 +154,12 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
|
||||
performReload: reload,
|
||||
storeCalls: true
|
||||
});
|
||||
}, {
|
||||
request: { reload: Option(0, "boolean") },
|
||||
oneway: true
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops listening for function calls.
|
||||
*/
|
||||
finalize: method(function () {
|
||||
finalize: function () {
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
@ -291,35 +167,29 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
|
||||
|
||||
this._callWatcher.finalize();
|
||||
this._callWatcher = null;
|
||||
}, {
|
||||
oneway: true
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether this actor has been set up.
|
||||
*/
|
||||
isInitialized: method(function () {
|
||||
isInitialized: function () {
|
||||
return !!this._initialized;
|
||||
}, {
|
||||
response: { initialized: RetVal("boolean") }
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether or not the CanvasActor is recording an animation.
|
||||
* Used in tests.
|
||||
*/
|
||||
isRecording: method(function () {
|
||||
isRecording: function () {
|
||||
return !!this._callWatcher.isRecording();
|
||||
}, {
|
||||
response: { recording: RetVal("boolean") }
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Records a snapshot of all the calls made during the next animation frame.
|
||||
* The animation should be implemented via the de-facto requestAnimationFrame
|
||||
* utility, or inside recursive `setTimeout`s. `setInterval` at this time are not supported.
|
||||
*/
|
||||
recordAnimationFrame: method(function () {
|
||||
recordAnimationFrame: function () {
|
||||
if (this._callWatcher.isRecording()) {
|
||||
return this._currentAnimationFrameSnapshot.promise;
|
||||
}
|
||||
@ -332,14 +202,12 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
|
||||
|
||||
let deferred = this._currentAnimationFrameSnapshot = promise.defer();
|
||||
return deferred.promise;
|
||||
}, {
|
||||
response: { snapshot: RetVal("nullable:frame-snapshot") }
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Cease attempts to record an animation frame.
|
||||
*/
|
||||
stopRecordingAnimationFrame: method(function () {
|
||||
stopRecordingAnimationFrame: function () {
|
||||
if (!this._callWatcher.isRecording()) {
|
||||
return;
|
||||
}
|
||||
@ -348,9 +216,7 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
|
||||
this._callWatcher.eraseRecording();
|
||||
this._currentAnimationFrameSnapshot.resolve(null);
|
||||
this._currentAnimationFrameSnapshot = null;
|
||||
}, {
|
||||
oneway: true
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever an instrumented function is called, be it on a
|
||||
@ -841,33 +707,6 @@ var ContextUtils = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the CanvasActor.
|
||||
*/
|
||||
var CanvasFront = exports.CanvasFront = protocol.FrontClass(CanvasActor, {
|
||||
initialize: function (client, { canvasActor }) {
|
||||
protocol.Front.prototype.initialize.call(this, client, { actor: canvasActor });
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
CanvasFront.CANVAS_CONTEXTS = new Set(CANVAS_CONTEXTS);
|
||||
CanvasFront.ANIMATION_GENERATORS = new Set(ANIMATION_GENERATORS);
|
||||
CanvasFront.LOOP_GENERATORS = new Set(LOOP_GENERATORS);
|
||||
CanvasFront.DRAW_CALLS = new Set(DRAW_CALLS);
|
||||
CanvasFront.INTERESTING_CALLS = new Set(INTERESTING_CALLS);
|
||||
CanvasFront.THUMBNAIL_SIZE = 50; // px
|
||||
CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT = 256; // px
|
||||
CanvasFront.INVALID_SNAPSHOT_IMAGE = {
|
||||
index: -1,
|
||||
width: 0,
|
||||
height: 0,
|
||||
pixels: []
|
||||
};
|
||||
|
||||
/**
|
||||
* Goes through all the arguments and creates a one-level shallow copy
|
||||
* of all arrays and array buffers.
|
||||
|
@ -13,6 +13,7 @@ loader.lazyGetter(this, "DOMUtils", () => {
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const { ActorClassWithSpec, Actor } = protocol;
|
||||
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
|
||||
const clientCssDatabase = require("devtools/shared/css-properties-db")
|
||||
|
||||
var CssPropertiesActor = exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
|
||||
typeName: "cssProperties",
|
||||
@ -27,8 +28,22 @@ var CssPropertiesActor = exports.CssPropertiesActor = ActorClassWithSpec(cssProp
|
||||
},
|
||||
|
||||
getCSSDatabase: function() {
|
||||
const propertiesList = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
|
||||
return { propertiesList };
|
||||
const db = {};
|
||||
const properties = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
|
||||
|
||||
properties.forEach(name => {
|
||||
// In order to maintain any backwards compatible changes when debugging
|
||||
// older clients, take the definition from the static database in the
|
||||
// devtools client, and fill it in with the most recent property
|
||||
// definition from the server.
|
||||
const clientDefinition = clientCssDatabase[name] || {};
|
||||
const serverDefinition = {
|
||||
isInherited: DOMUtils.isInheritedProperty(name)
|
||||
};
|
||||
db[name] = Object.assign(clientDefinition, serverDefinition);
|
||||
});
|
||||
|
||||
return db;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9,6 +9,8 @@ const { method, Arg } = protocol;
|
||||
const Services = require("Services");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
|
||||
const { heapSnapshotFileSpec } = require("devtools/shared/specs/heap-snapshot-file");
|
||||
|
||||
loader.lazyRequireGetter(this, "DevToolsUtils",
|
||||
"devtools/shared/DevToolsUtils");
|
||||
loader.lazyRequireGetter(this, "OS", "resource://gre/modules/osfile.jsm", true);
|
||||
@ -21,9 +23,7 @@ loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
|
||||
* because child processes are sandboxed and do not have access to the file
|
||||
* system.
|
||||
*/
|
||||
exports.HeapSnapshotFileActor = protocol.ActorClass({
|
||||
typeName: "heapSnapshotFile",
|
||||
|
||||
exports.HeapSnapshotFileActor = protocol.ActorClassWithSpec(heapSnapshotFileSpec, {
|
||||
initialize: function (conn, parent) {
|
||||
if (Services.appInfo &&
|
||||
(Services.appInfo.processType !==
|
||||
@ -42,7 +42,7 @@ exports.HeapSnapshotFileActor = protocol.ActorClass({
|
||||
/**
|
||||
* @see MemoryFront.prototype.transferHeapSnapshot
|
||||
*/
|
||||
transferHeapSnapshot: method(Task.async(function* (snapshotId) {
|
||||
transferHeapSnapshot: Task.async(function* (snapshotId) {
|
||||
const snapshotFilePath =
|
||||
HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
|
||||
if (!snapshotFilePath) {
|
||||
@ -65,10 +65,6 @@ exports.HeapSnapshotFileActor = protocol.ActorClass({
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}), {
|
||||
request: {
|
||||
snapshotId: Arg(0, "string")
|
||||
}
|
||||
}),
|
||||
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user