зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to s-c.
This commit is contained in:
Коммит
63b2c29f6e
1
.hgtags
1
.hgtags
|
@ -87,3 +87,4 @@ b6627f28b7ec17e1b46a594df0f780d3a40847e4 FIREFOX_AURORA_13_BASE
|
|||
9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
|
||||
6fdf9985acfe6f939da584b2559464ab22264fe7 FIREFOX_AURORA_16_BASE
|
||||
fd72dbbd692012224145be1bf13df1d7675fd277 FIREFOX_AURORA_17_BASE
|
||||
2704e441363fe2a48e992dfac694482dfd82664a FIREFOX_AURORA_18_BASE
|
||||
|
|
|
@ -46,7 +46,9 @@ function virtualCursorControl(aMessage) {
|
|||
moved = vc[details.action](rule);
|
||||
}
|
||||
} catch (x) {
|
||||
moved = vc.moveNext(rule, content.document.activeElement, true);
|
||||
let acc = Utils.AccRetrieval.
|
||||
getAccessibleFor(content.document.activeElement);
|
||||
moved = vc.moveNext(rule, acc, true);
|
||||
}
|
||||
break;
|
||||
case 'moveToPoint':
|
||||
|
@ -88,7 +90,7 @@ function forwardMessage(aVirtualCursor, aMessage) {
|
|||
return true;
|
||||
}
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
// Frame may be hidden, we regard this case as false.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
MOZ_APP_BASENAME=B2G
|
||||
MOZ_APP_VENDOR=Mozilla
|
||||
|
||||
MOZ_APP_VERSION=18.0a1
|
||||
MOZ_APP_VERSION=19.0a1
|
||||
MOZ_APP_UA_NAME=Firefox
|
||||
|
||||
MOZ_UA_OS_AGNOSTIC=1
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
resource testpilot ./
|
||||
content testpilot content/
|
||||
locale testpilot en-US locale/en-US/
|
||||
skin testpilot skin skin/all/
|
||||
skin testpilot-os skin skin/linux/ os=Linux
|
||||
skin testpilot-os skin skin/linux/ os=SunOS
|
||||
|
@ -10,6 +11,7 @@ overlay chrome://browser/content/macBrowserOverlay.xul chrome://testpilot/conten
|
|||
|
||||
overlay chrome://browser/content/browser.xul chrome://testpilot/content/browser.xul
|
||||
|
||||
style chrome://global/content/customizeToolbar.xul chrome://testpilot/content/browser.css
|
||||
# For the menubar on Mac
|
||||
overlay chrome://testpilot/content/all-studies-window.xul chrome://browser/content/macBrowserOverlay.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} os=Darwin
|
||||
|
||||
|
|
|
@ -23,9 +23,8 @@
|
|||
-moz-padding-start: 5px;
|
||||
}
|
||||
|
||||
|
||||
#pilot-notifications-button {
|
||||
margin-right: 10px;
|
||||
#tp-notification-popup-icon {
|
||||
list-style-image: url("chrome://testpilot/skin/testpilot_16x16.png");
|
||||
}
|
||||
|
||||
/* Popup Bounding Box */
|
||||
|
@ -36,25 +35,15 @@
|
|||
margin-top: -6px;
|
||||
margin-right: -3px;
|
||||
width: 480px;
|
||||
/* FIXES: #725850 based on #717262
|
||||
/* Needed whilst we support Gecko < 13 */
|
||||
border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 / 26px 56px 22px 18px round stretch;
|
||||
/* Supported in Gecko >= 13 */
|
||||
border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 50 22 18 fill stretch;
|
||||
border-width: 26px 56px 22px 18px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.tail-up {
|
||||
border-width: 26px 56px 22px 18px;
|
||||
border-style: solid;
|
||||
-moz-border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 fill round stretch;
|
||||
}
|
||||
|
||||
/* tail-down uses the old styling; it doesn't look as good as the new styling,
|
||||
but the new styling doesn't work on 3.6.
|
||||
TODO: If someone is using 3.7.* or 4.* but is NOT on the beta channel and
|
||||
installed Test Pilot from AMO, they should get the new styling, similar
|
||||
to .tail-up! */
|
||||
.tail-down {
|
||||
border-width: 26px 50px 22px 18px;
|
||||
border-style: solid;
|
||||
-moz-border-image: url(chrome://testpilot/skin/notification-tail-down.png) 26 50 22 18 fill repeat;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pilot-notification-popup-container {
|
||||
-moz-appearance: none;
|
||||
|
@ -67,9 +56,14 @@
|
|||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#pilot-notification-text,
|
||||
#pilot-notification-text {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#pilot-notification-link {
|
||||
margin-bottom: 5px;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pilot-notification-close {
|
||||
|
@ -240,11 +234,6 @@ image.results-thumbnail {
|
|||
margin: 10px;
|
||||
}
|
||||
|
||||
.notification-link {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
prefpane .groupbox-body {
|
||||
-moz-appearance: none;
|
||||
padding: 8px 4px 4px 4px;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://testpilot-os/skin/feedback.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE overlay [
|
||||
<!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<!-- 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/. -->
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html> <head>
|
||||
<title>Test Pilot Debug Page</title>
|
||||
<script type="text/javascript" src="jquery.min.js"></script>
|
||||
<script src="experiment-page.js" type="application/javascript;version=1.8"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
var numstartupattempts=0;
|
||||
function startup() {
|
||||
numstartupattempts += 1;
|
||||
dump('numattempts: ' + numstartupattempts + "\n");
|
||||
var dropsuccess = populateFileDropdown();
|
||||
if (! dropsuccess) {
|
||||
$('h1#status').fadeIn('slow','linear');
|
||||
window.setTimeout(function() {startup()}, 2000); // try again!
|
||||
} else {
|
||||
showSelectedTaskStatus()
|
||||
showIndexFileDropdown();
|
||||
$('h1#status').fadeOut("slow", "linear");
|
||||
};
|
||||
};
|
||||
|
||||
function getEid() {
|
||||
var selector = document.getElementById("task-selector");
|
||||
var i = selector.selectedIndex;
|
||||
|
@ -30,7 +45,7 @@
|
|||
if (errors.length > 0) {
|
||||
str = "<ul>";
|
||||
for each (let errStr in errors) {
|
||||
str += "<li>" + errStr + "</li>";
|
||||
str += "<li>" + errStr + "</li>";
|
||||
}
|
||||
str += "</ul>";
|
||||
} else {
|
||||
|
@ -62,7 +77,7 @@
|
|||
[jarStore, loader.fs])});
|
||||
dump("Debug page Requiring toolbar study.\n");
|
||||
var toolbarStudy = clientLoader.require("toolbar-study");
|
||||
|
||||
|
||||
}
|
||||
|
||||
function remindMe() {
|
||||
|
@ -74,6 +89,7 @@
|
|||
function getCodeStorage() {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
var loader = TestPilotSetup._remoteExperimentLoader;
|
||||
if (! loader) {return undefined;}
|
||||
return loader._jarStore;
|
||||
}
|
||||
|
||||
|
@ -136,6 +152,8 @@
|
|||
|
||||
function populateFileDropdown() {
|
||||
var codeStore = getCodeStorage();
|
||||
if (! codeStore ) { return false }
|
||||
|
||||
var files = codeStore.listAllFiles();
|
||||
var selector = document.getElementById("file-selector");
|
||||
var opt, i;
|
||||
|
@ -155,6 +173,7 @@
|
|||
opt.setAttribute("value", tasks[i].id);
|
||||
selector.appendChild(opt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function showSelectedTaskStatus() {
|
||||
|
@ -216,6 +235,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
$(function(){startup()});
|
||||
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -224,7 +246,8 @@
|
|||
|
||||
</head>
|
||||
|
||||
<body onload="populateFileDropdown();showSelectedTaskStatus();showIndexFileDropdown();">
|
||||
<body>
|
||||
<h1 id='status'><blink>loading experiments</blink></h1> <!-- take that, web standards! -->
|
||||
|
||||
<fieldset>
|
||||
<p><select id="task-selector" onchange="showSelectedTaskStatus();"></select> Current Status = <span id="show-status-span"></span>.
|
||||
|
@ -252,7 +275,7 @@ or set it to
|
|||
<button onclick="reloadAllExperiments();">Reload All Experiments</button>
|
||||
<button onclick="remindMe();">Notify Me</button>
|
||||
<button onclick="testJarStore();">Test Jar Store</button>
|
||||
Index file:
|
||||
Index file:
|
||||
<select id="index-file-selector" onchange="setSelectedIndexFile();">
|
||||
<option value="index.json">index.json</option>
|
||||
<option value="index-dev.json">index-dev.json</option>
|
||||
|
|
|
@ -158,7 +158,7 @@ var stringBundle;
|
|||
}
|
||||
|
||||
// write, create, truncate
|
||||
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
|
||||
foStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0664", 8), 0);
|
||||
converter.init(foStream, "UTF-8", 0, 0);
|
||||
converter.writeString(csvString);
|
||||
converter.close();
|
||||
|
|
|
@ -0,0 +1,428 @@
|
|||
/* 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/. */
|
||||
|
||||
const PAGE_TYPE_STATUS = 0;
|
||||
const PAGE_TYPE_QUIT = 1;
|
||||
var stringBundle;
|
||||
|
||||
function showRawData(experimentId) {
|
||||
window.openDialog(
|
||||
"chrome://testpilot/content/raw-data-dialog.xul",
|
||||
"TestPilotRawDataDialog", "chrome,centerscreen,resizable,scrollbars",
|
||||
experimentId);
|
||||
}
|
||||
|
||||
function getUrlParam(name) {
|
||||
// from http://www.netlobo.com/url_query_string_javascript.html
|
||||
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
|
||||
var regexS = "[\\?&]"+name+"=([^&#]*)";
|
||||
var regex = new RegExp(regexS);
|
||||
var results = regex.exec(window.location.href);
|
||||
if( results == null )
|
||||
return "";
|
||||
else
|
||||
return results[1];
|
||||
}
|
||||
|
||||
function uploadData() {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
let eid = getUrlParam("eid");
|
||||
let task = TestPilotSetup.getTaskById(eid);
|
||||
|
||||
// If always-submit-checkbox is checked, set the pref
|
||||
if (task._recursAutomatically) {
|
||||
let checkBox = document.getElementById("always-submit-checkbox");
|
||||
if (checkBox && checkBox.checked) {
|
||||
task.setRecurPref(TaskConstants.ALWAYS_SUBMIT);
|
||||
}
|
||||
}
|
||||
|
||||
// Study web content must provide an element with id 'upload-status'.
|
||||
// Fill it first with a message about data being uploaded; if there's
|
||||
// an error, replace it with the error message.
|
||||
let uploadStatus = document.getElementById("upload-status");
|
||||
uploadStatus.innerHTML =
|
||||
stringBundle.GetStringFromName("testpilot.statusPage.uploadingData");
|
||||
task.upload( function(success) {
|
||||
if (success) {
|
||||
window.location =
|
||||
"chrome://testpilot/content/status.html?eid=" + eid;
|
||||
} else {
|
||||
// Replace 'now uploading' message
|
||||
let errorParagraph = document.createElement("p");
|
||||
errorParagraph.innerHTML = stringBundle.GetStringFromName("testpilot.statusPage.uploadErrorMsg");
|
||||
let willRetryParagraph = document.createElement("p");
|
||||
willRetryParagraph.innerHTML = stringBundle.GetStringFromName("testpilot.statusPage.willRetry");
|
||||
uploadStatus.innerHTML = "";
|
||||
uploadStatus.appendChild(errorParagraph);
|
||||
uploadStatus.appendChild(willRetryParagraph);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteData() {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
Components.utils.import("resource://testpilot/modules/tasks.js");
|
||||
let eid = getUrlParam("eid");
|
||||
let task = TestPilotSetup.getTaskById(eid);
|
||||
task.dataStore.wipeAllData();
|
||||
// reload the URL after wiping all data.
|
||||
window.location = "chrome://testpilot/content/status.html?eid=" + eid;
|
||||
}
|
||||
|
||||
function saveCanvas(canvas) {
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
let fp = Components.classes["@mozilla.org/filepicker;1"].
|
||||
createInstance(nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == nsIFilePicker.returnOK ||
|
||||
aResult == nsIFilePicker.returnReplace) {
|
||||
const nsIWebBrowserPersist =
|
||||
Components.interfaces.nsIWebBrowserPersist;
|
||||
let file = fp.file;
|
||||
|
||||
// create a data url from the canvas and then create URIs of the
|
||||
// source and targets
|
||||
let io = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
let source = io.newURI(canvas.toDataURL("image/png"), "UTF8", null);
|
||||
let target = io.newFileURI(file);
|
||||
|
||||
// prepare to save the canvas data
|
||||
let persist = Components.classes[
|
||||
"@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
|
||||
createInstance(nsIWebBrowserPersist);
|
||||
persist.persistFlags = nsIWebBrowserPersist.
|
||||
PERSIST_FLAGS_REPLACE_EXISTING_FILES;
|
||||
persist.persistFlags |= nsIWebBrowserPersist.
|
||||
PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
||||
|
||||
// displays a download dialog (remove these 3 lines for silent download)
|
||||
let xfer = Components.classes["@mozilla.org/transfer;1"].
|
||||
createInstance(Components.interfaces.nsITransfer);
|
||||
xfer.init(source, target, "", null, null, null, persist, false);
|
||||
persist.progressListener = xfer;
|
||||
|
||||
// save the canvas data to the file
|
||||
persist.saveURI(source, null, null, null, null, file, null);
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, null, nsIFilePicker.modeSave);
|
||||
fp.appendFilters(nsIFilePicker.filterImages | nsIFilePicker.filterAll);
|
||||
fp.defaultString = "canvas.png";
|
||||
fp.open(fpCallback);
|
||||
}
|
||||
|
||||
function exportData() {
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
let eid = getUrlParam("eid");
|
||||
let task = TestPilotSetup.getTaskById(eid);
|
||||
let fp = Components.classes["@mozilla.org/filepicker;1"].
|
||||
createInstance(nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == nsIFilePicker.returnOK ||
|
||||
aResult == nsIFilePicker.returnReplace) {
|
||||
const nsIWebBrowserPersist =
|
||||
Components.interfaces.nsIWebBrowserPersist;
|
||||
let foStream =
|
||||
Components.classes["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Components.interfaces.nsIFileOutputStream);
|
||||
let converter =
|
||||
Components.classes["@mozilla.org/intl/converter-output-stream;1"].
|
||||
createInstance(Components.interfaces.nsIConverterOutputStream);
|
||||
let file = fp.file;
|
||||
let dataStore = task.dataStore;
|
||||
let columnNames = dataStore.getHumanReadableColumnNames();
|
||||
let propertyNames = dataStore.getPropertyNames();
|
||||
let csvString = "";
|
||||
|
||||
// titles
|
||||
for (let i = 0; i < columnNames.length; i++) {
|
||||
csvString += "\"" + columnNames[i] + "\",";
|
||||
}
|
||||
if (csvString.length > 0) {
|
||||
csvString = csvString.substring(0, (csvString.length - 1));
|
||||
csvString += "\n";
|
||||
}
|
||||
|
||||
dataStore.getAllDataAsJSON(true, function(rawData) {
|
||||
// data
|
||||
for (let i = 0; i < rawData.length; i++) {
|
||||
for (let j = 0; j < columnNames.length; j++) {
|
||||
csvString += "\"" + rawData[i][propertyNames[j]] + "\",";
|
||||
}
|
||||
csvString = csvString.substring(0, (csvString.length - 1));
|
||||
csvString += "\n";
|
||||
}
|
||||
|
||||
// write, create, truncate
|
||||
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
|
||||
converter.init(foStream, "UTF-8", 0, 0);
|
||||
converter.writeString(csvString);
|
||||
converter.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, null, nsIFilePicker.modeSave);
|
||||
fp.appendFilters(nsIFilePicker.filterImages | nsIFilePicker.filterAll);
|
||||
fp.defaultString = task.title + ".csv";
|
||||
fp.open(fpCallback);
|
||||
}
|
||||
|
||||
function openLink(url) {
|
||||
// open the link in the chromeless window
|
||||
let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
let recentWindow = wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
if (recentWindow) {
|
||||
recentWindow.TestPilotWindowUtils.openInTab(url);
|
||||
} else {
|
||||
window.open(url);
|
||||
}
|
||||
}
|
||||
|
||||
function getTestEndingDate(experimentId) {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
var task = TestPilotSetup.getTaskById(experimentId);
|
||||
var endDate = new Date(task.endDate);
|
||||
var diff = (endDate - Date.now());
|
||||
var span = document.getElementById("test-end-time");
|
||||
if (!span) {
|
||||
return;
|
||||
}
|
||||
if (diff < 0) {
|
||||
span.innerHTML =
|
||||
stringBundle.GetStringFromName("testpilot.statusPage.endedAlready");
|
||||
return;
|
||||
}
|
||||
var hours = diff / (60 * 60 * 1000);
|
||||
if (hours < 24) {
|
||||
span.innerHTML =
|
||||
stringBundle.formatStringFromName(
|
||||
"testpilot.statusPage.todayAt", [endDate.toLocaleTimeString()], 1);
|
||||
} else {
|
||||
span.innerHTML =
|
||||
stringBundle.formatStringFromName(
|
||||
"testpilot.statusPage.endOn", [endDate.toLocaleString()], 1);
|
||||
}
|
||||
}
|
||||
|
||||
function showMetaData() {
|
||||
Components.utils.import("resource://testpilot/modules/metadata.js");
|
||||
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
||||
MetadataCollector.getMetadata(function(md) {
|
||||
var mdLocale = document.getElementById("md-locale");
|
||||
if (mdLocale)
|
||||
mdLocale.innerHTML = md.location;
|
||||
var mdVersion = document.getElementById("md-version");
|
||||
if (mdVersion)
|
||||
mdVersion.innerHTML = md.version;
|
||||
var mdOs = document.getElementById("md-os");
|
||||
if (mdOs)
|
||||
mdOs.innerHTML = md.operatingSystem;
|
||||
var mdNumExt = document.getElementById("md-num-ext");
|
||||
if (mdNumExt) {
|
||||
// This computes the correctly localized singular or plural string
|
||||
// of the number of extensions, e.g. "1 extension", "2 extensions", etc.
|
||||
let str = stringBundle.GetStringFromName("testpilot.statusPage.numExtensions");
|
||||
var numExt = md.extensions.length;
|
||||
mdNumExt.innerHTML = PluralForm.get(numExt, str).replace("#1", numExt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onQuitPageLoad() {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
setStrings(PAGE_TYPE_QUIT);
|
||||
let eid = getUrlParam("eid");
|
||||
let task = TestPilotSetup.getTaskById(eid);
|
||||
let header = document.getElementById("about-quit-title");
|
||||
header.innerHTML =
|
||||
stringBundle.formatStringFromName(
|
||||
"testpilot.quitPage.aboutToQuit", [task.title], 1);
|
||||
|
||||
if (task._recursAutomatically) {
|
||||
document.getElementById("recur-options").setAttribute("style", "");
|
||||
document.getElementById("recur-checkbox-container").
|
||||
setAttribute("style", "");
|
||||
}
|
||||
}
|
||||
|
||||
function quitExperiment() {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
Components.utils.import("resource://testpilot/modules/tasks.js");
|
||||
let eid = getUrlParam("eid");
|
||||
let reason = document.getElementById("reason-for-quit").value;
|
||||
let task = TestPilotSetup.getTaskById(eid);
|
||||
task.optOut(reason, function(success) {
|
||||
// load the you-are-canceleed page.
|
||||
window.location = "chrome://testpilot/content/status.html?eid=" + eid;
|
||||
});
|
||||
|
||||
// If opt-out-forever checkbox is checked, opt out forever!
|
||||
if (task._recursAutomatically) {
|
||||
let checkBox = document.getElementById("opt-out-forever");
|
||||
if (checkBox.checked) {
|
||||
task.setRecurPref(TaskConstants.NEVER_SUBMIT);
|
||||
}
|
||||
// quit test so rescheduling
|
||||
task._reschedule();
|
||||
}
|
||||
}
|
||||
|
||||
function updateRecurSettings() {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
let eid = getUrlParam("eid");
|
||||
let experiment = TestPilotSetup.getTaskById(eid);
|
||||
let recurSelector = document.getElementById("recur-selector");
|
||||
let newValue = recurSelector.options[recurSelector.selectedIndex].value;
|
||||
experiment.setRecurPref(parseInt(newValue));
|
||||
}
|
||||
|
||||
function showRecurControls(experiment) {
|
||||
Components.utils.import("resource://testpilot/modules/tasks.js");
|
||||
let recurPrefSpan = document.getElementById("recur-pref");
|
||||
if (!recurPrefSpan) {
|
||||
return;
|
||||
}
|
||||
let days = experiment._recurrenceInterval;
|
||||
recurPrefSpan.innerHTML =
|
||||
stringBundle.formatStringFromName(
|
||||
"testpilot.statusPage.recursEveryNumberOfDays", [days], 1);
|
||||
|
||||
let controls = document.getElementById("recur-controls");
|
||||
let selector = document.createElement("select");
|
||||
controls.appendChild(selector);
|
||||
selector.setAttribute("onchange", "updateRecurSettings();");
|
||||
selector.setAttribute("id", "recur-selector");
|
||||
|
||||
let option = document.createElement("option");
|
||||
option.setAttribute("value", TaskConstants.ASK_EACH_TIME);
|
||||
if (experiment.recurPref == TaskConstants.ASK_EACH_TIME) {
|
||||
option.setAttribute("selected", "true");
|
||||
}
|
||||
option.innerHTML =
|
||||
stringBundle.GetStringFromName(
|
||||
"testpilot.statusPage.askMeBeforeSubmitData");
|
||||
selector.appendChild(option);
|
||||
|
||||
option = document.createElement("option");
|
||||
option.setAttribute("value", TaskConstants.ALWAYS_SUBMIT);
|
||||
if (experiment.recurPref == TaskConstants.ALWAYS_SUBMIT) {
|
||||
option.setAttribute("selected", "true");
|
||||
}
|
||||
option.innerHTML =
|
||||
stringBundle.GetStringFromName(
|
||||
"testpilot.statusPage.alwaysSubmitData");
|
||||
selector.appendChild(option);
|
||||
|
||||
option = document.createElement("option");
|
||||
option.setAttribute("value", TaskConstants.NEVER_SUBMIT);
|
||||
if (experiment.recurPref == TaskConstants.NEVER_SUBMIT) {
|
||||
option.setAttribute("selected", "true");
|
||||
}
|
||||
option.innerHTML =
|
||||
stringBundle.GetStringFromName(
|
||||
"testpilot.statusPage.neverSubmitData");
|
||||
selector.appendChild(option);
|
||||
}
|
||||
|
||||
function loadExperimentPage() {
|
||||
Components.utils.import("resource://testpilot/modules/setup.js");
|
||||
Components.utils.import("resource://testpilot/modules/tasks.js");
|
||||
var contentDiv = document.getElementById("experiment-specific-text");
|
||||
var dataPrivacyDiv = document.getElementById("data-privacy-text");
|
||||
// Get experimentID from the GET args of page
|
||||
var eid = getUrlParam("eid");
|
||||
var experiment = TestPilotSetup.getTaskById(eid);
|
||||
if (!experiment) {
|
||||
// Possible that experiments aren't done loading yet. Try again in
|
||||
// a few seconds.
|
||||
contentDiv.innerHTML =
|
||||
stringBundle.GetStringFromName("testpilot.statusPage.loading");
|
||||
window.setTimeout(function() { loadExperimentPage(); }, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the experiment fill in its web content (asynchronous)
|
||||
experiment.getWebContent(function(webContent) {
|
||||
contentDiv.innerHTML = webContent;
|
||||
|
||||
// Metadata and start/end date should be filled in for every experiment:
|
||||
showMetaData();
|
||||
getTestEndingDate(eid);
|
||||
if (experiment._recursAutomatically &&
|
||||
experiment.status != TaskConstants.STATUS_FINISHED) {
|
||||
showRecurControls(experiment);
|
||||
}
|
||||
|
||||
// Do whatever the experiment's web content wants done on load
|
||||
// (Usually drawing a graph) - must be done after innerHTML is set.
|
||||
experiment.webContent.onPageLoad(experiment, document, jQuery);
|
||||
});
|
||||
|
||||
experiment.getDataPrivacyContent(function(dataPrivacyContent) {
|
||||
if (dataPrivacyContent && dataPrivacyContent.length > 0) {
|
||||
dataPrivacyDiv.innerHTML = dataPrivacyContent;
|
||||
dataPrivacyDiv.removeAttribute("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onStatusPageLoad() {
|
||||
setStrings(PAGE_TYPE_STATUS);
|
||||
/* An experiment ID (eid) must be provided in the url params. Show status
|
||||
* for that experiment.*/
|
||||
loadExperimentPage();
|
||||
}
|
||||
|
||||
function setStrings(pageType) {
|
||||
stringBundle =
|
||||
Components.classes["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Components.interfaces.nsIStringBundleService).
|
||||
createBundle("chrome://testpilot/locale/main.properties");
|
||||
let map;
|
||||
let mapLength;
|
||||
|
||||
if (pageType == PAGE_TYPE_STATUS) {
|
||||
map = [
|
||||
{ id: "page-title", stringKey: "testpilot.fullBrandName" },
|
||||
{ id: "comments-and-discussions-link",
|
||||
stringKey: "testpilot.page.commentsAndDiscussions" },
|
||||
{ id: "propose-test-link",
|
||||
stringKey: "testpilot.page.proposeATest" },
|
||||
{ id: "testpilot-twitter-link",
|
||||
stringKey: "testpilot.page.testpilotOnTwitter" }
|
||||
];
|
||||
} else if (pageType == PAGE_TYPE_QUIT) {
|
||||
map = [
|
||||
{ id: "page-title", stringKey: "testpilot.fullBrandName" },
|
||||
{ id: "comments-and-discussions-link",
|
||||
stringKey: "testpilot.page.commentsAndDiscussions" },
|
||||
{ id: "propose-test-link",
|
||||
stringKey: "testpilot.page.proposeATest" },
|
||||
{ id: "testpilot-twitter-link",
|
||||
stringKey: "testpilot.page.testpilotOnTwitter" },
|
||||
{ id: "optional-message",
|
||||
stringKey: "testpilot.quitPage.optionalMessage" },
|
||||
{ id: "reason-text",
|
||||
stringKey: "testpilot.quitPage.reason" },
|
||||
{ id: "recur-options",
|
||||
stringKey: "testpilot.quitPage.recurringStudy" },
|
||||
{ id: "quit-forever-text",
|
||||
stringKey: "testpilot.quitPage.quitForever" },
|
||||
{ id: "quit-study-link",
|
||||
stringKey: "testpilot.quitPage.quitStudy" }
|
||||
];
|
||||
}
|
||||
mapLength = map.length;
|
||||
for (let i = 0; i < mapLength; i++) {
|
||||
let entry = map[i];
|
||||
document.getElementById(entry.id).innerHTML =
|
||||
stringBundle.GetStringFromName(entry.stringKey);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
--- content/experiment-page.js
|
||||
+++ content/experiment-page.js
|
||||
@@ -156,17 +156,17 @@
|
||||
for (let j = 0; j < columnNames.length; j++) {
|
||||
csvString += "\"" + rawData[i][propertyNames[j]] + "\",";
|
||||
}
|
||||
csvString = csvString.substring(0, (csvString.length - 1));
|
||||
csvString += "\n";
|
||||
}
|
||||
|
||||
// write, create, truncate
|
||||
- foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
|
||||
+ foStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0664", 8), 0);
|
||||
converter.init(foStream, "UTF-8", 0, 0);
|
||||
converter.writeString(csvString);
|
||||
converter.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function openLink(url) {
|
|
@ -3,9 +3,6 @@
|
|||
- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://testpilot-os/skin/feedback.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE overlay [
|
||||
<!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
|
||||
%testpilotDTD;
|
||||
|
|
2
browser/app/profile/extensions/testpilot@labs.mozilla.com/content/jquery.min.js
поставляемый
Normal file
2
browser/app/profile/extensions/testpilot@labs.mozilla.com/content/jquery.min.js
поставляемый
Normal file
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
|
||||
%notificationDTD;
|
||||
]>
|
||||
|
||||
<bindings id="popupBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="testpilot-notification"
|
||||
extends="chrome://global/content/bindings/notification.xml#popup-notification">
|
||||
|
||||
<!-- Remember to keep this up to date with the changes to base binding
|
||||
at http://hg.mozilla.org/mozilla-central/file/tip/toolkit/content/widgets/notification.xml-->
|
||||
<content align="start">
|
||||
<xul:image class="popup-notification-icon" anonid="notification-icon"
|
||||
xbl:inherits="popupid"/>
|
||||
<xul:vbox flex="1">
|
||||
<xul:label anonid="notification-title" class="testpilot-notification-title"/>
|
||||
<xul:description class="popup-notification-description"
|
||||
xbl:inherits="xbl:text=label"/>
|
||||
<xul:spacer flex="1"/>
|
||||
<xul:hbox class="popup-notification-button-container"
|
||||
pack="end" align="center">
|
||||
<xul:button anonid="button"
|
||||
class="popup-notification-menubutton"
|
||||
type="menu-button"
|
||||
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
|
||||
<xul:menupopup anonid="menupopup"
|
||||
xbl:inherits="oncommand=menucommand">
|
||||
<children/>
|
||||
<xul:menuitem class="menuitem-iconic popup-notification-closeitem"
|
||||
label="&closeNotificationItem.label;"
|
||||
xbl:inherits="oncommand=closeitemcommand"/>
|
||||
</xul:menupopup>
|
||||
</xul:button>
|
||||
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
<xul:vbox pack="start">
|
||||
<xul:toolbarbutton anonid="closebutton"
|
||||
class="messageCloseButton popup-notification-closebutton tabbable"
|
||||
xbl:inherits="oncommand=closebuttoncommand"
|
||||
tooltiptext="&closeNotification.tooltip;"/>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
this.title.value = this.notification.options.title;
|
||||
this.icon.classList.add(this.notification.options.iconClass);
|
||||
this.closebutton.addEventListener("command",
|
||||
this.notification.options.closeButtonFunc,
|
||||
false);
|
||||
]]>
|
||||
</constructor>
|
||||
<field name="title" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "notification-title");
|
||||
</field>
|
||||
<field name="icon" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "notification-icon");
|
||||
</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
|
@ -3,51 +3,40 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
html {
|
||||
padding-bottom: 0px;
|
||||
padding-bottom: 0px;
|
||||
padding-top: 20px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
body {
|
||||
body {
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif;
|
||||
font-family: 'DroidSans';
|
||||
background: #fff url('chrome://testpilot/skin/bg.jpg') repeat-x top center;
|
||||
padding: 0px;
|
||||
padding: 0px;
|
||||
color:#787878;
|
||||
font-size:12px;
|
||||
line-height: 18px;
|
||||
margin: 0 auto;
|
||||
|
||||
|
||||
}
|
||||
|
||||
h1 {font-family: 'DroidSans'; color: #3a3a3a; font-size: 28px; font-weight: normal; letter-spacing: -1px}
|
||||
h2 {font-family: 'DroidSans'; color: #54717b; font-size: 24px; line-height: 22px;font-weight: normal; letter-spacing: -1px}
|
||||
|
||||
h1 {font-family: 'sans-serif'; color: #3a3a3a; font-size: 28px; font-weight: normal; letter-spacing: -1px}
|
||||
h2 {font-family: 'sans-serif'; color: #54717b; font-size: 24px; line-height: 22px;font-weight: normal; letter-spacing: -1px}
|
||||
|
||||
p, ul {font-size: 12px;}
|
||||
|
||||
|
||||
a:link, a:hover, a:active, a:focus, a:visited {color: #9f423b; text-decoration: none;}
|
||||
a:hover {color: #9f423b; text-decoration: none;}
|
||||
|
||||
.bold {font-family: 'DroidSans-Bold'; color:#3a3a3a;}
|
||||
|
||||
.bold {color:#3a3a3a;}
|
||||
.address {margin-left: 20px;}
|
||||
.inactive {color:#ccc;}
|
||||
|
||||
|
||||
li {list-style-type: circle;}
|
||||
|
||||
.spacer {height: 75px;}
|
||||
|
||||
|
||||
@font-face{
|
||||
font-family: 'DroidSans';
|
||||
src: url('chrome://testpilot/skin/fonts/DroidSans.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face{
|
||||
font-family: 'DroidSans-Bold';
|
||||
src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: 0px auto;
|
||||
width: 950px;
|
||||
|
@ -61,14 +50,14 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
|||
#contentWelcome {
|
||||
margin-top: 120px;
|
||||
padding: 8px 24px 24px 24px;
|
||||
font-family: 'DroidSans';
|
||||
font-family: 'sans-serif';
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-top: 60px;
|
||||
padding: 8px 24px 24px 24px;
|
||||
font-family: 'DroidSans';
|
||||
font-family: 'sans-serif';
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
@ -78,16 +67,6 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
|||
margin-top: 240px;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
margin-bottom: -10px;
|
||||
margin-top: -2px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.downloadH1 {font-family: 'DroidSans-bold'; color: #3a3a3a; font-size: 24px; font-weight: normal; letter-spacing: -1px; margin-right: 8px;}
|
||||
|
||||
.downloadH2 {font-family: 'DroidSans'; color: #3a3a3a; font-size: 22px; font-weight: normal; letter-spacing: -1px; line-height: 28px; margin-left: 46px;}
|
||||
|
||||
#intro {
|
||||
float: left;
|
||||
width: 500px;
|
||||
|
@ -102,60 +81,60 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
|||
}
|
||||
|
||||
.button {
|
||||
font-family: 'DroidSans';
|
||||
font-family: 'sans-serif';
|
||||
font-size: 16px;
|
||||
padding: 8px 12px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
|
||||
inset rgba(255, 255, 255, 1) 0 3px 1px,
|
||||
inset rgba(255, 255, 255, 0.3) 0 16px 0px,
|
||||
inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
rgba(255, 255, 255, 1) 0 1px,
|
||||
rgba(133, 153, 166, 0.3) 0px 1px 12px;
|
||||
background-color: #e7eaec;
|
||||
//display: inline;
|
||||
/*display: inline;*/
|
||||
}
|
||||
|
||||
.home_button {
|
||||
font-family: 'DroidSans';
|
||||
font-family: 'sans-serif';
|
||||
font-size: 16px;
|
||||
padding: 8px 12px;
|
||||
width: 240px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
|
||||
inset rgba(255, 255, 255, 1) 0 3px 1px,
|
||||
inset rgba(255, 255, 255, 0.3) 0 16px 0px,
|
||||
inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
rgba(255, 255, 255, 1) 0 1px,
|
||||
rgba(133, 153, 166, 0.3) 0px 1px 12px;
|
||||
background-color: #e7eaec;
|
||||
//display: inline;
|
||||
/*display: inline;*/
|
||||
}
|
||||
|
||||
|
||||
.callout {
|
||||
font-family: 'DroidSans';
|
||||
font-family: 'sans-serif';
|
||||
font-size: 16px;
|
||||
padding: 8px 24px;
|
||||
margin: 24px auto;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/callout.png') no-repeat top center;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(185, 221, 234, 0.2) 0 -10px 12px,
|
||||
inset rgba(185, 221, 234, 1) 0 0px 1px,
|
||||
inset rgba(255, 255, 255, 0.2) 0 10px 12px;
|
||||
//display: inline;
|
||||
/*display: inline;*/
|
||||
}
|
||||
|
||||
.home_callout {
|
||||
font-family: 'DroidSans';
|
||||
font-family: 'sans-serif';
|
||||
font-size: 16px;
|
||||
vertical-align: middle;
|
||||
width: 240px;
|
||||
|
@ -164,11 +143,11 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
|||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/callout.png') no-repeat top center;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(185, 221, 234, 0.2) 0 -10px 12px,
|
||||
inset rgba(185, 221, 234, 1) 0 0px 1px,
|
||||
inset rgba(255, 255, 255, 0.2) 0 10px 12px;
|
||||
//display: inline;
|
||||
/*display: inline;*/
|
||||
}
|
||||
|
||||
.homeIcon {
|
||||
|
@ -189,7 +168,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
|||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* ------- MENU -------
|
||||
/* ------- MENU -------
|
||||
|
||||
#menu {
|
||||
margin: 20px auto;
|
||||
|
@ -204,7 +183,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
|||
border-bottom: 3px solid #adb6ba;
|
||||
-moz-border-bottom-colors:#adb6ba #e7eaec #e7eaec;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
@ -222,7 +201,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
|
|||
font-size: 14px;
|
||||
text-shadow: 1px 1px 1px rgba(173, 182, 186, 1);
|
||||
background-color: rgba(173, 182, 186, 0.3);
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(0, 0, 0, 0.2) 0 -10px 12px;
|
||||
padding: 9px 8px 8px 8px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE overlay [
|
||||
<!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
|
||||
%testpilotDTD;
|
||||
]>
|
||||
|
||||
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<statusbar id="status-bar">
|
||||
<statusbarpanel id="pilot-notifications-button"
|
||||
class="statusbarpanel-iconic"
|
||||
insertbefore="security-button"
|
||||
onmousedown="
|
||||
event.preventDefault();
|
||||
TestPilotMenuUtils.onMenuButtonMouseDown();"
|
||||
image="chrome://testpilot/skin/testpilot_16x16.png"/>
|
||||
|
||||
<panel id="pilot-notification-popup" hidden="true" noautofocus="true"
|
||||
level="parent" position="after_start">
|
||||
<vbox class="pilot-notification-popup-container">
|
||||
<hbox class="pilot-notification-toprow">
|
||||
<image id="pilot-notification-icon" />
|
||||
<vbox pack="center">
|
||||
<label id="pilot-notification-title" class="pilot-title" />
|
||||
</vbox>
|
||||
<spacer flex="1" />
|
||||
<vbox pack="start">
|
||||
<image id="pilot-notification-close"
|
||||
tooltiptext="&testpilot.notification.close.tooltip;" />
|
||||
</vbox>
|
||||
</hbox>
|
||||
<description id="pilot-notification-text" />
|
||||
<hbox align="right"><label id="pilot-notification-link" /></hbox>
|
||||
<hbox>
|
||||
<checkbox id="pilot-notification-always-submit-checkbox"
|
||||
label="&testpilot.settings.alwaysSubmitData.shortLabel;" />
|
||||
<spacer flex="1" />
|
||||
</hbox>
|
||||
<hbox align="right">
|
||||
<button id="pilot-notification-submit" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
</panel>
|
||||
</statusbar>
|
||||
</overlay>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE overlay [
|
||||
<!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
|
||||
%testpilotDTD;
|
||||
]>
|
||||
|
||||
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<!-- anchor for the fx4 style notifications -->
|
||||
<toolbar id="nav-bar">
|
||||
<box id="tp-notification-popup-box" hidden="true" align="center">
|
||||
<image id="tp-notification-popup-icon" class="notification-anchor-icon" role="button"/>
|
||||
</box>
|
||||
<panel id="testpilot-notification-popup" type="arrow" position="after_start"
|
||||
hidden="true" orient="vertical" role="alert">
|
||||
</panel>
|
||||
</toolbar>
|
||||
</overlay>
|
|
@ -54,15 +54,8 @@
|
|||
</menupopup>
|
||||
</menu>
|
||||
|
||||
<statusbar id="status-bar">
|
||||
<statusbarpanel id="pilot-notifications-button"
|
||||
class="statusbarpanel-iconic"
|
||||
insertbefore="security-button"
|
||||
onmousedown="
|
||||
event.preventDefault();
|
||||
TestPilotMenuUtils.onMenuButtonMouseDown();"
|
||||
image="chrome://testpilot/skin/testpilot_16x16.png"/>
|
||||
|
||||
<toolbar id="nav-bar">
|
||||
<image id="tp-notification-popup-icon" role="button" hidden="true"/>
|
||||
<panel id="pilot-notification-popup" hidden="true" noautofocus="true"
|
||||
level="parent" position="after_start">
|
||||
<vbox class="pilot-notification-popup-container">
|
||||
|
@ -78,7 +71,7 @@
|
|||
</vbox>
|
||||
</hbox>
|
||||
<description id="pilot-notification-text" />
|
||||
<hbox align="right"><label id="pilot-notification-link" /></hbox>
|
||||
<hbox align="right"><label id="pilot-notification-link"/></hbox>
|
||||
<hbox>
|
||||
<checkbox id="pilot-notification-always-submit-checkbox"
|
||||
label="&testpilot.settings.alwaysSubmitData.label;" />
|
||||
|
@ -89,5 +82,5 @@
|
|||
</hbox>
|
||||
</vbox>
|
||||
</panel>
|
||||
</statusbar>
|
||||
</toolbar>
|
||||
</overlay>
|
||||
|
|
|
@ -49,11 +49,11 @@ var TestPilotWelcomePage = {
|
|||
{ id: "testpilot-addon-text",
|
||||
stringKey: "testpilot.welcomePage.testpilotAddon" },
|
||||
{ id: "icon-explanation-text",
|
||||
stringKey: "testpilot.welcomePage.iconExplanation" },
|
||||
stringKey: "testpilot.welcomePage.iconExplanation2" },
|
||||
{ id: "icon-explanation-more-text",
|
||||
stringKey: "testpilot.welcomePage.moreIconExplanation" },
|
||||
stringKey: "testpilot.welcomePage.moreIconExplanation2" },
|
||||
{ id: "notification-info-text",
|
||||
stringKey: "testpilot.welcomePage.notificationInfo" },
|
||||
stringKey: "testpilot.welcomePage.notificationInfo2" },
|
||||
{ id: "privacy-policy-link",
|
||||
stringKey: "testpilot.welcomePage.privacyPolicy" },
|
||||
{ id: "legal-notices-link",
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>testpilot@labs.mozilla.com</em:id>
|
||||
<em:version>1.1.2</em:version>
|
||||
<em:version>1.2.2</em:version>
|
||||
<em:type>2</em:type>
|
||||
|
||||
<!-- Target Application this extension can install into,
|
||||
with minimum and maximum supported versions. -->
|
||||
<!-- Target Application this extension can install into,
|
||||
with minimum and maximum supported versions. -->
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>Feedback</em:name>
|
||||
<em:description>Help make Firefox better by giving feedback.</em:description>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<!ENTITY testpilot.brand.label "Test Pilot">
|
||||
<!ENTITY testpilot.settings.label "Settings">
|
||||
<!ENTITY testpilot.settings.dataSubmission.label "Data Submission">
|
||||
<!ENTITY testpilot.settings.notifications.label "Notifications">
|
||||
<!ENTITY testpilot.settings.notifyWhen.label "Notify me when…">
|
||||
<!ENTITY testpilot.settings.readyToSubmit.label "A study is ready to submit">
|
||||
<!ENTITY testpilot.settings.newStudy.label "There's a new study">
|
||||
<!ENTITY testpilot.settings.hasNewResults.label "A study has new results">
|
||||
<!ENTITY testpilot.settings.alwaysSubmitData.label "Automatically submit my data (don't ask me)">
|
||||
<!ENTITY testpilot.allYourStudies.label "All Your User Studies…">
|
||||
<!ENTITY testpilot.about.label "About Test Pilot">
|
||||
|
||||
<!-- all studies window -->
|
||||
<!ENTITY testpilot.studiesWindow.title "Your Test Pilot Studies">
|
||||
<!ENTITY testpilot.studiesWindow.currentStudies.label "Current Studies">
|
||||
<!ENTITY testpilot.studiesWindow.finishedStudies.label "Finished Studies">
|
||||
<!ENTITY testpilot.studiesWindow.studyFindings.label "Study Findings">
|
||||
<!ENTITY testpilot.studiesWindow.settings.label "Settings">
|
||||
<!ENTITY testpilot.studiesWindow.stillLoadingMessage "Loading, please wait…">
|
||||
|
||||
<!-- raw data dialog -->
|
||||
<!ENTITY testpilot.rawDataWindow.title "Test Pilot: Raw Data">
|
||||
|
||||
<!-- notification -->
|
||||
<!ENTITY testpilot.notification.close.tooltip "Close">
|
||||
|
||||
<!-- Firefox 4 beta version UI -->
|
||||
<!ENTITY testpilot.enable.label "Turn On User Studies">
|
||||
<!ENTITY testpilot.feedbackbutton.label "Feedback">
|
||||
<!ENTITY testpilot.happy.label "Firefox Made Me Happy Because…">
|
||||
<!ENTITY testpilot.sad.label "Firefox Made Me Sad Because…">
|
||||
<!ENTITY testpilot.broken.label "Report this website as broken…">
|
||||
<!ENTITY testpilot.idea.label "Give us a suggestion…">
|
|
@ -0,0 +1,98 @@
|
|||
# description for add-on manager
|
||||
extensions.testpilot@labs.mozilla.com.description = Help make Firefox better by running user studies.
|
||||
|
||||
# common
|
||||
testpilot.fullBrandName = Mozilla Labs Test Pilot
|
||||
testpilot.moreInfo = More Info
|
||||
testpilot.submit = Submit
|
||||
testpilot.takeSurvey = Take the Survey
|
||||
|
||||
# Feedback button menu
|
||||
testpilot.turnOn = Turn On User Studies
|
||||
testpilot.turnOff = Turn Off User Studies
|
||||
|
||||
# studies window
|
||||
testpilot.studiesWindow.noStudies = We are working on a new study now; it will knock on your door soon! Stay Tuned!
|
||||
testpilot.studiesWindow.uploading = Uploading…
|
||||
testpilot.studiesWindow.unableToReachServer = Unable to reach Mozilla; please try again later.
|
||||
testpilot.studiesWindow.thanksForContributing = Thanks for contributing!
|
||||
testpilot.studiesWindow.finishedOn = Finished on %S
|
||||
testpilot.studiesWindow.canceledStudy = (You canceled this study)
|
||||
testpilot.studiesWindow.missedStudy = (You missed this study)
|
||||
testpilot.studiesWindow.willStart = Will start on %S
|
||||
testpilot.studiesWindow.gatheringData = Currently gathering data.
|
||||
testpilot.studiesWindow.willFinish = Will finish on %S
|
||||
testpilot.studiesWindow.proposeStudy = Propose your own study
|
||||
|
||||
# for pages
|
||||
testpilot.page.commentsAndDiscussions = Comments & Discussions »
|
||||
testpilot.page.proposeATest = Propose a Test »
|
||||
testpilot.page.testpilotOnTwitter = @MozTestPilot on Twitter »
|
||||
|
||||
# status page
|
||||
testpilot.statusPage.uploadingData = Now uploading data…
|
||||
testpilot.statusPage.uploadErrorMsg = Oops! There was an error connecting to the Mozilla servers. Maybe your network connection is down?
|
||||
testpilot.statusPage.willRetry = Test Pilot will retry automatically, so it's OK to close this page now.
|
||||
testpilot.statusPage.endedAlready = (It has already ended and you should not be seeing this page)
|
||||
testpilot.statusPage.todayAt = today, at %S
|
||||
testpilot.statusPage.endOn = on %S
|
||||
# LOCALIZATION NOTE (numExtensions): Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# #1 = number of extensions
|
||||
# example: "2 extensions"
|
||||
testpilot.statusPage.numExtensions = #1 extension;#1 extensions
|
||||
testpilot.statusPage.recursEveryNumberOfDays = This test recurs every %S days. Each time it completes:
|
||||
testpilot.statusPage.askMeBeforeSubmitData = Ask me whether I want to submit my data.
|
||||
testpilot.statusPage.alwaysSubmitData = Always submit my data, and don't ask me about it.
|
||||
testpilot.statusPage.neverSubmitData = Never submit my data, and don't ask me about it.
|
||||
testpilot.statusPage.loading = Loading, please wait a moment…
|
||||
|
||||
# quit page
|
||||
testpilot.quitPage.aboutToQuit = You are about to quit the "%S" study.
|
||||
testpilot.quitPage.optionalMessage = (Optional) If you have a minute, please let us know why you have chosen to quit the study.
|
||||
testpilot.quitPage.reason = Reason:
|
||||
testpilot.quitPage.recurringStudy = This is a recurring study. Normally we will let you know the next time we run the study. If you never want to hear about this study again, check the box below:
|
||||
testpilot.quitPage.quitForever = Quit this recurring study.
|
||||
testpilot.quitPage.quitStudy = Quit the Study »
|
||||
|
||||
# welcome page
|
||||
testpilot.welcomePage.thankYou = Thank You for Joining the Test Pilot Team!
|
||||
testpilot.welcomePage.gettingStarted = Getting Started
|
||||
testpilot.welcomePage.pleaseTake = Please take the
|
||||
testpilot.welcomePage.backgroundSurvey = Pilot Background Survey
|
||||
testpilot.welcomePage.clickToOpenStudiesWindow = Click here to see the studies that are currently running.
|
||||
testpilot.welcomePage.testpilotAddon = Test Pilot Add-on
|
||||
testpilot.welcomePage.iconExplanation2 = « look for this icon in the Tools menu to find the Test Pilot sub-menu.
|
||||
testpilot.welcomePage.moreIconExplanation2 = Use the sub-menu to control settings and see what studies are running.
|
||||
testpilot.welcomePage.notificationInfo2 = Test Pilot will pop up a notification when a study needs your attention.
|
||||
testpilot.welcomePage.privacyPolicy = Privacy Policy
|
||||
testpilot.welcomePage.legalNotices = Legal Notices
|
||||
|
||||
# survey page
|
||||
testpilot.surveyPage.saveAnswers = Save Answers
|
||||
testpilot.surveyPage.submitAnswers = Submit Answers
|
||||
testpilot.surveyPage.changeAnswers = Change Answers
|
||||
testpilot.surveyPage.loading = Loading, please wait a moment…
|
||||
testpilot.surveyPage.thankYouForFinishingSurvey = Thank you for finishing this survey. Your answers will be uploaded along with the next set of experimental data.
|
||||
testpilot.surveyPage.reviewOrChangeYourAnswers = If you would like to review or change your answers, you can do so at any time using the button below.
|
||||
|
||||
# modules/task.js
|
||||
testpilot.finishedTask.finishedStudy = Excellent! You finished the "%S" Study!
|
||||
testpilot.finishedTask.allRelatedDataDeleted = All data related to this study has been removed from your computer.
|
||||
|
||||
# modules/setup.js
|
||||
testpilot.notification.update = Update…
|
||||
testpilot.notification.thankYouForUploadingData = Thanks!
|
||||
testpilot.notification.thankYouForUploadingData.message = Thank you for uploading your data.
|
||||
testpilot.notification.readyToSubmit = Ready to Submit
|
||||
testpilot.notification.readyToSubmit.message = The Test Pilot "%S" study is finished gathering data and is ready to submit.
|
||||
testpilot.notification.newTestPilotStudy = New Test Pilot Study
|
||||
testpilot.notification.newTestPilotStudy.pre.message = The Test Pilot "%S" study is about to begin.
|
||||
testpilot.notification.newTestPilotSurvey = New Test Pilot Survey
|
||||
testpilot.notification.newTestPilotSurvey.message = %S
|
||||
testpilot.notification.newTestPilotResults = New Test Pilot Results
|
||||
testpilot.notification.newTestPilotResults.message = New results are now available for the Test Pilot "%S" study.
|
||||
testpilot.notification.autoUploadedData = Thank you!
|
||||
testpilot.notification.autoUploadedData.message = The Test Pilot "%S" study is completed and your data has been submitted!
|
||||
testpilot.notification.extensionUpdate = Extension Update
|
||||
testpilot.notification.extensionUpdate.message = One of your studies requires a newer version of Test Pilot. You can get the latest version using the Add-ons window.
|
|
@ -15,14 +15,34 @@ EXPORTED_SYMBOLS = ["TestPilotUIBuilder"];
|
|||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const UPDATE_CHANNEL_PREF = "app.update.channel";
|
||||
const POPUP_SHOW_ON_NEW = "extensions.testpilot.popup.showOnNewStudy";
|
||||
const POPUP_CHECK_INTERVAL = "extensions.testpilot.popup.delayAfterStartup";
|
||||
|
||||
var TestPilotUIBuilder = {
|
||||
__prefs: null,
|
||||
get _prefs() {
|
||||
this.__prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
delete this._prefs;
|
||||
return this._prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
return this.__prefs;
|
||||
},
|
||||
|
||||
get _prefDefaultBranch() {
|
||||
delete this._prefDefaultBranch;
|
||||
return this._prefDefaultBranch = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService).getDefaultBranch("");
|
||||
},
|
||||
|
||||
get _comparator() {
|
||||
delete this._comparator;
|
||||
return this._comparator = Cc["@mozilla.org/xpcom/version-comparator;1"]
|
||||
.getService(Ci.nsIVersionComparator);
|
||||
},
|
||||
|
||||
get _appVersion() {
|
||||
delete this._appVersion;
|
||||
return this._appVersion = Cc["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Ci.nsIXULAppInfo).version;
|
||||
},
|
||||
|
||||
buildTestPilotInterface: function(window) {
|
||||
|
@ -34,6 +54,12 @@ var TestPilotUIBuilder = {
|
|||
feedbackButton = palette.getElementsByAttribute("id", "feedback-menu-button").item(0);
|
||||
}
|
||||
feedbackButton.parentNode.removeChild(feedbackButton);
|
||||
|
||||
/* Default prefs for test pilot version - default to NOT notifying user about new
|
||||
* studies starting. Note we're setting default values, not current values -- we
|
||||
* want these to be overridden by any user set values!!*/
|
||||
this._prefDefaultBranch.setBoolPref(POPUP_SHOW_ON_NEW, false);
|
||||
this._prefDefaultBranch.setIntPref(POPUP_CHECK_INTERVAL, 180000);
|
||||
},
|
||||
|
||||
buildFeedbackInterface: function(window) {
|
||||
|
@ -62,23 +88,24 @@ var TestPilotUIBuilder = {
|
|||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Pref defaults for Feedback version: default to notifying user about new
|
||||
* studies starting. Note we're setting default values, not current values -- we
|
||||
* want these to be overridden by any user set values!!*/
|
||||
this._prefDefaultBranch.setBoolPref(POPUP_SHOW_ON_NEW, true);
|
||||
this._prefDefaultBranch.setIntPref(POPUP_CHECK_INTERVAL, 600000);
|
||||
},
|
||||
|
||||
isBetaChannel: function() {
|
||||
channelUsesFeedback: function() {
|
||||
// Beta and aurora channels use feedback interface; nightly and release channels don't.
|
||||
let channel = this._prefs.getCharPref(UPDATE_CHANNEL_PREF);
|
||||
let channel = this._prefDefaultBranch.getCharPref(UPDATE_CHANNEL_PREF);
|
||||
return (channel == "beta") || (channel == "betatest") || (channel == "aurora");
|
||||
},
|
||||
|
||||
appVersionIsFinal: function() {
|
||||
// Return true iff app version >= 4.0 AND there is no "beta" or "rc" in version string.
|
||||
let appInfo = Cc["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Ci.nsIXULAppInfo);
|
||||
let version = appInfo.version;
|
||||
let versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
|
||||
.getService(Components.interfaces.nsIVersionComparator);
|
||||
if (versionChecker.compare(version, "4.0") >= 0) {
|
||||
if (version.indexOf("b") == -1 && version.indexOf("rc") == -1) {
|
||||
if (this._comparator.compare(this._appVersion, "4.0") >= 0) {
|
||||
if (this._appVersion.indexOf("b") == -1 && this._appVersion.indexOf("rc") == -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -93,23 +120,14 @@ var TestPilotUIBuilder = {
|
|||
return;
|
||||
}
|
||||
|
||||
/* Overlay Feedback XUL if we're in the beta update channel, Test Pilot XUL otherwise.
|
||||
* Once the overlay is complete, call buildFeedbackInterface() or buildTestPilotInterface(). */
|
||||
let self = this;
|
||||
if (this.isBetaChannel()) {
|
||||
window.document.loadOverlay("chrome://testpilot/content/feedback-browser.xul",
|
||||
{observe: function(subject, topic, data) {
|
||||
if (topic == "xul-overlay-merged") {
|
||||
self.buildFeedbackInterface(window);
|
||||
}
|
||||
}});
|
||||
/* Overlay Feedback XUL if we're in the beta update channel, Test Pilot XUL otherwise, and
|
||||
* call buildFeedbackInterface() or buildTestPilotInterface(). */
|
||||
if (this.channelUsesFeedback()) {
|
||||
window.document.loadOverlay("chrome://testpilot/content/feedback-browser.xul", null);
|
||||
this.buildFeedbackInterface(window);
|
||||
} else {
|
||||
window.document.loadOverlay("chrome://testpilot/content/tp-browser.xul",
|
||||
{observe: function(subject, topic, data) {
|
||||
if (topic == "xul-overlay-merged") {
|
||||
self.buildTestPilotInterface(window);
|
||||
}
|
||||
}});
|
||||
window.document.loadOverlay("chrome://testpilot/content/tp-browser.xul", null);
|
||||
this.buildTestPilotInterface(window);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -71,7 +71,7 @@ JarStore.prototype = {
|
|||
let istream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
// open for reading
|
||||
istream.init(jarFile, 0x01, 0444, 0);
|
||||
istream.init(jarFile, 0x01, parseInt("0444", 8), 0);
|
||||
let ch = Cc["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Ci.nsICryptoHash);
|
||||
// Use SHA256, it's more secure than MD5:
|
||||
|
@ -112,7 +112,7 @@ JarStore.prototype = {
|
|||
jarFile.create( Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
|
||||
let stream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(jarFile, 0x04 | 0x08 | 0x20, 0600, 0); // readwrite, create, truncate
|
||||
stream.init(jarFile, 0x04 | 0x08 | 0x20, parseInt("0600", 8), 0); // readwrite, create, truncate
|
||||
stream.write(rawData, rawData.length);
|
||||
if (stream instanceof Ci.nsISafeOutputStream) {
|
||||
stream.finish();
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/* 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/. */
|
||||
|
||||
(function(global) {
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
var exports = {};
|
||||
|
||||
var dirsvc = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties);
|
||||
|
||||
function MozFile(path) {
|
||||
var file = Cc['@mozilla.org/file/local;1']
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(path);
|
||||
return {
|
||||
get directoryEntries() {
|
||||
try {
|
||||
return file.directoryEntries;
|
||||
} catch (e if e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
|
||||
throw new Error("path does not exist: " + file.path);
|
||||
}
|
||||
},
|
||||
__proto__: file
|
||||
};
|
||||
}
|
||||
|
||||
exports.join = function join(base) {
|
||||
if (arguments.length < 2)
|
||||
throw new Error("need at least 2 args");
|
||||
base = MozFile(base);
|
||||
for (var i = 1; i < arguments.length; i++)
|
||||
base.append(arguments[i]);
|
||||
return base.path;
|
||||
};
|
||||
|
||||
exports.dirname = function dirname(path) {
|
||||
return MozFile(path).parent.path;
|
||||
};
|
||||
|
||||
exports.list = function list(path) {
|
||||
var entries = MozFile(path).directoryEntries;
|
||||
var entryNames = [];
|
||||
while(entries.hasMoreElements()) {
|
||||
var entry = entries.getNext();
|
||||
entry.QueryInterface(Ci.nsIFile);
|
||||
entryNames.push(entry.leafName);
|
||||
}
|
||||
return entryNames;
|
||||
};
|
||||
|
||||
if (global.window) {
|
||||
// We're being loaded in a chrome window, or a web page with
|
||||
// UniversalXPConnect privileges.
|
||||
global.File = exports;
|
||||
} else if (global.exports) {
|
||||
// We're being loaded in a SecurableModule.
|
||||
for (name in exports) {
|
||||
global.exports[name] = exports[name];
|
||||
}
|
||||
} else {
|
||||
// We're being loaded in a JS module.
|
||||
global.EXPORTED_SYMBOLS = [];
|
||||
for (name in exports) {
|
||||
global.EXPORTED_SYMBOLS.push(name);
|
||||
global[name] = exports[name];
|
||||
}
|
||||
}
|
||||
})(this);
|
|
@ -88,7 +88,7 @@
|
|||
|
||||
exports.SandboxFactory.prototype = {
|
||||
createSandbox: function createSandbox(options) {
|
||||
var principal = resolvePrincipal(options.principal,
|
||||
var principal = resolvePrincipal('b' in options ? options.b : undefined,
|
||||
this._defaultPrincipal);
|
||||
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
/* 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/. */
|
||||
|
||||
/* The TestPilotSetup object will choose one of these implementations to instantiate.
|
||||
* The interface for all NotificationManager implementations is:
|
||||
showNotification: function(window, features, choices) {},
|
||||
hideNotification: function(window) {}
|
||||
*/
|
||||
|
||||
EXPORTED_SYMBOLS = ["CustomNotificationManager", "PopupNotificationManager"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
/* CustomNotificationManager: the one where notifications
|
||||
* come up from the Test Pilot icon in the addon bar. For Firefox 3.6. */
|
||||
function CustomNotificationManager(anchorId, tailIsUp) {
|
||||
this._anchorId = anchorId;
|
||||
this._tailIsUp = tailIsUp;
|
||||
}
|
||||
CustomNotificationManager.prototype = {
|
||||
showNotification: function TP_OldNotfn_showNotification(window, features, choices) {
|
||||
let doc = window.document;
|
||||
let popup = doc.getElementById("pilot-notification-popup");
|
||||
let textLabel = doc.getElementById("pilot-notification-text");
|
||||
let titleLabel = doc.getElementById("pilot-notification-title");
|
||||
let icon = doc.getElementById("pilot-notification-icon");
|
||||
let button = doc.getElementById("pilot-notification-submit");
|
||||
let closeBtn = doc.getElementById("pilot-notification-close");
|
||||
let link = doc.getElementById("pilot-notification-link");
|
||||
let checkbox = doc.getElementById("pilot-notification-always-submit-checkbox");
|
||||
let self = this;
|
||||
let buttonChoice = null;
|
||||
let linkChoice = null;
|
||||
let checkBoxChoice = null;
|
||||
|
||||
if (this._tailIsUp) {
|
||||
popup.setAttribute("class", "tail-up");
|
||||
} else {
|
||||
popup.setAttribute("class", "tail-down");
|
||||
}
|
||||
|
||||
popup.setAttribute("noautohide", !(features.fragile));
|
||||
if (features.title) {
|
||||
titleLabel.setAttribute("value", features.title);
|
||||
}
|
||||
while (textLabel.lastChild) {
|
||||
textLabel.removeChild(textLabel.lastChild);
|
||||
}
|
||||
if (features.text) {
|
||||
textLabel.appendChild(doc.createTextNode(features.text));
|
||||
}
|
||||
if (features.iconClass) {
|
||||
// css will set the image url based on the class.
|
||||
icon.setAttribute("class", features.iconClass);
|
||||
}
|
||||
|
||||
/* Go through the specified choices and figure out which one to turn into a link, which one
|
||||
* (if any) to turn into a button, and which one (if any) to turn into a check box. */
|
||||
for (let i = 0; i < choices.length; i++) {
|
||||
switch(choices[i].customUiType) {
|
||||
case "button":
|
||||
buttonChoice = choices[i];
|
||||
break;
|
||||
case "link":
|
||||
linkChoice = choices[i];
|
||||
break;
|
||||
case "checkbox":
|
||||
checkBoxChoice = choices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Create check box if specified:
|
||||
if (checkBoxChoice) {
|
||||
checkbox.removeAttribute("hidden");
|
||||
checkbox.setAttribute("label", checkBoxChoice.label);
|
||||
} else {
|
||||
checkbox.setAttribute("hidden", true);
|
||||
}
|
||||
|
||||
// Create button if specified:
|
||||
if (buttonChoice) {
|
||||
button.setAttribute("label", buttonChoice.label);
|
||||
button.onclick = function(event) {
|
||||
if (event.button == 0) {
|
||||
if (checkbox.checked && checkBoxChoice) {
|
||||
checkBoxChoice.callback();
|
||||
}
|
||||
buttonChoice.callback();
|
||||
self.hideNotification(window);
|
||||
if (features.closeCallback) {
|
||||
features.closeCallback();
|
||||
}
|
||||
}
|
||||
};
|
||||
button.removeAttribute("hidden");
|
||||
} else {
|
||||
button.setAttribute("hidden", true);
|
||||
}
|
||||
|
||||
// Create the link if specified:
|
||||
if (linkChoice) {
|
||||
link.setAttribute("value", linkChoice.label);
|
||||
link.setAttribute("class", "notification-link");
|
||||
link.onclick = function(event) {
|
||||
if (event.button == 0) {
|
||||
linkChoice.callback();
|
||||
self.hideNotification(window);
|
||||
if (features.closeCallback) {
|
||||
features.closeCallback();
|
||||
}
|
||||
}
|
||||
};
|
||||
link.removeAttribute("hidden");
|
||||
} else {
|
||||
link.setAttribute("hidden", true);
|
||||
}
|
||||
|
||||
closeBtn.onclick = function() {
|
||||
self.hideNotification(window);
|
||||
if (features.closeCallback) {
|
||||
features.closeCallback();
|
||||
}
|
||||
};
|
||||
|
||||
// Show the popup:
|
||||
popup.hidden = false;
|
||||
popup.setAttribute("open", "true");
|
||||
let anchorElement = window.document.getElementById(this._anchorId);
|
||||
popup.openPopup(anchorElement, "after_end");
|
||||
},
|
||||
|
||||
hideNotification: function TP_OldNotfn_hideNotification(window) {
|
||||
let popup = window.document.getElementById("pilot-notification-popup");
|
||||
popup.removeAttribute("open");
|
||||
popup.hidePopup();
|
||||
}
|
||||
};
|
||||
|
||||
// For Fx 4.0 + , uses the built-in doorhanger notification system (but with my own anchor icon)
|
||||
function PopupNotificationManager() {
|
||||
/* In the future, we may want to anchor these to the Feedback button if present,
|
||||
* but for now that option is unimplemented. */
|
||||
this._popupModule = {};
|
||||
Components.utils.import("resource://gre/modules/PopupNotifications.jsm", this._popupModule);
|
||||
this._pn = null;
|
||||
}
|
||||
PopupNotificationManager.prototype = {
|
||||
showNotification: function TP_NewNotfn_showNotification(window, features, choices) {
|
||||
let self = this;
|
||||
let tabbrowser = window.getBrowser();
|
||||
let panel = window.document.getElementById("testpilot-notification-popup");
|
||||
let iconBox = window.document.getElementById("tp-notification-popup-box");
|
||||
let defaultChoice = null;
|
||||
let additionalChoices = [];
|
||||
|
||||
// hide any existing notification so we don't get a weird stack
|
||||
this.hideNotification();
|
||||
|
||||
// Create notifications object for window
|
||||
this._pn = new this._popupModule.PopupNotifications(tabbrowser, panel, iconBox);
|
||||
|
||||
/* Add hideNotification() calls to the callbacks of each choice -- the client code shouldn't
|
||||
* have to worry about hiding the notification in its callbacks.*/
|
||||
for (let i = 0; i < choices.length; i++) {
|
||||
let choice = choices[i];
|
||||
let choiceWithHide = {
|
||||
label: choice.label,
|
||||
accessKey: choice.accessKey,
|
||||
callback: function() {
|
||||
self.hideNotification();
|
||||
choice.callback();
|
||||
}};
|
||||
// Take the first one to be the default choice:
|
||||
if (i == 0) {
|
||||
defaultChoice = choiceWithHide;
|
||||
} else {
|
||||
additionalChoices.push(choiceWithHide);
|
||||
}
|
||||
}
|
||||
|
||||
this._notifRef = this._pn.show(tabbrowser.selectedBrowser,
|
||||
"testpilot",
|
||||
features.text,
|
||||
"tp-notification-popup-icon", // All TP notifications use this icon
|
||||
defaultChoice,
|
||||
additionalChoices,
|
||||
{persistWhileVisible: true,
|
||||
removeOnDismissal: features.fragile,
|
||||
title: features.title,
|
||||
iconClass: features.iconClass,
|
||||
closeButtonFunc: function() {
|
||||
self.hideNotification();
|
||||
},
|
||||
eventCallback: function(stateChange){
|
||||
/* Note - closeCallback() will be called AFTER the button handler,
|
||||
* and will be called no matter whether the notification is closed via
|
||||
* close button or a menu button item.
|
||||
*/
|
||||
if (stateChange == "removed" && features.closeCallback) {
|
||||
features.closeCallback();
|
||||
}
|
||||
self._notifRef = null;
|
||||
}});
|
||||
// See http://mxr.mozilla.org/mozilla-central/source/toolkit/content/PopupNotifications.jsm
|
||||
},
|
||||
|
||||
hideNotification: function TP_NewNotfn_hideNotification() {
|
||||
if (this._notifRef && this._pn) {
|
||||
this._pn.remove(this._notifRef);
|
||||
this._notifRef = null;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -3,10 +3,13 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const BASE_URL_PREF = "extensions.testpilot.indexBaseURL";
|
||||
const SSL_DOWNLOAD_REQUIRED_PREF = "extensions.testpilot.ssldownloadrequired";
|
||||
|
||||
var Cuddlefish = require("cuddlefish");
|
||||
var resolveUrl = require("url").resolve;
|
||||
var SecurableModule = require("securable-module");
|
||||
let JarStore = require("jar-code-store").JarStore;
|
||||
let prefs = require("preferences-service");
|
||||
|
||||
/* Security info should look like this:
|
||||
* Security Info:
|
||||
|
@ -100,11 +103,16 @@ function downloadFile(url, cb, lastModified) {
|
|||
console.info("Using binary mode to download jar file.");
|
||||
req.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
}
|
||||
req.addEventListener("readystatechange", function(aEvt) {
|
||||
req.onreadystatechange = function(aEvt) {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200) {
|
||||
// check security channel:
|
||||
if (verifyChannelSecurity(req.channel)) {
|
||||
// check security channel, unless the user is ignoring that.
|
||||
let ssldownloadrequired= prefs.get(SSL_DOWNLOAD_REQUIRED_PREF,true);
|
||||
if (!ssldownloadrequired) {
|
||||
dump("not requiring ssl download for experiements. use at your own risk!\n");
|
||||
dump("change this with: " + SSL_DOWNLOAD_REQUIRED_PREF + "\n");
|
||||
}
|
||||
if (!ssldownloadrequired | verifyChannelSecurity(req.channel)) {
|
||||
cb(req.responseText);
|
||||
} else {
|
||||
cb(null);
|
||||
|
@ -122,7 +130,7 @@ function downloadFile(url, cb, lastModified) {
|
|||
cb(null);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
};
|
||||
req.send();
|
||||
}
|
||||
|
||||
|
@ -377,7 +385,7 @@ exports.RemoteExperimentLoader.prototype = {
|
|||
let foStream = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
|
||||
foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
|
||||
foStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0666", 8), 0);
|
||||
// write, create, truncate
|
||||
let converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
|
||||
createInstance(Ci.nsIConverterOutputStream);
|
||||
|
|
|
@ -24,6 +24,8 @@ const UPDATE_CHANNEL_PREF = "app.update.channel";
|
|||
const LOG_FILE_NAME = "TestPilotErrorLog.log";
|
||||
const RANDOM_DEPLOY_PREFIX = "extensions.testpilot.deploymentRandomizer";
|
||||
|
||||
Cu.import("resource://testpilot/modules/interface.js");
|
||||
|
||||
let TestPilotSetup = {
|
||||
didReminderAfterStartup: false,
|
||||
startupComplete: false,
|
||||
|
@ -151,30 +153,6 @@ let TestPilotSetup = {
|
|||
return this.__obs;
|
||||
},
|
||||
|
||||
_isBetaChannel: function TPS__isBetaChannel() {
|
||||
// Beta and aurora channels use feedback interface; nightly and release channels don't.
|
||||
let channel = this._prefs.getValue(UPDATE_CHANNEL_PREF, "");
|
||||
return (channel == "beta") || (channel == "betatest") || (channel == "aurora");
|
||||
},
|
||||
|
||||
_setPrefDefaultsForVersion: function TPS__setPrefDefaultsForVersion() {
|
||||
/* A couple of preferences need different default values depending on
|
||||
* whether we're in the Firefox 4 beta version or the standalone TP version
|
||||
*/
|
||||
let ps = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService);
|
||||
let prefBranch = ps.getDefaultBranch("");
|
||||
/* note we're setting default values, not current values -- these
|
||||
* get overridden by any user set values. */
|
||||
if (this._isBetaChannel()) {
|
||||
prefBranch.setBoolPref(POPUP_SHOW_ON_NEW, true);
|
||||
prefBranch.setIntPref(POPUP_CHECK_INTERVAL, 600000);
|
||||
} else {
|
||||
prefBranch.setBoolPref(POPUP_SHOW_ON_NEW, false);
|
||||
prefBranch.setIntPref(POPUP_CHECK_INTERVAL, 180000);
|
||||
}
|
||||
},
|
||||
|
||||
globalStartup: function TPS__doGlobalSetup() {
|
||||
// Only ever run this stuff ONCE, on the first window restore.
|
||||
// Should get called by the Test Pilot component.
|
||||
|
@ -182,7 +160,6 @@ let TestPilotSetup = {
|
|||
logger.trace("TestPilotSetup.globalStartup was called.");
|
||||
|
||||
try {
|
||||
this._setPrefDefaultsForVersion();
|
||||
if (!this._prefs.getValue(RUN_AT_ALL_PREF, true)) {
|
||||
logger.trace("Test Pilot globally disabled: Not starting up.");
|
||||
return;
|
||||
|
@ -222,7 +199,7 @@ let TestPilotSetup = {
|
|||
/* Show first run page (in front window) only the first time after install;
|
||||
* Don't show first run page in Feedback UI version. */
|
||||
if ((self._prefs.getValue(VERSION_PREF, "") == "") &&
|
||||
(!self._interfaceBuilder.channelUsesFeedback())) {
|
||||
(!TestPilotUIBuilder.channelUsesFeedback())) {
|
||||
self._prefs.setValue(VERSION_PREF, self.version);
|
||||
let browser = self._getFrontBrowserWindow().getBrowser();
|
||||
let url = self._prefs.getValue(FIRST_RUN_PREF, "");
|
||||
|
@ -343,16 +320,21 @@ let TestPilotSetup = {
|
|||
let doc = window.document;
|
||||
let popup = doc.getElementById("pilot-notification-popup");
|
||||
|
||||
let anchor;
|
||||
if (this._isBetaChannel()) {
|
||||
/* If we're in the Ffx4Beta version, popups come down from feedback
|
||||
* button, but if we're in the standalone extension version, they
|
||||
* come up from status bar icon. */
|
||||
let anchor, xOffset;
|
||||
if (TestPilotUIBuilder.channelUsesFeedback()) {
|
||||
/* If we're in the Ffx4Beta version, popups hang down from the feedback
|
||||
* button. In the standalone extension version, they hang down from
|
||||
* a temporary Test Pilot icon that appears in the toolbar. */
|
||||
anchor = doc.getElementById("feedback-menu-button");
|
||||
popup.setAttribute("class", "tail-up");
|
||||
xOffset = 0;
|
||||
} else {
|
||||
anchor = doc.getElementById("pilot-notifications-button");
|
||||
popup.setAttribute("class", "tail-down");
|
||||
anchor = doc.getElementById("tp-notification-popup-icon");
|
||||
anchor.hidden = false;
|
||||
/* By default, right edge of notification will line up with right edge of anchor.
|
||||
* That looks fine for feedback button, but the test pilot icon is narrower and
|
||||
* this alignment causes the pointy part of the arrow to miss the icon. Fix this
|
||||
* by shifting the notification to the right 24 pixels. */
|
||||
xOffset = 24;
|
||||
}
|
||||
let textLabel = doc.getElementById("pilot-notification-text");
|
||||
let titleLabel = doc.getElementById("pilot-notification-title");
|
||||
|
@ -421,7 +403,6 @@ let TestPilotSetup = {
|
|||
// Create the link if specified:
|
||||
if (linkText && (linkUrl || task)) {
|
||||
link.setAttribute("value", linkText);
|
||||
link.setAttribute("class", "notification-link");
|
||||
link.onclick = function(event) {
|
||||
if (event.button == 0) {
|
||||
if (task) {
|
||||
|
@ -444,7 +425,7 @@ let TestPilotSetup = {
|
|||
// Show the popup:
|
||||
popup.hidden = false;
|
||||
popup.setAttribute("open", "true");
|
||||
popup.openPopup( anchor, "after_end");
|
||||
popup.openPopup( anchor, "after_end", xOffset, 0);
|
||||
},
|
||||
|
||||
_openChromeless: function TPS__openChromeless(url) {
|
||||
|
@ -462,6 +443,12 @@ let TestPilotSetup = {
|
|||
popup.setAttribute("open", "false");
|
||||
popup.removeAttribute("tpisextensionupdate");
|
||||
popup.hidePopup();
|
||||
|
||||
if (!TestPilotUIBuilder.channelUsesFeedback()) {
|
||||
// If we're using the temporary test pilot icon as an anchor, hide it now
|
||||
let icon = window.document.getElementById("tp-notification-popup-icon");
|
||||
icon.hidden = true;
|
||||
}
|
||||
if (onCloseCallback) {
|
||||
onCloseCallback();
|
||||
}
|
||||
|
@ -487,7 +474,7 @@ let TestPilotSetup = {
|
|||
|
||||
// Highest priority is if there is a finished test (needs a decision)
|
||||
if (this._prefs.getValue(POPUP_SHOW_ON_FINISH, false)) {
|
||||
for (i = 0; i < this.taskList.length; i++) {
|
||||
for (let i = 0; i < this.taskList.length; i++) {
|
||||
task = this.taskList[i];
|
||||
if (task.status == TaskConstants.STATUS_FINISHED) {
|
||||
if (!this._prefs.getValue(ALWAYS_SUBMIT_DATA, false)) {
|
||||
|
@ -512,7 +499,7 @@ let TestPilotSetup = {
|
|||
// If there's no finished test, next highest priority is new tests that
|
||||
// are starting...
|
||||
if (this._prefs.getValue(POPUP_SHOW_ON_NEW, false)) {
|
||||
for (i = 0; i < this.taskList.length; i++) {
|
||||
for (let i = 0; i < this.taskList.length; i++) {
|
||||
task = this.taskList[i];
|
||||
if (task.status == TaskConstants.STATUS_PENDING ||
|
||||
task.status == TaskConstants.STATUS_NEW) {
|
||||
|
@ -538,11 +525,12 @@ let TestPilotSetup = {
|
|||
task, false,
|
||||
this._stringBundle.formatStringFromName(
|
||||
"testpilot.notification.newTestPilotSurvey.message",
|
||||
[task.title], 1),
|
||||
// in task.js summary falls back to title if undefined or empty, but we double make sure :)
|
||||
[task.summary || task.title],1),
|
||||
this._stringBundle.GetStringFromName(
|
||||
"testpilot.notification.newTestPilotSurvey"),
|
||||
"new-study", false, false,
|
||||
this._stringBundle.GetStringFromName("testpilot.moreInfo"),
|
||||
this._stringBundle.GetStringFromName("testpilot.takeSurvey"),
|
||||
task.defaultUrl);
|
||||
task.changeStatus(TaskConstants.STATUS_IN_PROGRESS, true);
|
||||
return;
|
||||
|
@ -553,7 +541,7 @@ let TestPilotSetup = {
|
|||
|
||||
// And finally, new experiment results:
|
||||
if (this._prefs.getValue(POPUP_SHOW_ON_RESULTS, false)) {
|
||||
for (i = 0; i < this.taskList.length; i++) {
|
||||
for (let i = 0; i < this.taskList.length; i++) {
|
||||
task = this.taskList[i];
|
||||
if (task.taskType == TaskConstants.TYPE_RESULTS &&
|
||||
task.status == TaskConstants.STATUS_NEW) {
|
||||
|
@ -645,15 +633,17 @@ let TestPilotSetup = {
|
|||
}
|
||||
},
|
||||
|
||||
_experimentRequirementsAreMet: function TPS__requirementsMet(experiment) {
|
||||
/* Returns true if we we meet the requirements to run this experiment
|
||||
* (e.g. meet the minimum Test Pilot version and Firefox version)
|
||||
* false if not.
|
||||
* Default is always to run the study - return true UNLESS the study
|
||||
* specifies a requirement that we don't meet. */
|
||||
_checkExperimentRequirements: function TPS__requirementsMet(experiment, callback) {
|
||||
/* Async.
|
||||
* Calls callback with a true if we we meet the requirements to run this
|
||||
* experiment (e.g. meet the minimum Test Pilot version and Firefox version)
|
||||
* calls callback with a false if not.
|
||||
* All of the requirements that a study can specify - firefox version, test pilot
|
||||
* version, filter function etc - default to true if not provided. callback(true)
|
||||
* UNLESS the study specifies a requirement that we don't meet. */
|
||||
let logger = this._logger;
|
||||
try {
|
||||
let minTpVer, minFxVer, expName, runOrNotFunc, randomDeployment;
|
||||
let minTpVer, minFxVer, expName, filterFunc, randomDeployment;
|
||||
/* Could be an experiment, which specifies experimentInfo, or survey,
|
||||
* which specifies surveyInfo. */
|
||||
let info = experiment.experimentInfo ?
|
||||
|
@ -662,12 +652,13 @@ let TestPilotSetup = {
|
|||
if (!info) {
|
||||
// If neither one is supplied, study lacks metadata required to run
|
||||
logger.warn("Study lacks minimum metadata to run.");
|
||||
return false;
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
minTpVer = info.minTPVersion;
|
||||
minFxVer = info.minFXVersion;
|
||||
expName = info.testName;
|
||||
runOrNotFunc = info.runOrNotFunc;
|
||||
filterFunc = info.filter;
|
||||
randomDeployment = info.randomDeployment;
|
||||
|
||||
// Minimum test pilot version:
|
||||
|
@ -685,44 +676,54 @@ let TestPilotSetup = {
|
|||
"testpilot.notification.extensionUpdate"),
|
||||
"update-extension", true, false, "", "", true);
|
||||
}
|
||||
return false;
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Minimum firefox version:
|
||||
if (minFxVer && this._isNewerThanFirefox(minFxVer)) {
|
||||
logger.warn("Not loading " + expName);
|
||||
logger.warn("Because it requires Firefox version " + minFxVer);
|
||||
return false;
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Random deployment (used to give study to random subsample of users)
|
||||
if (randomDeployment) {
|
||||
/* Roll a hundred-sided die. Remember what we roll for later reference. A study
|
||||
/* Roll a 100*uniform_random. Remember what we roll for later reference or reuse. A study
|
||||
* using random subsample deployment will provide a range (say, 0 ~ 30) which means
|
||||
* only users who roll within that range will run the study. */
|
||||
* only users who roll not outside that range (inclusive) will run the study.
|
||||
* ie., [0,1.5] -> 1.5% prob
|
||||
*/
|
||||
let prefName = RANDOM_DEPLOY_PREFIX + "." + randomDeployment.rolloutCode;
|
||||
let myRoll = this._prefs.getValue(prefName, null);
|
||||
if (myRoll == null) {
|
||||
myRoll = Math.floor(Math.random()*100);
|
||||
this._prefs.setValue(prefName, myRoll);
|
||||
myRoll = Math.random()*100;
|
||||
this._prefs.setValue(prefName, JSON.stringify(myRoll));
|
||||
} else {
|
||||
myRoll = Number(myRoll); // cast it
|
||||
}
|
||||
if (myRoll < randomDeployment.minRoll) {
|
||||
return false;
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
if (myRoll > randomDeployment.maxRoll) {
|
||||
return false;
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* The all-purpose, arbitrary code "Should this study run?" function - if
|
||||
* provided, use its return value. */
|
||||
if (runOrNotFunc) {
|
||||
return runOrNotFunc();
|
||||
* provided, use it. (filterFunc must be asynchronous too!)*/
|
||||
if (filterFunc) {
|
||||
filterFunc(callback);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn("Error in requirements check " + e);
|
||||
callback(false); // if something went wrong, err on the side of not running the study
|
||||
}
|
||||
return true;
|
||||
callback(true);
|
||||
},
|
||||
|
||||
checkForTasks: function TPS_checkForTasks(callback) {
|
||||
|
@ -743,65 +744,71 @@ let TestPilotSetup = {
|
|||
// downloading new contents or not...
|
||||
let experiments = self._remoteExperimentLoader.getExperiments();
|
||||
|
||||
let numExperimentsProcessed = 0;
|
||||
let numExperiments = Object.keys(experiments).length;
|
||||
for (let filename in experiments) {
|
||||
if (!self._experimentRequirementsAreMet(experiments[filename])) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// The try-catch ensures that if something goes wrong in loading one
|
||||
// experiment, the other experiments after that one still get loaded.
|
||||
logger.trace("Attempting to load experiment " + filename);
|
||||
|
||||
let task;
|
||||
// Could be a survey: check if surveyInfo is exported:
|
||||
if (experiments[filename].surveyInfo != undefined) {
|
||||
let sInfo = experiments[filename].surveyInfo;
|
||||
// If it supplies questions, it's a built-in survey.
|
||||
// If not, it's a web-based survey.
|
||||
if (!sInfo.surveyQuestions) {
|
||||
task = new self._taskModule.TestPilotWebSurvey(sInfo);
|
||||
} else {
|
||||
task = new self._taskModule.TestPilotBuiltinSurvey(sInfo);
|
||||
self._checkExperimentRequirements(experiments[filename], function(requirementsMet) {
|
||||
if (requirementsMet) {
|
||||
try {
|
||||
/* The try-catch ensures that if something goes wrong in loading one
|
||||
* experiment, the other experiments after that one still get loaded. */
|
||||
logger.trace("Attempting to load experiment " + filename);
|
||||
let task;
|
||||
// Could be a survey: check if surveyInfo is exported:
|
||||
if (experiments[filename].surveyInfo != undefined) {
|
||||
let sInfo = experiments[filename].surveyInfo;
|
||||
// If it supplies questions, it's a built-in survey.
|
||||
// If not, it's a web-based survey.
|
||||
if (!sInfo.surveyQuestions) {
|
||||
task = new self._taskModule.TestPilotWebSurvey(sInfo);
|
||||
} else {
|
||||
task = new self._taskModule.TestPilotBuiltinSurvey(sInfo);
|
||||
}
|
||||
} else {
|
||||
// This one must be an experiment.
|
||||
let expInfo = experiments[filename].experimentInfo;
|
||||
let dsInfo = experiments[filename].dataStoreInfo;
|
||||
let dataStore = new self._dataStoreModule.ExperimentDataStore(
|
||||
dsInfo.fileName, dsInfo.tableName, dsInfo.columns );
|
||||
let webContent = experiments[filename].webContent;
|
||||
task = new self._taskModule.TestPilotExperiment(expInfo,
|
||||
dataStore,
|
||||
experiments[filename].handlers,
|
||||
webContent);
|
||||
}
|
||||
self.addTask(task);
|
||||
logger.info("Loaded task " + filename);
|
||||
} catch (e) {
|
||||
logger.warn("Failed to load task " + filename + ": " + e);
|
||||
}
|
||||
} else {
|
||||
// This one must be an experiment.
|
||||
let expInfo = experiments[filename].experimentInfo;
|
||||
let dsInfo = experiments[filename].dataStoreInfo;
|
||||
let dataStore = new self._dataStoreModule.ExperimentDataStore(
|
||||
dsInfo.fileName, dsInfo.tableName, dsInfo.columns );
|
||||
let webContent = experiments[filename].webContent;
|
||||
task = new self._taskModule.TestPilotExperiment(expInfo,
|
||||
dataStore,
|
||||
experiments[filename].handlers,
|
||||
webContent);
|
||||
}
|
||||
self.addTask(task);
|
||||
logger.info("Loaded task " + filename);
|
||||
} catch (e) {
|
||||
logger.warn("Failed to load task " + filename + ": " + e);
|
||||
}
|
||||
} // end if requirements met
|
||||
// whether loading succeeded or failed, we're done processing that one; increment the count:
|
||||
numExperimentsProcessed ++;
|
||||
if (numExperimentsProcessed == numExperiments) {
|
||||
// all done with experiments -- do results and legacy studies:
|
||||
let results = self._remoteExperimentLoader.getStudyResults();
|
||||
for (let r in results) {
|
||||
let studyResult = new self._taskModule.TestPilotStudyResults(results[r]);
|
||||
self.addTask(studyResult);
|
||||
}
|
||||
|
||||
/* Legacy studies = stuff we no longer have the code for, but
|
||||
* if the user participated in it we want to keep that metadata. */
|
||||
let legacyStudies = self._remoteExperimentLoader.getLegacyStudies();
|
||||
for (let l in legacyStudies) {
|
||||
let legacyStudy = new self._taskModule.TestPilotLegacyStudy(legacyStudies[l]);
|
||||
self.addTask(legacyStudy);
|
||||
}
|
||||
|
||||
// Finally, call the callback if there is one
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
} // end of if all experiments are processed
|
||||
}); // end of call to check experiment requirements
|
||||
} // end for filename in experiments
|
||||
|
||||
// Handling new results is much simpler:
|
||||
let results = self._remoteExperimentLoader.getStudyResults();
|
||||
for (let r in results) {
|
||||
let studyResult = new self._taskModule.TestPilotStudyResults(results[r]);
|
||||
self.addTask(studyResult);
|
||||
}
|
||||
|
||||
/* Legacy studies = stuff we no longer have the code for, but
|
||||
* if the user participated in it we want to keep that metadata. */
|
||||
let legacyStudies = self._remoteExperimentLoader.getLegacyStudies();
|
||||
for (let l in legacyStudies) {
|
||||
let legacyStudy = new self._taskModule.TestPilotLegacyStudy(legacyStudies[l]);
|
||||
self.addTask(legacyStudy);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
); // end of call to checkForUpdates
|
||||
},
|
||||
|
||||
reloadRemoteExperiments: function TPS_reloadRemoteExperiments(callback) {
|
||||
|
|
|
@ -813,7 +813,7 @@ TestPilotExperiment.prototype = {
|
|||
req.setRequestHeader("Content-type", "application/json");
|
||||
req.setRequestHeader("Content-length", dataString.length);
|
||||
req.setRequestHeader("Connection", "close");
|
||||
req.addEventListener("readystatechange", function(aEvt) {
|
||||
req.onreadystatechange = function(aEvt) {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200 || req.status == 201 || req.status == 202) {
|
||||
let location = req.getResponseHeader("Location");
|
||||
|
@ -854,7 +854,7 @@ TestPilotExperiment.prototype = {
|
|||
callback(false);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
};
|
||||
req.send(dataString);
|
||||
});
|
||||
},
|
||||
|
@ -886,7 +886,7 @@ TestPilotExperiment.prototype = {
|
|||
req.setRequestHeader("Content-type", "application/json");
|
||||
req.setRequestHeader("Content-length", dataString.length);
|
||||
req.setRequestHeader("Connection", "close");
|
||||
req.addEventListener("readystatechange", function(aEvt) {
|
||||
req.onreadystatechange = function(aEvt) {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200 || req.status == 201 || req.status == 202) {
|
||||
logger.info("Quit reason posted successfully " + req.responseText);
|
||||
|
@ -900,7 +900,7 @@ TestPilotExperiment.prototype = {
|
|||
}
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
};
|
||||
logger.trace("Sending quit reason.");
|
||||
req.send(dataString);
|
||||
} else {
|
||||
|
@ -1036,7 +1036,7 @@ TestPilotBuiltinSurvey.prototype = {
|
|||
req.setRequestHeader("Content-type", "application/json");
|
||||
req.setRequestHeader("Content-length", params.length);
|
||||
req.setRequestHeader("Connection", "close");
|
||||
req.addEventListener("readystatechange", function(aEvt) {
|
||||
req.onreadystatechange = function(aEvt) {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200 || req.status == 201 ||
|
||||
req.status == 202) {
|
||||
|
@ -1066,7 +1066,7 @@ TestPilotBuiltinSurvey.prototype = {
|
|||
callback(false);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
};
|
||||
req.send(params);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,43 +3,43 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
html {
|
||||
padding-bottom: 40px;
|
||||
padding-bottom: 40px;
|
||||
padding-top: 20px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
body {
|
||||
body {
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif;
|
||||
background: #fff url('chrome://testpilot/skin/images/bg-status.jpg') repeat-x top center; padding: 0px;
|
||||
background: #fff url('chrome://testpilot/skin/images/bg-status.jpg') repeat-x top center; padding: 0px;
|
||||
color:#787878;
|
||||
font-size:12px;
|
||||
line-height: 18px;
|
||||
margin: 0 auto;
|
||||
|
||||
|
||||
}
|
||||
|
||||
h1 {color: #3a3a3a; font-size: 28px; font-weight: normal; letter-spacing: -1px}
|
||||
h2 {color: #54717b; font-size: 24px; line-height: 24px;font-weight: normal; letter-spacing: -1px}
|
||||
h3 {color: #54717b; font-size: 18px; line-height: 18px;font-weight: normal; letter-spacing: -1px}
|
||||
|
||||
|
||||
p, ul {font-size: 12px;}
|
||||
|
||||
|
||||
a:link, a:hover, a:active, a:focus, a:visited {color: #9f423b; text-decoration: none;}
|
||||
a:hover {color: #9f423b; text-decoration: none;}
|
||||
|
||||
|
||||
.bold {color:#3a3a3a;}
|
||||
.address {margin-left: 20px;}
|
||||
.inactive {color:#ccc;}
|
||||
|
||||
|
||||
li {list-style-type: circle;}
|
||||
|
||||
.spacer {height: 40px;}
|
||||
|
||||
|
||||
.center {text-align: center;}
|
||||
|
||||
.data {
|
||||
|
||||
|
||||
background-color: #fff;
|
||||
margin-bottom: 6px;
|
||||
border: 1px solid rgba(133, 153, 166, 0.2);
|
||||
|
@ -48,11 +48,9 @@ body {
|
|||
padding: 6px;
|
||||
box-shadow:
|
||||
rgba(133, 153, 166, 0.4) 0px 1px 17px;
|
||||
-webkit-box-shadow:
|
||||
rgba(133, 153, 166, 0.4) 0px 1px 24px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
.dataBox {
|
||||
font-size: 16px;
|
||||
padding: 6px 20px 20px 20px;
|
||||
|
@ -89,16 +87,6 @@ body {
|
|||
margin-top: 240px;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
margin-bottom: -10px;
|
||||
margin-top: -2px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.downloadH1 {color: #3a3a3a; font-size: 24px; font-weight: normal; letter-spacing: -1px; margin-right: 8px;}
|
||||
|
||||
.downloadH2 {color: #3a3a3a; font-size: 22px; font-weight: normal; letter-spacing: -1px; line-height: 28px; margin-left: 46px;}
|
||||
|
||||
#intro {
|
||||
float: left;
|
||||
width: 500px;
|
||||
|
@ -117,12 +105,12 @@ body {
|
|||
padding: 8px 12px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
|
||||
inset rgba(255, 255, 255, 1) 0 3px 1px,
|
||||
inset rgba(255, 255, 255, 0.3) 0 16px 0px,
|
||||
inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
rgba(255, 255, 255, 1) 0 1px,
|
||||
rgba(133, 153, 166, 0.3) 0px 1px 8.5px;
|
||||
background-color: #e7eaec;
|
||||
|
@ -135,12 +123,12 @@ body {
|
|||
width: 240px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
|
||||
inset rgba(255, 255, 255, 1) 0 3px 1px,
|
||||
inset rgba(255, 255, 255, 0.3) 0 16px 0px,
|
||||
inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
|
||||
rgba(255, 255, 255, 1) 0 1px,
|
||||
rgba(133, 153, 166, 0.3) 0px 1px 8.5px;
|
||||
background-color: #e7eaec;
|
||||
|
@ -154,7 +142,7 @@ body {
|
|||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
|
||||
inset rgba(185, 221, 234, 1) 0 0px 1px,
|
||||
inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
|
||||
|
@ -175,7 +163,7 @@ body {
|
|||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
|
||||
inset rgba(185, 221, 234, 1) 0 0px 1px,
|
||||
inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
|
||||
|
@ -191,7 +179,7 @@ body {
|
|||
color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5em;
|
||||
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout_continue.png') no-repeat top center;
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
|
||||
inset rgba(185, 221, 234, 1) 0 0px 1px,
|
||||
inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
|
||||
|
@ -227,7 +215,7 @@ body {
|
|||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* ------- MENU -------
|
||||
/* ------- MENU -------
|
||||
|
||||
#menu {
|
||||
margin: 20px auto;
|
||||
|
@ -242,7 +230,7 @@ body {
|
|||
border-bottom: 3px solid #adb6ba;
|
||||
-moz-border-bottom-colors:#adb6ba #e7eaec #e7eaec;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
@ -260,7 +248,7 @@ body {
|
|||
font-size: 14px;
|
||||
text-shadow: 1px 1px 1px rgba(173, 182, 186, 1);
|
||||
background-color: rgba(173, 182, 186, 0.3);
|
||||
box-shadow:
|
||||
box-shadow:
|
||||
inset rgba(0, 0, 0, 0.2) 0 -10px 8.5px;
|
||||
padding: 9px 8px 8px 8px;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pilot-notification-popup {
|
||||
-moz-appearance: none;
|
||||
border-image: none;
|
||||
background-color: Menu;
|
||||
background-image: -moz-linear-gradient(hsla(0,0%,100%,.2), transparent);
|
||||
box-shadow: inset 0 0 7px hsla(0,0%,100%,.2),
|
||||
|
@ -14,11 +15,6 @@
|
|||
width: 480px;
|
||||
}
|
||||
|
||||
.tail-up,
|
||||
.tail-down {
|
||||
-moz-border-image: none;
|
||||
}
|
||||
|
||||
.pilot-notification-popup-container {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#pilot-notification-submit {
|
||||
-moz-appearance: none;
|
||||
background: #666
|
||||
background: #666
|
||||
-moz-linear-gradient(rgba(110,110,110,.9), rgba(70,70,70,.9) 49%,
|
||||
rgba(60,60,60,.9) 51%, rgba(50,50,50,.9));
|
||||
background-clip: padding-box;
|
||||
|
@ -33,6 +33,6 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.notification-link {
|
||||
#pilot-notification-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
|
|
@ -407,7 +407,7 @@ function clearAllPrefsForStudy(studyId) {
|
|||
dump("Looking for prefs to delete...\n");
|
||||
let prefService = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService)
|
||||
.QueryInterface(Ci.nsIPrefBranch);
|
||||
.QueryInterface(Ci.nsIPrefBranch2);
|
||||
let prefStem = "extensions.testpilot";
|
||||
let prefNames = prefService.getChildList(prefStem);
|
||||
for each (let prefName in prefNames) {
|
||||
|
@ -783,4 +783,4 @@ function testSameGUIDs() {
|
|||
}
|
||||
|
||||
// TODO test for continuity of GUID with recurring study (longitudinal) - i don't think this
|
||||
// has actually been working so far because a new GUID is generted every time the study starts up...
|
||||
// has actually been working so far because a new GUID is generted every time the study starts up...
|
|
@ -1010,7 +1010,7 @@ pref("devtools.inspector.htmlHeight", 112);
|
|||
pref("devtools.inspector.htmlPanelOpen", false);
|
||||
pref("devtools.inspector.sidebarOpen", false);
|
||||
pref("devtools.inspector.activeSidebar", "ruleview");
|
||||
pref("devtools.inspector.markupPreview", true);
|
||||
pref("devtools.inspector.markupPreview", false);
|
||||
|
||||
// Enable the Layout View
|
||||
pref("devtools.layoutview.enabled", true);
|
||||
|
|
|
@ -158,26 +158,7 @@ const gXPInstallObserver = {
|
|||
}
|
||||
else {
|
||||
messageString = gNavigatorBundle.getString("addonsInstalled");
|
||||
action = {
|
||||
label: gNavigatorBundle.getString("addonInstallManage"),
|
||||
accessKey: gNavigatorBundle.getString("addonInstallManage.accesskey"),
|
||||
callback: function() {
|
||||
// Calculate the add-on type that is most popular in the list of
|
||||
// installs
|
||||
var types = {};
|
||||
var bestType = null;
|
||||
for (let install of installInfo.installs) {
|
||||
if (install.type in types)
|
||||
types[install.type]++;
|
||||
else
|
||||
types[install.type] = 1;
|
||||
if (!bestType || types[install.type] > types[bestType])
|
||||
bestType = install.type;
|
||||
}
|
||||
|
||||
BrowserOpenAddonsMgr("addons://list/" + bestType);
|
||||
}
|
||||
};
|
||||
action = null;
|
||||
}
|
||||
|
||||
messageString = PluralForm.get(installInfo.installs.length, messageString);
|
||||
|
|
|
@ -484,7 +484,7 @@ var gPluginHandler = {
|
|||
|
||||
let messageString = gNavigatorBundle.getString("activatePluginsMessage.message");
|
||||
let mainAction = {
|
||||
label: gNavigatorBundle.getString("activatePluginsMessage.label"),
|
||||
label: gNavigatorBundle.getString("activateAllPluginsMessage.label"),
|
||||
accessKey: gNavigatorBundle.getString("activatePluginsMessage.accesskey"),
|
||||
callback: function() { gPluginHandler.activatePlugins(contentWindow); }
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// The minimum sizes for the auto-resize panel code.
|
||||
const PANEL_MIN_HEIGHT = 300;
|
||||
const PANEL_MIN_HEIGHT = 100;
|
||||
const PANEL_MIN_WIDTH = 330;
|
||||
|
||||
let SocialUI = {
|
||||
|
@ -203,7 +203,7 @@ let SocialChatBar = {
|
|||
}
|
||||
}
|
||||
|
||||
function sizeSocialPanelToContent(iframe) {
|
||||
function sizeSocialPanelToContent(panel, iframe) {
|
||||
// FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
|
||||
let doc = iframe.contentDocument;
|
||||
if (!doc || !doc.body) {
|
||||
|
@ -214,9 +214,17 @@ function sizeSocialPanelToContent(iframe) {
|
|||
let cs = doc.defaultView.getComputedStyle(body);
|
||||
let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
|
||||
let height = Math.max(computedHeight, PANEL_MIN_HEIGHT);
|
||||
iframe.style.height = height + "px";
|
||||
let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
|
||||
let width = Math.max(computedWidth, PANEL_MIN_WIDTH);
|
||||
let wDiff = width - iframe.getBoundingClientRect().width;
|
||||
// A panel resize will move the right margin - if that is where the anchor
|
||||
// arrow is, the arrow will be mis-aligned from the anchor. So we move the
|
||||
// popup to compensate for that. See bug 799014.
|
||||
if (wDiff !== 0 && panel.getAttribute("side") == "right") {
|
||||
let box = panel.boxObject;
|
||||
panel.moveTo(box.screenX - wDiff, box.screenY);
|
||||
}
|
||||
iframe.style.height = height + "px";
|
||||
iframe.style.width = width + "px";
|
||||
}
|
||||
|
||||
|
@ -225,18 +233,18 @@ function DynamicResizeWatcher() {
|
|||
}
|
||||
|
||||
DynamicResizeWatcher.prototype = {
|
||||
start: function DynamicResizeWatcher_start(iframe) {
|
||||
start: function DynamicResizeWatcher_start(panel, iframe) {
|
||||
this.stop(); // just in case...
|
||||
let doc = iframe.contentDocument;
|
||||
this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
|
||||
sizeSocialPanelToContent(iframe);
|
||||
sizeSocialPanelToContent(panel, iframe);
|
||||
});
|
||||
// Observe anything that causes the size to change.
|
||||
let config = {attributes: true, characterData: true, childList: true, subtree: true};
|
||||
this._mutationObserver.observe(doc, config);
|
||||
// and since this may be setup after the load event has fired we do an
|
||||
// initial resize now.
|
||||
sizeSocialPanelToContent(iframe);
|
||||
sizeSocialPanelToContent(panel, iframe);
|
||||
},
|
||||
stop: function DynamicResizeWatcher_stop() {
|
||||
if (this._mutationObserver) {
|
||||
|
@ -309,19 +317,20 @@ let SocialFlyout = {
|
|||
},
|
||||
|
||||
onShown: function(aEvent) {
|
||||
let iframe = this.panel.firstChild;
|
||||
let panel = this.panel;
|
||||
let iframe = panel.firstChild;
|
||||
this._dynamicResizer = new DynamicResizeWatcher();
|
||||
iframe.docShell.isActive = true;
|
||||
iframe.docShell.isAppTab = true;
|
||||
if (iframe.contentDocument.readyState == "complete") {
|
||||
this._dynamicResizer.start(iframe);
|
||||
this._dynamicResizer.start(panel, iframe);
|
||||
this.dispatchPanelEvent("socialFrameShow");
|
||||
} else {
|
||||
// first time load, wait for load and dispatch after load
|
||||
iframe.addEventListener("load", function panelBrowserOnload(e) {
|
||||
iframe.removeEventListener("load", panelBrowserOnload, true);
|
||||
setTimeout(function() {
|
||||
SocialFlyout._dynamicResizer.start(iframe);
|
||||
SocialFlyout._dynamicResizer.start(panel, iframe);
|
||||
SocialFlyout.dispatchPanelEvent("socialFrameShow");
|
||||
}, 0);
|
||||
}, true);
|
||||
|
@ -366,7 +375,7 @@ let SocialFlyout = {
|
|||
}
|
||||
}
|
||||
|
||||
sizeSocialPanelToContent(iframe);
|
||||
sizeSocialPanelToContent(panel, iframe);
|
||||
let anchor = document.getElementById("social-sidebar-browser");
|
||||
if (panel.state == "open") {
|
||||
// this is painful - there is no way to say "move to a new anchor offset",
|
||||
|
@ -624,21 +633,57 @@ var SocialToolbar = {
|
|||
updateButton: function SocialToolbar_updateButton() {
|
||||
this.updateButtonHiddenState();
|
||||
let provider = Social.provider;
|
||||
let iconNames = Object.keys(provider.ambientNotificationIcons);
|
||||
let icons = provider.ambientNotificationIcons;
|
||||
let iconNames = Object.keys(icons);
|
||||
let iconBox = document.getElementById("social-toolbar-item");
|
||||
let notifBox = document.getElementById("social-notification-box");
|
||||
let panel = document.getElementById("social-notification-panel");
|
||||
panel.hidden = false;
|
||||
|
||||
let command = document.getElementById("Social:ToggleNotifications");
|
||||
command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
|
||||
|
||||
const CACHE_PREF_NAME = "social.cached.notificationIcons";
|
||||
// provider.profile == undefined means no response yet from the provider
|
||||
// to tell us whether the user is logged in or not.
|
||||
if (!SocialUI.haveLoggedInUser() && provider.profile !== undefined) {
|
||||
// The provider has responded with a profile and the user isn't logged
|
||||
// in. The icons etc have already been removed by
|
||||
// updateButtonHiddenState, so we want to nuke any cached icons we
|
||||
// have and get out of here!
|
||||
Services.prefs.clearUserPref(CACHE_PREF_NAME);
|
||||
return;
|
||||
}
|
||||
if (Social.provider.profile === undefined) {
|
||||
// provider has not told us about the login state yet - see if we have
|
||||
// a cached version for this provider.
|
||||
let cached;
|
||||
try {
|
||||
cached = JSON.parse(Services.prefs.getCharPref(CACHE_PREF_NAME));
|
||||
} catch (ex) {}
|
||||
if (cached && cached.provider == Social.provider.origin && cached.data) {
|
||||
icons = cached.data;
|
||||
iconNames = Object.keys(icons);
|
||||
// delete the counter data as it is almost certainly stale.
|
||||
for each(let name in iconNames) {
|
||||
icons[name].counter = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We have a logged in user - save the current set of icons back to the
|
||||
// "cache" so we can use them next startup.
|
||||
Services.prefs.setCharPref(CACHE_PREF_NAME,
|
||||
JSON.stringify({provider: Social.provider.origin,
|
||||
data: icons}));
|
||||
}
|
||||
|
||||
let notificationFrames = document.createDocumentFragment();
|
||||
let iconContainers = document.createDocumentFragment();
|
||||
|
||||
let createdFrames = [];
|
||||
|
||||
let command = document.getElementById("Social:ToggleNotifications");
|
||||
command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
|
||||
|
||||
for each(let name in iconNames) {
|
||||
let icon = provider.ambientNotificationIcons[name];
|
||||
let icon = icons[name];
|
||||
|
||||
let notificationFrameId = "social-status-" + icon.name;
|
||||
let notificationFrame = document.getElementById(notificationFrameId);
|
||||
|
@ -756,13 +801,13 @@ var SocialToolbar = {
|
|||
notificationFrame.docShell.isActive = true;
|
||||
notificationFrame.docShell.isAppTab = true;
|
||||
if (notificationFrame.contentDocument.readyState == "complete") {
|
||||
dynamicResizer.start(notificationFrame);
|
||||
dynamicResizer.start(panel, notificationFrame);
|
||||
dispatchPanelEvent("socialFrameShow");
|
||||
} else {
|
||||
// first time load, wait for load and dispatch after load
|
||||
notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
|
||||
notificationFrame.removeEventListener("load", panelBrowserOnload, true);
|
||||
dynamicResizer.start(notificationFrame);
|
||||
dynamicResizer.start(panel, notificationFrame);
|
||||
setTimeout(function() {
|
||||
dispatchPanelEvent("socialFrameShow");
|
||||
}, 0);
|
||||
|
|
|
@ -4158,7 +4158,7 @@ var XULBrowserWindow = {
|
|||
// Don't need to re-enable/disable find commands for same-document location changes
|
||||
// (e.g. the replaceStates in about:addons)
|
||||
if (!(aFlags & nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
|
||||
if (content.document.readyState == 'interactive' || content.document.readyState == "complete")
|
||||
if (content.document.readyState == "interactive" || content.document.readyState == "complete")
|
||||
disableFindCommands(shouldDisableFind(content.document));
|
||||
else {
|
||||
content.document.addEventListener("readystatechange", onContentRSChange);
|
||||
|
|
|
@ -336,7 +336,6 @@ function test_restartless() {
|
|||
wait_for_notification(function(aPanel) {
|
||||
let notification = aPanel.childNodes[0];
|
||||
is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
|
||||
is(notification.button.label, "Open Add-ons Manager", "Should have seen the right button");
|
||||
is(notification.getAttribute("label"),
|
||||
"XPI Test has been installed successfully.",
|
||||
"Should have seen the right message");
|
||||
|
|
|
@ -1 +1 @@
|
|||
18.0a1
|
||||
19.0a1
|
||||
|
|
|
@ -28,79 +28,82 @@ gcli.addCommand({
|
|||
description: gcli.lookup('jsbUrlDesc')
|
||||
},
|
||||
{
|
||||
name: 'indentSize',
|
||||
type: 'number',
|
||||
description: gcli.lookup('jsbIndentSizeDesc'),
|
||||
manual: gcli.lookup('jsbIndentSizeManual'),
|
||||
defaultValue: 2
|
||||
},
|
||||
{
|
||||
name: 'indentChar',
|
||||
type: {
|
||||
name: 'selection',
|
||||
lookup: [
|
||||
{ name: "space", value: " " },
|
||||
{ name: "tab", value: "\t" }
|
||||
]
|
||||
},
|
||||
description: gcli.lookup('jsbIndentCharDesc'),
|
||||
manual: gcli.lookup('jsbIndentCharManual'),
|
||||
defaultValue: ' ',
|
||||
},
|
||||
{
|
||||
name: 'preserveNewlines',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbPreserveNewlinesDesc'),
|
||||
manual: gcli.lookup('jsbPreserveNewlinesManual')
|
||||
},
|
||||
{
|
||||
name: 'preserveMaxNewlines',
|
||||
type: 'number',
|
||||
description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
|
||||
manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
|
||||
defaultValue: -1
|
||||
},
|
||||
{
|
||||
name: 'jslintHappy',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbJslintHappyDesc'),
|
||||
manual: gcli.lookup('jsbJslintHappyManual')
|
||||
},
|
||||
{
|
||||
name: 'braceStyle',
|
||||
type: {
|
||||
name: 'selection',
|
||||
data: ['collapse', 'expand', 'end-expand', 'expand-strict']
|
||||
},
|
||||
description: gcli.lookup('jsbBraceStyleDesc'),
|
||||
manual: gcli.lookup('jsbBraceStyleManual'),
|
||||
defaultValue: "collapse"
|
||||
},
|
||||
{
|
||||
name: 'spaceBeforeConditional',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbSpaceBeforeConditionalDesc'),
|
||||
manual: gcli.lookup('jsbSpaceBeforeConditionalManual')
|
||||
},
|
||||
{
|
||||
name: 'unescapeStrings',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbUnescapeStringsDesc'),
|
||||
manual: gcli.lookup('jsbUnescapeStringsManual')
|
||||
group: gcli.lookup("jsbOptionsDesc"),
|
||||
params: [
|
||||
{
|
||||
name: 'indentSize',
|
||||
type: 'number',
|
||||
description: gcli.lookup('jsbIndentSizeDesc'),
|
||||
manual: gcli.lookup('jsbIndentSizeManual'),
|
||||
defaultValue: 2
|
||||
},
|
||||
{
|
||||
name: 'indentChar',
|
||||
type: {
|
||||
name: 'selection',
|
||||
lookup: [
|
||||
{ name: "space", value: " " },
|
||||
{ name: "tab", value: "\t" }
|
||||
]
|
||||
},
|
||||
description: gcli.lookup('jsbIndentCharDesc'),
|
||||
manual: gcli.lookup('jsbIndentCharManual'),
|
||||
defaultValue: ' ',
|
||||
},
|
||||
{
|
||||
name: 'doNotPreserveNewlines',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbDoNotPreserveNewlinesDesc')
|
||||
},
|
||||
{
|
||||
name: 'preserveMaxNewlines',
|
||||
type: 'number',
|
||||
description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
|
||||
manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
|
||||
defaultValue: -1
|
||||
},
|
||||
{
|
||||
name: 'jslintHappy',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbJslintHappyDesc'),
|
||||
manual: gcli.lookup('jsbJslintHappyManual')
|
||||
},
|
||||
{
|
||||
name: 'braceStyle',
|
||||
type: {
|
||||
name: 'selection',
|
||||
data: ['collapse', 'expand', 'end-expand', 'expand-strict']
|
||||
},
|
||||
description: gcli.lookup('jsbBraceStyleDesc'),
|
||||
manual: gcli.lookup('jsbBraceStyleManual'),
|
||||
defaultValue: "collapse"
|
||||
},
|
||||
{
|
||||
name: 'noSpaceBeforeConditional',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbNoSpaceBeforeConditionalDesc')
|
||||
},
|
||||
{
|
||||
name: 'unescapeStrings',
|
||||
type: 'boolean',
|
||||
description: gcli.lookup('jsbUnescapeStringsDesc'),
|
||||
manual: gcli.lookup('jsbUnescapeStringsManual')
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
exec: function(args, context) {
|
||||
let opts = {
|
||||
indent_size: args.indentSize,
|
||||
indent_char: args.indentChar,
|
||||
preserve_newlines: args.preserveNewlines,
|
||||
preserve_newlines: !args.doNotPreserveNewlines,
|
||||
max_preserve_newlines: args.preserveMaxNewlines == -1 ?
|
||||
undefined : args.preserveMaxNewlines,
|
||||
jslint_happy: args.jslintHappy,
|
||||
brace_style: args.braceStyle,
|
||||
space_before_conditional: args.spaceBeforeConditional,
|
||||
space_before_conditional: !args.noSpaceBeforeConditional,
|
||||
unescape_strings: args.unescapeStrings
|
||||
}
|
||||
};
|
||||
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
|
@ -117,13 +120,13 @@ gcli.addCommand({
|
|||
if (xhr.status == 200 || xhr.status == 0) {
|
||||
let browserDoc = context.environment.chromeDocument;
|
||||
let browserWindow = browserDoc.defaultView;
|
||||
let browser = browserWindow.gBrowser;
|
||||
|
||||
browser.selectedTab = browser.addTab("data:text/plain;base64," +
|
||||
browserWindow.btoa(js_beautify(xhr.responseText, opts)));
|
||||
let gBrowser = browserWindow.gBrowser;
|
||||
let result = js_beautify(xhr.responseText, opts);
|
||||
|
||||
browserWindow.Scratchpad.ScratchpadManager.openScratchpad({text: result});
|
||||
|
||||
promise.resolve();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
promise.resolve("Unable to load page to beautify: " + args.url + " " +
|
||||
xhr.status + " " + xhr.statusText);
|
||||
}
|
||||
|
|
|
@ -34,3 +34,6 @@
|
|||
direction: ltr;
|
||||
}
|
||||
|
||||
.gcli-row-out .nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -3056,7 +3056,10 @@ exports.setContents = function(elem, contents) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (exports.isXmlDocument(elem.ownerDocument)) {
|
||||
if ('innerHTML' in elem) {
|
||||
elem.innerHTML = contents;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
var ns = elem.ownerDocument.documentElement.namespaceURI;
|
||||
if (!ns) {
|
||||
|
@ -3076,9 +3079,6 @@ exports.setContents = function(elem, contents) {
|
|||
throw ex;
|
||||
}
|
||||
}
|
||||
else {
|
||||
elem.innerHTML = contents;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -5469,7 +5469,7 @@ function Requisition(environment, doc) {
|
|||
// The command that we are about to execute.
|
||||
// @see setCommandConversion()
|
||||
this.commandAssignment = new CommandAssignment();
|
||||
this._setAssignment(this.commandAssignment, null, true);
|
||||
this.setAssignment(this.commandAssignment, null);
|
||||
|
||||
// The object that stores of Assignment objects that we are filling out.
|
||||
// The Assignment objects are stored under their param.name for named
|
||||
|
@ -5556,7 +5556,7 @@ Requisition.prototype._commandAssignmentChanged = function(ev) {
|
|||
for (var i = 0; i < command.params.length; i++) {
|
||||
var param = command.params[i];
|
||||
var assignment = new Assignment(param, i);
|
||||
this._setAssignment(assignment, null, true);
|
||||
this.setAssignment(assignment, null);
|
||||
assignment.onAssignmentChange.add(this._assignmentChanged, this);
|
||||
this._assignments[param.name] = assignment;
|
||||
}
|
||||
|
@ -5678,27 +5678,23 @@ Requisition.prototype.getAssignments = function(includeCommand) {
|
|||
return assignments;
|
||||
};
|
||||
|
||||
/**
|
||||
* Alter the given assignment using the given arg.
|
||||
* @param assignment The assignment to alter
|
||||
* @param arg The new value for the assignment. An instance of Argument, or an
|
||||
* instance of Conversion, or null to set the blank value.
|
||||
*/
|
||||
Requisition.prototype.setAssignment = function(assignment, arg) {
|
||||
this._setAssignment(assignment, arg, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal function to alter the given assignment using the given arg.
|
||||
* @param assignment The assignment to alter
|
||||
* @param arg The new value for the assignment. An instance of Argument, or an
|
||||
* instance of Conversion, or null to set the blank value.
|
||||
* @param skipArgUpdate (default=false) Adjusts the args in this requisition to
|
||||
* keep things up to date. Args should only be skipped when setAssignment is
|
||||
* being called as part of the update process.
|
||||
* @param options There are a number of ways to customize how the assignment
|
||||
* is made, including:
|
||||
* - argUpdate: (default:false) Adjusts the args in this requisition to keep
|
||||
* things up to date. Args should only be skipped when setAssignment is being
|
||||
* called as part of the update process.
|
||||
* - matchPadding: (default:false) If argUpdate=true, and matchPadding=true
|
||||
* then further take the step of altering the whitespace on the prefix and
|
||||
* suffix of the new argument to match that of the old argument.
|
||||
*/
|
||||
Requisition.prototype._setAssignment = function(assignment, arg, skipArgUpdate) {
|
||||
if (!skipArgUpdate) {
|
||||
Requisition.prototype.setAssignment = function(assignment, arg, options) {
|
||||
options = options || {};
|
||||
if (options.argUpdate) {
|
||||
var originalArgs = assignment.arg.getArgs();
|
||||
|
||||
// Update the args array
|
||||
|
@ -5724,6 +5720,16 @@ Requisition.prototype._setAssignment = function(assignment, arg, skipArgUpdate)
|
|||
this._args.splice(index, 1);
|
||||
}
|
||||
else {
|
||||
if (options.matchPadding) {
|
||||
if (replacementArgs[i].prefix.length === 0 &&
|
||||
this._args[index].prefix.length !== 0) {
|
||||
replacementArgs[i].prefix = this._args[index].prefix;
|
||||
}
|
||||
if (replacementArgs[i].suffix.length === 0 &&
|
||||
this._args[index].suffix.length !== 0) {
|
||||
replacementArgs[i].suffix = this._args[index].suffix;
|
||||
}
|
||||
}
|
||||
this._args[index] = replacementArgs[i];
|
||||
}
|
||||
}
|
||||
|
@ -5761,7 +5767,7 @@ Requisition.prototype._setAssignment = function(assignment, arg, skipArgUpdate)
|
|||
*/
|
||||
Requisition.prototype.setBlankArguments = function() {
|
||||
this.getAssignments().forEach(function(assignment) {
|
||||
this._setAssignment(assignment, null, true);
|
||||
this.setAssignment(assignment, null);
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
@ -5801,13 +5807,13 @@ Requisition.prototype.complete = function(cursor, predictionChoice) {
|
|||
// logic, so we don't use addSpace
|
||||
if (assignment.isInName()) {
|
||||
var newArg = assignment.conversion.arg.beget({ prefixPostSpace: true });
|
||||
this.setAssignment(assignment, newArg);
|
||||
this.setAssignment(assignment, newArg, { argUpdate: true });
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Mutate this argument to hold the completion
|
||||
var arg = assignment.arg.beget({ text: prediction.name });
|
||||
this.setAssignment(assignment, arg);
|
||||
this.setAssignment(assignment, arg, { argUpdate: true });
|
||||
|
||||
if (!prediction.incomplete) {
|
||||
// The prediction is complete, add a space to let the user move-on
|
||||
|
@ -5832,7 +5838,7 @@ Requisition.prototype.complete = function(cursor, predictionChoice) {
|
|||
Requisition.prototype._addSpace = function(assignment) {
|
||||
var arg = assignment.conversion.arg.beget({ suffixSpace: true });
|
||||
if (arg !== assignment.conversion.arg) {
|
||||
this.setAssignment(assignment, arg);
|
||||
this.setAssignment(assignment, arg, { argUpdate: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5844,7 +5850,7 @@ Requisition.prototype.decrement = function(assignment) {
|
|||
if (replacement != null) {
|
||||
var str = assignment.param.type.stringify(replacement);
|
||||
var arg = assignment.conversion.arg.beget({ text: str });
|
||||
this.setAssignment(assignment, arg);
|
||||
this.setAssignment(assignment, arg, { argUpdate: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5856,7 +5862,7 @@ Requisition.prototype.increment = function(assignment) {
|
|||
if (replacement != null) {
|
||||
var str = assignment.param.type.stringify(replacement);
|
||||
var arg = assignment.conversion.arg.beget({ text: str });
|
||||
this.setAssignment(assignment, arg);
|
||||
this.setAssignment(assignment, arg, { argUpdate: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6514,7 +6520,7 @@ Requisition.prototype._split = function(args) {
|
|||
// Special case: if the user enters { console.log('foo'); } then we need to
|
||||
// use the hidden 'eval' command
|
||||
conversion = new Conversion(evalCommand, new ScriptArgument());
|
||||
this._setAssignment(this.commandAssignment, conversion, true);
|
||||
this.setAssignment(this.commandAssignment, conversion);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6541,7 +6547,7 @@ Requisition.prototype._split = function(args) {
|
|||
argsUsed++;
|
||||
}
|
||||
|
||||
this._setAssignment(this.commandAssignment, conversion, true);
|
||||
this.setAssignment(this.commandAssignment, conversion);
|
||||
|
||||
for (var i = 0; i < argsUsed; i++) {
|
||||
args.shift();
|
||||
|
@ -6588,7 +6594,7 @@ Requisition.prototype._assign = function(args) {
|
|||
var assignment = this.getAssignment(0);
|
||||
if (assignment.param.type instanceof StringType) {
|
||||
var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
|
||||
this._setAssignment(assignment, arg, true);
|
||||
this.setAssignment(assignment, arg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -6633,7 +6639,7 @@ Requisition.prototype._assign = function(args) {
|
|||
arrayArg.addArgument(arg);
|
||||
}
|
||||
else {
|
||||
this._setAssignment(assignment, arg, true);
|
||||
this.setAssignment(assignment, arg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -6650,7 +6656,7 @@ Requisition.prototype._assign = function(args) {
|
|||
// If not set positionally, and we can't set it non-positionally,
|
||||
// we have to default it to prevent previous values surviving
|
||||
if (!assignment.param.isPositionalAllowed) {
|
||||
this._setAssignment(assignment, null, true);
|
||||
this.setAssignment(assignment, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6667,7 +6673,7 @@ Requisition.prototype._assign = function(args) {
|
|||
}
|
||||
else {
|
||||
if (args.length === 0) {
|
||||
this._setAssignment(assignment, null, true);
|
||||
this.setAssignment(assignment, null);
|
||||
}
|
||||
else {
|
||||
var arg = args.splice(0, 1)[0];
|
||||
|
@ -6681,7 +6687,7 @@ Requisition.prototype._assign = function(args) {
|
|||
this._unassigned.push(new UnassignedAssignment(this, arg));
|
||||
}
|
||||
else {
|
||||
this._setAssignment(assignment, arg, true);
|
||||
this.setAssignment(assignment, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6690,7 +6696,7 @@ Requisition.prototype._assign = function(args) {
|
|||
// Now we need to assign the array argument (if any)
|
||||
Object.keys(arrayArgs).forEach(function(name) {
|
||||
var assignment = this.getAssignment(name);
|
||||
this._setAssignment(assignment, arrayArgs[name], true);
|
||||
this.setAssignment(assignment, arrayArgs[name]);
|
||||
}, this);
|
||||
|
||||
// What's left is can't be assigned, but we need to extract
|
||||
|
@ -8066,8 +8072,10 @@ JavascriptField.prototype.setConversion = function(conversion) {
|
|||
};
|
||||
|
||||
JavascriptField.prototype.itemClicked = function(ev) {
|
||||
this.onFieldChange(ev);
|
||||
this.setMessage(ev.conversion.message);
|
||||
var conversion = this.type.parse(ev.arg);
|
||||
|
||||
this.onFieldChange({ conversion: conversion });
|
||||
this.setMessage(conversion.message);
|
||||
};
|
||||
|
||||
JavascriptField.prototype.onInputChange = function(ev) {
|
||||
|
@ -8180,12 +8188,11 @@ Menu.prototype.destroy = function() {
|
|||
* @param ev The click event from the browser
|
||||
*/
|
||||
Menu.prototype.onItemClickInternal = function(ev) {
|
||||
var name = ev.currentTarget.querySelector('.gcli-menu-name').innerHTML;
|
||||
var name = ev.currentTarget.querySelector('.gcli-menu-name').textContent;
|
||||
var arg = new Argument(name);
|
||||
arg.suffix = ' ';
|
||||
|
||||
var conversion = this.type.parse(arg);
|
||||
this.onItemClick({ conversion: conversion });
|
||||
this.onItemClick({ arg: arg });
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -8201,7 +8208,7 @@ Menu.prototype.show = function(items, match) {
|
|||
|
||||
if (match) {
|
||||
this.items = this.items.map(function(item) {
|
||||
return gethighlightingProxy(item, match, this.template.ownerDocument);
|
||||
return getHighlightingProxy(item, match, this.template.ownerDocument);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
|
@ -8227,7 +8234,7 @@ Menu.prototype.show = function(items, match) {
|
|||
/**
|
||||
* Create a proxy around an item that highlights matching text
|
||||
*/
|
||||
function gethighlightingProxy(item, match, document) {
|
||||
function getHighlightingProxy(item, match, document) {
|
||||
if (typeof Proxy === 'undefined') {
|
||||
return item;
|
||||
}
|
||||
|
@ -8289,12 +8296,12 @@ Menu.prototype.selectChoice = function() {
|
|||
return false;
|
||||
}
|
||||
|
||||
var name = selected.innerHTML;
|
||||
var name = selected.textContent;
|
||||
var arg = new Argument(name);
|
||||
arg.suffix = ' ';
|
||||
arg.prefix = ' ';
|
||||
|
||||
var conversion = this.type.parse(arg);
|
||||
this.onItemClick({ conversion: conversion });
|
||||
this.onItemClick({ arg: arg });
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -8498,8 +8505,10 @@ SelectionTooltipField.prototype.setConversion = function(conversion) {
|
|||
};
|
||||
|
||||
SelectionTooltipField.prototype.itemClicked = function(ev) {
|
||||
this.onFieldChange(ev);
|
||||
this.setMessage(ev.conversion.message);
|
||||
var conversion = this.type.parse(ev.arg);
|
||||
|
||||
this.onFieldChange({ conversion: conversion });
|
||||
this.setMessage(conversion.message);
|
||||
};
|
||||
|
||||
SelectionTooltipField.prototype.onInputChange = function(ev) {
|
||||
|
@ -10340,7 +10349,8 @@ Tooltip.prototype.selectChoice = function(ev) {
|
|||
* Called by the onFieldChange event on the current Field
|
||||
*/
|
||||
Tooltip.prototype.fieldChanged = function(ev) {
|
||||
this.requisition.setAssignment(this.assignment, ev.conversion.arg);
|
||||
var options = { argUpdate: true, matchPadding: true };
|
||||
this.requisition.setAssignment(this.assignment, ev.conversion.arg, options);
|
||||
|
||||
var isError = ev.conversion.message != null && ev.conversion.message !== '';
|
||||
this.focusManager.setError(isError);
|
||||
|
|
|
@ -6,52 +6,68 @@
|
|||
const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
|
||||
"test/browser_cmd_jsb_script.jsi";
|
||||
|
||||
let scratchpadWin = null;
|
||||
let Scratchpad = null;
|
||||
|
||||
function test() {
|
||||
DeveloperToolbarTest.test("about:blank", [ /*GJT_test*/ ]);
|
||||
DeveloperToolbarTest.test("about:blank", [ GJT_test ]);
|
||||
}
|
||||
|
||||
function GJT_test() {
|
||||
helpers.setInput('jsb');
|
||||
helpers.check({
|
||||
input: 'jsb',
|
||||
hints: ' <url> [indentSize] [indentChar] [preserveNewlines] [preserveMaxNewlines] [jslintHappy] [braceStyle] [spaceBeforeConditional] [unescapeStrings]',
|
||||
hints: ' <url> [options]',
|
||||
markup: 'VVV',
|
||||
status: 'ERROR'
|
||||
});
|
||||
DeveloperToolbarTest.exec({ completed: false });
|
||||
|
||||
gBrowser.addTabsProgressListener({
|
||||
onProgressChange: DeveloperToolbarTest.checkCalled(function GJT_onProgressChange(aBrowser) {
|
||||
gBrowser.removeTabsProgressListener(this);
|
||||
Services.ww.registerNotification(function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowopened") {
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
|
||||
let win = aBrowser._contentWindow;
|
||||
let uri = win.document.location.href;
|
||||
let result = win.atob(uri.replace(/.*,/, ""));
|
||||
scratchpadWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
scratchpadWin.addEventListener("load", function GDT_onLoad() {
|
||||
scratchpadWin.removeEventListener("load", GDT_onLoad, false);
|
||||
Scratchpad = scratchpadWin.Scratchpad;
|
||||
|
||||
result = result.replace(/[\r\n]]/g, "\n");
|
||||
let observer = {
|
||||
onReady: function GJT_onReady() {
|
||||
Scratchpad.removeObserver(observer);
|
||||
|
||||
let correct = "function somefunc() {\n" +
|
||||
" for (let n = 0; n < 500; n++) {\n" +
|
||||
" if (n % 2 == 1) {\n" +
|
||||
" console.log(n);\n" +
|
||||
" console.log(n + 1);\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
is(result, correct, "JS has been correctly prettified");
|
||||
})
|
||||
});
|
||||
let result = Scratchpad.getText();
|
||||
result = result.replace(/[\r\n]]*/g, "\n");
|
||||
let correct = "function somefunc() {\n" +
|
||||
" if (true) // Some comment\n" +
|
||||
" doSomething();\n" +
|
||||
" for (let n = 0; n < 500; n++) {\n" +
|
||||
" if (n % 2 == 1) {\n" +
|
||||
" console.log(n);\n" +
|
||||
" console.log(n + 1);\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
is(result, correct, "JS has been correctly prettified");
|
||||
|
||||
finishUp();
|
||||
},
|
||||
};
|
||||
Scratchpad.addObserver(observer);
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
info("Checking beautification");
|
||||
|
||||
helpers.setInput('jsb ' + TEST_URI);
|
||||
/*
|
||||
helpers.check({
|
||||
input: 'jsb',
|
||||
hints: ' [options]',
|
||||
markup: 'VVV',
|
||||
status: 'VALID'
|
||||
DeveloperToolbarTest.exec({
|
||||
typed: "jsb " + TEST_URI,
|
||||
completed: false
|
||||
});
|
||||
*/
|
||||
|
||||
DeveloperToolbarTest.exec({ completed: false });
|
||||
}
|
||||
|
||||
let finishUp = DeveloperToolbarTest.checkCalled(function GJT_finishUp() {
|
||||
if (scratchpadWin) {
|
||||
scratchpadWin.close();
|
||||
scratchpadWin = null;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
function somefunc(){for(let n=0;n<500;n++){if(n%2==1){console.log(n);console.log(n+1);}}}
|
||||
function somefunc(){if (true) // Some comment
|
||||
doSomething();for(let n=0;n<500;n++){if(n%2==1){console.log(n);console.log(n+1);}}}
|
||||
|
|
|
@ -1,804 +0,0 @@
|
|||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2007, Parakey Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use of this software in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Parakey Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior
|
||||
* written permission of Parakey Inc.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Creator:
|
||||
* Joe Hewitt
|
||||
* Contributors
|
||||
* John J. Barton (IBM Almaden)
|
||||
* Jan Odvarko (Mozilla Corp.)
|
||||
* Max Stepanov (Aptana Inc.)
|
||||
* Rob Campbell (Mozilla Corp.)
|
||||
* Hans Hillen (Paciello Group, Mozilla)
|
||||
* Curtis Bartley (Mozilla Corp.)
|
||||
* Mike Collins (IBM Almaden)
|
||||
* Kevin Decker
|
||||
* Mike Ratcliffe (Comartis AG)
|
||||
* Hernan Rodríguez Colmeiro
|
||||
* Austin Andrews
|
||||
* Christoph Dorn
|
||||
* Steven Roussey (AppCenter Inc, Network54)
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//// InsideOutBox
|
||||
|
||||
/**
|
||||
* InsideOutBoxView is a simple interface definition for views implementing
|
||||
* InsideOutBox controls. All implementors must define these methods.
|
||||
* Implemented in InspectorUI.
|
||||
*/
|
||||
|
||||
/*
|
||||
InsideOutBoxView = {
|
||||
//
|
||||
* Retrieves the parent object for a given child object.
|
||||
* @param aChild
|
||||
* The child node to retrieve the parent object for.
|
||||
* @returns a DOM node | null
|
||||
//
|
||||
getParentObject: function(aChild) {},
|
||||
|
||||
//
|
||||
* Retrieves a given child node.
|
||||
*
|
||||
* If both index and previousSibling are passed, the implementation
|
||||
* may assume that previousSibling will be the return for getChildObject
|
||||
* with index-1.
|
||||
* @param aParent
|
||||
* The parent object of the child object to retrieve.
|
||||
* @param aIndex
|
||||
* The index of the child object to retrieve from aParent.
|
||||
* @param aPreviousSibling
|
||||
* The previous sibling of the child object to retrieve.
|
||||
* Supercedes aIndex.
|
||||
* @returns a DOM object | null
|
||||
//
|
||||
getChildObject: function(aParent, aIndex, aPreviousSibling) {},
|
||||
|
||||
//
|
||||
* Renders the HTML representation of the object. Should return an HTML
|
||||
* object which will be displayed to the user.
|
||||
* @param aObject
|
||||
* The object to create the box object for.
|
||||
* @param aIsRoot
|
||||
* Is the object the root object. May not be used in all
|
||||
* implementations.
|
||||
* @returns an object box | null
|
||||
//
|
||||
createObjectBox: function(aObject, aIsRoot) {},
|
||||
|
||||
//
|
||||
* Convenience wrappers for classList API.
|
||||
* @param aObject
|
||||
* DOM node to query/set.
|
||||
* @param aClassName
|
||||
* String containing the class name to query/set.
|
||||
//
|
||||
hasClass: function(aObject, aClassName) {},
|
||||
addClass: function(aObject, aClassName) {},
|
||||
removeClass: function(aObject, aClassName) {}
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a tree based on objects provided by a separate "view" object.
|
||||
*
|
||||
* Construction uses an "inside-out" algorithm, meaning that the view's job is
|
||||
* first to tell us the ancestry of each object, and secondarily its
|
||||
* descendants.
|
||||
*
|
||||
* Constructor
|
||||
* @param aView
|
||||
* The view requiring the InsideOutBox.
|
||||
* @param aBox
|
||||
* The box object containing the InsideOutBox. Required to add/remove
|
||||
* children during box manipulation (toggling opened or closed).
|
||||
*/
|
||||
|
||||
var EXPORTED_SYMBOLS = ["InsideOutBox"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
|
||||
function InsideOutBox(aView, aBox)
|
||||
{
|
||||
this.view = aView;
|
||||
this.box = aBox;
|
||||
|
||||
this.rootObject = null;
|
||||
|
||||
this.rootObjectBox = null;
|
||||
this.selectedObjectBox = null;
|
||||
this.highlightedObjectBox = null;
|
||||
this.scrollIntoView = false;
|
||||
};
|
||||
|
||||
InsideOutBox.prototype =
|
||||
{
|
||||
/**
|
||||
* Highlight the given object node in the tree.
|
||||
* @param aObject
|
||||
* the object to highlight.
|
||||
* @returns objectBox
|
||||
*/
|
||||
highlight: function IOBox_highlight(aObject)
|
||||
{
|
||||
let objectBox = this.createObjectBox(aObject);
|
||||
this.highlightObjectBox(objectBox);
|
||||
return objectBox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the given object node in the tree.
|
||||
* @param aObject
|
||||
* The object node to open.
|
||||
* @returns objectBox
|
||||
*/
|
||||
openObject: function IOBox_openObject(aObject)
|
||||
{
|
||||
let object = aObject;
|
||||
let firstChild = this.view.getChildObject(object, 0);
|
||||
if (firstChild)
|
||||
object = firstChild;
|
||||
|
||||
return this.openToObject(object);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the tree up to the given object node.
|
||||
* @param aObject
|
||||
* The object in the tree to open to.
|
||||
* @returns objectBox
|
||||
*/
|
||||
openToObject: function IOBox_openToObject(aObject)
|
||||
{
|
||||
let objectBox = this.createObjectBox(aObject);
|
||||
this.openObjectBox(objectBox);
|
||||
return objectBox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Select the given object node in the tree.
|
||||
* @param aObject
|
||||
* The object node to select.
|
||||
* @param makeBoxVisible
|
||||
* Boolean. Open the object box in the tree?
|
||||
* @param forceOpen
|
||||
* Force the object box open by expanding all elements in the tree?
|
||||
* @param scrollIntoView
|
||||
* Scroll the objectBox into view?
|
||||
* @returns nsIDOMNode|null
|
||||
* A DOM node that represents the "object box", the element that
|
||||
* holds/displays the given aObject representation in the tree. If
|
||||
* the object cannot be selected, if it is a stale object, null is
|
||||
* returned.
|
||||
*/
|
||||
select:
|
||||
function IOBox_select(aObject, makeBoxVisible, forceOpen, scrollIntoView)
|
||||
{
|
||||
let objectBox = this.createObjectBox(aObject);
|
||||
if (!objectBox) {
|
||||
return null;
|
||||
}
|
||||
this.selectObjectBox(objectBox, forceOpen);
|
||||
if (makeBoxVisible) {
|
||||
this.openObjectBox(objectBox);
|
||||
}
|
||||
if (scrollIntoView) {
|
||||
// We want to center the label of the element, not the whole tag
|
||||
// (which includes all of its children, and is vertically huge).
|
||||
LayoutHelpers.scrollIntoViewIfNeeded(objectBox.firstElementChild);
|
||||
}
|
||||
|
||||
return objectBox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Expands/contracts the given object, depending on its state.
|
||||
* @param aObject
|
||||
* The tree node to expand/contract.
|
||||
*/
|
||||
toggleObject: function IOBox_toggleObject(aObject)
|
||||
{
|
||||
let box = this.createObjectBox(aObject);
|
||||
if (!(this.view.hasClass(box, "open")))
|
||||
this.expandObjectBox(box);
|
||||
else
|
||||
this.contractObjectBox(box);
|
||||
},
|
||||
|
||||
/**
|
||||
* Expand the given object in the tree.
|
||||
* @param aObject
|
||||
* The tree node to expand.
|
||||
*/
|
||||
expandObject: function IOBox_expandObject(aObject)
|
||||
{
|
||||
let objectBox = this.createObjectBox(aObject);
|
||||
if (objectBox)
|
||||
this.expandObjectBox(objectBox);
|
||||
},
|
||||
|
||||
/**
|
||||
* Contract the given object in the tree.
|
||||
* @param aObject
|
||||
* The tree node to contract.
|
||||
*/
|
||||
contractObject: function IOBox_contractObject(aObject)
|
||||
{
|
||||
let objectBox = this.createObjectBox(aObject);
|
||||
if (objectBox)
|
||||
this.contractObjectBox(objectBox);
|
||||
},
|
||||
|
||||
/**
|
||||
* General method for iterating over an object's ancestors and performing
|
||||
* some function.
|
||||
* @param aObject
|
||||
* The object whose ancestors we wish to iterate over.
|
||||
* @param aCallback
|
||||
* The function to call with the object as argument.
|
||||
*/
|
||||
|
||||
iterateObjectAncestors: function IOBox_iterateObjectAncesors(aObject, aCallback)
|
||||
{
|
||||
let object = aObject;
|
||||
if (!(aCallback && typeof(aCallback) == "function")) {
|
||||
this.view._log("Illegal argument in IOBox.iterateObjectAncestors");
|
||||
return;
|
||||
}
|
||||
while ((object = this.getParentObjectBox(object)))
|
||||
aCallback(object);
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlight the given objectBox in the tree.
|
||||
* @param aObjectBox
|
||||
* The objectBox to highlight.
|
||||
*/
|
||||
highlightObjectBox: function IOBox_highlightObjectBox(aObjectBox)
|
||||
{
|
||||
let self = this;
|
||||
|
||||
if (!aObjectBox)
|
||||
return;
|
||||
|
||||
if (this.highlightedObjectBox) {
|
||||
this.view.removeClass(this.highlightedObjectBox, "highlighted");
|
||||
this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
|
||||
self.view.removeClass(box, "highlightOpen");
|
||||
});
|
||||
}
|
||||
|
||||
this.highlightedObjectBox = aObjectBox;
|
||||
|
||||
this.view.addClass(aObjectBox, "highlighted");
|
||||
this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
|
||||
self.view.addClass(box, "highlightOpen");
|
||||
});
|
||||
|
||||
aObjectBox.scrollIntoView(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Select the given objectBox in the tree, forcing it to be open if necessary.
|
||||
* @param aObjectBox
|
||||
* The objectBox to select.
|
||||
* @param forceOpen
|
||||
* Force the box (subtree) to be open?
|
||||
*/
|
||||
selectObjectBox: function IOBox_selectObjectBox(aObjectBox, forceOpen)
|
||||
{
|
||||
let isSelected = this.selectedObjectBox &&
|
||||
aObjectBox == this.selectedObjectBox;
|
||||
|
||||
// aObjectBox is already selected, return
|
||||
if (isSelected)
|
||||
return;
|
||||
|
||||
if (this.selectedObjectBox)
|
||||
this.view.removeClass(this.selectedObjectBox, "selected");
|
||||
|
||||
this.selectedObjectBox = aObjectBox;
|
||||
|
||||
if (aObjectBox) {
|
||||
this.view.addClass(aObjectBox, "selected");
|
||||
|
||||
// Force it open the first time it is selected
|
||||
if (forceOpen)
|
||||
this.expandObjectBox(aObjectBox, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the next object box in the tree for navigation purposes.
|
||||
*/
|
||||
nextObjectBox: function IOBox_nextObjectBox(aBoxObject)
|
||||
{
|
||||
let candidate;
|
||||
let boxObject = aBoxObject || this.selectedObjectBox;
|
||||
if (!boxObject)
|
||||
return this.rootObjectBox;
|
||||
|
||||
// If expanded, return the first child.
|
||||
let isOpen = this.view.hasClass(boxObject, "open");
|
||||
let childObjectBox = this.getChildObjectBox(boxObject);
|
||||
if (isOpen && childObjectBox && childObjectBox.firstChild) {
|
||||
candidate = childObjectBox.firstChild;
|
||||
} else {
|
||||
// Otherwise we get the next available sibling.
|
||||
while (boxObject) {
|
||||
if (boxObject.nextSibling) {
|
||||
boxObject = boxObject.nextSibling;
|
||||
break;
|
||||
}
|
||||
boxObject = this.getParentObjectBox(boxObject);
|
||||
}
|
||||
candidate = boxObject;
|
||||
}
|
||||
|
||||
// If the node is not an element (comments or text nodes), we
|
||||
// jump to the next line.
|
||||
if (candidate &&
|
||||
candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) {
|
||||
return this.nextObjectBox(candidate);
|
||||
}
|
||||
|
||||
return candidate;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the next object in the tree for navigation purposes.
|
||||
*/
|
||||
nextObject: function IOBox_nextObject()
|
||||
{
|
||||
let next = this.nextObjectBox();
|
||||
return next ? next.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the object that is below the selection.
|
||||
*
|
||||
* @param aDistance Number of lines to jump.
|
||||
*/
|
||||
farNextObject: function IOBox_farPreviousProject(aDistance)
|
||||
{
|
||||
let boxObject = this.selectedObjectBox;
|
||||
while (aDistance-- > 0) {
|
||||
let newBoxObject = this.nextObjectBox(boxObject);
|
||||
if (!newBoxObject) {
|
||||
break;
|
||||
}
|
||||
boxObject = newBoxObject;
|
||||
}
|
||||
return boxObject ? boxObject.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the last visible child box of an object box.
|
||||
*/
|
||||
lastVisible: function IOBox_lastVisibleChild(aNode)
|
||||
{
|
||||
if (!this.view.hasClass(aNode, "open"))
|
||||
return aNode;
|
||||
|
||||
let childBox = this.getChildObjectBox(aNode);
|
||||
if (!childBox || !childBox.lastChild)
|
||||
return aNode;
|
||||
|
||||
return this.lastVisible(childBox.lastChild);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the previous object box in the tree for navigation purposes.
|
||||
*/
|
||||
previousObjectBox: function IOBox_previousObjectBox(aBoxObject)
|
||||
{
|
||||
let boxObject = aBoxObject || this.selectedObjectBox;
|
||||
if (!boxObject)
|
||||
return this.rootObjectBox;
|
||||
|
||||
let candidate;
|
||||
let sibling = boxObject.previousSibling;
|
||||
if (sibling) {
|
||||
candidate = this.lastVisible(sibling);
|
||||
} else {
|
||||
candidate = this.getParentObjectBox(boxObject);
|
||||
}
|
||||
|
||||
// If the node is not an element (comments or text nodes), we
|
||||
// jump to the previous line.
|
||||
if (candidate &&
|
||||
candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) {
|
||||
return this.previousObjectBox(candidate);
|
||||
}
|
||||
|
||||
return candidate;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the previous object in the tree for navigation purposes.
|
||||
*/
|
||||
previousObject: function IOBox_previousObject()
|
||||
{
|
||||
let boxObject = this.previousObjectBox();
|
||||
return boxObject ? boxObject.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the object that is above the selection.
|
||||
*
|
||||
* @param aDistance Number of lines to jump.
|
||||
*/
|
||||
farPreviousObject: function IOBox_farPreviousProject(aDistance)
|
||||
{
|
||||
let boxObject = this.selectedObjectBox;
|
||||
while (aDistance-- > 0) {
|
||||
let newBoxObject = this.previousObjectBox(boxObject);
|
||||
if (!newBoxObject) {
|
||||
break;
|
||||
}
|
||||
boxObject = newBoxObject;
|
||||
if (boxObject === this.rootObjectBox)
|
||||
break;
|
||||
}
|
||||
return boxObject ? boxObject.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the ancestors of the given object box.
|
||||
* @param aObjectBox
|
||||
* The object box to open.
|
||||
*/
|
||||
openObjectBox: function IOBox_openObjectBox(aObjectBox)
|
||||
{
|
||||
if (!aObjectBox)
|
||||
return;
|
||||
|
||||
let self = this;
|
||||
this.iterateObjectAncestors(aObjectBox, function (box) {
|
||||
self.view.addClass(box, "open");
|
||||
let labelBox = box.querySelector(".nodeLabelBox");
|
||||
if (labelBox)
|
||||
labelBox.setAttribute("aria-expanded", "true");
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Expand the given object box.
|
||||
* @param aObjectBox
|
||||
* The object box to expand.
|
||||
*/
|
||||
expandObjectBox: function IOBox_expandObjectBox(aObjectBox)
|
||||
{
|
||||
let nodeChildBox = this.getChildObjectBox(aObjectBox);
|
||||
|
||||
// no children means nothing to expand, return
|
||||
if (!nodeChildBox)
|
||||
return;
|
||||
|
||||
if (!aObjectBox.populated) {
|
||||
let firstChild = this.view.getChildObject(aObjectBox.repObject, 0);
|
||||
this.populateChildBox(firstChild, nodeChildBox);
|
||||
}
|
||||
let labelBox = aObjectBox.querySelector(".nodeLabelBox");
|
||||
if (labelBox)
|
||||
labelBox.setAttribute("aria-expanded", "true");
|
||||
this.view.addClass(aObjectBox, "open");
|
||||
},
|
||||
|
||||
/**
|
||||
* Contract the given object box.
|
||||
* @param aObjectBox
|
||||
* The object box to contract.
|
||||
*/
|
||||
contractObjectBox: function IOBox_contractObjectBox(aObjectBox)
|
||||
{
|
||||
this.view.removeClass(aObjectBox, "open");
|
||||
let nodeLabel = aObjectBox.querySelector(".nodeLabel");
|
||||
let labelBox = nodeLabel.querySelector(".nodeLabelBox");
|
||||
if (labelBox)
|
||||
labelBox.setAttribute("aria-expanded", "false");
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the given object box, forcing open if requested.
|
||||
* @param aObjectBox
|
||||
* The object box to toggle.
|
||||
* @param forceOpen
|
||||
* Force the objectbox open?
|
||||
*/
|
||||
toggleObjectBox: function IOBox_toggleObjectBox(aObjectBox, forceOpen)
|
||||
{
|
||||
let isOpen = this.view.hasClass(aObjectBox, "open");
|
||||
|
||||
if (!forceOpen && isOpen)
|
||||
this.contractObjectBox(aObjectBox);
|
||||
else if (!isOpen)
|
||||
this.expandObjectBox(aObjectBox);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates all of the boxes for an object, its ancestors, and siblings.
|
||||
* @param aObject
|
||||
* The tree node to create the object boxes for.
|
||||
* @returns anObjectBox or null
|
||||
*/
|
||||
createObjectBox: function IOBox_createObjectBox(aObject)
|
||||
{
|
||||
if (!aObject)
|
||||
return null;
|
||||
|
||||
this.rootObject = this.getRootNode(aObject) || aObject;
|
||||
|
||||
// Get or create all of the boxes for the target and its ancestors
|
||||
let objectBox = this.createObjectBoxes(aObject, this.rootObject);
|
||||
|
||||
if (!objectBox)
|
||||
return null;
|
||||
|
||||
if (aObject == this.rootObject)
|
||||
return objectBox;
|
||||
|
||||
return this.populateChildBox(aObject, objectBox.parentNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates all of the boxes for an object, its ancestors, and siblings up to
|
||||
* a root.
|
||||
* @param aObject
|
||||
* The tree's object node to create the object boxes for.
|
||||
* @param aRootObject
|
||||
* The root object at which to stop building object boxes.
|
||||
* @returns an object box or null
|
||||
*/
|
||||
createObjectBoxes: function IOBox_createObjectBoxes(aObject, aRootObject)
|
||||
{
|
||||
if (!aObject)
|
||||
return null;
|
||||
|
||||
if (aObject == aRootObject) {
|
||||
if (!this.rootObjectBox || this.rootObjectBox.repObject != aRootObject) {
|
||||
if (this.rootObjectBox) {
|
||||
try {
|
||||
this.box.removeChild(this.rootObjectBox);
|
||||
} catch (exc) {
|
||||
this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " +
|
||||
this.box + " must not contain " + this.rootObjectBox);
|
||||
}
|
||||
}
|
||||
|
||||
this.highlightedObjectBox = null;
|
||||
this.selectedObjectBox = null;
|
||||
this.rootObjectBox = this.view.createObjectBox(aObject, true);
|
||||
this.box.appendChild(this.rootObjectBox);
|
||||
}
|
||||
return this.rootObjectBox;
|
||||
}
|
||||
|
||||
let parentNode = this.view.getParentObject(aObject);
|
||||
let parentObjectBox = this.createObjectBoxes(parentNode, aRootObject);
|
||||
|
||||
if (!parentObjectBox)
|
||||
return null;
|
||||
|
||||
let parentChildBox = this.getChildObjectBox(parentObjectBox);
|
||||
|
||||
if (!parentChildBox)
|
||||
return null;
|
||||
|
||||
let childObjectBox = this.findChildObjectBox(parentChildBox, aObject);
|
||||
|
||||
return childObjectBox ? childObjectBox
|
||||
: this.populateChildBox(aObject, parentChildBox);
|
||||
},
|
||||
|
||||
/**
|
||||
* Locate the object box for a given object node.
|
||||
* @param aObject
|
||||
* The given object node in the tree.
|
||||
* @returns an object box or null.
|
||||
*/
|
||||
findObjectBox: function IOBox_findObjectBox(aObject)
|
||||
{
|
||||
if (!aObject)
|
||||
return null;
|
||||
|
||||
if (aObject == this.rootObject)
|
||||
return this.rootObjectBox;
|
||||
|
||||
let parentNode = this.view.getParentObject(aObject);
|
||||
let parentObjectBox = this.findObjectBox(parentNode);
|
||||
if (!parentObjectBox)
|
||||
return null;
|
||||
|
||||
let parentChildBox = this.getChildObjectBox(parentObjectBox);
|
||||
if (!parentChildBox)
|
||||
return null;
|
||||
|
||||
return this.findChildObjectBox(parentChildBox, aObject);
|
||||
},
|
||||
|
||||
getAncestorByClass: function IOBox_getAncestorByClass(node, className)
|
||||
{
|
||||
for (let parent = node; parent; parent = parent.parentNode) {
|
||||
if (this.view.hasClass(parent, className))
|
||||
return parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* We want all children of the parent of repObject.
|
||||
*/
|
||||
populateChildBox: function IOBox_populateChildBox(repObject, nodeChildBox)
|
||||
{
|
||||
if (!repObject)
|
||||
return null;
|
||||
|
||||
let parentObjectBox = this.getAncestorByClass(nodeChildBox, "nodeBox");
|
||||
|
||||
if (parentObjectBox.populated)
|
||||
return this.findChildObjectBox(nodeChildBox, repObject);
|
||||
|
||||
let lastSiblingBox = this.getChildObjectBox(nodeChildBox);
|
||||
let siblingBox = nodeChildBox.firstChild;
|
||||
let targetBox = null;
|
||||
let view = this.view;
|
||||
let targetSibling = null;
|
||||
let parentNode = view.getParentObject(repObject);
|
||||
|
||||
for (let i = 0; 1; ++i) {
|
||||
targetSibling = view.getChildObject(parentNode, i, targetSibling);
|
||||
if (!targetSibling)
|
||||
break;
|
||||
|
||||
// Check if we need to start appending, or continue to insert before
|
||||
if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
|
||||
lastSiblingBox = null;
|
||||
|
||||
if (!siblingBox || siblingBox.repObject != targetSibling) {
|
||||
let newBox = view.createObjectBox(targetSibling);
|
||||
if (newBox) {
|
||||
if (lastSiblingBox)
|
||||
nodeChildBox.insertBefore(newBox, lastSiblingBox);
|
||||
else
|
||||
nodeChildBox.appendChild(newBox);
|
||||
}
|
||||
|
||||
siblingBox = newBox;
|
||||
}
|
||||
|
||||
if (targetSibling == repObject)
|
||||
targetBox = siblingBox;
|
||||
|
||||
if (siblingBox && siblingBox.repObject == targetSibling)
|
||||
siblingBox = siblingBox.nextSibling;
|
||||
}
|
||||
|
||||
if (targetBox)
|
||||
parentObjectBox.populated = true;
|
||||
|
||||
return targetBox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the parent object box of a given object box.
|
||||
* @params aObjectBox
|
||||
* The object box of the parent.
|
||||
* @returns an object box or null
|
||||
*/
|
||||
getParentObjectBox: function IOBox_getParentObjectBox(aObjectBox)
|
||||
{
|
||||
let parent = aObjectBox.parentNode ? aObjectBox.parentNode.parentNode : null;
|
||||
return parent && parent.repObject ? parent : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the child object box of a given object box.
|
||||
* @param aObjectBox
|
||||
* The object box whose child you want.
|
||||
* @returns an object box or null
|
||||
*/
|
||||
getChildObjectBox: function IOBox_getChildObjectBox(aObjectBox)
|
||||
{
|
||||
return aObjectBox.querySelector(".nodeChildBox");
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the child object box for a given repObject within the subtree
|
||||
* rooted at aParentNodeBox.
|
||||
* @param aParentNodeBox
|
||||
* root of the subtree in which to search for repObject.
|
||||
* @param aRepObject
|
||||
* The object you wish to locate in the subtree.
|
||||
* @returns an object box or null
|
||||
*/
|
||||
findChildObjectBox: function IOBox_findChildObjectBox(aParentNodeBox, aRepObject)
|
||||
{
|
||||
let childBox = aParentNodeBox.firstChild;
|
||||
while (childBox) {
|
||||
if (childBox.repObject == aRepObject)
|
||||
return childBox;
|
||||
childBox = childBox.nextSibling;
|
||||
}
|
||||
return null; // not found
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if the given node is an ancestor of the current root.
|
||||
* @param aNode
|
||||
* The node to look for within the tree.
|
||||
* @returns boolean
|
||||
*/
|
||||
isInExistingRoot: function IOBox_isInExistingRoot(aNode)
|
||||
{
|
||||
let parentNode = aNode;
|
||||
while (parentNode && parentNode != this.rootObject) {
|
||||
parentNode = this.view.getParentObject(parentNode);
|
||||
}
|
||||
return parentNode == this.rootObject;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the root node of a given node.
|
||||
* @param aNode
|
||||
* The node whose root you wish to retrieve.
|
||||
* @returns a root node or null
|
||||
*/
|
||||
getRootNode: function IOBox_getRootNode(aNode)
|
||||
{
|
||||
let node = aNode;
|
||||
let tmpNode;
|
||||
while ((tmpNode = this.view.getParentObject(node)))
|
||||
node = tmpNode;
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up our mess.
|
||||
*/
|
||||
destroy: function IOBox_destroy()
|
||||
{
|
||||
delete this.view;
|
||||
delete this.box;
|
||||
delete this.rootObject;
|
||||
delete this.rootObjectBox;
|
||||
delete this.selectedObjectBox;
|
||||
delete this.highlightedObjectBox;
|
||||
delete this.scrollIntoView;
|
||||
}
|
||||
};
|
|
@ -11,9 +11,6 @@ VPATH = @srcdir@
|
|||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
EXTRA_JS_MODULES = \
|
||||
domplate.jsm \
|
||||
InsideOutBox.jsm \
|
||||
TreePanel.jsm \
|
||||
highlighter.jsm \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -1,844 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource:///modules/domplate.jsm");
|
||||
Cu.import("resource:///modules/InsideOutBox.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/inspector.jsm");
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
|
||||
var EXPORTED_SYMBOLS = ["TreePanel", "DOMHelpers"];
|
||||
|
||||
const INSPECTOR_URI = "chrome://browser/content/inspector.html";
|
||||
|
||||
/**
|
||||
* TreePanel
|
||||
* A container for the Inspector's HTML Tree Panel widget constructor function.
|
||||
* @param aContext nsIDOMWindow (xulwindow)
|
||||
* @param aIUI global InspectorUI object
|
||||
*/
|
||||
function TreePanel(aContext, aIUI) {
|
||||
this._init(aContext, aIUI);
|
||||
};
|
||||
|
||||
TreePanel.prototype = {
|
||||
showTextNodesWithWhitespace: false,
|
||||
id: "treepanel", // DO NOT LOCALIZE
|
||||
_open: false,
|
||||
|
||||
/**
|
||||
* The tree panel container element.
|
||||
* @returns xul:panel|xul:vbox|null
|
||||
* xul:panel is returned when the tree panel is not docked, or
|
||||
* xul:vbox when when the tree panel is docked.
|
||||
* null is returned when no container is available.
|
||||
*/
|
||||
get container()
|
||||
{
|
||||
return this.document.getElementById("inspector-tree-box");
|
||||
},
|
||||
|
||||
/**
|
||||
* Main TreePanel boot-strapping method. Initialize the TreePanel with the
|
||||
* originating context and the InspectorUI global.
|
||||
* @param aContext nsIDOMWindow (xulwindow)
|
||||
* @param aIUI global InspectorUI object
|
||||
*/
|
||||
_init: function TP__init(aContext, aIUI)
|
||||
{
|
||||
this.IUI = aIUI;
|
||||
this.window = aContext;
|
||||
this.document = this.window.document;
|
||||
this.button =
|
||||
this.IUI.chromeDoc.getElementById("inspector-treepanel-toolbutton");
|
||||
|
||||
domplateUtils.setDOM(this.window);
|
||||
|
||||
this.DOMHelpers = new DOMHelpers(this.window);
|
||||
|
||||
let isOpen = this.isOpen.bind(this);
|
||||
|
||||
this.editingEvents = {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialization function for the TreePanel.
|
||||
*/
|
||||
initializeIFrame: function TP_initializeIFrame()
|
||||
{
|
||||
if (!this.initializingTreePanel || this.treeLoaded) {
|
||||
return;
|
||||
}
|
||||
this.treeBrowserDocument = this.treeIFrame.contentDocument;
|
||||
this.treePanelDiv = this.treeBrowserDocument.createElement("div");
|
||||
this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
|
||||
this.treePanelDiv.ownerPanel = this;
|
||||
this.ioBox = new InsideOutBox(this, this.treePanelDiv);
|
||||
this.ioBox.createObjectBox(this.IUI.win.document.documentElement);
|
||||
this.treeLoaded = true;
|
||||
this._boundTreeKeyPress = this.onTreeKeyPress.bind(this);
|
||||
this.treeIFrame.addEventListener("keypress", this._boundTreeKeyPress.bind(this), true);
|
||||
this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
|
||||
this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
|
||||
this.treeIFrame.focus();
|
||||
delete this.initializingTreePanel;
|
||||
Services.obs.notifyObservers(null,
|
||||
this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null);
|
||||
if (this.pendingSelection) {
|
||||
this.select(this.pendingSelection.node, this.pendingSelection.scroll);
|
||||
delete this.pendingSelection;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the inspector's tree panel and initialize it.
|
||||
*/
|
||||
open: function TP_open()
|
||||
{
|
||||
if (this._open) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._open = true;
|
||||
|
||||
this.button.setAttribute("checked", true);
|
||||
this.initializingTreePanel = true;
|
||||
|
||||
this.treeIFrame = this.document.getElementById("inspector-tree-iframe");
|
||||
if (!this.treeIFrame) {
|
||||
this.treeIFrame = this.document.createElement("iframe");
|
||||
this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
|
||||
this.treeIFrame.flex = 1;
|
||||
this.treeIFrame.setAttribute("type", "content");
|
||||
this.treeIFrame.setAttribute("context", "inspector-node-popup");
|
||||
}
|
||||
|
||||
let treeBox = null;
|
||||
treeBox = this.document.createElement("vbox");
|
||||
treeBox.id = "inspector-tree-box";
|
||||
treeBox.state = "open";
|
||||
try {
|
||||
treeBox.height =
|
||||
Services.prefs.getIntPref("devtools.inspector.htmlHeight");
|
||||
} catch(e) {
|
||||
treeBox.height = 112;
|
||||
}
|
||||
|
||||
treeBox.minHeight = 64;
|
||||
|
||||
this.splitter = this.document.createElement("splitter");
|
||||
this.splitter.id = "inspector-tree-splitter";
|
||||
this.splitter.className = "devtools-horizontal-splitter";
|
||||
|
||||
let container = this.document.getElementById("appcontent");
|
||||
container.appendChild(this.splitter);
|
||||
container.appendChild(treeBox);
|
||||
|
||||
treeBox.appendChild(this.treeIFrame);
|
||||
|
||||
this._boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
|
||||
{
|
||||
this.treeIFrame.removeEventListener("load",
|
||||
this._boundLoadedInitializeTreePanel, true);
|
||||
delete this._boundLoadedInitializeTreePanel;
|
||||
this.initializeIFrame();
|
||||
}.bind(this);
|
||||
|
||||
this.treeIFrame.addEventListener("load",
|
||||
this._boundLoadedInitializeTreePanel, true);
|
||||
|
||||
let src = this.treeIFrame.getAttribute("src");
|
||||
if (src != INSPECTOR_URI) {
|
||||
this.treeIFrame.setAttribute("src", INSPECTOR_URI);
|
||||
} else {
|
||||
this.treeIFrame.contentWindow.location.reload();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Close the TreePanel.
|
||||
*/
|
||||
close: function TP_close()
|
||||
{
|
||||
this._open = false;
|
||||
|
||||
// Stop caring about the tree iframe load if it's in progress.
|
||||
if (this._boundLoadedInitializeTreePanel) {
|
||||
this.treeIFrame.removeEventListener("load",
|
||||
this._boundLoadedInitializeTreePanel, true);
|
||||
delete this._boundLoadedInitializeTreePanel;
|
||||
}
|
||||
|
||||
this.button.removeAttribute("checked");
|
||||
let treeBox = this.container;
|
||||
Services.prefs.setIntPref("devtools.inspector.htmlHeight", treeBox.height);
|
||||
let treeBoxParent = treeBox.parentNode;
|
||||
treeBoxParent.removeChild(this.splitter);
|
||||
treeBoxParent.removeChild(treeBox);
|
||||
|
||||
if (this.treePanelDiv) {
|
||||
this.treePanelDiv.ownerPanel = null;
|
||||
let parent = this.treePanelDiv.parentNode;
|
||||
parent.removeChild(this.treePanelDiv);
|
||||
this.treeIFrame.removeEventListener("keypress", this._boundTreeKeyPress, true);
|
||||
delete this.treePanelDiv;
|
||||
delete this.treeBrowserDocument;
|
||||
}
|
||||
|
||||
if (this.ioBox) {
|
||||
this.ioBox.destroy();
|
||||
delete this.ioBox;
|
||||
}
|
||||
|
||||
this.treeLoaded = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Is the TreePanel open?
|
||||
* @returns boolean
|
||||
*/
|
||||
isOpen: function TP_isOpen()
|
||||
{
|
||||
return this._open;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the TreePanel.
|
||||
*/
|
||||
toggle: function TP_toggle()
|
||||
{
|
||||
this.isOpen() ? this.close() : this.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the ObjectBox for the given object.
|
||||
* @param object nsIDOMNode
|
||||
* @param isRoot boolean - Is this the root object?
|
||||
* @returns InsideOutBox
|
||||
*/
|
||||
createObjectBox: function TP_createObjectBox(object, isRoot)
|
||||
{
|
||||
let tag = domplateUtils.getNodeTag(object);
|
||||
if (tag)
|
||||
return tag.replace({object: object}, this.treeBrowserDocument);
|
||||
},
|
||||
|
||||
getParentObject: function TP_getParentObject(node)
|
||||
{
|
||||
return this.DOMHelpers.getParentObject(node);
|
||||
},
|
||||
|
||||
getChildObject: function TP_getChildObject(node, index, previousSibling)
|
||||
{
|
||||
return this.DOMHelpers.getChildObject(node, index, previousSibling,
|
||||
this.showTextNodesWithWhitespace);
|
||||
},
|
||||
|
||||
getFirstChild: function TP_getFirstChild(node)
|
||||
{
|
||||
return this.DOMHelpers.getFirstChild(node);
|
||||
},
|
||||
|
||||
getNextSibling: function TP_getNextSibling(node)
|
||||
{
|
||||
return this.DOMHelpers.getNextSibling(node);
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Event Handling
|
||||
|
||||
/**
|
||||
* Handle click events in the html tree panel.
|
||||
* @param aEvent
|
||||
* The mouse event.
|
||||
*/
|
||||
onTreeClick: function TP_onTreeClick(aEvent)
|
||||
{
|
||||
let node;
|
||||
let target = aEvent.target;
|
||||
let hitTwisty = false;
|
||||
if (this.hasClass(target, "twisty")) {
|
||||
node = this.getRepObject(aEvent.target.nextSibling);
|
||||
hitTwisty = true;
|
||||
} else {
|
||||
node = this.getRepObject(aEvent.target);
|
||||
}
|
||||
|
||||
if (node) {
|
||||
if (hitTwisty) {
|
||||
this.ioBox.toggleObject(node);
|
||||
} else {
|
||||
if (this.IUI.inspecting) {
|
||||
this.IUI.stopInspecting(true);
|
||||
} else {
|
||||
this.navigate(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle double-click events in the html tree panel.
|
||||
* Double-clicking an attribute name or value allows it to be edited.
|
||||
* @param aEvent
|
||||
* The mouse event.
|
||||
*/
|
||||
onTreeDblClick: function TP_onTreeDblClick(aEvent)
|
||||
{
|
||||
// if already editing an attribute value, double-clicking elsewhere
|
||||
// in the tree is the same as a click, which dismisses the editor
|
||||
if (this.editingContext)
|
||||
this.closeEditor();
|
||||
|
||||
let target = aEvent.target;
|
||||
|
||||
if (!this.hasClass(target, "editable")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let repObj = this.getRepObject(target);
|
||||
|
||||
if (this.hasClass(target, "nodeValue")) {
|
||||
let attrName = target.getAttribute("data-attributeName");
|
||||
let attrVal = target.innerHTML;
|
||||
|
||||
this.editAttribute(target, repObj, attrName, attrVal);
|
||||
}
|
||||
|
||||
if (this.hasClass(target, "nodeName")) {
|
||||
let attrName = target.innerHTML;
|
||||
let attrValNode = target.nextSibling.nextSibling; // skip 2 (=)
|
||||
|
||||
if (attrValNode)
|
||||
this.editAttribute(target, repObj, attrName, attrValNode.innerHTML);
|
||||
}
|
||||
},
|
||||
|
||||
navigate: function TP_navigate(node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
this.ioBox.select(node, false, false, true);
|
||||
|
||||
if (this.IUI.highlighter.isNodeHighlightable(node)) {
|
||||
this.IUI.select(node, true, false, "treepanel");
|
||||
this.IUI.highlighter.highlight(node);
|
||||
}
|
||||
},
|
||||
|
||||
onTreeKeyPress: function TP_onTreeKeyPress(aEvent)
|
||||
{
|
||||
let handled = true;
|
||||
switch(aEvent.keyCode) {
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
|
||||
this.ioBox.contractObjectBox(this.ioBox.selectedObjectBox);
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_RIGHT:
|
||||
this.ioBox.expandObjectBox(this.ioBox.selectedObjectBox);
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_UP:
|
||||
this.navigate(this.ioBox.previousObject());
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_DOWN:
|
||||
this.navigate(this.ioBox.nextObject());
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP:
|
||||
this.navigate(this.ioBox.farPreviousObject(10));
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN:
|
||||
this.navigate(this.ioBox.farNextObject(10));
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_HOME:
|
||||
this.navigate(this.ioBox.rootObject);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
}
|
||||
if (handled) {
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the editor for an attribute name or value.
|
||||
* @param aAttrObj
|
||||
* The DOM object representing the attribute name or value in the HTML
|
||||
* Tree.
|
||||
* @param aRepObj
|
||||
* The original DOM (target) object being inspected/edited
|
||||
* @param aAttrName
|
||||
* The name of the attribute being edited
|
||||
* @param aAttrVal
|
||||
* The current value of the attribute being edited
|
||||
*/
|
||||
editAttribute:
|
||||
function TP_editAttribute(aAttrObj, aRepObj, aAttrName, aAttrVal)
|
||||
{
|
||||
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
|
||||
let editorInput =
|
||||
this.treeBrowserDocument.getElementById("attribute-editor-input");
|
||||
let attrDims = aAttrObj.getBoundingClientRect();
|
||||
// figure out actual viewable viewport dimensions (sans scrollbars)
|
||||
let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
|
||||
let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
|
||||
|
||||
// saves the editing context for use when the editor is saved/closed
|
||||
this.editingContext = {
|
||||
attrObj: aAttrObj,
|
||||
repObj: aRepObj,
|
||||
attrName: aAttrName,
|
||||
attrValue: aAttrVal
|
||||
};
|
||||
|
||||
// highlight attribute-value node in tree while editing
|
||||
this.addClass(aAttrObj, "editingAttributeValue");
|
||||
|
||||
// show the editor
|
||||
this.addClass(editor, "editing");
|
||||
|
||||
// offset the editor below the attribute-value node being edited
|
||||
let editorVerticalOffset = 2;
|
||||
|
||||
// keep the editor comfortably within the bounds of the viewport
|
||||
let editorViewportBoundary = 5;
|
||||
|
||||
// outer editor is sized based on the <input> box inside it
|
||||
editorInput.style.width = Math.min(attrDims.width, viewportWidth -
|
||||
editorViewportBoundary) + "px";
|
||||
editorInput.style.height = Math.min(attrDims.height, viewportHeight -
|
||||
editorViewportBoundary) + "px";
|
||||
let editorDims = editor.getBoundingClientRect();
|
||||
|
||||
// calculate position for the editor according to the attribute node
|
||||
let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
|
||||
// center the editor against the attribute value
|
||||
((editorDims.width - attrDims.width) / 2);
|
||||
let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
|
||||
attrDims.height + editorVerticalOffset;
|
||||
|
||||
// but, make sure the editor stays within the visible viewport
|
||||
editorLeft = Math.max(0, Math.min(
|
||||
(this.treeIFrame.contentWindow.scrollX +
|
||||
viewportWidth - editorDims.width),
|
||||
editorLeft)
|
||||
);
|
||||
editorTop = Math.max(0, Math.min(
|
||||
(this.treeIFrame.contentWindow.scrollY +
|
||||
viewportHeight - editorDims.height),
|
||||
editorTop)
|
||||
);
|
||||
|
||||
// position the editor
|
||||
editor.style.left = editorLeft + "px";
|
||||
editor.style.top = editorTop + "px";
|
||||
|
||||
// set and select the text
|
||||
if (this.hasClass(aAttrObj, "nodeValue")) {
|
||||
editorInput.value = aAttrVal;
|
||||
editorInput.select();
|
||||
} else {
|
||||
editorInput.value = aAttrName;
|
||||
editorInput.select();
|
||||
}
|
||||
|
||||
// listen for editor specific events
|
||||
this.bindEditorEvent(editor, "click", function(aEvent) {
|
||||
aEvent.stopPropagation();
|
||||
});
|
||||
this.bindEditorEvent(editor, "dblclick", function(aEvent) {
|
||||
aEvent.stopPropagation();
|
||||
});
|
||||
this.bindEditorEvent(editor, "keypress",
|
||||
this.handleEditorKeypress.bind(this));
|
||||
|
||||
// event notification
|
||||
Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED,
|
||||
null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle binding an event handler for the editor.
|
||||
* (saves the callback for easier unbinding later)
|
||||
* @param aEditor
|
||||
* The DOM object for the editor
|
||||
* @param aEventName
|
||||
* The name of the event to listen for
|
||||
* @param aEventCallback
|
||||
* The callback to bind to the event (and also to save for later
|
||||
* unbinding)
|
||||
*/
|
||||
bindEditorEvent:
|
||||
function TP_bindEditorEvent(aEditor, aEventName, aEventCallback)
|
||||
{
|
||||
this.editingEvents[aEventName] = aEventCallback;
|
||||
aEditor.addEventListener(aEventName, aEventCallback, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle unbinding an event handler from the editor.
|
||||
* (unbinds the previously bound and saved callback)
|
||||
* @param aEditor
|
||||
* The DOM object for the editor
|
||||
* @param aEventName
|
||||
* The name of the event being listened for
|
||||
*/
|
||||
unbindEditorEvent: function TP_unbindEditorEvent(aEditor, aEventName)
|
||||
{
|
||||
aEditor.removeEventListener(aEventName, this.editingEvents[aEventName],
|
||||
false);
|
||||
this.editingEvents[aEventName] = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle keypress events in the editor.
|
||||
* @param aEvent
|
||||
* The keyboard event.
|
||||
*/
|
||||
handleEditorKeypress: function TP_handleEditorKeypress(aEvent)
|
||||
{
|
||||
if (aEvent.which == this.window.KeyEvent.DOM_VK_RETURN) {
|
||||
this.saveEditor();
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
} else if (aEvent.keyCode == this.window.KeyEvent.DOM_VK_ESCAPE) {
|
||||
this.closeEditor();
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Close the editor and cleanup.
|
||||
*/
|
||||
closeEditor: function TP_closeEditor()
|
||||
{
|
||||
if (!this.treeBrowserDocument) // already closed, bug 706092
|
||||
return;
|
||||
|
||||
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
|
||||
|
||||
let editorInput =
|
||||
this.treeBrowserDocument.getElementById("attribute-editor-input");
|
||||
|
||||
// remove highlight from attribute-value node in tree
|
||||
this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
|
||||
|
||||
// hide editor
|
||||
this.removeClass(editor, "editing");
|
||||
|
||||
// stop listening for editor specific events
|
||||
this.unbindEditorEvent(editor, "click");
|
||||
this.unbindEditorEvent(editor, "dblclick");
|
||||
this.unbindEditorEvent(editor, "keypress");
|
||||
|
||||
// clean up after the editor
|
||||
editorInput.value = "";
|
||||
editorInput.blur();
|
||||
this.editingContext = null;
|
||||
this.editingEvents = {};
|
||||
|
||||
// event notification
|
||||
Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
|
||||
null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Commit the edits made in the editor, then close it.
|
||||
*/
|
||||
saveEditor: function TP_saveEditor()
|
||||
{
|
||||
let editorInput =
|
||||
this.treeBrowserDocument.getElementById("attribute-editor-input");
|
||||
let dirty = false;
|
||||
|
||||
if (this.hasClass(this.editingContext.attrObj, "nodeValue")) {
|
||||
// set the new attribute value on the original target DOM element
|
||||
this.editingContext.repObj.setAttribute(this.editingContext.attrName,
|
||||
editorInput.value);
|
||||
|
||||
// update the HTML tree attribute value
|
||||
this.editingContext.attrObj.innerHTML = editorInput.value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (this.hasClass(this.editingContext.attrObj, "nodeName")) {
|
||||
// remove the original attribute from the original target DOM element
|
||||
this.editingContext.repObj.removeAttribute(this.editingContext.attrName);
|
||||
|
||||
// set the new attribute value on the original target DOM element
|
||||
this.editingContext.repObj.setAttribute(editorInput.value,
|
||||
this.editingContext.attrValue);
|
||||
|
||||
// update the HTML tree attribute value
|
||||
this.editingContext.attrObj.innerHTML = editorInput.value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
this.IUI.isDirty = dirty;
|
||||
this.IUI.nodeChanged("treepanel");
|
||||
|
||||
// event notification
|
||||
Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
|
||||
null);
|
||||
|
||||
this.closeEditor();
|
||||
},
|
||||
|
||||
/**
|
||||
* Simple tree select method.
|
||||
* @param aNode the DOM node in the content document to select.
|
||||
* @param aScroll boolean scroll to the visible node?
|
||||
*/
|
||||
select: function TP_select(aNode, aScroll, aFrom)
|
||||
{
|
||||
if (this.ioBox) {
|
||||
this.ioBox.select(aNode, true, aFrom != "treepanel", aScroll);
|
||||
} else {
|
||||
this.pendingSelection = { node: aNode, scroll: aScroll };
|
||||
}
|
||||
},
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//// Utility functions
|
||||
|
||||
/**
|
||||
* Does the given object have a class attribute?
|
||||
* @param aNode
|
||||
* the DOM node.
|
||||
* @param aClass
|
||||
* The class string.
|
||||
* @returns boolean
|
||||
*/
|
||||
hasClass: function TP_hasClass(aNode, aClass)
|
||||
{
|
||||
if (!(aNode instanceof this.window.Element))
|
||||
return false;
|
||||
return aNode.classList.contains(aClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the class name to the given object.
|
||||
* @param aNode
|
||||
* the DOM node.
|
||||
* @param aClass
|
||||
* The class string.
|
||||
*/
|
||||
addClass: function TP_addClass(aNode, aClass)
|
||||
{
|
||||
if (aNode instanceof this.window.Element)
|
||||
aNode.classList.add(aClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the class name from the given object
|
||||
* @param aNode
|
||||
* the DOM node.
|
||||
* @param aClass
|
||||
* The class string.
|
||||
*/
|
||||
removeClass: function TP_removeClass(aNode, aClass)
|
||||
{
|
||||
if (aNode instanceof this.window.Element)
|
||||
aNode.classList.remove(aClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the "repObject" from the HTML panel's domplate-constructed DOM node.
|
||||
* In this system, a "repObject" is the Object being Represented by the box
|
||||
* object. It is the "real" object that we're building our facade around.
|
||||
*
|
||||
* @param element
|
||||
* The element in the HTML panel the user clicked.
|
||||
* @returns either a real node or null
|
||||
*/
|
||||
getRepObject: function TP_getRepObject(element)
|
||||
{
|
||||
let target = null;
|
||||
for (let child = element; child; child = child.parentNode) {
|
||||
if (this.hasClass(child, "repTarget"))
|
||||
target = child;
|
||||
|
||||
if (child.repObject) {
|
||||
if (!target && this.hasClass(child.repObject, "repIgnore"))
|
||||
break;
|
||||
else
|
||||
return child.repObject;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a node box from the tree view.
|
||||
* @param aElement
|
||||
* The DOM node to remove from the HTML IOBox.
|
||||
*/
|
||||
deleteChildBox: function TP_deleteChildBox(aElement)
|
||||
{
|
||||
let childBox = this.ioBox.findObjectBox(aElement);
|
||||
if (!childBox) {
|
||||
return;
|
||||
}
|
||||
childBox.parentNode.removeChild(childBox);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destructor function. Cleanup.
|
||||
*/
|
||||
destroy: function TP_destroy()
|
||||
{
|
||||
if (this.isOpen()) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
domplateUtils.setDOM(null);
|
||||
|
||||
if (this.DOMHelpers) {
|
||||
this.DOMHelpers.destroy();
|
||||
delete this.DOMHelpers;
|
||||
}
|
||||
|
||||
if (this.treePanelDiv) {
|
||||
this.treePanelDiv.ownerPanel = null;
|
||||
let parent = this.treePanelDiv.parentNode;
|
||||
parent.removeChild(this.treePanelDiv);
|
||||
delete this.treePanelDiv;
|
||||
delete this.treeBrowserDocument;
|
||||
}
|
||||
|
||||
if (this.treeIFrame) {
|
||||
this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false);
|
||||
this.treeIFrame.removeEventListener("click", this.onTreeClick, false);
|
||||
let parent = this.treeIFrame.parentNode;
|
||||
parent.removeChild(this.treeIFrame);
|
||||
delete this.treeIFrame;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* DOMHelpers
|
||||
* Makes DOM traversal easier. Goes through iframes.
|
||||
*
|
||||
* @constructor
|
||||
* @param nsIDOMWindow aWindow
|
||||
* The content window, owning the document to traverse.
|
||||
*/
|
||||
function DOMHelpers(aWindow) {
|
||||
this.window = aWindow;
|
||||
};
|
||||
|
||||
DOMHelpers.prototype = {
|
||||
getParentObject: function Helpers_getParentObject(node)
|
||||
{
|
||||
let parentNode = node ? node.parentNode : null;
|
||||
|
||||
if (!parentNode) {
|
||||
// Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
|
||||
// and Notation. top level windows have no parentNode
|
||||
if (node && node == this.window.Node.DOCUMENT_NODE) {
|
||||
// document type
|
||||
if (node.defaultView) {
|
||||
let embeddingFrame = node.defaultView.frameElement;
|
||||
if (embeddingFrame)
|
||||
return embeddingFrame.parentNode;
|
||||
}
|
||||
}
|
||||
// a Document object without a parentNode or window
|
||||
return null; // top level has no parent
|
||||
}
|
||||
|
||||
if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
|
||||
if (parentNode.defaultView) {
|
||||
return parentNode.defaultView.frameElement;
|
||||
}
|
||||
// parent is document element, but no window at defaultView.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!parentNode.localName)
|
||||
return null;
|
||||
|
||||
return parentNode;
|
||||
},
|
||||
|
||||
getChildObject: function Helpers_getChildObject(node, index, previousSibling,
|
||||
showTextNodesWithWhitespace)
|
||||
{
|
||||
if (!node)
|
||||
return null;
|
||||
|
||||
if (node.contentDocument) {
|
||||
// then the node is a frame
|
||||
if (index == 0) {
|
||||
return node.contentDocument.documentElement; // the node's HTMLElement
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node instanceof this.window.GetSVGDocument) {
|
||||
let svgDocument = node.getSVGDocument();
|
||||
if (svgDocument) {
|
||||
// then the node is a frame
|
||||
if (index == 0) {
|
||||
return svgDocument.documentElement; // the node's SVGElement
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let child = null;
|
||||
if (previousSibling) // then we are walking
|
||||
child = this.getNextSibling(previousSibling);
|
||||
else
|
||||
child = this.getFirstChild(node);
|
||||
|
||||
if (showTextNodesWithWhitespace)
|
||||
return child;
|
||||
|
||||
for (; child; child = this.getNextSibling(child)) {
|
||||
if (!this.isWhitespaceText(child))
|
||||
return child;
|
||||
}
|
||||
|
||||
return null; // we have no children worth showing.
|
||||
},
|
||||
|
||||
getFirstChild: function Helpers_getFirstChild(node)
|
||||
{
|
||||
let SHOW_ALL = Components.interfaces.nsIDOMNodeFilter.SHOW_ALL;
|
||||
this.treeWalker = node.ownerDocument.createTreeWalker(node,
|
||||
SHOW_ALL, null, false);
|
||||
return this.treeWalker.firstChild();
|
||||
},
|
||||
|
||||
getNextSibling: function Helpers_getNextSibling(node)
|
||||
{
|
||||
let next = this.treeWalker.nextSibling();
|
||||
|
||||
if (!next)
|
||||
delete this.treeWalker;
|
||||
|
||||
return next;
|
||||
},
|
||||
|
||||
isWhitespaceText: function Helpers_isWhitespaceText(node)
|
||||
{
|
||||
return node.nodeType == this.window.Node.TEXT_NODE &&
|
||||
!/[^\s]/.exec(node.nodeValue);
|
||||
},
|
||||
|
||||
destroy: function Helpers_destroy()
|
||||
{
|
||||
delete this.window;
|
||||
delete this.treeWalker;
|
||||
}
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,18 +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/. -->
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" href="chrome://browser/skin/devtools/htmlpanel.css" type="text/css"/>
|
||||
</head>
|
||||
<body role="application">
|
||||
<div id="attribute-editor">
|
||||
<input id="attribute-editor-input" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -13,12 +13,12 @@ var EXPORTED_SYMBOLS = ["InspectorUI"];
|
|||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/TreePanel.jsm");
|
||||
Cu.import("resource:///modules/devtools/MarkupView.jsm");
|
||||
Cu.import("resource:///modules/highlighter.jsm");
|
||||
Cu.import("resource:///modules/devtools/LayoutView.jsm");
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
Cu.import("resource:///modules/devtools/EventEmitter.jsm");
|
||||
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
|
||||
|
||||
// Inspector notifications dispatched through the nsIObserverService.
|
||||
const INSPECTOR_NOTIFICATIONS = {
|
||||
|
|
|
@ -3,11 +3,9 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/inspector.html (highlighter/inspector.html)
|
||||
content/browser/devtools/markup-view.xhtml (markupview/markup-view.xhtml)
|
||||
content/browser/devtools/markup-view.css (markupview/markup-view.css)
|
||||
content/browser/NetworkPanel.xhtml (webconsole/NetworkPanel.xhtml)
|
||||
content/browser/devtools/HUDService-content.js (webconsole/HUDService-content.js)
|
||||
content/browser/devtools/webconsole.js (webconsole/webconsole.js)
|
||||
* content/browser/devtools/webconsole.xul (webconsole/webconsole.xul)
|
||||
* content/browser/scratchpad.xul (scratchpad/scratchpad.xul)
|
||||
|
|
|
@ -129,6 +129,8 @@ LayoutView.prototype = {
|
|||
this.iframe.removeEventListener("keypress", this.bound_handleKeypress, true);
|
||||
this.inspector.chromeWindow.removeEventListener("message", this.onMessage, true);
|
||||
this.close();
|
||||
this.sizeHeadingLabel = null;
|
||||
this.sizeLabel = null;
|
||||
this.iframe = null;
|
||||
this.view.parentNode.removeChild(this.view);
|
||||
},
|
||||
|
@ -159,6 +161,10 @@ LayoutView.prototype = {
|
|||
this.documentReady = true;
|
||||
this.doc = this.iframe.contentDocument;
|
||||
|
||||
// Save reference to the labels displaying size of the node.
|
||||
this.sizeLabel = this.doc.querySelector(".size > span");
|
||||
this.sizeHeadingLabel = this.doc.getElementById("element-size");
|
||||
|
||||
// We can't do that earlier because open() and close() need to do stuff
|
||||
// inside the iframe.
|
||||
|
||||
|
@ -299,10 +305,9 @@ LayoutView.prototype = {
|
|||
let width = Math.round(clientRect.width);
|
||||
let height = Math.round(clientRect.height);
|
||||
|
||||
let elt = this.doc.querySelector("#element-size");
|
||||
let newLabel = width + "x" + height;
|
||||
if (elt.textContent != newLabel) {
|
||||
elt.textContent = newLabel;
|
||||
if (this.sizeHeadingLabel.textContent != newLabel) {
|
||||
this.sizeHeadingLabel.textContent = newLabel;
|
||||
}
|
||||
|
||||
// If the view is closed, no need to do anything more.
|
||||
|
@ -312,7 +317,6 @@ LayoutView.prototype = {
|
|||
let style = this.browser.contentWindow.getComputedStyle(node);;
|
||||
|
||||
for (let i in this.map) {
|
||||
let selector = this.map[i].selector;
|
||||
let property = this.map[i].property;
|
||||
this.map[i].value = parseInt(style.getPropertyValue(property));
|
||||
}
|
||||
|
@ -326,6 +330,10 @@ LayoutView.prototype = {
|
|||
for (let i in this.map) {
|
||||
let selector = this.map[i].selector;
|
||||
let span = this.doc.querySelector(selector);
|
||||
if (span.textContent.length > 0 &&
|
||||
span.textContent == this.map[i].value) {
|
||||
continue;
|
||||
}
|
||||
span.textContent = this.map[i].value;
|
||||
}
|
||||
|
||||
|
@ -335,7 +343,10 @@ LayoutView.prototype = {
|
|||
height -= this.map.borderTop.value + this.map.borderBottom.value +
|
||||
this.map.paddingTop.value + this.map.paddingBottom.value;
|
||||
|
||||
this.doc.querySelector(".size > span").textContent = width + "x" + height;
|
||||
let newValue = width + "x" + height;
|
||||
if (this.sizeLabel.textContent != newValue) {
|
||||
this.sizeLabel.textContent = newValue;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@ const Cu = Components.utils;
|
|||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/devtools/FloatingScrollbars.jsm");
|
||||
Cu.import("resource:///modules/devtools/EventEmitter.jsm");
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
|
||||
|
||||
|
@ -67,6 +68,13 @@ let ResponsiveUIManager = {
|
|||
default:
|
||||
}
|
||||
},
|
||||
|
||||
get events() {
|
||||
if (!this._eventEmitter) {
|
||||
this._eventEmitter = new EventEmitter();
|
||||
}
|
||||
return this._eventEmitter;
|
||||
},
|
||||
}
|
||||
|
||||
let presets = [
|
||||
|
@ -162,6 +170,7 @@ function ResponsiveUI(aWindow, aTab)
|
|||
} catch(e) {}
|
||||
|
||||
switchToFloatingScrollbars(this.tab);
|
||||
ResponsiveUIManager.events.emit("on", this.tab, this);
|
||||
}
|
||||
|
||||
ResponsiveUI.prototype = {
|
||||
|
@ -215,6 +224,7 @@ ResponsiveUI.prototype = {
|
|||
this.stack.removeAttribute("responsivemode");
|
||||
|
||||
delete this.tab.__responsiveUI;
|
||||
ResponsiveUIManager.events.emit("off", this.tab, this);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,15 +45,23 @@ include $(DEPTH)/config/autoconf.mk
|
|||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_responsiveui.js \
|
||||
browser_responsiveruleview.js \
|
||||
browser_responsive_cmd.js \
|
||||
browser_responsivecomputedview.js \
|
||||
browser_responsiveuiaddcustompreset.js \
|
||||
head.js \
|
||||
helpers.js \
|
||||
$(NULL)
|
||||
|
||||
# Disabled on Mac for frequent intermittent failures
|
||||
ifneq ($(OS_ARCH), Darwin)
|
||||
_BROWSER_FILES += \
|
||||
browser_responsiveui.js \
|
||||
browser_responsiveuiaddcustompreset.js \
|
||||
$(NULL)
|
||||
else
|
||||
$(warning browser_responsiveui.js is disabled on OS X for intermittent failures. Bug 798772) \
|
||||
$(warning browser_responsiveuiaddcustompreset.js is disabled on OS X for intermittent failures. Bugs 798775, 798777)
|
||||
endif
|
||||
|
||||
libs:: $(_BROWSER_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
function test() {
|
||||
let instance, widthBeforeClose, heightBeforeClose;
|
||||
let events = ResponsiveUI.ResponsiveUIManager.events;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -16,8 +17,8 @@ function test() {
|
|||
|
||||
function startTest() {
|
||||
document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
|
||||
events.once("on", function() {executeSoon(onUIOpen)});
|
||||
synthesizeKeyFromKeyTag("key_responsiveUI");
|
||||
executeSoon(onUIOpen);
|
||||
}
|
||||
|
||||
function onUIOpen() {
|
||||
|
@ -118,14 +119,13 @@ function test() {
|
|||
widthBeforeClose = content.innerWidth;
|
||||
heightBeforeClose = content.innerHeight;
|
||||
|
||||
events.once("off", function() {executeSoon(restart)});
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
|
||||
executeSoon(restart);
|
||||
}
|
||||
|
||||
function restart() {
|
||||
events.once("on", function() {executeSoon(onUIOpen2)});
|
||||
synthesizeKeyFromKeyTag("key_responsiveUI");
|
||||
executeSoon(onUIOpen2);
|
||||
}
|
||||
|
||||
function onUIOpen2() {
|
||||
|
@ -138,8 +138,8 @@ function test() {
|
|||
is(content.innerWidth, widthBeforeClose, "width restored.");
|
||||
is(content.innerHeight, heightBeforeClose, "height restored.");
|
||||
|
||||
events.once("off", function() {executeSoon(finishUp)});
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
executeSoon(finishUp);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/* 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/. */
|
||||
|
||||
const EXPORTED_SYMBOLS = ["DOMHelpers"];
|
||||
|
||||
/**
|
||||
* DOMHelpers
|
||||
* Makes DOM traversal easier. Goes through iframes.
|
||||
*
|
||||
* @constructor
|
||||
* @param nsIDOMWindow aWindow
|
||||
* The content window, owning the document to traverse.
|
||||
*/
|
||||
function DOMHelpers(aWindow) {
|
||||
this.window = aWindow;
|
||||
};
|
||||
|
||||
DOMHelpers.prototype = {
|
||||
getParentObject: function Helpers_getParentObject(node)
|
||||
{
|
||||
let parentNode = node ? node.parentNode : null;
|
||||
|
||||
if (!parentNode) {
|
||||
// Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
|
||||
// and Notation. top level windows have no parentNode
|
||||
if (node && node == this.window.Node.DOCUMENT_NODE) {
|
||||
// document type
|
||||
if (node.defaultView) {
|
||||
let embeddingFrame = node.defaultView.frameElement;
|
||||
if (embeddingFrame)
|
||||
return embeddingFrame.parentNode;
|
||||
}
|
||||
}
|
||||
// a Document object without a parentNode or window
|
||||
return null; // top level has no parent
|
||||
}
|
||||
|
||||
if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
|
||||
if (parentNode.defaultView) {
|
||||
return parentNode.defaultView.frameElement;
|
||||
}
|
||||
// parent is document element, but no window at defaultView.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!parentNode.localName)
|
||||
return null;
|
||||
|
||||
return parentNode;
|
||||
},
|
||||
|
||||
getChildObject: function Helpers_getChildObject(node, index, previousSibling,
|
||||
showTextNodesWithWhitespace)
|
||||
{
|
||||
if (!node)
|
||||
return null;
|
||||
|
||||
if (node.contentDocument) {
|
||||
// then the node is a frame
|
||||
if (index == 0) {
|
||||
return node.contentDocument.documentElement; // the node's HTMLElement
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node instanceof this.window.GetSVGDocument) {
|
||||
let svgDocument = node.getSVGDocument();
|
||||
if (svgDocument) {
|
||||
// then the node is a frame
|
||||
if (index == 0) {
|
||||
return svgDocument.documentElement; // the node's SVGElement
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let child = null;
|
||||
if (previousSibling) // then we are walking
|
||||
child = this.getNextSibling(previousSibling);
|
||||
else
|
||||
child = this.getFirstChild(node);
|
||||
|
||||
if (showTextNodesWithWhitespace)
|
||||
return child;
|
||||
|
||||
for (; child; child = this.getNextSibling(child)) {
|
||||
if (!this.isWhitespaceText(child))
|
||||
return child;
|
||||
}
|
||||
|
||||
return null; // we have no children worth showing.
|
||||
},
|
||||
|
||||
getFirstChild: function Helpers_getFirstChild(node)
|
||||
{
|
||||
let SHOW_ALL = Components.interfaces.nsIDOMNodeFilter.SHOW_ALL;
|
||||
this.treeWalker = node.ownerDocument.createTreeWalker(node,
|
||||
SHOW_ALL, null, false);
|
||||
return this.treeWalker.firstChild();
|
||||
},
|
||||
|
||||
getNextSibling: function Helpers_getNextSibling(node)
|
||||
{
|
||||
let next = this.treeWalker.nextSibling();
|
||||
|
||||
if (!next)
|
||||
delete this.treeWalker;
|
||||
|
||||
return next;
|
||||
},
|
||||
|
||||
isWhitespaceText: function Helpers_isWhitespaceText(node)
|
||||
{
|
||||
return node.nodeType == this.window.Node.TEXT_NODE &&
|
||||
!/[^\s]/.exec(node.nodeValue);
|
||||
},
|
||||
|
||||
destroy: function Helpers_destroy()
|
||||
{
|
||||
delete this.window;
|
||||
delete this.treeWalker;
|
||||
}
|
||||
};
|
|
@ -7,9 +7,7 @@
|
|||
const EXPORTED_SYMBOLS = [ "DeveloperToolbar" ];
|
||||
|
||||
const NS_XHTML = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const WEBCONSOLE_CONTENT_SCRIPT_URL =
|
||||
"chrome://browser/content/devtools/HUDService-content.js";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -26,6 +24,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "gcli",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
|
||||
"resource:///modules/devtools/CmdCmd.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageErrorListener",
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
|
||||
/**
|
||||
* Due to a number of panel bugs we need a way to check if we are running on
|
||||
* Linux. See the comments for TooltipPanel and OutputPanel for further details.
|
||||
|
@ -56,7 +57,8 @@ function DeveloperToolbar(aChromeWindow, aToolbarElement)
|
|||
this._lastState = NOTIFICATIONS.HIDE;
|
||||
this._pendingShowCallback = undefined;
|
||||
this._pendingHide = false;
|
||||
this._errorsCount = {};
|
||||
this._errorsCount = Object.create(null);
|
||||
this._errorListeners = Object.create(null);
|
||||
this._webConsoleButton = this._doc
|
||||
.getElementById("developer-toolbar-webconsole");
|
||||
|
||||
|
@ -88,9 +90,6 @@ const NOTIFICATIONS = {
|
|||
*/
|
||||
DeveloperToolbar.prototype.NOTIFICATIONS = NOTIFICATIONS;
|
||||
|
||||
DeveloperToolbar.prototype._contentMessageListeners =
|
||||
["WebConsole:CachedMessages", "WebConsole:PageError"];
|
||||
|
||||
/**
|
||||
* Is the toolbar open?
|
||||
*/
|
||||
|
@ -285,21 +284,18 @@ DeveloperToolbar.prototype._initErrorsCount = function DT__initErrorsCount(aTab)
|
|||
return;
|
||||
}
|
||||
|
||||
let messageManager = aTab.linkedBrowser.messageManager;
|
||||
messageManager.loadFrameScript(WEBCONSOLE_CONTENT_SCRIPT_URL, true);
|
||||
let window = aTab.linkedBrowser.contentWindow;
|
||||
let listener = new PageErrorListener(window, {
|
||||
onPageError: this._onPageError.bind(this, tabId),
|
||||
});
|
||||
listener.init();
|
||||
|
||||
this._errorListeners[tabId] = listener;
|
||||
this._errorsCount[tabId] = 0;
|
||||
|
||||
this._contentMessageListeners.forEach(function(aName) {
|
||||
messageManager.addMessageListener(aName, this);
|
||||
}, this);
|
||||
let messages = listener.getCachedMessages();
|
||||
messages.forEach(this._onPageError.bind(this, tabId));
|
||||
|
||||
let message = {
|
||||
features: ["PageError"],
|
||||
cachedMessages: ["PageError"],
|
||||
};
|
||||
|
||||
this.sendMessageToTab(aTab, "WebConsole:Init", message);
|
||||
this._updateErrorsCount();
|
||||
};
|
||||
|
||||
|
@ -319,14 +315,10 @@ DeveloperToolbar.prototype._stopErrorsCount = function DT__stopErrorsCount(aTab)
|
|||
return;
|
||||
}
|
||||
|
||||
this.sendMessageToTab(aTab, "WebConsole:Destroy", {});
|
||||
|
||||
let messageManager = aTab.linkedBrowser.messageManager;
|
||||
this._contentMessageListeners.forEach(function(aName) {
|
||||
messageManager.removeMessageListener(aName, this);
|
||||
}, this);
|
||||
|
||||
this._errorListeners[tabId].destroy();
|
||||
delete this._errorListeners[tabId];
|
||||
delete this._errorsCount[tabId];
|
||||
|
||||
this._updateErrorsCount();
|
||||
};
|
||||
|
||||
|
@ -434,61 +426,13 @@ DeveloperToolbar.prototype.handleEvent = function DT_handleEvent(aEvent)
|
|||
};
|
||||
|
||||
/**
|
||||
* The handler of messages received from the nsIMessageManager.
|
||||
*
|
||||
* @param object aMessage the message received from the content process.
|
||||
*/
|
||||
DeveloperToolbar.prototype.receiveMessage = function DT_receiveMessage(aMessage)
|
||||
{
|
||||
if (!aMessage.json || !(aMessage.json.hudId in this._errorsCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tabId = aMessage.json.hudId;
|
||||
let errors = this._errorsCount[tabId];
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "WebConsole:PageError":
|
||||
this._onPageError(tabId, aMessage.json.pageError);
|
||||
break;
|
||||
case "WebConsole:CachedMessages":
|
||||
aMessage.json.messages.forEach(this._onPageError.bind(this, tabId));
|
||||
break;
|
||||
}
|
||||
|
||||
if (errors != this._errorsCount[tabId]) {
|
||||
this._updateErrorsCount(tabId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a message to the content process using the nsIMessageManager of the
|
||||
* given tab.
|
||||
*
|
||||
* @param nsIDOMNode aTab the tab you want to send a message to.
|
||||
* @param string aName the name of the message you want to send.
|
||||
* @param object aMessage the message to send.
|
||||
*/
|
||||
DeveloperToolbar.prototype.sendMessageToTab =
|
||||
function DT_sendMessageToTab(aTab, aName, aMessage)
|
||||
{
|
||||
let tabId = aTab.linkedPanel;
|
||||
aMessage.hudId = tabId;
|
||||
if (!("id" in aMessage)) {
|
||||
aMessage.id = "DevToolbar-" + this.sequenceId;
|
||||
}
|
||||
|
||||
aTab.linkedBrowser.messageManager.sendAsyncMessage(aName, aMessage);
|
||||
};
|
||||
|
||||
/**
|
||||
* Process a "WebConsole:PageError" message received from the given tab. This
|
||||
* method counts the JavaScript exceptions received.
|
||||
* Count a page error received for the currently selected tab. This
|
||||
* method counts the JavaScript exceptions received and CSS errors/warnings.
|
||||
*
|
||||
* @private
|
||||
* @param string aTabId the ID of the tab from where the page error comes.
|
||||
* @param object aPageError the page error object received from the content
|
||||
* process.
|
||||
* @param object aPageError the page error object received from the
|
||||
* PageErrorListener.
|
||||
*/
|
||||
DeveloperToolbar.prototype._onPageError =
|
||||
function DT__onPageError(aTabId, aPageError)
|
||||
|
@ -501,6 +445,7 @@ function DT__onPageError(aTabId, aPageError)
|
|||
}
|
||||
|
||||
this._errorsCount[aTabId]++;
|
||||
this._updateErrorsCount(aTabId);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -638,7 +583,6 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
|
|||
this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
|
||||
this._frame.id = "gcli-output-frame";
|
||||
this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlineoutput.xhtml");
|
||||
this._frame.setAttribute("flex", "1");
|
||||
this._panel.appendChild(this._frame);
|
||||
|
||||
this.displayedOutput = undefined;
|
||||
|
@ -674,6 +618,33 @@ OutputPanel.prototype._onload = function OP_onload()
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine the scrollbar width in the current document.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
Object.defineProperty(OutputPanel.prototype, 'scrollbarWidth', {
|
||||
get: function() {
|
||||
if (this.__scrollbarWidth) {
|
||||
return this.__scrollbarWidth;
|
||||
}
|
||||
|
||||
let hbox = this.document.createElementNS(XUL_NS, "hbox");
|
||||
hbox.setAttribute("style", "height: 0%; overflow: hidden");
|
||||
|
||||
let scrollbar = this.document.createElementNS(XUL_NS, "scrollbar");
|
||||
scrollbar.setAttribute("orient", "vertical");
|
||||
hbox.appendChild(scrollbar);
|
||||
|
||||
this.document.documentElement.appendChild(hbox);
|
||||
this.__scrollbarWidth = scrollbar.clientWidth;
|
||||
this.document.documentElement.removeChild(hbox);
|
||||
|
||||
return this.__scrollbarWidth;
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Prevent the popup from hiding if it is not permitted via this.canHide.
|
||||
*/
|
||||
|
@ -691,17 +662,15 @@ OutputPanel.prototype._onpopuphiding = function OP_onpopuphiding(aEvent)
|
|||
*/
|
||||
OutputPanel.prototype.show = function OP_show()
|
||||
{
|
||||
// This is nasty, but displaying the panel causes it to re-flow, which can
|
||||
// change the size it should be, so we need to resize the iframe after the
|
||||
// panel has displayed
|
||||
this._panel.ownerDocument.defaultView.setTimeout(function() {
|
||||
this._resize();
|
||||
}.bind(this), 0);
|
||||
|
||||
if (isLinux) {
|
||||
this.canHide = false;
|
||||
}
|
||||
|
||||
// We need to reset the iframe size in order for future size calculations to
|
||||
// be correct
|
||||
this._frame.style.minHeight = this._frame.style.maxHeight = 0;
|
||||
this._frame.style.minWidth = 0;
|
||||
|
||||
this._panel.openPopup(this._input, "before_start", 0, 0, false, false, null);
|
||||
this._resize();
|
||||
|
||||
|
@ -718,8 +687,38 @@ OutputPanel.prototype._resize = function CLP_resize()
|
|||
return
|
||||
}
|
||||
|
||||
this._frame.height = this.document.body.scrollHeight;
|
||||
this._frame.width = this._input.clientWidth + 2;
|
||||
// Set max panel width to match any content with a max of the width of the
|
||||
// browser window.
|
||||
let maxWidth = this._panel.ownerDocument.documentElement.clientWidth;
|
||||
let width = Math.min(maxWidth, this.document.documentElement.scrollWidth);
|
||||
|
||||
// Add scrollbar width to content size in case a scrollbar is needed.
|
||||
width += this.scrollbarWidth;
|
||||
|
||||
// Set the width of the iframe.
|
||||
this._frame.style.minWidth = width + "px";
|
||||
|
||||
// browserAdjustment is used to correct the panel height according to the
|
||||
// browsers borders etc.
|
||||
const browserAdjustment = 15;
|
||||
|
||||
// Set max panel height to match any content with a max of the height of the
|
||||
// browser window.
|
||||
let maxHeight =
|
||||
this._panel.ownerDocument.documentElement.clientHeight - browserAdjustment;
|
||||
let height = Math.min(maxHeight, this.document.documentElement.scrollHeight);
|
||||
|
||||
// Set the height of the iframe. Setting iframe.height does not work.
|
||||
this._frame.style.minHeight = this._frame.style.maxHeight = height + "px";
|
||||
|
||||
// Set the height and width of the panel to match the iframe.
|
||||
this._panel.sizeTo(width, height);
|
||||
|
||||
// Move the panel to the correct position in the case that it has been
|
||||
// positioned incorrectly.
|
||||
let screenX = this._input.boxObject.screenX;
|
||||
let screenY = this._toolbar.boxObject.screenY;
|
||||
this._panel.moveTo(screenX, screenY - height);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -47,8 +47,11 @@ function test() {
|
|||
|
||||
function addErrors() {
|
||||
expectUncaughtException();
|
||||
let button = content.document.querySelector("button");
|
||||
EventUtils.synthesizeMouse(button, 2, 2, {}, content);
|
||||
|
||||
waitForFocus(function() {
|
||||
let button = content.document.querySelector("button");
|
||||
EventUtils.synthesizeMouse(button, 2, 2, {}, content);
|
||||
}, content);
|
||||
|
||||
waitForValue({
|
||||
name: "button shows one more error after click in page",
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -18,12 +18,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
|||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
|
||||
"resource:///modules/WebConsoleUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "l10n", function() {
|
||||
return WebConsoleUtils.l10n;
|
||||
});
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
|
||||
|
||||
var EXPORTED_SYMBOLS = ["HUDService"];
|
||||
|
||||
|
@ -59,9 +57,6 @@ const MINIMUM_PAGE_HEIGHT = 50;
|
|||
// The default console height, as a ratio from the content window inner height.
|
||||
const DEFAULT_CONSOLE_HEIGHT = 0.33;
|
||||
|
||||
// This script is inserted into the content process.
|
||||
const CONTENT_SCRIPT_URL = "chrome://browser/content/devtools/HUDService-content.js";
|
||||
|
||||
// points to the file to load in the Web Console iframe.
|
||||
const UI_IFRAME_URL = "chrome://browser/content/devtools/webconsole.xul";
|
||||
|
||||
|
@ -172,7 +167,10 @@ HUD_SERVICE.prototype =
|
|||
let hud = this.getHudReferenceById(hudId);
|
||||
let document = hud.chromeDocument;
|
||||
|
||||
hud.destroy();
|
||||
hud.destroy(function() {
|
||||
let id = WebConsoleUtils.supportsString(hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-destroyed", null);
|
||||
});
|
||||
|
||||
delete this.hudReferences[hudId];
|
||||
|
||||
|
@ -199,9 +197,6 @@ HUD_SERVICE.prototype =
|
|||
contentWindow.focus();
|
||||
|
||||
HeadsUpDisplayUICommands.refreshCommand();
|
||||
|
||||
let id = WebConsoleUtils.supportsString(hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-destroyed", null);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -502,9 +497,11 @@ HUD_SERVICE.prototype =
|
|||
function WebConsole(aTab)
|
||||
{
|
||||
this.tab = aTab;
|
||||
this.chromeDocument = this.tab.ownerDocument;
|
||||
this.chromeWindow = this.chromeDocument.defaultView;
|
||||
this.hudId = "hud_" + this.tab.linkedPanel;
|
||||
this._onIframeLoad = this._onIframeLoad.bind(this);
|
||||
this._asyncRequests = {};
|
||||
this._init();
|
||||
this._initUI();
|
||||
}
|
||||
|
||||
WebConsole.prototype = {
|
||||
|
@ -514,6 +511,9 @@ WebConsole.prototype = {
|
|||
*/
|
||||
tab: null,
|
||||
|
||||
chromeWindow: null,
|
||||
chromeDocument: null,
|
||||
|
||||
/**
|
||||
* Getter for HUDService.lastFinishedRequestCallback.
|
||||
*
|
||||
|
@ -522,41 +522,12 @@ WebConsole.prototype = {
|
|||
*/
|
||||
get lastFinishedRequestCallback() HUDService.lastFinishedRequestCallback,
|
||||
|
||||
/**
|
||||
* Track callback functions registered for specific async requests sent to
|
||||
* the content process.
|
||||
*
|
||||
* @private
|
||||
* @type object
|
||||
*/
|
||||
_asyncRequests: null,
|
||||
|
||||
/**
|
||||
* Message names that the HUD listens for. These messages come from the remote
|
||||
* Web Console content script.
|
||||
*
|
||||
* @private
|
||||
* @type array
|
||||
*/
|
||||
_messageListeners: ["JSTerm:EvalObject", "WebConsole:ConsoleAPI",
|
||||
"WebConsole:CachedMessages", "WebConsole:PageError", "JSTerm:EvalResult",
|
||||
"JSTerm:AutocompleteProperties", "JSTerm:ClearOutput",
|
||||
"JSTerm:InspectObject", "WebConsole:NetworkActivity",
|
||||
"WebConsole:FileActivity", "WebConsole:LocationChange",
|
||||
"JSTerm:NonNativeConsoleAPI"],
|
||||
|
||||
/**
|
||||
* The xul:panel that holds the Web Console when it is positioned as a window.
|
||||
* @type nsIDOMElement
|
||||
*/
|
||||
consolePanel: null,
|
||||
|
||||
/**
|
||||
* The current tab location.
|
||||
* @type string
|
||||
*/
|
||||
contentLocation: "",
|
||||
|
||||
/**
|
||||
* Getter for the xul:popupset that holds any popups we open.
|
||||
* @type nsIDOMElement
|
||||
|
@ -577,22 +548,6 @@ WebConsole.prototype = {
|
|||
|
||||
get gViewSourceUtils() this.chromeWindow.gViewSourceUtils,
|
||||
|
||||
/**
|
||||
* Initialize the Web Console instance.
|
||||
* @private
|
||||
*/
|
||||
_init: function WC__init()
|
||||
{
|
||||
this.chromeDocument = this.tab.ownerDocument;
|
||||
this.chromeWindow = this.chromeDocument.defaultView;
|
||||
this.messageManager = this.tab.linkedBrowser.messageManager;
|
||||
this.hudId = "hud_" + this.tab.linkedPanel;
|
||||
this.notificationBox = this.chromeDocument
|
||||
.getElementById(this.tab.linkedPanel);
|
||||
|
||||
this._initUI();
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the Web Console UI. This method sets up the iframe.
|
||||
* @private
|
||||
|
@ -627,7 +582,6 @@ WebConsole.prototype = {
|
|||
|
||||
this.iframeWindow = this.iframe.contentWindow.wrappedJSObject;
|
||||
this.ui = new this.iframeWindow.WebConsoleFrame(this, position);
|
||||
this._setupMessageManager();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -772,8 +726,8 @@ WebConsole.prototype = {
|
|||
*/
|
||||
getPanelTitle: function WC_getPanelTitle()
|
||||
{
|
||||
return l10n.getFormatStr("webConsoleWindowTitleAndURL",
|
||||
[this.contentLocation]);
|
||||
let url = this.ui ? this.ui.contentLocation : "";
|
||||
return l10n.getFormatStr("webConsoleWindowTitleAndURL", [url]);
|
||||
},
|
||||
|
||||
positions: {
|
||||
|
@ -806,7 +760,7 @@ WebConsole.prototype = {
|
|||
|
||||
// get the node position index
|
||||
let nodeIdx = this.positions[aPosition];
|
||||
let nBox = this.notificationBox;
|
||||
let nBox = this.chromeDocument.getElementById(this.tab.linkedPanel);
|
||||
let node = nBox.childNodes[nodeIdx];
|
||||
|
||||
// check to see if console is already positioned in aPosition
|
||||
|
@ -903,126 +857,24 @@ WebConsole.prototype = {
|
|||
|
||||
/**
|
||||
* The clear output button handler.
|
||||
* @private
|
||||
*/
|
||||
onClearButton: function WC_onClearButton()
|
||||
_onClearButton: function WC__onClearButton()
|
||||
{
|
||||
this.ui.jsterm.clearOutput(true);
|
||||
this.chromeWindow.DeveloperToolbar.resetErrorsCount(this.tab);
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup the message manager used to communicate with the Web Console content
|
||||
* script. This method loads the content script, adds the message listeners
|
||||
* and initializes the connection to the content script.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_setupMessageManager: function WC__setupMessageManager()
|
||||
{
|
||||
this.messageManager.loadFrameScript(CONTENT_SCRIPT_URL, true);
|
||||
|
||||
this._messageListeners.forEach(function(aName) {
|
||||
this.messageManager.addMessageListener(aName, this.ui);
|
||||
}, this);
|
||||
|
||||
let message = {
|
||||
features: ["ConsoleAPI", "JSTerm", "PageError", "NetworkMonitor",
|
||||
"LocationChange"],
|
||||
cachedMessages: ["ConsoleAPI", "PageError"],
|
||||
NetworkMonitor: { monitorFileActivity: true },
|
||||
JSTerm: { notifyNonNativeConsoleAPI: true },
|
||||
preferences: {
|
||||
"NetworkMonitor.saveRequestAndResponseBodies":
|
||||
this.ui.saveRequestAndResponseBodies,
|
||||
},
|
||||
};
|
||||
|
||||
this.sendMessageToContent("WebConsole:Init", message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback method for when the Web Console initialization is complete. For
|
||||
* now this method sends the web-console-created notification using the
|
||||
* nsIObserverService.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onInitComplete: function WC__onInitComplete()
|
||||
{
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-created", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for messages that have an associated callback function. The
|
||||
* this.sendMessageToContent() allows one to provide a function to be invoked
|
||||
* when the content script replies to the message previously sent. This is the
|
||||
* method that invokes the callback.
|
||||
*
|
||||
* @see this.sendMessageToContent
|
||||
* @private
|
||||
* @param object aResponse
|
||||
* Message object received from the content script.
|
||||
*/
|
||||
_receiveMessageWithCallback:
|
||||
function WC__receiveMessageWithCallback(aResponse)
|
||||
{
|
||||
if (aResponse.id in this._asyncRequests) {
|
||||
let request = this._asyncRequests[aResponse.id];
|
||||
request.callback(aResponse, request.message);
|
||||
delete this._asyncRequests[aResponse.id];
|
||||
}
|
||||
else {
|
||||
Cu.reportError("receiveMessageWithCallback response for stale request " +
|
||||
"ID " + aResponse.id);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a message to the content script.
|
||||
*
|
||||
* @param string aName
|
||||
* The name of the message you want to send.
|
||||
*
|
||||
* @param object aMessage
|
||||
* The message object you want to send. This object needs to have no
|
||||
* cyclic references and it needs to be JSON-stringifiable.
|
||||
*
|
||||
* @param function [aCallback]
|
||||
* Optional function you want to have called when the content script
|
||||
* replies to your message. Your callback receives two arguments:
|
||||
* (1) the response object from the content script and (2) the message
|
||||
* you sent to the content script (which is aMessage here).
|
||||
*/
|
||||
sendMessageToContent:
|
||||
function WC_sendMessageToContent(aName, aMessage, aCallback)
|
||||
{
|
||||
aMessage.hudId = this.hudId;
|
||||
if (!("id" in aMessage)) {
|
||||
aMessage.id = "HUDChrome-" + HUDService.sequenceId();
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
this._asyncRequests[aMessage.id] = {
|
||||
name: aName,
|
||||
message: aMessage,
|
||||
callback: aCallback,
|
||||
};
|
||||
}
|
||||
this.messageManager.sendAsyncMessage(aName, aMessage);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the "WebConsole:LocationChange" message. If the Web Console is
|
||||
* Handler for page location changes. If the Web Console is
|
||||
* opened in a panel the panel title is updated.
|
||||
*
|
||||
* @param object aMessage
|
||||
* The message received from the content script. It needs to hold two
|
||||
* properties: location and title.
|
||||
* @param string aURI
|
||||
* New page location.
|
||||
* @param string aTitle
|
||||
* New page title.
|
||||
*/
|
||||
onLocationChange: function WC_onLocationChange(aMessage)
|
||||
onLocationChange: function WC_onLocationChange(aURI, aTitle)
|
||||
{
|
||||
this.contentLocation = aMessage.location;
|
||||
if (this.consolePanel) {
|
||||
this.consolePanel.label = this.getPanelTitle();
|
||||
}
|
||||
|
@ -1051,15 +903,13 @@ WebConsole.prototype = {
|
|||
/**
|
||||
* Destroy the object. Call this method to avoid memory leaks when the Web
|
||||
* Console is closed.
|
||||
*
|
||||
* @param function [aOnDestroy]
|
||||
* Optional function to invoke when the Web Console instance is
|
||||
* destroyed.
|
||||
*/
|
||||
destroy: function WC_destroy()
|
||||
destroy: function WC_destroy(aOnDestroy)
|
||||
{
|
||||
this.sendMessageToContent("WebConsole:Destroy", {});
|
||||
|
||||
this._messageListeners.forEach(function(aName) {
|
||||
this.messageManager.removeMessageListener(aName, this.ui);
|
||||
}, this);
|
||||
|
||||
// Make sure that the console panel does not try to call
|
||||
// deactivateHUDForContext() again.
|
||||
this.consoleWindowUnregisterOnHide = false;
|
||||
|
@ -1072,24 +922,31 @@ WebConsole.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
let onDestroy = function WC_onDestroyUI() {
|
||||
// Remove the iframe and the consolePanel if the Web Console is inside a
|
||||
// floating panel.
|
||||
if (this.consolePanel && this.consolePanel.parentNode) {
|
||||
this.consolePanel.hidePopup();
|
||||
this.consolePanel.parentNode.removeChild(this.consolePanel);
|
||||
this.consolePanel = null;
|
||||
}
|
||||
|
||||
if (this.iframe.parentNode) {
|
||||
this.iframe.parentNode.removeChild(this.iframe);
|
||||
}
|
||||
|
||||
if (this.splitter.parentNode) {
|
||||
this.splitter.parentNode.removeChild(this.splitter);
|
||||
}
|
||||
|
||||
aOnDestroy && aOnDestroy();
|
||||
}.bind(this);
|
||||
|
||||
if (this.ui) {
|
||||
this.ui.destroy();
|
||||
this.ui.destroy(onDestroy);
|
||||
}
|
||||
|
||||
// Remove the iframe and the consolePanel if the Web Console is inside a
|
||||
// floating panel.
|
||||
if (this.consolePanel && this.consolePanel.parentNode) {
|
||||
this.consolePanel.hidePopup();
|
||||
this.consolePanel.parentNode.removeChild(this.consolePanel);
|
||||
this.consolePanel = null;
|
||||
}
|
||||
|
||||
if (this.iframe.parentNode) {
|
||||
this.iframe.parentNode.removeChild(this.iframe);
|
||||
}
|
||||
|
||||
if (this.splitter.parentNode) {
|
||||
this.splitter.parentNode.removeChild(this.splitter);
|
||||
else {
|
||||
onDestroy();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -13,10 +13,8 @@ include $(DEPTH)/config/autoconf.mk
|
|||
EXTRA_JS_MODULES = \
|
||||
HUDService.jsm \
|
||||
PropertyPanel.jsm \
|
||||
NetworkHelper.jsm \
|
||||
NetworkPanel.jsm \
|
||||
AutocompletePopup.jsm \
|
||||
WebConsoleUtils.jsm \
|
||||
$(NULL)
|
||||
|
||||
TEST_DIRS = test
|
||||
|
|
|
@ -16,17 +16,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "mimeService", "@mozilla.org/mime;1",
|
|||
"nsIMIMEService");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetworkHelper",
|
||||
"resource:///modules/NetworkHelper.jsm");
|
||||
"resource://gre/modules/devtools/NetworkHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
|
||||
"resource:///modules/WebConsoleUtils.jsm");
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "l10n", function() {
|
||||
return WebConsoleUtils.l10n;
|
||||
});
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
|
||||
|
||||
var EXPORTED_SYMBOLS = ["NetworkPanel"];
|
||||
|
||||
|
@ -67,7 +66,6 @@ function NetworkPanel(aParent, aHttpActivity)
|
|||
self.panel.parentNode.removeChild(self.panel);
|
||||
self.panel = null;
|
||||
self.iframe = null;
|
||||
self.document = null;
|
||||
self.httpActivity = null;
|
||||
|
||||
if (self.linkNode) {
|
||||
|
@ -77,9 +75,17 @@ function NetworkPanel(aParent, aHttpActivity)
|
|||
}, false);
|
||||
|
||||
// Set the document object and update the content once the panel is loaded.
|
||||
this.panel.addEventListener("load", function onLoad() {
|
||||
self.panel.removeEventListener("load", onLoad, true);
|
||||
self.document = self.iframe.contentWindow.document;
|
||||
this.iframe.addEventListener("load", function onLoad() {
|
||||
if (!self.iframe) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.iframe.removeEventListener("load", onLoad, true);
|
||||
self.update();
|
||||
}, true);
|
||||
|
||||
this.panel.addEventListener("popupshown", function onPopupShown() {
|
||||
self.panel.removeEventListener("popupshown", onPopupShown, true);
|
||||
self.update();
|
||||
}, true);
|
||||
|
||||
|
@ -95,12 +101,6 @@ function NetworkPanel(aParent, aHttpActivity)
|
|||
|
||||
NetworkPanel.prototype =
|
||||
{
|
||||
/**
|
||||
* Callback is called once the NetworkPanel is processed completely. Used by
|
||||
* unit tests.
|
||||
*/
|
||||
isDoneCallback: null,
|
||||
|
||||
/**
|
||||
* The current state of the output.
|
||||
*/
|
||||
|
@ -119,6 +119,20 @@ NetworkPanel.prototype =
|
|||
|
||||
_contentType: null,
|
||||
|
||||
/**
|
||||
* Function callback invoked whenever the panel content is updated. This is
|
||||
* used only by tests.
|
||||
*
|
||||
* @private
|
||||
* @type function
|
||||
*/
|
||||
_onUpdate: null,
|
||||
|
||||
get document() {
|
||||
return this.iframe && this.iframe.contentWindow ?
|
||||
this.iframe.contentWindow.document : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Small helper function that is nearly equal to l10n.getFormatStr
|
||||
* except that it prefixes aName with "NetworkPanel.".
|
||||
|
@ -151,9 +165,8 @@ NetworkPanel.prototype =
|
|||
return this._contentType;
|
||||
}
|
||||
|
||||
let entry = this.httpActivity.log.entries[0];
|
||||
let request = entry.request;
|
||||
let response = entry.response;
|
||||
let request = this.httpActivity.request;
|
||||
let response = this.httpActivity.response;
|
||||
|
||||
let contentType = "";
|
||||
let types = response.content ?
|
||||
|
@ -237,7 +250,7 @@ NetworkPanel.prototype =
|
|||
*/
|
||||
get _isResponseCached()
|
||||
{
|
||||
return this.httpActivity.log.entries[0].response.status == 304;
|
||||
return this.httpActivity.response.status == 304;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -248,7 +261,7 @@ NetworkPanel.prototype =
|
|||
*/
|
||||
get _isRequestBodyFormData()
|
||||
{
|
||||
let requestBody = this.httpActivity.log.entries[0].request.postData.text;
|
||||
let requestBody = this.httpActivity.request.postData.text;
|
||||
return this._fromDataRegExp.test(requestBody);
|
||||
},
|
||||
|
||||
|
@ -342,9 +355,8 @@ NetworkPanel.prototype =
|
|||
*/
|
||||
_displayRequestHeader: function NP__displayRequestHeader()
|
||||
{
|
||||
let entry = this.httpActivity.log.entries[0];
|
||||
let request = entry.request;
|
||||
let requestTime = new Date(entry.startedDateTime);
|
||||
let request = this.httpActivity.request;
|
||||
let requestTime = new Date(this.httpActivity.startedDateTime);
|
||||
|
||||
this._appendTextNode("headUrl", request.url);
|
||||
this._appendTextNode("headMethod", request.method);
|
||||
|
@ -365,8 +377,9 @@ NetworkPanel.prototype =
|
|||
*
|
||||
* @returns void
|
||||
*/
|
||||
_displayRequestBody: function NP__displayRequestBody() {
|
||||
let postData = this.httpActivity.log.entries[0].request.postData;
|
||||
_displayRequestBody: function NP__displayRequestBody()
|
||||
{
|
||||
let postData = this.httpActivity.request.postData;
|
||||
this._displayNode("requestBody");
|
||||
this._appendTextNode("requestBodyContent", postData.text);
|
||||
},
|
||||
|
@ -377,8 +390,9 @@ NetworkPanel.prototype =
|
|||
*
|
||||
* @returns void
|
||||
*/
|
||||
_displayRequestForm: function NP__processRequestForm() {
|
||||
let postData = this.httpActivity.log.entries[0].request.postData.text;
|
||||
_displayRequestForm: function NP__processRequestForm()
|
||||
{
|
||||
let postData = this.httpActivity.request.postData.text;
|
||||
let requestBodyLines = postData.split("\n");
|
||||
let formData = requestBodyLines[requestBodyLines.length - 1].
|
||||
replace(/\+/g, " ").split("&");
|
||||
|
@ -418,9 +432,8 @@ NetworkPanel.prototype =
|
|||
*/
|
||||
_displayResponseHeader: function NP__displayResponseHeader()
|
||||
{
|
||||
let entry = this.httpActivity.log.entries[0];
|
||||
let timing = entry.timings;
|
||||
let response = entry.response;
|
||||
let timing = this.httpActivity.timings;
|
||||
let response = this.httpActivity.response;
|
||||
|
||||
this._appendTextNode("headStatus",
|
||||
[response.httpVersion, response.status,
|
||||
|
@ -454,16 +467,16 @@ NetworkPanel.prototype =
|
|||
_displayResponseImage: function NP__displayResponseImage()
|
||||
{
|
||||
let self = this;
|
||||
let entry = this.httpActivity.log.entries[0];
|
||||
let timing = entry.timings;
|
||||
let request = entry.request;
|
||||
let timing = this.httpActivity.timings;
|
||||
let request = this.httpActivity.request;
|
||||
let cached = "";
|
||||
|
||||
if (this._isResponseCached) {
|
||||
cached = "Cached";
|
||||
}
|
||||
|
||||
let imageNode = this.document.getElementById("responseImage" + cached +"Node");
|
||||
let imageNode = this.document.getElementById("responseImage" +
|
||||
cached + "Node");
|
||||
imageNode.setAttribute("src", request.url);
|
||||
|
||||
// This function is called to set the imageInfo.
|
||||
|
@ -499,9 +512,8 @@ NetworkPanel.prototype =
|
|||
*/
|
||||
_displayResponseBody: function NP__displayResponseBody()
|
||||
{
|
||||
let entry = this.httpActivity.log.entries[0];
|
||||
let timing = entry.timings;
|
||||
let response = entry.response;
|
||||
let timing = this.httpActivity.timings;
|
||||
let response = this.httpActivity.response;
|
||||
let cached = this._isResponseCached ? "Cached" : "";
|
||||
|
||||
this._appendTextNode("responseBody" + cached + "Info",
|
||||
|
@ -520,7 +532,7 @@ NetworkPanel.prototype =
|
|||
*/
|
||||
_displayResponseBodyUnknownType: function NP__displayResponseBodyUnknownType()
|
||||
{
|
||||
let timing = this.httpActivity.log.entries[0].timings;
|
||||
let timing = this.httpActivity.timings;
|
||||
|
||||
this._displayNode("responseBodyUnknownType");
|
||||
this._appendTextNode("responseBodyUnknownTypeInfo",
|
||||
|
@ -538,7 +550,7 @@ NetworkPanel.prototype =
|
|||
*/
|
||||
_displayNoResponseBody: function NP_displayNoResponseBody()
|
||||
{
|
||||
let timing = this.httpActivity.log.entries[0].timings;
|
||||
let timing = this.httpActivity.timings;
|
||||
|
||||
this._displayNode("responseNoBody");
|
||||
this._appendTextNode("responseNoBodyInfo",
|
||||
|
@ -554,15 +566,14 @@ NetworkPanel.prototype =
|
|||
{
|
||||
// After the iframe's contentWindow is ready, the document object is set.
|
||||
// If the document object is not available yet nothing needs to be updated.
|
||||
if (!this.document) {
|
||||
if (!this.document || !this.document.getElementById("headUrl")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let stages = this.httpActivity.meta.stages;
|
||||
let entry = this.httpActivity.log.entries[0];
|
||||
let timing = entry.timings;
|
||||
let request = entry.request;
|
||||
let response = entry.response;
|
||||
let updates = this.httpActivity.updates;
|
||||
let timing = this.httpActivity.timings;
|
||||
let request = this.httpActivity.request;
|
||||
let response = this.httpActivity.response;
|
||||
|
||||
switch (this._state) {
|
||||
case this._INIT:
|
||||
|
@ -572,7 +583,7 @@ NetworkPanel.prototype =
|
|||
|
||||
case this._DISPLAYED_REQUEST_HEADER:
|
||||
// Process the request body if there is one.
|
||||
if (!this.httpActivity.meta.discardRequestBody && request.postData) {
|
||||
if (!this.httpActivity.discardRequestBody && request.postData.text) {
|
||||
// Check if we send some form data. If so, display the form data special.
|
||||
if (this._isRequestBodyFormData) {
|
||||
this._displayRequestForm();
|
||||
|
@ -585,9 +596,6 @@ NetworkPanel.prototype =
|
|||
// FALL THROUGH
|
||||
|
||||
case this._DISPLAYED_REQUEST_BODY:
|
||||
// There is always a response header. Therefore we can skip here if
|
||||
// we don't have a response header yet and don't have to try updating
|
||||
// anything else in the NetworkPanel.
|
||||
if (!response.headers.length || !Object.keys(timing).length) {
|
||||
break;
|
||||
}
|
||||
|
@ -596,13 +604,13 @@ NetworkPanel.prototype =
|
|||
// FALL THROUGH
|
||||
|
||||
case this._DISPLAYED_RESPONSE_HEADER:
|
||||
if (stages.indexOf("REQUEST_STOP") == -1 ||
|
||||
stages.indexOf("TRANSACTION_CLOSE") == -1) {
|
||||
if (updates.indexOf("responseContent") == -1 ||
|
||||
updates.indexOf("eventTimings") == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
this._state = this._TRANSITION_CLOSED;
|
||||
if (this.httpActivity.meta.discardResponseBody) {
|
||||
if (this.httpActivity.discardResponseBody) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -618,9 +626,12 @@ NetworkPanel.prototype =
|
|||
else if (response.content.text) {
|
||||
this._displayResponseBody();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (this._onUpdate) {
|
||||
this._onUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ const Cu = Components.utils;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
|
||||
"resource:///modules/WebConsoleUtils.jsm");
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
|
||||
var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView"];
|
||||
|
||||
|
@ -27,7 +27,7 @@ var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView"];
|
|||
*/
|
||||
var PropertyTreeView = function() {
|
||||
this._rows = [];
|
||||
this._objectCache = {};
|
||||
this._objectActors = [];
|
||||
};
|
||||
|
||||
PropertyTreeView.prototype = {
|
||||
|
@ -44,10 +44,24 @@ PropertyTreeView.prototype = {
|
|||
_treeBox: null,
|
||||
|
||||
/**
|
||||
* Stores cached information about local objects being inspected.
|
||||
* Track known object actor IDs. We clean these when the panel is
|
||||
* destroyed/cleaned up.
|
||||
*
|
||||
* @private
|
||||
* @type array
|
||||
*/
|
||||
_objectCache: null,
|
||||
_objectActors: null,
|
||||
|
||||
/**
|
||||
* Map fake object actors to their IDs. This is used when we inspect local
|
||||
* objects.
|
||||
* @private
|
||||
* @type Object
|
||||
*/
|
||||
_localObjectActors: null,
|
||||
|
||||
_releaseObject: null,
|
||||
_objectPropertiesProvider: null,
|
||||
|
||||
/**
|
||||
* Use this setter to update the content of the tree.
|
||||
|
@ -58,54 +72,47 @@ PropertyTreeView.prototype = {
|
|||
* - object:
|
||||
* This is the raw object you want to display. You can only provide
|
||||
* this object if you want the property panel to work in sync mode.
|
||||
* - remoteObject:
|
||||
* - objectProperties:
|
||||
* An array that holds information on the remote object being
|
||||
* inspected. Each element in this array describes each property in the
|
||||
* remote object. See WebConsoleUtils.namesAndValuesOf() for details.
|
||||
* - rootCacheId:
|
||||
* The cache ID where the objects referenced in remoteObject are found.
|
||||
* - panelCacheId:
|
||||
* The cache ID where any object retrieved by this property panel
|
||||
* instance should be stored into.
|
||||
* - remoteObjectProvider:
|
||||
* remote object. See WebConsoleUtils.inspectObject() for details.
|
||||
* - objectPropertiesProvider:
|
||||
* A function that is invoked when a new object is needed. This is
|
||||
* called when the user tries to expand an inspectable property. The
|
||||
* callback must take four arguments:
|
||||
* - fromCacheId:
|
||||
* Tells from where to retrieve the object the user picked (from
|
||||
* which cache ID).
|
||||
* - objectId:
|
||||
* The object ID the user wants.
|
||||
* - panelCacheId:
|
||||
* Tells in which cache ID to store the objects referenced by
|
||||
* objectId so they can be retrieved later.
|
||||
* - actorID:
|
||||
* The object actor ID from which we request the properties.
|
||||
* - callback:
|
||||
* The callback function to be invoked when the remote object is
|
||||
* received. This function takes one argument: the raw message
|
||||
* received from the Web Console content script.
|
||||
* received. This function takes one argument: the array of
|
||||
* descriptors for each property in the object represented by the
|
||||
* actor.
|
||||
* - releaseObject:
|
||||
* Function to invoke when an object actor should be released. The
|
||||
* function must take one argument: the object actor ID.
|
||||
*/
|
||||
set data(aData) {
|
||||
let oldLen = this._rows.length;
|
||||
|
||||
this._cleanup();
|
||||
this.cleanup();
|
||||
|
||||
if (!aData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aData.remoteObject) {
|
||||
this._rootCacheId = aData.rootCacheId;
|
||||
this._panelCacheId = aData.panelCacheId;
|
||||
this._remoteObjectProvider = aData.remoteObjectProvider;
|
||||
this._rows = [].concat(aData.remoteObject);
|
||||
this._updateRemoteObject(this._rows, 0);
|
||||
if (aData.objectPropertiesProvider) {
|
||||
this._objectPropertiesProvider = aData.objectPropertiesProvider;
|
||||
this._releaseObject = aData.releaseObject;
|
||||
this._propertiesToRows(aData.objectProperties, 0);
|
||||
this._rows = aData.objectProperties;
|
||||
}
|
||||
else if (aData.object) {
|
||||
this._localObjectActors = Object.create(null);
|
||||
this._rows = this._inspectObject(aData.object);
|
||||
}
|
||||
else {
|
||||
throw new Error("First argument must have a .remoteObject or " +
|
||||
"an .object property!");
|
||||
throw new Error("First argument must have an objectActor or an " +
|
||||
"object property!");
|
||||
}
|
||||
|
||||
if (this._treeBox) {
|
||||
|
@ -128,13 +135,22 @@ PropertyTreeView.prototype = {
|
|||
* @param number aLevel
|
||||
* The level you want to give to each property in the remote object.
|
||||
*/
|
||||
_updateRemoteObject: function PTV__updateRemoteObject(aObject, aLevel)
|
||||
_propertiesToRows: function PTV__propertiesToRows(aObject, aLevel)
|
||||
{
|
||||
aObject.forEach(function(aElement) {
|
||||
aElement.level = aLevel;
|
||||
aElement.isOpened = false;
|
||||
aElement.children = null;
|
||||
});
|
||||
aObject.forEach(function(aItem) {
|
||||
aItem._level = aLevel;
|
||||
aItem._open = false;
|
||||
aItem._children = null;
|
||||
|
||||
if (this._releaseObject) {
|
||||
["value", "get", "set"].forEach(function(aProp) {
|
||||
let val = aItem[aProp];
|
||||
if (val && val.actor) {
|
||||
this._objectActors.push(val.actor);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -143,42 +159,53 @@ PropertyTreeView.prototype = {
|
|||
* @private
|
||||
* @param object aObject
|
||||
* The object you want to inspect.
|
||||
* @return array
|
||||
* The array of properties, each being described in a way that is
|
||||
* usable by the tree view.
|
||||
*/
|
||||
_inspectObject: function PTV__inspectObject(aObject)
|
||||
{
|
||||
this._objectCache = {};
|
||||
this._remoteObjectProvider = this._localObjectProvider.bind(this);
|
||||
let children = WebConsoleUtils.namesAndValuesOf(aObject, this._objectCache);
|
||||
this._updateRemoteObject(children, 0);
|
||||
this._objectPropertiesProvider = this._localPropertiesProvider.bind(this);
|
||||
let children =
|
||||
WebConsoleUtils.inspectObject(aObject, this._localObjectGrip.bind(this));
|
||||
this._propertiesToRows(children, 0);
|
||||
return children;
|
||||
},
|
||||
|
||||
/**
|
||||
* An object provider for when the user inspects local objects (not remote
|
||||
* Make a local fake object actor for the given object.
|
||||
*
|
||||
* @private
|
||||
* @param object aObject
|
||||
* The object to make an actor for.
|
||||
* @return object
|
||||
* The fake actor grip that represents the given object.
|
||||
*/
|
||||
_localObjectGrip: function PTV__localObjectGrip(aObject)
|
||||
{
|
||||
let grip = WebConsoleUtils.getObjectGrip(aObject);
|
||||
grip.actor = "obj" + gSequenceId();
|
||||
this._localObjectActors[grip.actor] = aObject;
|
||||
return grip;
|
||||
},
|
||||
|
||||
/**
|
||||
* A properties provider for when the user inspects local objects (not remote
|
||||
* ones).
|
||||
*
|
||||
* @private
|
||||
* @param string aFromCacheId
|
||||
* The cache ID from where to retrieve the desired object.
|
||||
* @param string aObjectId
|
||||
* The ID of the object you want.
|
||||
* @param string aDestCacheId
|
||||
* The ID of the cache where to store any objects referenced by the
|
||||
* desired object.
|
||||
* @param string aActor
|
||||
* The ID of the object actor you want.
|
||||
* @param function aCallback
|
||||
* The function you want to receive the object.
|
||||
* The function you want to receive the list of properties.
|
||||
*/
|
||||
_localObjectProvider:
|
||||
function PTV__localObjectProvider(aFromCacheId, aObjectId, aDestCacheId,
|
||||
aCallback)
|
||||
_localPropertiesProvider:
|
||||
function PTV__localPropertiesProvider(aActor, aCallback)
|
||||
{
|
||||
let object = WebConsoleUtils.namesAndValuesOf(this._objectCache[aObjectId],
|
||||
this._objectCache);
|
||||
aCallback({cacheId: aFromCacheId,
|
||||
objectId: aObjectId,
|
||||
object: object,
|
||||
childrenCacheId: aDestCacheId || aFromCacheId,
|
||||
});
|
||||
let object = this._localObjectActors[aActor];
|
||||
let properties =
|
||||
WebConsoleUtils.inspectObject(object, this._localObjectGrip.bind(this));
|
||||
aCallback(properties);
|
||||
},
|
||||
|
||||
/** nsITreeView interface implementation **/
|
||||
|
@ -187,18 +214,20 @@ PropertyTreeView.prototype = {
|
|||
|
||||
get rowCount() { return this._rows.length; },
|
||||
setTree: function(treeBox) { this._treeBox = treeBox; },
|
||||
getCellText: function(idx, column) {
|
||||
getCellText: function PTV_getCellText(idx, column)
|
||||
{
|
||||
let row = this._rows[idx];
|
||||
return row.name + ": " + row.value;
|
||||
return row.name + ": " + WebConsoleUtils.getPropertyPanelValue(row);
|
||||
},
|
||||
getLevel: function(idx) {
|
||||
return this._rows[idx].level;
|
||||
return this._rows[idx]._level;
|
||||
},
|
||||
isContainer: function(idx) {
|
||||
return !!this._rows[idx].inspectable;
|
||||
return typeof this._rows[idx].value == "object" && this._rows[idx].value &&
|
||||
this._rows[idx].value.inspectable;
|
||||
},
|
||||
isContainerOpen: function(idx) {
|
||||
return this._rows[idx].isOpened;
|
||||
return this._rows[idx]._open;
|
||||
},
|
||||
isContainerEmpty: function(idx) { return false; },
|
||||
isSeparator: function(idx) { return false; },
|
||||
|
@ -221,22 +250,22 @@ PropertyTreeView.prototype = {
|
|||
|
||||
hasNextSibling: function(idx, after)
|
||||
{
|
||||
var thisLevel = this.getLevel(idx);
|
||||
return this._rows.slice(after + 1).some(function (r) r.level == thisLevel);
|
||||
let thisLevel = this.getLevel(idx);
|
||||
return this._rows.slice(after + 1).some(function (r) r._level == thisLevel);
|
||||
},
|
||||
|
||||
toggleOpenState: function(idx)
|
||||
{
|
||||
let item = this._rows[idx];
|
||||
if (!item.inspectable) {
|
||||
if (!this.isContainer(idx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.isOpened) {
|
||||
if (item._open) {
|
||||
this._treeBox.beginUpdateBatch();
|
||||
item.isOpened = false;
|
||||
item._open = false;
|
||||
|
||||
var thisLevel = item.level;
|
||||
var thisLevel = item._level;
|
||||
var t = idx + 1, deleteCount = 0;
|
||||
while (t < this._rows.length && this.getLevel(t++) > thisLevel) {
|
||||
deleteCount++;
|
||||
|
@ -251,31 +280,27 @@ PropertyTreeView.prototype = {
|
|||
}
|
||||
else {
|
||||
let levelUpdate = true;
|
||||
let callback = function _onRemoteResponse(aResponse) {
|
||||
let callback = function _onRemoteResponse(aProperties) {
|
||||
this._treeBox.beginUpdateBatch();
|
||||
item.isOpened = true;
|
||||
|
||||
if (levelUpdate) {
|
||||
this._updateRemoteObject(aResponse.object, item.level + 1);
|
||||
item.children = aResponse.object;
|
||||
this._propertiesToRows(aProperties, item._level + 1);
|
||||
item._children = aProperties;
|
||||
}
|
||||
|
||||
this._rows.splice.apply(this._rows, [idx + 1, 0].concat(item.children));
|
||||
this._rows.splice.apply(this._rows, [idx + 1, 0].concat(item._children));
|
||||
|
||||
this._treeBox.rowCountChanged(idx + 1, item.children.length);
|
||||
this._treeBox.rowCountChanged(idx + 1, item._children.length);
|
||||
this._treeBox.invalidateRow(idx);
|
||||
this._treeBox.endUpdateBatch();
|
||||
item._open = true;
|
||||
}.bind(this);
|
||||
|
||||
if (!item.children) {
|
||||
let fromCacheId = item.level > 0 ? this._panelCacheId :
|
||||
this._rootCacheId;
|
||||
this._remoteObjectProvider(fromCacheId, item.objectId,
|
||||
this._panelCacheId, callback);
|
||||
if (!item._children) {
|
||||
this._objectPropertiesProvider(item.value.actor, callback);
|
||||
}
|
||||
else {
|
||||
levelUpdate = false;
|
||||
callback({object: item.children});
|
||||
callback(item._children);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -298,18 +323,23 @@ PropertyTreeView.prototype = {
|
|||
drop: function(index, orientation, dataTransfer) { },
|
||||
canDrop: function(index, orientation, dataTransfer) { return false; },
|
||||
|
||||
_cleanup: function PTV__cleanup()
|
||||
/**
|
||||
* Cleanup the property tree view.
|
||||
*/
|
||||
cleanup: function PTV_cleanup()
|
||||
{
|
||||
if (this._rows.length) {
|
||||
// Reset the existing _rows children to the initial state.
|
||||
this._updateRemoteObject(this._rows, 0);
|
||||
this._rows = [];
|
||||
if (this._releaseObject) {
|
||||
this._objectActors.forEach(this._releaseObject);
|
||||
delete this._objectPropertiesProvider;
|
||||
delete this._releaseObject;
|
||||
}
|
||||
if (this._localObjectActors) {
|
||||
delete this._localObjectActors;
|
||||
delete this._objectPropertiesProvider;
|
||||
}
|
||||
|
||||
delete this._objectCache;
|
||||
delete this._rootCacheId;
|
||||
delete this._panelCacheId;
|
||||
delete this._remoteObjectProvider;
|
||||
this._rows = [];
|
||||
this._objectActors = [];
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -459,3 +489,9 @@ PropertyPanel.prototype.destroy = function PP_destroy()
|
|||
this.tree = null;
|
||||
}
|
||||
|
||||
|
||||
function gSequenceId()
|
||||
{
|
||||
return gSequenceId.n++;
|
||||
}
|
||||
gSequenceId.n = 0;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,7 +8,7 @@ function test()
|
|||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTab("data:text/html,test for bug 676722 - inspectable objects for window.console");
|
||||
addTab("data:text/html;charset=utf8,test for bug 676722 - inspectable objects for window.console");
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
|
|
|
@ -21,7 +21,7 @@ function test() {
|
|||
browser.removeEventListener("DOMContentLoaded", testTimestamp, false);
|
||||
const TEST_TIMESTAMP = 12345678;
|
||||
let date = new Date(TEST_TIMESTAMP);
|
||||
let localizedString = WebConsoleUtils.l10n.timestampString(TEST_TIMESTAMP);
|
||||
let localizedString = WCU_l10n.timestampString(TEST_TIMESTAMP);
|
||||
isnot(localizedString.indexOf(date.getHours()), -1, "the localized " +
|
||||
"timestamp contains the hours");
|
||||
isnot(localizedString.indexOf(date.getMinutes()), -1, "the localized " +
|
||||
|
|
|
@ -81,7 +81,7 @@ function testContextMenuCopy() {
|
|||
}
|
||||
|
||||
function getExpectedClipboardText(aItem) {
|
||||
return "[" + WebConsoleUtils.l10n.timestampString(aItem.timestamp) + "] " +
|
||||
return "[" + WCU_l10n.timestampString(aItem.timestamp) + "] " +
|
||||
aItem.clipboardText;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,14 +37,14 @@ function test() {
|
|||
|
||||
let uri = Services.io.newFileURI(dir);
|
||||
|
||||
addTab(uri.spec);
|
||||
addTab("data:text/html;charset=utf8,<p>test file URI");
|
||||
browser.addEventListener("load", function tabLoad() {
|
||||
browser.removeEventListener("load", tabLoad, true);
|
||||
openConsole(null, function(aHud) {
|
||||
hud = aHud;
|
||||
hud.jsterm.clearOutput();
|
||||
browser.addEventListener("load", tabReload, true);
|
||||
content.location.reload();
|
||||
content.location = uri.spec;
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
|
|
@ -14,25 +14,35 @@ let tab1, tab2, win1, win2;
|
|||
let noErrors = true;
|
||||
|
||||
function tab1Loaded(aEvent) {
|
||||
browser.removeEventListener(aEvent.type, arguments.callee, true);
|
||||
browser.removeEventListener(aEvent.type, tab1Loaded, true);
|
||||
|
||||
win2 = OpenBrowserWindow();
|
||||
win2.addEventListener("load", win2Loaded, true);
|
||||
}
|
||||
|
||||
function win2Loaded(aEvent) {
|
||||
win2.removeEventListener(aEvent.type, arguments.callee, true);
|
||||
win2.removeEventListener(aEvent.type, win2Loaded, true);
|
||||
|
||||
tab2 = win2.gBrowser.addTab();
|
||||
tab2 = win2.gBrowser.addTab(TEST_URI);
|
||||
win2.gBrowser.selectedTab = tab2;
|
||||
tab2.linkedBrowser.addEventListener("load", tab2Loaded, true);
|
||||
tab2.linkedBrowser.contentWindow.location = TEST_URI;
|
||||
}
|
||||
|
||||
function tab2Loaded(aEvent) {
|
||||
tab2.linkedBrowser.removeEventListener(aEvent.type, arguments.callee, true);
|
||||
tab2.linkedBrowser.removeEventListener(aEvent.type, tab2Loaded, true);
|
||||
|
||||
waitForFocus(function() {
|
||||
let consolesOpened = 0;
|
||||
function onWebConsoleOpen() {
|
||||
consolesOpened++;
|
||||
if (consolesOpened == 2) {
|
||||
Services.obs.removeObserver(onWebConsoleOpen, "web-console-created");
|
||||
executeSoon(closeConsoles);
|
||||
}
|
||||
}
|
||||
|
||||
Services.obs.addObserver(onWebConsoleOpen, "web-console-created", false);
|
||||
|
||||
function openConsoles() {
|
||||
try {
|
||||
HUDService.activateHUDForContext(tab1);
|
||||
}
|
||||
|
@ -48,6 +58,20 @@ function tab2Loaded(aEvent) {
|
|||
ok(false, "HUDService.activateHUDForContext(tab2) exception: " + ex);
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
|
||||
let consolesClosed = 0;
|
||||
function onWebConsoleClose()
|
||||
{
|
||||
consolesClosed++;
|
||||
if (consolesClosed == 2) {
|
||||
Services.obs.removeObserver(onWebConsoleClose, "web-console-destroyed");
|
||||
executeSoon(testEnd);
|
||||
}
|
||||
}
|
||||
|
||||
function closeConsoles() {
|
||||
Services.obs.addObserver(onWebConsoleClose, "web-console-destroyed", false);
|
||||
|
||||
try {
|
||||
HUDService.deactivateHUDForContext(tab1);
|
||||
|
@ -64,20 +88,26 @@ function tab2Loaded(aEvent) {
|
|||
ok(false, "HUDService.deactivateHUDForContext(tab2) exception: " + ex);
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (noErrors) {
|
||||
ok(true, "there were no errors");
|
||||
}
|
||||
function testEnd() {
|
||||
ok(noErrors, "there were no errors");
|
||||
|
||||
win2.gBrowser.removeTab(tab2);
|
||||
Array.forEach(win1.gBrowser.tabs, function(aTab) {
|
||||
win1.gBrowser.removeTab(aTab);
|
||||
});
|
||||
Array.forEach(win2.gBrowser.tabs, function(aTab) {
|
||||
win2.gBrowser.removeTab(aTab);
|
||||
});
|
||||
|
||||
executeSoon(function() {
|
||||
win2.close();
|
||||
tab1 = tab2 = win1 = win2 = null;
|
||||
finishTest();
|
||||
});
|
||||
}
|
||||
|
||||
}, tab2.linkedBrowser.contentWindow);
|
||||
waitForFocus(openConsoles, tab2.linkedBrowser.contentWindow);
|
||||
}
|
||||
|
||||
function test() {
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-599725-response-headers.sjs";
|
||||
|
||||
function performTest(lastFinishedRequest)
|
||||
function performTest(lastFinishedRequest, aConsole)
|
||||
{
|
||||
ok(lastFinishedRequest, "page load was logged");
|
||||
|
||||
let headers = null;
|
||||
|
||||
function readHeader(aName)
|
||||
{
|
||||
for (let header of headers) {
|
||||
|
@ -24,13 +26,16 @@ function performTest(lastFinishedRequest)
|
|||
return null;
|
||||
}
|
||||
|
||||
let headers = lastFinishedRequest.log.entries[0].response.headers;
|
||||
ok(headers, "we have the response headers");
|
||||
ok(!readHeader("Content-Type"), "we do not have the Content-Type header");
|
||||
isnot(readHeader("Content-Length"), 60, "Content-Length != 60");
|
||||
aConsole.webConsoleClient.getResponseHeaders(lastFinishedRequest.actor,
|
||||
function (aResponse) {
|
||||
headers = aResponse.headers;
|
||||
ok(headers, "we have the response headers");
|
||||
ok(!readHeader("Content-Type"), "we do not have the Content-Type header");
|
||||
isnot(readHeader("Content-Length"), 60, "Content-Length != 60");
|
||||
executeSoon(finishTest);
|
||||
});
|
||||
|
||||
HUDService.lastFinishedRequestCallback = null;
|
||||
executeSoon(finishTest);
|
||||
}
|
||||
|
||||
function test()
|
||||
|
|
|
@ -10,39 +10,49 @@
|
|||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-600183-charset.html";
|
||||
|
||||
function performTest(lastFinishedRequest)
|
||||
function performTest(lastFinishedRequest, aConsole)
|
||||
{
|
||||
ok(lastFinishedRequest, "charset test page was loaded and logged");
|
||||
|
||||
let body = lastFinishedRequest.log.entries[0].response.content.text;
|
||||
ok(body, "we have the response body");
|
||||
aConsole.webConsoleClient.getResponseContent(lastFinishedRequest.actor,
|
||||
function (aResponse) {
|
||||
ok(!aResponse.contentDiscarded, "response body was not discarded");
|
||||
|
||||
let chars = "\u7684\u95ee\u5019!"; // 的问候!
|
||||
isnot(body.indexOf("<p>" + chars + "</p>"), -1,
|
||||
"found the chinese simplified string");
|
||||
let body = aResponse.content.text;
|
||||
ok(body, "we have the response body");
|
||||
|
||||
let chars = "\u7684\u95ee\u5019!"; // 的问候!
|
||||
isnot(body.indexOf("<p>" + chars + "</p>"), -1,
|
||||
"found the chinese simplified string");
|
||||
executeSoon(finishTest);
|
||||
});
|
||||
|
||||
HUDService.lastFinishedRequestCallback = null;
|
||||
executeSoon(finishTest);
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
addTab("data:text/html;charset=utf-8,Web Console - bug 600183 test");
|
||||
|
||||
let initialLoad = true;
|
||||
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
if (initialLoad) {
|
||||
openConsole(null, function(hud) {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
|
||||
hud.ui.saveRequestAndResponseBodies = true;
|
||||
HUDService.lastFinishedRequestCallback = performTest;
|
||||
openConsole(null, function(hud) {
|
||||
hud.ui.saveRequestAndResponseBodies = true;
|
||||
|
||||
content.location = TEST_URI;
|
||||
waitForSuccess({
|
||||
name: "saveRequestAndResponseBodies update",
|
||||
validatorFn: function()
|
||||
{
|
||||
return hud.ui.saveRequestAndResponseBodies;
|
||||
},
|
||||
successFn: function()
|
||||
{
|
||||
HUDService.lastFinishedRequestCallback = performTest;
|
||||
content.location = TEST_URI;
|
||||
},
|
||||
failureFn: finishTest,
|
||||
});
|
||||
initialLoad = false;
|
||||
} else {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
|
|
@ -16,14 +16,14 @@ const MINIMUM_CONSOLE_HEIGHT = 150;
|
|||
const MINIMUM_PAGE_HEIGHT = 50;
|
||||
const HEIGHT_PREF = "devtools.hud.height";
|
||||
|
||||
let hud, newHeight, height, innerHeight;
|
||||
let hud, newHeight, height, innerHeight, testDriver;
|
||||
|
||||
function performTests(aWebConsole)
|
||||
function testGen()
|
||||
{
|
||||
hud = aWebConsole.iframe;
|
||||
height = parseInt(hud.style.height);
|
||||
|
||||
toggleConsole();
|
||||
yield;
|
||||
|
||||
is(newHeight, height, "same height after reopening the console");
|
||||
is(Services.prefs.getIntPref(HEIGHT_PREF), HUDService.lastConsoleHeight,
|
||||
|
@ -31,6 +31,7 @@ function performTests(aWebConsole)
|
|||
|
||||
setHeight(Math.ceil(innerHeight * 0.5));
|
||||
toggleConsole();
|
||||
yield;
|
||||
|
||||
is(newHeight, height, "same height after reopening the console");
|
||||
is(Services.prefs.getIntPref(HEIGHT_PREF), HUDService.lastConsoleHeight,
|
||||
|
@ -38,6 +39,7 @@ function performTests(aWebConsole)
|
|||
|
||||
setHeight(MINIMUM_CONSOLE_HEIGHT - 1);
|
||||
toggleConsole();
|
||||
yield;
|
||||
|
||||
is(newHeight, MINIMUM_CONSOLE_HEIGHT, "minimum console height is respected");
|
||||
is(Services.prefs.getIntPref(HEIGHT_PREF), HUDService.lastConsoleHeight,
|
||||
|
@ -45,6 +47,7 @@ function performTests(aWebConsole)
|
|||
|
||||
setHeight(innerHeight - MINIMUM_PAGE_HEIGHT + 1);
|
||||
toggleConsole();
|
||||
yield;
|
||||
|
||||
is(newHeight, innerHeight - MINIMUM_PAGE_HEIGHT,
|
||||
"minimum page height is respected");
|
||||
|
@ -54,6 +57,7 @@ function performTests(aWebConsole)
|
|||
setHeight(Math.ceil(innerHeight * 0.6));
|
||||
Services.prefs.setIntPref(HEIGHT_PREF, -1);
|
||||
toggleConsole();
|
||||
yield;
|
||||
|
||||
is(newHeight, height, "same height after reopening the console");
|
||||
is(Services.prefs.getIntPref(HEIGHT_PREF), -1, "pref is not updated");
|
||||
|
@ -62,17 +66,23 @@ function performTests(aWebConsole)
|
|||
HUDService.lastConsoleHeight = 0;
|
||||
Services.prefs.setIntPref(HEIGHT_PREF, 0);
|
||||
|
||||
hud = testDriver = null;
|
||||
executeSoon(finishTest);
|
||||
|
||||
yield;
|
||||
}
|
||||
|
||||
function toggleConsole()
|
||||
{
|
||||
closeConsole();
|
||||
openConsole();
|
||||
closeConsole(null, function() {
|
||||
openConsole(null, function() {
|
||||
let hudId = HUDService.getHudIdByWindow(content);
|
||||
hud = HUDService.hudReferences[hudId].iframe;
|
||||
newHeight = parseInt(hud.style.height);
|
||||
|
||||
let hudId = HUDService.getHudIdByWindow(content);
|
||||
hud = HUDService.hudReferences[hudId].iframe;
|
||||
newHeight = parseInt(hud.style.height);
|
||||
testDriver.next();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setHeight(aHeight)
|
||||
|
@ -87,7 +97,11 @@ function test()
|
|||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
innerHeight = content.innerHeight;
|
||||
openConsole(null, performTests);
|
||||
openConsole(null, function(aHud) {
|
||||
hud = aHud.iframe;
|
||||
testDriver = testGen();
|
||||
testDriver.next();
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,8 +86,17 @@ function onpopupshown2(aEvent)
|
|||
});
|
||||
}, false);
|
||||
|
||||
executeSoon(function() {
|
||||
menupopups[1].hidePopup();
|
||||
waitForSuccess({
|
||||
name: "saveRequestAndResponseBodies update",
|
||||
validatorFn: function()
|
||||
{
|
||||
return huds[1].ui.saveRequestAndResponseBodies;
|
||||
},
|
||||
successFn: function()
|
||||
{
|
||||
menupopups[1].hidePopup();
|
||||
},
|
||||
failureFn: finishTest,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -147,8 +156,17 @@ function onpopupshown1(aEvent)
|
|||
}, tabs[runCount*2 + 1].linkedBrowser.contentWindow);
|
||||
}, false);
|
||||
|
||||
executeSoon(function() {
|
||||
menupopups[0].hidePopup();
|
||||
waitForSuccess({
|
||||
name: "saveRequestAndResponseBodies update",
|
||||
validatorFn: function()
|
||||
{
|
||||
return huds[0].ui.saveRequestAndResponseBodies;
|
||||
},
|
||||
successFn: function()
|
||||
{
|
||||
menupopups[0].hidePopup();
|
||||
},
|
||||
failureFn: finishTest,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,6 @@ function performTest(HUD) {
|
|||
}
|
||||
|
||||
function getExpectedClipboardText(aItem) {
|
||||
return "[" + WebConsoleUtils.l10n.timestampString(aItem.timestamp) + "] " +
|
||||
return "[" + WCU_l10n.timestampString(aItem.timestamp) + "] " +
|
||||
aItem.clipboardText;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ function getExpectedClipboardText(aItemCount) {
|
|||
for (let i = 0; i < aItemCount; i++) {
|
||||
let item = outputNode.getItemAtIndex(i);
|
||||
expectedClipboardText.push("[" +
|
||||
WebConsoleUtils.l10n.timestampString(item.timestamp) + "] " +
|
||||
WCU_l10n.timestampString(item.timestamp) + "] " +
|
||||
item.clipboardText);
|
||||
}
|
||||
return expectedClipboardText.join("\n");
|
||||
|
|
|
@ -10,20 +10,86 @@
|
|||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-630733-response-redirect-headers.sjs";
|
||||
|
||||
let lastFinishedRequests = {};
|
||||
let webConsoleClient;
|
||||
|
||||
function requestDoneCallback(aHttpRequest)
|
||||
function requestDoneCallback(aHttpRequest )
|
||||
{
|
||||
let status = aHttpRequest.log.entries[0].response.status;
|
||||
let status = aHttpRequest.response.status;
|
||||
lastFinishedRequests[status] = aHttpRequest;
|
||||
}
|
||||
|
||||
function performTest(aEvent)
|
||||
function consoleOpened(hud)
|
||||
{
|
||||
webConsoleClient = hud.ui.webConsoleClient;
|
||||
hud.ui.saveRequestAndResponseBodies = true;
|
||||
|
||||
waitForSuccess({
|
||||
name: "saveRequestAndResponseBodies update",
|
||||
validatorFn: function()
|
||||
{
|
||||
return hud.ui.saveRequestAndResponseBodies;
|
||||
},
|
||||
successFn: function()
|
||||
{
|
||||
HUDService.lastFinishedRequestCallback = requestDoneCallback;
|
||||
waitForSuccess(waitForResponses);
|
||||
content.location = TEST_URI;
|
||||
},
|
||||
failureFn: finishTest,
|
||||
});
|
||||
|
||||
let waitForResponses = {
|
||||
name: "301 and 404 responses",
|
||||
validatorFn: function()
|
||||
{
|
||||
return "301" in lastFinishedRequests &&
|
||||
"404" in lastFinishedRequests;
|
||||
},
|
||||
successFn: getHeaders,
|
||||
failureFn: finishTest,
|
||||
};
|
||||
}
|
||||
|
||||
function getHeaders()
|
||||
{
|
||||
HUDService.lastFinishedRequestCallback = null;
|
||||
|
||||
ok("301" in lastFinishedRequests, "request 1: 301 Moved Permanently");
|
||||
ok("404" in lastFinishedRequests, "request 2: 404 Not found");
|
||||
|
||||
webConsoleClient.getResponseHeaders(lastFinishedRequests["301"].actor,
|
||||
function (aResponse) {
|
||||
lastFinishedRequests["301"].response.headers = aResponse.headers;
|
||||
|
||||
webConsoleClient.getResponseHeaders(lastFinishedRequests["404"].actor,
|
||||
function (aResponse) {
|
||||
lastFinishedRequests["404"].response.headers = aResponse.headers;
|
||||
executeSoon(getContent);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getContent()
|
||||
{
|
||||
webConsoleClient.getResponseContent(lastFinishedRequests["301"].actor,
|
||||
function (aResponse) {
|
||||
lastFinishedRequests["301"].response.content = aResponse.content;
|
||||
lastFinishedRequests["301"].discardResponseBody = aResponse.contentDiscarded;
|
||||
|
||||
webConsoleClient.getResponseContent(lastFinishedRequests["404"].actor,
|
||||
function (aResponse) {
|
||||
lastFinishedRequests["404"].response.content = aResponse.content;
|
||||
lastFinishedRequests["404"].discardResponseBody =
|
||||
aResponse.contentDiscarded;
|
||||
|
||||
webConsoleClient = null;
|
||||
executeSoon(performTest);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function performTest()
|
||||
{
|
||||
function readHeader(aName)
|
||||
{
|
||||
for (let header of headers) {
|
||||
|
@ -34,7 +100,7 @@ function performTest(aEvent)
|
|||
return null;
|
||||
}
|
||||
|
||||
let headers = lastFinishedRequests["301"].log.entries[0].response.headers;
|
||||
let headers = lastFinishedRequests["301"].response.headers;
|
||||
is(readHeader("Content-Type"), "text/html",
|
||||
"we do have the Content-Type header");
|
||||
is(readHeader("Content-Length"), 71, "Content-Length is correct");
|
||||
|
@ -42,14 +108,17 @@ function performTest(aEvent)
|
|||
"Content-Length is correct");
|
||||
is(readHeader("x-foobar-bug630733"), "bazbaz",
|
||||
"X-Foobar-bug630733 is correct");
|
||||
let body = lastFinishedRequests["301"].log.entries[0].response.content;
|
||||
ok(!body.text, "body discarded for request 1");
|
||||
|
||||
headers = lastFinishedRequests["404"].log.entries[0].response.headers;
|
||||
let body = lastFinishedRequests["301"].response.content;
|
||||
ok(!body.text, "body discarded for request 1");
|
||||
ok(lastFinishedRequests["301"].discardResponseBody,
|
||||
"body discarded for request 1 (confirmed)");
|
||||
|
||||
headers = lastFinishedRequests["404"].response.headers;
|
||||
ok(!readHeader("Location"), "no Location header");
|
||||
ok(!readHeader("x-foobar-bug630733"), "no X-Foobar-bug630733 header");
|
||||
|
||||
body = lastFinishedRequests["404"].log.entries[0].response.content.text;
|
||||
body = lastFinishedRequests["404"].response.content.text;
|
||||
isnot(body.indexOf("404"), -1,
|
||||
"body is correct for request 2");
|
||||
|
||||
|
@ -61,19 +130,8 @@ function test()
|
|||
{
|
||||
addTab("data:text/html;charset=utf-8,<p>Web Console test for bug 630733");
|
||||
|
||||
browser.addEventListener("load", function onLoad1(aEvent) {
|
||||
browser.removeEventListener(aEvent.type, onLoad1, true);
|
||||
|
||||
openConsole(null, function(hud) {
|
||||
hud.ui.saveRequestAndResponseBodies = true;
|
||||
HUDService.lastFinishedRequestCallback = requestDoneCallback;
|
||||
|
||||
browser.addEventListener("load", function onLoad2(aEvent) {
|
||||
browser.removeEventListener(aEvent.type, onLoad2, true);
|
||||
executeSoon(performTest);
|
||||
}, true);
|
||||
|
||||
content.location = TEST_URI;
|
||||
});
|
||||
browser.addEventListener("load", function onLoad(aEvent) {
|
||||
browser.removeEventListener(aEvent.type, onLoad, true);
|
||||
openConsole(null, consoleOpened);
|
||||
}, true);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ function test() {
|
|||
|
||||
function consoleOpened(HUD) {
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/WebConsoleUtils.jsm", tmp);
|
||||
Cu.import("resource://gre/modules/devtools/WebConsoleUtils.jsm", tmp);
|
||||
let WCU = tmp.WebConsoleUtils;
|
||||
let JSPropertyProvider = tmp.JSPropertyProvider;
|
||||
tmp = null;
|
||||
|
@ -117,7 +117,7 @@ function testPropertyPanel(aPanel) {
|
|||
ok(find("iter1: Iterator", false),
|
||||
"iter1 is correctly displayed in the Property Panel");
|
||||
|
||||
ok(find("iter2: Iterator", false),
|
||||
ok(find("iter2: Object", false),
|
||||
"iter2 is correctly displayed in the Property Panel");
|
||||
|
||||
executeSoon(finishTest);
|
||||
|
|
|
@ -25,7 +25,7 @@ function test()
|
|||
hud = aHud;
|
||||
|
||||
HUDService.lastFinishedRequestCallback = function(aRequest) {
|
||||
lastRequest = aRequest.log.entries[0];
|
||||
lastRequest = aRequest;
|
||||
if (requestCallback) {
|
||||
requestCallback();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ function consoleOpened(aHud) {
|
|||
let completeNode = jsterm.completeNode;
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/WebConsoleUtils.jsm", tmp);
|
||||
Cu.import("resource://gre/modules/devtools/WebConsoleUtils.jsm", tmp);
|
||||
let WCU = tmp.WebConsoleUtils;
|
||||
tmp = null;
|
||||
|
||||
|
@ -38,7 +38,8 @@ function consoleOpened(aHud) {
|
|||
// __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__
|
||||
// constructor hasOwnProperty isPrototypeOf propertyIsEnumerable
|
||||
// toLocaleString toSource toString unwatch valueOf watch.
|
||||
let props = WCU.namesAndValuesOf(content.wrappedJSObject.document.body);
|
||||
let props = WCU.inspectObject(content.wrappedJSObject.document.body,
|
||||
function() { });
|
||||
is(popup.itemCount, 14 + props.length, "popup.itemCount is correct");
|
||||
|
||||
popup._panel.addEventListener("popuphidden", autocompletePopupHidden, false);
|
||||
|
|
|
@ -41,7 +41,7 @@ function testConsoleDir(outputNode) {
|
|||
if (text == "querySelectorAll: function querySelectorAll()") {
|
||||
foundQSA = true;
|
||||
}
|
||||
else if (text == "location: Object") {
|
||||
else if (text == "location: Location") {
|
||||
foundLocation = true;
|
||||
}
|
||||
else if (text == "write: function write()") {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
const TEST_HTTPS_URI = "https://example.com/browser/browser/devtools/webconsole/test/test-bug-737873-mixedcontent.html";
|
||||
|
||||
function test() {
|
||||
addTab("data:text/html,Web Console basic network logging test");
|
||||
addTab("data:text/html;charset=utf8,Web Console mixed content test");
|
||||
browser.addEventListener("load", onLoad, true);
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,9 @@ function testClickOpenNewTab(warningNode) {
|
|||
let oldOpenUILinkIn = window.openUILinkIn;
|
||||
|
||||
window.openUILinkIn = function(aLink) {
|
||||
if (aLink == "https://developer.mozilla.org/en/Security/MixedContent");
|
||||
linkOpened = true;
|
||||
if (aLink == "https://developer.mozilla.org/en/Security/MixedContent") {
|
||||
linkOpened = true;
|
||||
}
|
||||
}
|
||||
|
||||
EventUtils.synthesizeMouse(warningNode, 2, 2, {},
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// Tests that code completion works properly.
|
||||
|
||||
function test() {
|
||||
addTab("about:addons");
|
||||
addTab("about:credits");
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, testChrome);
|
||||
|
@ -15,7 +15,7 @@ function test() {
|
|||
|
||||
function testChrome(hud) {
|
||||
ok(hud, "we have a console");
|
||||
|
||||
|
||||
ok(hud.iframe, "we have the console iframe");
|
||||
|
||||
let jsterm = hud.jsterm;
|
||||
|
|
|
@ -52,7 +52,7 @@ function testClipboard() {
|
|||
for (let i = 0; i < outputNode.itemCount; i++) {
|
||||
let item = outputNode.getItemAtIndex(i);
|
||||
clipboardTexts.push("[" +
|
||||
WebConsoleUtils.l10n.timestampString(item.timestamp) +
|
||||
WCU_l10n.timestampString(item.timestamp) +
|
||||
"] " + item.clipboardText);
|
||||
}
|
||||
|
||||
|
|
|
@ -111,10 +111,10 @@ function testJSTerm(hud)
|
|||
|
||||
let foundTab = null;
|
||||
waitForSuccess({
|
||||
name: "help tab opened",
|
||||
name: "help tabs opened",
|
||||
validatorFn: function()
|
||||
{
|
||||
let newTabOpen = gBrowser.tabs.length == tabs + 1;
|
||||
let newTabOpen = gBrowser.tabs.length == tabs + 3;
|
||||
if (!newTabOpen) {
|
||||
return false;
|
||||
}
|
||||
|
@ -124,7 +124,9 @@ function testJSTerm(hud)
|
|||
},
|
||||
successFn: function()
|
||||
{
|
||||
gBrowser.removeTab(foundTab);
|
||||
gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
nextTest();
|
||||
},
|
||||
failureFn: nextTest,
|
||||
|
@ -176,7 +178,7 @@ function testJSTerm(hud)
|
|||
jsterm.clearOutput();
|
||||
jsterm.execute("pprint(print)");
|
||||
checkResult(function(nodes) {
|
||||
return nodes[0].textContent.indexOf("aJSTerm.") > -1;
|
||||
return nodes[0].textContent.indexOf("aOwner.helperResult") > -1;
|
||||
}, "pprint(function) shows source", 1);
|
||||
yield;
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ const TEST_DATA_JSON_CONTENT =
|
|||
|
||||
let lastRequest = null;
|
||||
let requestCallback = null;
|
||||
let lastActivity = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
|
@ -33,19 +32,34 @@ function test()
|
|||
openConsole(null, function(aHud) {
|
||||
hud = aHud;
|
||||
|
||||
HUDService.lastFinishedRequestCallback = function(aRequest) {
|
||||
lastRequest = aRequest.log.entries[0];
|
||||
lastActivity = aRequest;
|
||||
if (requestCallback) {
|
||||
requestCallback();
|
||||
}
|
||||
};
|
||||
HUDService.lastFinishedRequestCallback = requestCallbackWrapper;
|
||||
|
||||
executeSoon(testPageLoad);
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
function requestCallbackWrapper(aRequest)
|
||||
{
|
||||
lastRequest = aRequest;
|
||||
|
||||
hud.ui.webConsoleClient.getResponseContent(lastRequest.actor,
|
||||
function(aResponse) {
|
||||
lastRequest.response.content = aResponse.content;
|
||||
lastRequest.discardResponseBody = aResponse.contentDiscarded;
|
||||
|
||||
hud.ui.webConsoleClient.getRequestPostData(lastRequest.actor,
|
||||
function(aResponse) {
|
||||
lastRequest.request.postData = aResponse.postData;
|
||||
lastRequest.discardRequestBody = aResponse.postDataDiscarded;
|
||||
|
||||
if (requestCallback) {
|
||||
requestCallback();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testPageLoad()
|
||||
{
|
||||
requestCallback = function() {
|
||||
|
@ -55,8 +69,10 @@ function testPageLoad()
|
|||
is(lastRequest.request.url, TEST_NETWORK_REQUEST_URI,
|
||||
"Logged network entry is page load");
|
||||
is(lastRequest.request.method, "GET", "Method is correct");
|
||||
ok(!lastRequest.request.postData, "No request body was stored");
|
||||
ok(!lastRequest.request.postData.text, "No request body was stored");
|
||||
ok(lastRequest.discardRequestBody, "Request body was discarded");
|
||||
ok(!lastRequest.response.content.text, "No response body was stored");
|
||||
ok(lastRequest.discardResponseBody, "Response body was discarded");
|
||||
|
||||
lastRequest = null;
|
||||
requestCallback = null;
|
||||
|
@ -67,14 +83,29 @@ function testPageLoad()
|
|||
}
|
||||
|
||||
function testPageLoadBody()
|
||||
{
|
||||
// Turn on logging of request bodies and check again.
|
||||
hud.ui.saveRequestAndResponseBodies = true;
|
||||
|
||||
waitForSuccess({
|
||||
name: "saveRequestAndResponseBodies update",
|
||||
validatorFn: function()
|
||||
{
|
||||
return hud.ui.saveRequestAndResponseBodies;
|
||||
},
|
||||
successFn: testPageLoadBodyAfterSettingUpdate,
|
||||
failureFn: finishTest,
|
||||
});
|
||||
}
|
||||
|
||||
function testPageLoadBodyAfterSettingUpdate()
|
||||
{
|
||||
let loaded = false;
|
||||
let requestCallbackInvoked = false;
|
||||
|
||||
// Turn on logging of request bodies and check again.
|
||||
hud.ui.saveRequestAndResponseBodies = true;
|
||||
requestCallback = function() {
|
||||
ok(lastRequest, "Page load was logged again");
|
||||
ok(!lastRequest.discardResponseBody, "Response body was not discarded");
|
||||
is(lastRequest.response.content.text.indexOf("<!DOCTYPE HTML>"), 0,
|
||||
"Response body's beginning is okay");
|
||||
|
||||
|
@ -104,7 +135,8 @@ function testXhrGet()
|
|||
requestCallback = function() {
|
||||
ok(lastRequest, "testXhrGet() was logged");
|
||||
is(lastRequest.request.method, "GET", "Method is correct");
|
||||
ok(!lastRequest.request.postData, "No request body was sent");
|
||||
ok(!lastRequest.request.postData.text, "No request body was sent");
|
||||
ok(!lastRequest.discardRequestBody, "Request body was not discarded");
|
||||
is(lastRequest.response.content.text, TEST_DATA_JSON_CONTENT,
|
||||
"Response is correct");
|
||||
|
||||
|
@ -165,19 +197,18 @@ function testNetworkPanel()
|
|||
{
|
||||
// Open the NetworkPanel. The functionality of the NetworkPanel is tested
|
||||
// within separate test files.
|
||||
let networkPanel = hud.ui.openNetworkPanel(hud.ui.filterBox, lastActivity);
|
||||
is(networkPanel, hud.ui.filterBox._netPanel,
|
||||
"Network panel stored on anchor node");
|
||||
let networkPanel = hud.ui.openNetworkPanel(hud.ui.filterBox, lastRequest);
|
||||
|
||||
networkPanel.panel.addEventListener("load", function onLoad(aEvent) {
|
||||
networkPanel.panel.removeEventListener(aEvent.type, onLoad, true);
|
||||
networkPanel.panel.addEventListener("popupshown", function onPopupShown() {
|
||||
networkPanel.panel.removeEventListener("popupshown", onPopupShown, true);
|
||||
|
||||
is(hud.ui.filterBox._netPanel, networkPanel,
|
||||
"Network panel stored on anchor node");
|
||||
ok(true, "NetworkPanel was opened");
|
||||
|
||||
// All tests are done. Shutdown.
|
||||
networkPanel.panel.hidePopup();
|
||||
lastRequest = null;
|
||||
lastActivity = null;
|
||||
HUDService.lastFinishedRequestCallback = null;
|
||||
executeSoon(finishTest);
|
||||
}, true);
|
||||
|
|
|
@ -64,48 +64,37 @@ function testGen() {
|
|||
let hud = HUDService.getHudByWindow(content);
|
||||
let filterBox = hud.ui.filterBox;
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/WebConsoleUtils.jsm", tempScope);
|
||||
let l10n = tempScope.WebConsoleUtils.l10n;
|
||||
tempScope = null;
|
||||
|
||||
let httpActivity = {
|
||||
meta: {
|
||||
stages: [],
|
||||
discardRequestBody: true,
|
||||
discardResponseBody: true,
|
||||
updates: [],
|
||||
discardRequestBody: true,
|
||||
discardResponseBody: true,
|
||||
startedDateTime: (new Date()).toISOString(),
|
||||
request: {
|
||||
url: "http://www.testpage.com",
|
||||
method: "GET",
|
||||
cookies: [],
|
||||
headers: [
|
||||
{ name: "foo", value: "bar" },
|
||||
],
|
||||
},
|
||||
log: {
|
||||
entries: [{
|
||||
startedDateTime: (new Date()).toISOString(),
|
||||
request: {
|
||||
url: "http://www.testpage.com",
|
||||
method: "GET",
|
||||
cookies: [],
|
||||
headers: [
|
||||
{ name: "foo", value: "bar" },
|
||||
],
|
||||
},
|
||||
response: {
|
||||
headers: [],
|
||||
content: {},
|
||||
},
|
||||
timings: {},
|
||||
}],
|
||||
response: {
|
||||
headers: [],
|
||||
content: {},
|
||||
},
|
||||
timings: {},
|
||||
};
|
||||
|
||||
let entry = httpActivity.log.entries[0];
|
||||
|
||||
let networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
|
||||
is(filterBox._netPanel, networkPanel,
|
||||
"Network panel stored on the anchor object");
|
||||
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -128,8 +117,8 @@ function testGen() {
|
|||
|
||||
// Test request body.
|
||||
info("test 2: request body");
|
||||
httpActivity.meta.discardRequestBody = false;
|
||||
entry.request.postData = { text: "hello world" };
|
||||
httpActivity.discardRequestBody = false;
|
||||
httpActivity.request.postData = { text: "hello world" };
|
||||
networkPanel.update();
|
||||
|
||||
checkIsVisible(networkPanel, {
|
||||
|
@ -146,12 +135,12 @@ function testGen() {
|
|||
|
||||
// Test response header.
|
||||
info("test 3: response header");
|
||||
entry.timings.wait = 10;
|
||||
entry.response.httpVersion = "HTTP/3.14";
|
||||
entry.response.status = 999;
|
||||
entry.response.statusText = "earthquake win";
|
||||
entry.response.content.mimeType = "text/html";
|
||||
entry.response.headers.push(
|
||||
httpActivity.timings.wait = 10;
|
||||
httpActivity.response.httpVersion = "HTTP/3.14";
|
||||
httpActivity.response.status = 999;
|
||||
httpActivity.response.statusText = "earthquake win";
|
||||
httpActivity.response.content.mimeType = "text/html";
|
||||
httpActivity.response.headers.push(
|
||||
{ name: "Content-Type", value: "text/html" },
|
||||
{ name: "leaveHouses", value: "true" }
|
||||
);
|
||||
|
@ -175,8 +164,8 @@ function testGen() {
|
|||
|
||||
info("test 4");
|
||||
|
||||
httpActivity.meta.discardResponseBody = false;
|
||||
entry.timings.receive = 2;
|
||||
httpActivity.discardResponseBody = false;
|
||||
httpActivity.timings.receive = 2;
|
||||
networkPanel.update();
|
||||
|
||||
checkIsVisible(networkPanel, {
|
||||
|
@ -192,7 +181,7 @@ function testGen() {
|
|||
|
||||
info("test 5");
|
||||
|
||||
httpActivity.meta.stages.push("REQUEST_STOP", "TRANSACTION_CLOSE");
|
||||
httpActivity.updates.push("responseContent", "eventTimings");
|
||||
networkPanel.update();
|
||||
|
||||
checkNodeContent(networkPanel, "responseNoBodyInfo", "2ms");
|
||||
|
@ -210,20 +199,22 @@ function testGen() {
|
|||
|
||||
// Second run: Test for cookies and response body.
|
||||
info("test 6: cookies and response body");
|
||||
entry.request.cookies.push(
|
||||
httpActivity.request.cookies.push(
|
||||
{ name: "foo", value: "bar" },
|
||||
{ name: "hello", value: "world" }
|
||||
);
|
||||
entry.response.content.text = "get out here";
|
||||
httpActivity.response.content.text = "get out here";
|
||||
|
||||
networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
is(filterBox._netPanel, networkPanel,
|
||||
"Network panel stored on httpActivity object");
|
||||
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -247,15 +238,17 @@ function testGen() {
|
|||
|
||||
// Check image request.
|
||||
info("test 7: image request");
|
||||
entry.response.headers[1].value = "image/png";
|
||||
entry.response.content.mimeType = "image/png";
|
||||
entry.request.url = TEST_IMG;
|
||||
httpActivity.response.headers[1].value = "image/png";
|
||||
httpActivity.response.content.mimeType = "image/png";
|
||||
httpActivity.request.url = TEST_IMG;
|
||||
|
||||
networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -296,15 +289,17 @@ function testGen() {
|
|||
|
||||
// Check cached image request.
|
||||
info("test 8: cached image request");
|
||||
entry.response.httpVersion = "HTTP/1.1";
|
||||
entry.response.status = 304;
|
||||
entry.response.statusText = "Not Modified";
|
||||
httpActivity.response.httpVersion = "HTTP/1.1";
|
||||
httpActivity.response.status = 304;
|
||||
httpActivity.response.statusText = "Not Modified";
|
||||
|
||||
networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -326,17 +321,19 @@ function testGen() {
|
|||
|
||||
// Test sent form data.
|
||||
info("test 9: sent form data");
|
||||
entry.request.postData.text = [
|
||||
httpActivity.request.postData.text = [
|
||||
"Content-Type: application/x-www-form-urlencoded",
|
||||
"Content-Length: 59",
|
||||
"name=rob&age=20"
|
||||
].join("\n");
|
||||
|
||||
networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -357,13 +354,15 @@ function testGen() {
|
|||
|
||||
// Test no space after Content-Type:
|
||||
info("test 10: no space after Content-Type header in post data");
|
||||
entry.request.postData.text = "Content-Type:application/x-www-form-urlencoded\n";
|
||||
httpActivity.request.postData.text = "Content-Type:application/x-www-form-urlencoded\n";
|
||||
|
||||
networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -384,16 +383,18 @@ function testGen() {
|
|||
|
||||
info("test 11: cached data");
|
||||
|
||||
entry.request.url = TEST_ENCODING_ISO_8859_1;
|
||||
entry.response.headers[1].value = "application/json";
|
||||
entry.response.content.mimeType = "application/json";
|
||||
entry.response.content.text = "my cached data is here!";
|
||||
httpActivity.request.url = TEST_ENCODING_ISO_8859_1;
|
||||
httpActivity.response.headers[1].value = "application/json";
|
||||
httpActivity.response.content.mimeType = "application/json";
|
||||
httpActivity.response.content.text = "my cached data is here!";
|
||||
|
||||
networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -417,14 +418,16 @@ function testGen() {
|
|||
// Test a response with a content type that can't be displayed in the
|
||||
// NetworkPanel.
|
||||
info("test 12: unknown content type");
|
||||
entry.response.headers[1].value = "application/x-shockwave-flash";
|
||||
entry.response.content.mimeType = "application/x-shockwave-flash";
|
||||
httpActivity.response.headers[1].value = "application/x-shockwave-flash";
|
||||
httpActivity.response.content.mimeType = "application/x-shockwave-flash";
|
||||
|
||||
networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
|
||||
networkPanel.panel.addEventListener("load", function onLoad() {
|
||||
networkPanel.panel.removeEventListener("load", onLoad, true);
|
||||
testDriver.next();
|
||||
}, true);
|
||||
networkPanel._onUpdate = function() {
|
||||
networkPanel._onUpdate = null;
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
};
|
||||
|
||||
yield;
|
||||
|
||||
|
@ -442,7 +445,7 @@ function testGen() {
|
|||
});
|
||||
|
||||
let responseString =
|
||||
l10n.getFormatStr("NetworkPanel.responseBodyUnableToDisplay.content",
|
||||
WCU_l10n.getFormatStr("NetworkPanel.responseBodyUnableToDisplay.content",
|
||||
["application/x-shockwave-flash"]);
|
||||
checkNodeContent(networkPanel, "responseBodyUnknownTypeContent", responseString);
|
||||
networkPanel.panel.hidePopup();
|
||||
|
|
|
@ -17,7 +17,7 @@ function testPropertyProvider() {
|
|||
browser.removeEventListener("load", testPropertyProvider, true);
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/WebConsoleUtils.jsm", tmp);
|
||||
Cu.import("resource://gre/modules/devtools/WebConsoleUtils.jsm", tmp);
|
||||
let JSPropertyProvider = tmp.JSPropertyProvider;
|
||||
tmp = null;
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
let tempScope = {};
|
||||
Cu.import("resource:///modules/HUDService.jsm", tempScope);
|
||||
let HUDService = tempScope.HUDService;
|
||||
Cu.import("resource:///modules/WebConsoleUtils.jsm", tempScope);
|
||||
Cu.import("resource://gre/modules/devtools/WebConsoleUtils.jsm", tempScope);
|
||||
let WebConsoleUtils = tempScope.WebConsoleUtils;
|
||||
const WEBCONSOLE_STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
let WCU_l10n = new WebConsoleUtils.l10n(WEBCONSOLE_STRINGS_URI);
|
||||
|
||||
function log(aMsg)
|
||||
{
|
||||
|
@ -252,7 +254,7 @@ function tearDown()
|
|||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
tab = browser = hudId = hud = filterBox = outputNode = cs = null;
|
||||
WCU_l10n = tab = browser = hudId = hud = filterBox = outputNode = cs = null;
|
||||
}
|
||||
|
||||
registerCleanupFunction(tearDown);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
|
||||
<meta charset="utf8">
|
||||
<title>Mixed Content test - http on https</title>
|
||||
<script src="testscript.js"></script>
|
||||
<!--
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -266,6 +266,9 @@
|
|||
@BINPATH@/components/necko.xpt
|
||||
@BINPATH@/components/loginmgr.xpt
|
||||
@BINPATH@/components/parentalcontrols.xpt
|
||||
#ifdef MOZ_WEBRTC
|
||||
@BINPATH@/components/peerconnection.xpt
|
||||
#endif
|
||||
@BINPATH@/components/places.xpt
|
||||
@BINPATH@/components/plugin.xpt
|
||||
@BINPATH@/components/pref.xpt
|
||||
|
@ -490,6 +493,11 @@
|
|||
@BINPATH@/components/TCPSocketParentIntermediary.js
|
||||
@BINPATH@/components/TCPSocket.manifest
|
||||
|
||||
#ifdef MOZ_WEBRTC
|
||||
@BINPATH@/components/PeerConnection.js
|
||||
@BINPATH@/components/PeerConnection.manifest
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MARIONETTE
|
||||
@BINPATH@/chrome/marionette@JAREXT@
|
||||
@BINPATH@/chrome/marionette.manifest
|
||||
|
|
|
@ -48,8 +48,6 @@ addonsInstalled=#1 has been installed successfully.;#2 add-ons have been install
|
|||
addonsInstalledNeedsRestart=#1 will be installed after you restart #3.;#2 add-ons will be installed after you restart #3.
|
||||
addonInstallRestartButton=Restart Now
|
||||
addonInstallRestartButton.accesskey=R
|
||||
addonInstallManage=Open Add-ons Manager
|
||||
addonInstallManage.accesskey=O
|
||||
|
||||
# LOCALIZATION NOTE (addonError-1, addonError-2, addonError-3, addonError-4):
|
||||
# #1 is the add-on name, #2 is the host name, #3 is the application name
|
||||
|
@ -120,7 +118,7 @@ carbonFailurePluginsMessage.message=This page asks to use a plugin that can only
|
|||
carbonFailurePluginsMessage.restartButton.label=Restart in 32-bit mode
|
||||
carbonFailurePluginsMessage.restartButton.accesskey=R
|
||||
activatePluginsMessage.message=Would you like to activate the plugins on this page?
|
||||
activatePluginsMessage.label=Activate All Plugins
|
||||
activateAllPluginsMessage.label=Activate All Plugins
|
||||
activatePluginsMessage.accesskey=A
|
||||
activatePluginsMessage.always=Always activate plugins for this site
|
||||
activatePluginsMessage.always.accesskey=c
|
||||
|
|
|
@ -817,11 +817,10 @@ jsbIndentCharDesc=The chars used to indent each line
|
|||
# does.
|
||||
jsbIndentCharManual=The chars used to indent each line. The possible choices are space or tab.
|
||||
|
||||
# LOCALIZATION NOTE (jsbPreserveNewlinesDesc) A very short description of the
|
||||
# 'jsb <jsbPreserveNewlines>' parameter. This string is designed to be shown
|
||||
# in a menu alongside the command name, which is why it should be as short as
|
||||
# possible.
|
||||
jsbPreserveNewlinesDesc=Keep existing line breaks?
|
||||
# the 'jsb <doNotPreserveNewlines>' parameter. This string is designed to be
|
||||
# shown in a menu alongside the command name, which is why it should be as short
|
||||
# as possible.
|
||||
jsbDoNotPreserveNewlinesDesc=Do not preserve line breaks
|
||||
|
||||
# LOCALIZATION NOTE (jsbPreserveNewlinesManual) A fuller description of the
|
||||
# 'jsb <jsbPreserveNewlines>' parameter, displayed when the user asks for help
|
||||
|
@ -854,23 +853,18 @@ jsbJslintHappyManual=When set to true, jslint-stricter mode is enforced
|
|||
# 'jsb <braceStyle>' parameter. This string is designed to be shown
|
||||
# in a menu alongside the command name, which is why it should be as short as
|
||||
# possible.
|
||||
jsbBraceStyleDesc=Collapse, expand, end-expand, expand-strict
|
||||
jsbBraceStyleDesc=Select the coding style of braces
|
||||
|
||||
# LOCALIZATION NOTE (jsbBraceStyleManual) A fuller description of the
|
||||
# 'jsb <braceStyle>' parameter, displayed when the user asks for help
|
||||
# on what it does.
|
||||
jsbBraceStyleManual=The coding style of braces. Either collapse, expand, end-expand or expand-strict
|
||||
jsbBraceStyleManual=<p class="nowrap">The coding style of braces. Select from one of the following:</p><ul><li>collapse<br/><pre>if (x == 1) {\n ...\n} else {\n ...\n}</pre></li><li>expand<br/><pre>if (x == 1)\n{\n ...\n}\nelse\n{\n ...\n}</pre></li><li>end-expand<br/><pre>if (x == 1) {\n ...\n}\nelse {\n ...\n}</pre></li><li>expand-strict<br/><pre>if (x == 1)\n{\n return // This option can break scripts\n {\n a: 1\n };\n} else {\n ...\n}</pre></li></ul>
|
||||
|
||||
# LOCALIZATION NOTE (jsbSpaceBeforeConditionalDesc) A very short description of
|
||||
# the 'jsb <spaceBeforeConditional>' parameter. This string is designed to be
|
||||
# shown in a menu alongside the command name, which is why it should be as short
|
||||
# as possible.
|
||||
jsbSpaceBeforeConditionalDesc=Space before if statements?
|
||||
|
||||
# LOCALIZATION NOTE (jsbSpaceBeforeConditionalManual) A fuller description of
|
||||
# the 'jsb <spaceBeforeConditional>' parameter, displayed when the user asks for
|
||||
# help on what it does.
|
||||
jsbSpaceBeforeConditionalManual=Should a space be added before conditional statements?
|
||||
# LOCALIZATION NOTE (jsbNoSpaceBeforeConditionalDesc) A very short description
|
||||
# of the 'jsb <noSpaceBeforeConditional>' parameter. This string is designed to
|
||||
# be shown in a menu alongside the command name, which is why it should be as
|
||||
# short as possible.
|
||||
jsbNoSpaceBeforeConditionalDesc=No space before conditional statements
|
||||
|
||||
# LOCALIZATION NOTE (jsbUnescapeStringsDesc) A very short description of the
|
||||
# 'jsb <unescapeStrings>' parameter. This string is designed to be shown
|
||||
|
@ -887,6 +881,10 @@ jsbUnescapeStringsManual=Should printable characters in strings encoded in \\xNN
|
|||
# the jsb command.
|
||||
jsbInvalidURL=Please enter a valid URL
|
||||
|
||||
# LOCALIZATION NOTE (jsbOptionsDesc) The title of a set of options to
|
||||
# the 'jsb' command, displayed as a heading to the list of options.
|
||||
jsbOptionsDesc=Options
|
||||
|
||||
# LOCALIZATION NOTE (calllogDesc) A very short description of the
|
||||
# 'calllog' command. This string is designed to be shown in a menu
|
||||
# alongside the command name, which is why it should be as short as possible.
|
||||
|
|
|
@ -2444,6 +2444,12 @@ html|*#gcli-output-frame {
|
|||
0 1px 0 hsla(210,16%,76%,.15);
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
|
||||
background-color: hsl(210,30%,85%);
|
||||
color: hsl(210,11%,16%);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.gclitoolbar-complete-node {
|
||||
padding-left: 21px;
|
||||
background-color: transparent;
|
||||
|
@ -2835,17 +2841,11 @@ chatbox[minimized="true"] {
|
|||
height: 20px;
|
||||
}
|
||||
|
||||
panel[type="arrow"][popupid="click-to-play-plugins"] > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow[side="top"],
|
||||
panel[type="arrow"][popupid="click-to-play-plugins"] > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow[side="bottom"] {
|
||||
list-style-image: url("chrome://global/skin/icons/panelarrow-light-vertical.svg");
|
||||
}
|
||||
|
||||
.click-to-play-plugins-notification-content {
|
||||
margin: -10px;
|
||||
}
|
||||
|
||||
.click-to-play-plugins-notification-icon-box {
|
||||
background: hsla(0,0%,100%,.4);
|
||||
-moz-border-end: 1px solid hsla(0,0%,100%,.2);
|
||||
padding-top: 16px;
|
||||
-moz-padding-start: 16px;
|
||||
|
|
|
@ -63,10 +63,15 @@
|
|||
.gcli-row-out h4,
|
||||
.gcli-row-out h5,
|
||||
.gcli-row-out th,
|
||||
.gcli-row-out strong {
|
||||
.gcli-row-out strong,
|
||||
.gcli-row-out pre {
|
||||
color: hsl(210,30%,95%);
|
||||
}
|
||||
|
||||
.gcli-row-out pre {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.gcli-out-shortcut,
|
||||
.gcli-help-synopsis {
|
||||
padding: 0 3px;
|
||||
|
|
|
@ -1,402 +0,0 @@
|
|||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2007, Parakey Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use of this software in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Parakey Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior
|
||||
* written permission of Parakey Inc.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Creator:
|
||||
* Joe Hewitt
|
||||
* Contributors
|
||||
* John J. Barton (IBM Almaden)
|
||||
* Jan Odvarko (Mozilla Corp.)
|
||||
* Max Stepanov (Aptana Inc.)
|
||||
* Rob Campbell (Mozilla Corp.)
|
||||
* Hans Hillen (Paciello Group, Mozilla)
|
||||
* Curtis Bartley (Mozilla Corp.)
|
||||
* Mike Collins (IBM Almaden)
|
||||
* Kevin Decker
|
||||
* Mike Ratcliffe (Comartis AG)
|
||||
* Hernan Rodríguez Colmeiro
|
||||
* Austin Andrews
|
||||
* Christoph Dorn
|
||||
* Steven Roussey (AppCenter Inc, Network54)
|
||||
*/
|
||||
|
||||
html {
|
||||
background-color: -moz-dialog;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
font-family: Lucida Grande, sans-serif;
|
||||
font-size: 11px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 17px;
|
||||
border-bottom: 1px solid threedlightshadow;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0000ff;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
code {
|
||||
display: block;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* DOMPlate */
|
||||
|
||||
.objectLink-element,
|
||||
.objectLink-textNode,
|
||||
.objectLink-function,
|
||||
.objectBox-stackTrace,
|
||||
.objectLink-profile {
|
||||
font-family: Menlo, Andale Mono, monospace;
|
||||
}
|
||||
|
||||
.objectLink-textNode {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.objectLink-styleRule,
|
||||
.objectLink-element,
|
||||
.objectLink-textNode {
|
||||
color: #000088;
|
||||
}
|
||||
|
||||
.selectorTag,
|
||||
.selectorId,
|
||||
.selectorClass {
|
||||
font-family: Menlo, Andale Mono, monospace;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.selectorTag {
|
||||
color: #0000FF;
|
||||
}
|
||||
|
||||
.selectorId {
|
||||
color: DarkBlue;
|
||||
}
|
||||
|
||||
.selectorClass {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.selectorHidden > .selectorTag {
|
||||
color: #5F82D9;
|
||||
}
|
||||
|
||||
.selectorHidden > .selectorId {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.selectorHidden > .selectorClass {
|
||||
color: #D86060;
|
||||
}
|
||||
|
||||
.selectorValue {
|
||||
font-family: Menlo, Andale Mono, monospace;
|
||||
font-style: italic;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.panelNode-html {
|
||||
-moz-box-sizing: padding-box;
|
||||
padding: 4px 0 0 2px;
|
||||
}
|
||||
|
||||
.nodeBox {
|
||||
position: relative;
|
||||
font-family: Menlo, Andale Mono, monospace;
|
||||
padding-left: 13px;
|
||||
-moz-user-select: -moz-none;
|
||||
}
|
||||
|
||||
.nodeBox.search-selection {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
.twisty {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.nodeChildBox {
|
||||
margin-left: 12px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nodeLabel,
|
||||
.nodeCloseLabel {
|
||||
margin: -2px 2px 0 2px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 3px;
|
||||
padding: 0 2px;
|
||||
color: #000088;
|
||||
}
|
||||
|
||||
.nodeCloseLabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nodeTag {
|
||||
cursor: pointer;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.nodeValue {
|
||||
color: #FF0000;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.nodeText,
|
||||
.nodeComment {
|
||||
margin: 0 2px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.nodeText {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.docType {
|
||||
position: absolute;
|
||||
/* position DOCTYPE element above/outside the "nodeBox" that contains it */
|
||||
/* Note: to be fixed in Bug #688439 */
|
||||
top: -16px;
|
||||
font-family: Menlo, Andale Mono, monospace;
|
||||
padding-left: 8px;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.htmlNodeBox {
|
||||
/* make room for DOCTYPE element to be rendered above/outside "nodeBox" */
|
||||
/* Note: to be fixed in Bug #688439 */
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.nodeWhiteSpace {
|
||||
border: 1px solid LightGray;
|
||||
white-space: pre; /* otherwise the border will be collapsed around zero pixels */
|
||||
margin-left: 1px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.nodeWhiteSpace_Space {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.nodeTextEntity {
|
||||
border: 1px solid gray;
|
||||
white-space: pre; /* otherwise the border will be collapsed around zero pixels */
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.nodeComment {
|
||||
color: DarkGreen;
|
||||
}
|
||||
|
||||
.nodeBox.highlightOpen > .nodeLabel {
|
||||
background-color: #EEEEEE;
|
||||
}
|
||||
|
||||
.nodeBox.highlightOpen > .nodeCloseLabel,
|
||||
.nodeBox.highlightOpen > .nodeChildBox,
|
||||
.nodeBox.open > .nodeCloseLabel,
|
||||
.nodeBox.open > .nodeChildBox {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.selected > .nodeLabel {
|
||||
border-color: Highlight;
|
||||
background-color: Highlight;
|
||||
color: HighlightText !important;
|
||||
}
|
||||
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeTag,
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeAttr > .nodeValue,
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeText {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.nodeBox.highlighted > .nodeLabel {
|
||||
border-color: Highlight !important;
|
||||
background-color: cyan !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
.nodeBox.highlighted > .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.highlighted > .nodeLabel > .nodeLabelBox > .nodeTag,
|
||||
.nodeBox.highlighted > .nodeLabel > .nodeLabelBox > .nodeAttr > .nodeValue,
|
||||
.nodeBox.highlighted > .nodeLabel > .nodeLabelBox > .nodeText {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
.nodeBox.nodeHidden .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.nodeHidden .nodeCloseLabel,
|
||||
.nodeBox.nodeHidden .nodeLabel > .nodeLabelBox > .nodeText,
|
||||
.nodeBox.nodeHidden .nodeText {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.nodeBox.nodeHidden .nodeLabel > .nodeLabelBox > .nodeTag,
|
||||
.nodeBox.nodeHidden .nodeCloseLabel > .nodeCloseLabelBox > .nodeTag {
|
||||
color: #5F82D9;
|
||||
}
|
||||
|
||||
.nodeBox.nodeHidden .nodeLabel > .nodeLabelBox > .nodeAttr > .nodeValue {
|
||||
color: #D86060;
|
||||
}
|
||||
|
||||
.nodeBox.nodeHidden.selected > .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.nodeHidden.selected > .nodeLabel > .nodeLabelBox > .nodeTag,
|
||||
.nodeBox.nodeHidden.selected > .nodeLabel > .nodeLabelBox > .nodeAttr > .nodeValue,
|
||||
.nodeBox.nodeHidden.selected > .nodeLabel > .nodeLabelBox > .nodeText {
|
||||
color: SkyBlue !important;
|
||||
}
|
||||
|
||||
.nodeBox.mutated > .nodeLabel,
|
||||
.nodeAttr.mutated,
|
||||
.nodeValue.mutated,
|
||||
.nodeText.mutated,
|
||||
.nodeBox.mutated > .nodeText {
|
||||
background-color: #EFFF79;
|
||||
color: #FF0000 !important;
|
||||
}
|
||||
|
||||
.nodeBox.selected.mutated > .nodeLabel,
|
||||
.nodeBox.selected.mutated > .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeAttr.mutated > .nodeValue,
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeAttr > .nodeValue.mutated,
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeText.mutated {
|
||||
background-color: #EFFF79;
|
||||
border-color: #EFFF79;
|
||||
color: #FF0000 !important;
|
||||
}
|
||||
|
||||
.logRow-dirxml {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.soloElement > .nodeBox {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.useA11y .nodeLabel.focused {
|
||||
outline: 2px solid #FF9933;
|
||||
-moz-outline-radius: 3px;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.useA11y .nodeLabelBox:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* from panel.css */
|
||||
|
||||
/* HTML panel */
|
||||
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.selected > .nodeLabel {
|
||||
border-color: #3875d7;
|
||||
background-color: #3875d7;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.nodeBox.highlighted > .nodeLabel {
|
||||
border-color: #3875d7 !important;
|
||||
}
|
||||
|
||||
/************************************************************************************************/
|
||||
/* Twisties */
|
||||
|
||||
.twisty
|
||||
{
|
||||
-moz-appearance: treetwisty;
|
||||
}
|
||||
|
||||
.nodeBox.highlightOpen > .nodeLabel > .twisty,
|
||||
.nodeBox.open > .nodeLabel > .twisty
|
||||
{
|
||||
-moz-appearance: treetwistyopen;
|
||||
}
|
||||
|
||||
/************************************************************************************************/
|
||||
/* HTML panel */
|
||||
|
||||
.nodeBox.selected > .nodeLabel > .nodeLabelBox,
|
||||
.nodeBox.selected > .nodeLabel {
|
||||
border-color: #3875d7;
|
||||
background-color: #3875d7;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.nodeBox.highlighted > .nodeLabel {
|
||||
border-color: #3875d7 !important;
|
||||
}
|
||||
|
||||
.editingAttributeValue {
|
||||
background-color: #492;
|
||||
}
|
||||
|
||||
#attribute-editor {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
z-index: 5000;
|
||||
background-color: #fff;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
#attribute-editor.editing {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#attribute-editor-input {
|
||||
border: none;
|
||||
padding: 2px 5px;
|
||||
font-family: Menlo, Andale Mono, monospace;
|
||||
font-size: 11px;
|
||||
}
|
|
@ -110,7 +110,6 @@ browser.jar:
|
|||
skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)
|
||||
skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png)
|
||||
skin/classic/browser/devtools/commandline.css (devtools/commandline.css)
|
||||
skin/classic/browser/devtools/htmlpanel.css (devtools/htmlpanel.css)
|
||||
skin/classic/browser/devtools/markup-view.css (devtools/markup-view.css)
|
||||
skin/classic/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
|
|
|
@ -3866,6 +3866,12 @@ html|*#gcli-output-frame {
|
|||
0 1px 0 hsla(210,16%,76%,.15);
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
|
||||
background-color: hsl(210,30%,85%);
|
||||
color: hsl(210,11%,16%);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.gclitoolbar-complete-node {
|
||||
padding-left: 21px;
|
||||
background-color: transparent;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче