зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c.
This commit is contained in:
Коммит
4fdf1ddb09
|
@ -244,6 +244,7 @@ pref("lightweightThemes.update.enabled", true);
|
|||
|
||||
// UI tour experience.
|
||||
pref("browser.uitour.enabled", true);
|
||||
pref("browser.uitour.requireSecure", true);
|
||||
pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
|
||||
pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
|
||||
pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
|
||||
|
|
|
@ -146,7 +146,8 @@ let gPage = {
|
|||
handleEvent: function Page_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "unload":
|
||||
this._mutationObserver.disconnect();
|
||||
if (this._mutationObserver)
|
||||
this._mutationObserver.disconnect();
|
||||
gAllPages.unregister(this);
|
||||
break;
|
||||
case "click":
|
||||
|
|
|
@ -15,24 +15,20 @@ function test() {
|
|||
// Verify that about:preferences tab is displayed when
|
||||
// browser.preferences.inContent is set to true
|
||||
Services.prefs.setBoolPref("browser.preferences.inContent", true);
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabOpen", function(aEvent) {
|
||||
|
||||
gBrowser.tabContainer.removeEventListener("TabOpen", arguments.callee, true);
|
||||
let browser = aEvent.originalTarget.linkedBrowser;
|
||||
browser.addEventListener("load", function(aEvent) {
|
||||
browser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
is(Services.prefs.getBoolPref("browser.preferences.inContent"), true, "In-content prefs are enabled");
|
||||
is(browser.contentWindow.location.href, "about:preferences", "Checking if the preferences tab was opened");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
Services.prefs.setBoolPref("browser.preferences.inContent", false);
|
||||
openPreferences();
|
||||
|
||||
}, true);
|
||||
}, true);
|
||||
|
||||
|
||||
// Open a new tab.
|
||||
whenNewTabLoaded(window, testPreferences);
|
||||
}
|
||||
|
||||
function testPreferences() {
|
||||
whenTabLoaded(gBrowser.selectedTab, function () {
|
||||
is(Services.prefs.getBoolPref("browser.preferences.inContent"), true, "In-content prefs are enabled");
|
||||
is(content.location.href, "about:preferences", "Checking if the preferences tab was opened");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
Services.prefs.setBoolPref("browser.preferences.inContent", false);
|
||||
openPreferences();
|
||||
});
|
||||
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
|
|
|
@ -237,9 +237,14 @@ function whenNewTabLoaded(aWindow, aCallback) {
|
|||
return;
|
||||
}
|
||||
|
||||
whenTabLoaded(aWindow.gBrowser.selectedTab, aCallback);
|
||||
}
|
||||
|
||||
function whenTabLoaded(aTab, aCallback) {
|
||||
let browser = aTab.linkedBrowser;
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
aCallback();
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -473,8 +473,37 @@ function openAboutDialog() {
|
|||
|
||||
function openPreferences(paneID, extraArgs)
|
||||
{
|
||||
if (Services.prefs.getBoolPref("browser.preferences.inContent")) {
|
||||
openUILinkIn("about:preferences", "tab");
|
||||
function switchToAdvancedSubPane(doc) {
|
||||
if (extraArgs && extraArgs["advancedTab"]) {
|
||||
let advancedPaneTabs = doc.getElementById("advancedPrefs");
|
||||
advancedPaneTabs.selectedTab = doc.getElementById(extraArgs["advancedTab"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (getBoolPref("browser.preferences.inContent")) {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newLoad = !win.switchToTabHavingURI("about:preferences", true);
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
|
||||
function switchToPane() {
|
||||
if (paneID) {
|
||||
browser.contentWindow.selectCategory(paneID);
|
||||
}
|
||||
switchToAdvancedSubPane(browser.contentDocument);
|
||||
}
|
||||
|
||||
if (newLoad) {
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
switchToPane();
|
||||
}, true);
|
||||
} else {
|
||||
switchToPane();
|
||||
}
|
||||
} else {
|
||||
var instantApply = getBoolPref("browser.preferences.instantApply", false);
|
||||
var features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
|
||||
|
@ -487,16 +516,11 @@ function openPreferences(paneID, extraArgs)
|
|||
win.document.documentElement.showPane(pane);
|
||||
}
|
||||
|
||||
if (extraArgs && extraArgs["advancedTab"]) {
|
||||
var advancedPaneTabs = win.document.getElementById("advancedPrefs");
|
||||
advancedPaneTabs.selectedTab = win.document.getElementById(extraArgs["advancedTab"]);
|
||||
}
|
||||
|
||||
return;
|
||||
switchToAdvancedSubPane(win.document);
|
||||
} else {
|
||||
openDialog("chrome://browser/content/preferences/preferences.xul",
|
||||
"Preferences", features, paneID, extraArgs);
|
||||
}
|
||||
|
||||
openDialog("chrome://browser/content/preferences/preferences.xul",
|
||||
"Preferences", features, paneID, extraArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -452,8 +452,10 @@ nsBrowserContentHandler.prototype = {
|
|||
var chromeParam = cmdLine.handleFlagWithParam("chrome", false);
|
||||
if (chromeParam) {
|
||||
|
||||
// Handle the old preference dialog URL separately (bug 285416)
|
||||
if (chromeParam == "chrome://browser/content/pref/pref.xul") {
|
||||
// Handle old preference dialog URLs.
|
||||
if (chromeParam == "chrome://browser/content/pref/pref.xul" ||
|
||||
(Services.prefs.getBoolPref("browser.preferences.inContent") &&
|
||||
chromeParam == "chrome://browser/content/preferences/preferences.xul")) {
|
||||
openPreferences();
|
||||
cmdLine.preventDefault = true;
|
||||
} else try {
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
* 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/. */
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
-moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler");
|
||||
}
|
||||
|
||||
richlistitem[selected="true"] {
|
||||
#handlersView > richlistitem[selected="true"] {
|
||||
-moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler-selected");
|
||||
}
|
||||
|
||||
|
|
|
@ -128,11 +128,6 @@
|
|||
#endif
|
||||
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
|
||||
<hbox class="heading" data-category="paneAdvanced" hidden="true">
|
||||
<image class="preference-icon" type="advanced"/>
|
||||
<html:h1>&paneAdvanced.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<tabbox id="advancedPrefs" flex="1"
|
||||
data-category="paneAdvanced" hidden="true"
|
||||
onselect="gAdvancedPane.tabSelectionChanged();">
|
||||
|
|
|
@ -55,11 +55,6 @@
|
|||
<key key="&focusSearch2.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/>
|
||||
</keyset>
|
||||
|
||||
<hbox class="heading" data-category="paneApplications" hidden="true">
|
||||
<image class="preference-icon" type="applications"/>
|
||||
<html:h1>&paneApplications.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<vbox data-category="paneApplications" hidden="true" flex="1">
|
||||
<hbox>
|
||||
<textbox id="filter" flex="1"
|
||||
|
|
|
@ -21,12 +21,9 @@
|
|||
<script type="application/javascript"
|
||||
src="chrome://browser/content/preferences/in-content/content.js"/>
|
||||
|
||||
<hbox class="heading" data-category="paneContent" hidden="true">
|
||||
<image class="preference-icon" type="content"/>
|
||||
<html:h1>&paneContent.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="miscGroup" data-category="paneContent" hidden="true">
|
||||
<caption label="&popups.label;"/>
|
||||
|
||||
<grid id="contentGrid">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
browser.jar:
|
||||
content/browser/preferences/in-content/preferences.js
|
||||
content/browser/preferences/in-content/landing.xul
|
||||
* content/browser/preferences/in-content/preferences.xul
|
||||
* content/browser/preferences/in-content/main.xul
|
||||
* content/browser/preferences/in-content/main.js
|
||||
|
|
|
@ -1,55 +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/. -->
|
||||
|
||||
<vbox data-category="landing">
|
||||
<html:h1 class="indent-small">&brandShortName;</html:h1>
|
||||
|
||||
<hbox id="preferences-home" flex="1">
|
||||
|
||||
<button label="&paneGeneral.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneGeneral');">
|
||||
<image class="landingButton-icon" type="general"/>
|
||||
<label class="landingButton-label">&paneGeneral.title;</label>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
<button label="&paneContent.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneContent');">
|
||||
<image class="landingButton-icon" type="content"/>
|
||||
<label class="landingButton-label">&paneContent.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneApplications.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneApplications');">
|
||||
<image class="landingButton-icon" type="applications"/>
|
||||
<label class="landingButton-label">&paneApplications.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&panePrivacy.title;" class="landingButton"
|
||||
oncommand="gotoPref('panePrivacy');">
|
||||
<image class="landingButton-icon" type="privacy"/>
|
||||
<label class="landingButton-label">&panePrivacy.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneSecurity.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneSecurity');">
|
||||
<image class="landingButton-icon" type="security"/>
|
||||
<label class="landingButton-label">&paneSecurity.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneSync.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneSync');">
|
||||
<image class="landingButton-icon" type="sync"/>
|
||||
<label class="landingButton-label">&paneSync.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneAdvanced.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneAdvanced');">
|
||||
<image class="landingButton-icon" type="advanced"/>
|
||||
<label class="landingButton-label">&paneAdvanced.title;</label>
|
||||
</button>
|
||||
|
||||
</hbox>
|
||||
</vbox>
|
|
@ -85,13 +85,8 @@
|
|||
#endif
|
||||
</preferences>
|
||||
|
||||
<hbox class="heading" data-category="paneGeneral" hidden="true">
|
||||
<image class="preference-icon" type="general"/>
|
||||
<html:h1>&paneGeneral.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<!-- Startup -->
|
||||
<groupbox id="startupGroup" data-category="paneGeneral" hidden="true">
|
||||
<groupbox id="startupGroup" data-category="paneGeneral">
|
||||
<caption label="&startup.label;"/>
|
||||
|
||||
<hbox align="center">
|
||||
|
@ -150,7 +145,7 @@
|
|||
</groupbox>
|
||||
|
||||
<!-- Downloads -->
|
||||
<groupbox id="downloadsGroup" data-category="paneGeneral" hidden="true">
|
||||
<groupbox id="downloadsGroup" data-category="paneGeneral">
|
||||
<caption label="&downloads.label;"/>
|
||||
|
||||
<radiogroup id="saveWhere"
|
||||
|
@ -189,7 +184,7 @@
|
|||
</groupbox>
|
||||
|
||||
<!-- Tab preferences -->
|
||||
<groupbox data-category="paneGeneral" hidden="true">
|
||||
<groupbox data-category="paneGeneral">
|
||||
<caption label="&tabsGroup.label;"/>
|
||||
<checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
|
||||
accesskey="&newWindowsAsTabs.accesskey;"
|
||||
|
|
|
@ -12,11 +12,13 @@ const Cr = Components.results;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
addEventListener("DOMContentLoaded", function onLoad() {
|
||||
removeEventListener("DOMContentLoaded", onLoad);
|
||||
init_all();
|
||||
});
|
||||
|
||||
function init_all() {
|
||||
document.documentElement.instantApply = true;
|
||||
window.history.replaceState("landing", document.title);
|
||||
window.addEventListener("popstate", onStatePopped, true);
|
||||
updateCommands();
|
||||
gMainPane.init();
|
||||
gPrivacyPane.init();
|
||||
gAdvancedPane.init();
|
||||
|
@ -27,12 +29,32 @@ function init_all() {
|
|||
var initFinished = document.createEvent("Event");
|
||||
initFinished.initEvent("Initialized", true, true);
|
||||
document.dispatchEvent(initFinished);
|
||||
|
||||
let categories = document.getElementById("categories");
|
||||
categories.addEventListener("select", event => gotoPref(event.target.value));
|
||||
window.addEventListener("popstate", event => selectCategory(event.state));
|
||||
|
||||
if (history.length > 1 && history.state) {
|
||||
updateCommands();
|
||||
selectCategory(history.state);
|
||||
} else {
|
||||
history.replaceState("paneGeneral", document.title);
|
||||
}
|
||||
}
|
||||
|
||||
function selectCategory(name) {
|
||||
let categories = document.getElementById("categories");
|
||||
let item = categories.querySelector(".category[value=" + name + "]");
|
||||
categories.selectedItem = item;
|
||||
}
|
||||
|
||||
function gotoPref(page) {
|
||||
search(page, "data-category");
|
||||
window.history.pushState(page, document.title);
|
||||
if (history.state != page) {
|
||||
window.history.pushState(page, document.title);
|
||||
}
|
||||
|
||||
updateCommands();
|
||||
search(page, "data-category");
|
||||
}
|
||||
|
||||
function cmd_back() {
|
||||
|
@ -43,11 +65,6 @@ function cmd_forward() {
|
|||
window.history.forward();
|
||||
}
|
||||
|
||||
function onStatePopped(aEvent) {
|
||||
updateCommands();
|
||||
search(aEvent.state, "data-category");
|
||||
}
|
||||
|
||||
function updateCommands() {
|
||||
document.getElementById("back-btn").disabled = !canGoBack();
|
||||
document.getElementById("forward-btn").disabled = !canGoForward();
|
||||
|
|
|
@ -54,8 +54,7 @@
|
|||
#define USE_WIN_TITLE_STYLE
|
||||
#endif
|
||||
|
||||
<page onload="init_all();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
#ifdef USE_WIN_TITLE_STYLE
|
||||
title="&prefWindow.titleWin;">
|
||||
|
@ -86,10 +85,58 @@
|
|||
oncommand="cmd_forward()" tooltiptext="&buttonForward.tooltip;"
|
||||
disabled="true"/>
|
||||
</hbox>
|
||||
|
||||
<hbox class="main-content" flex="1">
|
||||
<prefpane flex="1" id="mainPrefPane">
|
||||
#include landing.xul
|
||||
|
||||
<hbox flex="1">
|
||||
|
||||
<!-- category list -->
|
||||
<richlistbox id="categories">
|
||||
<richlistitem id="category-general" class="category" align="center"
|
||||
value="paneGeneral" tooltiptext="&paneGeneral.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneGeneral.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-content" class="category" align="center"
|
||||
value="paneContent" tooltiptext="&paneContent.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneContent.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-application" class="category" align="center"
|
||||
value="paneApplications" tooltiptext="&paneApplications.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneApplications.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-privacy" class="category" align="center"
|
||||
value="panePrivacy" tooltiptext="&panePrivacy.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&panePrivacy.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-security" class="category" align="center"
|
||||
value="paneSecurity" tooltiptext="&paneSecurity.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneSecurity.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<richlistitem id="category-sync" class="category" align="center"
|
||||
value="paneSync" tooltiptext="&paneSync.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneSync.title;"/>
|
||||
</richlistitem>
|
||||
#endif
|
||||
|
||||
<richlistitem id="category-advanced" class="category" align="center"
|
||||
value="paneAdvanced" tooltiptext="&paneAdvanced.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneAdvanced.title;"/>
|
||||
</richlistitem>
|
||||
</richlistbox>
|
||||
|
||||
<box class="main-content" flex="1">
|
||||
<prefpane flex="1" id="mainPrefPane">
|
||||
#include main.xul
|
||||
#include privacy.xul
|
||||
#include advanced.xul
|
||||
|
@ -99,7 +146,8 @@
|
|||
#ifdef MOZ_SERVICES_SYNC
|
||||
#include sync.xul
|
||||
#endif
|
||||
</prefpane>
|
||||
</prefpane>
|
||||
</box>
|
||||
|
||||
</hbox>
|
||||
|
||||
</page>
|
||||
|
|
|
@ -65,11 +65,6 @@
|
|||
|
||||
</preferences>
|
||||
|
||||
<hbox class="heading" data-category="panePrivacy" hidden="true">
|
||||
<image class="preference-icon" type="privacy"/>
|
||||
<html:h1>&panePrivacy.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<!-- Tracking -->
|
||||
<groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" align="start">
|
||||
<caption label="&tracking.label;"/>
|
||||
|
|
|
@ -30,13 +30,10 @@
|
|||
|
||||
</preferences>
|
||||
|
||||
<hbox class="heading" data-category="paneSecurity" hidden="true">
|
||||
<image class="preference-icon" type="security"/>
|
||||
<html:h1>&paneSecurity.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<!-- addons, forgery (phishing) UI -->
|
||||
<groupbox id="addonsPhishingGroup" data-category="paneSecurity" hidden="true">
|
||||
<caption label="&general.label;"/>
|
||||
|
||||
<hbox id="addonInstallBox">
|
||||
<checkbox id="warnAddonInstall" flex="1"
|
||||
label="&warnAddonInstall.label;"
|
||||
|
|
|
@ -28,11 +28,6 @@
|
|||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/utils.js"/>
|
||||
|
||||
<hbox class="heading" data-category="paneSync" hidden="true">
|
||||
<image class="preference-icon" type="sync"/>
|
||||
<html:h1>&paneSync.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
|
||||
<vbox id="noAccount" align="center">
|
||||
<spacer flex="1"/>
|
||||
|
|
|
@ -149,7 +149,7 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
|
|||
try {
|
||||
indexURL = Services.io.newURI(path, null, Services.io.newURI(origin, null, null)).spec;
|
||||
} catch(e) {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [origin + path], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1));
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
@ -158,25 +158,25 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
|
|||
try {
|
||||
req.open("HEAD", indexURL, true);
|
||||
} catch(e) {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [indexURL], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
|
||||
req.onload = () => {
|
||||
if (req.status >= 400)
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPathBadHttpCode", [indexURL, req.status], 2));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
|
||||
deferred.resolve();
|
||||
};
|
||||
req.onerror = () => {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [indexURL], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
try {
|
||||
req.send(null);
|
||||
} catch(e) {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [indexURL], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
let validator = createHosted("wrong-launch-path");
|
||||
validator.validate().then(() => {
|
||||
is(validator.errors.length, 1, "app with non-existant launch path got an error");
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.invalidLaunchPathBadHttpCode", [origin + "wrong-path.html", 404], 2),
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [origin + "wrong-path.html", 404], 2),
|
||||
"with the right error message");
|
||||
is(validator.warnings.length, 0, "but no warning");
|
||||
next();
|
||||
|
@ -112,7 +112,7 @@
|
|||
let file = nsFile(validator.project.location);
|
||||
file.append("wrong-path.html");
|
||||
let url = Services.io.newFileURI(file);
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.invalidLaunchPath", [url.spec], 1),
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1),
|
||||
"with the expected message");
|
||||
is(validator.warnings.length, 0, "but no warning");
|
||||
|
||||
|
|
|
@ -358,6 +358,9 @@ InspectorPanel.prototype = {
|
|||
|
||||
this._initMarkup();
|
||||
this.once("markuploaded", () => {
|
||||
if (this._destroyPromise) {
|
||||
return;
|
||||
}
|
||||
this.markup.expandNode(this.selection.nodeFront);
|
||||
this.setupSearchBox();
|
||||
this.emit("new-root");
|
||||
|
|
|
@ -14,6 +14,7 @@ const COLLAPSE_ATTRIBUTE_LENGTH = 120;
|
|||
const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
|
||||
const COLLAPSE_DATA_URL_LENGTH = 60;
|
||||
const CONTAINER_FLASHING_DURATION = 500;
|
||||
const IMAGE_PREVIEW_MAX_DIM = 400;
|
||||
|
||||
const {UndoStack} = require("devtools/shared/undo");
|
||||
const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
|
||||
|
@ -99,6 +100,10 @@ function MarkupView(aInspector, aFrame, aControllerWindow) {
|
|||
gDevTools.on("pref-changed", this._handlePrefChange);
|
||||
|
||||
this._initPreview();
|
||||
|
||||
this.tooltip = new Tooltip(this._inspector.panelDoc);
|
||||
this.tooltip.startTogglingOnHover(this._elt,
|
||||
this._buildTooltipContent.bind(this));
|
||||
}
|
||||
|
||||
exports.MarkupView = MarkupView;
|
||||
|
@ -148,6 +153,25 @@ MarkupView.prototype = {
|
|||
updateChildren(documentElement);
|
||||
},
|
||||
|
||||
_buildTooltipContent: function(target) {
|
||||
// From the target passed here, let's find the parent MarkupContainer
|
||||
// and ask it if the tooltip should be shown
|
||||
let parent = target, container;
|
||||
while (parent !== this.doc.body) {
|
||||
if (parent.container) {
|
||||
container = parent.container;
|
||||
break;
|
||||
}
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
if (container) {
|
||||
// With the newly found container, delegate the tooltip content creation
|
||||
// and decision to show or not the tooltip
|
||||
return container._buildTooltipContent(target, this.tooltip);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlight the inspector selected node.
|
||||
*/
|
||||
|
@ -954,6 +978,9 @@ MarkupView.prototype = {
|
|||
container.destroy();
|
||||
}
|
||||
delete this._containers;
|
||||
|
||||
this.tooltip.destroy();
|
||||
delete this.tooltip;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1099,8 +1126,8 @@ function MarkupContainer(aMarkupView, aNode, aInspector) {
|
|||
this._onMouseDown = this._onMouseDown.bind(this);
|
||||
this.elt.addEventListener("mousedown", this._onMouseDown, false);
|
||||
|
||||
this.tooltip = null;
|
||||
this._attachTooltipIfNeeded();
|
||||
// Prepare the image preview tooltip data if any
|
||||
this._prepareImagePreview();
|
||||
}
|
||||
|
||||
MarkupContainer.prototype = {
|
||||
|
@ -1108,36 +1135,43 @@ MarkupContainer.prototype = {
|
|||
return "[MarkupContainer for " + this.node + "]";
|
||||
},
|
||||
|
||||
_attachTooltipIfNeeded: function() {
|
||||
_prepareImagePreview: function() {
|
||||
if (this.node.tagName) {
|
||||
let tagName = this.node.tagName.toLowerCase();
|
||||
let isImage = tagName === "img" &&
|
||||
this.editor.getAttributeElement("src");
|
||||
let isCanvas = tagName && tagName === "canvas";
|
||||
let srcAttr = this.editor.getAttributeElement("src");
|
||||
let isImage = tagName === "img" && srcAttr;
|
||||
let isCanvas = tagName === "canvas";
|
||||
|
||||
// Get the image data for later so that when the user actually hovers over
|
||||
// the element, the tooltip does contain the image
|
||||
if (isImage || isCanvas) {
|
||||
this.tooltip = new Tooltip(this._inspector.panelDoc);
|
||||
let def = promise.defer();
|
||||
|
||||
this.node.getImageData().then(data => {
|
||||
this.tooltipData = {
|
||||
target: isImage ? srcAttr : this.editor.tag,
|
||||
data: def.promise
|
||||
};
|
||||
|
||||
this.node.getImageData(IMAGE_PREVIEW_MAX_DIM).then(data => {
|
||||
if (data) {
|
||||
data.string().then(str => {
|
||||
this.tooltip.setImageContent(str);
|
||||
data.data.string().then(str => {
|
||||
// Resolving the data promise and, to always keep tooltipData.data
|
||||
// as a promise, create a new one that resolves immediately
|
||||
def.resolve(str, data.size);
|
||||
this.tooltipData.data = promise.resolve(str, data.size);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// If it's an image, show the tooltip on the src attribute
|
||||
if (isImage) {
|
||||
this.tooltip.startTogglingOnHover(this.editor.getAttributeElement("src"));
|
||||
}
|
||||
|
||||
// If it's a canvas, show it on the tag
|
||||
if (isCanvas) {
|
||||
this.tooltip.startTogglingOnHover(this.editor.tag);
|
||||
}
|
||||
_buildTooltipContent: function(target, tooltip) {
|
||||
if (this.tooltipData && target === this.tooltipData.target) {
|
||||
this.tooltipData.data.then((data, size) => {
|
||||
tooltip.setImageContent(data, size);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1375,12 +1409,6 @@ MarkupContainer.prototype = {
|
|||
|
||||
// Destroy my editor
|
||||
this.editor.destroy();
|
||||
|
||||
// Destroy the tooltip if any
|
||||
if (this.tooltip) {
|
||||
this.tooltip.destroy();
|
||||
this.tooltip = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -88,14 +88,14 @@ function testImageTooltip(index) {
|
|||
target = container.editor.getAttributeElement("src");
|
||||
}
|
||||
|
||||
assertTooltipShownOn(container.tooltip, target, () => {
|
||||
let images = container.tooltip.panel.getElementsByTagName("image");
|
||||
assertTooltipShownOn(target, () => {
|
||||
let images = markup.tooltip.panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image");
|
||||
if (isImg) {
|
||||
compareImageData(node, images[0].src);
|
||||
}
|
||||
|
||||
container.tooltip.hide();
|
||||
markup.tooltip.hide();
|
||||
|
||||
testImageTooltip(index + 1);
|
||||
});
|
||||
|
@ -115,17 +115,17 @@ function compareImageData(img, imgData) {
|
|||
is(data, imgData, "Tooltip image has the right content");
|
||||
}
|
||||
|
||||
function assertTooltipShownOn(tooltip, element, cb) {
|
||||
function assertTooltipShownOn(element, cb) {
|
||||
// If there is indeed a show-on-hover on element, the xul panel will be shown
|
||||
tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
markup.tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
markup.tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
|
||||
// Poll until the image gets loaded in the tooltip. This is required because
|
||||
// markup containers only load images in their associated tooltips when
|
||||
// the image data comes back from the server. However, this test is executed
|
||||
// synchronously as soon as "inspector-updated" is fired, which is before
|
||||
// the data for images is known.
|
||||
let hasImage = () => tooltip.panel.getElementsByTagName("image").length;
|
||||
let hasImage = () => markup.tooltip.panel.getElementsByTagName("image").length;
|
||||
let poll = setInterval(() => {
|
||||
if (hasImage()) {
|
||||
clearInterval(poll);
|
||||
|
@ -133,5 +133,5 @@ function assertTooltipShownOn(tooltip, element, cb) {
|
|||
}
|
||||
}, 200);
|
||||
}, true);
|
||||
tooltip._showOnHover(element);
|
||||
markup.tooltip._showOnHover(element);
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ var Scratchpad = {
|
|||
{
|
||||
this._dirty = aValue;
|
||||
if (!aValue && this.editor)
|
||||
this.editor.markClean();
|
||||
this.editor.setClean();
|
||||
this._updateTitle();
|
||||
},
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
|||
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||
const promise = require("sdk/core/promise");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
|
||||
// The panel's window global is an EventEmitter firing the following events:
|
||||
|
@ -31,12 +32,14 @@ const EVENTS = {
|
|||
};
|
||||
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/shadereditor.properties"
|
||||
const HIGHLIGHT_COLOR = [1, 0, 0, 1];
|
||||
const BLACKBOX_COLOR = [0, 0, 0, 0];
|
||||
const TYPING_MAX_DELAY = 500;
|
||||
const HIGHLIGHT_COLOR = [1, 0, 0, 1]; // rgba
|
||||
const TYPING_MAX_DELAY = 500; // ms
|
||||
const SHADERS_AUTOGROW_ITEMS = 4;
|
||||
const GUTTER_ERROR_PANEL_OFFSET_X = 7; // px
|
||||
const GUTTER_ERROR_PANEL_DELAY = 100; // ms
|
||||
const DEFAULT_EDITOR_CONFIG = {
|
||||
mode: Editor.modes.text,
|
||||
gutters: ["errors"],
|
||||
lineNumbers: true,
|
||||
showAnnotationRuler: true
|
||||
};
|
||||
|
@ -174,25 +177,25 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
|||
showItemCheckboxes: true
|
||||
});
|
||||
|
||||
this._onShaderSelect = this._onShaderSelect.bind(this);
|
||||
this._onShaderCheck = this._onShaderCheck.bind(this);
|
||||
this._onShaderMouseEnter = this._onShaderMouseEnter.bind(this);
|
||||
this._onShaderMouseLeave = this._onShaderMouseLeave.bind(this);
|
||||
this._onProgramSelect = this._onProgramSelect.bind(this);
|
||||
this._onProgramCheck = this._onProgramCheck.bind(this);
|
||||
this._onProgramMouseEnter = this._onProgramMouseEnter.bind(this);
|
||||
this._onProgramMouseLeave = this._onProgramMouseLeave.bind(this);
|
||||
|
||||
this.widget.addEventListener("select", this._onShaderSelect, false);
|
||||
this.widget.addEventListener("check", this._onShaderCheck, false);
|
||||
this.widget.addEventListener("mouseenter", this._onShaderMouseEnter, true);
|
||||
this.widget.addEventListener("mouseleave", this._onShaderMouseLeave, true);
|
||||
this.widget.addEventListener("select", this._onProgramSelect, false);
|
||||
this.widget.addEventListener("check", this._onProgramCheck, false);
|
||||
this.widget.addEventListener("mouseenter", this._onProgramMouseEnter, true);
|
||||
this.widget.addEventListener("mouseleave", this._onProgramMouseLeave, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destruction function, called when the tool is closed.
|
||||
*/
|
||||
destroy: function() {
|
||||
this.widget.removeEventListener("select", this._onShaderSelect, false);
|
||||
this.widget.removeEventListener("check", this._onShaderCheck, false);
|
||||
this.widget.removeEventListener("mouseenter", this._onShaderMouseEnter, true);
|
||||
this.widget.removeEventListener("mouseleave", this._onShaderMouseLeave, true);
|
||||
this.widget.removeEventListener("select", this._onProgramSelect, false);
|
||||
this.widget.removeEventListener("check", this._onProgramCheck, false);
|
||||
this.widget.removeEventListener("mouseenter", this._onProgramMouseEnter, true);
|
||||
this.widget.removeEventListener("mouseleave", this._onProgramMouseLeave, true);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -248,9 +251,9 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
|||
},
|
||||
|
||||
/**
|
||||
* The select listener for the sources container.
|
||||
* The select listener for the programs container.
|
||||
*/
|
||||
_onShaderSelect: function({ detail: sourceItem }) {
|
||||
_onProgramSelect: function({ detail: sourceItem }) {
|
||||
if (!sourceItem) {
|
||||
return;
|
||||
}
|
||||
|
@ -280,19 +283,19 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
|||
},
|
||||
|
||||
/**
|
||||
* The check listener for the sources container.
|
||||
* The check listener for the programs container.
|
||||
*/
|
||||
_onShaderCheck: function({ detail: { checked }, target }) {
|
||||
_onProgramCheck: function({ detail: { checked }, target }) {
|
||||
let sourceItem = this.getItemForElement(target);
|
||||
let attachment = sourceItem.attachment;
|
||||
attachment.isBlackBoxed = !checked;
|
||||
attachment.programActor[checked ? "unhighlight" : "highlight"](BLACKBOX_COLOR);
|
||||
attachment.programActor[checked ? "unblackbox" : "blackbox"]();
|
||||
},
|
||||
|
||||
/**
|
||||
* The mouseenter listener for the sources container.
|
||||
* The mouseenter listener for the programs container.
|
||||
*/
|
||||
_onShaderMouseEnter: function(e) {
|
||||
_onProgramMouseEnter: function(e) {
|
||||
let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
|
||||
if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
|
||||
sourceItem.attachment.programActor.highlight(HIGHLIGHT_COLOR);
|
||||
|
@ -305,9 +308,9 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
|||
},
|
||||
|
||||
/**
|
||||
* The mouseleave listener for the sources container.
|
||||
* The mouseleave listener for the programs container.
|
||||
*/
|
||||
_onShaderMouseLeave: function(e) {
|
||||
_onProgramMouseLeave: function(e) {
|
||||
let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
|
||||
if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
|
||||
sourceItem.attachment.programActor.unhighlight();
|
||||
|
@ -427,6 +430,9 @@ let ShadersEditorsView = {
|
|||
*/
|
||||
_onChanged: function(type) {
|
||||
setNamedTimeout("gl-typed", TYPING_MAX_DELAY, () => this._doCompile(type));
|
||||
|
||||
// Remove all the gutter markers and line classes from the editor.
|
||||
this._cleanEditor(type);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -443,13 +449,117 @@ let ShadersEditorsView = {
|
|||
|
||||
try {
|
||||
yield shaderActor.compile(editor.getText());
|
||||
window.emit(EVENTS.SHADER_COMPILED, null);
|
||||
// TODO: remove error gutter markers, after bug 919709 lands.
|
||||
} catch (error) {
|
||||
window.emit(EVENTS.SHADER_COMPILED, error);
|
||||
// TODO: add error gutter markers, after bug 919709 lands.
|
||||
this._onSuccessfulCompilation();
|
||||
} catch (e) {
|
||||
this._onFailedCompilation(type, editor, e);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Called uppon a successful shader compilation.
|
||||
*/
|
||||
_onSuccessfulCompilation: function() {
|
||||
// Signal that the shader was compiled successfully.
|
||||
window.emit(EVENTS.SHADER_COMPILED, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called uppon an unsuccessful shader compilation.
|
||||
*/
|
||||
_onFailedCompilation: function(type, editor, errors) {
|
||||
let lineCount = editor.lineCount();
|
||||
let currentLine = editor.getCursor().line;
|
||||
let listeners = { mouseenter: this._onMarkerMouseEnter };
|
||||
|
||||
function matchLinesAndMessages(string) {
|
||||
return {
|
||||
// First number that is not equal to 0.
|
||||
lineMatch: string.match(/\d{2,}|[1-9]/),
|
||||
// The string after all the numbers, semicolons and spaces.
|
||||
textMatch: string.match(/[^\s\d:][^\r\n|]*/)
|
||||
};
|
||||
}
|
||||
function discardInvalidMatches(e) {
|
||||
// Discard empty line and text matches.
|
||||
return e.lineMatch && e.textMatch;
|
||||
}
|
||||
function sanitizeValidMatches(e) {
|
||||
return {
|
||||
// Drivers might yield retarded line numbers under some obscure
|
||||
// circumstances. Don't throw the errors away in those cases,
|
||||
// just display them on the currently edited line.
|
||||
line: e.lineMatch[0] > lineCount ? currentLine : e.lineMatch[0] - 1,
|
||||
// Trim whitespace from the beginning and the end of the message,
|
||||
// and replace all other occurences of double spaces to a single space.
|
||||
text: e.textMatch[0].trim().replace(/\s{2,}/g, " ")
|
||||
};
|
||||
}
|
||||
function sortByLine(first, second) {
|
||||
// Sort all the errors ascending by their corresponding line number.
|
||||
return first.line > second.line ? 1 : -1;
|
||||
}
|
||||
function groupSameLineMessages(accumulator, current) {
|
||||
// Group errors corresponding to the same line number to a single object.
|
||||
let previous = accumulator[accumulator.length - 1];
|
||||
if (!previous || previous.line != current.line) {
|
||||
return [...accumulator, {
|
||||
line: current.line,
|
||||
messages: [current.text]
|
||||
}];
|
||||
} else {
|
||||
previous.messages.push(current.text);
|
||||
return accumulator;
|
||||
}
|
||||
}
|
||||
function displayErrors({ line, messages }) {
|
||||
// Add gutter markers and line classes for every error in the source.
|
||||
editor.addMarker(line, "errors", "error");
|
||||
editor.setMarkerListeners(line, "errors", "error", listeners, messages);
|
||||
editor.addLineClass(line, "error-line");
|
||||
}
|
||||
|
||||
(this._errors[type] = errors.link
|
||||
.split("ERROR")
|
||||
.map(matchLinesAndMessages)
|
||||
.filter(discardInvalidMatches)
|
||||
.map(sanitizeValidMatches)
|
||||
.sort(sortByLine)
|
||||
.reduce(groupSameLineMessages, []))
|
||||
.forEach(displayErrors);
|
||||
|
||||
// Signal that the shader wasn't compiled successfully.
|
||||
window.emit(EVENTS.SHADER_COMPILED, errors);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event listener for the 'mouseenter' event on a marker in the editor gutter.
|
||||
*/
|
||||
_onMarkerMouseEnter: function(line, node, messages) {
|
||||
if (node._markerErrorsTooltip) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tooltip = node._markerErrorsTooltip = new Tooltip(document);
|
||||
tooltip.defaultOffsetX = GUTTER_ERROR_PANEL_OFFSET_X;
|
||||
tooltip.setTextContent.apply(tooltip, messages);
|
||||
tooltip.startTogglingOnHover(node, () => true, GUTTER_ERROR_PANEL_DELAY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all the gutter markers and line classes from the editor.
|
||||
*/
|
||||
_cleanEditor: function(type) {
|
||||
this._getEditor(type).then(editor => {
|
||||
editor.removeAllMarkers("errors");
|
||||
this._errors[type].forEach(e => editor.removeLineClass(e.line));
|
||||
this._errors[type].length = 0;
|
||||
});
|
||||
},
|
||||
|
||||
_errors: {
|
||||
vs: [],
|
||||
fs: []
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
doc_multiple-contexts.html
|
||||
doc_overlapping-geometry.html
|
||||
doc_shader-order.html
|
||||
doc_simple-canvas.html
|
||||
head.js
|
||||
|
@ -8,6 +9,8 @@ support-files =
|
|||
[browser_se_aaa_run_first_leaktest.js]
|
||||
[browser_se_bfcache.js]
|
||||
[browser_se_editors-contents.js]
|
||||
[browser_se_editors-error-gutter.js]
|
||||
[browser_se_editors-error-tooltip.js]
|
||||
[browser_se_editors-lazy-init.js]
|
||||
[browser_se_first-run.js]
|
||||
[browser_se_navigation.js]
|
||||
|
@ -34,3 +37,4 @@ support-files =
|
|||
[browser_webgl-actor-test-14.js]
|
||||
[browser_webgl-actor-test-15.js]
|
||||
[browser_webgl-actor-test-16.js]
|
||||
[browser_webgl-actor-test-17.js]
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if error indicators are shown in the editor's gutter and text area
|
||||
* when there's a shader compilation error.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
|
||||
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
||||
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks added in the vertex shader editor.");
|
||||
|
||||
vsEditor.insertText(" ", { line: 1, ch: 0 });
|
||||
is(vsEditor.getText(1), " precision lowp float;", "Typed space.");
|
||||
checkHasVertFirstError(false, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks removed while typing in the vertex shader editor.");
|
||||
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks were re-added after recompiling the vertex shader.");
|
||||
|
||||
fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
|
||||
let fragError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks added in the fragment shader editor.");
|
||||
|
||||
fsEditor.insertText(" ", { line: 1, ch: 0 });
|
||||
is(fsEditor.getText(1), " precision lowp float;", "Typed space.");
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(false, fragError);
|
||||
info("Error marks removed while typing in the fragment shader editor.");
|
||||
|
||||
let fragError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks were re-added after recompiling the fragment shader.");
|
||||
|
||||
vsEditor.replaceText("2", { line: 3, ch: 19 }, { line: 3, ch: 20 });
|
||||
checkHasVertFirstError(false, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks removed while typing in the vertex shader editor again.");
|
||||
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(true, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks were re-added after recompiling the fragment shader again.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
||||
function checkHasVertFirstError(bool, error) {
|
||||
ok(error, "Vertex shader compiled with errors.");
|
||||
isnot(error.link, "", "The linkage status should not be empty.");
|
||||
|
||||
let line = 7;
|
||||
info("Checking first vertex shader error on line " + line + "...");
|
||||
|
||||
is(vsEditor.hasMarker(line, "errors", "error"), bool,
|
||||
"Error is " + (bool ? "" : "not ") + "shown in the editor's gutter.");
|
||||
is(vsEditor.hasLineClass(line, "error-line"), bool,
|
||||
"Error style is " + (bool ? "" : "not ") + "applied to the faulty line.");
|
||||
|
||||
let parsed = ShadersEditorsView._errors.vs;
|
||||
is(parsed.length >= 1, bool,
|
||||
"There's " + (bool ? ">= 1" : "< 1") + " parsed vertex shader error(s).");
|
||||
|
||||
if (bool) {
|
||||
is(parsed[0].line, line,
|
||||
"The correct line was parsed.");
|
||||
is(parsed[0].messages.length, 2,
|
||||
"There are 2 parsed messages.");
|
||||
ok(parsed[0].messages[0].contains("'constructor' : too many arguments"),
|
||||
"The correct first message was parsed.");
|
||||
ok(parsed[0].messages[1].contains("'assign' : cannot convert from"),
|
||||
"The correct second message was parsed.");
|
||||
}
|
||||
}
|
||||
|
||||
function checkHasVertSecondError(bool, error) {
|
||||
ok(error, "Vertex shader compiled with errors.");
|
||||
isnot(error.link, "", "The linkage status should not be empty.");
|
||||
|
||||
let line = 8;
|
||||
info("Checking second vertex shader error on line " + line + "...");
|
||||
|
||||
is(vsEditor.hasMarker(line, "errors", "error"), bool,
|
||||
"Error is " + (bool ? "" : "not ") + "shown in the editor's gutter.");
|
||||
is(vsEditor.hasLineClass(line, "error-line"), bool,
|
||||
"Error style is " + (bool ? "" : "not ") + "applied to the faulty line.");
|
||||
|
||||
let parsed = ShadersEditorsView._errors.vs;
|
||||
is(parsed.length >= 2, bool,
|
||||
"There's " + (bool ? ">= 2" : "< 2") + " parsed vertex shader error(s).");
|
||||
|
||||
if (bool) {
|
||||
is(parsed[1].line, line,
|
||||
"The correct line was parsed.");
|
||||
is(parsed[1].messages.length, 1,
|
||||
"There is 1 parsed message.");
|
||||
ok(parsed[1].messages[0].contains("'assign' : cannot convert from"),
|
||||
"The correct message was parsed.");
|
||||
}
|
||||
}
|
||||
|
||||
function checkHasFragError(bool, error) {
|
||||
ok(error, "Fragment shader compiled with errors.");
|
||||
isnot(error.link, "", "The linkage status should not be empty.");
|
||||
|
||||
let line = 5;
|
||||
info("Checking first vertex shader error on line " + line + "...");
|
||||
|
||||
is(fsEditor.hasMarker(line, "errors", "error"), bool,
|
||||
"Error is " + (bool ? "" : "not ") + "shown in the editor's gutter.");
|
||||
is(fsEditor.hasLineClass(line, "error-line"), bool,
|
||||
"Error style is " + (bool ? "" : "not ") + "applied to the faulty line.");
|
||||
|
||||
let parsed = ShadersEditorsView._errors.fs;
|
||||
is(parsed.length >= 1, bool,
|
||||
"There's " + (bool ? ">= 2" : "< 1") + " parsed fragment shader error(s).");
|
||||
|
||||
if (bool) {
|
||||
is(parsed[0].line, line,
|
||||
"The correct line was parsed.");
|
||||
is(parsed[0].messages.length, 1,
|
||||
"There is 1 parsed message.");
|
||||
ok(parsed[0].messages[0].contains("'constructor' : too many arguments"),
|
||||
"The correct message was parsed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
|
||||
return deferred.promise;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if error tooltips can be opened from the editor's gutter when there's
|
||||
* a shader compilation error.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
|
||||
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
||||
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
|
||||
yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
|
||||
// Synthesizing 'mouseenter' events doesn't work, hack around this by
|
||||
// manually calling the event listener with the expected arguments.
|
||||
let editorDocument = vsEditor.container.contentDocument;
|
||||
let marker = editorDocument.querySelector(".error");
|
||||
let parsed = ShadersEditorsView._errors.vs[0].messages;
|
||||
ShadersEditorsView._onMarkerMouseEnter(7, marker, parsed);
|
||||
|
||||
let tooltip = marker._markerErrorsTooltip;
|
||||
ok(tooltip, "A tooltip was created successfully.");
|
||||
|
||||
let content = tooltip.content;
|
||||
ok(tooltip.content,
|
||||
"Some tooltip's content was set.");
|
||||
is(tooltip.content.className, "devtools-tooltip-simple-text-container",
|
||||
"The tooltip's content container was created correctly.");
|
||||
|
||||
let messages = content.childNodes;
|
||||
is(messages.length, 2,
|
||||
"There are two messages displayed in the tooltip.");
|
||||
is(messages[0].className, "devtools-tooltip-simple-text",
|
||||
"The first message was created correctly.");
|
||||
is(messages[1].className, "devtools-tooltip-simple-text",
|
||||
"The second message was created correctly.");
|
||||
|
||||
ok(messages[0].textContent.contains("'constructor' : too many arguments"),
|
||||
"The first message contains the correct text.");
|
||||
ok(messages[1].textContent.contains("'assign' : cannot convert"),
|
||||
"The second message contains the correct text.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
|
||||
return deferred.promise;
|
||||
}
|
|
@ -55,9 +55,9 @@ function ifWebGLSupported() {
|
|||
is(getBlackBoxCheckbox(panel, 1).checked, true,
|
||||
"The second blackbox checkbox should still be checked.");
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The first program was correctly blackboxed.");
|
||||
|
||||
|
@ -72,35 +72,35 @@ function ifWebGLSupported() {
|
|||
is(getBlackBoxCheckbox(panel, 1).checked, false,
|
||||
"The second blackbox checkbox should now be unchecked.");
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The second program was correctly blackboxed.");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "Highlighting didn't work while blackboxed (1).");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "Highlighting didn't work while blackboxed (2).");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "Highlighting didn't work while blackboxed (3).");
|
||||
|
||||
getBlackBoxCheckbox(panel, 0).click();
|
||||
|
@ -121,7 +121,7 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The two programs were correctly unblackboxed.");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
@ -129,8 +129,8 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The first program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
|
@ -138,7 +138,7 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The second program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
|
|
@ -35,7 +35,7 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
@ -43,8 +43,8 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The first program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
|
@ -52,7 +52,7 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The second program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
@ -60,7 +60,7 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The two programs were correctly unhighlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
@ -68,7 +68,7 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The two programs were left unchanged after hovering a blackbox checkbox.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if editing a vertex and a fragment shader works properly.
|
||||
* Tests if editing a vertex and a fragment shader would permanently store
|
||||
* their new source on the backend and reshow it in the frontend when required.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the highlight/unhighlight operations on program actors
|
||||
* work as expected.
|
||||
* Tests that the highlight/unhighlight and blackbox/unblackbox operations on
|
||||
* program actors work as expected.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
|
@ -17,19 +17,31 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are correct before highlighting.");
|
||||
ok(true, "The top left pixel color was correct before highlighting.");
|
||||
ok(true, "The corner pixel colors are correct before highlighting.");
|
||||
|
||||
yield programActor.highlight([0, 0, 1, 1]);
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are preserved after highlighting.");
|
||||
ok(true, "The top left pixel color is correct after highlighting.");
|
||||
ok(true, "The corner pixel colors are correct after highlighting.");
|
||||
|
||||
yield programActor.unhighlight();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are correct after unhighlighting.");
|
||||
ok(true, "The top left pixel color is correct after unhighlighting.");
|
||||
ok(true, "The corner pixel colors are correct after unhighlighting.");
|
||||
|
||||
yield programActor.blackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are preserved after blackboxing.");
|
||||
ok(true, "The corner pixel colors are correct after blackboxing.");
|
||||
|
||||
yield programActor.unblackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are correct after unblackboxing.");
|
||||
ok(true, "The corner pixel colors are correct after unblackboxing.");
|
||||
|
||||
function checkShaderSource(aMessage) {
|
||||
return Task.spawn(function() {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the blackbox/unblackbox operations work as expected with
|
||||
* overlapping geometry.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, front] = yield initBackend(OVERLAPPING_GEOMETRY_CANVAS_URL);
|
||||
front.setup({ reload: true });
|
||||
|
||||
let firstProgramActor = yield once(front, "program-linked");
|
||||
let secondProgramActor = yield once(front, "program-linked");
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct before blackboxing.");
|
||||
|
||||
yield firstProgramActor.blackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after blackboxing (1).");
|
||||
|
||||
yield firstProgramActor.unblackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after unblackboxing (1).");
|
||||
|
||||
yield secondProgramActor.blackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after blackboxing (2).");
|
||||
|
||||
yield secondProgramActor.unblackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after unblackboxing (2).");
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>WebGL editor test page</title>
|
||||
|
||||
<script id="shader-vs" type="x-shader/x-vertex">
|
||||
precision lowp float;
|
||||
attribute vec3 aVertexPosition;
|
||||
uniform float uDepth;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = vec4(aVertexPosition, uDepth);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="shader-fs-0" type="x-shader/x-fragment">
|
||||
precision lowp float;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="shader-fs-1" type="x-shader/x-fragment">
|
||||
precision lowp float;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="canvas" width="128" height="128"></canvas>
|
||||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
let canvas, gl;
|
||||
let program = [];
|
||||
let squareVerticesPositionBuffer;
|
||||
let vertexPositionAttribute = [];
|
||||
let depthUniform = [];
|
||||
|
||||
window.onload = function() {
|
||||
canvas = document.querySelector("canvas");
|
||||
gl = canvas.getContext("webgl");
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
initProgram(0);
|
||||
initProgram(1);
|
||||
initBuffers();
|
||||
drawScene();
|
||||
}
|
||||
|
||||
function initProgram(i) {
|
||||
let vertexShader = getShader("shader-vs");
|
||||
let fragmentShader = getShader("shader-fs-" + i);
|
||||
|
||||
program[i] = gl.createProgram();
|
||||
gl.attachShader(program[i], vertexShader);
|
||||
gl.attachShader(program[i], fragmentShader);
|
||||
gl.linkProgram(program[i]);
|
||||
|
||||
vertexPositionAttribute[i] = gl.getAttribLocation(program[i], "aVertexPosition");
|
||||
gl.enableVertexAttribArray(vertexPositionAttribute[i]);
|
||||
|
||||
depthUniform[i] = gl.getUniformLocation(program[i], "uDepth");
|
||||
}
|
||||
|
||||
function getShader(id) {
|
||||
let script = document.getElementById(id);
|
||||
let source = script.textContent;
|
||||
let shader;
|
||||
|
||||
if (script.type == "x-shader/x-fragment") {
|
||||
shader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
} else if (script.type == "x-shader/x-vertex") {
|
||||
shader = gl.createShader(gl.VERTEX_SHADER);
|
||||
}
|
||||
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
function initBuffers() {
|
||||
squareVerticesPositionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
||||
1.0, 1.0, 0.0,
|
||||
-1.0, 1.0, 0.0,
|
||||
1.0, -1.0, 0.0,
|
||||
-1.0, -1.0, 0.0
|
||||
]), gl.STATIC_DRAW);
|
||||
}
|
||||
|
||||
function drawScene() {
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
|
||||
gl.vertexAttribPointer(vertexPositionAttribute[i], 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.useProgram(program[i]);
|
||||
gl.uniform1f(depthUniform[i], i + 1);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(drawScene);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -27,6 +27,7 @@ const EXAMPLE_URL = "http://example.com/browser/browser/devtools/shadereditor/te
|
|||
const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
|
||||
const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html";
|
||||
const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html";
|
||||
const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html";
|
||||
|
||||
// All tests are asynchronous.
|
||||
waitForExplicitFinish();
|
||||
|
|
|
@ -95,6 +95,10 @@ function Tooltip(doc) {
|
|||
module.exports.Tooltip = Tooltip;
|
||||
|
||||
Tooltip.prototype = {
|
||||
defaultPosition: "before_start",
|
||||
defaultOffsetX: 0,
|
||||
defaultOffsetY: 0,
|
||||
|
||||
/**
|
||||
* Show the tooltip. It might be wise to append some content first if you
|
||||
* don't want the tooltip to be empty. You may access the content of the
|
||||
|
@ -105,9 +109,12 @@ Tooltip.prototype = {
|
|||
* https://developer.mozilla.org/en-US/docs/XUL/PopupGuide/Positioning
|
||||
* Defaults to before_start
|
||||
*/
|
||||
show: function(anchor, position="before_start") {
|
||||
show: function(anchor,
|
||||
position = this.defaultPosition,
|
||||
x = this.defaultOffsetX,
|
||||
y = this.defaultOffsetY) {
|
||||
this.panel.hidden = false;
|
||||
this.panel.openPopup(anchor, position);
|
||||
this.panel.openPopup(anchor, position, x, y);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -258,11 +265,44 @@ Tooltip.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Fill the tooltip with an image, displayed over a tiled background useful
|
||||
* for transparent images.
|
||||
* Also adds the image dimension as a label at the bottom.
|
||||
* Sets some text as the content of this tooltip.
|
||||
*
|
||||
* @param string[] messages
|
||||
* A list of text messages.
|
||||
*/
|
||||
setImageContent: function(imageUrl, maxDim=400) {
|
||||
setTextContent: function(...messages) {
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.className = "devtools-tooltip-simple-text-container";
|
||||
vbox.setAttribute("flex", "1");
|
||||
|
||||
for (let text of messages) {
|
||||
let description = this.doc.createElement("description");
|
||||
description.setAttribute("flex", "1");
|
||||
description.className = "devtools-tooltip-simple-text";
|
||||
description.textContent = text;
|
||||
vbox.appendChild(description);
|
||||
}
|
||||
|
||||
this.content = vbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the tooltip with an image, displayed over a tiled background useful
|
||||
* for transparent images. Also adds the image dimension as a label at the
|
||||
* bottom.
|
||||
* @param {string} imageUrl
|
||||
* The url to load the image from
|
||||
* @param {Object} options
|
||||
* The following options are supported:
|
||||
* - resized : whether or not the image identified by imageUrl has been
|
||||
* resized before this function was called.
|
||||
* - naturalWidth/naturalHeight : the original size of the image before
|
||||
* it was resized, if if was resized before this function was called.
|
||||
* If not provided, will be measured on the loaded image.
|
||||
* - maxDim : if the image should be resized before being shown, pass
|
||||
* a number here
|
||||
*/
|
||||
setImageContent: function(imageUrl, options={}) {
|
||||
// Main container
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.setAttribute("align", "center")
|
||||
|
@ -279,9 +319,9 @@ Tooltip.prototype = {
|
|||
// Display the image
|
||||
let image = this.doc.createElement("image");
|
||||
image.setAttribute("src", imageUrl);
|
||||
if (maxDim) {
|
||||
image.style.maxWidth = maxDim + "px";
|
||||
image.style.maxHeight = maxDim + "px";
|
||||
if (options.maxDim) {
|
||||
image.style.maxWidth = options.maxDim + "px";
|
||||
image.style.maxHeight = options.maxDim + "px";
|
||||
}
|
||||
tiles.appendChild(image);
|
||||
|
||||
|
@ -294,11 +334,9 @@ Tooltip.prototype = {
|
|||
imgObj.onload = null;
|
||||
|
||||
// Display dimensions
|
||||
label.textContent = imgObj.naturalWidth + " x " + imgObj.naturalHeight;
|
||||
if (imgObj.naturalWidth > maxDim ||
|
||||
imgObj.naturalHeight > maxDim) {
|
||||
label.textContent += " *";
|
||||
}
|
||||
let w = options.naturalWidth || imgObj.naturalWidth;
|
||||
let h = options.naturalHeight || imgObj.naturalHeight;
|
||||
label.textContent = w + " x " + h;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -309,7 +347,9 @@ Tooltip.prototype = {
|
|||
setCssBackgroundImageContent: function(cssBackground, sheetHref, maxDim=400) {
|
||||
let uri = getBackgroundImageUri(cssBackground, sheetHref);
|
||||
if (uri) {
|
||||
this.setImageContent(uri, maxDim);
|
||||
this.setImageContent(uri, {
|
||||
maxDim: maxDim
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
/* 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/. */
|
||||
|
||||
.errors,
|
||||
.breakpoints {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.breakpoint, .debugLocation, .breakpoint-debugLocation {
|
||||
.error, .breakpoint, .debugLocation, .breakpoint-debugLocation {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 12px;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-image: url("chrome://browser/skin/devtools/orion-error.png");
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.breakpoint {
|
||||
|
@ -23,4 +33,8 @@
|
|||
.breakpoint.debugLocation {
|
||||
background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
|
||||
url("chrome://browser/skin/devtools/orion-breakpoint.png");
|
||||
}
|
||||
}
|
||||
|
||||
.error-line {
|
||||
background: rgba(255,0,0,0.2);
|
||||
}
|
||||
|
|
|
@ -6,38 +6,6 @@
|
|||
|
||||
const dbginfo = new WeakMap();
|
||||
|
||||
// Private functions
|
||||
|
||||
/**
|
||||
* Adds a marker to the breakpoints gutter.
|
||||
* Type should be either a 'breakpoint' or a 'debugLocation'.
|
||||
*/
|
||||
function addMarker(cm, line, type) {
|
||||
let info = cm.lineInfo(line);
|
||||
|
||||
if (info.gutterMarkers)
|
||||
return void info.gutterMarkers.breakpoints.classList.add(type);
|
||||
|
||||
let mark = cm.getWrapperElement().ownerDocument.createElement("div");
|
||||
mark.className = type;
|
||||
mark.innerHTML = "";
|
||||
|
||||
cm.setGutterMarker(info.line, "breakpoints", mark);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a marker from the breakpoints gutter.
|
||||
* Type should be either a 'breakpoint' or a 'debugLocation'.
|
||||
*/
|
||||
function removeMarker(cm, line, type) {
|
||||
let info = cm.lineInfo(line);
|
||||
|
||||
if (!info || !info.gutterMarkers)
|
||||
return;
|
||||
|
||||
info.gutterMarkers.breakpoints.classList.remove(type);
|
||||
}
|
||||
|
||||
// These functions implement search within the debugger. Since
|
||||
// search in the debugger is different from other components,
|
||||
// we can't use search.js CodeMirror addon. This is a slightly
|
||||
|
@ -155,7 +123,7 @@ function addBreakpoint(ctx, line, cond) {
|
|||
let meta = dbginfo.get(ed);
|
||||
let info = cm.lineInfo(line);
|
||||
|
||||
addMarker(cm, line, "breakpoint");
|
||||
ed.addMarker(line, "breakpoints", "breakpoint");
|
||||
meta.breakpoints[line] = { condition: cond };
|
||||
|
||||
info.handle.on("delete", function onDelete() {
|
||||
|
@ -179,7 +147,7 @@ function removeBreakpoint(ctx, line) {
|
|||
let info = cm.lineInfo(line);
|
||||
|
||||
meta.breakpoints[info.line] = null;
|
||||
removeMarker(cm, info.line, "breakpoint");
|
||||
ed.removeMarker(info.line, "breakpoints", "breakpoint");
|
||||
ed.emit("breakpointRemoved", line);
|
||||
}
|
||||
|
||||
|
@ -203,11 +171,11 @@ function getBreakpoints(ctx) {
|
|||
* display the line on which the Debugger is currently paused.
|
||||
*/
|
||||
function setDebugLocation(ctx, line) {
|
||||
let { ed, cm } = ctx;
|
||||
let { ed } = ctx;
|
||||
let meta = dbginfo.get(ed);
|
||||
|
||||
meta.debugLocation = line;
|
||||
addMarker(cm, line, "debugLocation");
|
||||
ed.addMarker(line, "breakpoints", "debugLocation");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -226,11 +194,11 @@ function getDebugLocation(ctx) {
|
|||
* also removes a visual anchor from the breakpoints gutter.
|
||||
*/
|
||||
function clearDebugLocation(ctx) {
|
||||
let { ed, cm } = ctx;
|
||||
let { ed } = ctx;
|
||||
let meta = dbginfo.get(ed);
|
||||
|
||||
if (meta.debugLocation != null) {
|
||||
removeMarker(cm, meta.debugLocation, "debugLocation");
|
||||
ed.removeMarker(meta.debugLocation, "breakpoints", "debugLocation");
|
||||
meta.debugLocation = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -340,7 +340,7 @@ Editor.prototype = {
|
|||
* Replaces contents of a text area within the from/to {line, ch}
|
||||
* range. If neither from nor to arguments are provided works
|
||||
* exactly like setText. If only from object is provided, inserts
|
||||
* text at that point.
|
||||
* text at that point, *overwriting* as many characters as needed.
|
||||
*/
|
||||
replaceText: function (value, from, to) {
|
||||
let cm = editors.get(this);
|
||||
|
@ -356,6 +356,15 @@ Editor.prototype = {
|
|||
cm.replaceRange(value, from, to);
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts text at the specified {line, ch} position, shifting existing
|
||||
* contents as necessary.
|
||||
*/
|
||||
insertText: function (value, at) {
|
||||
let cm = editors.get(this);
|
||||
cm.replaceRange(value, at, at);
|
||||
},
|
||||
|
||||
/**
|
||||
* Deselects contents of the text area.
|
||||
*/
|
||||
|
@ -370,7 +379,7 @@ Editor.prototype = {
|
|||
* Marks the contents as clean and returns the current
|
||||
* version number.
|
||||
*/
|
||||
markClean: function () {
|
||||
setClean: function () {
|
||||
let cm = editors.get(this);
|
||||
this.version = cm.changeGeneration();
|
||||
return this.version;
|
||||
|
@ -519,6 +528,120 @@ Editor.prototype = {
|
|||
this.setFirstVisibleLine(topLine);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether a marker of a specified class exists in a line's gutter.
|
||||
*/
|
||||
hasMarker: function (line, gutterName, markerClass) {
|
||||
let cm = editors.get(this);
|
||||
let info = cm.lineInfo(line);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
let gutterMarkers = info.gutterMarkers;
|
||||
if (!gutterMarkers)
|
||||
return false;
|
||||
|
||||
let marker = gutterMarkers[gutterName];
|
||||
if (!marker)
|
||||
return false;
|
||||
|
||||
return marker.classList.contains(markerClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a marker with a specified class to a line's gutter. If another marker
|
||||
* exists on that line, the new marker class is added to its class list.
|
||||
*/
|
||||
addMarker: function (line, gutterName, markerClass) {
|
||||
let cm = editors.get(this);
|
||||
let info = cm.lineInfo(line);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
let gutterMarkers = info.gutterMarkers;
|
||||
if (gutterMarkers) {
|
||||
let marker = gutterMarkers[gutterName];
|
||||
if (marker) {
|
||||
marker.classList.add(markerClass);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let marker = cm.getWrapperElement().ownerDocument.createElement("div");
|
||||
marker.className = markerClass;
|
||||
cm.setGutterMarker(info.line, gutterName, marker);
|
||||
},
|
||||
|
||||
/**
|
||||
* The reverse of addMarker. Removes a marker of a specified class from a
|
||||
* line's gutter.
|
||||
*/
|
||||
removeMarker: function (line, gutterName, markerClass) {
|
||||
if (!this.hasMarker(line, gutterName, markerClass))
|
||||
return;
|
||||
|
||||
let cm = editors.get(this);
|
||||
cm.lineInfo(line).gutterMarkers[gutterName].classList.remove(markerClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all gutter markers in the gutter with the given name.
|
||||
*/
|
||||
removeAllMarkers: function (gutterName) {
|
||||
let cm = editors.get(this);
|
||||
cm.clearGutter(gutterName);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles attaching a set of events listeners on a marker. They should
|
||||
* be passed as an object literal with keys as event names and values as
|
||||
* function listeners. The line number, marker node and optional data
|
||||
* will be passed as arguments to the function listener.
|
||||
*
|
||||
* You don't need to worry about removing these event listeners.
|
||||
* They're automatically orphaned when clearing markers.
|
||||
*/
|
||||
setMarkerListeners: function(line, gutterName, markerClass, events, data) {
|
||||
if (!this.hasMarker(line, gutterName, markerClass))
|
||||
return;
|
||||
|
||||
let cm = editors.get(this);
|
||||
let marker = cm.lineInfo(line).gutterMarkers[gutterName];
|
||||
|
||||
for (let name in events) {
|
||||
let listener = events[name].bind(this, line, marker, data);
|
||||
marker.addEventListener(name, listener);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether a line is decorated using the specified class name.
|
||||
*/
|
||||
hasLineClass: function (line, className) {
|
||||
let cm = editors.get(this);
|
||||
let info = cm.lineInfo(line);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
return info.wrapClass == className;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a CSS class name for the given line, including the text and gutter.
|
||||
*/
|
||||
addLineClass: function (line, className) {
|
||||
let cm = editors.get(this);
|
||||
cm.addLineClass(line, "wrap", className);
|
||||
},
|
||||
|
||||
/**
|
||||
* The reverse of addLineClass.
|
||||
*/
|
||||
removeLineClass: function (line, className) {
|
||||
let cm = editors.get(this);
|
||||
cm.removeLineClass(line, "wrap", className);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.container = null;
|
||||
this.config = null;
|
||||
|
|
|
@ -376,7 +376,7 @@ StyleSheetEditor.prototype = {
|
|||
if (callback) {
|
||||
callback(returnFile);
|
||||
}
|
||||
this.sourceEditor.markClean();
|
||||
this.sourceEditor.setClean();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ validator.invalidAppType=Unknown app type: '%S'.
|
|||
validator.invalidHostedPriviledges=Hosted App can't be type '%S'.
|
||||
validator.noCertifiedSupport='certified' apps are not fully supported on the App manager.
|
||||
validator.nonAbsoluteLaunchPath=Launch path has to be an absolute path starting with '/': '%S'
|
||||
validator.invalidLaunchPath=Unable to access to app starting document '%S'
|
||||
# LOCALIZATION NOTE (validator.invalidLaunchPathBadHttpCode): %1$S is the URI of
|
||||
validator.accessFailedLaunchPath=Unable to access the app starting document '%S'
|
||||
# LOCALIZATION NOTE (validator.accessFailedLaunchPathBadHttpCode): %1$S is the URI of
|
||||
# the launch document, %2$S is the http error code.
|
||||
validator.invalidLaunchPathBadHttpCode=Unable to access to app starting document '%1$S', got HTTP code %2$S
|
||||
validator.accessFailedLaunchPathBadHttpCode=Unable to access the app starting document '%1$S', got HTTP code %2$S
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
- 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/. -->
|
||||
|
||||
<!ENTITY popups.label "Pop-ups">
|
||||
|
||||
<!ENTITY blockPopups.label "Block pop-up windows">
|
||||
<!ENTITY blockPopups.accesskey "B">
|
||||
<!ENTITY popupExceptions.label "Exceptions…">
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
- 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/. -->
|
||||
|
||||
<!ENTITY general.label "General">
|
||||
|
||||
<!ENTITY warnAddonInstall.label "Warn me when sites try to install add-ons">
|
||||
<!ENTITY warnAddonInstall.accesskey "W">
|
||||
|
||||
|
|
|
@ -255,7 +255,11 @@ this.UITour = {
|
|||
if (uri.schemeIs("chrome"))
|
||||
return true;
|
||||
|
||||
if (!uri.schemeIs("https"))
|
||||
let allowedSchemes = new Set(["https"]);
|
||||
if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
|
||||
allowedSchemes.add("http");
|
||||
|
||||
if (!allowedSchemes.has(uri.scheme))
|
||||
return false;
|
||||
|
||||
this.importPermissions();
|
||||
|
|
|
@ -32,13 +32,12 @@ function is_element_hidden(element, msg) {
|
|||
ok(is_hidden(element), msg);
|
||||
}
|
||||
|
||||
function loadTestPage(callback, untrustedHost = false) {
|
||||
function loadTestPage(callback, host = "https://example.com/") {
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
|
||||
let url = getRootDirectory(gTestPath) + "uitour.html";
|
||||
if (untrustedHost)
|
||||
url = url.replace("chrome://mochitests/content/", "http://example.com/");
|
||||
url = url.replace("chrome://mochitests/content/", host);
|
||||
|
||||
gTestTab = gBrowser.addTab(url);
|
||||
gBrowser.selectedTab = gTestTab;
|
||||
|
@ -55,6 +54,8 @@ function loadTestPage(callback, untrustedHost = false) {
|
|||
|
||||
function test() {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
let testUri = Services.io.newURI("http://example.com", null, null);
|
||||
Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -65,6 +66,7 @@ function test() {
|
|||
gBrowser.removeTab(gTestTab);
|
||||
delete window.gTestTab;
|
||||
Services.prefs.clearUserPref("browser.uitour.enabled", true);
|
||||
Services.perms.remove("example.com", "uitour");
|
||||
});
|
||||
|
||||
function done() {
|
||||
|
@ -98,6 +100,41 @@ function test() {
|
|||
}
|
||||
|
||||
let tests = [
|
||||
function test_untrusted_host(done) {
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown on a untrusted host");
|
||||
|
||||
done();
|
||||
}, "http://mochi.test:8888/");
|
||||
},
|
||||
function test_unsecure_host(done) {
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown on a unsecure host");
|
||||
|
||||
done();
|
||||
}, "http://example.com/");
|
||||
},
|
||||
function test_unsecure_host_override(done) {
|
||||
Services.prefs.setBoolPref("browser.uitour.requireSecure", false);
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_visible(highlight, "Highlight should be shown on a unsecure host when override pref is set");
|
||||
|
||||
Services.prefs.setBoolPref("browser.uitour.requireSecure", true);
|
||||
done();
|
||||
}, "http://example.com/");
|
||||
},
|
||||
function test_disabled(done) {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", false);
|
||||
|
||||
|
@ -110,17 +147,6 @@ let tests = [
|
|||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
done();
|
||||
},
|
||||
function test_untrusted_host(done) {
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown on a untrusted domain");
|
||||
|
||||
done();
|
||||
}, true);
|
||||
},
|
||||
function test_highlight(done) {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.7 KiB |
|
@ -105,7 +105,7 @@ browser.jar:
|
|||
skin/classic/browser/preferences/Options-sync.png (preferences/Options-sync.png)
|
||||
#endif
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
|
@ -143,6 +143,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
skin/classic/browser/devtools/breadcrumbs-scrollbutton.png (devtools/breadcrumbs-scrollbutton.png)
|
||||
|
|
|
@ -21,12 +21,12 @@
|
|||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
richlistitem label {
|
||||
#handlersView > richlistitem label {
|
||||
-moz-margin-start: 1px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,95 +6,115 @@
|
|||
|
||||
@namespace html "http://www.w3.org/1999/xhtml";
|
||||
|
||||
#preferences-home {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.landingButton {
|
||||
-moz-box-align: center;
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
.landingButton:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.landingButton-label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.landingButton-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.preference-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.preference-icon[type="general"],
|
||||
.landingButton-icon[type="general"] {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="content"],
|
||||
.landingButton-icon[type="content"] {
|
||||
background-position: -64px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="applications"],
|
||||
.landingButton-icon[type="applications"] {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="privacy"],
|
||||
.landingButton-icon[type="privacy"] {
|
||||
background-position: -128px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="security"],
|
||||
.landingButton-icon[type="security"] {
|
||||
background-position: -160px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="advanced"],
|
||||
.landingButton-icon[type="advanced"] {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="sync"],
|
||||
.landingButton-icon[type="sync"] {
|
||||
background-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 20px;
|
||||
font-size: 1.667rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 50px;
|
||||
background-color: rgba(192,199,210,0.7);
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-bottom: 15px;
|
||||
-moz-box-align: center;
|
||||
.main-content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
prefpane > .content-box {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
#categories {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
-moz-margin-end: -1px;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-appearance: none;
|
||||
border-width: 1px;
|
||||
-moz-border-end-width: 0;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
padding: 9px 4px 10px;
|
||||
-moz-padding-end: 8px;
|
||||
-moz-box-align: center;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
color: WindowText;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.category[selected] {
|
||||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
border-color: ThreeDShadow;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 1.5rem;
|
||||
-moz-padding-end: 24px;
|
||||
}
|
||||
|
||||
/* Maximize the size of the viewport when the window is small */
|
||||
@media (max-width: 800px) {
|
||||
.category-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0 6px;
|
||||
-moz-margin-start: 6px;
|
||||
-moz-margin-end: 5px;
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
}
|
||||
|
||||
#category-general > .category-icon {
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
|
||||
#category-content > .category-icon {
|
||||
-moz-image-region: rect(0, 96px, 32px, 64px)
|
||||
}
|
||||
|
||||
#category-application > .category-icon {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px)
|
||||
}
|
||||
|
||||
#category-privacy > .category-icon {
|
||||
-moz-image-region: rect(0, 160px, 32px, 128px)
|
||||
}
|
||||
|
||||
#category-security > .category-icon {
|
||||
-moz-image-region: rect(0, 192px, 32px, 160px)
|
||||
}
|
||||
|
||||
#category-advanced > .category-icon {
|
||||
-moz-image-region: rect(0, 224px, 32px, 192px)
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#category-sync > .category-icon {
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
/* Applications Pane Styles */
|
||||
|
||||
#applications-content {
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.7 KiB |
|
@ -169,7 +169,7 @@ browser.jar:
|
|||
#endif
|
||||
skin/classic/browser/preferences/saveFile.png (preferences/saveFile.png)
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
|
@ -232,6 +232,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
richlistitem label {
|
||||
#handlersView > richlistitem label {
|
||||
-moz-margin-start: 3px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,102 +2,119 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
%include ../../shared.inc
|
||||
|
||||
@import url("chrome://global/skin/inContentUI.css");
|
||||
|
||||
@namespace html "http://www.w3.org/1999/xhtml";
|
||||
|
||||
#preferences-home {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.landingButton {
|
||||
-moz-box-align: center;
|
||||
-moz-box-orient: vertical;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.landingButton:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.landingButton-label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.landingButton-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.preference-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.preference-icon[type="general"],
|
||||
.landingButton-icon[type="general"] {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="content"],
|
||||
.landingButton-icon[type="content"] {
|
||||
background-position: -64px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="applications"],
|
||||
.landingButton-icon[type="applications"] {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="privacy"],
|
||||
.landingButton-icon[type="privacy"] {
|
||||
background-position: -128px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="security"],
|
||||
.landingButton-icon[type="security"] {
|
||||
background-position: -160px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="advanced"],
|
||||
.landingButton-icon[type="advanced"] {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="sync"],
|
||||
.landingButton-icon[type="sync"] {
|
||||
background-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 20px;
|
||||
font-size: 1.667rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 50px;
|
||||
background-color: rgba(192,199,210,0.7);
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-bottom: 15px;
|
||||
-moz-box-align: center;
|
||||
.main-content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
prefpane > .content-box {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
#categories {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
-moz-margin-end: -1px;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
margin-top: 31px;
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-appearance: none;
|
||||
color: #252F3B;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
padding: 10px 4px;
|
||||
-moz-padding-end: 8px;
|
||||
-moz-box-align: center;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.category[selected] {
|
||||
background-color: rgba(255, 255, 255, 0.35);
|
||||
color: -moz-dialogtext;
|
||||
border-color: rgba(50, 65, 92, 0.4);
|
||||
-moz-border-end-color: #C9CFD7;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 1.5rem;
|
||||
-moz-padding-end: 24px;
|
||||
}
|
||||
|
||||
/* Maximize the size of the viewport when the window is small */
|
||||
@media (max-width: 800px) {
|
||||
.category-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
-moz-margin-start: 6px;
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
}
|
||||
|
||||
#category-general > .category-icon {
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
|
||||
#category-content > .category-icon {
|
||||
-moz-image-region: rect(0, 96px, 32px, 64px)
|
||||
}
|
||||
|
||||
#category-application > .category-icon {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px)
|
||||
}
|
||||
|
||||
#category-privacy > .category-icon {
|
||||
-moz-image-region: rect(0, 160px, 32px, 128px)
|
||||
}
|
||||
|
||||
#category-security > .category-icon {
|
||||
-moz-image-region: rect(0, 192px, 32px, 160px)
|
||||
}
|
||||
|
||||
#category-advanced > .category-icon {
|
||||
-moz-image-region: rect(0, 224px, 32px, 192px)
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#category-sync > .category-icon {
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
/* Applications Pane Styles */
|
||||
|
||||
#applications-content {
|
||||
|
|
|
@ -121,11 +121,29 @@
|
|||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.devtools-tooltip.devtools-tooltip-panel .panel-arrowcontent {
|
||||
/* If the tooltip uses a <panel> XUL element instead */
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text {
|
||||
background: linear-gradient(1deg, transparent 0%, rgba(94,136,176,0.1) 100%);
|
||||
max-width: 400px;
|
||||
margin: 0 -4px; /* Compensate for the .panel-arrowcontent padding. */
|
||||
padding: 8px 12px;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text:first-child {
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text:last-child {
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip-tiles {
|
||||
background-color: #eee;
|
||||
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.7 KiB |
|
@ -125,7 +125,7 @@ browser.jar:
|
|||
#endif
|
||||
skin/classic/browser/preferences/saveFile.png (preferences/saveFile.png)
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
|
@ -167,6 +167,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
|
@ -402,7 +403,7 @@ browser.jar:
|
|||
#endif
|
||||
skin/classic/aero/browser/preferences/saveFile.png (preferences/saveFile-aero.png)
|
||||
* skin/classic/aero/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/aero/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/aero/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/aero/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/aero/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/aero/browser/social/services-16.png (social/services-16.png)
|
||||
|
@ -444,6 +445,7 @@ browser.jar:
|
|||
skin/classic/aero/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/aero/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/aero/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/aero/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/aero/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/aero/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
* skin/classic/aero/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
|
|
|
@ -21,12 +21,12 @@
|
|||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
richlistitem label {
|
||||
#handlersView > richlistitem label {
|
||||
-moz-margin-start: 1px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,98 +6,116 @@
|
|||
|
||||
@namespace html "http://www.w3.org/1999/xhtml";
|
||||
|
||||
#preferences-home {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.landingButton {
|
||||
-moz-box-align: center;
|
||||
-moz-box-orient: vertical;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.landingButton:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.landingButton-label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.landingButton-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.preference-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.preference-icon[type="general"],
|
||||
.landingButton-icon[type="general"] {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="content"],
|
||||
.landingButton-icon[type="content"] {
|
||||
background-position: -64px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="applications"],
|
||||
.landingButton-icon[type="applications"] {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="privacy"],
|
||||
.landingButton-icon[type="privacy"] {
|
||||
background-position: -128px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="security"],
|
||||
.landingButton-icon[type="security"] {
|
||||
background-position: -160px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="advanced"],
|
||||
.landingButton-icon[type="advanced"] {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="sync"],
|
||||
.landingButton-icon[type="sync"] {
|
||||
background-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 20px;
|
||||
font-size: 1.667rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 50px;
|
||||
background-color: rgba(192,199,210,0.7);
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-bottom: 15px;
|
||||
-moz-box-align: center;
|
||||
.main-content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
prefpane > .content-box {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
#categories {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
-moz-margin-end: -1px;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
margin-top: 31px;
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-appearance: none;
|
||||
background-color: transparent;
|
||||
color: #252F3B;
|
||||
padding: 10px 4px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
-moz-padding-end: 8px;
|
||||
-moz-box-align: center;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.category[selected] {
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
color: #252F3B;
|
||||
border-color: #C3CEDF;
|
||||
-moz-border-end-color: #E2E9F2;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 1.5rem;
|
||||
-moz-padding-end: 24px;
|
||||
}
|
||||
|
||||
/* Maximize the size of the viewport when the window is small */
|
||||
@media (max-width: 800px) {
|
||||
.category-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0 6px;
|
||||
-moz-margin-start: 6px;
|
||||
-moz-margin-end: 5px;
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
}
|
||||
|
||||
#category-general > .category-icon {
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
|
||||
#category-content > .category-icon {
|
||||
-moz-image-region: rect(0, 96px, 32px, 64px)
|
||||
}
|
||||
|
||||
#category-application > .category-icon {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px)
|
||||
}
|
||||
|
||||
#category-privacy > .category-icon {
|
||||
-moz-image-region: rect(0, 160px, 32px, 128px)
|
||||
}
|
||||
|
||||
#category-security > .category-icon {
|
||||
-moz-image-region: rect(0, 192px, 32px, 160px)
|
||||
}
|
||||
|
||||
#category-advanced > .category-icon {
|
||||
-moz-image-region: rect(0, 224px, 32px, 192px)
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#category-sync > .category-icon {
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
/* Applications Pane Styles */
|
||||
|
||||
#applications-content {
|
||||
|
|
|
@ -37,7 +37,6 @@ import org.json.JSONObject;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -1475,15 +1474,6 @@ abstract public class BrowserApp extends GeckoApp
|
|||
return;
|
||||
}
|
||||
|
||||
// If the keywordUrl is in ReadingList, convert the url to an about:reader url and load it.
|
||||
final ContentResolver cr = getContentResolver();
|
||||
final boolean inReadingList = BrowserDB.isReadingListItem(cr, keywordUrl);
|
||||
if (inReadingList) {
|
||||
final String readerUrl = ReaderModeUtils.getAboutReaderForUrl(keywordUrl);
|
||||
Tabs.getInstance().loadUrl(readerUrl, Tabs.LOADURL_USER_ENTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
recordSearch(null, "barkeyword");
|
||||
|
||||
// Otherwise, construct a search query from the bookmark keyword.
|
||||
|
|
|
@ -32,18 +32,29 @@ public final class VideoPlayer extends Activity {
|
|||
MediaController mediaController = new MediaController(this);
|
||||
mediaController.setAnchorView(mVideoView);
|
||||
Intent intent = getIntent();
|
||||
Uri data = intent.getData();
|
||||
if (data == null)
|
||||
final Uri data = intent.getData();
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String spec = null;
|
||||
if ("vnd.youtube".equals(data.getScheme())) {
|
||||
String ssp = data.getSchemeSpecificPart();
|
||||
String id = ssp.substring(0, ssp.indexOf('?'));
|
||||
int paramIndex = ssp.indexOf('?');
|
||||
String id;
|
||||
if (paramIndex == -1) {
|
||||
id = ssp;
|
||||
} else {
|
||||
id = ssp.substring(0, paramIndex);
|
||||
}
|
||||
spec = getSpecFromYouTubeVideoID(id);
|
||||
}
|
||||
if (spec == null)
|
||||
|
||||
if (spec == null) {
|
||||
return;
|
||||
Uri video = Uri.parse(spec);
|
||||
}
|
||||
|
||||
final Uri video = Uri.parse(spec);
|
||||
mVideoView.setMediaController(mediaController);
|
||||
mVideoView.setVideoURI(video);
|
||||
mVideoView.start();
|
||||
|
|
|
@ -6173,7 +6173,7 @@ k12.pa.us
|
|||
k12.pr.us
|
||||
k12.ri.us
|
||||
k12.sc.us
|
||||
k12.sd.us
|
||||
// k12.sd.us Bug 934131 - South Dakota has a centralized hosting
|
||||
k12.tn.us
|
||||
k12.tx.us
|
||||
k12.ut.us
|
||||
|
|
|
@ -185,9 +185,6 @@ Tester.prototype = {
|
|||
var failCount = this.tests.reduce(function(a, f) a + f.failCount, 0);
|
||||
var todoCount = this.tests.reduce(function(a, f) a + f.todoCount, 0);
|
||||
|
||||
if (failCount > 0 && this.runUntilFailure)
|
||||
this.repeat = 0;
|
||||
|
||||
if (this.repeat > 0) {
|
||||
--this.repeat;
|
||||
this.currentTestIndex = -1;
|
||||
|
@ -222,6 +219,12 @@ Tester.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
haltTests: function Tester_haltTests() {
|
||||
// Do not run any further tests
|
||||
this.currentTestIndex = this.tests.length - 1;
|
||||
this.repeat = 0;
|
||||
},
|
||||
|
||||
observe: function Tester_observe(aSubject, aTopic, aData) {
|
||||
if (!aTopic) {
|
||||
this.onConsoleMessage(aSubject);
|
||||
|
@ -354,6 +357,10 @@ Tester.prototype = {
|
|||
this.dumper.dump("INFO TEST-END | " + this.currentTest.path + " | finished in " + time + "ms\n");
|
||||
this.currentTest.setDuration(time);
|
||||
|
||||
if (this.runUntilFailure && this.currentTest.failCount > 0) {
|
||||
this.haltTests();
|
||||
}
|
||||
|
||||
testScope.destroy();
|
||||
this.currentTest.scope = null;
|
||||
}
|
||||
|
|
|
@ -392,9 +392,9 @@ def MochitestCommand(func):
|
|||
func = repeat(func)
|
||||
|
||||
runUntilFailure = CommandArgument("--run-until-failure", action='store_true',
|
||||
help='Run a test repeatedly and stops on the first time the test fails. ' \
|
||||
'Only available when running a single test. Default cap is 30 runs, ' \
|
||||
'which can be overwritten with the --repeat parameter.')
|
||||
help='Run tests repeatedly and stops on the first time a test fails. ' \
|
||||
'Default cap is 30 runs, which can be overwritten ' \
|
||||
'with the --repeat parameter.')
|
||||
func = runUntilFailure(func)
|
||||
|
||||
slow = CommandArgument('--slow', action='store_true',
|
||||
|
|
|
@ -274,9 +274,8 @@ class MochitestOptions(optparse.OptionParser):
|
|||
[["--run-until-failure"],
|
||||
{ "action": "store_true",
|
||||
"dest": "runUntilFailure",
|
||||
"help": "Run a test repeatedly and stops on the first time the test fails. "
|
||||
"Only available when running a single test. Default cap is 30 runs, "
|
||||
"which can be overwritten with the --repeat parameter.",
|
||||
"help": "Run tests repeatedly and stops on the first time a test fails. "
|
||||
"Default cap is 30 runs, which can be overwritten with the --repeat parameter.",
|
||||
"default": False,
|
||||
}],
|
||||
[["--run-only-tests"],
|
||||
|
@ -495,8 +494,6 @@ class MochitestOptions(optparse.OptionParser):
|
|||
mochitest.immersiveHelperPath)
|
||||
|
||||
if options.runUntilFailure:
|
||||
if not os.path.isfile(os.path.join(mochitest.oldcwd, os.path.dirname(__file__), mochitest.getTestRoot(options), options.testPath)):
|
||||
self.error("--run-until-failure can only be used together with --test-path specifying a single test.")
|
||||
if not options.repeat:
|
||||
options.repeat = 29
|
||||
|
||||
|
|
|
@ -214,6 +214,10 @@ TestRunner.error = function(msg) {
|
|||
dump(msg + "\n");
|
||||
}
|
||||
|
||||
if (TestRunner.runUntilFailure) {
|
||||
TestRunner._haltTests = true;
|
||||
}
|
||||
|
||||
if (TestRunner.debugOnFailure) {
|
||||
// You've hit this line because you requested to break into the
|
||||
// debugger upon a testcase failure on your test run.
|
||||
|
@ -366,10 +370,7 @@ TestRunner.runNextTest = function() {
|
|||
TestRunner.onComplete();
|
||||
}
|
||||
|
||||
var failCount = parseInt($("fail-count").innerHTML);
|
||||
var stopLooping = failCount > 0 && TestRunner.runUntilFailure;
|
||||
|
||||
if (TestRunner._currentLoop <= TestRunner.repeat && !stopLooping) {
|
||||
if (TestRunner._currentLoop <= TestRunner.repeat && !TestRunner._haltTests) {
|
||||
TestRunner._currentLoop++;
|
||||
TestRunner.resetTests(TestRunner._urls);
|
||||
TestRunner._loopIsRestarting = true;
|
||||
|
|
|
@ -1,10 +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/.
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
MOCHITEST_BROWSER_FILES += \
|
||||
browser_thumbnails_background_crash.js \
|
||||
thumbnails_crash_content_helper.js \
|
||||
$(NULL)
|
||||
endif
|
|
@ -6,11 +6,13 @@ support-files =
|
|||
head.js
|
||||
privacy_cache_control.sjs
|
||||
thumbnails_background.sjs
|
||||
thumbnails_crash_content_helper.js
|
||||
thumbnails_update.sjs
|
||||
|
||||
[browser_thumbnails_background.js]
|
||||
# Too many intermittent failures (bug 931889)
|
||||
skip-if = os == "linux"
|
||||
[browser_thumbnails_background_crash.js]
|
||||
[browser_thumbnails_bug726727.js]
|
||||
[browser_thumbnails_bug727765.js]
|
||||
[browser_thumbnails_bug818225.js]
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
// server updates.
|
||||
const kKeyFilename = "urlclassifierkey3.txt";
|
||||
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
Components.utils.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
/**
|
||||
* A key manager for UrlCrypto. There should be exactly one of these
|
||||
* per appplication, and all UrlCrypto's should share it. This is
|
||||
|
@ -234,13 +237,15 @@ PROT_UrlCryptoKeyManager.prototype.unUrlSafe = function(key)
|
|||
/**
|
||||
* Set a new key and serialize it to disk.
|
||||
*
|
||||
* @param clientKey String containing the base64-encoded client key
|
||||
* @param clientKey String containing the base64-encoded client key
|
||||
* we wish to use
|
||||
*
|
||||
* @param wrappedKey String containing the opaque base64-encoded WrappedKey
|
||||
* the server gave us (i.e., K_C encrypted with K_S)
|
||||
*
|
||||
* @returns Promise of a boolean indicating whether we succeeded in replacing
|
||||
*/
|
||||
PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey,
|
||||
PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey,
|
||||
wrappedKey) {
|
||||
if (this.clientKey_)
|
||||
G_Debug(this, "Replacing " + this.clientKey_ + " with " + clientKey);
|
||||
|
@ -250,57 +255,52 @@ PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey,
|
|||
function(c) { return c.charCodeAt(0); });
|
||||
this.wrappedKey_ = wrappedKey;
|
||||
|
||||
this.serializeKey_(this.clientKey_, this.wrappedKey_);
|
||||
let promise = this.serializeKey_(this.clientKey_, this.wrappedKey_);
|
||||
|
||||
if (this.onNewKey_) {
|
||||
this.onNewKey_();
|
||||
}
|
||||
return promise.then(() => {
|
||||
if (this.onNewKey_) {
|
||||
this.onNewKey_();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to write the key to disk so we can fall back on it. Fail
|
||||
* silently if we cannot. The keys are serialized in protocol4 format.
|
||||
*
|
||||
* @returns Boolean indicating whether we succeeded in serializing
|
||||
* @returns Promise of a boolean indicating whether we succeeded in serializing
|
||||
*/
|
||||
PROT_UrlCryptoKeyManager.prototype.serializeKey_ = function() {
|
||||
|
||||
var map = {};
|
||||
map[this.CLIENT_KEY_NAME] = this.clientKey_;
|
||||
map[this.WRAPPED_KEY_NAME] = this.wrappedKey_;
|
||||
|
||||
try {
|
||||
|
||||
var keyfile = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("ProfD", Ci.nsILocalFile); /* profile directory */
|
||||
keyfile.append(this.keyFilename_);
|
||||
let keypath = OS.Path.join(OS.Constants.Path.profileDir, this.keyFilename_);
|
||||
|
||||
if (!this.clientKey_ || !this.wrappedKey_) {
|
||||
keyfile.remove(true);
|
||||
return;
|
||||
}
|
||||
// if we have an invalid client key or wrapped key, we remove the
|
||||
// invalid keyfile from disk
|
||||
if (!this.clientKey_ || !this.wrappedKey_) {
|
||||
return OS.File.remove(keypath).then(() => false,
|
||||
e => {
|
||||
if (!e.becauseNoSuchFile)
|
||||
throw e;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
var data = (new G_Protocol4Parser()).serialize(map);
|
||||
|
||||
try {
|
||||
var stream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(keyfile,
|
||||
0x02 | 0x08 | 0x20 /* PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE */,
|
||||
-1 /* default perms */, 0 /* no special behavior */);
|
||||
stream.write(data, data.length);
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch(e) {
|
||||
let data = (new G_Protocol4Parser()).serialize(map);
|
||||
|
||||
let encoder = new TextEncoder();
|
||||
let array = encoder.encode(data);
|
||||
let promise = OS.File.writeAtomic(keypath, array, { tmpPath: keypath + ".tmp",
|
||||
flush: false });
|
||||
return promise.then(() => true,
|
||||
e => {
|
||||
G_Error(this, "Failed to serialize new key: " + e);
|
||||
return false;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -308,7 +308,10 @@ PROT_UrlCryptoKeyManager.prototype.serializeKey_ = function() {
|
|||
* request. Try to parse it and set this key as the new one if we can.
|
||||
*
|
||||
* @param responseText String containing the protocol4 getkey response
|
||||
*/
|
||||
*
|
||||
* @returns Promise of a boolean indicating whether we succeeded in setting
|
||||
* the new key
|
||||
*/
|
||||
PROT_UrlCryptoKeyManager.prototype.onGetKeyResponse = function(responseText) {
|
||||
|
||||
var response = (new G_Protocol4Parser).parse(responseText);
|
||||
|
@ -320,9 +323,10 @@ PROT_UrlCryptoKeyManager.prototype.onGetKeyResponse = function(responseText) {
|
|||
|
||||
if (response && clientKey && wrappedKey) {
|
||||
G_Debug(this, "Got new key from: " + responseText);
|
||||
this.replaceKey_(clientKey, wrappedKey);
|
||||
return this.replaceKey_(clientKey, wrappedKey);
|
||||
} else {
|
||||
G_Debug(this, "Not a valid response for /newkey");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,7 +348,10 @@ PROT_UrlCryptoKeyManager.prototype.onNewKey = function(callback)
|
|||
*
|
||||
* This method should be invoked early, like when the user's profile
|
||||
* becomes available.
|
||||
*/
|
||||
*
|
||||
* @returns Promise of a boolean indicating whether we succeeded in
|
||||
* loading old key
|
||||
*/
|
||||
PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
|
||||
|
||||
var oldKey = null;
|
||||
|
@ -369,12 +376,12 @@ PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
|
|||
}
|
||||
} catch(e) {
|
||||
G_Debug(this, "Caught " + e + " trying to read keyfile");
|
||||
return;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
|
||||
if (!oldKey) {
|
||||
G_Debug(this, "Couldn't find old key.");
|
||||
return;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
oldKey = (new G_Protocol4Parser).parse(oldKey);
|
||||
|
@ -383,8 +390,9 @@ PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
|
|||
|
||||
if (oldKey && clientKey && wrappedKey && !this.hasKey()) {
|
||||
G_Debug(this, "Read old key from disk.");
|
||||
this.replaceKey_(clientKey, wrappedKey);
|
||||
return this.replaceKey_(clientKey, wrappedKey);
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
PROT_UrlCryptoKeyManager.prototype.shutdown = function() {
|
||||
|
@ -393,100 +401,3 @@ PROT_UrlCryptoKeyManager.prototype.shutdown = function() {
|
|||
this.fetcher_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Cheesey tests
|
||||
*/
|
||||
function TEST_PROT_UrlCryptoKeyManager() {
|
||||
if (G_GDEBUG) {
|
||||
var z = "urlcryptokeymanager UNITTEST";
|
||||
G_debugService.enableZone(z);
|
||||
|
||||
G_Debug(z, "Starting");
|
||||
|
||||
// Let's not clobber any real keyfile out there
|
||||
var kf = "keytest.txt";
|
||||
|
||||
// Let's be able to clean up after ourselves
|
||||
function removeTestFile(f) {
|
||||
var file = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("ProfD", Ci.nsILocalFile); /* profile directory */
|
||||
file.append(f);
|
||||
if (file.exists())
|
||||
file.remove(false /* do not recurse */);
|
||||
};
|
||||
removeTestFile(kf);
|
||||
|
||||
var km = new PROT_UrlCryptoKeyManager(kf, true /* testing */);
|
||||
|
||||
// CASE: simulate nothing on disk, then get something from server
|
||||
|
||||
G_Assert(z, !km.hasKey(), "KM already has key?");
|
||||
km.maybeLoadOldKey();
|
||||
G_Assert(z, !km.hasKey(), "KM loaded nonexistent key?");
|
||||
km.onGetKeyResponse(null);
|
||||
G_Assert(z, !km.hasKey(), "KM got key from null response?");
|
||||
km.onGetKeyResponse("");
|
||||
G_Assert(z, !km.hasKey(), "KM got key from empty response?");
|
||||
km.onGetKeyResponse("aslkaslkdf:34:a230\nskdjfaljsie");
|
||||
G_Assert(z, !km.hasKey(), "KM got key from garbage response?");
|
||||
|
||||
var realResponse = "clientkey:24:zGbaDbx1pxoYe7siZYi8VA==\n" +
|
||||
"wrappedkey:24:MTr1oDt6TSOFQDTvKCWz9PEn";
|
||||
km.onGetKeyResponse(realResponse);
|
||||
// Will have written it to file as a side effect
|
||||
G_Assert(z, km.hasKey(), "KM couldn't get key from real response?");
|
||||
G_Assert(z, km.clientKey_ == "zGbaDbx1pxoYe7siZYi8VA==",
|
||||
"Parsed wrong client key from response?");
|
||||
G_Assert(z, km.wrappedKey_ == "MTr1oDt6TSOFQDTvKCWz9PEn",
|
||||
"Parsed wrong wrapped key from response?");
|
||||
|
||||
// CASE: simulate something on disk, then get something from server
|
||||
|
||||
km = new PROT_UrlCryptoKeyManager(kf, true /* testing */);
|
||||
G_Assert(z, !km.hasKey(), "KM already has key?");
|
||||
km.maybeLoadOldKey();
|
||||
G_Assert(z, km.hasKey(), "KM couldn't load existing key from disk?");
|
||||
G_Assert(z, km.clientKey_ == "zGbaDbx1pxoYe7siZYi8VA==",
|
||||
"Parsed wrong client key from disk?");
|
||||
G_Assert(z, km.wrappedKey_ == "MTr1oDt6TSOFQDTvKCWz9PEn",
|
||||
"Parsed wrong wrapped key from disk?");
|
||||
var realResponse2 = "clientkey:24:dtmbEN1kgN/LmuEoYifaFw==\n" +
|
||||
"wrappedkey:24:MTpPH3pnLDKihecOci+0W5dk";
|
||||
km.onGetKeyResponse(realResponse2);
|
||||
// Will have written it to disk
|
||||
G_Assert(z, km.hasKey(), "KM couldn't replace key from server response?");
|
||||
G_Assert(z, km.clientKey_ == "dtmbEN1kgN/LmuEoYifaFw==",
|
||||
"Replace client key from server failed?");
|
||||
G_Assert(z, km.wrappedKey == "MTpPH3pnLDKihecOci+0W5dk",
|
||||
"Replace wrapped key from server failed?");
|
||||
|
||||
// CASE: check overwriting a key on disk
|
||||
|
||||
km = new PROT_UrlCryptoKeyManager(kf, true /* testing */);
|
||||
G_Assert(z, !km.hasKey(), "KM already has key?");
|
||||
km.maybeLoadOldKey();
|
||||
G_Assert(z, km.hasKey(), "KM couldn't load existing key from disk?");
|
||||
G_Assert(z, km.clientKey_ == "dtmbEN1kgN/LmuEoYifaFw==",
|
||||
"Replace client on from disk failed?");
|
||||
G_Assert(z, km.wrappedKey_ == "MTpPH3pnLDKihecOci+0W5dk",
|
||||
"Replace wrapped key on disk failed?");
|
||||
|
||||
// Test that we only fetch at most two getkey's per lifetime of the manager
|
||||
|
||||
km = new PROT_UrlCryptoKeyManager(kf, true /* testing */);
|
||||
km.reKey();
|
||||
for (var i = 0; i < km.MAX_REKEY_TRIES; i++)
|
||||
G_Assert(z, km.maybeReKey(), "Couldn't rekey?");
|
||||
G_Assert(z, !km.maybeReKey(), "Rekeyed when max hit");
|
||||
|
||||
removeTestFile(kf);
|
||||
|
||||
G_Debug(z, "PASSED");
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* 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/. */
|
||||
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
var kf = "keytest.txt"; // not an actual keyfile
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function empty_disk() {
|
||||
var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
|
||||
.getService().wrappedJSObject;
|
||||
this.PROT_UrlCryptoKeyManager = jslib.PROT_UrlCryptoKeyManager;
|
||||
yield OS.File.remove(kf);
|
||||
do_print("simulate nothing on disk, then get something from server");
|
||||
var km = new PROT_UrlCryptoKeyManager(kf, true);
|
||||
do_check_false(km.hasKey()); // KM already has key?
|
||||
km.maybeLoadOldKey();
|
||||
do_check_false(km.hasKey()); // KM loaded nonexistent key?
|
||||
yield km.onGetKeyResponse(null);
|
||||
do_check_false(km.hasKey()); // KM got key from null response?
|
||||
yield km.onGetKeyResponse("");
|
||||
do_check_false(km.hasKey()); // KM got key from an empty response?
|
||||
yield km.onGetKeyResponse("aslkaslkdf:34:a230\nskdjfaljsie");
|
||||
do_check_false(km.hasKey()); // KM got key from garbage response?
|
||||
var realResponse = "clientkey:24:zGbaDbx1pxoYe7siZYi8VA==\n" +
|
||||
"wrappedkey:24:MTr1oDt6TSOFQDTvKCWz9PEn";
|
||||
yield km.onGetKeyResponse(realResponse);
|
||||
// Will have written it to the file as a side effect
|
||||
do_check_true(km.hasKey()); // KM could not get key from a real response?
|
||||
do_check_eq(km.clientKey_, "zGbaDbx1pxoYe7siZYi8VA=="); // Parsed wrong client key from response?
|
||||
do_check_eq(km.wrappedKey_, "MTr1oDt6TSOFQDTvKCWz9PEn"); // Parsed wrong wrapped key from response?
|
||||
|
||||
do_print("simulate something on disk, then get something from server");
|
||||
var km = new PROT_UrlCryptoKeyManager(kf, true);
|
||||
do_check_false(km.hasKey()); // KM already has key?
|
||||
yield km.maybeLoadOldKey();
|
||||
do_check_true(km.hasKey()); // KM couldn't load existing key from disk?
|
||||
do_check_eq(km.clientKey_ , "zGbaDbx1pxoYe7siZYi8VA=="); // Parsed wrong client key from disk?
|
||||
do_check_eq(km.wrappedKey_, "MTr1oDt6TSOFQDTvKCWz9PEn"); // Parsed wrong wrapped key from disk?
|
||||
var realResponse2 = "clientkey:24:dtmbEN1kgN/LmuEoYifaFw==\n" +
|
||||
"wrappedkey:24:MTpPH3pnLDKihecOci+0W5dk";
|
||||
yield km.onGetKeyResponse(realResponse2);
|
||||
do_check_true(km.hasKey()); // KM couldn't replace key from server response?
|
||||
do_check_eq(km.clientKey_, "dtmbEN1kgN/LmuEoYifaFw=="); // Replace client key from server failed?
|
||||
do_check_eq(km.wrappedKey_, "MTpPH3pnLDKihecOci+0W5dk"); // Replace wrapped key from server failed?
|
||||
|
||||
do_print("check overwriting a key on disk");
|
||||
km = new PROT_UrlCryptoKeyManager(kf, true);
|
||||
do_check_false(km.hasKey()); // KM already has key?
|
||||
yield km.maybeLoadOldKey();
|
||||
do_check_true(km.hasKey()); // KM couldn't load existing key from file?
|
||||
do_check_eq(km.clientKey_, "dtmbEN1kgN/LmuEoYifaFw=="); // Replace client key on disk failed?
|
||||
do_check_eq(km.wrappedKey_, "MTpPH3pnLDKihecOci+0W5dk"); // Replace wrapped key on disk failed?
|
||||
|
||||
do_print("Test that we only fetch at most two getkey's per lifetime of the manager");
|
||||
var km = new PROT_UrlCryptoKeyManager(kf, true);
|
||||
km.reKey();
|
||||
for (var i = 0; i < km.MAX_REKEY_TRIES; i++)
|
||||
do_check_true(km.maybeReKey()); // Couldn't rekey?
|
||||
do_check_false(km.maybeReKey()); // Rekeyed when max hit?
|
||||
|
||||
yield OS.File.remove(kf);
|
||||
});
|
|
@ -5,6 +5,7 @@ support-files =
|
|||
data/digest1.chunk
|
||||
data/digest2.chunk
|
||||
|
||||
[test_keymanager.js]
|
||||
[test_addsub.js]
|
||||
[test_backoff.js]
|
||||
[test_dbservice.js]
|
||||
|
|
|
@ -110,6 +110,13 @@ function delayedResolve(value) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
types.addDictType("imageData", {
|
||||
// The image data
|
||||
data: "nullable:longstring",
|
||||
// The original image dimensions
|
||||
size: "json"
|
||||
});
|
||||
|
||||
/**
|
||||
* We only send nodeValue up to a certain size by default. This stuff
|
||||
* controls that size.
|
||||
|
@ -255,8 +262,12 @@ var NodeActor = protocol.ActorClass({
|
|||
* A null return value means the node isn't an image
|
||||
* An empty string return value means the node is an image but image data
|
||||
* could not be retrieved (missing/broken image).
|
||||
*
|
||||
* Accepts a maxDim request parameter to resize images that are larger. This
|
||||
* is important as the resizing occurs server-side so that image-data being
|
||||
* transfered in the longstring back to the client will be that much smaller
|
||||
*/
|
||||
getImageData: method(function() {
|
||||
getImageData: method(function(maxDim) {
|
||||
let isImg = this.rawNode.tagName.toLowerCase() === "img";
|
||||
let isCanvas = this.rawNode.tagName.toLowerCase() === "canvas";
|
||||
|
||||
|
@ -264,29 +275,44 @@ var NodeActor = protocol.ActorClass({
|
|||
return null;
|
||||
}
|
||||
|
||||
let imageData;
|
||||
if (isImg) {
|
||||
let canvas = this.rawNode.ownerDocument.createElement("canvas");
|
||||
canvas.width = this.rawNode.naturalWidth;
|
||||
canvas.height = this.rawNode.naturalHeight;
|
||||
let ctx = canvas.getContext("2d");
|
||||
try {
|
||||
// This will fail if the image is missing
|
||||
ctx.drawImage(this.rawNode, 0, 0);
|
||||
imageData = canvas.toDataURL("image/png");
|
||||
} catch (e) {
|
||||
imageData = "";
|
||||
}
|
||||
} else if (isCanvas) {
|
||||
imageData = this.rawNode.toDataURL("image/png");
|
||||
// Get the image resize ratio if a maxDim was provided
|
||||
let resizeRatio = 1;
|
||||
let imgWidth = isImg ? this.rawNode.naturalWidth : this.rawNode.width;
|
||||
let imgHeight = isImg ? this.rawNode.naturalHeight : this.rawNode.height;
|
||||
let imgMax = Math.max(imgWidth, imgHeight);
|
||||
if (maxDim && imgMax > maxDim) {
|
||||
resizeRatio = maxDim / imgMax;
|
||||
}
|
||||
|
||||
return LongStringActor(this.conn, imageData);
|
||||
}, {
|
||||
request: {},
|
||||
response: {
|
||||
data: RetVal("nullable:longstring")
|
||||
// Create a canvas to copy the rawNode into and get the imageData from
|
||||
let canvas = this.rawNode.ownerDocument.createElement("canvas");
|
||||
canvas.width = imgWidth * resizeRatio;
|
||||
canvas.height = imgHeight * resizeRatio;
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
// Copy the rawNode image or canvas in the new canvas and extract data
|
||||
let imageData;
|
||||
// This may fail if the image is missing
|
||||
try {
|
||||
ctx.drawImage(this.rawNode, 0, 0, canvas.width, canvas.height);
|
||||
imageData = canvas.toDataURL("image/png");
|
||||
} catch (e) {
|
||||
imageData = "";
|
||||
}
|
||||
|
||||
return {
|
||||
data: LongStringActor(this.conn, imageData),
|
||||
size: {
|
||||
naturalWidth: imgWidth,
|
||||
naturalHeight: imgHeight,
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
resized: resizeRatio !== 1
|
||||
}
|
||||
}
|
||||
}, {
|
||||
request: {maxDim: Arg(0, "nullable:number")},
|
||||
response: RetVal("imageData")
|
||||
}),
|
||||
|
||||
/**
|
||||
|
|
|
@ -140,6 +140,24 @@ let ProgramActor = protocol.ActorClass({
|
|||
oneway: true
|
||||
}),
|
||||
|
||||
/**
|
||||
* Prevents any geometry from being rendered using this program.
|
||||
*/
|
||||
blackbox: method(function() {
|
||||
this.observer.cache.blackboxedPrograms.add(this.program);
|
||||
}, {
|
||||
oneway: true
|
||||
}),
|
||||
|
||||
/**
|
||||
* Allows geometry to be rendered using this program.
|
||||
*/
|
||||
unblackbox: method(function() {
|
||||
this.observer.cache.blackboxedPrograms.delete(this.program);
|
||||
}, {
|
||||
oneway: true
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns a cached ShaderActor instance based on the required shader type.
|
||||
*
|
||||
|
@ -492,6 +510,15 @@ let WebGLInstrumenter = {
|
|||
"uniform1fv", "uniform2fv", "uniform3fv", "uniform4fv",
|
||||
"uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv"
|
||||
]
|
||||
}, {
|
||||
timing: "after",
|
||||
functions: ["useProgram"]
|
||||
}, {
|
||||
timing: "before",
|
||||
callback: "draw_",
|
||||
functions: [
|
||||
"drawArrays", "drawElements"
|
||||
]
|
||||
}]
|
||||
// TODO: It'd be a good idea to handle other functions as well:
|
||||
// - getActiveUniform
|
||||
|
@ -577,7 +604,7 @@ WebGLObserver.prototype = {
|
|||
*/
|
||||
toggleVertexAttribArray: function(gl, glArgs) {
|
||||
glArgs[0] = this.cache.call("getCurrentAttributeLocation", glArgs[0]);
|
||||
return glArgs[0] < 0;
|
||||
return glArgs[0] < 0; // Return true to break original function call.
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -590,7 +617,7 @@ WebGLObserver.prototype = {
|
|||
*/
|
||||
attribute_: function(gl, glArgs) {
|
||||
glArgs[0] = this.cache.call("getCurrentAttributeLocation", glArgs[0]);
|
||||
return glArgs[0] < 0;
|
||||
return glArgs[0] < 0; // Return true to break original function call.
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -603,7 +630,37 @@ WebGLObserver.prototype = {
|
|||
*/
|
||||
uniform_: function(gl, glArgs) {
|
||||
glArgs[0] = this.cache.call("getCurrentUniformLocation", glArgs[0]);
|
||||
return !glArgs[0];
|
||||
return !glArgs[0]; // Return true to break original function call.
|
||||
},
|
||||
|
||||
/**
|
||||
* Called immediately *after* 'useProgram' is requested in the context.
|
||||
*
|
||||
* @param WebGLRenderingContext gl
|
||||
* The WebGL context initiating this call.
|
||||
* @param array glArgs
|
||||
* Overridable arguments with which the function is called.
|
||||
* @param void glResult
|
||||
* The returned value of the original function call.
|
||||
*/
|
||||
useProgram: function(gl, glArgs, glResult) {
|
||||
// Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM)
|
||||
// because gl.get* functions are slow as potatoes.
|
||||
this.cache.currentProgram = glArgs[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Called immediately *before* 'drawArrays' or 'drawElements' is requested
|
||||
* in the context.
|
||||
*
|
||||
* @param WebGLRenderingContext gl
|
||||
* The WebGL context initiating this call.
|
||||
* @param array glArgs
|
||||
* Overridable arguments with which the function is called.
|
||||
*/
|
||||
draw_: function(gl, glArgs) {
|
||||
// Return true to break original function call.
|
||||
return this.cache.blackboxedPrograms.has(this.cache.currentProgram);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -634,6 +691,9 @@ WebGLObserver.prototype = {
|
|||
function WebGLCache(observer) {
|
||||
this._observer = observer;
|
||||
|
||||
this.currentProgram = null;
|
||||
this.blackboxedPrograms = new Set();
|
||||
|
||||
this._shaders = new Map();
|
||||
this._attributes = [];
|
||||
this._uniforms = [];
|
||||
|
@ -642,6 +702,16 @@ function WebGLCache(observer) {
|
|||
}
|
||||
|
||||
WebGLCache.prototype = {
|
||||
/**
|
||||
* The current program in the observed WebGL context.
|
||||
*/
|
||||
currentProgram: null,
|
||||
|
||||
/**
|
||||
* A set of blackboxed programs in the observed WebGL context.
|
||||
*/
|
||||
blackboxedPrograms: null,
|
||||
|
||||
/**
|
||||
* Adds shader information to the cache.
|
||||
*
|
||||
|
|
|
@ -5,6 +5,9 @@ support-files =
|
|||
inspector-styles-data.html
|
||||
inspector-traversal-data.html
|
||||
nonchrome_unsafeDereference.html
|
||||
inspector_getImageData.html
|
||||
large-image.jpg
|
||||
small-image.gif
|
||||
|
||||
[test_connection-manager.html]
|
||||
[test_device.html]
|
||||
|
@ -30,3 +33,4 @@ support-files =
|
|||
[test_styles-svg.html]
|
||||
[test_unsafeDereference.html]
|
||||
[test_evalInGlobal-outerized_this.html]
|
||||
[test_inspector_getImageData.html]
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<body>
|
||||
<img class="big-horizontal" src="large-image.jpg" style="width:500px;" />
|
||||
<canvas class="big-vertical" style="width:500px;"></canvas>
|
||||
<img class="small" src="small-image.gif"></img>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
var canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d");
|
||||
canvas.width = 1000;
|
||||
canvas.height = 2000;
|
||||
ctx.fillStyle = "red";
|
||||
ctx.fillRect(0, 0, 1000, 2000);
|
||||
|
||||
window.opener.postMessage('ready', '*')
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 775 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 499 KiB |
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=932937
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 932937</title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
|
||||
|
||||
const promise = devtools.require("sdk/core/promise");
|
||||
const inspector = devtools.require("devtools/server/actors/inspector");
|
||||
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
var gWalker = null;
|
||||
|
||||
addTest(function setup() {
|
||||
let url = document.getElementById("inspectorContent").href;
|
||||
attachURL(url, function(err, client, tab, doc) {
|
||||
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
|
||||
let inspector = InspectorFront(client, tab);
|
||||
|
||||
promiseDone(inspector.getWalker().then(walker => {
|
||||
gWalker = walker;
|
||||
}).then(runNextTest));
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function testLargeImage() {
|
||||
// Select the image node from the test page
|
||||
gWalker.querySelector(gWalker.rootNode, ".big-horizontal").then(img => {
|
||||
ok(img, "Image node found in the test page");
|
||||
ok(img.getImageData, "Image node has the getImageData function");
|
||||
|
||||
img.getImageData(100).then(imageData => {
|
||||
ok(imageData.data, "Image data actor was sent back");
|
||||
ok(imageData.size, "Image size info was sent back too");
|
||||
is(imageData.size.naturalWidth, 5333, "Natural width of the image correct");
|
||||
is(imageData.size.naturalHeight, 3000, "Natural width of the image correct");
|
||||
is(imageData.size.width, 100, "Resized image width correct");
|
||||
is(imageData.size.height, 56, "Resized image height correct");
|
||||
ok(imageData.size.resized, "Image was resized");
|
||||
|
||||
imageData.data.string().then(str => {
|
||||
ok(str, "We have an image data string!");
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function testLargeCanvas() {
|
||||
// Select the canvas node from the test page
|
||||
gWalker.querySelector(gWalker.rootNode, ".big-vertical").then(canvas => {
|
||||
ok(canvas, "Image node found in the test page");
|
||||
ok(canvas.getImageData, "Image node has the getImageData function");
|
||||
|
||||
canvas.getImageData(350).then(imageData => {
|
||||
ok(imageData.data, "Image data actor was sent back");
|
||||
ok(imageData.size, "Image size info was sent back too");
|
||||
is(imageData.size.naturalWidth, 1000, "Natural width of the image correct");
|
||||
is(imageData.size.naturalHeight, 2000, "Natural width of the image correct");
|
||||
is(imageData.size.width, 175, "Resized image width correct");
|
||||
is(imageData.size.height, 350, "Resized image height correct");
|
||||
ok(imageData.size.resized, "Image was resized");
|
||||
|
||||
imageData.data.string().then(str => {
|
||||
ok(str, "We have an image data string!");
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function testSmallImage() {
|
||||
// Select the small image node from the test page
|
||||
gWalker.querySelector(gWalker.rootNode, ".small").then(img => {
|
||||
ok(img, "Image node found in the test page");
|
||||
ok(img.getImageData, "Image node has the getImageData function");
|
||||
|
||||
img.getImageData().then(imageData => {
|
||||
ok(imageData.data, "Image data actor was sent back");
|
||||
ok(imageData.size, "Image size info was sent back too");
|
||||
is(imageData.size.naturalWidth, 245, "Natural width of the image correct");
|
||||
is(imageData.size.naturalHeight, 240, "Natural width of the image correct");
|
||||
is(imageData.size.width, 245, "Resized image width correct");
|
||||
is(imageData.size.height, 240, "Resized image height correct");
|
||||
ok(!imageData.size.resized, "Image was NOT resized");
|
||||
|
||||
imageData.data.string().then(str => {
|
||||
ok(str, "We have an image data string!");
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function cleanup() {
|
||||
delete gWalker;
|
||||
runNextTest();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=932937">Mozilla Bug 932937</a>
|
||||
<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -2201,7 +2201,7 @@ var XPIProvider = {
|
|||
*/
|
||||
getAddonStates: function XPI_getAddonStates(aLocation) {
|
||||
let addonStates = {};
|
||||
aLocation.addonLocations.forEach(function(file) {
|
||||
for (let file of aLocation.addonLocations) {
|
||||
let id = aLocation.getIDForLocation(file);
|
||||
let unpacked = 0;
|
||||
let [modFile, modTime] = recursiveLastModifiedTime(file);
|
||||
|
@ -2219,7 +2219,8 @@ var XPIProvider = {
|
|||
catch (e) { }
|
||||
this._mostRecentlyModifiedFile[id] = modFile;
|
||||
this.setTelemetry(id, "unpacked", unpacked);
|
||||
}, this);
|
||||
this.setTelemetry(id, "location", aLocation.name);
|
||||
}
|
||||
|
||||
return addonStates;
|
||||
},
|
||||
|
@ -3182,7 +3183,7 @@ var XPIProvider = {
|
|||
// The install locations are iterated in reverse order of priority so when
|
||||
// there are multiple add-ons installed with the same ID the one that
|
||||
// should be visible is the first one encountered.
|
||||
aState.reverse().forEach(function(aSt) {
|
||||
for (let aSt of aState.reverse()) {
|
||||
|
||||
// We can't include the install location directly in the state as it has
|
||||
// to be cached as JSON.
|
||||
|
@ -3195,7 +3196,7 @@ var XPIProvider = {
|
|||
let addons = XPIDatabase.getAddonsInLocation(installLocation.name);
|
||||
// Iterate through the add-ons installed the last time the application
|
||||
// ran
|
||||
addons.forEach(function(aOldAddon) {
|
||||
for (let aOldAddon of addons) {
|
||||
// If a version of this add-on has been installed in an higher
|
||||
// priority install location then count it as changed
|
||||
if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
|
||||
|
@ -3213,10 +3214,25 @@ var XPIProvider = {
|
|||
if (aOldAddon.visible && !aOldAddon.active)
|
||||
XPIProvider.inactiveAddonIDs.push(aOldAddon.id);
|
||||
|
||||
// record a bit more per-addon telemetry
|
||||
let loc = aOldAddon.defaultLocale;
|
||||
if (loc) {
|
||||
XPIProvider.setTelemetry(aOldAddon.id, "name", loc.name);
|
||||
XPIProvider.setTelemetry(aOldAddon.id, "creator", loc.creator);
|
||||
}
|
||||
|
||||
// Check if the add-on has been changed outside the XPI provider
|
||||
if (aOldAddon.updateDate != addonState.mtime) {
|
||||
// Did time change in the wrong direction?
|
||||
if (addonState.mtime < aOldAddon.updateDate) {
|
||||
this.setTelemetry(aOldAddon.id, "olderFile", {
|
||||
name: this._mostRecentlyModifiedFile[aOldAddon.id],
|
||||
mtime: addonState.mtime,
|
||||
oldtime: aOldAddon.updateDate
|
||||
});
|
||||
}
|
||||
// Is the add-on unpacked?
|
||||
if (addonState.rdfTime) {
|
||||
else if (addonState.rdfTime) {
|
||||
// Was the addon manifest "install.rdf" modified, or some other file?
|
||||
if (addonState.rdfTime > aOldAddon.updateDate) {
|
||||
this.setTelemetry(aOldAddon.id, "modifiedInstallRDF", 1);
|
||||
|
@ -3256,7 +3272,7 @@ var XPIProvider = {
|
|||
else {
|
||||
changed = removeMetadata(aOldAddon) || changed;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
// All the remaining add-ons in this install location must be new.
|
||||
|
@ -3269,7 +3285,7 @@ var XPIProvider = {
|
|||
changed = addMetadata(installLocation, id, addonStates[id],
|
||||
locMigrateData[id]) || changed;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
// The remaining locations that had add-ons installed in them no longer
|
||||
// have any add-ons installed in them, or the locations no longer exist.
|
||||
|
@ -3277,9 +3293,9 @@ var XPIProvider = {
|
|||
// database.
|
||||
for (let location of knownLocations) {
|
||||
let addons = XPIDatabase.getAddonsInLocation(location);
|
||||
addons.forEach(function(aOldAddon) {
|
||||
for (let aOldAddon of addons) {
|
||||
changed = removeMetadata(aOldAddon) || changed;
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the new install location states
|
||||
|
@ -5402,6 +5418,7 @@ AddonInstall.prototype = {
|
|||
let stagedAddon = stagingDir.clone();
|
||||
|
||||
Task.spawn((function() {
|
||||
let installedUnpacked = 0;
|
||||
yield this.installLocation.requestStagingDir();
|
||||
|
||||
// First stage the file regardless of whether restarting is necessary
|
||||
|
@ -5412,6 +5429,7 @@ AddonInstall.prototype = {
|
|||
yield recursiveRemoveAsync(stagedAddon);
|
||||
yield OS.File.makeDir(stagedAddon.path);
|
||||
yield extractFilesAsync(this.file, stagedAddon);
|
||||
installedUnpacked = 1;
|
||||
}
|
||||
else {
|
||||
LOG("Addon " + this.addon.id + " will be installed as " +
|
||||
|
@ -5546,11 +5564,18 @@ AddonInstall.prototype = {
|
|||
reason, extraParams);
|
||||
}
|
||||
else {
|
||||
// XXX this makes it dangerous to do many things in onInstallEnded
|
||||
// XXX this makes it dangerous to do some things in onInstallEnded
|
||||
// listeners because important cleanup hasn't been done yet
|
||||
XPIProvider.unloadBootstrapScope(this.addon.id);
|
||||
}
|
||||
}
|
||||
XPIProvider.setTelemetry(this.addon.id, "unpacked", installedUnpacked);
|
||||
XPIProvider.setTelemetry(this.addon.id, "location", this.installLocation.name);
|
||||
let loc = this.addon.defaultLocale;
|
||||
if (loc) {
|
||||
XPIProvider.setTelemetry(this.addon.id, "name", loc.name);
|
||||
XPIProvider.setTelemetry(this.addon.id, "creator", loc.creator);
|
||||
}
|
||||
}
|
||||
}).bind(this)).then(null, (e) => {
|
||||
WARN("Failed to install " + this.file.path + " from " + this.sourceURI.spec, e);
|
||||
|
|
Загрузка…
Ссылка в новой задаче