merge m-c to fx-team

This commit is contained in:
Tim Taubert 2012-05-22 15:20:39 +02:00
commit b19af7fcad
21 changed files with 458 additions and 191 deletions

View File

@ -1026,7 +1026,7 @@ pref("devtools.layoutview.enabled", false);
pref("devtools.layoutview.open", false);
// Enable the Debugger
pref("devtools.debugger.enabled", false);
pref("devtools.debugger.enabled", true);
pref("devtools.debugger.remote-enabled", false);
pref("devtools.debugger.remote-host", "localhost");
pref("devtools.debugger.remote-port", 6000);

View File

@ -73,16 +73,16 @@ let DocumentUtils = {
// <select>s without the multiple attribute are hard to determine the
// default value, so assume we don't have the default.
hasDefaultValue = false;
value = node.selectedIndex;
value = { selectedIndex: node.selectedIndex, value: node.value };
} else {
// <select>s with the multiple attribute are easier to determine the
// default value since each <option> has a defaultSelected
let options = Array.map(node.options, function(aOpt, aIx) {
let oSelected = aOpt.selected;
hasDefaultValue = hasDefaultValue && (oSelected == aOpt.defaultSelected);
return oSelected ? aIx : -1;
return oSelected ? aOpt.value : -1;
});
value = options.filter(function(aIx) aIx >= 0);
value = options.filter(function(aIx) aIx !== -1);
}
// In order to reduce XPath generation (which is slow), we only save data
@ -177,23 +177,41 @@ let DocumentUtils = {
aNode.checked = aValue;
eventType = "change";
} else if (typeof aValue == "number") {
// handle select backwards compatibility, example { "#id" : index }
// We saved the value blindly since selects take more work to determine
// default values. So now we should check to avoid unnecessary events.
if (aNode.selectedIndex == aValue) {
return;
}
try {
if (aValue < aNode.options.length) {
aNode.selectedIndex = aValue;
eventType = "change";
} catch (ex) { /* throws for invalid indices */ }
}
} else if (aValue && aValue.selectedIndex >= 0 && aValue.value) {
// handle select new format
// Don't dispatch a change event for no change
if (aNode.options[aNode.selectedIndex].value == aValue.value) {
return;
}
// find first option with matching aValue if possible
for (let i = 0; i < aNode.options.length; i++) {
if (aNode.options[i].value == aValue.value) {
aNode.selectedIndex = i;
break;
}
}
eventType = "change";
} else if (aValue && aValue.fileList && aValue.type == "file" &&
aNode.type == "file") {
aNode.mozSetFileNameArray(aValue.fileList, aValue.fileList.length);
eventType = "input";
} else if (aValue && typeof aValue.indexOf == "function" && aNode.options) {
Array.forEach(aNode.options, function(opt, index) {
opt.selected = aValue.indexOf(index) > -1;
// don't worry about malformed options with same values
opt.selected = aValue.indexOf(opt.value) > -1;
// Only fire the event here if this wasn't selected by default
if (!opt.defaultSelected) {

View File

@ -121,6 +121,8 @@ _BROWSER_TEST_FILES = \
browser_644409-scratchpads.js \
browser_645428.js \
browser_659591.js \
browser_662743.js \
browser_662743_sample.html \
browser_662812.js \
browser_665702-state_session.js \
browser_682507.js \

View File

@ -0,0 +1,128 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// This tests that session restore component does restore the right <select> option.
// Session store should not rely only on previous user's selectedIndex, it should
// check its value as well.
function test() {
/** Tests selected options **/
waitForExplicitFinish();
let testTabCount = 0;
let formData = [
// default case
{ },
// old format
{ "#select_id" : 0 },
{ "#select_id" : 2 },
// invalid index
{ "#select_id" : 8 },
{ "/xhtml:html/xhtml:body/xhtml:select" : 5},
{ "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : 6},
// new format
// index doesn't match value (testing an option in between (two))
{ id:{ "select_id": {"selectedIndex":0,"value":"val2"} } },
// index doesn't match value (testing an invalid value)
{ id:{ "select_id": {"selectedIndex":4,"value":"val8"} } },
// index doesn't match value (testing an invalid index)
{ id:{ "select_id": {"selectedIndex":8,"value":"val5"} } },
// index and value match position zero
{ id:{ "select_id": {"selectedIndex":0,"value":"val0"} }, xpath: {} },
// index doesn't match value (testing the last option (seven))
{ id:{},"xpath":{ "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']": {"selectedIndex":1,"value":"val7"} } },
// index and value match the default option "selectedIndex":3,"value":"val3"
{ xpath: { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : {"selectedIndex":3,"value":"val3"} } },
// index matches default option however it doesn't match value
{ id:{ "select_id": {"selectedIndex":3,"value":"val4"} } },
// combinations
{ "#select_id" : 3, id:{ "select_id": {"selectedIndex":1,"value":"val1"} } },
{ "#select_id" : 5, xpath: { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : {"selectedIndex":4,"value":"val4"} } },
{ "/xhtml:html/xhtml:body/xhtml:select" : 5, id:{ "select_id": {"selectedIndex":1,"value":"val1"} }},
{ "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : 2, xpath: { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : {"selectedIndex":7,"value":"val7"} } }
];
let expectedValues = [
[ "val3"], // default value
[ "val0"],
[ "val2"],
[ "val3"], // default value (invalid index)
[ "val5"],
[ "val6"],
[ "val2"],
[ "val3"], // default value (invalid value)
[ "val5"], // value is still valid (even it has an invalid index)
[ "val0"],
[ "val7"],
[ "val3"],
[ "val4"],
[ "val1"],
[ "val4"],
[ "val1"],
[ "val7"]
];
let callback = function() {
testTabCount--;
if (testTabCount == 0) {
finish();
}
};
for (let i = 0; i < formData.length; i++) {
testTabCount++;
testTabRestoreData(formData[i], expectedValues[i], callback);
}
}
function testTabRestoreData(aFormData, aExpectedValues, aCallback) {
let testURL =
getRootDirectory(gTestPath) + "browser_662743_sample.html";
let tab = gBrowser.addTab(testURL);
let tabState = { entries: [{ url: testURL, formdata: aFormData}] };
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
ss.setTabState(tab, JSON.stringify(tabState));
tab.addEventListener("SSTabRestored", function(aEvent) {
tab.removeEventListener("SSTabRestored", arguments.callee);
let doc = tab.linkedBrowser.contentDocument;
let select = doc.getElementById("select_id");
let value = select.options[select.selectedIndex].value;
// test select options values
is(value, aExpectedValues[0],
"Select Option by selectedIndex &/or value has been restored correctly");
// clean up
gBrowser.removeTab(tab);
aCallback();
});
tab.addEventListener("TabClose", function(aEvent) {
tab.removeEventListener("TabClose", arguments.callee);
let restoredTabState = JSON.parse(ss.getTabState(tab));
let restoredFormData = restoredTabState.entries[0].formdata;
let selectIdFormData = restoredFormData.id.select_id;
let value = restoredFormData.id.select_id.value;
// test format
ok("id" in restoredFormData && "xpath" in restoredFormData,
"FormData format is valid");
// validate that there are no old keys
is(Object.keys(restoredFormData).length, 2,
"FormData key length is valid");
// test format
ok("selectedIndex" in selectIdFormData && "value" in selectIdFormData,
"select format is valid");
// validate that there are no old keys
is(Object.keys(selectIdFormData).length, 2,
"select_id length is valid");
// test set collection values
is(value, aExpectedValues[0],
"Collection has been saved correctly");
});
}, true);
}

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<title>Test 662743</title>
<!-- Select events -->
<h3>Select options</h3>
<select id="select_id" name="select_name">
<option value="val0">Zero</option>
<option value="val1">One</option>
<option value="val2">Two</option>
<option value="val3" selected="selected">Three</option>
<option value="val4">Four</option>
<option value="val5">Five</option>
<option value="val6">Six</option>
<option value="val7">Seven</option>
</select>

View File

@ -6201,7 +6201,7 @@ var eagerHelperSettingSpec = {
{ name: 'always', value: Eagerness.ALWAYS },
]
},
defaultValue: 1,
defaultValue: Eagerness.SOMETIMES,
description: l10n.lookup('eagerHelperDesc'),
ignoreTypeDifference: true
};
@ -6346,7 +6346,8 @@ FocusManager.prototype.removeMonitoredElement = function(element, where) {
FocusManager.prototype.updatePosition = function(dimensions) {
var ev = {
tooltipVisible: this.isTooltipVisible,
outputVisible: this.isOutputVisible
outputVisible: this.isOutputVisible,
dimensions: dimensions
};
this.onVisibilityChange(ev);
};

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
[
<!ENTITY % webConsoleDTD SYSTEM "chrome://browser/locale/devtools/webConsole.dtd">
%webConsoleDTD;
]
>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is GCLI.
-
- The Initial Developer of the Original Code is
- Mozilla Foundation.
- Portions created by the Initial Developer are Copyright (C) 2012
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Joe Walker <jwalker@mozilla.com>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<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://global/skin/global.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/>
</head>
<body class="gcli-body">
<div id="gcli-output-root"></div>
</body>
</html>

View File

@ -19,8 +19,8 @@
<link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/>
</head>
<body id="gclichrome-body">
<div>
</div>
<body class="gcli-body">
<div id="gcli-tooltip-root"></div>
<div id="gcli-tooltip-connector"></div>
</body>
</html>

View File

@ -533,32 +533,10 @@ StackFrames.prototype = {
paramVar.setGrip(paramVal);
this._addExpander(paramVar, paramVal);
}
// If we already found 'arguments', we are done here.
if ("arguments" in frame.environment.bindings.variables) {
// Signal that variables have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
return;
}
}
// Sometimes in call frames with arguments we don't get 'arguments' in the
// environment (bug 746601) and we have to construct it manually. Note, that
// in this case arguments.callee will be absent, even in the cases where it
// shouldn't be.
if (frame.arguments && frame.arguments.length > 0) {
// Add "arguments".
let argsVar = localScope.addVar("arguments");
argsVar.setGrip({
type: "object",
class: "Arguments"
});
this._addExpander(argsVar, frame.arguments);
// Signal that variables have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
}
// Signal that variables have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
},
/**
@ -566,10 +544,9 @@ StackFrames.prototype = {
* new properties.
*/
_addExpander: function SF__addExpander(aVar, aObject) {
// No need for expansion for null and undefined values, but we do need them
// for frame.arguments which is a regular array.
// No need for expansion for null and undefined values.
if (!aVar || !aObject || typeof aObject !== "object" ||
(aObject.type !== "object" && !Array.isArray(aObject))) {
aObject.type !== "object") {
return;
}
@ -588,23 +565,6 @@ StackFrames.prototype = {
return;
}
// For arrays we have to construct a grip-like object.
if (Array.isArray(aObject)) {
let properties = { length: { value: aObject.length } };
for (let i = 0, l = aObject.length; i < l; i++) {
properties[i] = { value: aObject[i] };
}
aVar.addProperties(properties);
// Expansion handlers must be set after the properties are added.
for (let i = 0, l = aObject.length; i < l; i++) {
this._addExpander(aVar[i], aObject[i]);
}
aVar.fetched = true;
return;
}
let objClient = this.activeThread.pauseGrip(aObject);
objClient.getPrototypeAndProperties(function SF_onProtoAndProps(aResponse) {
// Add __proto__.

View File

@ -76,18 +76,17 @@ function testFrameParameters()
is(localNodes[6].querySelector(".info").textContent, "undefined",
"Should have the right property value for 'fArg'.");
// FIXME bug TODO: reenable
//is(localNodes[7].querySelector(".info").textContent, "1",
// "Should have the right property value for 'a'.");
is(localNodes[7].querySelector(".info").textContent, "1",
"Should have the right property value for 'a'.");
//is(localNodes[8].querySelector(".info").textContent, "[object Object]",
// "Should have the right property value for 'b'.");
is(localNodes[8].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'b'.");
//is(localNodes[9].querySelector(".info").textContent, "[object Object]",
// "Should have the right property value for 'c'.");
is(localNodes[9].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'c'.");
//is(localNodes[10].querySelector(".info").textContent, "[object Arguments]",
// "Should have the right property value for 'arguments'.");
is(localNodes[10].querySelector(".info").textContent, "[object Arguments]",
"Should have the right property value for 'arguments'.");
resumeAndFinish();
}}, 0);

View File

@ -56,7 +56,7 @@ function testFrameParameters()
is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
"Should have the right property value for 'this'.");
// Expand the '__proto__', 'arguments' and 'a' tree nodes. This causes
// Expand the 'this', 'arguments' and 'c' tree nodes. This causes
// their properties to be retrieved and displayed.
localNodes[0].expand();
localNodes[9].expand();
@ -67,6 +67,7 @@ function testFrameParameters()
// content window timers are disabled while the debuggee is paused.
let count = 0;
let intervalID = window.setInterval(function(){
dump("count: "+count+" ");
if (++count > 50) {
ok(false, "Timed out while polling for the properties.");
resumeAndFinish();
@ -96,17 +97,17 @@ function testFrameParameters()
.textContent, 1,
"Should have the right value for 'c.a'.");
//is(localNodes[10].querySelector(".info").textContent,
// "[object Arguments]",
// "Should have the right property value for 'arguments'.");
is(localNodes[10].querySelector(".info").textContent,
"[object Arguments]",
"Should have the right property value for 'arguments'.");
//is(localNodes[10].querySelector(".property > .title > .key")
// .textContent, "length",
// "Should have the right property name for 'length'.");
is(localNodes[10].querySelectorAll(".property > .title > .key")[7]
.textContent, "length",
"Should have the right property name for 'length'.");
//is(localNodes[10].querySelector(".property > .title > .value")
// .textContent, 5,
// "Should have the right argument length.");
is(localNodes[10].querySelectorAll(".property > .title > .value")[7]
.textContent, 5,
"Should have the right argument length.");
resumeAndFinish();
}, 100);

View File

@ -23,4 +23,5 @@ browser.jar:
content/browser/debugger-controller.js (debugger/debugger-controller.js)
content/browser/debugger-view.js (debugger/debugger-view.js)
content/browser/devtools/gcli.css (commandline/gcli.css)
content/browser/devtools/gcliblank.xhtml (commandline/gcliblank.xhtml)
content/browser/devtools/gclioutput.xhtml (commandline/gclioutput.xhtml)
content/browser/devtools/gclitooltip.xhtml (commandline/gclitooltip.xhtml)

View File

@ -7,7 +7,6 @@
const EXPORTED_SYMBOLS = [ "DeveloperToolbar" ];
const NS_XHTML = "http://www.w3.org/1999/xhtml";
const URI_GCLIBLANK = "chrome://browser/content/devtools/gcliblank.xhtml";
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
@ -79,7 +78,6 @@ DeveloperToolbar.prototype.toggle = function DT_toggle()
this.hide();
} else {
this.show();
this._input.focus();
}
};
@ -154,9 +152,11 @@ DeveloperToolbar.prototype._onload = function DT_onload()
this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
this._chromeWindow.getBrowser().tabContainer.addEventListener("TabSelect", this, false);
this._chromeWindow.getBrowser().addEventListener("load", this, true);
this._chromeWindow.getBrowser().addEventListener("load", this, true);
this._chromeWindow.addEventListener("resize", this, false);
this._element.hidden = false;
this._input.focus();
this._notify(NOTIFICATIONS.SHOW);
if (this._pendingShowCallback) {
@ -263,65 +263,10 @@ DeveloperToolbar.prototype.handleEvent = function DT_handleEvent(aEvent)
});
}
}
};
/**
* Add class="gcli-panel-inner-arrowcontent" to a panel's
* |<xul:box class="panel-inner-arrowcontent">| so we can alter the styling
* without complex CSS expressions.
* @param aPanel The panel to affect
*/
function getContentBox(aPanel)
{
let container = aPanel.ownerDocument.getAnonymousElementByAttribute(
aPanel, "anonid", "container");
return container.querySelector(".panel-inner-arrowcontent");
}
/**
* Helper function to calculate the sum of the vertical padding and margins
* between a nested node |aNode| and an ancestor |aRoot|. Iff all of the
* children of aRoot are 'only-childs' until you get to aNode then to avoid
* scroll-bars, the 'correct' height of aRoot is verticalSpacing + aNode.height.
* @param aNode The child node whose height is known.
* @param aRoot The parent height whose height we can affect.
* @return The sum of the vertical padding/margins in between aNode and aRoot.
*/
function getVerticalSpacing(aNode, aRoot)
{
let win = aNode.ownerDocument.defaultView;
function pxToNum(styles, property) {
return parseInt(styles.getPropertyValue(property).replace(/px$/, ''), 10);
else if (aEvent.type == "resize") {
this.outputPanel._resize();
}
let vertSpacing = 0;
do {
let styles = win.getComputedStyle(aNode);
vertSpacing += pxToNum(styles, "padding-top");
vertSpacing += pxToNum(styles, "padding-bottom");
vertSpacing += pxToNum(styles, "margin-top");
vertSpacing += pxToNum(styles, "margin-bottom");
vertSpacing += pxToNum(styles, "border-top-width");
vertSpacing += pxToNum(styles, "border-bottom-width");
let prev = aNode.previousSibling;
while (prev != null) {
vertSpacing += prev.clientHeight;
prev = prev.previousSibling;
}
let next = aNode.nextSibling;
while (next != null) {
vertSpacing += next.clientHeight;
next = next.nextSibling;
}
aNode = aNode.parentNode;
} while (aNode !== aRoot);
return vertSpacing + 9;
}
};
/**
* Panel to handle command line output.
@ -332,32 +277,31 @@ function getVerticalSpacing(aNode, aRoot)
function OutputPanel(aChromeDoc, aInput, aLoadCallback)
{
this._input = aInput;
this._anchor = aChromeDoc.getElementById("developer-toolbar");
this._toolbar = aChromeDoc.getElementById("developer-toolbar");
this._loadCallback = aLoadCallback;
/*
<panel id="gcli-output"
type="arrow"
noautofocus="true"
noautohide="true"
class="gcli-panel">
<iframe id="gcli-output-frame"
src=URI_GCLIBLANK
flex="1"/>
<html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
id="gcli-output-frame"
src="chrome://browser/content/devtools/gclioutput.xhtml"
flex="1"/>
</panel>
*/
this._panel = aChromeDoc.createElement("panel");
this._panel.id = "gcli-output";
this._panel.classList.add("gcli-panel");
this._panel.setAttribute("type", "arrow");
this._panel.setAttribute("noautofocus", "true");
this._panel.setAttribute("noautohide", "true");
this._anchor.parentElement.insertBefore(this._panel, this._anchor);
this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
this._frame = aChromeDoc.createElement("iframe");
this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
this._frame.id = "gcli-output-frame";
this._frame.setAttribute("src", URI_GCLIBLANK);
this._frame.setAttribute("src", "chrome://browser/content/devtools/gclioutput.xhtml");
this._frame.setAttribute("flex", "1");
this._panel.appendChild(this._frame);
@ -377,13 +321,9 @@ OutputPanel.prototype._onload = function OP_onload()
this._frame.removeEventListener("load", this._onload, true);
delete this._onload;
this._content = getContentBox(this._panel);
this._content.classList.add("gcli-panel-inner-arrowcontent");
this.document = this._frame.contentDocument;
this.document.body.classList.add("gclichrome-output");
this._div = this.document.querySelector("div");
this._div = this.document.getElementById("gcli-output-root");
this._div.classList.add('gcli-row-out');
this._div.setAttribute('aria-live', 'assertive');
@ -399,12 +339,15 @@ OutputPanel.prototype._onload = function OP_onload()
*/
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);
this._panel.openPopup(this._input, "before_start", 0, 0, false, false, null);
this._resize();
this._panel.openPopup(this._anchor, "before_end", -300, 0, false, false, null);
this._input.focus();
};
@ -415,9 +358,12 @@ OutputPanel.prototype.show = function OP_show()
*/
OutputPanel.prototype._resize = function CLP_resize()
{
let vertSpacing = getVerticalSpacing(this._content, this._panel);
let idealHeight = this.document.body.scrollHeight + vertSpacing;
this._panel.sizeTo(400, Math.min(idealHeight, 500));
if (this._panel == null || this.document == null || !this._panel.state == "closed") {
return
}
this._frame.height = this.document.body.scrollHeight;
this._frame.width = this._input.clientWidth + 2;
};
/**
@ -476,10 +422,10 @@ OutputPanel.prototype.destroy = function OP_destroy()
this.remove();
this._panel.removeChild(this._frame);
this._anchor.parentElement.removeChild(this._panel);
this._toolbar.parentElement.removeChild(this._panel);
delete this._input;
delete this._anchor;
delete this._toolbar;
delete this._panel;
delete this._frame;
delete this._content;
@ -510,7 +456,8 @@ OutputPanel.prototype._visibilityChanged = function OP_visibilityChanged(aEvent)
function TooltipPanel(aChromeDoc, aInput, aLoadCallback)
{
this._input = aInput;
this._anchor = aChromeDoc.getElementById("developer-toolbar");
this._toolbar = aChromeDoc.getElementById("developer-toolbar");
this._dimensions = { start: 0, end: 0 };
this._onload = this._onload.bind(this);
this._loadCallback = aLoadCallback;
@ -520,22 +467,22 @@ function TooltipPanel(aChromeDoc, aInput, aLoadCallback)
noautofocus="true"
noautohide="true"
class="gcli-panel">
<iframe id="gcli-tooltip-frame"
src=URI_GCLIBLANK
flex="1"/>
<html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
id="gcli-tooltip-frame"
src="chrome://browser/content/devtools/gclitooltip.xhtml"
flex="1"/>
</panel>
*/
this._panel = aChromeDoc.createElement("panel");
this._panel.id = "gcli-tooltip";
this._panel.classList.add("gcli-panel");
this._panel.setAttribute("type", "arrow");
this._panel.setAttribute("noautofocus", "true");
this._panel.setAttribute("noautohide", "true");
this._anchor.parentElement.insertBefore(this._panel, this._anchor);
this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
this._frame = aChromeDoc.createElement("iframe");
this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
this._frame.id = "gcli-tooltip-frame";
this._frame.setAttribute("src", URI_GCLIBLANK);
this._frame.setAttribute("src", "chrome://browser/content/devtools/gclitooltip.xhtml");
this._frame.setAttribute("flex", "1");
this._panel.appendChild(this._frame);
@ -550,13 +497,9 @@ TooltipPanel.prototype._onload = function TP_onload()
{
this._frame.removeEventListener("load", this._onload, true);
this._content = getContentBox(this._panel);
this._content.classList.add("gcli-panel-inner-arrowcontent");
this.document = this._frame.contentDocument;
this.document.body.classList.add("gclichrome-tooltip");
this.hintElement = this.document.querySelector("div");
this.hintElement = this.document.getElementById("gcli-tooltip-root");
this._connector = this.document.getElementById("gcli-tooltip-connector");
this.loaded = true;
@ -569,16 +512,58 @@ TooltipPanel.prototype._onload = function TP_onload()
/**
* Display the TooltipPanel.
*/
TooltipPanel.prototype.show = function TP_show()
TooltipPanel.prototype.show = function TP_show(aDimensions)
{
let vertSpacing = getVerticalSpacing(this._content, this._panel);
let idealHeight = this.document.body.scrollHeight + vertSpacing;
this._panel.sizeTo(350, Math.min(idealHeight, 500));
this._panel.openPopup(this._anchor, "before_start", 0, 0, false, false, null);
if (!aDimensions) {
aDimensions = { start: 0, end: 0 };
}
this._dimensions = aDimensions;
// 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);
this._resize();
this._panel.openPopup(this._input, "before_start", aDimensions.start * 10, 0, false, false, null);
this._input.focus();
};
/**
* One option is to spend lots of time taking an average width of characters
* in the current font, dynamically, and weighting for the frequency of use of
* various characters, or even to render the given string off screen, and then
* measure the width.
* Or we could do this...
*/
const AVE_CHAR_WIDTH = 4.5;
/**
* Display the TooltipPanel.
*/
TooltipPanel.prototype._resize = function TP_resize()
{
if (this._panel == null || this.document == null || !this._panel.state == "closed") {
return
}
let offset = 10 + Math.floor(this._dimensions.start * AVE_CHAR_WIDTH);
this._frame.style.marginLeft = offset + "px";
/*
// Bug 744906: UX review - Not sure if we want this code to fatten connector
// with param width
let width = Math.floor(this._dimensions.end * AVE_CHAR_WIDTH);
width = Math.min(width, 100);
width = Math.max(width, 10);
this._connector.style.width = width + "px";
*/
this._frame.height = this.document.body.scrollHeight;
};
/**
* Hide the TooltipPanel.
*/
@ -595,13 +580,15 @@ TooltipPanel.prototype.destroy = function TP_destroy()
this.remove();
this._panel.removeChild(this._frame);
this._anchor.parentElement.removeChild(this._panel);
this._toolbar.parentElement.removeChild(this._panel);
delete this._connector;
delete this._dimensions;
delete this._input;
delete this._onload;
delete this._panel;
delete this._frame;
delete this._anchor;
delete this._toolbar;
delete this._content;
delete this.document;
delete this.hintElement;
@ -614,7 +601,7 @@ TooltipPanel.prototype.destroy = function TP_destroy()
TooltipPanel.prototype._visibilityChanged = function TP_visibilityChanged(aEvent)
{
if (aEvent.tooltipVisible === true) {
this.show();
this.show(aEvent.dimensions);
} else {
this._panel.hidePopup();
}

View File

@ -18,6 +18,7 @@ _BROWSER_TEST_FILES = \
browser_require_basic.js \
browser_templater_basic.js \
browser_toolbar_basic.js \
browser_toolbar_tooltip.js \
head.js \
$(NULL)

View File

@ -9,10 +9,10 @@ registerCleanupFunction(function() {
imported = {};
});
const URL = "http://example.com/browser/browser/devtools/shared/test/browser_toolbar_basic.html";
const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_toolbar_basic.html";
function test() {
addTab(URL, function(browser, tab) {
addTab(TEST_URI, function(browser, tab) {
info("Starting browser_toolbar_basic.js");
runTest();
});

View File

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the developer toolbar works properly
const TEST_URI = "data:text/html;charset=utf-8,<p>Tooltip Tests</p>";
function test() {
DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
runTest();
finish();
});
}
function runTest() {
let tooltipPanel = DeveloperToolbar.tooltipPanel;
DeveloperToolbar.display.focusManager.helpRequest();
DeveloperToolbar.display.inputter.setInput('help help');
DeveloperToolbar.display.inputter.setCursor({ start: 'help help'.length });
is(tooltipPanel._dimensions.start, 'help '.length,
'search param start, when cursor at end');
ok(getLeftMargin() > 30, 'tooltip offset, when cursor at end')
DeveloperToolbar.display.inputter.setCursor({ start: 'help'.length });
is(tooltipPanel._dimensions.start, 0,
'search param start, when cursor at end of command');
ok(getLeftMargin() > 9, 'tooltip offset, when cursor at end of command')
DeveloperToolbar.display.inputter.setCursor({ start: 'help help'.length - 1 });
is(tooltipPanel._dimensions.start, 'help '.length,
'search param start, when cursor at penultimate position');
ok(getLeftMargin() > 30, 'tooltip offset, when cursor at penultimate position')
DeveloperToolbar.display.inputter.setCursor({ start: 0 });
is(tooltipPanel._dimensions.start, 0,
'search param start, when cursor at start');
ok(getLeftMargin() > 9, 'tooltip offset, when cursor at start')
}
function getLeftMargin() {
let style = DeveloperToolbar.tooltipPanel._frame.style.marginLeft;
return parseInt(style.slice(0, -2), 10);
}

View File

@ -67,7 +67,55 @@ let DeveloperToolbarTest = {
DeveloperToolbar.display.inputter.setInput("");
DeveloperToolbar.hide();
}
}
},
/**
* Quick wrapper around the things you need to do to run DeveloperToolbar
* command tests:
* - Set the pref 'devtools.toolbar.enabled' to true
* - Add a tab pointing at |uri|
* - Open the DeveloperToolbar
* - Register a cleanup function to undo the above
* - Run the tests
*
* @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
* @param testFunc A function containing the tests to run. This should
* arrange for 'finish()' to be called on completion.
*/
test: function DTT_test(uri, testFunc) {
let menuItem = document.getElementById("menu_devToolbar");
let command = document.getElementById("Tools:DevToolbar");
let appMenuItem = document.getElementById("appmenu_devToolbar");
registerCleanupFunction(function() {
DeveloperToolbarTest.hide();
// a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
if (menuItem) menuItem.hidden = true;
if (command) command.setAttribute("disabled", "true");
if (appMenuItem) appMenuItem.hidden = true;
});
// a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
if (menuItem) menuItem.hidden = false;
if (command) command.removeAttribute("disabled");
if (appMenuItem) appMenuItem.hidden = false;
addTab(uri, function(browser, tab) {
DeveloperToolbarTest.show(function() {
try {
testFunc(browser, tab);
}
catch (ex) {
ok(false, "" + ex);
console.error(ex);
finish();
throw ex;
}
});
});
},
};
function catchFail(func) {

View File

@ -1758,6 +1758,7 @@ HUD_SERVICE.prototype =
// Remove the HUDBox and the consolePanel if the Web Console is inside a
// floating panel.
if (hud.consolePanel && hud.consolePanel.parentNode) {
hud.consolePanel.hidePopup();
hud.consolePanel.parentNode.removeChild(hud.consolePanel);
hud.consolePanel.removeAttribute("hudId");
hud.consolePanel = null;

View File

@ -1092,14 +1092,14 @@ Function .onInit
WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Left "0"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Right "-1"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Top "5"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Bottom "15"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Bottom "25"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Type "checkbox"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Text "$(MAINTENANCE_SERVICE_CHECKBOX_DESC)"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Left "0"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Right "-1"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Top "20"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Bottom "30"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Top "27"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Bottom "37"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" State "1"
WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Flags "GROUP"

View File

@ -229,9 +229,9 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
<!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
<!ENTITY devToolbarMenu.label "Developer Toolbar">
<!ENTITY devToolbarMenu.accesskey "v">
<!ENTITY devToolbar.commandkey "v">
<!ENTITY devToolbarMenu.label "Developer Toolbar">
<!ENTITY devToolbarMenu.accesskey "v">
<!ENTITY devToolbar.commandkey "v">
<!ENTITY webConsoleButton.label "Web Console">
<!ENTITY inspectorButton.label "Inspector">

View File

@ -173,6 +173,7 @@ TelemetryPing.prototype = {
_startupHistogramRegex: /SQLITE|HTTP|SPDY|CACHE|DNS/,
_slowSQLStartup: {},
_prevSession: null,
_hasWindowRestoredObserver : false,
// Bug 756152
_disablePersistentTelemetrySending: true,
@ -628,6 +629,7 @@ TelemetryPing.prototype = {
Services.obs.addObserver(this, "profile-before-change", false);
Services.obs.addObserver(this, "sessionstore-windows-restored", false);
Services.obs.addObserver(this, "quit-application-granted", false);
this._hasWindowRestoredObserver = true;
// Delay full telemetry initialization to give the browser time to
// run various late initializers. Otherwise our gathered memory
@ -661,10 +663,9 @@ TelemetryPing.prototype = {
*/
uninstall: function uninstall() {
this.detachObservers()
try {
if (this._hasWindowRestoredObserver) {
Services.obs.removeObserver(this, "sessionstore-windows-restored");
} catch (e) {
// Already observed this event.
this._hasWindowRestoredObserver = false;
}
Services.obs.removeObserver(this, "profile-before-change");
Services.obs.removeObserver(this, "private-browsing");
@ -706,6 +707,7 @@ TelemetryPing.prototype = {
break;
case "sessionstore-windows-restored":
Services.obs.removeObserver(this, "sessionstore-windows-restored");
this._hasWindowRestoredObserver = false;
// fall through
case "test-gather-startup":
this.gatherStartupInformation();