mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge from mozilla-inbound.
This commit is contained in:
commit
89bdac61f0
@ -409,7 +409,7 @@ nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible)
|
||||
uint32_t childCount = aAccessible->ChildCount();
|
||||
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
|
||||
Accessible* child = aAccessible->GetChildAt(childIdx);
|
||||
if (IsText(child)) {
|
||||
if (!IsEmbeddedObject(child)) {
|
||||
foundText = true;
|
||||
break;
|
||||
}
|
||||
@ -429,7 +429,7 @@ nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible)
|
||||
uint32_t
|
||||
nsAccUtils::TextLength(Accessible* aAccessible)
|
||||
{
|
||||
if (!IsText(aAccessible))
|
||||
if (IsEmbeddedObject(aAccessible))
|
||||
return 1;
|
||||
|
||||
TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
|
||||
|
@ -232,16 +232,6 @@ public:
|
||||
static bool IsTextInterfaceSupportCorrect(Accessible* aAccessible);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return true if the given accessible has text role.
|
||||
*/
|
||||
static bool IsText(nsIAccessible *aAcc)
|
||||
{
|
||||
uint32_t role = Role(aAcc);
|
||||
return role == nsIAccessibleRole::ROLE_TEXT_LEAF ||
|
||||
role == nsIAccessibleRole::ROLE_STATICTEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return text length of the given accessible, return 0 on failure.
|
||||
*/
|
||||
|
@ -68,10 +68,6 @@ public:
|
||||
{
|
||||
return GetNode() && GetNode()->IsNodeOfType(nsINode::eCONTENT);
|
||||
}
|
||||
bool IsDocumentNode() const
|
||||
{
|
||||
return GetNode() && GetNode()->IsNodeOfType(nsINode::eDOCUMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unique identifier of the accessible.
|
||||
|
@ -265,7 +265,7 @@ nsTextEquivUtils::AppendFromValue(Accessible* aAccessible,
|
||||
}
|
||||
|
||||
//XXX: is it necessary to care the accessible is not a document?
|
||||
if (aAccessible->IsDocumentNode())
|
||||
if (aAccessible->IsDoc())
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
nsIContent *content = aAccessible->GetContent();
|
||||
|
@ -2671,7 +2671,7 @@ Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
|
||||
mChildren[idx]->mIndexInParent = idx;
|
||||
}
|
||||
|
||||
if (nsAccUtils::IsText(aChild))
|
||||
if (!nsAccUtils::IsEmbeddedObject(aChild))
|
||||
SetChildrenFlag(eMixedChildren);
|
||||
|
||||
mEmbeddedObjCollector = nullptr;
|
||||
@ -3038,7 +3038,7 @@ Accessible::ContainerWidget() const
|
||||
}
|
||||
|
||||
// Don't cross DOM document boundaries.
|
||||
if (parent->IsDocumentNode())
|
||||
if (parent->IsDoc())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
|
||||
}
|
||||
nsIFrame *primaryFrame = frame;
|
||||
endFrame = frame;
|
||||
if (nsAccUtils::IsText(childAcc)) {
|
||||
if (!nsAccUtils::IsEmbeddedObject(childAcc)) {
|
||||
// We only need info up to rendered offset -- that is what we're
|
||||
// converting to content offset
|
||||
int32_t substringEndOffset = -1;
|
||||
@ -708,14 +708,12 @@ HyperTextAccessible::GetRelativeOffset(nsIPresShell* aPresShell,
|
||||
|
||||
nsresult rv;
|
||||
int32_t contentOffset = aFromOffset;
|
||||
if (nsAccUtils::IsText(aFromAccessible)) {
|
||||
nsIFrame *frame = aFromAccessible->GetFrame();
|
||||
NS_ENSURE_TRUE(frame, -1);
|
||||
nsIFrame *frame = aFromAccessible->GetFrame();
|
||||
NS_ENSURE_TRUE(frame, -1);
|
||||
|
||||
if (frame->GetType() == nsGkAtoms::textFrame) {
|
||||
rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset);
|
||||
NS_ENSURE_SUCCESS(rv, -1);
|
||||
}
|
||||
if (frame->GetType() == nsGkAtoms::textFrame) {
|
||||
rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset);
|
||||
NS_ENSURE_SUCCESS(rv, -1);
|
||||
}
|
||||
|
||||
nsPeekOffsetStruct pos(aAmount, aDirection, contentOffset,
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
function doTest()
|
||||
{
|
||||
SimpleTest.expectAssertions(60);
|
||||
SimpleTest.expectAssertions(52);
|
||||
|
||||
// __o__n__e__w__o__r__d__\n
|
||||
// 0 1 2 3 4 5 6 7
|
||||
|
@ -12,9 +12,9 @@
|
||||
src="../text.js"></script>
|
||||
<script type="application/javascript">
|
||||
if (navigator.platform.startsWith("Mac")) {
|
||||
SimpleTest.expectAssertions(0, 23);
|
||||
SimpleTest.expectAssertions(0, 19);
|
||||
} else {
|
||||
SimpleTest.expectAssertions(23);
|
||||
SimpleTest.expectAssertions(19);
|
||||
}
|
||||
|
||||
function doTest()
|
||||
|
@ -14,9 +14,9 @@
|
||||
src="../text.js"></script>
|
||||
<script type="application/javascript">
|
||||
if (navigator.platform.startsWith("Mac")) {
|
||||
SimpleTest.expectAssertions(0, 10);
|
||||
SimpleTest.expectAssertions(0, 8);
|
||||
} else {
|
||||
SimpleTest.expectAssertions(10);
|
||||
SimpleTest.expectAssertions(8);
|
||||
}
|
||||
|
||||
function doTest()
|
||||
|
@ -659,3 +659,6 @@ pref("dom.disable_input_file", true);
|
||||
pref("general.useragent.enable_overrides", true);
|
||||
|
||||
pref("b2g.version", @MOZ_B2G_VERSION@);
|
||||
|
||||
// Disable console buffering to save memory.
|
||||
pref("consoleservice.buffered", false);
|
||||
|
@ -89,6 +89,7 @@ function doInternalWatch() {
|
||||
},
|
||||
JSON.stringify(options),
|
||||
function(...things) {
|
||||
// internal watch log callback
|
||||
log("(watch) internal: ", things);
|
||||
}
|
||||
);
|
||||
@ -136,7 +137,7 @@ addEventListener("DOMContentLoaded", function(e) {
|
||||
|
||||
// listen for request
|
||||
addMessageListener(kIdentityDelegateRequest, function(aMessage) {
|
||||
log("injected identity.js received", kIdentityDelegateRequest, "\n\n\n");
|
||||
log("injected identity.js received", kIdentityDelegateRequest);
|
||||
options = aMessage.json;
|
||||
showUI = true;
|
||||
func = doInternalRequest;
|
||||
@ -145,7 +146,7 @@ addMessageListener(kIdentityDelegateRequest, function(aMessage) {
|
||||
|
||||
// listen for watch
|
||||
addMessageListener(kIdentityDelegateWatch, function(aMessage) {
|
||||
log("injected identity.js received", kIdentityDelegateWatch, "\n\n\n");
|
||||
log("injected identity.js received", kIdentityDelegateWatch);
|
||||
options = aMessage.json;
|
||||
showUI = false;
|
||||
func = doInternalWatch;
|
||||
@ -154,7 +155,7 @@ addMessageListener(kIdentityDelegateWatch, function(aMessage) {
|
||||
|
||||
// listen for logout
|
||||
addMessageListener(kIdentityDelegateLogout, function(aMessage) {
|
||||
log("injected identity.js received", kIdentityDelegateLogout, "\n\n\n");
|
||||
log("injected identity.js received", kIdentityDelegateLogout);
|
||||
options = aMessage.json;
|
||||
showUI = false;
|
||||
func = doInternalLogout;
|
||||
|
@ -92,7 +92,7 @@ addMessageListener("Payment:LoadShim", function receiveMessage(aMessage) {
|
||||
requestId = aMessage.json.requestId;
|
||||
});
|
||||
|
||||
addEventListener("DOMContentLoaded", function(e) {
|
||||
addEventListener("DOMWindowCreated", function(e) {
|
||||
content.wrappedJSObject.paymentSuccess = paymentSuccess;
|
||||
content.wrappedJSObject.paymentFailed = paymentFailed;
|
||||
});
|
||||
|
@ -25,7 +25,7 @@
|
||||
*
|
||||
* In order for navigator.id.request() to maintain state in a single
|
||||
* cookie jar, we cause all Persona interactions to take place in a
|
||||
* gaia context that is launched by the system application, with the
|
||||
* content context that is launched by the system application, with the
|
||||
* result that Persona has a single cookie jar that all Relying
|
||||
* Parties can use. Since of course those Relying Parties cannot
|
||||
* reach into the system cookie jar, the Controller in this module
|
||||
@ -43,10 +43,10 @@
|
||||
* requesting Persona functions (doWatch, doReady, doLogout).
|
||||
*
|
||||
* The Identity service sends these observer messages to the
|
||||
* Controller in this module, which in turn triggers gaia to open a
|
||||
* Controller in this module, which in turn triggers content to open a
|
||||
* window to host the Persona js. If user interaction is required,
|
||||
* gaia will open the trusty UI. If user interaction is not required,
|
||||
* and we only need to get to Persona functions, gaia will open a
|
||||
* content will open the trusty UI. If user interaction is not required,
|
||||
* and we only need to get to Persona functions, content will open a
|
||||
* hidden iframe. In either case, a window is opened into which the
|
||||
* controller causes the script identity.js to be injected. This
|
||||
* script provides the glue between the in-page javascript and the
|
||||
@ -89,11 +89,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "Logger",
|
||||
const kIdentityShimFile = "chrome://browser/content/identity.js";
|
||||
|
||||
// Type of MozChromeEvents to handle id dialogs.
|
||||
const kOpenIdentityDialog = "open-id-dialog";
|
||||
const kCloseIdentityDialog = "close-id-dialog";
|
||||
const kOpenIdentityDialog = "id-dialog-open";
|
||||
const kDoneIdentityDialog = "id-dialog-done";
|
||||
const kCloseIdentityDialog = "id-dialog-close-iframe";
|
||||
|
||||
// Observer messages to communicate to shim
|
||||
const kReceivedIdentityAssertion = "received-id-assertion";
|
||||
const kIdentityDelegateWatch = "identity-delegate-watch";
|
||||
const kIdentityDelegateRequest = "identity-delegate-request";
|
||||
const kIdentityDelegateLogout = "identity-delegate-logout";
|
||||
@ -107,12 +107,12 @@ function log(...aMessageArgs) {
|
||||
}
|
||||
|
||||
/*
|
||||
* GaiaInterface encapsulates the our gaia functions. There are only two:
|
||||
* ContentInterface encapsulates the our content functions. There are only two:
|
||||
*
|
||||
* getContent - return the current content window
|
||||
* sendChromeEvent - send a chromeEvent from the browser shell
|
||||
*/
|
||||
let GaiaInterface = {
|
||||
let ContentInterface = {
|
||||
_getBrowser: function SignInToWebsiteController__getBrowser() {
|
||||
return Services.wm.getMostRecentWindow("navigator:browser");
|
||||
},
|
||||
@ -126,40 +126,100 @@ let GaiaInterface = {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* The Pipe abstracts the communcation channel between the Controller
|
||||
* and the identity.js code running in the browser window.
|
||||
*/
|
||||
let Pipe = {
|
||||
function Pipe() {
|
||||
this._watchers = [];
|
||||
}
|
||||
|
||||
/*
|
||||
* communicate - launch a gaia window with certain options and
|
||||
* provide a callback for handling messages.
|
||||
*
|
||||
* @param aRpOptions options describing the Relying Party's
|
||||
* (dictionary) call, such as origin and loggedInUser.
|
||||
*
|
||||
* @param aGaiaOptions showUI: boolean
|
||||
* (dictionary) message: name of the message to emit
|
||||
* (request, logout, watch)
|
||||
*
|
||||
* @param aMessageCallback function to call on receipt of a
|
||||
* (function) do-method message. These messages name
|
||||
* a method ('login', 'logout', etc.) and
|
||||
* carry optional parameters. The Pipe does
|
||||
* not know what the messages mean; it is
|
||||
* up to the caller to interpret them and
|
||||
* act on them.
|
||||
*/
|
||||
communicate: function(aRpOptions, aGaiaOptions, aMessageCallback) {
|
||||
log("open gaia dialog with options:", aGaiaOptions);
|
||||
Pipe.prototype = {
|
||||
init: function pipe_init() {
|
||||
Services.obs.addObserver(this, "identity-child-process-shutdown", false);
|
||||
Services.obs.addObserver(this, "identity-controller-unwatch", false);
|
||||
},
|
||||
|
||||
uninit: function pipe_init() {
|
||||
Services.obs.removeObserver(this, "identity-child-process-shutdown");
|
||||
Services.obs.removeObserver(this, "identity-controller-unwatch");
|
||||
},
|
||||
|
||||
observe: function Pipe_observe(aSubject, aTopic, aData) {
|
||||
let options = {};
|
||||
if (aSubject) {
|
||||
options = aSubject.wrappedJSObject;
|
||||
}
|
||||
switch (aTopic) {
|
||||
case "identity-child-process-shutdown":
|
||||
log("pipe removing watchers by message manager");
|
||||
this._removeWatchers(null, options.messageManager);
|
||||
break;
|
||||
|
||||
case "identity-controller-unwatch":
|
||||
log("unwatching", options.id);
|
||||
this._removeWatchers(options.id, options.messageManager);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_addWatcher: function Pipe__addWatcher(aId, aMm) {
|
||||
log("Adding watcher with id", aId);
|
||||
for (let i = 0; i < this._watchers.length; ++i) {
|
||||
let watcher = this._watchers[i];
|
||||
if (this._watcher.id === aId) {
|
||||
watcher.count++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._watchers.push({id: aId, count: 1, mm: aMm});
|
||||
},
|
||||
|
||||
_removeWatchers: function Pipe__removeWatcher(aId, aMm) {
|
||||
let checkId = aId !== null;
|
||||
let index = -1;
|
||||
for (let i = 0; i < this._watchers.length; ++i) {
|
||||
let watcher = this._watchers[i];
|
||||
if (watcher.mm === aMm &&
|
||||
(!checkId || (checkId && watcher.id === aId))) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index !== -1) {
|
||||
if (checkId) {
|
||||
if (--(this._watchers[index].count) === 0) {
|
||||
this._watchers.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
this._watchers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._watchers.length === 0) {
|
||||
log("No more watchers; clean up persona host iframe");
|
||||
let detail = {
|
||||
type: kCloseIdentityDialog
|
||||
};
|
||||
log('telling content to close the dialog');
|
||||
// tell content to close the dialog
|
||||
ContentInterface.sendChromeEvent(detail);
|
||||
}
|
||||
},
|
||||
|
||||
communicate: function(aRpOptions, aContentOptions, aMessageCallback) {
|
||||
let rpID = aRpOptions.id;
|
||||
let rpMM = aRpOptions.mm;
|
||||
if (rpMM) {
|
||||
this._addWatcher(rpID, rpMM);
|
||||
}
|
||||
|
||||
log("RP options:", aRpOptions, "\n content options:", aContentOptions);
|
||||
|
||||
// This content variable is injected into the scope of
|
||||
// kIdentityShimFile, where it is used to access the BrowserID object
|
||||
// and its internal API.
|
||||
let content = GaiaInterface.getContent();
|
||||
let content = ContentInterface.getContent();
|
||||
let mm = null;
|
||||
let uuid = getRandomId();
|
||||
let self = this;
|
||||
|
||||
if (!content) {
|
||||
log("ERROR: what the what? no content window?");
|
||||
@ -178,14 +238,14 @@ let Pipe = {
|
||||
removeMessageListeners();
|
||||
|
||||
let detail = {
|
||||
type: kReceivedIdentityAssertion,
|
||||
showUI: aGaiaOptions.showUI || false,
|
||||
id: kReceivedIdentityAssertion + "-" + uuid,
|
||||
requestId: aRpOptions.id
|
||||
type: kDoneIdentityDialog,
|
||||
showUI: aContentOptions.showUI || false,
|
||||
id: kDoneIdentityDialog + "-" + uuid,
|
||||
requestId: aRpOptions.id
|
||||
};
|
||||
log('telling gaia to close the dialog');
|
||||
// tell gaia to close the dialog
|
||||
GaiaInterface.sendChromeEvent(detail);
|
||||
log('received delegate finished; telling content to close the dialog');
|
||||
ContentInterface.sendChromeEvent(detail);
|
||||
self._removeWatchers(rpID, rpMM);
|
||||
}
|
||||
|
||||
content.addEventListener("mozContentEvent", function getAssertion(evt) {
|
||||
@ -224,11 +284,11 @@ let Pipe = {
|
||||
mm.addMessageListener(kIdentityControllerDoMethod, aMessageCallback);
|
||||
mm.addMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
|
||||
|
||||
mm.sendAsyncMessage(aGaiaOptions.message, aRpOptions);
|
||||
mm.sendAsyncMessage(aContentOptions.message, aRpOptions);
|
||||
}
|
||||
break;
|
||||
|
||||
case kReceivedIdentityAssertion + '-' + uuid:
|
||||
case kDoneIdentityDialog + '-' + uuid:
|
||||
// Received our assertion. The message manager callbacks will handle
|
||||
// communicating back to the IDService. All we have to do is remove
|
||||
// this listener.
|
||||
@ -242,27 +302,27 @@ let Pipe = {
|
||||
|
||||
});
|
||||
|
||||
// Tell gaia to open the identity iframe or trusty popup. The parameter
|
||||
// showUI signals whether user interaction is needed. If it is, gaia will
|
||||
// Tell content to open the identity iframe or trusty popup. The parameter
|
||||
// showUI signals whether user interaction is needed. If it is, content will
|
||||
// open a dialog; if not, a hidden iframe. In each case, BrowserID is
|
||||
// available in the context.
|
||||
let detail = {
|
||||
type: kOpenIdentityDialog,
|
||||
showUI: aGaiaOptions.showUI || false,
|
||||
showUI: aContentOptions.showUI || false,
|
||||
id: kOpenIdentityDialog + "-" + uuid,
|
||||
requestId: aRpOptions.id
|
||||
};
|
||||
|
||||
GaiaInterface.sendChromeEvent(detail);
|
||||
ContentInterface.sendChromeEvent(detail);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* The controller sits between the IdentityService used by DOMIdentity
|
||||
* and a gaia process launches an (invisible) iframe or (visible)
|
||||
* and a content process launches an (invisible) iframe or (visible)
|
||||
* trusty UI. Using an injected js script (identity.js), the
|
||||
* controller enables the gaia window to access the persona identity
|
||||
* controller enables the content window to access the persona identity
|
||||
* storage in the system cookie jar and send events back via the
|
||||
* controller into IdentityService and DOM, and ultimately up to the
|
||||
* Relying Party, which is open in a different window context.
|
||||
@ -270,12 +330,12 @@ let Pipe = {
|
||||
this.SignInToWebsiteController = {
|
||||
|
||||
/*
|
||||
* Initialize the controller. To use a different gaia communication pipe,
|
||||
* Initialize the controller. To use a different content communication pipe,
|
||||
* such as when mocking it in tests, pass aOptions.pipe.
|
||||
*/
|
||||
init: function SignInToWebsiteController_init(aOptions) {
|
||||
aOptions = aOptions || {};
|
||||
this.pipe = aOptions.pipe || Pipe;
|
||||
this.pipe = aOptions.pipe || new Pipe();
|
||||
Services.obs.addObserver(this, "identity-controller-watch", false);
|
||||
Services.obs.addObserver(this, "identity-controller-request", false);
|
||||
Services.obs.addObserver(this, "identity-controller-logout", false);
|
||||
@ -293,7 +353,7 @@ this.SignInToWebsiteController = {
|
||||
if (aSubject) {
|
||||
options = aSubject.wrappedJSObject;
|
||||
}
|
||||
switch(aTopic) {
|
||||
switch (aTopic) {
|
||||
case "identity-controller-watch":
|
||||
this.doWatch(options);
|
||||
break;
|
||||
@ -320,7 +380,7 @@ this.SignInToWebsiteController = {
|
||||
message = JSON.parse(message);
|
||||
}
|
||||
|
||||
switch(message.method) {
|
||||
switch (message.method) {
|
||||
case "ready":
|
||||
IdentityService.doReady(aRpId);
|
||||
break;
|
||||
@ -350,11 +410,12 @@ this.SignInToWebsiteController = {
|
||||
|
||||
doWatch: function SignInToWebsiteController_doWatch(aRpOptions) {
|
||||
// dom prevents watch from being called twice
|
||||
var gaiaOptions = {
|
||||
let contentOptions = {
|
||||
message: kIdentityDelegateWatch,
|
||||
showUI: false
|
||||
};
|
||||
this.pipe.communicate(aRpOptions, gaiaOptions, this._makeDoMethodCallback(aRpOptions.id));
|
||||
this.pipe.communicate(aRpOptions, contentOptions,
|
||||
this._makeDoMethodCallback(aRpOptions.id));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -362,12 +423,12 @@ this.SignInToWebsiteController = {
|
||||
*/
|
||||
doRequest: function SignInToWebsiteController_doRequest(aRpOptions) {
|
||||
log("doRequest", aRpOptions);
|
||||
// tell gaia to open the identity popup
|
||||
var gaiaOptions = {
|
||||
let contentOptions = {
|
||||
message: kIdentityDelegateRequest,
|
||||
showUI: true
|
||||
};
|
||||
this.pipe.communicate(aRpOptions, gaiaOptions, this._makeDoMethodCallback(aRpOptions.id));
|
||||
this.pipe.communicate(aRpOptions, contentOptions,
|
||||
this._makeDoMethodCallback(aRpOptions.id));
|
||||
},
|
||||
|
||||
/*
|
||||
@ -375,11 +436,12 @@ this.SignInToWebsiteController = {
|
||||
*/
|
||||
doLogout: function SignInToWebsiteController_doLogout(aRpOptions) {
|
||||
log("doLogout", aRpOptions);
|
||||
var gaiaOptions = {
|
||||
let contentOptions = {
|
||||
message: kIdentityDelegateLogout,
|
||||
showUI: false
|
||||
};
|
||||
this.pipe.communicate(aRpOptions, gaiaOptions, this._makeDoMethodCallback(aRpOptions.id));
|
||||
this.pipe.communicate(aRpOptions, contentOptions,
|
||||
this._makeDoMethodCallback(aRpOptions.id));
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1043,7 +1043,7 @@ pref("devtools.toolbox.sidebar.width", 500);
|
||||
pref("devtools.toolbox.host", "bottom");
|
||||
pref("devtools.toolbox.selectedTool", "webconsole");
|
||||
pref("devtools.toolbox.toolbarSpec", '["paintflashing toggle","tilt toggle","scratchpad","resize toggle"]');
|
||||
pref("devtools.toolbox.sideEnabled", false);
|
||||
pref("devtools.toolbox.sideEnabled", true);
|
||||
|
||||
// Enable the Inspector
|
||||
pref("devtools.inspector.enabled", true);
|
||||
|
@ -7198,7 +7198,6 @@ let gPrivateBrowsingUI = {
|
||||
// temporary fix until bug 463607 is fixed
|
||||
document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
|
||||
|
||||
// Adjust the window's title
|
||||
if (window.location.href == getBrowserURL()) {
|
||||
#ifdef XP_MACOSX
|
||||
if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||
@ -7206,6 +7205,7 @@ let gPrivateBrowsingUI = {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Adjust the window's title
|
||||
let docElement = document.documentElement;
|
||||
docElement.setAttribute("title",
|
||||
docElement.getAttribute("title_privatebrowsing"));
|
||||
@ -7214,6 +7214,23 @@ let gPrivateBrowsingUI = {
|
||||
docElement.setAttribute("privatebrowsingmode",
|
||||
PrivateBrowsingUtils.permanentPrivateBrowsing ? "permanent" : "temporary");
|
||||
gBrowser.updateTitlebar();
|
||||
|
||||
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||
// Adjust the New Window menu entries
|
||||
[
|
||||
{ normal: "menu_newNavigator", private: "menu_newPrivateWindow" },
|
||||
{ normal: "appmenu_newNavigator", private: "appmenu_newPrivateWindow" },
|
||||
].forEach(function(menu) {
|
||||
let newWindow = document.getElementById(menu.normal);
|
||||
let newPrivateWindow = document.getElementById(menu.private);
|
||||
if (newWindow && newPrivateWindow) {
|
||||
newPrivateWindow.hidden = true;
|
||||
newWindow.label = newPrivateWindow.label;
|
||||
newWindow.accessKey = newPrivateWindow.accessKey;
|
||||
newWindow.command = newPrivateWindow.command;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (gURLBar) {
|
||||
|
@ -16,9 +16,50 @@ function test() {
|
||||
whenDelayedStartupFinished(privateWin, function() {
|
||||
nonPrivateWin = privateWin.OpenBrowserWindow({private: false});
|
||||
ok(!PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), "privateWin.OpenBrowserWindow({private: false}) should open a normal window");
|
||||
|
||||
nonPrivateWin.close();
|
||||
|
||||
[
|
||||
{ normal: "menu_newNavigator", private: "menu_newPrivateWindow", accesskey: true },
|
||||
{ normal: "appmenu_newNavigator", private: "appmenu_newPrivateWindow", accesskey: false },
|
||||
].forEach(function(menu) {
|
||||
let newWindow = privateWin.document.getElementById(menu.normal);
|
||||
let newPrivateWindow = privateWin.document.getElementById(menu.private);
|
||||
if (newWindow && newPrivateWindow) {
|
||||
ok(!newPrivateWindow.hidden, "New Private Window menu item should be hidden");
|
||||
isnot(newWindow.label, newPrivateWindow.label, "New Window's label shouldn't be overwritten");
|
||||
if (menu.accesskey) {
|
||||
isnot(newWindow.accessKey, newPrivateWindow.accessKey, "New Window's accessKey shouldn't be overwritten");
|
||||
}
|
||||
isnot(newWindow.command, newPrivateWindow.command, "New Window's command shouldn't be overwritten");
|
||||
}
|
||||
});
|
||||
|
||||
privateWin.close();
|
||||
finish();
|
||||
|
||||
Services.prefs.setBoolPref("browser.privatebrowsing.autostart", true);
|
||||
privateWin = OpenBrowserWindow({private: true});
|
||||
whenDelayedStartupFinished(privateWin, function() {
|
||||
[
|
||||
{ normal: "menu_newNavigator", private: "menu_newPrivateWindow", accessKey: true },
|
||||
{ normal: "appmenu_newNavigator", private: "appmenu_newPrivateWindow", accessKey: false },
|
||||
].forEach(function(menu) {
|
||||
let newWindow = privateWin.document.getElementById(menu.normal);
|
||||
let newPrivateWindow = privateWin.document.getElementById(menu.private);
|
||||
if (newWindow && newPrivateWindow) {
|
||||
ok(newPrivateWindow.hidden, "New Private Window menu item should be hidden");
|
||||
is(newWindow.label, newPrivateWindow.label, "New Window's label should be overwritten");
|
||||
if (menu.accesskey) {
|
||||
is(newWindow.accessKey, newPrivateWindow.accessKey, "New Window's accessKey should be overwritten");
|
||||
}
|
||||
is(newWindow.command, newPrivateWindow.command, "New Window's command should be overwritten");
|
||||
}
|
||||
});
|
||||
|
||||
privateWin.close();
|
||||
Services.prefs.clearUserPref("browser.privatebrowsing.autostart");
|
||||
finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -402,23 +402,32 @@ Toolbox.prototype = {
|
||||
let id = toolDefinition.id;
|
||||
|
||||
let radio = this.doc.createElement("radio");
|
||||
radio.setAttribute("label", toolDefinition.label);
|
||||
radio.className = "toolbox-tab devtools-tab";
|
||||
radio.id = "toolbox-tab-" + id;
|
||||
radio.setAttribute("flex", "1");
|
||||
radio.setAttribute("toolid", id);
|
||||
radio.setAttribute("tooltiptext", toolDefinition.tooltip);
|
||||
if (toolDefinition.icon) {
|
||||
radio.setAttribute("src", toolDefinition.icon);
|
||||
}
|
||||
|
||||
radio.addEventListener("command", function(id) {
|
||||
this.selectTool(id);
|
||||
}.bind(this, id));
|
||||
|
||||
if (toolDefinition.icon) {
|
||||
let image = this.doc.createElement("image");
|
||||
image.setAttribute("src", toolDefinition.icon);
|
||||
radio.appendChild(image);
|
||||
}
|
||||
|
||||
let label = this.doc.createElement("label");
|
||||
label.setAttribute("value", toolDefinition.label)
|
||||
label.setAttribute("crop", "end");
|
||||
label.setAttribute("flex", "1");
|
||||
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.className = "toolbox-panel";
|
||||
vbox.id = "toolbox-panel-" + id;
|
||||
|
||||
radio.appendChild(label);
|
||||
tabs.appendChild(radio);
|
||||
deck.appendChild(vbox);
|
||||
|
||||
@ -434,6 +443,13 @@ Toolbox.prototype = {
|
||||
selectTool: function TBOX_selectTool(id) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let selected = this.doc.querySelector(".devtools-tab[selected]");
|
||||
if (selected) {
|
||||
selected.removeAttribute("selected");
|
||||
}
|
||||
let tab = this.doc.getElementById("toolbox-tab-" + id);
|
||||
tab.setAttribute("selected", "true");
|
||||
|
||||
if (this._currentToolId == id) {
|
||||
// Return the existing panel in order to have a consistent return value.
|
||||
return Promise.resolve(this._toolPanels.get(id));
|
||||
|
@ -11,7 +11,8 @@ let toolbox, toolIDs, idIndex;
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
if (window.navigator.oscpu.match(/osx 10\.8/i) || window.navigator.oscpu.match(/windows nt 5\.1/i)) {
|
||||
if (window.navigator.userAgent.indexOf("Mac OS X 10.8") != -1 ||
|
||||
window.navigator.userAgent.indexOf("Windows NT 5.1") != -1) {
|
||||
info("Skipping Mac OSX 10.8 and Windows xp, see bug 838069");
|
||||
finish();
|
||||
return;
|
||||
|
@ -28,9 +28,9 @@
|
||||
<hbox id="toolbox-dock-buttons"/>
|
||||
</hbox>
|
||||
#endif
|
||||
<radiogroup id="toolbox-tabs" orient="horizontal">
|
||||
</radiogroup>
|
||||
<hbox id="toolbox-buttons" flex="1" pack="end"/>
|
||||
<hbox id="toolbox-tabs" flex="1">
|
||||
</hbox>
|
||||
<hbox id="toolbox-buttons" pack="end"/>
|
||||
#ifndef XP_MACOSX
|
||||
<vbox id="toolbox-controls-separator"/>
|
||||
<hbox id="toolbox-controls">
|
||||
|
@ -24,6 +24,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Highlighter",
|
||||
"resource:///modules/devtools/Highlighter.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ToolSidebar",
|
||||
"resource:///modules/devtools/Sidebar.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SelectorSearch",
|
||||
"resource:///modules/devtools/SelectorSearch.jsm");
|
||||
|
||||
const LAYOUT_CHANGE_TIMER = 250;
|
||||
|
||||
@ -50,9 +52,7 @@ InspectorPanel.prototype = {
|
||||
open: function InspectorPanel_open() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
this.preventNavigateAway = this.preventNavigateAway.bind(this);
|
||||
this.onNavigatedAway = this.onNavigatedAway.bind(this);
|
||||
this.target.on("will-navigate", this.preventNavigateAway);
|
||||
this.target.on("navigate", this.onNavigatedAway);
|
||||
|
||||
this.nodemenu = this.panelDoc.getElementById("inspector-node-popup");
|
||||
@ -62,16 +62,6 @@ InspectorPanel.prototype = {
|
||||
this.nodemenu.addEventListener("popupshowing", this._setupNodeMenu, true);
|
||||
this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
|
||||
|
||||
// Initialize the search related items
|
||||
this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
|
||||
this._lastSearched = null;
|
||||
this._searchResults = null;
|
||||
this._searchIndex = 0;
|
||||
this._onHTMLSearch = this._onHTMLSearch.bind(this);
|
||||
this._onSearchKeypress = this._onSearchKeypress.bind(this);
|
||||
this.searchBox.addEventListener("command", this._onHTMLSearch, true);
|
||||
this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
|
||||
|
||||
// Create an empty selection
|
||||
this._selection = new Selection();
|
||||
this.onNewSelection = this.onNewSelection.bind(this);
|
||||
@ -153,6 +143,7 @@ InspectorPanel.prototype = {
|
||||
deferred.resolve(this);
|
||||
}.bind(this));
|
||||
|
||||
this.setupSearchBox();
|
||||
this.setupSidebar();
|
||||
|
||||
return deferred.promise;
|
||||
@ -195,6 +186,24 @@ InspectorPanel.prototype = {
|
||||
this.isDirty = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hooks the searchbar to show result and auto completion suggestions.
|
||||
*/
|
||||
setupSearchBox: function InspectorPanel_setupSearchBox() {
|
||||
// Initiate the selectors search object.
|
||||
let setNodeFunction = function(node) {
|
||||
this.selection.setNode(node, "selectorsearch");
|
||||
}.bind(this);
|
||||
if (this.searchSuggestions) {
|
||||
this.searchSuggestions.destroy();
|
||||
this.searchSuggestions = null;
|
||||
}
|
||||
this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
|
||||
this.searchSuggestions = new SelectorSearch(this.browser.contentDocument,
|
||||
this.searchBox,
|
||||
setNodeFunction);
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the sidebar.
|
||||
*/
|
||||
@ -256,6 +265,7 @@ InspectorPanel.prototype = {
|
||||
self.selection.setNode(newWindow.document.documentElement, "navigateaway");
|
||||
}
|
||||
self._initMarkup();
|
||||
self.setupSearchBox();
|
||||
}
|
||||
|
||||
if (newWindow.document.readyState == "loading") {
|
||||
@ -265,77 +275,6 @@ InspectorPanel.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a message if the inspector is dirty.
|
||||
*/
|
||||
preventNavigateAway: function InspectorPanel_preventNavigateAway(event, request) {
|
||||
if (!this.isDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
request.suspend();
|
||||
|
||||
let notificationBox = null;
|
||||
if (this.target.isLocalTab) {
|
||||
let gBrowser = this.target.tab.ownerDocument.defaultView.gBrowser;
|
||||
notificationBox = gBrowser.getNotificationBox();
|
||||
}
|
||||
else {
|
||||
notificationBox = this._toolbox.getNotificationBox();
|
||||
}
|
||||
|
||||
let notification = notificationBox.
|
||||
getNotificationWithValue("inspector-page-navigation");
|
||||
|
||||
if (notification) {
|
||||
notificationBox.removeNotification(notification, true);
|
||||
}
|
||||
|
||||
let cancelRequest = function onCancelRequest() {
|
||||
if (request) {
|
||||
request.cancel(Cr.NS_BINDING_ABORTED);
|
||||
request.resume(); // needed to allow the connection to be cancelled.
|
||||
request = null;
|
||||
}
|
||||
};
|
||||
|
||||
let eventCallback = function onNotificationCallback(event) {
|
||||
if (event == "removed") {
|
||||
cancelRequest();
|
||||
}
|
||||
};
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
id: "inspector.confirmNavigationAway.buttonLeave",
|
||||
label: this.strings.GetStringFromName("confirmNavigationAway.buttonLeave"),
|
||||
accessKey: this.strings.GetStringFromName("confirmNavigationAway.buttonLeaveAccesskey"),
|
||||
callback: function onButtonLeave() {
|
||||
if (request) {
|
||||
request.resume();
|
||||
request = null;
|
||||
}
|
||||
}.bind(this),
|
||||
},
|
||||
{
|
||||
id: "inspector.confirmNavigationAway.buttonStay",
|
||||
label: this.strings.GetStringFromName("confirmNavigationAway.buttonStay"),
|
||||
accessKey: this.strings.GetStringFromName("confirmNavigationAway.buttonStayAccesskey"),
|
||||
callback: cancelRequest
|
||||
},
|
||||
];
|
||||
|
||||
let message = this.strings.GetStringFromName("confirmNavigationAway.message2");
|
||||
|
||||
notification = notificationBox.appendNotification(message,
|
||||
"inspector-page-navigation", "chrome://browser/skin/Info.png",
|
||||
notificationBox.PRIORITY_WARNING_HIGH, buttons, eventCallback);
|
||||
|
||||
// Make sure this not a transient notification, to avoid the automatic
|
||||
// transient notification removal.
|
||||
notification.persistence = -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* When a new node is selected.
|
||||
*/
|
||||
@ -379,7 +318,6 @@ InspectorPanel.prototype = {
|
||||
this.browser = null;
|
||||
}
|
||||
|
||||
this.target.off("will-navigate", this.preventNavigateAway);
|
||||
this.target.off("navigate", this.onNavigatedAway);
|
||||
|
||||
if (this.highlighter) {
|
||||
@ -400,9 +338,8 @@ InspectorPanel.prototype = {
|
||||
|
||||
this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
|
||||
this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
|
||||
this.searchBox.removeEventListener("command", this._onHTMLSearch, true);
|
||||
this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true);
|
||||
this.breadcrumbs.destroy();
|
||||
this.searchSuggestions.destroy();
|
||||
this.selection.off("new-node", this.onNewSelection);
|
||||
this.selection.off("before-new-node", this.onBeforeNewSelection);
|
||||
this.selection.off("detached", this.onDetached);
|
||||
@ -414,82 +351,14 @@ InspectorPanel.prototype = {
|
||||
this.panelDoc = null;
|
||||
this.panelWin = null;
|
||||
this.breadcrumbs = null;
|
||||
this.searchSuggestions = null;
|
||||
this.lastNodemenuItem = null;
|
||||
this.nodemenu = null;
|
||||
this.searchBox = null;
|
||||
this.highlighter = null;
|
||||
this._searchResults = null;
|
||||
|
||||
return Promise.resolve(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* The command callback for the HTML search box. This function is
|
||||
* automatically invoked as the user is typing.
|
||||
*/
|
||||
_onHTMLSearch: function InspectorPanel__onHTMLSearch() {
|
||||
let query = this.searchBox.value;
|
||||
if (query == this._lastSearched) {
|
||||
return;
|
||||
}
|
||||
this._lastSearched = query;
|
||||
this._searchIndex = 0;
|
||||
|
||||
if (query.length == 0) {
|
||||
this.searchBox.removeAttribute("filled");
|
||||
this.searchBox.classList.remove("devtools-no-search-result");
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchBox.setAttribute("filled", true);
|
||||
this._searchResults = this.browser.contentDocument.querySelectorAll(query);
|
||||
if (this._searchResults.length > 0) {
|
||||
this.searchBox.classList.remove("devtools-no-search-result");
|
||||
this.cancelLayoutChange();
|
||||
this.selection.setNode(this._searchResults[0]);
|
||||
} else {
|
||||
this.searchBox.classList.add("devtools-no-search-result");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Search for the search box value as a query selector.
|
||||
*/
|
||||
_onSearchKeypress: function InspectorPanel__onSearchKeypress(aEvent) {
|
||||
let query = this.searchBox.value;
|
||||
switch(aEvent.keyCode) {
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
if (query == this._lastSearched) {
|
||||
this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
|
||||
} else {
|
||||
this._onHTMLSearch();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_UP:
|
||||
if (--this._searchIndex < 0) {
|
||||
this._searchIndex = this._searchResults.length - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_DOWN:
|
||||
this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
this.cancelLayoutChange();
|
||||
if (this._searchResults.length > 0) {
|
||||
this.selection.setNode(this._searchResults[this._searchIndex]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the node menu.
|
||||
*/
|
||||
|
549
browser/devtools/inspector/SelectorSearch.jsm
Normal file
549
browser/devtools/inspector/SelectorSearch.jsm
Normal file
@ -0,0 +1,549 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AutocompletePopup",
|
||||
"resource:///modules/devtools/AutocompletePopup.jsm");
|
||||
this.EXPORTED_SYMBOLS = ["SelectorSearch"];
|
||||
|
||||
// Maximum number of selector suggestions shown in the panel.
|
||||
const MAX_SUGGESTIONS = 15;
|
||||
|
||||
/**
|
||||
* Converts any input box on a page to a CSS selector search and suggestion box.
|
||||
*
|
||||
* @constructor
|
||||
* @param nsIDOMDocument aContentDocument
|
||||
* The content document which inspector is attached to.
|
||||
* @param nsiInputElement aInputNode
|
||||
* The input element to which the panel will be attached and from where
|
||||
* search input will be taken.
|
||||
* @param Function aCallback
|
||||
* The method to callback when a search is available.
|
||||
* This method is called with the matched node as the first argument.
|
||||
*/
|
||||
this.SelectorSearch = function(aContentDocument, aInputNode, aCallback) {
|
||||
this.doc = aContentDocument;
|
||||
this.callback = aCallback;
|
||||
this.searchBox = aInputNode;
|
||||
this.panelDoc = this.searchBox.ownerDocument;
|
||||
|
||||
// initialize variables.
|
||||
this._lastSearched = null;
|
||||
this._lastValidSearch = "";
|
||||
this._lastToLastValidSearch = null;
|
||||
this._searchResults = null;
|
||||
this._searchSuggestions = {};
|
||||
this._searchIndex = 0;
|
||||
|
||||
// bind!
|
||||
this._showPopup = this._showPopup.bind(this);
|
||||
this._onHTMLSearch = this._onHTMLSearch.bind(this);
|
||||
this._onSearchKeypress = this._onSearchKeypress.bind(this);
|
||||
this._onListBoxKeypress = this._onListBoxKeypress.bind(this);
|
||||
|
||||
// Options for the AutocompletePopup.
|
||||
let options = {
|
||||
panelId: "inspector-searchbox-panel",
|
||||
listBoxId: "searchbox-panel-listbox",
|
||||
fixedWidth: true,
|
||||
autoSelect: true,
|
||||
position: "before_start",
|
||||
direction: "ltr",
|
||||
onClick: this._onListBoxKeypress,
|
||||
onKeypress: this._onListBoxKeypress,
|
||||
};
|
||||
this.searchPopup = new AutocompletePopup(this.panelDoc, options);
|
||||
|
||||
// event listeners.
|
||||
this.searchBox.addEventListener("command", this._onHTMLSearch, true);
|
||||
this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
|
||||
}
|
||||
|
||||
this.SelectorSearch.prototype = {
|
||||
|
||||
// The possible states of the query.
|
||||
States: {
|
||||
CLASS: "class",
|
||||
ID: "id",
|
||||
TAG: "tag",
|
||||
},
|
||||
|
||||
// The current state of the query.
|
||||
_state: null,
|
||||
|
||||
// The query corresponding to last state computation.
|
||||
_lastStateCheckAt: null,
|
||||
|
||||
/**
|
||||
* Computes the state of the query. State refers to whether the query
|
||||
* currently requires a class suggestion, or a tag, or an Id suggestion.
|
||||
* This getter will effectively compute the state by traversing the query
|
||||
* character by character each time the query changes.
|
||||
*
|
||||
* @example
|
||||
* '#f' requires an Id suggestion, so the state is States.ID
|
||||
* 'div > .foo' requires class suggestion, so state is States.CLASS
|
||||
*/
|
||||
get state() {
|
||||
if (!this.searchBox || !this.searchBox.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let query = this.searchBox.value;
|
||||
if (this._lastStateCheckAt == query) {
|
||||
// If query is the same, return early.
|
||||
return this._state;
|
||||
}
|
||||
this._lastStateCheckAt = query;
|
||||
|
||||
this._state = null;
|
||||
let subQuery = "";
|
||||
// Now we iterate over the query and decide the state character by character.
|
||||
// The logic here is that while iterating, the state can go from one to
|
||||
// another with some restrictions. Like, if the state is Class, then it can
|
||||
// never go to Tag state without a space or '>' character; Or like, a Class
|
||||
// state with only '.' cannot go to an Id state without any [a-zA-Z] after
|
||||
// the '.' which means that '.#' is a selector matching a class name '#'.
|
||||
// Similarily for '#.' which means a selctor matching an id '.'.
|
||||
for (let i = 1; i <= query.length; i++) {
|
||||
// Calculate the state.
|
||||
subQuery = query.slice(0, i);
|
||||
let [secondLastChar, lastChar] = subQuery.slice(-2);
|
||||
switch (this._state) {
|
||||
case null:
|
||||
// This will happen only in the first iteration of the for loop.
|
||||
lastChar = secondLastChar;
|
||||
case this.States.TAG:
|
||||
this._state = lastChar == "."
|
||||
? this.States.CLASS
|
||||
: lastChar == "#"
|
||||
? this.States.ID
|
||||
: this.States.TAG;
|
||||
break;
|
||||
|
||||
case this.States.CLASS:
|
||||
if (subQuery.match(/[\.]+[^\.]*$/)[0].length > 2) {
|
||||
// Checks whether the subQuery has atleast one [a-zA-Z] after the '.'.
|
||||
this._state = (lastChar == " " || lastChar == ">")
|
||||
? this.States.TAG
|
||||
: lastChar == "#"
|
||||
? this.States.ID
|
||||
: this.States.CLASS;
|
||||
}
|
||||
break;
|
||||
|
||||
case this.States.ID:
|
||||
if (subQuery.match(/[#]+[^#]*$/)[0].length > 2) {
|
||||
// Checks whether the subQuery has atleast one [a-zA-Z] after the '#'.
|
||||
this._state = (lastChar == " " || lastChar == ">")
|
||||
? this.States.TAG
|
||||
: lastChar == "."
|
||||
? this.States.CLASS
|
||||
: this.States.ID;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this._state;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes event listeners and cleans up references.
|
||||
*/
|
||||
destroy: function SelectorSearch_destroy() {
|
||||
// event listeners.
|
||||
this.searchBox.removeEventListener("command", this._onHTMLSearch, true);
|
||||
this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true);
|
||||
this.searchPopup.destroy();
|
||||
this.searchPopup = null;
|
||||
this.searchBox = null;
|
||||
this.doc = null;
|
||||
this.panelDoc = null;
|
||||
this._searchResults = null;
|
||||
this._searchSuggestions = null;
|
||||
this.callback = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* The command callback for the input box. This function is automatically
|
||||
* invoked as the user is typing if the input box type is search.
|
||||
*/
|
||||
_onHTMLSearch: function SelectorSearch__onHTMLSearch() {
|
||||
let query = this.searchBox.value;
|
||||
if (query == this._lastSearched) {
|
||||
return;
|
||||
}
|
||||
this._lastSearched = query;
|
||||
this._searchIndex = 0;
|
||||
|
||||
if (query.length == 0) {
|
||||
this._lastValidSearch = "";
|
||||
this.searchBox.removeAttribute("filled");
|
||||
this.searchBox.classList.remove("devtools-no-search-result");
|
||||
if (this.searchPopup.isOpen) {
|
||||
this.searchPopup.hidePopup();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchBox.setAttribute("filled", true);
|
||||
try {
|
||||
this._searchResults = this.doc.querySelectorAll(query);
|
||||
}
|
||||
catch (ex) {
|
||||
this._searchResults = [];
|
||||
}
|
||||
if (this._searchResults.length > 0) {
|
||||
this._lastValidSearch = query;
|
||||
// Even though the selector matched atleast one node, there is still
|
||||
// possibility of suggestions.
|
||||
if (query.match(/[\s>+]$/)) {
|
||||
// If the query has a space or '>' at the end, create a selector to match
|
||||
// the children of the selector inside the search box by adding a '*'.
|
||||
this._lastValidSearch += "*";
|
||||
}
|
||||
else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
|
||||
// If the query is a partial descendant selector which does not matches
|
||||
// any node, remove the last incomplete part and add a '*' to match
|
||||
// everything. For ex, convert 'foo > b' to 'foo > *' .
|
||||
let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+]*$/)[0];
|
||||
this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
|
||||
}
|
||||
|
||||
if (!query.slice(-1).match(/[\.#\s>+]/)) {
|
||||
// Hide the popup if we have some matching nodes and the query is not
|
||||
// ending with [.# >] which means that the selector is not at the
|
||||
// beginning of a new class, tag or id.
|
||||
if (this.searchPopup.isOpen) {
|
||||
this.searchPopup.hidePopup();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.showSuggestions();
|
||||
}
|
||||
this.searchBox.classList.remove("devtools-no-search-result");
|
||||
this.callback(this._searchResults[0]);
|
||||
}
|
||||
else {
|
||||
if (query.match(/[\s>+]$/)) {
|
||||
this._lastValidSearch = query + "*";
|
||||
}
|
||||
else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
|
||||
let lastPart = query.match(/[\s+>][\.#a-zA-Z][^>\s+]*$/)[0];
|
||||
this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
|
||||
}
|
||||
this.searchBox.classList.add("devtools-no-search-result");
|
||||
this.showSuggestions();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles keypresses inside the input box.
|
||||
*/
|
||||
_onSearchKeypress: function SelectorSearch__onSearchKeypress(aEvent) {
|
||||
let query = this.searchBox.value;
|
||||
switch(aEvent.keyCode) {
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
if (query == this._lastSearched) {
|
||||
this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
|
||||
}
|
||||
else {
|
||||
this._onHTMLSearch();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_UP:
|
||||
if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
|
||||
this.searchPopup.focus();
|
||||
if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
|
||||
this.searchPopup.selectedIndex =
|
||||
Math.max(0, this.searchPopup.itemCount - 2);
|
||||
}
|
||||
else {
|
||||
this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
|
||||
}
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
}
|
||||
else if (--this._searchIndex < 0) {
|
||||
this._searchIndex = this._searchResults.length - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_DOWN:
|
||||
if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
|
||||
this.searchPopup.focus();
|
||||
this.searchPopup.selectedIndex = 0;
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
}
|
||||
this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_TAB:
|
||||
if (this.searchPopup.isOpen &&
|
||||
this.searchPopup.getItemAtIndex(this.searchPopup.itemCount - 1)
|
||||
.preLabel == query) {
|
||||
this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
this._onHTMLSearch();
|
||||
}
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_BACK_SPACE:
|
||||
case aEvent.DOM_VK_DELETE:
|
||||
// need to throw away the lastValidSearch.
|
||||
this._lastToLastValidSearch = null;
|
||||
// This gets the most complete selector from the query. For ex.
|
||||
// '.foo.ba' returns '.foo' , '#foo > .bar.baz' returns '#foo > .bar'
|
||||
// '.foo +bar' returns '.foo +' and likewise.
|
||||
this._lastValidSearch = (query.match(/(.*)[\.#][^\.# ]{0,}$/) ||
|
||||
query.match(/(.*[\s>+])[a-zA-Z][^\.# ]{0,}$/) ||
|
||||
["",""])[1];
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
if (this._searchResults.length > 0) {
|
||||
this.callback(this._searchResults[this._searchIndex]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles keypress and mouse click on the suggestions richlistbox.
|
||||
*/
|
||||
_onListBoxKeypress: function SelectorSearch__onListBoxKeypress(aEvent) {
|
||||
switch(aEvent.keyCode || aEvent.button) {
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
case aEvent.DOM_VK_TAB:
|
||||
case 0: // left mouse button
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
this.searchBox.value = this.searchPopup.selectedItem.label;
|
||||
this.searchBox.focus();
|
||||
this._onHTMLSearch();
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_UP:
|
||||
if (this.searchPopup.selectedIndex == 0) {
|
||||
this.searchPopup.selectedIndex = -1;
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
this.searchBox.focus();
|
||||
}
|
||||
else {
|
||||
let index = this.searchPopup.selectedIndex;
|
||||
this.searchBox.value = this.searchPopup.getItemAtIndex(index - 1).label;
|
||||
}
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_DOWN:
|
||||
if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
|
||||
this.searchPopup.selectedIndex = -1;
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
this.searchBox.focus();
|
||||
}
|
||||
else {
|
||||
let index = this.searchPopup.selectedIndex;
|
||||
this.searchBox.value = this.searchPopup.getItemAtIndex(index + 1).label;
|
||||
}
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_BACK_SPACE:
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
this.searchBox.focus();
|
||||
if (this.searchBox.selectionStart > 0) {
|
||||
this.searchBox.value =
|
||||
this.searchBox.value.substring(0, this.searchBox.selectionStart - 1);
|
||||
}
|
||||
this._lastToLastValidSearch = null;
|
||||
let query = this.searchBox.value;
|
||||
this._lastValidSearch = (query.match(/(.*)[\.#][^\.# ]{0,}$/) ||
|
||||
query.match(/(.*[\s>+])[a-zA-Z][^\.# ]{0,}$/) ||
|
||||
["",""])[1];
|
||||
this._onHTMLSearch();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Populates the suggestions list and show the suggestion popup.
|
||||
*/
|
||||
_showPopup: function SelectorSearch__showPopup(aList, aFirstPart) {
|
||||
// Sort alphabetically in increaseing order.
|
||||
aList = aList.sort();
|
||||
// Sort based on count= in decreasing order.
|
||||
aList = aList.sort(function([a1,a2], [b1,b2]) {
|
||||
return a2 < b2;
|
||||
});
|
||||
|
||||
let total = 0;
|
||||
let query = this.searchBox.value;
|
||||
let toLowerCase = false;
|
||||
let items = [];
|
||||
// In case of tagNames, change the case to small.
|
||||
if (query.match(/.*[\.#][^\.#]{0,}$/) == null) {
|
||||
toLowerCase = true;
|
||||
}
|
||||
for (let [value, count] of aList) {
|
||||
// for cases like 'div ' or 'div >' or 'div+'
|
||||
if (query.match(/[\s>+]$/)) {
|
||||
value = query + value;
|
||||
}
|
||||
// for cases like 'div #a' or 'div .a' or 'div > d' and likewise
|
||||
else if (query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#]*$/)) {
|
||||
let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+\.#]*$/)[0];
|
||||
value = query.slice(0, -1 * lastPart.length + 1) + value;
|
||||
}
|
||||
// for cases like 'div.class' or '#foo.bar' and likewise
|
||||
else if (query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)) {
|
||||
let lastPart = query.match(/[a-zA-Z][#\.][^#\.\s>+]*$/)[0];
|
||||
value = query.slice(0, -1 * lastPart.length + 1) + value;
|
||||
}
|
||||
let item = {
|
||||
preLabel: query,
|
||||
label: value,
|
||||
count: count
|
||||
};
|
||||
if (toLowerCase) {
|
||||
item.label = value.toLowerCase();
|
||||
}
|
||||
items.unshift(item);
|
||||
if (++total > MAX_SUGGESTIONS - 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (total > 0) {
|
||||
this.searchPopup.setItems(items);
|
||||
this.searchPopup.openPopup(this.searchBox);
|
||||
}
|
||||
else {
|
||||
this.searchPopup.hidePopup();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Suggests classes,ids and tags based on the user input as user types in the
|
||||
* searchbox.
|
||||
*/
|
||||
showSuggestions: function SelectorSearch_showSuggestions() {
|
||||
let query = this.searchBox.value;
|
||||
if (this._lastValidSearch != "" &&
|
||||
this._lastToLastValidSearch != this._lastValidSearch) {
|
||||
this._searchSuggestions = {
|
||||
ids: new Map(),
|
||||
classes: new Map(),
|
||||
tags: new Map(),
|
||||
};
|
||||
|
||||
let nodes = [];
|
||||
try {
|
||||
nodes = this.doc.querySelectorAll(this._lastValidSearch);
|
||||
} catch (ex) {}
|
||||
for (let node of nodes) {
|
||||
this._searchSuggestions.ids.set(node.id, 1);
|
||||
this._searchSuggestions.tags
|
||||
.set(node.tagName,
|
||||
(this._searchSuggestions.tags.get(node.tagName) || 0) + 1);
|
||||
for (let className of node.classList) {
|
||||
this._searchSuggestions.classes
|
||||
.set(className,
|
||||
(this._searchSuggestions.classes.get(className) || 0) + 1);
|
||||
}
|
||||
}
|
||||
this._lastToLastValidSearch = this._lastValidSearch;
|
||||
}
|
||||
else if (this._lastToLastValidSearch != this._lastValidSearch) {
|
||||
this._searchSuggestions = {
|
||||
ids: new Map(),
|
||||
classes: new Map(),
|
||||
tags: new Map(),
|
||||
};
|
||||
|
||||
if (query.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nodes = null;
|
||||
if (this.state == this.States.CLASS) {
|
||||
nodes = this.doc.querySelectorAll("[class]");
|
||||
for (let node of nodes) {
|
||||
for (let className of node.classList) {
|
||||
this._searchSuggestions.classes
|
||||
.set(className,
|
||||
(this._searchSuggestions.classes.get(className) || 0) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.state == this.States.ID) {
|
||||
nodes = this.doc.querySelectorAll("[id]");
|
||||
for (let node of nodes) {
|
||||
this._searchSuggestions.ids.set(node.id, 1);
|
||||
}
|
||||
}
|
||||
else if (this.state == this.States.TAG) {
|
||||
nodes = this.doc.getElementsByTagName("*");
|
||||
for (let node of nodes) {
|
||||
this._searchSuggestions.tags
|
||||
.set(node.tagName,
|
||||
(this._searchSuggestions.tags.get(node.tagName) || 0) + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
this._lastToLastValidSearch = this._lastValidSearch;
|
||||
}
|
||||
|
||||
// Filter the suggestions based on search box value.
|
||||
let result = [];
|
||||
let firstPart = "";
|
||||
if (this.state == this.States.TAG) {
|
||||
// gets the tag that is being completed. For ex. 'div.foo > s' returns 's',
|
||||
// 'di' returns 'di' and likewise.
|
||||
firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["",query])[1];
|
||||
for (let [tag, count] of this._searchSuggestions.tags) {
|
||||
if (tag.toLowerCase().startsWith(firstPart.toLowerCase())) {
|
||||
result.push([tag, count]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.state == this.States.CLASS) {
|
||||
// gets the class that is being completed. For ex. '.foo.b' returns 'b'
|
||||
firstPart = query.match(/\.([^\.]*)$/)[1];
|
||||
for (let [className, count] of this._searchSuggestions.classes) {
|
||||
if (className.startsWith(firstPart)) {
|
||||
result.push(["." + className, count]);
|
||||
}
|
||||
}
|
||||
firstPart = "." + firstPart;
|
||||
}
|
||||
else if (this.state == this.States.ID) {
|
||||
// gets the id that is being completed. For ex. '.foo#b' returns 'b'
|
||||
firstPart = query.match(/#([^#]*)$/)[1];
|
||||
for (let [id, count] of this._searchSuggestions.ids) {
|
||||
if (id.startsWith(firstPart)) {
|
||||
result.push(["#" + id, 1]);
|
||||
}
|
||||
}
|
||||
firstPart = "#" + firstPart;
|
||||
}
|
||||
|
||||
this._showPopup(result, firstPart);
|
||||
},
|
||||
};
|
@ -6,3 +6,23 @@
|
||||
#inspector-sidebar {
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
#searchbox-panel-listbox {
|
||||
width: 250px;
|
||||
max-width: 250px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#searchbox-panel-listbox > richlistitem,
|
||||
#searchbox-panel-listbox > richlistitem[selected] {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#searchbox-panel-listbox > richlistitem > .initial-value {
|
||||
max-width: 130px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#searchbox-panel-listbox > richlistitem > .autocomplete-value {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ _BROWSER_FILES = \
|
||||
browser_inspector_destroyselection.js \
|
||||
browser_inspector_bug_699308_iframe_navigation.js \
|
||||
browser_inspector_bug_672902_keyboard_shortcuts.js \
|
||||
browser_inspector_bug_566084_location_changed.js \
|
||||
browser_inspector_sidebarstate.js \
|
||||
browser_inspector_pseudoclass_lock.js \
|
||||
browser_inspector_cmd_inspect.js \
|
||||
@ -39,6 +38,10 @@ _BROWSER_FILES = \
|
||||
browser_inspector_bug_817558_delete_node.js \
|
||||
browser_inspector_bug_650804_search.js \
|
||||
browser_inspector_bug_650804_search.html \
|
||||
browser_inspector_bug_831693_input_suggestion.js \
|
||||
browser_inspector_bug_831693_searchbox_panel_navigation.js \
|
||||
browser_inspector_bug_831693_combinator_suggestions.js \
|
||||
browser_inspector_bug_831693_search_suggestions.html \
|
||||
browser_inspector_bug_835722_infobar_reappears.js \
|
||||
browser_inspector_bug_840156_destroy_after_navigation.js \
|
||||
head.js \
|
||||
|
@ -1,131 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
|
||||
let TargetFactory = tempScope.TargetFactory;
|
||||
|
||||
function test() {
|
||||
let notificationBox, inspector;
|
||||
let alertActive1_called = false;
|
||||
let alertActive2_called = false;
|
||||
|
||||
function startLocationTests() {
|
||||
openInspector(runInspectorTests);
|
||||
}
|
||||
|
||||
function runInspectorTests(aInspector) {
|
||||
inspector = aInspector;
|
||||
|
||||
let para = content.document.querySelector("p");
|
||||
ok(para, "found the paragraph element");
|
||||
is(para.textContent, "init", "paragraph content is correct");
|
||||
|
||||
inspector.markDirty();
|
||||
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
notificationBox = gBrowser.getNotificationBox();
|
||||
notificationBox.addEventListener("AlertActive", alertActive1, false);
|
||||
|
||||
ok(toolbox, "We have access to the notificationBox");
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", onPageLoad, true);
|
||||
|
||||
content.location = "data:text/html,<div>location change test 1 for " +
|
||||
"inspector</div><p>test1</p>";
|
||||
}
|
||||
|
||||
function alertActive1() {
|
||||
alertActive1_called = true;
|
||||
notificationBox.removeEventListener("AlertActive", alertActive1, false);
|
||||
|
||||
let notification = notificationBox.
|
||||
getNotificationWithValue("inspector-page-navigation");
|
||||
ok(notification, "found the inspector-page-navigation notification");
|
||||
|
||||
// By closing the notification it is expected that page navigation is
|
||||
// canceled.
|
||||
executeSoon(function() {
|
||||
notification.close();
|
||||
locationTest2();
|
||||
});
|
||||
}
|
||||
|
||||
function locationTest2() {
|
||||
// Location did not change.
|
||||
let para = content.document.querySelector("p");
|
||||
ok(para, "found the paragraph element, second time");
|
||||
is(para.textContent, "init", "paragraph content is correct");
|
||||
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
let inspector = gDevTools.getToolbox(target).getPanel("inspector");
|
||||
ok(inspector, "Inspector still alive");
|
||||
|
||||
notificationBox.addEventListener("AlertActive", alertActive2, false);
|
||||
|
||||
content.location = "data:text/html,<div>location change test 2 for " +
|
||||
"inspector</div><p>test2</p>";
|
||||
}
|
||||
|
||||
function alertActive2() {
|
||||
alertActive2_called = true;
|
||||
notificationBox.removeEventListener("AlertActive", alertActive2, false);
|
||||
|
||||
let notification = notificationBox.
|
||||
getNotificationWithValue("inspector-page-navigation");
|
||||
ok(notification, "found the inspector-page-navigation notification");
|
||||
|
||||
let buttons = notification.querySelectorAll("button");
|
||||
let buttonLeave = null;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
if (buttons[i].buttonInfo.id == "inspector.confirmNavigationAway.buttonLeave") {
|
||||
buttonLeave = buttons[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ok(buttonLeave, "the Leave page button was found");
|
||||
|
||||
// Accept page navigation.
|
||||
executeSoon(function(){
|
||||
buttonLeave.doCommand();
|
||||
});
|
||||
}
|
||||
|
||||
function onPageLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onPageLoad, true);
|
||||
|
||||
isnot(content.location.href.indexOf("test2"), -1,
|
||||
"page navigated to the correct location");
|
||||
|
||||
let para = content.document.querySelector("p");
|
||||
ok(para, "found the paragraph element, third time");
|
||||
is(para.textContent, "test2", "paragraph content is correct");
|
||||
|
||||
let root = content.document.documentElement;
|
||||
is(inspector.selection.node, root, "Selection is the root of the new page.");
|
||||
|
||||
ok(alertActive1_called, "first notification box has been showed");
|
||||
ok(alertActive2_called, "second notification box has been showed");
|
||||
testEnd();
|
||||
}
|
||||
|
||||
|
||||
function testEnd() {
|
||||
notificationBox = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
executeSoon(finish);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
|
||||
waitForFocus(startLocationTests, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<div>location change tests for " +
|
||||
"inspector.</div><p>init</p>";
|
||||
}
|
@ -20,7 +20,7 @@ function test()
|
||||
["v", "d1", true],
|
||||
["VK_DOWN", "d2", true],
|
||||
["VK_ENTER", "d1", true],
|
||||
[".", "d1", true],
|
||||
[".", "d1", false],
|
||||
["c", "d1", false],
|
||||
["1", "d2", true],
|
||||
["VK_DOWN", "d2", true],
|
||||
@ -30,7 +30,7 @@ function test()
|
||||
["VK_BACK_SPACE", "d1", false],
|
||||
["VK_BACK_SPACE", "d1", false],
|
||||
["VK_BACK_SPACE", "d1", true],
|
||||
[".", "d1", true],
|
||||
[".", "d1", false],
|
||||
["c", "d1", false],
|
||||
["1", "d2", true],
|
||||
["VK_DOWN", "s2", true],
|
||||
|
@ -0,0 +1,113 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
let inspector, searchBox, state, popup;
|
||||
|
||||
// The various states of the inspector: [key, suggestions array]
|
||||
// [
|
||||
// what key to press,
|
||||
// suggestions array with count [
|
||||
// [suggestion1, count1], [suggestion2] ...
|
||||
// ] count can be left to represent 1
|
||||
// ]
|
||||
let keyStates = [
|
||||
["d", [["div", 4]]],
|
||||
["i", [["div", 4]]],
|
||||
["v", []],
|
||||
[" ", [["div div", 2], ["div span", 2]]],
|
||||
[">", [["div >div", 2], ["div >span", 2]]],
|
||||
["VK_BACK_SPACE", [["div div", 2], ["div span", 2]]],
|
||||
["+", [["div +span"]]],
|
||||
["VK_BACK_SPACE", [["div div", 2], ["div span", 2]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["VK_BACK_SPACE", [["div", 4]]],
|
||||
["VK_BACK_SPACE", [["div", 4]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["p", []],
|
||||
[" ", [["p strong"]]],
|
||||
["+", [["p +button"], ["p +p"]]],
|
||||
["b", [["p +button"]]],
|
||||
["u", [["p +button"]]],
|
||||
["t", [["p +button"]]],
|
||||
["t", [["p +button"]]],
|
||||
["o", [["p +button"]]],
|
||||
["n", []],
|
||||
["+", [["p +button+p"]]],
|
||||
];
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "http://mochi.test:8888/browser/browser/devtools/inspector/test/browser_inspector_bug_831693_search_suggestions.html";
|
||||
|
||||
function $(id) {
|
||||
if (id == null) return null;
|
||||
return content.document.getElementById(id);
|
||||
}
|
||||
|
||||
function setupTest()
|
||||
{
|
||||
openInspector(startTest);
|
||||
}
|
||||
|
||||
function startTest(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
searchBox =
|
||||
inspector.panelWin.document.getElementById("inspector-searchbox");
|
||||
popup = inspector.searchSuggestions.searchPopup;
|
||||
|
||||
focusSearchBoxUsingShortcut(inspector.panelWin, function() {
|
||||
searchBox.addEventListener("command", checkState, true);
|
||||
checkStateAndMoveOn(0);
|
||||
});
|
||||
}
|
||||
|
||||
function checkStateAndMoveOn(index) {
|
||||
if (index == keyStates.length) {
|
||||
finishUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let [key, suggestions] = keyStates[index];
|
||||
state = index;
|
||||
|
||||
info("pressing key " + key + " to get suggestions " +
|
||||
JSON.stringify(suggestions));
|
||||
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
||||
}
|
||||
|
||||
function checkState(event) {
|
||||
executeSoon(function() {
|
||||
let [key, suggestions] = keyStates[state];
|
||||
let actualSuggestions = popup.getItems();
|
||||
is(popup._panel.state == "open" || popup._panel.state == "showing"
|
||||
? actualSuggestions.length: 0, suggestions.length,
|
||||
"There are expected number of suggestions at " + state + "th step.");
|
||||
actualSuggestions = actualSuggestions.reverse();
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
is(suggestions[i][0], actualSuggestions[i].label,
|
||||
"The suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.")
|
||||
is(suggestions[i][1] || 1, actualSuggestions[i].count,
|
||||
"The count for suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.")
|
||||
}
|
||||
checkStateAndMoveOn(state + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
searchBox = null;
|
||||
popup = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
let inspector, searchBox, state, popup;
|
||||
|
||||
// The various states of the inspector: [key, suggestions array]
|
||||
// [
|
||||
// what key to press,
|
||||
// suggestions array with count [
|
||||
// [suggestion1, count1], [suggestion2] ...
|
||||
// ] count can be left to represent 1
|
||||
// ]
|
||||
let keyStates = [
|
||||
["d", [["div", 2]]],
|
||||
["i", [["div", 2]]],
|
||||
["v", []],
|
||||
[".", [["div.c1"]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["#", [["div#d1"], ["div#d2"]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["VK_BACK_SPACE", [["div", 2]]],
|
||||
["VK_BACK_SPACE", [["div", 2]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
[".", [[".c1", 3], [".c2"]]],
|
||||
["c", [[".c1", 3], [".c2"]]],
|
||||
["2", []],
|
||||
["VK_BACK_SPACE", [[".c1", 3], [".c2"]]],
|
||||
["1", []],
|
||||
["#", [["#d2"], ["#p1"], ["#s2"]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["VK_BACK_SPACE", [[".c1", 3], [".c2"]]],
|
||||
["VK_BACK_SPACE", [[".c1", 3], [".c2"]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["#", [["#b1"], ["#d1"], ["#d2"], ["#p1"], ["#p2"], ["#p3"], ["#s1"], ["#s2"]]],
|
||||
["p", [["#p1"], ["#p2"], ["#p3"]]],
|
||||
["VK_BACK_SPACE", [["#b1"], ["#d1"], ["#d2"], ["#p1"], ["#p2"], ["#p3"], ["#s1"], ["#s2"]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
];
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "http://mochi.test:8888/browser/browser/devtools/inspector/test/browser_inspector_bug_650804_search.html";
|
||||
|
||||
function $(id) {
|
||||
if (id == null) return null;
|
||||
return content.document.getElementById(id);
|
||||
}
|
||||
|
||||
function setupTest()
|
||||
{
|
||||
openInspector(startTest);
|
||||
}
|
||||
|
||||
function startTest(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
searchBox =
|
||||
inspector.panelWin.document.getElementById("inspector-searchbox");
|
||||
popup = inspector.searchSuggestions.searchPopup;
|
||||
|
||||
focusSearchBoxUsingShortcut(inspector.panelWin, function() {
|
||||
searchBox.addEventListener("command", checkState, true);
|
||||
checkStateAndMoveOn(0);
|
||||
});
|
||||
}
|
||||
|
||||
function checkStateAndMoveOn(index) {
|
||||
if (index == keyStates.length) {
|
||||
finishUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let [key, suggestions] = keyStates[index];
|
||||
state = index;
|
||||
|
||||
info("pressing key " + key + " to get suggestions " +
|
||||
JSON.stringify(suggestions));
|
||||
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
||||
}
|
||||
|
||||
function checkState(event) {
|
||||
executeSoon(function() {
|
||||
let [key, suggestions] = keyStates[state];
|
||||
let actualSuggestions = popup.getItems();
|
||||
is(popup._panel.state == "open" || popup._panel.state == "showing"
|
||||
? actualSuggestions.length: 0, suggestions.length,
|
||||
"There are expected number of suggestions at " + state + "th step.");
|
||||
actualSuggestions = actualSuggestions.reverse();
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
is(suggestions[i][0], actualSuggestions[i].label,
|
||||
"The suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.")
|
||||
is(suggestions[i][1] || 1, actualSuggestions[i].count,
|
||||
"The count for suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.")
|
||||
}
|
||||
checkStateAndMoveOn(state + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
searchBox = null;
|
||||
popup = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Inspector Search Box Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="d1">
|
||||
<div class="l1">
|
||||
<div id="d2" class="c1">Hello, I'm nested div</div>
|
||||
</div>
|
||||
</div>
|
||||
<span id="s1">Hello, I'm a span
|
||||
<div class="l1">
|
||||
<span>Hi I am a nested span</span>
|
||||
<span class="s4">Hi I am a nested classed span</span>
|
||||
</div>
|
||||
</span>
|
||||
<span class="c1" id="s2">And me</span>
|
||||
|
||||
<p class="c1" id="p1">.someclass</p>
|
||||
<p id="p2">#someid</p>
|
||||
<button id="b1" disabled>button[disabled]</button>
|
||||
<p id="p3" class="c2"><strong>p>strong</strong></p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,156 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
requestLongerTimeout(2);
|
||||
|
||||
let inspector, searchBox, state, panel;
|
||||
let panelOpeningStates = [0, 3, 14, 17];
|
||||
let panelClosingStates = [2, 13, 16];
|
||||
|
||||
// The various states of the inspector: [key, query]
|
||||
// [
|
||||
// what key to press,
|
||||
// what should be the text in the searchbox
|
||||
// ]
|
||||
let keyStates = [
|
||||
["d", "d"],
|
||||
["i", "di"],
|
||||
["v", "div"],
|
||||
[".", "div."],
|
||||
["VK_UP", "div.c1"],
|
||||
["VK_DOWN", "div.l1"],
|
||||
["VK_DOWN", "div.l1"],
|
||||
["VK_BACK_SPACE", "div.l"],
|
||||
["VK_TAB", "div.l1"],
|
||||
[" ", "div.l1 "],
|
||||
["VK_UP", "div.l1 DIV"],
|
||||
["VK_UP", "div.l1 DIV"],
|
||||
[".", "div.l1 DIV."],
|
||||
["VK_TAB", "div.l1 DIV.c1"],
|
||||
["VK_BACK_SPACE", "div.l1 DIV.c"],
|
||||
["VK_BACK_SPACE", "div.l1 DIV."],
|
||||
["VK_BACK_SPACE", "div.l1 DIV"],
|
||||
["VK_BACK_SPACE", "div.l1 DI"],
|
||||
["VK_BACK_SPACE", "div.l1 D"],
|
||||
["VK_BACK_SPACE", "div.l1 "],
|
||||
["VK_UP", "div.l1 DIV"],
|
||||
["VK_BACK_SPACE", "div.l1 DI"],
|
||||
["VK_BACK_SPACE", "div.l1 D"],
|
||||
["VK_BACK_SPACE", "div.l1 "],
|
||||
["VK_UP", "div.l1 DIV"],
|
||||
["VK_UP", "div.l1 DIV"],
|
||||
["VK_TAB", "div.l1 DIV"],
|
||||
["VK_BACK_SPACE", "div.l1 DI"],
|
||||
["VK_BACK_SPACE", "div.l1 D"],
|
||||
["VK_BACK_SPACE", "div.l1 "],
|
||||
["VK_DOWN", "div.l1 DIV"],
|
||||
["VK_DOWN", "div.l1 SPAN"],
|
||||
["VK_DOWN", "div.l1 SPAN"],
|
||||
["VK_BACK_SPACE", "div.l1 SPA"],
|
||||
["VK_BACK_SPACE", "div.l1 SP"],
|
||||
["VK_BACK_SPACE", "div.l1 S"],
|
||||
["VK_BACK_SPACE", "div.l1 "],
|
||||
["VK_BACK_SPACE", "div.l1"],
|
||||
["VK_BACK_SPACE", "div.l"],
|
||||
["VK_BACK_SPACE", "div."],
|
||||
["VK_BACK_SPACE", "div"],
|
||||
["VK_BACK_SPACE", "di"],
|
||||
["VK_BACK_SPACE", "d"],
|
||||
["VK_BACK_SPACE", ""],
|
||||
];
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "http://mochi.test:8888/browser/browser/devtools/inspector/test/browser_inspector_bug_831693_search_suggestions.html";
|
||||
|
||||
function $(id) {
|
||||
if (id == null) return null;
|
||||
return content.document.getElementById(id);
|
||||
}
|
||||
|
||||
function setupTest()
|
||||
{
|
||||
openInspector(startTest);
|
||||
}
|
||||
|
||||
function startTest(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
searchBox =
|
||||
inspector.panelWin.document.getElementById("inspector-searchbox");
|
||||
panel = inspector.searchSuggestions.searchPopup._list;
|
||||
|
||||
focusSearchBoxUsingShortcut(inspector.panelWin, function() {
|
||||
searchBox.addEventListener("keypress", checkState, true);
|
||||
panel.addEventListener("keypress", checkState, true);
|
||||
checkStateAndMoveOn(0);
|
||||
});
|
||||
}
|
||||
|
||||
function checkStateAndMoveOn(index) {
|
||||
if (index == keyStates.length) {
|
||||
finishUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let [key, query] = keyStates[index];
|
||||
state = index;
|
||||
|
||||
info("pressing key " + key + " to get searchbox value as " + query);
|
||||
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
||||
}
|
||||
|
||||
function checkState(event) {
|
||||
if (panelOpeningStates.indexOf(state) != -1 &&
|
||||
!inspector.searchSuggestions.searchPopup.isOpen) {
|
||||
info("Panel is not open, should wait before it shows up.");
|
||||
panel.parentNode.addEventListener("popupshown", function retry() {
|
||||
panel.parentNode.removeEventListener("popupshown", retry, false);
|
||||
info("Panel is visible now");
|
||||
executeSoon(checkState);
|
||||
}, false);
|
||||
return;
|
||||
}
|
||||
else if (panelClosingStates.indexOf(state) != -1 &&
|
||||
panel.parentNode.state != "closed") {
|
||||
info("Panel is open, should wait for it to close.");
|
||||
panel.parentNode.addEventListener("popuphidden", function retry() {
|
||||
panel.parentNode.removeEventListener("popuphidden", retry, false);
|
||||
info("Panel is hidden now");
|
||||
executeSoon(checkState);
|
||||
}, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Using setTimout as the "command" event fires at delay after keypress
|
||||
window.setTimeout(function() {
|
||||
let [key, query] = keyStates[state];
|
||||
|
||||
if (searchBox.value == query) {
|
||||
ok(true, "The suggestion at " + state + "th step on " +
|
||||
"pressing " + key + " key is correct.");
|
||||
}
|
||||
else {
|
||||
info("value is not correct, waiting longer for state " + state +
|
||||
" with panel " + panel.parentNode.state);
|
||||
checkState();
|
||||
return;
|
||||
}
|
||||
checkStateAndMoveOn(state + 1);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
searchBox = null;
|
||||
panel = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
@ -7,19 +7,10 @@ const Cu = Components.utils;
|
||||
|
||||
// The XUL and XHTML namespace.
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const HUD_STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "stringBundle", function () {
|
||||
return Services.strings.createBundle(HUD_STRINGS_URI);
|
||||
});
|
||||
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["AutocompletePopup"];
|
||||
|
||||
/**
|
||||
@ -28,21 +19,48 @@ this.EXPORTED_SYMBOLS = ["AutocompletePopup"];
|
||||
* @constructor
|
||||
* @param nsIDOMDocument aDocument
|
||||
* The document you want the popup attached to.
|
||||
* @param Object aOptions
|
||||
* An object consiting any of the following options:
|
||||
* - panelId {String} The id for the popup panel.
|
||||
* - listBoxId {String} The id for the richlistbox inside the panel.
|
||||
* - position {String} The position for the popup panel.
|
||||
* - theme {String} String related to the theme of the popup.
|
||||
* - autoSelect {Boolean} Boolean to allow the first entry of the popup
|
||||
* panel to be automatically selected when the popup shows.
|
||||
* - fixedWidth {Boolean} Boolean to control dynamic width of the popup.
|
||||
* - direction {String} The direction of the text in the panel. rtl or ltr
|
||||
* - onSelect {String} The select event handler for the richlistbox
|
||||
* - onClick {String} The click event handler for the richlistbox.
|
||||
* - onKeypress {String} The keypress event handler for the richlistitems.
|
||||
*/
|
||||
this.AutocompletePopup = function AutocompletePopup(aDocument)
|
||||
this.AutocompletePopup =
|
||||
function AutocompletePopup(aDocument, aOptions = {})
|
||||
{
|
||||
this._document = aDocument;
|
||||
|
||||
this.fixedWidth = aOptions.fixedWidth || false;
|
||||
this.autoSelect = aOptions.autoSelect || false;
|
||||
this.position = aOptions.position || "after_start";
|
||||
this.direction = aOptions.direction || "ltr";
|
||||
|
||||
this.onSelect = aOptions.onSelect;
|
||||
this.onClick = aOptions.onClick;
|
||||
this.onKeypress = aOptions.onKeypress;
|
||||
|
||||
let id = aOptions.panelId || "devtools_autoCompletePopup";
|
||||
let theme = aOptions.theme || "dark";
|
||||
// Reuse the existing popup elements.
|
||||
this._panel = this._document.getElementById("webConsole_autocompletePopup");
|
||||
this._panel = this._document.getElementById(id);
|
||||
if (!this._panel) {
|
||||
this._panel = this._document.createElementNS(XUL_NS, "panel");
|
||||
this._panel.setAttribute("id", "webConsole_autocompletePopup");
|
||||
this._panel.setAttribute("label",
|
||||
stringBundle.GetStringFromName("Autocomplete.label"));
|
||||
this._panel.setAttribute("id", id);
|
||||
this._panel.className = "devtools-autocomplete-popup " + theme + "-theme";
|
||||
|
||||
this._panel.setAttribute("noautofocus", "true");
|
||||
this._panel.setAttribute("ignorekeys", "true");
|
||||
this._panel.setAttribute("level", "top");
|
||||
if (!aOptions.onKeypress) {
|
||||
this._panel.setAttribute("ignorekeys", "true");
|
||||
}
|
||||
|
||||
let mainPopupSet = this._document.getElementById("mainPopupSet");
|
||||
if (mainPopupSet) {
|
||||
@ -51,22 +69,40 @@ this.AutocompletePopup = function AutocompletePopup(aDocument)
|
||||
else {
|
||||
this._document.documentElement.appendChild(this._panel);
|
||||
}
|
||||
|
||||
this._list = this._document.createElementNS(XUL_NS, "richlistbox");
|
||||
this._list.flex = 1;
|
||||
this._panel.appendChild(this._list);
|
||||
|
||||
// Open and hide the panel, so we initialize the API of the richlistbox.
|
||||
this._panel.width = 1;
|
||||
this._panel.height = 1;
|
||||
this._panel.openPopup(null, "overlap", 0, 0, false, false);
|
||||
this._panel.hidePopup();
|
||||
this._panel.width = "";
|
||||
this._panel.height = "";
|
||||
this._list = null;
|
||||
}
|
||||
else {
|
||||
this._list = this._panel.firstChild;
|
||||
}
|
||||
|
||||
if (!this._list) {
|
||||
this._list = this._document.createElementNS(XUL_NS, "richlistbox");
|
||||
this._panel.appendChild(this._list);
|
||||
|
||||
// Open and hide the panel, so we initialize the API of the richlistbox.
|
||||
this._panel.openPopup(null, this.popup, 0, 0);
|
||||
this._panel.hidePopup();
|
||||
}
|
||||
|
||||
this._list.flex = 1;
|
||||
this._list.setAttribute("seltype", "single");
|
||||
|
||||
if (aOptions.listBoxId) {
|
||||
this._list.setAttribute("id", aOptions.listBoxId);
|
||||
}
|
||||
this._list.className = "devtools-autocomplete-listbox " + theme + "-theme";
|
||||
|
||||
if (this.onSelect) {
|
||||
this._list.addEventListener("select", this.onSelect, false);
|
||||
}
|
||||
|
||||
if (this.onClick) {
|
||||
this._list.addEventListener("click", this.onClick, false);
|
||||
}
|
||||
|
||||
if (this.onKeypress) {
|
||||
this._list.addEventListener("keypress", this.onKeypress, false);
|
||||
}
|
||||
}
|
||||
|
||||
AutocompletePopup.prototype = {
|
||||
@ -74,6 +110,11 @@ AutocompletePopup.prototype = {
|
||||
_panel: null,
|
||||
_list: null,
|
||||
|
||||
// Event handlers.
|
||||
onSelect: null,
|
||||
onClick: null,
|
||||
onKeypress: null,
|
||||
|
||||
/**
|
||||
* Open the autocomplete popup panel.
|
||||
*
|
||||
@ -82,17 +123,14 @@ AutocompletePopup.prototype = {
|
||||
*/
|
||||
openPopup: function AP_openPopup(aAnchor)
|
||||
{
|
||||
this._panel.openPopup(aAnchor, "after_start", 0, 0, false, false);
|
||||
this._panel.openPopup(aAnchor, this.position, 0, 0);
|
||||
|
||||
if (this.onSelect) {
|
||||
this._list.addEventListener("select", this.onSelect, false);
|
||||
if (this.autoSelect) {
|
||||
this.selectFirstItem();
|
||||
}
|
||||
|
||||
if (this.onClick) {
|
||||
this._list.addEventListener("click", this.onClick, false);
|
||||
if (!this.fixedWidth) {
|
||||
this._updateSize();
|
||||
}
|
||||
|
||||
this._updateSize();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -101,14 +139,6 @@ AutocompletePopup.prototype = {
|
||||
hidePopup: function AP_hidePopup()
|
||||
{
|
||||
this._panel.hidePopup();
|
||||
|
||||
if (this.onSelect) {
|
||||
this._list.removeEventListener("select", this.onSelect, false);
|
||||
}
|
||||
|
||||
if (this.onClick) {
|
||||
this._list.removeEventListener("click", this.onClick, false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -131,11 +161,35 @@ AutocompletePopup.prototype = {
|
||||
}
|
||||
this.clearItems();
|
||||
|
||||
if (this.onSelect) {
|
||||
this._list.removeEventListener("select", this.onSelect, false);
|
||||
}
|
||||
|
||||
if (this.onClick) {
|
||||
this._list.removeEventListener("click", this.onClick, false);
|
||||
}
|
||||
|
||||
if (this.onKeypress) {
|
||||
this._list.removeEventListener("keypress", this.onKeypress, false);
|
||||
}
|
||||
|
||||
this._document = null;
|
||||
this._list = null;
|
||||
this._panel = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the autocomplete items array.
|
||||
*
|
||||
* @param Number aIndex The index of the item what is wanted.
|
||||
*
|
||||
* @return The autocomplete item at index aIndex.
|
||||
*/
|
||||
getItemAtIndex: function AP_getItemAtIndex(aIndex)
|
||||
{
|
||||
return this._list.getItemAtIndex(aIndex)._autocompleteItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the autocomplete items array.
|
||||
*
|
||||
@ -166,9 +220,27 @@ AutocompletePopup.prototype = {
|
||||
|
||||
// Make sure that the new content is properly fitted by the XUL richlistbox.
|
||||
if (this.isOpen) {
|
||||
// We need the timeout to allow the content to reflow. Attempting to
|
||||
// update the richlistbox size too early does not work.
|
||||
this._document.defaultView.setTimeout(this._updateSize.bind(this), 1);
|
||||
if (this.autoSelect) {
|
||||
this.selectFirstItem();
|
||||
}
|
||||
if (!this.fixedWidth) {
|
||||
this._updateSize();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Selects the first item of the richlistbox. Note that first item here is the
|
||||
* item closes to the input element, which means that 0th index if position is
|
||||
* below, and last index if position is above.
|
||||
*/
|
||||
selectFirstItem: function AP_selectFirstItem()
|
||||
{
|
||||
if (this.position.contains("before")) {
|
||||
this.selectedIndex = this.itemCount - 1;
|
||||
}
|
||||
else {
|
||||
this.selectedIndex = 0;
|
||||
}
|
||||
},
|
||||
|
||||
@ -179,11 +251,23 @@ AutocompletePopup.prototype = {
|
||||
*/
|
||||
_updateSize: function AP__updateSize()
|
||||
{
|
||||
if (!this._panel) {
|
||||
return;
|
||||
}
|
||||
this._list.width = this._panel.clientWidth +
|
||||
this._scrollbarWidth;
|
||||
// We need the timeout to allow the content to reflow. Attempting to
|
||||
// update the richlistbox size too early does not work.
|
||||
this._document.defaultView.setTimeout(function() {
|
||||
if (!this._panel) {
|
||||
return;
|
||||
}
|
||||
this._list.width = this._panel.clientWidth +
|
||||
this._scrollbarWidth;
|
||||
// Height change is required, otherwise the panel is drawn at an offset
|
||||
// the first time.
|
||||
this._list.height = this._panel.clientHeight;
|
||||
// This brings the panel back at right position.
|
||||
this._list.top = 0;
|
||||
// Changing panel height might make the selected item out of view, so
|
||||
// bring it back to view.
|
||||
this._list.ensureIndexIsVisible(this._list.selectedIndex);
|
||||
}.bind(this), 5);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -198,14 +282,16 @@ AutocompletePopup.prototype = {
|
||||
this._list.removeChild(this._list.firstChild);
|
||||
}
|
||||
|
||||
// Reset the panel and list dimensions. New dimensions are calculated when a
|
||||
// new set of items is added to the autocomplete popup.
|
||||
this._list.width = "";
|
||||
this._list.height = "";
|
||||
this._panel.width = "";
|
||||
this._panel.height = "";
|
||||
this._panel.top = "";
|
||||
this._panel.left = "";
|
||||
if (!this.fixedWidth) {
|
||||
// Reset the panel and list dimensions. New dimensions are calculated when
|
||||
// a new set of items is added to the autocomplete popup.
|
||||
this._list.width = "";
|
||||
this._list.height = "";
|
||||
this._panel.width = "";
|
||||
this._panel.height = "";
|
||||
this._panel.top = "";
|
||||
this._panel.left = "";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -225,7 +311,7 @@ AutocompletePopup.prototype = {
|
||||
*/
|
||||
set selectedIndex(aIndex) {
|
||||
this._list.selectedIndex = aIndex;
|
||||
if (this._list.ensureIndexIsVisible) {
|
||||
if (this.isOpen && this._list.ensureIndexIsVisible) {
|
||||
this._list.ensureIndexIsVisible(this._list.selectedIndex);
|
||||
}
|
||||
},
|
||||
@ -247,23 +333,51 @@ AutocompletePopup.prototype = {
|
||||
*/
|
||||
set selectedItem(aItem) {
|
||||
this._list.selectedItem = this._findListItem(aItem);
|
||||
this._list.ensureIndexIsVisible(this._list.selectedIndex);
|
||||
if (this.isOpen) {
|
||||
this._list.ensureIndexIsVisible(this._list.selectedIndex);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Append an item into the autocomplete list.
|
||||
*
|
||||
* @param object aItem
|
||||
* The item you want appended to the list. The object must have a
|
||||
* "label" property which is used as the displayed value.
|
||||
* The item you want appended to the list.
|
||||
* The item object can have the following properties:
|
||||
* - label {String} Property which is used as the displayed value.
|
||||
* - preLabel {String} [Optional] The String that will be displayed
|
||||
* before the label indicating that this is the already
|
||||
* present text in the input box, and label is the text
|
||||
* that will be auto completed. When this property is
|
||||
* present, |preLabel.length| starting characters will be
|
||||
* removed from label.
|
||||
* - count {Number} [Optional] The number to represent the count of
|
||||
* autocompleted label.
|
||||
*/
|
||||
appendItem: function AP_appendItem(aItem)
|
||||
{
|
||||
let description = this._document.createElementNS(XUL_NS, "description");
|
||||
description.textContent = aItem.label;
|
||||
|
||||
let listItem = this._document.createElementNS(XUL_NS, "richlistitem");
|
||||
listItem.appendChild(description);
|
||||
if (this.direction) {
|
||||
listItem.setAttribute("dir", this.direction);
|
||||
}
|
||||
let label = this._document.createElementNS(XUL_NS, "label");
|
||||
label.setAttribute("value", aItem.label);
|
||||
label.setAttribute("class", "autocomplete-value");
|
||||
if (aItem.preLabel) {
|
||||
let preDesc = this._document.createElementNS(XUL_NS, "label");
|
||||
preDesc.setAttribute("value", aItem.preLabel);
|
||||
preDesc.setAttribute("class", "initial-value");
|
||||
listItem.appendChild(preDesc);
|
||||
label.setAttribute("value", aItem.label.slice(aItem.preLabel.length));
|
||||
}
|
||||
listItem.appendChild(label);
|
||||
if (aItem.count && aItem.count > 1) {
|
||||
let countDesc = this._document.createElementNS(XUL_NS, "label");
|
||||
countDesc.setAttribute("value", aItem.count);
|
||||
countDesc.setAttribute("flex", "1");
|
||||
countDesc.setAttribute("class", "autocomplete-count");
|
||||
listItem.appendChild(countDesc);
|
||||
}
|
||||
listItem._autocompleteItem = aItem;
|
||||
|
||||
this._list.appendChild(listItem);
|
||||
@ -351,6 +465,14 @@ AutocompletePopup.prototype = {
|
||||
return this.selectedItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses the richlistbox.
|
||||
*/
|
||||
focus: function AP_focus()
|
||||
{
|
||||
this._list.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the scrollbar width in the current document.
|
||||
*
|
@ -13,7 +13,6 @@ EXTRA_JS_MODULES = \
|
||||
HUDService.jsm \
|
||||
PropertyPanel.jsm \
|
||||
NetworkPanel.jsm \
|
||||
AutocompletePopup.jsm \
|
||||
WebConsolePanel.jsm \
|
||||
$(NULL)
|
||||
|
||||
|
@ -41,7 +41,7 @@ function consoleOpened(aHud) {
|
||||
// toSource unwatch valueOf watch constructor.
|
||||
is(popup.itemCount, 18, "popup.itemCount is correct");
|
||||
|
||||
let sameItems = popup.getItems().map(function(e) {return e.label;});
|
||||
let sameItems = popup.getItems().reverse().map(function(e) {return e.label;});
|
||||
ok(sameItems.every(function(prop, index) {
|
||||
return [
|
||||
"__defineGetter__",
|
||||
@ -64,29 +64,31 @@ function consoleOpened(aHud) {
|
||||
"watch",
|
||||
][index] === prop}), "getItems returns the items we expect");
|
||||
|
||||
is(popup.selectedIndex, -1, "no index is selected");
|
||||
is(popup.selectedIndex, 17,
|
||||
"Index of the first item from bottom is selected.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
|
||||
is(completeNode.value, prefix + "__defineGetter__",
|
||||
"completeNode.value holds __defineGetter__");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
is(popup.selectedIndex, 1, "index 1 is selected");
|
||||
is(popup.selectedItem.label, "__defineSetter__", "__defineSetter__ is selected");
|
||||
is(completeNode.value, prefix + "__defineSetter__",
|
||||
"completeNode.value holds __defineSetter__");
|
||||
is(popup.selectedItem.label, "valueOf", "valueOf is selected");
|
||||
is(completeNode.value, prefix + "valueOf",
|
||||
"completeNode.value holds valueOf");
|
||||
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
|
||||
is(completeNode.value, prefix + "__defineGetter__",
|
||||
"completeNode.value holds __defineGetter__");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
popup._panel.addEventListener("popuphidden", autocompletePopupHidden, false);
|
||||
|
||||
@ -108,7 +110,7 @@ function autocompletePopupHidden()
|
||||
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
|
||||
is(inputNode.value, "window.foobarBug585991.__defineGetter__",
|
||||
is(inputNode.value, "window.foobarBug585991.watch",
|
||||
"completion was successful after VK_TAB");
|
||||
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
@ -120,15 +122,16 @@ function autocompletePopupHidden()
|
||||
|
||||
is(popup.itemCount, 18, "popup.itemCount is correct");
|
||||
|
||||
is(popup.selectedIndex, -1, "no index is selected");
|
||||
is(popup.selectedIndex, 17, "First index from bottom is selected");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
|
||||
is(completeNode.value, prefix + "__defineGetter__",
|
||||
"completeNode.value holds __defineGetter__");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
popup._panel.addEventListener("popuphidden", function onHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", onHidden, false);
|
||||
@ -168,29 +171,30 @@ function testReturnKey()
|
||||
|
||||
is(popup.itemCount, 18, "popup.itemCount is correct");
|
||||
|
||||
is(popup.selectedIndex, -1, "no index is selected");
|
||||
is(popup.selectedIndex, 17, "First index from bottom is selected");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
|
||||
is(completeNode.value, prefix + "__defineGetter__",
|
||||
"completeNode.value holds __defineGetter__");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
is(popup.selectedIndex, 1, "index 1 is selected");
|
||||
is(popup.selectedItem.label, "__defineSetter__", "__defineSetter__ is selected");
|
||||
is(completeNode.value, prefix + "__defineSetter__",
|
||||
"completeNode.value holds __defineSetter__");
|
||||
is(popup.selectedItem.label, "valueOf", "valueOf is selected");
|
||||
is(completeNode.value, prefix + "valueOf",
|
||||
"completeNode.value holds valueOf");
|
||||
|
||||
popup._panel.addEventListener("popuphidden", function onHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", onHidden, false);
|
||||
|
||||
ok(!popup.isOpen, "popup is not open after VK_RETURN");
|
||||
|
||||
is(inputNode.value, "window.foobarBug585991.__defineSetter__",
|
||||
is(inputNode.value, "window.foobarBug585991.valueOf",
|
||||
"completion was successful after VK_RETURN");
|
||||
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
|
@ -40,8 +40,9 @@ function consoleOpened(HUD) {
|
||||
return aItem === items[aIndex];
|
||||
}), true, "getItems returns back the same items");
|
||||
|
||||
is(popup.selectedIndex, -1, "no index is selected");
|
||||
ok(!popup.selectedItem, "no item is selected");
|
||||
is(popup.selectedIndex, 2,
|
||||
"Index of the first item from bottom is selected.");
|
||||
is(popup.selectedItem, items[2], "First item from bottom is selected");
|
||||
|
||||
popup.selectedIndex = 1;
|
||||
|
||||
|
@ -66,21 +66,21 @@ function testCompletion(hud) {
|
||||
yield;
|
||||
|
||||
is(input.value, "document.getElem", "'document.getElem' completion");
|
||||
is(jsterm.completeNode.value, " entById", "'document.getElem' completion");
|
||||
is(jsterm.completeNode.value, "", "'document.getElem' completion");
|
||||
|
||||
// Test pressing tab another time.
|
||||
jsterm.complete(jsterm.COMPLETE_FORWARD, testNext);
|
||||
yield;
|
||||
|
||||
is(input.value, "document.getElem", "'document.getElem' completion");
|
||||
is(jsterm.completeNode.value, " entsByClassName", "'document.getElem' another tab completion");
|
||||
is(jsterm.completeNode.value, " entsByTagNameNS", "'document.getElem' another tab completion");
|
||||
|
||||
// Test pressing shift_tab.
|
||||
jsterm.complete(jsterm.COMPLETE_BACKWARD, testNext);
|
||||
yield;
|
||||
|
||||
is(input.value, "document.getElem", "'document.getElem' untab completion");
|
||||
is(jsterm.completeNode.value, " entById", "'document.getElem' completion");
|
||||
is(jsterm.completeNode.value, "", "'document.getElem' completion");
|
||||
|
||||
jsterm.clearOutput();
|
||||
|
||||
|
@ -29,7 +29,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetworkPanel",
|
||||
"resource:///modules/NetworkPanel.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AutocompletePopup",
|
||||
"resource:///modules/AutocompletePopup.jsm");
|
||||
"resource:///modules/devtools/AutocompletePopup.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
@ -2731,9 +2731,18 @@ JSTerm.prototype = {
|
||||
init: function JST_init()
|
||||
{
|
||||
let chromeDocument = this.hud.owner.chromeDocument;
|
||||
this.autocompletePopup = new AutocompletePopup(chromeDocument);
|
||||
this.autocompletePopup.onSelect = this.onAutocompleteSelect.bind(this);
|
||||
this.autocompletePopup.onClick = this.acceptProposedCompletion.bind(this);
|
||||
let autocompleteOptions = {
|
||||
onSelect: this.onAutocompleteSelect.bind(this),
|
||||
onClick: this.acceptProposedCompletion.bind(this),
|
||||
panelId: "webConsole_autocompletePopup",
|
||||
listBoxId: "webConsole_autocompletePopupListBox",
|
||||
position: "before_start",
|
||||
theme: "light",
|
||||
direction: "ltr",
|
||||
autoSelect: true
|
||||
};
|
||||
this.autocompletePopup = new AutocompletePopup(chromeDocument,
|
||||
autocompleteOptions);
|
||||
|
||||
let doc = this.hud.document;
|
||||
this.completeNode = doc.querySelector(".jsterm-complete-node");
|
||||
@ -3479,13 +3488,14 @@ JSTerm.prototype = {
|
||||
}
|
||||
|
||||
let matches = aMessage.matches;
|
||||
let lastPart = aMessage.matchProp;
|
||||
if (!matches.length) {
|
||||
this.clearCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
let items = matches.map(function(aMatch) {
|
||||
return { label: aMatch };
|
||||
let items = matches.reverse().map(function(aMatch) {
|
||||
return { preLabel: lastPart, label: aMatch };
|
||||
});
|
||||
|
||||
let popup = this.autocompletePopup;
|
||||
@ -3494,7 +3504,7 @@ JSTerm.prototype = {
|
||||
let completionType = this.lastCompletion.completionType;
|
||||
this.lastCompletion = {
|
||||
value: inputValue,
|
||||
matchProp: aMessage.matchProp,
|
||||
matchProp: lastPart,
|
||||
};
|
||||
|
||||
if (items.length > 1 && !popup.isOpen) {
|
||||
|
@ -110,10 +110,6 @@ scratchpad.linkText=Shift+RETURN - Open in Scratchpad
|
||||
# string
|
||||
gcliterm.instanceLabel=Instance of %S
|
||||
|
||||
# LOCALIZATION NOTE (Autocomplete.label):
|
||||
# The autocomplete popup panel label/title.
|
||||
Autocomplete.label=Autocomplete popup
|
||||
|
||||
# LOCALIZATION NOTE (stacktrace.anonymousFunction):
|
||||
# This string is used to display JavaScript functions that have no given name -
|
||||
# they are said to be anonymous. See stacktrace.outputMessage.
|
||||
|
@ -3,6 +3,10 @@
|
||||
- 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 bindings [
|
||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||
%browserDTD;
|
||||
]>
|
||||
|
||||
<bindings
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
@ -21,7 +25,8 @@
|
||||
<xul:box anonid="selection" class="documenttab-crop"/>
|
||||
<xul:box anonid="selection" class="documenttab-selection"/>
|
||||
<xul:button anonid="close" class="documenttab-close" observes="bcast_urlbarState" end="0" top="0"
|
||||
onclick="event.stopPropagation(); document.getBindingParent(this)._onClose()"/>
|
||||
onclick="event.stopPropagation(); document.getBindingParent(this)._onClose()"
|
||||
label="&closetab.label;"/>
|
||||
</xul:stack>
|
||||
</content>
|
||||
|
||||
|
@ -404,10 +404,10 @@
|
||||
<button label="&sync.connect;" oncommand="WeaveGlue.tryConnect();" />
|
||||
</setting>
|
||||
<setting id="sync-connected" class="setting-group" title="&sync.connected;" type="control" collapsed="true">
|
||||
<button id="sync-pairdevice" label="&sync.pair.title;" oncommand="SyncPairDevice.open();" />
|
||||
<button id="sync-pairdevice" label="&sync.pair.button;" oncommand="SyncPairDevice.open();" />
|
||||
</setting>
|
||||
<setting id="sync-sync" class="setting-subgroup" type="control" collapsed="true">
|
||||
<button id="sync-syncButton" label="&sync.syncNow;" oncommand="WeaveGlue.sync();"/>
|
||||
<button id="sync-syncButton" label="&sync.syncNow2;" oncommand="WeaveGlue.sync();"/>
|
||||
</setting>
|
||||
<setting id="sync-device" class="setting-subgroup" type="string" title="&sync.deviceName;" onchange="WeaveGlue.changeName(this);" collapsed="true"/>
|
||||
<setting id="sync-disconnect" class="setting-subgroup" type="control" collapsed="true">
|
||||
@ -457,7 +457,7 @@
|
||||
</hbox>
|
||||
<vbox id="syncsetup-simple" class="syncsetup-page" flex="1">
|
||||
<scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
|
||||
<description class="syncsetup-desc" flex="1">&sync.setup.pair;</description>
|
||||
<description class="syncsetup-desc" flex="1">&sync.setup.pair2;</description>
|
||||
<description class="link" flex="1" onclick="WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
|
||||
<separator/>
|
||||
<vbox flex="1" pack="center" align="start">
|
||||
@ -514,7 +514,7 @@
|
||||
</hbox>
|
||||
<vbox id="syncpair-simple" class="syncsetup-page" flex="1">
|
||||
<scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
|
||||
<description class="syncsetup-desc" flex="1">&sync.pair.description;</description>
|
||||
<description class="syncsetup-desc" flex="1">&sync.pair.description2;</description>
|
||||
<description class="link" flex="1" onclick="SyncPairDevice.close(); WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
|
||||
<separator/>
|
||||
<vbox align="center" flex="1">
|
||||
@ -621,10 +621,10 @@
|
||||
|
||||
<!-- App bar: 'more' button context menu -->
|
||||
<richlistitem id="context-findinpage" type="find-in-page" onclick="ContextCommands.findInPage();">
|
||||
<label value="&appbarFindInPage.label;"/>
|
||||
<label value="&appbarFindInPage2.label;"/>
|
||||
</richlistitem>
|
||||
<richlistitem id="context-viewondesktop" type="view-on-desktop" onclick="ContextCommands.viewOnDesktop();">
|
||||
<label value="&appbarViewOnDesktop.label;"/>
|
||||
<label value="&appbarViewOnDesktop2.label;"/>
|
||||
</richlistitem>
|
||||
</richlistbox>
|
||||
</vbox>
|
||||
|
@ -515,7 +515,7 @@ let WeaveGlue = {
|
||||
message = message.replace("#1", brandName);
|
||||
message = message.replace("#2", Services.appinfo.version);
|
||||
let title = this._bundle.GetStringFromName("sync.update.title")
|
||||
let button = this._bundle.GetStringFromName("sync.update.button")
|
||||
let button = this._bundle.GetStringFromName("sync.update.learnmore")
|
||||
let close = this._bundle.GetStringFromName("sync.update.close")
|
||||
|
||||
let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
|
||||
|
@ -54,7 +54,7 @@ LoginManagerPrompter.prototype = {
|
||||
var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Ci.nsIStringBundleService);
|
||||
this.__strBundle = bunService.createBundle(
|
||||
"chrome://passwordmgr/locale/passwordmgr.properties");
|
||||
"chrome://browser/locale/passwordmgr.properties");
|
||||
if (!this.__strBundle)
|
||||
throw "String bundle for Login Manager not present!";
|
||||
}
|
||||
@ -337,7 +337,7 @@ LoginManagerPrompter.prototype = {
|
||||
var changeButtonAccessKey =
|
||||
this._getLocalizedString("notifyBarChangeButtonAccessKey");
|
||||
var dontChangeButtonText =
|
||||
this._getLocalizedString("notifyBarDontChangeButtonText");
|
||||
this._getLocalizedString("notifyBarDontChangeButtonText2");
|
||||
var dontChangeButtonAccessKey =
|
||||
this._getLocalizedString("notifyBarDontChangeButtonAccessKey");
|
||||
|
||||
|
@ -907,7 +907,7 @@ let PromptUtils = {
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () {
|
||||
return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
|
||||
return Services.strings.createBundle("chrome://browser/locale/passwordmgr.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () {
|
||||
|
@ -7,13 +7,11 @@
|
||||
|
||||
<!ENTITY back.label "Back">
|
||||
<!ENTITY forward.label "Forward">
|
||||
<!ENTITY showTabs.label "Show Tabs">
|
||||
<!ENTITY newtab.label "New Tab">
|
||||
<!ENTITY closetab.label "Close Tab">
|
||||
<!ENTITY undoclosetab.label "Undo Close Tab">
|
||||
|
||||
<!ENTITY appbarFindInPage.label "Find in Page">
|
||||
<!ENTITY appbarViewOnDesktop.label "View on Desktop">
|
||||
<!ENTITY appbarFindInPage2.label "Find in page">
|
||||
<!ENTITY appbarViewOnDesktop2.label "View on desktop">
|
||||
|
||||
<!ENTITY startTopSitesHeader.label "Top Sites">
|
||||
<!ENTITY startBookmarksHeader.label "Bookmarks">
|
||||
|
@ -38,16 +38,16 @@ alertDownloadsNoSpace=Not enough storage space
|
||||
# Popup Blocker
|
||||
popupWarning=%S prevented this site from opening a pop-up window.
|
||||
popupWarningMultiple=%S prevented this site from opening %S pop-up windows.
|
||||
popupButtonAllowOnce2=Allow Once
|
||||
popupButtonAlwaysAllow3=Always Allow
|
||||
popupButtonNeverWarn3=Never Allow
|
||||
popupButtonAllowOnce2=Allow once
|
||||
popupButtonAlwaysAllow3=Always allow
|
||||
popupButtonNeverWarn3=Never allow
|
||||
|
||||
# ContentPermissionsPrompt
|
||||
contentPermissions.alwaysForSite=Always for this Site
|
||||
contentPermissions.neverForSite=Never for this Site
|
||||
contentPermissions.alwaysForSite=Always for this site
|
||||
contentPermissions.neverForSite=Never for this site
|
||||
|
||||
# Geolocation UI
|
||||
geolocation2.allow=Share Location
|
||||
geolocation2.allow=Share location
|
||||
geolocation2.wantsTo=Share your location with %S?
|
||||
|
||||
geolocation.learnMore=Learn more…
|
||||
@ -64,9 +64,6 @@ offlineApps.wantsTo=%S wants to store data on your device for offline use.
|
||||
indexedDBQuota.allow=Allow
|
||||
indexedDBQuota.wantsTo=%S wants to store a lot of data on your device for offline use.
|
||||
|
||||
# Bookmark List
|
||||
bookmarkList.desktop=Desktop Bookmarks
|
||||
|
||||
# Closing Tabs
|
||||
tabs.closeWarningTitle=Confirm close
|
||||
|
||||
@ -82,8 +79,6 @@ tabs.emptyTabTitle=New Tab
|
||||
# Open Search
|
||||
opensearch.search=Search: %S
|
||||
|
||||
# Open in Another App
|
||||
# LOCALIZATION NOTE: openinapp.specific is the text displayed if there is a single external app
|
||||
# %S is the name of the app, like "YouTube" or "Picassa"
|
||||
openinapp.specific=Open in %S App
|
||||
openinapp.general=Open in Another App
|
||||
# Clear Private Data
|
||||
clearPrivateData.title=Clear Private Data
|
||||
clearPrivateData.message=Delete your browsing history and settings, including passwords and cookies?
|
||||
|
@ -2,7 +2,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/.
|
||||
|
||||
rememberValue = Use Password Manager to remember this value.
|
||||
rememberPassword = Use Password Manager to remember this password.
|
||||
savePasswordTitle = Confirm
|
||||
# 1st string is product name, 2nd is the username for the login, 3rd is the
|
||||
@ -10,29 +9,15 @@ savePasswordTitle = Confirm
|
||||
saveLoginText = Do you want %1$S to remember the password for "%2$S" on %3$S?
|
||||
# 1st string is product name, 2nd is the login's hostname
|
||||
saveLoginTextNoUsername = Do you want %1$S to remember this password on %2$S?
|
||||
promptNotNowButtonText = Not Now
|
||||
promptNeverForSiteButtonText = Never
|
||||
notifyBarNotForThisSiteButtonText = Not for this Site
|
||||
notifyBarNotForThisSiteButtonText = Not for this site
|
||||
notifyBarNotForThisSiteButtonAccessKey =
|
||||
promptRememberButtonText = Remember
|
||||
notifyBarRememberPasswordButtonText = Remember Password
|
||||
notifyBarRememberPasswordButtonText = Remember password
|
||||
notifyBarRememberPasswordButtonAccessKey =
|
||||
passwordChangeTitle = Confirm Password Change
|
||||
passwordChangeText = Would you like to change the stored password for %S?
|
||||
passwordChangeTextNoUser = Would you like to change the stored password for this login?
|
||||
notifyBarChangeButtonText = Change
|
||||
notifyBarChangeButtonAccessKey =
|
||||
notifyBarDontChangeButtonText = Don't Change
|
||||
notifyBarDontChangeButtonText2 = Don't change
|
||||
notifyBarDontChangeButtonAccessKey =
|
||||
userSelectText = Please confirm which user you are changing the password for
|
||||
hidePasswords=Hide Passwords
|
||||
hidePasswordsAccessKey=P
|
||||
showPasswords=Show Passwords
|
||||
showPasswordsAccessKey=P
|
||||
noMasterPasswordPrompt=Are you sure you wish to show your passwords?
|
||||
removeAllPasswordsPrompt=Are you sure you wish to remove all passwords?
|
||||
removeAllPasswordsTitle=Remove all passwords
|
||||
loginsSpielAll=Passwords for the following sites are stored on your computer:
|
||||
loginsSpielFiltered=The following passwords match your search:
|
||||
username=Username
|
||||
password=Password
|
@ -9,10 +9,10 @@
|
||||
<!ENTITY sync.connected "Connected">
|
||||
<!ENTITY sync.deviceName "This device">
|
||||
<!ENTITY sync.disconnect "Disconnect">
|
||||
<!ENTITY sync.syncNow "Sync Now">
|
||||
<!ENTITY sync.syncNow2 "Sync now">
|
||||
|
||||
<!ENTITY sync.setup.title "Connect to Sync">
|
||||
<!ENTITY sync.setup.pair "To activate, select "Pair a Device" on your other device.">
|
||||
<!ENTITY sync.setup.pair2 "To activate, select "Pair a device" on your other device.">
|
||||
<!ENTITY sync.fallback "I'm not near my computer…">
|
||||
<!ENTITY sync.setup.manual "Enter your Sync account information">
|
||||
<!ENTITY sync.account "Account Name">
|
||||
@ -26,6 +26,7 @@
|
||||
<!ENTITY sync.setup.waiting2 "Waiting for other device…">
|
||||
|
||||
<!ENTITY sync.pair.title "Pair a Device">
|
||||
<!ENTITY sync.pair.description "To activate your new device, select "Set Up Sync" on the device.">
|
||||
<!ENTITY sync.pair.button "Pair a device">
|
||||
<!ENTITY sync.pair.description2 "To activate your new device, select "Set up sync" on the device.">
|
||||
<!ENTITY sync.setup.close "Close">
|
||||
<!ENTITY sync.setup.waitingdownload "Your data is now being downloaded in the background. You can close this window at any time.">
|
||||
|
@ -22,7 +22,7 @@ notificationDisconnect.button=Undo
|
||||
sync.update.client=#1 #2 is not compatible with the latest version of Firefox Sync. Please update to the latest version.
|
||||
sync.update.remote=#1 #2 is not compatible with older versions of Firefox Sync. Please update Firefox on your other computer(s).
|
||||
sync.update.title=Firefox Sync
|
||||
sync.update.button=Learn More
|
||||
sync.update.learnmore=Learn more
|
||||
sync.update.close=Close
|
||||
sync.setup.error.title=Cannot Setup Sync
|
||||
sync.setup.error.network=No internet connection available
|
||||
|
@ -17,14 +17,13 @@
|
||||
locale/browser/checkbox.dtd (%chrome/checkbox.dtd)
|
||||
locale/browser/sync.dtd (%chrome/sync.dtd)
|
||||
locale/browser/sync.properties (%chrome/sync.properties)
|
||||
locale/browser/passwordmgr.properties (%chrome/passwordmgr.properties)
|
||||
locale/browser/prompt.dtd (%chrome/prompt.dtd)
|
||||
locale/browser/phishing.dtd (%chrome/phishing.dtd)
|
||||
|
||||
@AB_CD@.jar:
|
||||
% locale browser @AB_CD@ %locale/browser/
|
||||
locale/browser/bookmarks.json (bookmarks.json)
|
||||
locale/browser/passwordmgr.properties (%overrides/passwordmgr.properties)
|
||||
% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/passwordmgr.properties
|
||||
|
||||
#
|
||||
# Browser jar resources
|
||||
|
@ -33,6 +33,7 @@ this.RecentWindow = {
|
||||
return (!win.closed &&
|
||||
win.toolbar.visible &&
|
||||
(!checkPrivacy ||
|
||||
PrivateBrowsingUtils.permanentPrivateBrowsing ||
|
||||
PrivateBrowsingUtils.isWindowPrivate(win) == aOptions.private));
|
||||
}
|
||||
|
||||
|
@ -219,6 +219,10 @@
|
||||
|
||||
/* In-tools sidebar */
|
||||
|
||||
.devtools-toolbox-side-iframe {
|
||||
min-width: 465px;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
@ -360,3 +364,5 @@
|
||||
.devtools-theme-attrvalue {
|
||||
color: hsl(24,85%,39%); /* orange */
|
||||
}
|
||||
|
||||
%include ../../shared/devtools/common.inc.css
|
||||
|
@ -150,8 +150,10 @@
|
||||
|
||||
.devtools-tab {
|
||||
-moz-appearance: none;
|
||||
min-width: 88px;
|
||||
width: 47px;
|
||||
min-width: 47px;
|
||||
min-height: 32px;
|
||||
max-width: 137px;
|
||||
color: #b6babf;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -163,25 +165,23 @@
|
||||
border-right: 1px solid hsla(206,37%,4%,.45);
|
||||
}
|
||||
|
||||
.devtools-tab > .radio-label-center-box > .radio-label-box {
|
||||
-moz-appearance: none;
|
||||
.devtools-tab > image {
|
||||
border: none;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.devtools-tab > .radio-label-center-box >.radio-label-box > .radio-icon {
|
||||
-moz-margin-end: 6px;
|
||||
-moz-margin-start: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.devtools-tab:hover > .radio-label-center-box > .radio-label-box >
|
||||
.radio-icon {
|
||||
.devtools-tab > label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.devtools-tab:hover > image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.devtools-tab:active > .radio-label-center-box > .radio-label-box > .radio-icon,
|
||||
.devtools-tab[selected=true] > .radio-label-center-box > .radio-label-box >
|
||||
.radio-icon {
|
||||
.devtools-tab:active > image,
|
||||
.devtools-tab[selected=true] > label {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -233,6 +233,10 @@
|
||||
|
||||
/* In-tools sidebar */
|
||||
|
||||
.devtools-toolbox-side-iframe {
|
||||
min-width: 465px;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
@ -379,3 +383,5 @@
|
||||
.devtools-theme-attrvalue {
|
||||
color: hsl(24,85%,39%); /* orange */
|
||||
}
|
||||
|
||||
%include ../../shared/devtools/common.inc.css
|
||||
|
@ -139,8 +139,10 @@
|
||||
|
||||
.devtools-tab {
|
||||
-moz-appearance: none;
|
||||
min-width: 88px;
|
||||
width: 47px;
|
||||
min-width: 47px;
|
||||
min-height: 32px;
|
||||
max-width: 137px;
|
||||
color: #b6babf;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -152,21 +154,22 @@
|
||||
border-right: 1px solid hsla(206,37%,4%,.45);
|
||||
}
|
||||
|
||||
.devtools-tab > .radio-label-box {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.devtools-tab > .radio-label-box > .radio-icon {
|
||||
.devtools-tab > image {
|
||||
-moz-margin-end: 6px;
|
||||
-moz-margin-start: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.devtools-tab:hover > .radio-label-box > .radio-icon {
|
||||
.devtools-tab > label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.devtools-tab:hover > image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.devtools-tab:active > .radio-label-box > .radio-icon,
|
||||
.devtools-tab[selected=true] > .radio-label-box > .radio-icon {
|
||||
.devtools-tab:active > image,
|
||||
.devtools-tab[selected=true] > image {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
85
browser/themes/shared/devtools/common.inc.css
Normal file
85
browser/themes/shared/devtools/common.inc.css
Normal file
@ -0,0 +1,85 @@
|
||||
%if 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/. */
|
||||
%endif
|
||||
|
||||
/* Autocomplete Popup */
|
||||
/* Dark and light theme */
|
||||
|
||||
.devtools-autocomplete-popup {
|
||||
-moz-appearance: none !important;
|
||||
border: 1px solid hsl(210,11%,10%);
|
||||
box-shadow: 0 1px 0 hsla(209,29%,72%,.25) inset;
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(to bottom, hsla(209,18%,18%,0.9), hsl(210,11%,16%));
|
||||
border-radius: 3px;
|
||||
%ifdef XP_LINUX
|
||||
max-height: 32rem;
|
||||
%else
|
||||
max-height: 40rem;
|
||||
%endif
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox {
|
||||
-moz-appearance: none !important;
|
||||
background-color: transparent;
|
||||
border-width: 0px !important;
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox > richlistitem,
|
||||
.devtools-autocomplete-listbox > richlistitem[selected] {
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox.dark-theme > richlistitem[selected],
|
||||
.devtools-autocomplete-listbox.dark-theme > richlistitem:hover {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox.dark-theme > richlistitem[selected] > .autocomplete-value,
|
||||
.devtools-autocomplete-listbox:focus.dark-theme > richlistitem[selected] > .initial-value {
|
||||
color: hsl(208,100%,60%);
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox.dark-theme > richlistitem[selected] > label {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox.dark-theme > richlistitem > label {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox > richlistitem > .initial-value,
|
||||
.devtools-autocomplete-listbox > richlistitem > .autocomplete-value {
|
||||
margin: 0;
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox > richlistitem > .autocomplete-count {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Rest of the light theme */
|
||||
|
||||
.devtools-autocomplete-popup.light-theme {
|
||||
border: 1px solid hsl(210,24%,90%);
|
||||
box-shadow: 0 1px 0 hsla(209,29%,90%,.25) inset;
|
||||
background-image: linear-gradient(to bottom, hsla(209,18%,100%,0.9), hsl(210,24%,95%));
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox.light-theme > richlistitem[selected],
|
||||
.devtools-autocomplete-listbox.light-theme > richlistitem:hover {
|
||||
background-color: rgba(128,128,128,0.3);
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox.light-theme > richlistitem[selected] > .autocomplete-value,
|
||||
.devtools-autocomplete-listbox:focus.light-theme > richlistitem[selected] > .initial-value {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.devtools-autocomplete-listbox.light-theme > richlistitem > label {
|
||||
color: #666;
|
||||
}
|
@ -239,6 +239,10 @@
|
||||
|
||||
/* In-tools sidebar */
|
||||
|
||||
.devtools-toolbox-side-iframe {
|
||||
min-width: 465px;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
@ -388,3 +392,5 @@
|
||||
.devtools-theme-attrvalue {
|
||||
color: hsl(24,85%,39%); /* orange */
|
||||
}
|
||||
|
||||
%include ../../shared/devtools/common.inc.css
|
||||
|
@ -151,8 +151,10 @@
|
||||
|
||||
.devtools-tab {
|
||||
-moz-appearance: none;
|
||||
min-width: 88px;
|
||||
width: 47px;
|
||||
min-width: 47px;
|
||||
min-height: 32px;
|
||||
max-width: 137px;
|
||||
color: #b6babf;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -165,22 +167,18 @@
|
||||
border-right: 1px solid hsla(206,37%,4%,.45);
|
||||
}
|
||||
|
||||
.devtools-tab > .radio-label-box {
|
||||
border: none;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.devtools-tab > .radio-label-box > .radio-icon {
|
||||
.devtools-tab > image {
|
||||
-moz-margin-end: 6px;
|
||||
-moz-margin-start: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.devtools-tab:hover > .radio-label-box > .radio-icon {
|
||||
.devtools-tab:hover > image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.devtools-tab:active > .radio-label-box > .radio-icon,
|
||||
.devtools-tab[selected=true] > .radio-label-box > .radio-icon {
|
||||
.devtools-tab:active > image,
|
||||
.devtools-tab[selected=true] > image {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -22,4 +22,5 @@ public interface Assert {
|
||||
|
||||
// robocop-specific asserts
|
||||
void ispixel(int actual, int r, int g, int b, String name);
|
||||
void isnotpixel(int actual, int r, int g, int b, String name);
|
||||
}
|
||||
|
@ -148,6 +148,24 @@ public class FennecMochitestAssert implements Assert {
|
||||
}
|
||||
|
||||
public void ispixel(int actual, int r, int g, int b, String name) {
|
||||
int aAlpha = ((actual >> 24) & 0xFF);
|
||||
int aR = ((actual >> 16) & 0xFF);
|
||||
int aG = ((actual >> 8) & 0xFF);
|
||||
int aB = (actual & 0xFF);
|
||||
boolean pass = checkPixel(actual, r, g, b);
|
||||
ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
|
||||
}
|
||||
|
||||
public void isnotpixel(int actual, int r, int g, int b, String name) {
|
||||
int aAlpha = ((actual >> 24) & 0xFF);
|
||||
int aR = ((actual >> 16) & 0xFF);
|
||||
int aG = ((actual >> 8) & 0xFF);
|
||||
int aB = (actual & 0xFF);
|
||||
boolean pass = checkPixel(actual, r, g, b);
|
||||
ok(!pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (!pass ? " is" : " is not") + " different enough from rgb(" + r + "," + g + "," + b + ")");
|
||||
}
|
||||
|
||||
private boolean checkPixel(int actual, int r, int g, int b) {
|
||||
// When we read GL pixels the GPU has already processed them and they
|
||||
// are usually off by a little bit. For example a CSS-color pixel of color #64FFF5
|
||||
// was turned into #63FFF7 when it came out of glReadPixels. So in order to compare
|
||||
@ -160,10 +178,14 @@ public class FennecMochitestAssert implements Assert {
|
||||
int aG = ((actual >> 8) & 0xFF);
|
||||
int aB = (actual & 0xFF);
|
||||
boolean pass = (aAlpha == 0xFF) /* alpha */
|
||||
&& (Math.abs(aR - r) <= 8) /* red */
|
||||
&& (Math.abs(aG - g) <= 8) /* green */
|
||||
&& (Math.abs(aB - b) <= 8); /* blue */
|
||||
ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
|
||||
&& (Math.abs(aR - r) <= 8) /* red */
|
||||
&& (Math.abs(aG - g) <= 8) /* green */
|
||||
&& (Math.abs(aB - b) <= 8); /* blue */
|
||||
if (pass) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void todo(boolean condition, String name, String diag) {
|
||||
|
@ -53,6 +53,10 @@ public class FennecTalosAssert implements Assert {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void isnotpixel(int actual, int r, int g, int b, String name) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void todo(boolean condition, String name, String diag) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -120,13 +120,6 @@ template <class ErrorResult>
|
||||
class AudioEventTimeline
|
||||
{
|
||||
public:
|
||||
// This constructor should only be used for objects which are meant to be
|
||||
// copied from other properly constructed objects.
|
||||
AudioEventTimeline()
|
||||
: mValue(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
explicit AudioEventTimeline(float aDefaultValue)
|
||||
: mValue(aDefaultValue)
|
||||
{
|
||||
|
@ -21,6 +21,7 @@ class nsXPCClassInfo;
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
// X11 has a #define for CurrentTime. Unbelievable :-(.
|
||||
// See content/media/webaudio/AudioContext.h for more fun!
|
||||
#ifdef CurrentTime
|
||||
#undef CurrentTime
|
||||
#endif
|
||||
|
@ -191,5 +191,11 @@ AudioContext::DestinationStream() const
|
||||
return Destination()->Stream();
|
||||
}
|
||||
|
||||
double
|
||||
AudioContext::CurrentTime() const
|
||||
{
|
||||
return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,12 @@
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
|
||||
// X11 has a #define for CurrentTime. Unbelievable :-(.
|
||||
// See content/media/DOMMediaStream.h for more fun!
|
||||
#ifdef CurrentTime
|
||||
#undef CurrentTime
|
||||
#endif
|
||||
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
class nsIDOMWindow;
|
||||
@ -78,6 +84,8 @@ public:
|
||||
return float(IdealAudioRate());
|
||||
}
|
||||
|
||||
double CurrentTime() const;
|
||||
|
||||
AudioListener* Listener();
|
||||
|
||||
already_AddRefed<AudioBufferSourceNode> CreateBufferSource();
|
||||
|
@ -46,6 +46,8 @@ public:
|
||||
explicit GainNodeEngine(AudioDestinationNode* aDestination)
|
||||
: mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
// Keep the default value in sync with the default value in GainNode::GainNode.
|
||||
, mGain(1.f)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ MOCHITEST_FILES := \
|
||||
test_AudioListener.html \
|
||||
test_badConnect.html \
|
||||
test_biquadFilterNode.html \
|
||||
test_currentTime.html \
|
||||
test_delayNode.html \
|
||||
test_decodeAudioData.html \
|
||||
test_dynamicsCompressorNode.html \
|
||||
|
27
content/media/webaudio/test/test_currentTime.html
Normal file
27
content/media/webaudio/test/test_currentTime.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioContext.currentTime</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
var ac = new AudioContext();
|
||||
is(ac.currentTime, 0, "AudioContext.currentTime should be 0 initially");
|
||||
setTimeout(function() {
|
||||
ok(ac.currentTime > 0, "AudioContext.currentTime should have increased by now");
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -2192,7 +2192,7 @@ nsDOMWindowUtils::GetLayerManagerType(nsAString& aType)
|
||||
if (!widget)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
LayerManager *mgr = widget->GetLayerManager();
|
||||
LayerManager *mgr = widget->GetLayerManager(nsIWidget::LAYER_MANAGER_PERSISTENT);
|
||||
if (!mgr)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -10094,11 +10094,11 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
||||
TimeStamp deadline;
|
||||
|
||||
if (aTimeout && aTimeout->mWhen > now) {
|
||||
// The OS timer fired early (yikes!), and possibly out of order
|
||||
// too. Set |deadline| to be the time when the OS timer *should*
|
||||
// have fired so that any timers that *should* have fired before
|
||||
// aTimeout *will* be fired now. This happens most of the time on
|
||||
// Win2k.
|
||||
// The OS timer fired early (which can happen due to the timers
|
||||
// having lower precision than TimeStamp does). Set |deadline| to
|
||||
// be the time when the OS timer *should* have fired so that any
|
||||
// timers that *should* have fired before aTimeout *will* be fired
|
||||
// now.
|
||||
|
||||
deadline = aTimeout->mWhen;
|
||||
} else {
|
||||
|
@ -507,6 +507,7 @@ class CGHeaders(CGWrapper):
|
||||
# Now find all the things we'll need as arguments because we
|
||||
# need to wrap or unwrap them.
|
||||
bindingHeaders = set()
|
||||
declareIncludes = set(declareIncludes)
|
||||
def addHeadersForType(t, descriptor=None, dictionary=None):
|
||||
"""
|
||||
Add the relevant headers for this type. We use descriptor and
|
||||
@ -529,7 +530,19 @@ class CGHeaders(CGWrapper):
|
||||
typeDesc = p.getDescriptor(unrolled.inner.identifier.name)
|
||||
except NoSuchDescriptorError:
|
||||
continue
|
||||
implementationIncludes.add(typeDesc.headerFile)
|
||||
if dictionary:
|
||||
# Dictionaries with interface members rely on the
|
||||
# actual class definition of that interface member
|
||||
# being visible in the binding header, because they
|
||||
# store them in nsRefPtr and have inline
|
||||
# constructors/destructors.
|
||||
#
|
||||
# XXXbz maybe dictionaries with interface members
|
||||
# should just have out-of-line constructors and
|
||||
# destructors?
|
||||
declareIncludes.add(typeDesc.headerFile)
|
||||
else:
|
||||
implementationIncludes.add(typeDesc.headerFile)
|
||||
bindingHeaders.add(self.getDeclarationFilename(typeDesc.interface))
|
||||
elif unrolled.isDictionary():
|
||||
bindingHeaders.add(self.getDeclarationFilename(unrolled.inner))
|
||||
@ -556,7 +569,6 @@ class CGHeaders(CGWrapper):
|
||||
# Strip out the function name and convert "::" to "/"
|
||||
bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
|
||||
|
||||
declareIncludes = set(declareIncludes)
|
||||
for d in dictionaries:
|
||||
if d.parent:
|
||||
declareIncludes.add(self.getDeclarationFilename(d.parent))
|
||||
|
@ -1082,6 +1082,10 @@ class IDLDictionary(IDLObjectWithScope):
|
||||
return (False, None)
|
||||
|
||||
for member in self.members:
|
||||
if member.type.isDictionary() and member.type.nullable():
|
||||
raise WebIDLError("Dictionary %s has member with nullable "
|
||||
"dictionary type" % self.identifier.name,
|
||||
[member.location])
|
||||
(contains, locations) = typeContainsDictionary(member.type, self)
|
||||
if contains:
|
||||
raise WebIDLError("Dictionary %s has member with itself as type." %
|
||||
|
@ -425,3 +425,20 @@ def WebIDLTest(parser, harness):
|
||||
harness.ok(threw, "Member type must not be a Dictionary, one of whose "
|
||||
"members or inherited members has a type that includes "
|
||||
"its Dictionary.")
|
||||
|
||||
parser = parser.reset();
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
dictionary Foo {
|
||||
};
|
||||
|
||||
dictionary Bar {
|
||||
Foo? d;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Member type must not be a nullable dictionary")
|
||||
|
@ -63,7 +63,7 @@ IDPProvisioningContext.prototype = {
|
||||
|
||||
doError: function(msg) {
|
||||
log("Provisioning ERROR: " + msg);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
function IDPAuthenticationContext(aID, aOrigin, aTargetMM) {
|
||||
@ -85,7 +85,7 @@ IDPAuthenticationContext.prototype = {
|
||||
|
||||
doError: function IDPAC_doError(msg) {
|
||||
log("Authentication ERROR: " + msg);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
function RPWatchContext(aOptions, aTargetMM) {
|
||||
@ -152,11 +152,14 @@ this.DOMIdentity = {
|
||||
case "Identity:RP:Watch":
|
||||
this._watch(msg, targetMM);
|
||||
break;
|
||||
case "Identity:RP:Unwatch":
|
||||
this._unwatch(msg, targetMM);
|
||||
break;
|
||||
case "Identity:RP:Request":
|
||||
this._request(msg);
|
||||
this._request(msg, targetMM);
|
||||
break;
|
||||
case "Identity:RP:Logout":
|
||||
this._logout(msg);
|
||||
this._logout(msg, targetMM);
|
||||
break;
|
||||
// IDP
|
||||
case "Identity:IDP:BeginProvisioning":
|
||||
@ -180,6 +183,12 @@ this.DOMIdentity = {
|
||||
case "Identity:IDP:AuthenticationFailure":
|
||||
this._authenticationFailure(msg);
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
// we receive child-process-shutdown if the appliction crashes,
|
||||
// including if it is crashed by the OS (killed for out-of-memory,
|
||||
// for example)
|
||||
this._childProcessShutdown(targetMM);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -199,7 +208,9 @@ this.DOMIdentity = {
|
||||
"Identity:IDP:RegisterCertificate", "Identity:IDP:GenKeyPair",
|
||||
"Identity:IDP:BeginAuthentication",
|
||||
"Identity:IDP:CompleteAuthentication",
|
||||
"Identity:IDP:AuthenticationFailure"],
|
||||
"Identity:IDP:AuthenticationFailure",
|
||||
"Identity:RP:Unwatch",
|
||||
"child-process-shutdown"],
|
||||
|
||||
// Private.
|
||||
_init: function DOMIdentity__init() {
|
||||
@ -239,6 +250,10 @@ this.DOMIdentity = {
|
||||
IdentityService.RP.watch(context);
|
||||
},
|
||||
|
||||
_unwatch: function DOMIdentity_unwatch(message, targetMM) {
|
||||
IdentityService.RP.unwatch(message.id, targetMM);
|
||||
},
|
||||
|
||||
_request: function DOMIdentity__request(message) {
|
||||
IdentityService.RP.request(message.id, message);
|
||||
},
|
||||
@ -247,6 +262,10 @@ this.DOMIdentity = {
|
||||
IdentityService.RP.logout(message.id, message.origin, message);
|
||||
},
|
||||
|
||||
_childProcessShutdown: function DOMIdentity__childProcessShutdown(targetMM) {
|
||||
IdentityService.RP.childProcessShutdown(targetMM);
|
||||
},
|
||||
|
||||
_beginProvisioning: function DOMIdentity__beginProvisioning(message, targetMM) {
|
||||
let context = new IDPProvisioningContext(message.id, message.origin,
|
||||
targetMM);
|
||||
@ -277,7 +296,7 @@ this.DOMIdentity = {
|
||||
|
||||
_authenticationFailure: function DOMIdentity__authenticationFailure(message) {
|
||||
IdentityService.IDP.cancelAuthentication(message.id);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Object is initialized by nsIDService.js
|
||||
|
@ -49,7 +49,7 @@ nsDOMIdentity.prototype = {
|
||||
// Authentication
|
||||
beginAuthentication: 'r',
|
||||
completeAuthentication: 'r',
|
||||
raiseAuthenticationFailure: 'r',
|
||||
raiseAuthenticationFailure: 'r'
|
||||
},
|
||||
|
||||
// require native events unless syntheticEventsOk is set
|
||||
@ -405,7 +405,7 @@ nsDOMIdentity.prototype = {
|
||||
case "Identity:RP:Watch:OnLogin":
|
||||
// Do we have a watcher?
|
||||
if (!this._rpWatcher) {
|
||||
dump("WARNING: Received OnLogin message, but there is no RP watcher\n");
|
||||
this._log("WARNING: Received OnLogin message, but there is no RP watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -420,7 +420,7 @@ nsDOMIdentity.prototype = {
|
||||
case "Identity:RP:Watch:OnLogout":
|
||||
// Do we have a watcher?
|
||||
if (!this._rpWatcher) {
|
||||
dump("WARNING: Received OnLogout message, but there is no RP watcher\n");
|
||||
this._log("WARNING: Received OnLogout message, but there is no RP watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -431,7 +431,7 @@ nsDOMIdentity.prototype = {
|
||||
case "Identity:RP:Watch:OnReady":
|
||||
// Do we have a watcher?
|
||||
if (!this._rpWatcher) {
|
||||
dump("WARNING: Received OnReady message, but there is no RP watcher\n");
|
||||
this._log("WARNING: Received OnReady message, but there is no RP watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -442,7 +442,7 @@ nsDOMIdentity.prototype = {
|
||||
case "Identity:RP:Watch:OnCancel":
|
||||
// Do we have a watcher?
|
||||
if (!this._rpWatcher) {
|
||||
dump("WARNING: Received OnCancel message, but there is no RP watcher\n");
|
||||
this._log("WARNING: Received OnCancel message, but there is no RP watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -524,6 +524,14 @@ nsDOMIdentity.prototype = {
|
||||
return message;
|
||||
},
|
||||
|
||||
uninit: function DOMIdentity_uninit() {
|
||||
this._log("nsDOMIdentity uninit()");
|
||||
this._identityInternal._mm.sendAsyncMessage(
|
||||
"Identity:RP:Unwatch",
|
||||
{ id: this._id }
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -550,6 +558,8 @@ nsDOMIdentityInternal.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._identity.uninit();
|
||||
|
||||
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||
this._identity._initializeState();
|
||||
this._identity = null;
|
||||
@ -601,11 +611,11 @@ nsDOMIdentityInternal.prototype = {
|
||||
"Identity:RP:Watch:OnCancel",
|
||||
"Identity:IDP:CallBeginProvisioningCallback",
|
||||
"Identity:IDP:CallGenKeyPairCallback",
|
||||
"Identity:IDP:CallBeginAuthenticationCallback",
|
||||
"Identity:IDP:CallBeginAuthenticationCallback"
|
||||
];
|
||||
this._messages.forEach((function(msgName) {
|
||||
this._messages.forEach(function(msgName) {
|
||||
this._mm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
}, this);
|
||||
|
||||
// Setup observers so we can remove message listeners.
|
||||
Services.obs.addObserver(this, "inner-window-destroyed", false);
|
||||
|
@ -622,19 +622,22 @@ StackBasedEventTarget::QueryInterface(REFNSIID aIID,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MainThreadEventTarget::Dispatch(nsIRunnable* aRunnable,
|
||||
uint32_t aFlags)
|
||||
ImmediateRunEventTarget::Dispatch(nsIRunnable* aRunnable,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
NS_ASSERTION(aRunnable, "Null pointer!");
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = aRunnable;
|
||||
return NS_DispatchToMainThread(aRunnable, aFlags);
|
||||
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
||||
DebugOnly<nsresult> rv =
|
||||
runnable->Run();
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MainThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
|
||||
ImmediateRunEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
|
||||
{
|
||||
*aIsOnCurrentThread = NS_IsMainThread();
|
||||
*aIsOnCurrentThread = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,7 @@ public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
};
|
||||
|
||||
class MainThreadEventTarget : public StackBasedEventTarget
|
||||
class ImmediateRunEventTarget : public StackBasedEventTarget
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
|
@ -386,7 +386,7 @@ IndexedDBDatabaseChild::RecvSuccess(
|
||||
openHelper = new IPCOpenDatabaseHelper(mDatabase, request);
|
||||
}
|
||||
|
||||
MainThreadEventTarget target;
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(openHelper->Dispatch(&target))) {
|
||||
NS_WARNING("Dispatch of IPCOpenDatabaseHelper failed!");
|
||||
return false;
|
||||
@ -418,7 +418,7 @@ IndexedDBDatabaseChild::RecvError(const nsresult& aRv)
|
||||
|
||||
openHelper->SetError(aRv);
|
||||
|
||||
MainThreadEventTarget target;
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(openHelper->Dispatch(&target))) {
|
||||
NS_WARNING("Dispatch of IPCOpenDatabaseHelper failed!");
|
||||
return false;
|
||||
@ -436,7 +436,7 @@ IndexedDBDatabaseChild::RecvBlocked(const uint64_t& aOldVersion)
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
IDBVersionChangeEvent::CreateBlockedRunnable(mRequest, aOldVersion, mVersion);
|
||||
|
||||
MainThreadEventTarget target;
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of blocked event failed!");
|
||||
}
|
||||
@ -453,7 +453,7 @@ IndexedDBDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new VersionChangeRunnable(mDatabase, aOldVersion, aNewVersion);
|
||||
|
||||
MainThreadEventTarget target;
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of versionchange event failed!");
|
||||
}
|
||||
@ -521,14 +521,14 @@ IndexedDBDatabaseChild::RecvPIndexedDBTransactionConstructor(
|
||||
mDatabase->EnterSetVersionTransaction();
|
||||
mDatabase->mPreviousDatabaseInfo->version = oldVersion;
|
||||
|
||||
MainThreadEventTarget target;
|
||||
actor->SetTransaction(transaction);
|
||||
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(versionHelper->Dispatch(&target))) {
|
||||
NS_WARNING("Dispatch of IPCSetVersionHelper failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
actor->SetTransaction(transaction);
|
||||
|
||||
mOpenHelper = helper.forget();
|
||||
return true;
|
||||
}
|
||||
@ -604,7 +604,7 @@ IndexedDBTransactionChild::FireCompleteEvent(nsresult aRv)
|
||||
|
||||
nsRefPtr<CommitHelper> helper = new CommitHelper(transaction, aRv);
|
||||
|
||||
MainThreadEventTarget target;
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(helper, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of CommitHelper failed!");
|
||||
}
|
||||
@ -1240,7 +1240,7 @@ IndexedDBDeleteDatabaseRequestChild::Recv__delete__(const nsresult& aRv)
|
||||
helper->SetError(aRv);
|
||||
}
|
||||
|
||||
MainThreadEventTarget target;
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(helper->Dispatch(&target))) {
|
||||
NS_WARNING("Dispatch of IPCSetVersionHelper failed!");
|
||||
return false;
|
||||
@ -1259,7 +1259,7 @@ IndexedDBDeleteDatabaseRequestChild::RecvBlocked(
|
||||
IDBVersionChangeEvent::CreateBlockedRunnable(mOpenRequest,
|
||||
aCurrentVersion, 0);
|
||||
|
||||
MainThreadEventTarget target;
|
||||
ImmediateRunEventTarget target;
|
||||
if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatch of blocked event failed!");
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ interface AudioContext {
|
||||
|
||||
readonly attribute AudioDestinationNode destination;
|
||||
readonly attribute float sampleRate;
|
||||
readonly attribute double currentTime;
|
||||
readonly attribute AudioListener listener;
|
||||
|
||||
[Creator, Throws]
|
||||
|
@ -13,6 +13,7 @@ interface DummyInterface {
|
||||
RTCConfiguration rtcConfiguration();
|
||||
CFStateChangeEventDict cfstateChangeEvent();
|
||||
USSDReceivedEventDict ussdReceivedEvent();
|
||||
InspectorRGBTriple rgbTriple();
|
||||
};
|
||||
|
||||
interface DummyInterfaceWorkers {
|
||||
|
16
dom/webidl/InspectorUtils.webidl
Normal file
16
dom/webidl/InspectorUtils.webidl
Normal file
@ -0,0 +1,16 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
dictionary InspectorRGBTriple {
|
||||
/*
|
||||
* NOTE: Using octet for RGB components is not generally OK, because
|
||||
* they can be outside the 0-255 range, but for backwards-compatible
|
||||
* named colors (which is what we use this dictionary for) the 0-255
|
||||
* assumption is fine.
|
||||
*/
|
||||
octet r = 0;
|
||||
octet g = 0;
|
||||
octet b = 0;
|
||||
};
|
@ -119,6 +119,7 @@ webidl_files = \
|
||||
HTMLUListElement.webidl \
|
||||
IDBVersionChangeEvent.webidl \
|
||||
ImageData.webidl \
|
||||
InspectorUtils.webidl \
|
||||
LinkStyle.webidl \
|
||||
LocalMediaStream.webidl \
|
||||
Location.webidl \
|
||||
|
@ -288,3 +288,15 @@ NS_HSL2RGB(float h, float s, float l)
|
||||
b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f));
|
||||
return NS_RGB(r, g, b);
|
||||
}
|
||||
|
||||
NS_GFX_(const char*)
|
||||
NS_RGBToColorName(nscolor aColor)
|
||||
{
|
||||
for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) {
|
||||
if (kColors[idx] == aColor) {
|
||||
return kColorNames[idx];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -70,4 +70,11 @@ NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
|
||||
// the float parameters are all expected to be in the range 0-1
|
||||
NS_GFX_(nscolor) NS_HSL2RGB(float h, float s, float l);
|
||||
|
||||
// Return a color name for the given nscolor. If there is no color
|
||||
// name for it, returns null. If there are multiple possible color
|
||||
// names for the given color, the first one in nsColorNameList.h
|
||||
// (which is generally the first one in alphabetical order) will be
|
||||
// returned.
|
||||
NS_GFX_(const char*) NS_RGBToColorName(nscolor aColor);
|
||||
|
||||
#endif /* nsColor_h___ */
|
||||
|
@ -86,7 +86,8 @@ public:
|
||||
void ReplaceFontEntry(gfxFontEntry *aOldFontEntry,
|
||||
gfxFontEntry *aNewFontEntry) {
|
||||
uint32_t numFonts = mAvailableFonts.Length();
|
||||
for (uint32_t i = 0; i < numFonts; i++) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < numFonts; i++) {
|
||||
gfxFontEntry *fe = mAvailableFonts[i];
|
||||
if (fe == aOldFontEntry) {
|
||||
// note that this may delete aOldFontEntry, if there's no
|
||||
@ -96,6 +97,7 @@ public:
|
||||
break;
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(i < numFonts, "font entry not found in family!");
|
||||
ResetCharacterMap();
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsonparser.h"
|
||||
#include "jsprf.h"
|
||||
#include "jswatchpoint.h"
|
||||
|
||||
@ -620,6 +621,10 @@ AutoGCRooter::trace(JSTracer *trc)
|
||||
MarkValueUnbarriered(trc, &p->get(), "js::AutoWrapperVector.vector");
|
||||
return;
|
||||
}
|
||||
|
||||
case JSONPARSER:
|
||||
static_cast<js::JSONParser *>(this)->trace(trc);
|
||||
return;
|
||||
}
|
||||
|
||||
JS_ASSERT(tag_ >= 0);
|
||||
|
@ -142,7 +142,8 @@ class JS_PUBLIC_API(AutoGCRooter) {
|
||||
WRAPPER = -31, /* js::AutoWrapperRooter */
|
||||
OBJOBJHASHMAP=-32, /* js::AutoObjectObjectHashMap */
|
||||
OBJU32HASHMAP=-33, /* js::AutoObjectUnsigned32HashMap */
|
||||
OBJHASHSET = -34 /* js::AutoObjectHashSet */
|
||||
OBJHASHSET = -34, /* js::AutoObjectHashSet */
|
||||
JSONPARSER = -35 /* js::JSONParser */
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -3202,7 +3202,7 @@ struct types::ArrayTableKey
|
||||
};
|
||||
|
||||
void
|
||||
TypeCompartment::fixArrayType(JSContext *cx, HandleObject obj)
|
||||
TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
@ -3285,31 +3285,32 @@ TypeCompartment::fixArrayType(JSContext *cx, HandleObject obj)
|
||||
*/
|
||||
struct types::ObjectTableKey
|
||||
{
|
||||
jsid *ids;
|
||||
uint32_t nslots;
|
||||
jsid *properties;
|
||||
uint32_t nproperties;
|
||||
uint32_t nfixed;
|
||||
TaggedProto proto;
|
||||
|
||||
typedef JSObject * Lookup;
|
||||
struct Lookup {
|
||||
IdValuePair *properties;
|
||||
uint32_t nproperties;
|
||||
uint32_t nfixed;
|
||||
|
||||
static inline uint32_t hash(JSObject *obj) {
|
||||
return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^
|
||||
obj->slotSpan() ^ obj->numFixedSlots() ^
|
||||
((uint32_t)obj->getTaggedProto().toWord() >> 2));
|
||||
Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed)
|
||||
: properties(properties), nproperties(nproperties), nfixed(nfixed)
|
||||
{}
|
||||
};
|
||||
|
||||
static inline HashNumber hash(const Lookup &lookup) {
|
||||
return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
|
||||
lookup.nproperties ^
|
||||
lookup.nfixed);
|
||||
}
|
||||
|
||||
static inline bool match(const ObjectTableKey &v, RawObject obj) {
|
||||
if (obj->slotSpan() != v.nslots ||
|
||||
obj->numFixedSlots() != v.nfixed ||
|
||||
obj->getTaggedProto() != v.proto) {
|
||||
static inline bool match(const ObjectTableKey &v, const Lookup &lookup) {
|
||||
if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed)
|
||||
return false;
|
||||
}
|
||||
RawShape shape = obj->lastProperty();
|
||||
obj = NULL;
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (shape->propid() != v.ids[shape->slot()])
|
||||
for (size_t i = 0; i < lookup.nproperties; i++) {
|
||||
if (lookup.properties[i].id != v.properties[i])
|
||||
return false;
|
||||
shape = shape->previous();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3318,11 +3319,37 @@ struct types::ObjectTableKey
|
||||
struct types::ObjectTableEntry
|
||||
{
|
||||
ReadBarriered<TypeObject> object;
|
||||
ReadBarriered<Shape> shape;
|
||||
Type *types;
|
||||
};
|
||||
|
||||
static inline void
|
||||
UpdateObjectTableEntryTypes(JSContext *cx, ObjectTableEntry &entry,
|
||||
IdValuePair *properties, size_t nproperties)
|
||||
{
|
||||
for (size_t i = 0; i < nproperties; i++) {
|
||||
Type type = entry.types[i];
|
||||
Type ntype = GetValueTypeForTable(cx, properties[i].value);
|
||||
if (ntype == type)
|
||||
continue;
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
|
||||
type.isPrimitive(JSVAL_TYPE_DOUBLE))
|
||||
{
|
||||
/* The property types already reflect 'int32'. */
|
||||
} else {
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
|
||||
type.isPrimitive(JSVAL_TYPE_INT32))
|
||||
{
|
||||
/* Include 'double' in the property types to avoid the update below later. */
|
||||
entry.types[i] = Type::DoubleType();
|
||||
}
|
||||
entry.object->addPropertyType(cx, IdToTypeId(properties[i].id), ntype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::fixObjectType(JSContext *cx, HandleObject obj)
|
||||
TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
@ -3336,102 +3363,150 @@ TypeCompartment::fixObjectType(JSContext *cx, HandleObject obj)
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the same type object for all singleton/JSON arrays with the same
|
||||
* base shape, i.e. the same fields written in the same order. If there
|
||||
* is a type mismatch with previous objects of the same shape, use the
|
||||
* generic unknown type.
|
||||
* Use the same type object for all singleton/JSON objects with the same
|
||||
* base shape, i.e. the same fields written in the same order.
|
||||
*/
|
||||
JS_ASSERT(obj->isObject());
|
||||
|
||||
if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements())
|
||||
return;
|
||||
|
||||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj.get());
|
||||
RootedShape baseShape(cx, obj->lastProperty());
|
||||
Vector<IdValuePair> properties(cx);
|
||||
if (!properties.resize(obj->slotSpan())) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
Shape *shape = obj->lastProperty();
|
||||
while (!shape->isEmptyShape()) {
|
||||
IdValuePair &entry = properties[shape->slot()];
|
||||
entry.id = shape->propid();
|
||||
entry.value = obj->getSlot(shape->slot());
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
||||
ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots());
|
||||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
|
||||
|
||||
if (p) {
|
||||
/* The lookup ensures the shape matches, now check that the types match. */
|
||||
Type *types = p->value.types;
|
||||
for (unsigned i = 0; i < obj->slotSpan(); i++) {
|
||||
Type ntype = GetValueTypeForTable(cx, obj->getSlot(i));
|
||||
if (ntype != types[i]) {
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
|
||||
types[i].isPrimitive(JSVAL_TYPE_DOUBLE))
|
||||
{
|
||||
/* The property types already reflect 'int32'. */
|
||||
} else {
|
||||
if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
|
||||
types[i].isPrimitive(JSVAL_TYPE_INT32))
|
||||
{
|
||||
/* Include 'double' in the property types to avoid the walk below later. */
|
||||
types[i] = Type::DoubleType();
|
||||
}
|
||||
Shape *shape = baseShape;
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (shape->slot() == i) {
|
||||
if (!p->value.object->unknownProperties())
|
||||
p->value.object->addPropertyType(cx, IdToTypeId(shape->propid()), ntype);
|
||||
break;
|
||||
}
|
||||
shape = shape->previous();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JS_ASSERT(obj->getProto() == p->value.object->proto);
|
||||
JS_ASSERT(obj->lastProperty() == p->value.shape);
|
||||
|
||||
UpdateObjectTableEntryTypes(cx, p->value, properties.begin(), properties.length());
|
||||
obj->setType(p->value.object);
|
||||
} else {
|
||||
/* Make a new type to use for the object and similar future ones. */
|
||||
Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
|
||||
TypeObject *objType = newTypeObject(cx, &ObjectClass, objProto);
|
||||
if (!objType || !objType->addDefiniteProperties(cx, obj)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->isIndexed())
|
||||
objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
|
||||
|
||||
jsid *ids = cx->pod_calloc<jsid>(obj->slotSpan());
|
||||
if (!ids) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
Type *types = cx->pod_calloc<Type>(obj->slotSpan());
|
||||
if (!types) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
RootedShape shape(cx, baseShape);
|
||||
while (!shape->isEmptyShape()) {
|
||||
ids[shape->slot()] = shape->propid();
|
||||
types[shape->slot()] = GetValueTypeForTable(cx, obj->getSlot(shape->slot()));
|
||||
if (!objType->unknownProperties())
|
||||
objType->addPropertyType(cx, IdToTypeId(shape->propid()), types[shape->slot()]);
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
||||
ObjectTableKey key;
|
||||
key.ids = ids;
|
||||
key.nslots = obj->slotSpan();
|
||||
key.nfixed = obj->numFixedSlots();
|
||||
key.proto = obj->getTaggedProto();
|
||||
JS_ASSERT(ObjectTableKey::match(key, obj.get()));
|
||||
|
||||
ObjectTableEntry entry;
|
||||
entry.object = objType;
|
||||
entry.types = types;
|
||||
|
||||
p = objectTypeTable->lookupForAdd(obj.get());
|
||||
if (!objectTypeTable->add(p, key, entry)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
obj->setType(objType);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make a new type to use for the object and similar future ones. */
|
||||
Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
|
||||
TypeObject *objType = newTypeObject(cx, &ObjectClass, objProto);
|
||||
if (!objType || !objType->addDefiniteProperties(cx, obj)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->isIndexed())
|
||||
objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
|
||||
|
||||
jsid *ids = cx->pod_calloc<jsid>(properties.length());
|
||||
if (!ids) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
Type *types = cx->pod_calloc<Type>(properties.length());
|
||||
if (!types) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < properties.length(); i++) {
|
||||
ids[i] = properties[i].id;
|
||||
types[i] = GetValueTypeForTable(cx, obj->getSlot(i));
|
||||
if (!objType->unknownProperties())
|
||||
objType->addPropertyType(cx, IdToTypeId(ids[i]), types[i]);
|
||||
}
|
||||
|
||||
ObjectTableKey key;
|
||||
key.properties = ids;
|
||||
key.nproperties = properties.length();
|
||||
key.nfixed = obj->numFixedSlots();
|
||||
JS_ASSERT(ObjectTableKey::match(key, lookup));
|
||||
|
||||
ObjectTableEntry entry;
|
||||
entry.object = objType;
|
||||
entry.shape = obj->lastProperty();
|
||||
entry.types = types;
|
||||
|
||||
p = objectTypeTable->lookupForAdd(lookup);
|
||||
if (!objectTypeTable->add(p, key, entry)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
obj->setType(objType);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
if (!objectTypeTable) {
|
||||
objectTypeTable = cx->new_<ObjectTypeTable>();
|
||||
if (!objectTypeTable || !objectTypeTable->init()) {
|
||||
objectTypeTable = NULL;
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the object type table to allocate an object with the specified
|
||||
* properties, filling in its final type and shape and failing if no cache
|
||||
* entry could be found for the properties.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Filter out a few cases where we don't want to use the object type table.
|
||||
* Note that if the properties contain any duplicates or dense indexes,
|
||||
* the lookup below will fail as such arrays of properties cannot be stored
|
||||
* in the object type table --- fixObjectType populates the table with
|
||||
* properties read off its input object, which cannot be duplicates, and
|
||||
* ignores objects with dense indexes.
|
||||
*/
|
||||
if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT)
|
||||
return NULL;
|
||||
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
|
||||
size_t nfixed = gc::GetGCKindSlots(allocKind, &ObjectClass);
|
||||
|
||||
ObjectTableKey::Lookup lookup(properties, nproperties, nfixed);
|
||||
ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, allocKind));
|
||||
if (!obj) {
|
||||
cx->clearPendingException();
|
||||
return NULL;
|
||||
}
|
||||
JS_ASSERT(obj->getProto() == p->value.object->proto);
|
||||
|
||||
RootedShape shape(cx, p->value.shape);
|
||||
if (!JSObject::setLastProperty(cx, obj, shape)) {
|
||||
cx->clearPendingException();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UpdateObjectTableEntryTypes(cx, p->value, properties, nproperties);
|
||||
|
||||
for (size_t i = 0; i < nproperties; i++)
|
||||
obj->setSlot(i, properties[i].value);
|
||||
|
||||
obj->setType(p->value.object);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@ -3557,7 +3632,7 @@ TypeObject::addProperty(JSContext *cx, RawId id, Property **pprop)
|
||||
}
|
||||
|
||||
bool
|
||||
TypeObject::addDefiniteProperties(JSContext *cx, HandleObject obj)
|
||||
TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
if (unknownProperties())
|
||||
return true;
|
||||
@ -6471,17 +6546,18 @@ TypeCompartment::sweep(FreeOp *fop)
|
||||
for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
|
||||
const ObjectTableKey &key = e.front().key;
|
||||
ObjectTableEntry &entry = e.front().value;
|
||||
JS_ASSERT(uintptr_t(entry.object->proto.get()) == key.proto.toWord());
|
||||
|
||||
bool remove = false;
|
||||
if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()))
|
||||
remove = true;
|
||||
for (unsigned i = 0; !remove && i < key.nslots; i++) {
|
||||
if (JSID_IS_STRING(key.ids[i])) {
|
||||
JSString *str = JSID_TO_STRING(key.ids[i]);
|
||||
if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet()))
|
||||
remove = true;
|
||||
for (unsigned i = 0; !remove && i < key.nproperties; i++) {
|
||||
if (JSID_IS_STRING(key.properties[i])) {
|
||||
JSString *str = JSID_TO_STRING(key.properties[i]);
|
||||
if (IsStringAboutToBeFinalized(&str))
|
||||
remove = true;
|
||||
JS_ASSERT(AtomToId((JSAtom *)str) == key.ids[i]);
|
||||
JS_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]);
|
||||
}
|
||||
JS_ASSERT(!entry.types[i].isSingleObject());
|
||||
TypeObject *typeObject = NULL;
|
||||
@ -6495,7 +6571,7 @@ TypeCompartment::sweep(FreeOp *fop)
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
js_free(key.ids);
|
||||
js_free(key.properties);
|
||||
js_free(entry.types);
|
||||
e.removeFront();
|
||||
}
|
||||
@ -6825,7 +6901,7 @@ JSCompartment::sizeOfTypeInferenceData(TypeInferenceSizes *sizes, JSMallocSizeOf
|
||||
const ObjectTableEntry &value = e.front().value;
|
||||
|
||||
/* key.ids and values.types have the same length. */
|
||||
sizes->objectTypeTables += mallocSizeOf(key.ids) + mallocSizeOf(value.types);
|
||||
sizes->objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1054,7 +1054,7 @@ struct TypeObject : gc::Cell
|
||||
/* Helpers */
|
||||
|
||||
bool addProperty(JSContext *cx, RawId id, Property **pprop);
|
||||
bool addDefiniteProperties(JSContext *cx, HandleObject obj);
|
||||
bool addDefiniteProperties(JSContext *cx, JSObject *obj);
|
||||
bool matchDefiniteProperties(HandleObject obj);
|
||||
void addPrototype(JSContext *cx, TypeObject *proto);
|
||||
void addPropertyType(JSContext *cx, jsid id, Type type);
|
||||
@ -1382,8 +1382,10 @@ struct TypeCompartment
|
||||
ArrayTypeTable *arrayTypeTable;
|
||||
ObjectTypeTable *objectTypeTable;
|
||||
|
||||
void fixArrayType(JSContext *cx, HandleObject obj);
|
||||
void fixObjectType(JSContext *cx, HandleObject obj);
|
||||
void fixArrayType(JSContext *cx, JSObject *obj);
|
||||
void fixObjectType(JSContext *cx, JSObject *obj);
|
||||
|
||||
JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
|
||||
|
||||
/* Logging fields */
|
||||
|
||||
|
@ -23,6 +23,11 @@
|
||||
# include <pthread_np.h>
|
||||
# endif
|
||||
|
||||
# if defined(ANDROID)
|
||||
# include <unistd.h>
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
|
||||
@ -125,17 +130,43 @@ js::GetNativeStackBaseImpl()
|
||||
|
||||
void *stackBase = 0;
|
||||
size_t stackSize = 0;
|
||||
# ifdef DEBUG
|
||||
int rc =
|
||||
# endif
|
||||
int rc;
|
||||
# if defined(__OpenBSD__)
|
||||
pthread_stackseg_np(pthread_self(), &ss);
|
||||
rc = pthread_stackseg_np(pthread_self(), &ss);
|
||||
stackBase = (void*)((size_t) ss.ss_sp - ss.ss_size);
|
||||
stackSize = ss.ss_size;
|
||||
# elif defined(ANDROID)
|
||||
if (gettid() == getpid()) {
|
||||
// bionic's pthread_attr_getstack doesn't tell the truth for the main
|
||||
// thread (see bug 846670). So we scan /proc/self/maps to find the
|
||||
// segment which contains the stack.
|
||||
rc = -1;
|
||||
FILE *fs = fopen("/proc/self/maps", "r");
|
||||
if (fs) {
|
||||
char line[100];
|
||||
unsigned long stackAddr = (unsigned long)&sattr;
|
||||
while (fgets(line, sizeof(line), fs) != NULL) {
|
||||
unsigned long stackStart;
|
||||
unsigned long stackEnd;
|
||||
if (sscanf(line, "%lx-%lx ", &stackStart, &stackEnd) == 2 &&
|
||||
stackAddr >= stackStart && stackAddr < stackEnd) {
|
||||
stackBase = (void *)stackStart;
|
||||
stackSize = stackEnd - stackStart;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fs);
|
||||
}
|
||||
} else
|
||||
// For non main-threads pthread allocates the stack itself so it tells
|
||||
// the truth.
|
||||
rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
|
||||
# else
|
||||
pthread_attr_getstack(&sattr, &stackBase, &stackSize);
|
||||
rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
|
||||
# endif
|
||||
JS_ASSERT(!rc);
|
||||
if (rc)
|
||||
MOZ_CRASH();
|
||||
JS_ASSERT(stackBase);
|
||||
pthread_attr_destroy(&sattr);
|
||||
|
||||
|
@ -17,6 +17,40 @@ using namespace js;
|
||||
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
JSONParser::~JSONParser()
|
||||
{
|
||||
for (size_t i = 0; i < stack.length(); i++) {
|
||||
if (stack[i].state == FinishArrayElement)
|
||||
js_delete(&stack[i].elements());
|
||||
else
|
||||
js_delete(&stack[i].properties());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < freeElements.length(); i++)
|
||||
js_delete(freeElements[i]);
|
||||
|
||||
for (size_t i = 0; i < freeProperties.length(); i++)
|
||||
js_delete(freeProperties[i]);
|
||||
}
|
||||
|
||||
void
|
||||
JSONParser::trace(JSTracer *trc)
|
||||
{
|
||||
for (size_t i = 0; i < stack.length(); i++) {
|
||||
if (stack[i].state == FinishArrayElement) {
|
||||
ElementVector &elements = stack[i].elements();
|
||||
for (size_t j = 0; j < elements.length(); j++)
|
||||
gc::MarkValueRoot(trc, &elements[j], "JSONParser element");
|
||||
} else {
|
||||
PropertyVector &properties = stack[i].properties();
|
||||
for (size_t j = 0; j < properties.length(); j++) {
|
||||
gc::MarkValueRoot(trc, &properties[j].value, "JSONParser property value");
|
||||
gc::MarkIdRoot(trc, &properties[j].id, "JSONParser property id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JSONParser::error(const char *msg)
|
||||
{
|
||||
@ -483,17 +517,95 @@ JSONParser::advanceAfterProperty()
|
||||
return token(Error);
|
||||
}
|
||||
|
||||
/*
|
||||
* This enum is local to JSONParser::parse, below, but ISO C++98 doesn't allow
|
||||
* templates to depend on local types. Boo-urns!
|
||||
*/
|
||||
enum ParserState { FinishArrayElement, FinishObjectMember, JSONValue };
|
||||
JSObject *
|
||||
JSONParser::createFinishedObject(PropertyVector &properties)
|
||||
{
|
||||
/*
|
||||
* Look for an existing cached type and shape for objects with this set of
|
||||
* properties.
|
||||
*/
|
||||
if (cx->typeInferenceEnabled()) {
|
||||
JSObject *obj = cx->compartment->types.newTypedObject(cx, properties.begin(),
|
||||
properties.length());
|
||||
if (obj)
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a new object sized for the given number of properties and fill its
|
||||
* shape in manually.
|
||||
*/
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKind(properties.length());
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, allocKind));
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
RootedId propid(cx);
|
||||
RootedValue value(cx);
|
||||
|
||||
for (size_t i = 0; i < properties.length(); i++) {
|
||||
propid = properties[i].id;
|
||||
value = properties[i].value;
|
||||
if (!DefineNativeProperty(cx, obj, propid, value,
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE,
|
||||
0, 0))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to assign a new type to the object with type information for its
|
||||
* properties, and update the initializer type object cache with this
|
||||
* object's final shape.
|
||||
*/
|
||||
if (cx->typeInferenceEnabled())
|
||||
cx->compartment->types.fixObjectType(cx, obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSONParser::finishObject(MutableHandleValue vp, PropertyVector &properties)
|
||||
{
|
||||
JS_ASSERT(&properties == &stack.back().properties());
|
||||
|
||||
JSObject *obj = createFinishedObject(properties);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
vp.setObject(*obj);
|
||||
if (!freeProperties.append(&properties))
|
||||
return false;
|
||||
stack.popBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSONParser::finishArray(MutableHandleValue vp, ElementVector &elements)
|
||||
{
|
||||
JS_ASSERT(&elements == &stack.back().elements());
|
||||
|
||||
JSObject *obj = NewDenseCopiedArray(cx, elements.length(), elements.begin());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Try to assign a new type to the array according to its elements. */
|
||||
if (cx->typeInferenceEnabled())
|
||||
cx->compartment->types.fixArrayType(cx, obj);
|
||||
|
||||
vp.setObject(*obj);
|
||||
if (!freeElements.append(&elements))
|
||||
return false;
|
||||
stack.popBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSONParser::parse(MutableHandleValue vp)
|
||||
{
|
||||
Vector<ParserState> stateStack(cx);
|
||||
AutoValueVector valueStack(cx);
|
||||
RootedValue value(cx);
|
||||
JS_ASSERT(stack.empty());
|
||||
|
||||
vp.setUndefined();
|
||||
|
||||
@ -502,18 +614,15 @@ JSONParser::parse(MutableHandleValue vp)
|
||||
while (true) {
|
||||
switch (state) {
|
||||
case FinishObjectMember: {
|
||||
RootedValue v(cx, valueStack.popCopy());
|
||||
RootedId propid(cx, AtomToId(&valueStack.popCopy().toString()->asAtom()));
|
||||
RootedObject obj(cx, &valueStack.back().toObject());
|
||||
if (!DefineNativeProperty(cx, obj, propid, v,
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE,
|
||||
0, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PropertyVector &properties = stack.back().properties();
|
||||
properties.back().value = value;
|
||||
|
||||
token = advanceAfterProperty();
|
||||
if (token == ObjectClose)
|
||||
if (token == ObjectClose) {
|
||||
if (!finishObject(&value, properties))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (token != Comma) {
|
||||
if (token == OOM)
|
||||
return false;
|
||||
@ -527,20 +636,22 @@ JSONParser::parse(MutableHandleValue vp)
|
||||
|
||||
JSONMember:
|
||||
if (token == String) {
|
||||
if (!valueStack.append(atomValue()))
|
||||
jsid id = AtomToId(atomValue());
|
||||
PropertyVector &properties = stack.back().properties();
|
||||
if (!properties.append(IdValuePair(id)))
|
||||
return false;
|
||||
token = advancePropertyColon();
|
||||
if (token != Colon) {
|
||||
JS_ASSERT(token == Error);
|
||||
return errorReturn();
|
||||
}
|
||||
if (!stateStack.append(FinishObjectMember))
|
||||
return false;
|
||||
goto JSONValue;
|
||||
}
|
||||
if (token == ObjectClose) {
|
||||
JS_ASSERT(state == FinishObjectMember);
|
||||
JS_ASSERT(parsingMode == LegacyJSON);
|
||||
if (!finishObject(&value, stack.back().properties()))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (token == OOM)
|
||||
@ -550,18 +661,17 @@ JSONParser::parse(MutableHandleValue vp)
|
||||
return errorReturn();
|
||||
|
||||
case FinishArrayElement: {
|
||||
Value v = valueStack.popCopy();
|
||||
Rooted<JSObject*> obj(cx, &valueStack.back().toObject());
|
||||
if (!js_NewbornArrayPush(cx, obj, v))
|
||||
ElementVector &elements = stack.back().elements();
|
||||
if (!elements.append(value.get()))
|
||||
return false;
|
||||
token = advanceAfterArrayElement();
|
||||
if (token == Comma) {
|
||||
if (!stateStack.append(FinishArrayElement))
|
||||
return false;
|
||||
if (token == Comma)
|
||||
goto JSONValue;
|
||||
}
|
||||
if (token == ArrayClose)
|
||||
if (token == ArrayClose) {
|
||||
if (!finishArray(&value, elements))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
JS_ASSERT(token == Error);
|
||||
return errorReturn();
|
||||
}
|
||||
@ -572,49 +682,69 @@ JSONParser::parse(MutableHandleValue vp)
|
||||
JSONValueSwitch:
|
||||
switch (token) {
|
||||
case String:
|
||||
value = stringValue();
|
||||
break;
|
||||
case Number:
|
||||
if (!valueStack.append(token == String ? stringValue() : numberValue()))
|
||||
return false;
|
||||
value = numberValue();
|
||||
break;
|
||||
case True:
|
||||
if (!valueStack.append(BooleanValue(true)))
|
||||
return false;
|
||||
value = BooleanValue(true);
|
||||
break;
|
||||
case False:
|
||||
if (!valueStack.append(BooleanValue(false)))
|
||||
return false;
|
||||
value = BooleanValue(false);
|
||||
break;
|
||||
case Null:
|
||||
if (!valueStack.append(NullValue()))
|
||||
return false;
|
||||
value = NullValue();
|
||||
break;
|
||||
|
||||
case ArrayOpen: {
|
||||
JSObject *obj = NewDenseEmptyArray(cx);
|
||||
if (!obj || !valueStack.append(ObjectValue(*obj)))
|
||||
ElementVector *elements;
|
||||
if (!freeElements.empty()) {
|
||||
elements = freeElements.popCopy();
|
||||
elements->clear();
|
||||
} else {
|
||||
elements = cx->new_<ElementVector>(cx);
|
||||
if (!elements)
|
||||
return false;
|
||||
}
|
||||
if (!stack.append(elements))
|
||||
return false;
|
||||
|
||||
token = advance();
|
||||
if (token == ArrayClose)
|
||||
if (token == ArrayClose) {
|
||||
if (!finishArray(&value, *elements))
|
||||
return false;
|
||||
break;
|
||||
if (!stateStack.append(FinishArrayElement))
|
||||
return false;
|
||||
}
|
||||
goto JSONValueSwitch;
|
||||
}
|
||||
|
||||
case ObjectOpen: {
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
|
||||
if (!obj || !valueStack.append(ObjectValue(*obj)))
|
||||
PropertyVector *properties;
|
||||
if (!freeProperties.empty()) {
|
||||
properties = freeProperties.popCopy();
|
||||
properties->clear();
|
||||
} else {
|
||||
properties = cx->new_<PropertyVector>(cx);
|
||||
if (!properties)
|
||||
return false;
|
||||
}
|
||||
if (!stack.append(properties))
|
||||
return false;
|
||||
|
||||
token = advanceAfterObjectOpen();
|
||||
if (token == ObjectClose)
|
||||
if (token == ObjectClose) {
|
||||
if (!finishObject(&value, *properties))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
goto JSONMember;
|
||||
}
|
||||
|
||||
case ArrayClose:
|
||||
if (parsingMode == LegacyJSON &&
|
||||
!stateStack.empty() &&
|
||||
stateStack.back() == FinishArrayElement) {
|
||||
!stack.empty() &&
|
||||
stack.back().state == FinishArrayElement) {
|
||||
/*
|
||||
* Previous JSON parsing accepted trailing commas in
|
||||
* non-empty array syntax, and some users depend on this.
|
||||
@ -624,7 +754,8 @@ JSONParser::parse(MutableHandleValue vp)
|
||||
* such trailing commas only when specifically
|
||||
* instructed to do so.
|
||||
*/
|
||||
stateStack.popBack();
|
||||
if (!finishArray(&value, stack.back().elements()))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
@ -644,9 +775,9 @@ JSONParser::parse(MutableHandleValue vp)
|
||||
break;
|
||||
}
|
||||
|
||||
if (stateStack.empty())
|
||||
if (stack.empty())
|
||||
break;
|
||||
state = stateStack.popCopy();
|
||||
state = stack.back().state;
|
||||
}
|
||||
|
||||
for (; current < end; current++) {
|
||||
@ -657,7 +788,8 @@ JSONParser::parse(MutableHandleValue vp)
|
||||
}
|
||||
|
||||
JS_ASSERT(end == current);
|
||||
JS_ASSERT(valueStack.length() == 1);
|
||||
vp.set(valueStack[0]);
|
||||
JS_ASSERT(stack.empty());
|
||||
|
||||
vp.set(value);
|
||||
return true;
|
||||
}
|
||||
|
@ -14,10 +14,12 @@
|
||||
#include "jscntxt.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* NB: This class must only be used on the stack as it contains a js::Value.
|
||||
* NB: This class must only be used on the stack.
|
||||
*/
|
||||
class JSONParser
|
||||
class JSONParser : private AutoGCRooter
|
||||
{
|
||||
public:
|
||||
enum ErrorHandling { RaiseError, NoError };
|
||||
@ -27,10 +29,10 @@ class JSONParser
|
||||
/* Data members */
|
||||
|
||||
JSContext * const cx;
|
||||
JS::StableCharPtr current;
|
||||
const JS::StableCharPtr end;
|
||||
StableCharPtr current;
|
||||
const StableCharPtr end;
|
||||
|
||||
js::Value v;
|
||||
Value v;
|
||||
|
||||
const ParsingMode parsingMode;
|
||||
const ErrorHandling errorHandling;
|
||||
@ -40,6 +42,70 @@ class JSONParser
|
||||
ObjectOpen, ObjectClose,
|
||||
Colon, Comma,
|
||||
OOM, Error };
|
||||
|
||||
// State related to the parser's current position. At all points in the
|
||||
// parse this keeps track of the stack of arrays and objects which have
|
||||
// been started but not finished yet. The actual JS object is not
|
||||
// allocated until the literal is closed, so that the result can be sized
|
||||
// according to its contents and have its type and shape filled in using
|
||||
// caches.
|
||||
|
||||
// State for an array that is currently being parsed. This includes all
|
||||
// elements that have been seen so far.
|
||||
typedef Vector<Value, 20> ElementVector;
|
||||
|
||||
// State for an object that is currently being parsed. This includes all
|
||||
// the key/value pairs that have been seen so far.
|
||||
typedef Vector<IdValuePair, 10> PropertyVector;
|
||||
|
||||
// Possible states the parser can be in between values.
|
||||
enum ParserState {
|
||||
// An array element has just being parsed.
|
||||
FinishArrayElement,
|
||||
|
||||
// An object property has just been parsed.
|
||||
FinishObjectMember,
|
||||
|
||||
// At the start of the parse, before any values have been processed.
|
||||
JSONValue
|
||||
};
|
||||
|
||||
// Stack element for an in progress array or object.
|
||||
struct StackEntry {
|
||||
ElementVector &elements() {
|
||||
JS_ASSERT(state == FinishArrayElement);
|
||||
return * static_cast<ElementVector *>(vector);
|
||||
}
|
||||
|
||||
PropertyVector &properties() {
|
||||
JS_ASSERT(state == FinishObjectMember);
|
||||
return * static_cast<PropertyVector *>(vector);
|
||||
}
|
||||
|
||||
StackEntry(ElementVector *elements)
|
||||
: state(FinishArrayElement), vector(elements)
|
||||
{}
|
||||
|
||||
StackEntry(PropertyVector *properties)
|
||||
: state(FinishObjectMember), vector(properties)
|
||||
{}
|
||||
|
||||
ParserState state;
|
||||
|
||||
private:
|
||||
void *vector;
|
||||
};
|
||||
|
||||
// All in progress arrays and objects being parsed, in order from outermost
|
||||
// to innermost.
|
||||
Vector<StackEntry, 10> stack;
|
||||
|
||||
// Unused element and property vectors for previous in progress arrays and
|
||||
// objects. These vectors are not freed until the end of the parse to avoid
|
||||
// unnecessary freeing and allocation.
|
||||
Vector<ElementVector*, 5> freeElements;
|
||||
Vector<PropertyVector*, 5> freeProperties;
|
||||
|
||||
#ifdef DEBUG
|
||||
Token lastToken;
|
||||
#endif
|
||||
@ -58,11 +124,15 @@ class JSONParser
|
||||
JSONParser(JSContext *cx, JS::StableCharPtr data, size_t length,
|
||||
ParsingMode parsingMode = StrictJSON,
|
||||
ErrorHandling errorHandling = RaiseError)
|
||||
: cx(cx),
|
||||
: AutoGCRooter(cx, JSONPARSER),
|
||||
cx(cx),
|
||||
current(data),
|
||||
end((data + length).get(), data.get(), length),
|
||||
parsingMode(parsingMode),
|
||||
errorHandling(errorHandling)
|
||||
errorHandling(errorHandling),
|
||||
stack(cx),
|
||||
freeElements(cx),
|
||||
freeProperties(cx)
|
||||
#ifdef DEBUG
|
||||
, lastToken(Error)
|
||||
#endif
|
||||
@ -70,6 +140,8 @@ class JSONParser
|
||||
JS_ASSERT(current <= end);
|
||||
}
|
||||
|
||||
~JSONParser();
|
||||
|
||||
/*
|
||||
* Parse the JSON data specified at construction time. If it parses
|
||||
* successfully, store the prescribed value in *vp and return true. If an
|
||||
@ -95,10 +167,9 @@ class JSONParser
|
||||
return v;
|
||||
}
|
||||
|
||||
js::Value atomValue() const {
|
||||
JSAtom *atomValue() const {
|
||||
js::Value strval = stringValue();
|
||||
JS_ASSERT(strval.toString()->isAtom());
|
||||
return strval;
|
||||
return &strval.toString()->asAtom();
|
||||
}
|
||||
|
||||
Token token(Token t) {
|
||||
@ -141,9 +212,18 @@ class JSONParser
|
||||
void error(const char *msg);
|
||||
bool errorReturn();
|
||||
|
||||
JSObject *createFinishedObject(PropertyVector &properties);
|
||||
bool finishObject(MutableHandleValue vp, PropertyVector &properties);
|
||||
bool finishArray(MutableHandleValue vp, ElementVector &elements);
|
||||
|
||||
friend void AutoGCRooter::trace(JSTracer *trc);
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
private:
|
||||
JSONParser(const JSONParser &other) MOZ_DELETE;
|
||||
void operator=(const JSONParser &other) MOZ_DELETE;
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsonparser_h___ */
|
||||
|
@ -211,6 +211,17 @@ class XDRState;
|
||||
|
||||
class FreeOp;
|
||||
|
||||
struct IdValuePair
|
||||
{
|
||||
jsid id;
|
||||
Value value;
|
||||
|
||||
IdValuePair() {}
|
||||
IdValuePair(jsid idArg)
|
||||
: id(idArg)
|
||||
{}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JSC {
|
||||
|
@ -811,7 +811,7 @@ class CallCompiler : public BaseCompiler
|
||||
|
||||
if (!linker.verifyRange(f.chunk())) {
|
||||
disable();
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
linker.link(noIonCode, ic.icCall());
|
||||
|
@ -1734,7 +1734,6 @@ public:
|
||||
unsigned numSubpatterns = lastSubpatternId - subpatternId + 1;
|
||||
ByteDisjunction* parenthesesDisjunction = js_new<ByteDisjunction>(numSubpatterns, callFrameSize);
|
||||
|
||||
parenthesesDisjunction->terms.reserve(endTerm - beginTerm + 1);
|
||||
parenthesesDisjunction->terms.append(ByteTerm::SubpatternBegin());
|
||||
for (unsigned termInParentheses = beginTerm + 1; termInParentheses < endTerm; ++termInParentheses)
|
||||
parenthesesDisjunction->terms.append(m_bodyDisjunction->terms[termInParentheses]);
|
||||
|
@ -341,7 +341,7 @@ public:
|
||||
struct BytecodePattern {
|
||||
WTF_MAKE_FAST_ALLOCATED;
|
||||
public:
|
||||
BytecodePattern(PassOwnPtr<ByteDisjunction> body, Vector<ByteDisjunction*> &allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator)
|
||||
BytecodePattern(PassOwnPtr<ByteDisjunction> body, const Vector<ByteDisjunction*> &allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator)
|
||||
: m_body(body)
|
||||
, m_ignoreCase(pattern.m_ignoreCase)
|
||||
, m_multiline(pattern.m_multiline)
|
||||
@ -350,17 +350,12 @@ public:
|
||||
newlineCharacterClass = pattern.newlineCharacterClass();
|
||||
wordcharCharacterClass = pattern.wordcharCharacterClass();
|
||||
|
||||
// Trick: 'Steal' the YarrPattern's ParenthesesInfo!
|
||||
// The input vector isn't used afterwards anymore,
|
||||
// that way we don't have to copy the input.
|
||||
JS_ASSERT(m_allParenthesesInfo.size() == 0);
|
||||
m_allParenthesesInfo.swap(allParenthesesInfo);
|
||||
|
||||
// Trick: 'Steal' the YarrPattern's CharacterClasses!
|
||||
// The input vector isn't used afterwards anymore,
|
||||
// that way we don't have to copy the input.
|
||||
JS_ASSERT(m_userCharacterClasses.size() == 0);
|
||||
m_userCharacterClasses.swap(pattern.m_userCharacterClasses);
|
||||
m_allParenthesesInfo.append(allParenthesesInfo);
|
||||
m_userCharacterClasses.append(pattern.m_userCharacterClasses);
|
||||
// 'Steal' the YarrPattern's CharacterClasses! We clear its
|
||||
// array, so that it won't delete them on destruction. We'll
|
||||
// take responsibility for that.
|
||||
pattern.m_userCharacterClasses.clear();
|
||||
}
|
||||
|
||||
~BytecodePattern()
|
||||
|
@ -491,12 +491,11 @@ public:
|
||||
newDisjunction->m_parent = disjunction->m_parent;
|
||||
}
|
||||
PatternAlternative* newAlternative = newDisjunction->addNewAlternative();
|
||||
newAlternative->m_terms.reserve(alternative->m_terms.size());
|
||||
for (unsigned i = 0; i < alternative->m_terms.size(); ++i)
|
||||
newAlternative->m_terms.append(copyTerm(alternative->m_terms[i], filterStartsWithBOL));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (newDisjunction)
|
||||
m_pattern.m_disjunctions.append(newDisjunction);
|
||||
return newDisjunction;
|
||||
|
@ -204,10 +204,6 @@ class Vector {
|
||||
for (T *p = impl.begin(); p != impl.end(); ++p)
|
||||
js_delete(*p);
|
||||
}
|
||||
|
||||
bool reserve(size_t capacity) {
|
||||
return impl.reserve(capacity);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -235,11 +231,6 @@ class Vector<OwnPtr<T> > {
|
||||
delete_(*p);
|
||||
return impl.clear();
|
||||
}
|
||||
|
||||
void reserve(size_t capacity) {
|
||||
// XXX yarr-oom
|
||||
(void) impl.reserve(capacity);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
|
@ -5518,12 +5518,6 @@ PresShell::Paint(nsView* aViewToPaint,
|
||||
aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager);
|
||||
NS_ASSERTION(layerManager, "Must be in paint event");
|
||||
|
||||
if (!layerManager) {
|
||||
// Crash so we get a good stack on how this code is getting triggered.
|
||||
// See bug 847002 for details.
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
// Whether or not we should set first paint when painting is
|
||||
// suppressed is debatable. For now we'll do it because
|
||||
// B2G relies on first paint to configure the viewport and
|
||||
|
@ -14,7 +14,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=396024
|
||||
var is = window.opener.wrappedJSObject.is;
|
||||
var ok = window.opener.wrappedJSObject.ok;
|
||||
var todo = window.opener.wrappedJSObject.todo;
|
||||
var parentFinish = window.opener.wrappedJSObject.parentFinish;
|
||||
var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
|
||||
var gWbp;
|
||||
function printpreview() {
|
||||
gWbp = window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
@ -49,7 +49,7 @@ function exitprintpreview() {
|
||||
}
|
||||
|
||||
function finish() {
|
||||
parentFinish();
|
||||
SimpleTest.finish();
|
||||
window.close();
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=482976
|
||||
var is = window.opener.wrappedJSObject.is;
|
||||
var ok = window.opener.wrappedJSObject.ok;
|
||||
var todo = window.opener.wrappedJSObject.todo;
|
||||
var parentFinish = window.opener.wrappedJSObject.parentFinish;
|
||||
var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
|
||||
var gWbp;
|
||||
function printpreview() {
|
||||
gWbp = window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
@ -49,7 +49,7 @@ function exitprintpreview() {
|
||||
}
|
||||
|
||||
function finish() {
|
||||
parentFinish();
|
||||
SimpleTest.finish();
|
||||
window.close();
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ var is = window.opener.wrappedJSObject.is;
|
||||
var isnot = window.opener.wrappedJSObject.isnot;
|
||||
var ok = window.opener.wrappedJSObject.ok;
|
||||
var todo = window.opener.wrappedJSObject.todo;
|
||||
var parentFinish = window.opener.wrappedJSObject.parentFinish;
|
||||
var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
|
||||
var gWbp;
|
||||
var ctx1;
|
||||
var ctx2;
|
||||
@ -66,7 +66,7 @@ function exitprintpreview() {
|
||||
}
|
||||
|
||||
function finish() {
|
||||
parentFinish();
|
||||
SimpleTest.finish();
|
||||
window.close();
|
||||
}
|
||||
|
||||
|
@ -10,28 +10,6 @@
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
if (navigator.platform.startsWith("Linux")) {
|
||||
SimpleTest.expectAssertions(1); // bug 671976
|
||||
} else if (navigator.platform.startsWith("Win")) {
|
||||
// reliable 1 on Win7, but not on XP
|
||||
SimpleTest.expectAssertions(0, 1); // bug 671976
|
||||
}
|
||||
|
||||
function parentFinish() {
|
||||
// This is called while the helper window is still open. Call
|
||||
// doGC after it closes.
|
||||
setTimeout(doGC, 0);
|
||||
}
|
||||
function doGC() {
|
||||
// Garbage collecting the windows created in this test can cause
|
||||
// assertions, so GC now to blame those assertions to this test.
|
||||
// ("Destroying a currently-showing document", bug 671976)
|
||||
SpecialPowers.gc();
|
||||
setTimeout(doFinish, 0);
|
||||
}
|
||||
function doFinish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.open("printpreview_helper.xul", "printpreview", "chrome,width=100,height=100");
|
||||
]]></script>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user