This commit is contained in:
Rob Campbell 2011-06-29 12:18:34 -03:00
Родитель 9042eed1bf a4d1607c4e
Коммит f40c4e13ed
5 изменённых файлов: 164 добавлений и 19 удалений

Просмотреть файл

@ -75,6 +75,7 @@ function AutocompletePopup(aDocument)
stringBundle.GetStringFromName("Autocomplete.label"));
this._panel.setAttribute("noautofocus", "true");
this._panel.setAttribute("ignorekeys", "true");
this._panel.setAttribute("level", "top");
let mainPopupSet = this._document.getElementById("mainPopupSet");
if (mainPopupSet) {

Просмотреть файл

@ -51,6 +51,7 @@ const CONSOLEAPI_CLASS_ID = "{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/NetworkHelper.jsm");
Cu.import("resource:///modules/PropertyPanel.jsm");
var EXPORTED_SYMBOLS = ["HUDService", "ConsoleUtils"];
@ -4174,6 +4175,8 @@ function findCompletionBeginning(aStr)
function JSPropertyProvider(aScope, aInputValue)
{
let obj = unwrap(aScope);
// Store the scope object, since obj will be modified later on.
let win = obj;
// Analyse the aInputValue and find the beginning of the last part that
// should be completed.
@ -4212,10 +4215,15 @@ function JSPropertyProvider(aScope, aInputValue)
// Check if prop is a getter function on obj. Functions can change other
// stuff so we can't execute them to get the next object. Stop here.
if (obj.__lookupGetter__(prop)) {
if (isNonNativeGetter(win, obj, prop)) {
return null;
}
try {
obj = obj[prop];
}
catch (ex) {
return null;
}
obj = obj[prop];
}
}
else {
@ -4258,10 +4266,16 @@ function isIteratorOrGenerator(aObject)
return true;
}
let str = aObject.toString();
if (typeof aObject.next == "function" &&
str.indexOf("[object Generator") == 0) {
return true;
try {
let str = aObject.toString();
if (typeof aObject.next == "function" &&
str.indexOf("[object Generator") == 0) {
return true;
}
}
catch (ex) {
// window.history.next throws in the typeof check above.
return false;
}
}
@ -4564,8 +4578,7 @@ JSTerm.prototype = {
},
/**
* Evaluates a string in the sandbox. The string is currently wrapped by a
* with(window) { aString } construct, see bug 574033.
* Evaluates a string in the sandbox.
*
* @param string aString
* String to evaluate in the sandbox.

Просмотреть файл

@ -1,4 +1,5 @@
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -44,7 +45,8 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView", "namesAndValuesOf"];
var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView",
"namesAndValuesOf", "isNonNativeGetter"];
///////////////////////////////////////////////////////////////////////////
//// Helper for PropertyTreeView
@ -112,11 +114,20 @@ function presentableValueFor(aObject)
presentable = aObject.toString();
let m = /^\[object (\S+)\]/.exec(presentable);
if (typeof aObject == "object" && typeof aObject.next == "function" &&
m && m[1] == "Generator") {
try {
if (typeof aObject == "object" && typeof aObject.next == "function" &&
m && m[1] == "Generator") {
return {
type: TYPE_OTHER,
display: m[1]
};
}
}
catch (ex) {
// window.history.next throws in the typeof check above.
return {
type: TYPE_OTHER,
display: m[1]
type: TYPE_OBJECT,
display: m ? m[1] : "Object"
};
}
@ -148,6 +159,49 @@ function isNativeFunction(aFunction)
return typeof aFunction == "function" && !("prototype" in aFunction);
}
/**
* Tells if the given property of the provided object is a non-native getter or
* not.
*
* @param object aScope
* Scope to use for the check.
*
* @param object aObject
* The object that contains the property.
*
* @param string aProp
* The property you want to check if it is a getter or not.
*
* @return boolean
* True if the given property is a getter, false otherwise.
*/
function isNonNativeGetter(aScope, aObject, aProp) {
if (typeof aObject != "object") {
return false;
}
let desc;
while (aObject) {
try {
if (desc = aScope.Object.getOwnPropertyDescriptor(aObject, aProp)) {
break;
}
}
catch (ex) {
// Native getters throw here. See bug 520882.
if (ex.name == "NS_ERROR_XPC_BAD_CONVERT_JS" ||
ex.name == "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO") {
return false;
}
throw ex;
}
aObject = Object.getPrototypeOf(aObject);
}
if (desc && desc.get && !isNativeFunction(desc.get)) {
return true;
}
return false;
}
/**
* Get an array of property name value pairs for the tree.
*
@ -159,7 +213,7 @@ function isNativeFunction(aFunction)
function namesAndValuesOf(aObject)
{
let pairs = [];
let value, presentable, getter;
let value, presentable;
let isDOMDocument = aObject instanceof Ci.nsIDOMDocument;
@ -169,11 +223,11 @@ function namesAndValuesOf(aObject)
continue;
}
// Also skip non-native getters.
// TODO: implement a safer way to skip non-native getters. See bug 647235.
getter = aObject.__lookupGetter__ ?
aObject.__lookupGetter__(propName) : null;
if (getter && !isNativeFunction(getter)) {
// Also skip non-native getters. Pass the content window so that
// getOwnPropertyDescriptor can work later on.
let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
let contentWindow = chromeWindow.gBrowser.selectedBrowser.contentWindow;
if (isNonNativeGetter(contentWindow.wrappedJSObject, aObject, propName)) {
value = ""; // Value is never displayed.
presentable = {type: TYPE_OTHER, display: "Getter"};
}

Просмотреть файл

@ -143,6 +143,7 @@ _BROWSER_TEST_FILES = \
browser_webconsole_bug_585991_autocomplete_keys.js \
browser_webconsole_bug_663443_panel_title.js \
browser_webconsole_bug_660806_history_nav.js \
browser_webconsole_bug_651501_document_body_autocomplete.js \
head.js \
$(NULL)

Просмотреть файл

@ -0,0 +1,76 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that document.body autocompletes in the web console.
Cu.import("resource://gre/modules/PropertyPanel.jsm");
function test() {
addTab("data:text/html,Web Console autocompletion bug in document.body");
browser.addEventListener("load", onLoad, true);
}
var gHUD;
function onLoad(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
openConsole();
let hudId = HUDService.getHudIdByWindow(content);
gHUD = HUDService.hudReferences[hudId];
let jsterm = gHUD.jsterm;
let popup = jsterm.autocompletePopup;
let completeNode = jsterm.completeNode;
ok(!popup.isOpen, "popup is not open");
popup._panel.addEventListener("popupshown", function() {
popup._panel.removeEventListener("popupshown", arguments.callee, false);
ok(popup.isOpen, "popup is open");
let props = namesAndValuesOf(content.wrappedJSObject.document.body).length;
is(popup.itemCount, props, "popup.itemCount is correct");
popup._panel.addEventListener("popuphidden", autocompletePopupHidden, false);
EventUtils.synthesizeKey("VK_ESCAPE", {});
}, false);
jsterm.setInputValue("document.body");
EventUtils.synthesizeKey(".", {});
}
function autocompletePopupHidden()
{
let jsterm = gHUD.jsterm;
let popup = jsterm.autocompletePopup;
let completeNode = jsterm.completeNode;
let inputNode = jsterm.inputNode;
popup._panel.removeEventListener("popuphidden", arguments.callee, false);
ok(!popup.isOpen, "popup is not open");
let inputStr = "document.b";
jsterm.setInputValue(inputStr);
EventUtils.synthesizeKey("o", {});
let testStr = inputStr.replace(/./g, " ") + " ";
is(completeNode.value, testStr + "dy", "completeNode is empty");
jsterm.setInputValue("");
// Check the property panel as well.
let propPanel = jsterm.openPropertyPanel("Test", content.document);
let docProps = 0;
for (let prop in content.document) {
docProps++;
}
is (propPanel.treeView.rowCount, docProps, "all document properties shown in propertyPanel");
let treeRows = propPanel.treeView._rows;
is (treeRows[30].display, "body: Object", "found document.body");
propPanel.destroy();
executeSoon(finishTest);
}