Merge m-c to s-c.
@ -232,7 +232,7 @@ GetNativeFromGeckoAccessible(nsIAccessible *anAccessible)
|
||||
if (mRole == roles::DOCUMENT)
|
||||
return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
|
||||
|
||||
return NSAccessibilityRoleDescription([self role], nil);
|
||||
return NSAccessibilityRoleDescription([self role], [self subrole]);
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute])
|
||||
|
@ -290,7 +290,9 @@ ToNSString(id aValue)
|
||||
|
||||
- (NSString*)subrole
|
||||
{
|
||||
// TODO: text accessibles have two different subroles in Cocoa: secure textfield (passwords) and search field
|
||||
if(mRole == roles::PASSWORD_TEXT)
|
||||
return NSAccessibilitySecureTextFieldSubrole;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@ -347,7 +349,11 @@ ToNSString(id aValue)
|
||||
{
|
||||
if (!mGeckoTextAccessible)
|
||||
return nil;
|
||||
|
||||
|
||||
// A password text field returns an empty value
|
||||
if (mRole == roles::PASSWORD_TEXT)
|
||||
return @"";
|
||||
|
||||
nsAutoString text;
|
||||
nsresult rv = mGeckoTextAccessible->
|
||||
GetText(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
|
||||
|
@ -121,6 +121,7 @@ nsAccessibleWrap::GetNativeType ()
|
||||
case roles::CAPTION:
|
||||
case roles::ACCEL_LABEL:
|
||||
case roles::TEXT_LEAF:
|
||||
case roles::PASSWORD_TEXT:
|
||||
// normal textfield (static or editable)
|
||||
return [mozTextAccessible class];
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
<script type="application/javascript">
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
|
301
b2g/chrome/content/dbg-browser-actors.js
Normal file
@ -0,0 +1,301 @@
|
||||
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The function that creates the root actor. DebuggerServer expects to find this
|
||||
* function in the loaded actors in order to initialize properly.
|
||||
*/
|
||||
function createRootActor(connection) {
|
||||
return new DeviceRootActor(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the root actor that client-server communications always start with.
|
||||
* The root actor is responsible for the initial 'hello' packet and for
|
||||
* responding to a 'listTabs' request that produces the list of currently open
|
||||
* tabs.
|
||||
*
|
||||
* @param connection DebuggerServerConnection
|
||||
* The conection to the client.
|
||||
*/
|
||||
function DeviceRootActor(connection) {
|
||||
this.conn = connection;
|
||||
this._tabActors = new WeakMap();
|
||||
this._tabActorPool = null;
|
||||
this._actorFactories = null;
|
||||
this.browser = Services.wm.getMostRecentWindow('navigator:browser');
|
||||
}
|
||||
|
||||
DeviceRootActor.prototype = {
|
||||
/**
|
||||
* Return a 'hello' packet as specified by the Remote Debugging Protocol.
|
||||
*/
|
||||
sayHello: function DRA_sayHello() {
|
||||
return {
|
||||
from: 'root',
|
||||
applicationType: 'browser',
|
||||
traits: []
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnects the actor from the browser window.
|
||||
*/
|
||||
disconnect: function DRA_disconnect() {
|
||||
let actor = this._tabActors.get(this.browser);
|
||||
if (actor) {
|
||||
actor.exit();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the listTabs request. Builds a list of actors for the single
|
||||
* tab (window) running in the process. The actors will survive
|
||||
* until at least the next listTabs request.
|
||||
*/
|
||||
onListTabs: function DRA_onListTabs() {
|
||||
let actor = this._tabActors.get(this.browser);
|
||||
if (!actor) {
|
||||
actor = new DeviceTabActor(this.conn, this.browser);
|
||||
// this.actorID is set by ActorPool when an actor is put into one.
|
||||
actor.parentID = this.actorID;
|
||||
this._tabActors.set(this.browser, actor);
|
||||
}
|
||||
|
||||
let actorPool = new ActorPool(this.conn);
|
||||
actorPool.addActor(actor);
|
||||
|
||||
// Now drop the old actorID -> actor map. Actors that still mattered were
|
||||
// added to the new map, others will go away.
|
||||
if (this._tabActorPool) {
|
||||
this.conn.removeActorPool(this._tabActorPool);
|
||||
}
|
||||
this._tabActorPool = actorPool;
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
|
||||
return {
|
||||
'from': 'root',
|
||||
'selected': 0,
|
||||
'tabs': [actor.grip()]
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The request types this actor can handle.
|
||||
*/
|
||||
DeviceRootActor.prototype.requestTypes = {
|
||||
'listTabs': DeviceRootActor.prototype.onListTabs
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a tab actor for handling requests to the single tab, like attaching
|
||||
* and detaching.
|
||||
*
|
||||
* @param connection DebuggerServerConnection
|
||||
* The connection to the client.
|
||||
* @param browser browser
|
||||
* The browser instance that contains this tab.
|
||||
*/
|
||||
function DeviceTabActor(connection, browser) {
|
||||
this.conn = connection;
|
||||
this._browser = browser;
|
||||
}
|
||||
|
||||
DeviceTabActor.prototype = {
|
||||
get browser() {
|
||||
return this._browser;
|
||||
},
|
||||
|
||||
get exited() {
|
||||
return !this.browser;
|
||||
},
|
||||
|
||||
get attached() {
|
||||
return !!this._attached
|
||||
},
|
||||
|
||||
_tabPool: null,
|
||||
get tabActorPool() {
|
||||
return this._tabPool;
|
||||
},
|
||||
|
||||
_contextPool: null,
|
||||
get contextActorPool() {
|
||||
return this._contextPool;
|
||||
},
|
||||
|
||||
actorPrefix: 'tab',
|
||||
|
||||
grip: function DTA_grip() {
|
||||
dbg_assert(!this.exited,
|
||||
'grip() should not be called on exited browser actor.');
|
||||
dbg_assert(this.actorID,
|
||||
'tab should have an actorID.');
|
||||
return {
|
||||
'actor': this.actorID,
|
||||
'title': this.browser.contentTitle,
|
||||
'url': this.browser.document.documentURI
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the actor is removed from the connection.
|
||||
*/
|
||||
disconnect: function DTA_disconnect() {
|
||||
this._detach();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the root actor when the underlying tab is closed.
|
||||
*/
|
||||
exit: function DTA_exit() {
|
||||
if (this.exited) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.attached) {
|
||||
this._detach();
|
||||
this.conn.send({
|
||||
'from': this.actorID,
|
||||
'type': 'tabDetached'
|
||||
});
|
||||
}
|
||||
|
||||
this._browser = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the actual work of attaching to a tab.
|
||||
*/
|
||||
_attach: function DTA_attach() {
|
||||
if (this._attached) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a pool for tab-lifetime actors.
|
||||
dbg_assert(!this._tabPool, 'Should not have a tab pool if we were not attached.');
|
||||
this._tabPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._tabPool);
|
||||
|
||||
// ... and a pool for context-lifetime actors.
|
||||
this._pushContext();
|
||||
|
||||
this._attached = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a thread actor and a pool for context-lifetime actors. It then sets
|
||||
* up the content window for debugging.
|
||||
*/
|
||||
_pushContext: function DTA_pushContext() {
|
||||
dbg_assert(!this._contextPool, "Can't push multiple contexts");
|
||||
|
||||
this._contextPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
|
||||
this.threadActor = new ThreadActor(this);
|
||||
this._addDebuggees(this.browser.content.wrappedJSObject);
|
||||
this._contextPool.addActor(this.threadActor);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the provided window and all windows in its frame tree as debuggees.
|
||||
*/
|
||||
_addDebuggees: function DTA__addDebuggees(content) {
|
||||
this.threadActor.addDebuggee(content);
|
||||
let frames = content.frames;
|
||||
for (let i = 0; i < frames.length; i++) {
|
||||
this._addDebuggees(frames[i]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Exits the current thread actor and removes the context-lifetime actor pool.
|
||||
* The content window is no longer being debugged after this call.
|
||||
*/
|
||||
_popContext: function DTA_popContext() {
|
||||
dbg_assert(!!this._contextPool, 'No context to pop.');
|
||||
|
||||
this.conn.removeActorPool(this._contextPool);
|
||||
this._contextPool = null;
|
||||
this.threadActor.exit();
|
||||
this.threadActor = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the actual work of detaching from a tab.
|
||||
*/
|
||||
_detach: function DTA_detach() {
|
||||
if (!this.attached) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._popContext();
|
||||
|
||||
// Shut down actors that belong to this tab's pool.
|
||||
this.conn.removeActorPool(this._tabPool);
|
||||
this._tabPool = null;
|
||||
|
||||
this._attached = false;
|
||||
},
|
||||
|
||||
// Protocol Request Handlers
|
||||
|
||||
onAttach: function DTA_onAttach(aRequest) {
|
||||
if (this.exited) {
|
||||
return { type: 'exited' };
|
||||
}
|
||||
|
||||
this._attach();
|
||||
|
||||
return { type: 'tabAttached', threadActor: this.threadActor.actorID };
|
||||
},
|
||||
|
||||
onDetach: function DTA_onDetach(aRequest) {
|
||||
if (!this.attached) {
|
||||
return { error: 'wrongState' };
|
||||
}
|
||||
|
||||
this._detach();
|
||||
|
||||
return { type: 'detached' };
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare to enter a nested event loop by disabling debuggee events.
|
||||
*/
|
||||
preNest: function DTA_preNest() {
|
||||
let windowUtils = this.browser.content
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.suppressEventHandling(true);
|
||||
windowUtils.suspendTimeouts();
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare to exit a nested event loop by enabling debuggee events.
|
||||
*/
|
||||
postNest: function DTA_postNest(aNestData) {
|
||||
let windowUtils = this.browser.content
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.resumeTimeouts();
|
||||
windowUtils.suppressEventHandling(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The request types this actor can handle.
|
||||
*/
|
||||
DeviceTabActor.prototype.requestTypes = {
|
||||
'attach': DeviceTabActor.prototype.onAttach,
|
||||
'detach': DeviceTabActor.prototype.onDetach
|
||||
};
|
@ -45,6 +45,11 @@ XPCOMUtils.defineLazyServiceGetter(Services, 'fm', function() {
|
||||
.getService(Ci.nsFocusManager);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
|
||||
Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
|
||||
return DebuggerServer;
|
||||
});
|
||||
|
||||
// FIXME Bug 707625
|
||||
// until we have a proper security model, add some rights to
|
||||
// the pre-installed web applications
|
||||
@ -541,3 +546,24 @@ var WebappsHelper = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the debugger server.
|
||||
function startDebugger() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
|
||||
}
|
||||
|
||||
let port = Services.prefs.getIntPref('devtools.debugger.port') || 6000;
|
||||
try {
|
||||
DebuggerServer.openListener(port, false);
|
||||
} catch (e) {
|
||||
dump('Unable to start debugger server: ' + e + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('ContentStart', function(evt) {
|
||||
if (Services.prefs.getBoolPref('devtools.debugger.enabled')) {
|
||||
startDebugger();
|
||||
}
|
||||
}, false);
|
||||
|
@ -4,6 +4,7 @@ chrome.jar:
|
||||
% content branding %content/branding/
|
||||
% content browser %content/
|
||||
|
||||
content/dbg-browser-actors.js (content/dbg-browser-actors.js)
|
||||
* content/shell.xul (content/shell.xul)
|
||||
* content/shell.js (content/shell.js)
|
||||
#ifndef MOZ_TOUCH
|
||||
|
@ -270,8 +270,8 @@
|
||||
placespopup="true"
|
||||
context="placesContext"
|
||||
openInTabs="children"
|
||||
oncommand="BookmarksEventHandler.onCommand(event);"
|
||||
onclick="BookmarksEventHandler.onClick(event);"
|
||||
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
|
||||
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
|
||||
onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
|
||||
if (!this.parentNode._placesView)
|
||||
new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
|
||||
|
@ -26,6 +26,11 @@ let gBrowserThumbnails = {
|
||||
_tabEvents: ["TabClose", "TabSelect"],
|
||||
|
||||
init: function Thumbnails_init() {
|
||||
try {
|
||||
if (Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"))
|
||||
return;
|
||||
} catch (e) {}
|
||||
|
||||
gBrowser.addTabsProgressListener(this);
|
||||
|
||||
this._tabEvents.forEach(function (aEvent) {
|
||||
@ -93,6 +98,9 @@ let gBrowserThumbnails = {
|
||||
},
|
||||
|
||||
_shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
|
||||
if (gPrivateBrowsingUI.privateBrowsingEnabled)
|
||||
return false;
|
||||
|
||||
let doc = aBrowser.contentDocument;
|
||||
|
||||
// FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
|
||||
|
@ -3604,12 +3604,12 @@ function FillHistoryMenu(aParent) {
|
||||
item.setAttribute("index", j);
|
||||
|
||||
if (j != index) {
|
||||
function FHM_getFaviconURLCallback(aURI) {
|
||||
let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
|
||||
item.style.listStyleImage = "url(" + iconURL + ")";
|
||||
}
|
||||
PlacesUtils.favicons.getFaviconURLForPage(entry.URI,
|
||||
FHM_getFaviconURLCallback);
|
||||
PlacesUtils.favicons.getFaviconURLForPage(entry.URI, function (aURI) {
|
||||
if (aURI) {
|
||||
let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
|
||||
item.style.listStyleImage = "url(" + iconURL + ")";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (j < index) {
|
||||
|
@ -49,20 +49,29 @@
|
||||
-moz-transition-timing-function: linear;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
#highlighter-nodeinfobar-text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-container[locked] > #highlighter-nodeinfobar {
|
||||
.highlighter-nodeinfobar-button > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-container:not([locked]):not(:hover) > #highlighter-nodeinfobar > .highlighter-nodeinfobar-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-container[locked] > #highlighter-nodeinfobar,
|
||||
#highlighter-nodeinfobar-container:not([locked]):hover > #highlighter-nodeinfobar {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
html|*#highlighter-nodeinfobar-id,
|
||||
html|*#highlighter-nodeinfobar-classes,
|
||||
html|*#highlighter-nodeinfobar-pseudo-classes,
|
||||
html|*#highlighter-nodeinfobar-tagname {
|
||||
-moz-user-select: text;
|
||||
cursor: text;
|
||||
|
@ -51,7 +51,6 @@ _BROWSER_TEST_FILES = \
|
||||
browser_475045.js \
|
||||
browser_423515.js \
|
||||
browser_410196_paste_into_tags.js \
|
||||
browser_457473_no_copy_guid.js \
|
||||
browser_sort_in_library.js \
|
||||
browser_library_open_leak.js \
|
||||
browser_library_panel_leak.js \
|
||||
|
@ -1,146 +0,0 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dietrich Ayala <dietrich@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
// sanity check
|
||||
ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
|
||||
ok(PlacesUIUtils, "checking PlacesUIUtils, running in chrome context?");
|
||||
|
||||
/*
|
||||
- create, a test folder, add bookmark, separator to it
|
||||
- fetch guids for all
|
||||
- copy the folder
|
||||
- test that guids are all different
|
||||
- undo copy
|
||||
- redo copy
|
||||
- test that guids for the copy stay the same
|
||||
*/
|
||||
|
||||
var toolbarId = PlacesUtils.toolbarFolderId;
|
||||
var toolbarNode = PlacesUtils.getFolderContents(toolbarId).root;
|
||||
|
||||
var oldCount = toolbarNode.childCount;
|
||||
var testRootId = PlacesUtils.bookmarks.createFolder(toolbarId, "test root", -1);
|
||||
is(toolbarNode.childCount, oldCount+1, "confirm test root node is a container, and is empty");
|
||||
var testRootNode = toolbarNode.getChild(toolbarNode.childCount-1);
|
||||
PlacesUtils.asContainer(testRootNode);
|
||||
testRootNode.containerOpen = true;
|
||||
is(testRootNode.childCount, 0, "confirm test root node is a container, and is empty");
|
||||
|
||||
// create folder A, fill it w/ each item type
|
||||
var folderAId = PlacesUtils.bookmarks.createFolder(testRootId, "A", -1);
|
||||
PlacesUtils.bookmarks.insertBookmark(folderAId, PlacesUtils._uri("http://foo"),
|
||||
-1, "test bookmark");
|
||||
PlacesUtils.bookmarks.insertSeparator(folderAId, -1);
|
||||
var folderANode = testRootNode.getChild(0);
|
||||
var folderAGUIDs = getGUIDs(folderANode);
|
||||
|
||||
// test the test function
|
||||
ok(checkGUIDs(folderANode, folderAGUIDs, true), "confirm guid test works");
|
||||
|
||||
// serialize the folder
|
||||
var serializedNode = PlacesUtils.wrapNode(folderANode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER);
|
||||
var rawNode = PlacesUtils.unwrapNodes(serializedNode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER).shift();
|
||||
ok(rawNode.type, "confirm json node was made");
|
||||
|
||||
// Create a copy transaction from the serialization.
|
||||
// this exercises the guid-filtering
|
||||
var transaction = PlacesUIUtils.makeTransaction(rawNode,
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
|
||||
testRootId, -1, true);
|
||||
ok(transaction, "create transaction");
|
||||
|
||||
// execute it, copying to the test root folder
|
||||
PlacesUtils.transactionManager.doTransaction(transaction);
|
||||
is(testRootNode.childCount, 2, "create test folder via copy");
|
||||
|
||||
// check GUIDs are different
|
||||
var folderBNode = testRootNode.getChild(1);
|
||||
ok(checkGUIDs(folderBNode, folderAGUIDs, false), "confirm folder A GUIDs don't match folder B GUIDs");
|
||||
var folderBGUIDs = getGUIDs(folderBNode);
|
||||
ok(checkGUIDs(folderBNode, folderBGUIDs, true), "confirm test of new GUIDs");
|
||||
|
||||
// undo the transaction, confirm the removal
|
||||
PlacesUtils.transactionManager.undoTransaction();
|
||||
is(testRootNode.childCount, 1, "confirm undo removed the copied folder");
|
||||
|
||||
// redo the transaction
|
||||
// confirming GUIDs persist through undo/redo
|
||||
PlacesUtils.transactionManager.redoTransaction();
|
||||
is(testRootNode.childCount, 2, "confirm redo re-copied the folder");
|
||||
folderBNode = testRootNode.getChild(1);
|
||||
ok(checkGUIDs(folderBNode, folderAGUIDs, false), "folder B GUIDs after undo/redo don't match folder A GUIDs"); // sanity check
|
||||
ok(checkGUIDs(folderBNode, folderBGUIDs, true), "folder B GUIDs after under/redo should match pre-undo/redo folder B GUIDs");
|
||||
|
||||
// Close containers, cleaning up their observers.
|
||||
testRootNode.containerOpen = false;
|
||||
toolbarNode.containerOpen = false;
|
||||
|
||||
// clean up
|
||||
PlacesUtils.transactionManager.undoTransaction();
|
||||
PlacesUtils.bookmarks.removeItem(testRootId);
|
||||
}
|
||||
|
||||
function getGUIDs(aNode) {
|
||||
PlacesUtils.asContainer(aNode);
|
||||
aNode.containerOpen = true;
|
||||
var GUIDs = {
|
||||
folder: PlacesUtils.bookmarks.getItemGUID(aNode.itemId),
|
||||
bookmark: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(0).itemId),
|
||||
separator: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(1).itemId)
|
||||
};
|
||||
aNode.containerOpen = false;
|
||||
return GUIDs;
|
||||
}
|
||||
|
||||
function checkGUIDs(aFolderNode, aGUIDs, aShouldMatch) {
|
||||
|
||||
function check(aNode, aGUID, aEquals) {
|
||||
var nodeGUID = PlacesUtils.bookmarks.getItemGUID(aNode.itemId);
|
||||
return aEquals ? (nodeGUID == aGUID) : (nodeGUID != aGUID);
|
||||
}
|
||||
|
||||
PlacesUtils.asContainer(aFolderNode);
|
||||
aFolderNode.containerOpen = true;
|
||||
|
||||
var allMatch = check(aFolderNode, aGUIDs.folder, aShouldMatch) &&
|
||||
check(aFolderNode.getChild(0), aGUIDs.bookmark, aShouldMatch) &&
|
||||
check(aFolderNode.getChild(1), aGUIDs.separator, aShouldMatch)
|
||||
|
||||
aFolderNode.containerOpen = false;
|
||||
return allMatch;
|
||||
}
|
@ -62,7 +62,7 @@ const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
|
||||
const POST_DATA_ANNO = "bookmarkProperties/POSTData";
|
||||
|
||||
const TEST_FAVICON_PAGE_URL = "http://en-US.www.mozilla.com/en-US/firefox/central/";
|
||||
const TEST_FAVICON_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
|
||||
const TEST_FAVICON_DATA_SIZE = 580;
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
@ -84,8 +84,7 @@ function after_import(success) {
|
||||
|
||||
// Check that every bookmark is correct
|
||||
// Corrupt bookmarks should not have been imported
|
||||
database_check();
|
||||
waitForAsyncUpdates(function() {
|
||||
database_check(function () {
|
||||
// Create corruption in database
|
||||
var corruptItemId = bs.insertBookmark(bs.toolbarFolder,
|
||||
uri("http://test.mozilla.org"),
|
||||
@ -112,22 +111,23 @@ function after_import(success) {
|
||||
|
||||
// Import bookmarks
|
||||
try {
|
||||
BookmarkHTMLUtils.importFromFile(bookmarksFile, true, before_database_check);
|
||||
BookmarkHTMLUtils.importFromFile(bookmarksFile, true, before_database_check);
|
||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||
});
|
||||
}
|
||||
|
||||
function before_database_check(success) {
|
||||
// Check that every bookmark is correct
|
||||
database_check();
|
||||
|
||||
waitForAsyncUpdates(do_test_finished);
|
||||
// Check that every bookmark is correct
|
||||
database_check(do_test_finished);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for imported bookmarks correctness
|
||||
*
|
||||
* @param aCallback
|
||||
* Called when the checks are finished.
|
||||
*/
|
||||
function database_check() {
|
||||
function database_check(aCallback) {
|
||||
// BOOKMARKS MENU
|
||||
var query = hs.getNewQuery();
|
||||
query.setFolders([bs.bookmarksMenuFolder], 1);
|
||||
@ -226,7 +226,14 @@ function database_check() {
|
||||
unfiledBookmarks.containerOpen = false;
|
||||
|
||||
// favicons
|
||||
var faviconURI = icos.getFaviconForPage(uri(TEST_FAVICON_PAGE_URL));
|
||||
var dataURL = icos.getFaviconDataAsDataURL(faviconURI);
|
||||
do_check_eq(TEST_FAVICON_DATA_URL, dataURL);
|
||||
icos.getFaviconDataForPage(uri(TEST_FAVICON_PAGE_URL),
|
||||
function DC_onComplete(aURI, aDataLen, aData, aMimeType) {
|
||||
// aURI should never be null when aDataLen > 0.
|
||||
do_check_neq(aURI, null);
|
||||
// Favicon data is stored in the bookmarks file as a "data:" URI. For
|
||||
// simplicity, instead of converting the data we receive to a "data:" URI
|
||||
// and comparing it, we just check the data size.
|
||||
do_check_eq(TEST_FAVICON_DATA_SIZE, aDataLen);
|
||||
aCallback();
|
||||
});
|
||||
}
|
||||
|
@ -183,11 +183,14 @@ add_test(function test_emptytitle_export()
|
||||
{
|
||||
// Test exporting and importing with an empty-titled bookmark.
|
||||
// 1. import bookmarks
|
||||
// 1. create an empty-titled bookmark.
|
||||
// 2. export to bookmarks.exported.html
|
||||
// 3. empty bookmarks db
|
||||
// 4. import bookmarks.exported.html
|
||||
// 5. run the test-suite
|
||||
// 2. create an empty-titled bookmark.
|
||||
// 3. export to bookmarks.exported.html
|
||||
// 4. empty bookmarks db
|
||||
// 5. import bookmarks.exported.html
|
||||
// 6. run the test-suite
|
||||
// 7. remove the empty-titled bookmark
|
||||
// 8. export to bookmarks.exported.html
|
||||
// 9. empty bookmarks db and continue
|
||||
|
||||
try {
|
||||
BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
|
||||
@ -236,6 +239,83 @@ add_test(function test_emptytitle_export()
|
||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||
});
|
||||
|
||||
add_test(function test_import_chromefavicon()
|
||||
{
|
||||
// Test exporting and importing with a bookmark pointing to a chrome favicon.
|
||||
// 1. import bookmarks
|
||||
// 2. create a bookmark pointing to a chrome favicon.
|
||||
// 3. export to bookmarks.exported.html
|
||||
// 4. empty bookmarks db
|
||||
// 5. import bookmarks.exported.html
|
||||
// 6. run the test-suite
|
||||
// 7. remove the bookmark pointing to a chrome favicon.
|
||||
// 8. export to bookmarks.exported.html
|
||||
// 9. empty bookmarks db and continue
|
||||
|
||||
const PAGE_URI = NetUtil.newURI("http://example.com/chromefavicon_page");
|
||||
const CHROME_FAVICON_URI = NetUtil.newURI("chrome://global/skin/icons/information-16.png");
|
||||
const CHROME_FAVICON_URI_2 = NetUtil.newURI("chrome://global/skin/icons/error-16.png");
|
||||
|
||||
try {
|
||||
BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
|
||||
if (!success) {
|
||||
do_throw("couldn't import the exported file.");
|
||||
}
|
||||
let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
PAGE_URI,
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"Test");
|
||||
|
||||
PlacesUtils.favicons.setAndFetchFaviconForPage(
|
||||
PAGE_URI, CHROME_FAVICON_URI, true, function () {
|
||||
PlacesUtils.favicons.getFaviconDataForPage(
|
||||
PAGE_URI, function (aURI, aDataLen, aData, aMimeType) {
|
||||
let base64Icon = "data:image/png;base64," +
|
||||
base64EncodeString(String.fromCharCode.apply(String, aData));
|
||||
|
||||
test_bookmarks.unfiled.push(
|
||||
{ title: "Test", url: PAGE_URI.spec, icon: base64Icon });
|
||||
|
||||
try {
|
||||
exporter.exportHTMLToFile(gBookmarksFileNew);
|
||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||
|
||||
// Change the favicon to check it's really imported again later.
|
||||
PlacesUtils.favicons.setAndFetchFaviconForPage(
|
||||
PAGE_URI, CHROME_FAVICON_URI_2, true, function () {
|
||||
|
||||
remove_all_bookmarks();
|
||||
|
||||
try {
|
||||
BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
|
||||
if (!success) {
|
||||
do_throw("couldn't import the exported file.");
|
||||
}
|
||||
waitForAsyncUpdates(function () {
|
||||
testImportedBookmarks();
|
||||
|
||||
// Cleanup.
|
||||
test_bookmarks.unfiled.pop();
|
||||
PlacesUtils.bookmarks.removeItem(id);
|
||||
|
||||
try {
|
||||
exporter.exportHTMLToFile(gBookmarksFileNew);
|
||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||
|
||||
waitForAsyncUpdates(function () {
|
||||
remove_all_bookmarks();
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||
});
|
||||
|
||||
add_test(function test_import_ontop()
|
||||
{
|
||||
// Test importing the exported bookmarks.html file *on top of* the existing
|
||||
|
@ -92,28 +92,28 @@ Site.prototype = {
|
||||
* A callback function that takes a favicon image URL as a parameter.
|
||||
*/
|
||||
getFavicon: function Site_getFavicon(aCallback) {
|
||||
let callbackExecuted = false;
|
||||
function faviconDataCallback(aURI, aDataLen, aData, aMimeType) {
|
||||
// We don't need a second callback, so we can ignore it to avoid making
|
||||
// a second database query for the favicon data.
|
||||
if (callbackExecuted) {
|
||||
return;
|
||||
}
|
||||
function invokeCallback(aFaviconURI) {
|
||||
try {
|
||||
// Use getFaviconLinkForIcon to get image data from the database instead
|
||||
// of using the favicon URI to fetch image data over the network.
|
||||
aCallback(gFaviconService.getFaviconLinkForIcon(aURI).spec);
|
||||
callbackExecuted = true;
|
||||
aCallback(gFaviconService.getFaviconLinkForIcon(aFaviconURI).spec);
|
||||
} catch (e) {
|
||||
Cu.reportError("AboutPermissions: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find favicion for both URIs. Callback will only be called if a
|
||||
// favicon URI is found. We'll ignore the second callback if it is called,
|
||||
// so this means we'll always prefer the https favicon.
|
||||
gFaviconService.getFaviconURLForPage(this.httpsURI, faviconDataCallback);
|
||||
gFaviconService.getFaviconURLForPage(this.httpURI, faviconDataCallback);
|
||||
// Try to find favicon for both URIs, but always prefer the https favicon.
|
||||
gFaviconService.getFaviconURLForPage(this.httpsURI, function (aURI) {
|
||||
if (aURI) {
|
||||
invokeCallback(aURI);
|
||||
} else {
|
||||
gFaviconService.getFaviconURLForPage(this.httpURI, function (aURI) {
|
||||
if (aURI) {
|
||||
invokeCallback(aURI);
|
||||
}
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1192,7 +1192,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
addAppTab: function GroupItem_addAppTab(xulTab, options) {
|
||||
GroupItems.getAppTabFavIconUrl(xulTab, function(iconUrl) {
|
||||
// The tab might have been removed or unpinned while waiting.
|
||||
if (xulTab.closing || !xulTab.parentNode || !xulTab.pinned)
|
||||
if (!Utils.isValidXULTab(xulTab) || !xulTab.pinned)
|
||||
return;
|
||||
|
||||
let self = this;
|
||||
|
@ -636,6 +636,15 @@ let Utils = {
|
||||
return object instanceof Ci.nsIDOMElement;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isValidXULTab
|
||||
// A xulTab is valid if it has not been closed,
|
||||
// and it has not been removed from the DOM
|
||||
// Returns true if the tab is valid.
|
||||
isValidXULTab: function Utils_isValidXULTab(xulTab) {
|
||||
return !xulTab.closing && xulTab.parentNode;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isNumber
|
||||
// Returns true if the argument is a valid number.
|
||||
|
@ -239,7 +239,7 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
// Store persistent for this object.
|
||||
save: function TabItem_save() {
|
||||
try {
|
||||
if (!this.tab || this.tab.parentNode == null || !this._reconnected) // too soon/late to save
|
||||
if (!this.tab || !Utils.isValidXULTab(this.tab) || !this._reconnected) // too soon/late to save
|
||||
return;
|
||||
|
||||
let data = this.getStorageData();
|
||||
@ -571,7 +571,7 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
|
||||
Utils.assert(Utils.isRect(this.bounds), 'TabItem.setBounds: this.bounds is not a real rectangle!');
|
||||
|
||||
if (!this.parent && this.tab.parentNode != null)
|
||||
if (!this.parent && Utils.isValidXULTab(this.tab))
|
||||
this.setTrenches(rect);
|
||||
|
||||
this.save();
|
||||
|
@ -60,7 +60,7 @@ let AllTabs = {
|
||||
},
|
||||
|
||||
get tabs() {
|
||||
return Array.filter(gBrowser.tabs, function (tab) !tab.closing);
|
||||
return Array.filter(gBrowser.tabs, function (tab) Utils.isValidXULTab(tab));
|
||||
},
|
||||
|
||||
register: function AllTabs_register(eventName, callback) {
|
||||
|
@ -1,6 +1,12 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests that, when there is an app tab that references an invalid
|
||||
* favicon, the default favicon appears the group app tab tray, instead of an
|
||||
* empty image that would not be visible.
|
||||
*/
|
||||
|
||||
const fi = Cc["@mozilla.org/browser/favicon-service;1"].
|
||||
getService(Ci.nsIFaviconService);
|
||||
|
||||
@ -40,18 +46,20 @@ function onTabPinned() {
|
||||
// code.
|
||||
executeSoon(function() {
|
||||
let iconSrc = $icon.attr("src");
|
||||
let hasData = true;
|
||||
try {
|
||||
fi.getFaviconDataAsDataURL(iconSrc);
|
||||
} catch(e) {
|
||||
hasData = false;
|
||||
}
|
||||
ok(!hasData, "The icon src doesn't return any data");
|
||||
|
||||
// with moz-anno:favicon automatically redirects to the default favIcon
|
||||
// if the given url is invalid
|
||||
ok(/^moz-anno:favicon:/.test(iconSrc),
|
||||
"The icon url starts with moz-anno:favicon so the default fav icon would be displayed");
|
||||
|
||||
// At this point, as an additional integrity check we could also verify
|
||||
// that the iconSrc URI does not have any associated favicon data. This
|
||||
// kind of check, however, is not easily supported by the asynchronous
|
||||
// favicon API. Fortunately, the fact that we received the error event
|
||||
// already indicates that the original favicon was not available.
|
||||
// Morevover, since we are using a "moz-anno:favicon:" URI, we know that
|
||||
// we'll not display an empty icon, but the default favicon.
|
||||
|
||||
// clean up
|
||||
gBrowser.removeTab(newTab);
|
||||
let endGame = function() {
|
||||
|
@ -791,7 +791,7 @@ let UI = {
|
||||
if (gBrowser.tabs.length > 1) {
|
||||
// Don't return to TabView if there are any app tabs
|
||||
for (let a = 0; a < gBrowser._numPinnedTabs; a++) {
|
||||
if (!gBrowser.tabs[a].closing)
|
||||
if (Utils.isValidXULTab(gBrowser.tabs[a]))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1695,7 +1695,7 @@ let UI = {
|
||||
|
||||
// ----------
|
||||
// Function: getFavIconUrlForTab
|
||||
// Gets fav icon url for the given xul:tab.
|
||||
// Gets the "favicon link URI" for the given xul:tab, or null if unavailable.
|
||||
getFavIconUrlForTab: function UI_getFavIconUrlForTab(tab, callback) {
|
||||
this._isImageDocument(tab, function(isImageDoc) {
|
||||
if (isImageDoc) {
|
||||
@ -1709,12 +1709,20 @@ let UI = {
|
||||
|
||||
callback(tabImage);
|
||||
} else {
|
||||
// determine to load the default/cached icon or not and also ensure we don't show the default icon
|
||||
// for about:-style error pages
|
||||
let url = null;
|
||||
if (this._shouldLoadFavIcon(tab))
|
||||
url = gFavIconService.getFaviconImageForPage(tab.linkedBrowser.currentURI).spec;
|
||||
callback(url);
|
||||
// ensure we don't show the default icon for about:-style error pages
|
||||
if (!this._shouldLoadFavIcon(tab)) {
|
||||
callback(null);
|
||||
} else {
|
||||
// determine to load the default/cached icon or not
|
||||
gFavIconService.getFaviconURLForPage(tab.linkedBrowser.currentURI,
|
||||
function (uri) {
|
||||
if (!uri) {
|
||||
callback(gFavIconService.defaultFavicon.spec);
|
||||
} else {
|
||||
callback(gFavIconService.getFaviconLinkForIcon(uri).spec);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
|
@ -45,545 +45,143 @@ const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
|
||||
let EXPORTED_SYMBOLS = ["DebuggerUI"];
|
||||
|
||||
/**
|
||||
* Creates a pane that will host the debugger UI.
|
||||
* Provides a simple mechanism of managing debugger instances per tab.
|
||||
*
|
||||
* @param nsIDOMWindow aWindow
|
||||
* The chrome window for which the DebuggerUI instance is created.
|
||||
*/
|
||||
function DebuggerPane(aTab) {
|
||||
this._tab = aTab;
|
||||
this._close = this.close.bind(this);
|
||||
this._debugTab = this.debugTab.bind(this);
|
||||
this.breakpoints = {};
|
||||
}
|
||||
|
||||
DebuggerPane.prototype = {
|
||||
/**
|
||||
* Skip editor breakpoint change events.
|
||||
*
|
||||
* This property tells the source editor event handler to skip handling of
|
||||
* the BREAKPOINT_CHANGE events. This is used when the debugger adds/removes
|
||||
* breakpoints from the editor. Typically, the BREAKPOINT_CHANGE event handler
|
||||
* adds/removes events from the debugger, but when breakpoints are added from
|
||||
* the public debugger API, we need to do things in reverse.
|
||||
*
|
||||
* This implementation relies on the fact that the source editor fires the
|
||||
* BREAKPOINT_CHANGE events synchronously.
|
||||
*
|
||||
* @private
|
||||
* @type boolean
|
||||
*/
|
||||
_skipEditorBreakpointChange: false,
|
||||
|
||||
/**
|
||||
* The list of breakpoints in the debugger as tracked by the current
|
||||
* DebuggerPane instance. This an object where the values are BreakpointActor
|
||||
* objects received from the client, while the keys are actor names, for
|
||||
* example "conn0.breakpoint3".
|
||||
*
|
||||
* @type object
|
||||
*/
|
||||
breakpoints: null,
|
||||
|
||||
/**
|
||||
* Creates and initializes the widgets contained in the debugger UI.
|
||||
*/
|
||||
create: function DP_create(gBrowser) {
|
||||
this._tab._scriptDebugger = this;
|
||||
|
||||
this._nbox = gBrowser.getNotificationBox(this._tab.linkedBrowser);
|
||||
this._splitter = gBrowser.parentNode.ownerDocument.createElement("splitter");
|
||||
this._splitter.setAttribute("class", "hud-splitter");
|
||||
this.frame = gBrowser.parentNode.ownerDocument.createElement("iframe");
|
||||
this.frame.height = DebuggerUIPreferences.height;
|
||||
|
||||
this._nbox.appendChild(this._splitter);
|
||||
this._nbox.appendChild(this.frame);
|
||||
|
||||
let self = this;
|
||||
|
||||
this.frame.addEventListener("DOMContentLoaded", function initPane(aEvent) {
|
||||
if (aEvent.target != self.frame.contentDocument) {
|
||||
return;
|
||||
}
|
||||
self.frame.removeEventListener("DOMContentLoaded", initPane, true);
|
||||
// Initialize the source editor.
|
||||
self.frame.contentWindow.editor = self.editor = new SourceEditor();
|
||||
self.frame.contentWindow.updateEditorBreakpoints =
|
||||
self._updateEditorBreakpoints.bind(self);
|
||||
|
||||
let config = {
|
||||
mode: SourceEditor.MODES.JAVASCRIPT,
|
||||
showLineNumbers: true,
|
||||
readOnly: true,
|
||||
showAnnotationRuler: true,
|
||||
showOverviewRuler: true,
|
||||
};
|
||||
|
||||
let editorPlaceholder = self.frame.contentDocument.getElementById("editor");
|
||||
self.editor.init(editorPlaceholder, config, self._onEditorLoad.bind(self));
|
||||
}, true);
|
||||
this.frame.addEventListener("DebuggerClose", this._close, true);
|
||||
|
||||
this.frame.setAttribute("src", "chrome://browser/content/debugger.xul");
|
||||
},
|
||||
|
||||
/**
|
||||
* The load event handler for the source editor. This method does post-load
|
||||
* editor initialization.
|
||||
*/
|
||||
_onEditorLoad: function DP__onEditorLoad() {
|
||||
this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
|
||||
this._onEditorBreakpointChange.bind(this));
|
||||
// Connect to the debugger server.
|
||||
this.connect();
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for breakpoint changes that happen in the editor. This
|
||||
* function syncs the breakpoint changes in the editor to those in the
|
||||
* debugger.
|
||||
*
|
||||
* @private
|
||||
* @param object aEvent
|
||||
* The SourceEditor.EVENTS.BREAKPOINT_CHANGE event object.
|
||||
*/
|
||||
_onEditorBreakpointChange: function DP__onEditorBreakpointChange(aEvent) {
|
||||
if (this._skipEditorBreakpointChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
aEvent.added.forEach(this._onEditorBreakpointAdd, this);
|
||||
aEvent.removed.forEach(this._onEditorBreakpointRemove, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the URL of the selected script in the debugger view.
|
||||
*
|
||||
* @private
|
||||
* @return string
|
||||
* The URL of the selected script.
|
||||
*/
|
||||
_selectedScript: function DP__selectedScript() {
|
||||
return this.debuggerWindow ?
|
||||
this.debuggerWindow.DebuggerView.Scripts.selected : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for new breakpoints that come from the editor.
|
||||
*
|
||||
* @private
|
||||
* @param object aBreakpoint
|
||||
* The breakpoint object coming from the editor.
|
||||
*/
|
||||
_onEditorBreakpointAdd: function DP__onEditorBreakpointAdd(aBreakpoint) {
|
||||
let location = {
|
||||
url: this._selectedScript(),
|
||||
line: aBreakpoint.line + 1,
|
||||
};
|
||||
|
||||
if (location.url) {
|
||||
let callback = function (aClient, aError) {
|
||||
if (aError) {
|
||||
this._skipEditorBreakpointChange = true;
|
||||
let result = this.editor.removeBreakpoint(aBreakpoint.line);
|
||||
this._skipEditorBreakpointChange = false;
|
||||
}
|
||||
}.bind(this);
|
||||
this.addBreakpoint(location, callback, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for breakpoints that are removed from the editor.
|
||||
*
|
||||
* @private
|
||||
* @param object aBreakpoint
|
||||
* The breakpoint object that was removed from the editor.
|
||||
*/
|
||||
_onEditorBreakpointRemove: function DP__onEditorBreakpointRemove(aBreakpoint) {
|
||||
let url = this._selectedScript();
|
||||
let line = aBreakpoint.line + 1;
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
let breakpoint = this.getBreakpoint(url, line);
|
||||
if (breakpoint) {
|
||||
this.removeBreakpoint(breakpoint, null, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the breakpoints in the editor view. This function takes the list of
|
||||
* breakpoints in the debugger and adds them back into the editor view. This
|
||||
* is invoked when the selected script is changed.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_updateEditorBreakpoints: function DP__updateEditorBreakpoints()
|
||||
{
|
||||
let url = this._selectedScript();
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._skipEditorBreakpointChange = true;
|
||||
for each (let breakpoint in this.breakpoints) {
|
||||
if (breakpoint.location.url == url) {
|
||||
this.editor.addBreakpoint(breakpoint.location.line - 1);
|
||||
}
|
||||
}
|
||||
this._skipEditorBreakpointChange = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a breakpoint.
|
||||
*
|
||||
* @param object aLocation
|
||||
* The location where you want the breakpoint. This object must have
|
||||
* two properties:
|
||||
* - url - the URL of the script.
|
||||
* - line - the line number (starting from 1).
|
||||
* @param function [aCallback]
|
||||
* Optional function to invoke once the breakpoint is added. The
|
||||
* callback is invoked with two arguments:
|
||||
* - aBreakpointClient - the BreakpointActor client object, if the
|
||||
* breakpoint has been added successfully.
|
||||
* - aResponseError - if there was any error.
|
||||
* @param boolean [aNoEditorUpdate=false]
|
||||
* Tells if you want to skip editor updates. Typically the editor is
|
||||
* updated to visually indicate that a breakpoint has been added.
|
||||
*/
|
||||
addBreakpoint:
|
||||
function DP_addBreakpoint(aLocation, aCallback, aNoEditorUpdate) {
|
||||
let breakpoint = this.getBreakpoint(aLocation.url, aLocation.line);
|
||||
if (breakpoint) {
|
||||
aCallback && aCallback(breakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeThread.setBreakpoint(aLocation, function(aResponse, aBpClient) {
|
||||
if (!aResponse.error) {
|
||||
this.breakpoints[aBpClient.actor] = aBpClient;
|
||||
|
||||
if (!aNoEditorUpdate) {
|
||||
let url = this._selectedScript();
|
||||
if (url == aLocation.url) {
|
||||
this._skipEditorBreakpointChange = true;
|
||||
this.editor.addBreakpoint(aLocation.line - 1);
|
||||
this._skipEditorBreakpointChange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aCallback && aCallback(aBpClient, aResponse.error);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a breakpoint.
|
||||
*
|
||||
* @param object aBreakpoint
|
||||
* The breakpoint you want to remove.
|
||||
* @param function [aCallback]
|
||||
* Optional function to invoke once the breakpoint is removed. The
|
||||
* callback is invoked with one argument: the breakpoint location
|
||||
* object which holds the url and line properties.
|
||||
* @param boolean [aNoEditorUpdate=false]
|
||||
* Tells if you want to skip editor updates. Typically the editor is
|
||||
* updated to visually indicate that a breakpoint has been removed.
|
||||
*/
|
||||
removeBreakpoint:
|
||||
function DP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate) {
|
||||
if (!(aBreakpoint.actor in this.breakpoints)) {
|
||||
aCallback && aCallback(aBreakpoint.location);
|
||||
return;
|
||||
}
|
||||
|
||||
aBreakpoint.remove(function() {
|
||||
delete this.breakpoints[aBreakpoint.actor];
|
||||
|
||||
if (!aNoEditorUpdate) {
|
||||
let url = this._selectedScript();
|
||||
if (url == aBreakpoint.location.url) {
|
||||
this._skipEditorBreakpointChange = true;
|
||||
this.editor.removeBreakpoint(aBreakpoint.location.line - 1);
|
||||
this._skipEditorBreakpointChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
aCallback && aCallback(aBreakpoint.location);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the breakpoint object at the given location.
|
||||
*
|
||||
* @param string aUrl
|
||||
* The URL of where the breakpoint is.
|
||||
* @param number aLine
|
||||
* The line number where the breakpoint is.
|
||||
* @return object
|
||||
* The BreakpointActor object.
|
||||
*/
|
||||
getBreakpoint: function DP_getBreakpoint(aUrl, aLine) {
|
||||
for each (let breakpoint in this.breakpoints) {
|
||||
if (breakpoint.location.url == aUrl && breakpoint.location.line == aLine) {
|
||||
return breakpoint;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Closes the debugger UI removing child nodes and event listeners.
|
||||
*/
|
||||
close: function DP_close() {
|
||||
for each (let breakpoint in this.breakpoints) {
|
||||
this.removeBreakpoint(breakpoint);
|
||||
}
|
||||
|
||||
if (this._tab) {
|
||||
this._tab._scriptDebugger = null;
|
||||
this._tab = null;
|
||||
}
|
||||
if (this.frame) {
|
||||
DebuggerUIPreferences.height = this.frame.height;
|
||||
|
||||
this.frame.removeEventListener("unload", this._close, true);
|
||||
this.frame.removeEventListener("DebuggerClose", this._close, true);
|
||||
if (this.frame.parentNode) {
|
||||
this.frame.parentNode.removeChild(this.frame);
|
||||
}
|
||||
this.frame = null;
|
||||
}
|
||||
if (this._nbox) {
|
||||
this._nbox.removeChild(this._splitter);
|
||||
this._nbox = null;
|
||||
}
|
||||
|
||||
this._splitter = null;
|
||||
|
||||
if (this._client) {
|
||||
this._client.removeListener("newScript", this.onNewScript);
|
||||
this._client.removeListener("tabDetached", this._close);
|
||||
this._client.removeListener("tabNavigated", this._debugTab);
|
||||
this._client.close();
|
||||
this._client = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a debugger client and connects it to the debugger server,
|
||||
* wiring event handlers as necessary.
|
||||
*/
|
||||
connect: function DP_connect() {
|
||||
this.frame.addEventListener("unload", this._close, true);
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
this._client = new DebuggerClient(transport);
|
||||
// Store the new script handler locally, so when it's time to remove it we
|
||||
// don't need to go through the iframe, since it might be cleared.
|
||||
this.onNewScript = this.debuggerWindow.SourceScripts.onNewScript;
|
||||
let self = this;
|
||||
this._client.addListener("tabNavigated", this._debugTab);
|
||||
this._client.addListener("tabDetached", this._close);
|
||||
this._client.addListener("newScript", this.onNewScript);
|
||||
this._client.connect(function(aType, aTraits) {
|
||||
self._client.listTabs(function(aResponse) {
|
||||
let tab = aResponse.tabs[aResponse.selected];
|
||||
self.debuggerWindow.startDebuggingTab(self._client, tab);
|
||||
if (self.onConnected) {
|
||||
self.onConnected(self);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts debugging the current tab. This function is called on each location
|
||||
* change in this tab.
|
||||
*/
|
||||
debugTab: function DP_debugTab(aNotification, aPacket) {
|
||||
let self = this;
|
||||
this._client.activeThread.detach(function() {
|
||||
self._client.activeTab.detach(function() {
|
||||
self._client.listTabs(function(aResponse) {
|
||||
let tab = aResponse.tabs[aResponse.selected];
|
||||
self.debuggerWindow.startDebuggingTab(self._client, tab);
|
||||
if (self.onConnected) {
|
||||
self.onConnected(self);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
get debuggerWindow() {
|
||||
return this.frame ? this.frame.contentWindow : null;
|
||||
},
|
||||
|
||||
get debuggerClient() {
|
||||
return this._client;
|
||||
},
|
||||
|
||||
get activeThread() {
|
||||
try {
|
||||
return this.debuggerWindow.ThreadState.activeThread;
|
||||
} catch(ex) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function DebuggerUI(aWindow) {
|
||||
this.aWindow = aWindow;
|
||||
|
||||
aWindow.addEventListener("Debugger:LoadSource", this._onLoadSource.bind(this));
|
||||
this.chromeWindow = aWindow;
|
||||
}
|
||||
|
||||
DebuggerUI.prototype = {
|
||||
/**
|
||||
* Starts the debugger or stops it, if it is already started.
|
||||
*/
|
||||
toggleDebugger: function DebuggerUI_toggleDebugger() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
|
||||
let gBrowser = this.aWindow.gBrowser;
|
||||
let tab = gBrowser.selectedTab;
|
||||
/**
|
||||
* Starts a debugger for the current tab, or stops it if already started.
|
||||
* @return DebuggerPane if the debugger is started, null if it's stopped.
|
||||
*/
|
||||
toggleDebugger: function DUI_toggleDebugger() {
|
||||
let tab = this.chromeWindow.gBrowser.selectedTab;
|
||||
|
||||
if (tab._scriptDebugger) {
|
||||
// If the debugger is already open, just close it.
|
||||
tab._scriptDebugger.close();
|
||||
return tab._scriptDebugger;
|
||||
return null;
|
||||
}
|
||||
|
||||
let pane = new DebuggerPane(tab);
|
||||
pane.create(gBrowser);
|
||||
return pane;
|
||||
return new DebuggerPane(tab);
|
||||
},
|
||||
|
||||
getDebugger: function DebuggerUI_getDebugger(aTab) {
|
||||
/**
|
||||
* Get the debugger for a specified tab.
|
||||
* @return DebuggerPane if a debugger exists for the tab, null otherwise
|
||||
*/
|
||||
getDebugger: function DUI_getDebugger(aTab) {
|
||||
return aTab._scriptDebugger;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the preferences associated with the debugger frontend.
|
||||
* @return object
|
||||
*/
|
||||
get preferences() {
|
||||
return DebuggerUIPreferences;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a pane that will host the debugger.
|
||||
*
|
||||
* @param XULElement aTab
|
||||
* The tab in which to create the debugger.
|
||||
*/
|
||||
function DebuggerPane(aTab) {
|
||||
this._tab = aTab;
|
||||
this._create();
|
||||
}
|
||||
|
||||
DebuggerPane.prototype = {
|
||||
|
||||
/**
|
||||
* Creates and initializes the widgets containing the debugger UI.
|
||||
*/
|
||||
_create: function DP__create() {
|
||||
this._tab._scriptDebugger = this;
|
||||
|
||||
let gBrowser = this._tab.linkedBrowser.getTabBrowser();
|
||||
let ownerDocument = gBrowser.parentNode.ownerDocument;
|
||||
|
||||
this._splitter = ownerDocument.createElement("splitter");
|
||||
this._splitter.setAttribute("class", "hud-splitter");
|
||||
|
||||
this._frame = ownerDocument.createElement("iframe");
|
||||
this._frame.height = DebuggerUIPreferences.height;
|
||||
|
||||
this._nbox = gBrowser.getNotificationBox(this._tab.linkedBrowser);
|
||||
this._nbox.appendChild(this._splitter);
|
||||
this._nbox.appendChild(this._frame);
|
||||
|
||||
this.close = this.close.bind(this);
|
||||
let self = this;
|
||||
|
||||
this._frame.addEventListener("Debugger:Loaded", function dbgLoaded() {
|
||||
self._frame.removeEventListener("Debugger:Loaded", dbgLoaded, true);
|
||||
self._frame.addEventListener("Debugger:Close", self.close, true);
|
||||
self._frame.addEventListener("unload", self.close, true);
|
||||
|
||||
// Bind shortcuts for accessing the breakpoint methods in the debugger.
|
||||
let bkp = self.debuggerWindow.DebuggerController.Breakpoints;
|
||||
self.addBreakpoint = bkp.addBreakpoint;
|
||||
self.removeBreakpoint = bkp.removeBreakpoint;
|
||||
self.getBreakpoint = bkp.getBreakpoint;
|
||||
}, true);
|
||||
|
||||
this._frame.setAttribute("src", "chrome://browser/content/debugger.xul");
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles notifications to load a source script from the cache or from a
|
||||
* local file.
|
||||
* XXX: it may be better to use nsITraceableChannel to get to the sources
|
||||
* without relying on caching when we can (not for eval, etc.):
|
||||
* http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
|
||||
* Closes the debugger, removing child nodes and event listeners.
|
||||
*/
|
||||
_onLoadSource: function DebuggerUI__onLoadSource(aEvent) {
|
||||
let gBrowser = this.aWindow.gBrowser;
|
||||
|
||||
let url = aEvent.detail.url;
|
||||
let showOptions = aEvent.detail.options;
|
||||
let scheme = Services.io.extractScheme(url);
|
||||
switch (scheme) {
|
||||
case "file":
|
||||
case "chrome":
|
||||
case "resource":
|
||||
try {
|
||||
NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) {
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
return this.logError(url, aStatus);
|
||||
}
|
||||
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
|
||||
aStream.close();
|
||||
this._onSourceLoaded(url, source, showOptions);
|
||||
}.bind(this));
|
||||
} catch (ex) {
|
||||
return this.logError(url, ex.name);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
let channel = Services.io.newChannel(url, null, null);
|
||||
let chunks = [];
|
||||
let streamListener = { // nsIStreamListener inherits nsIRequestObserver
|
||||
onStartRequest: function (aRequest, aContext, aStatusCode) {
|
||||
if (!Components.isSuccessCode(aStatusCode)) {
|
||||
return this.logError(url, aStatusCode);
|
||||
}
|
||||
}.bind(this),
|
||||
onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
|
||||
chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
|
||||
},
|
||||
onStopRequest: function (aRequest, aContext, aStatusCode) {
|
||||
if (!Components.isSuccessCode(aStatusCode)) {
|
||||
return this.logError(url, aStatusCode);
|
||||
}
|
||||
|
||||
this._onSourceLoaded(url, chunks.join(""), channel.contentType,
|
||||
showOptions);
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
channel.loadFlags = channel.LOAD_FROM_CACHE;
|
||||
channel.asyncOpen(streamListener, null);
|
||||
break;
|
||||
close: function DP_close() {
|
||||
if (!this._tab) {
|
||||
return;
|
||||
}
|
||||
this._tab._scriptDebugger = null;
|
||||
this._tab = null;
|
||||
|
||||
DebuggerUIPreferences.height = this._frame.height;
|
||||
this._frame.removeEventListener("Debugger:Close", this.close, true);
|
||||
this._frame.removeEventListener("unload", this.close, true);
|
||||
|
||||
this._nbox.removeChild(this._splitter);
|
||||
this._nbox.removeChild(this._frame);
|
||||
|
||||
this._splitter = null;
|
||||
this._frame = null;
|
||||
this._nbox = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Log an error message in the error console when a script fails to load.
|
||||
*
|
||||
* @param string aUrl
|
||||
* The URL of the source script.
|
||||
* @param string aStatus
|
||||
* The failure status code.
|
||||
* Gets the debugger content window.
|
||||
* @return nsIDOMWindow if a debugger window exists, null otherwise
|
||||
*/
|
||||
logError: function DebuggerUI_logError(aUrl, aStatus) {
|
||||
let view = this.getDebugger(gBrowser.selectedTab).DebuggerView;
|
||||
Components.utils.reportError(view.getFormatStr("loadingError", [ aUrl, aStatus ]));
|
||||
get debuggerWindow() {
|
||||
return this._frame ? this._frame.contentWindow : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when source has been loaded.
|
||||
*
|
||||
* @private
|
||||
* @param string aSourceUrl
|
||||
* The URL of the source script.
|
||||
* @param string aSourceText
|
||||
* The text of the source script.
|
||||
* @param string aContentType
|
||||
* The content type of the source script.
|
||||
* @param object [aOptions]
|
||||
* Additional options for showing the script (optional). Supported
|
||||
* options:
|
||||
* - targetLine: place the editor at the given line number.
|
||||
* Shortcut for accessing the list of breakpoints in the debugger.
|
||||
* @return object if a debugger window exists, null otherwise
|
||||
*/
|
||||
_onSourceLoaded: function DebuggerUI__onSourceLoaded(aSourceUrl,
|
||||
aSourceText,
|
||||
aContentType,
|
||||
aOptions) {
|
||||
let dbg = this.getDebugger(this.aWindow.gBrowser.selectedTab);
|
||||
let doc = dbg.frame.contentDocument;
|
||||
let scripts = doc.getElementById("scripts");
|
||||
let elt = scripts.getElementsByAttribute("value", aSourceUrl)[0];
|
||||
let script = elt.getUserData("sourceScript");
|
||||
script.loaded = true;
|
||||
script.text = aSourceText;
|
||||
script.contentType = aContentType;
|
||||
elt.setUserData("sourceScript", script, null);
|
||||
|
||||
dbg.debuggerWindow.SourceScripts._onShowScript(script, aOptions);
|
||||
get breakpoints() {
|
||||
let debuggerWindow = this.debuggerWindow;
|
||||
if (debuggerWindow) {
|
||||
return debuggerWindow.DebuggerController.Breakpoints.store;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -592,16 +190,12 @@ DebuggerUI.prototype = {
|
||||
*/
|
||||
let DebuggerUIPreferences = {
|
||||
|
||||
_height: -1,
|
||||
|
||||
/**
|
||||
* Gets the preferred height of the debugger pane.
|
||||
*
|
||||
* @return number
|
||||
* The preferred height.
|
||||
*/
|
||||
get height() {
|
||||
if (this._height < 0) {
|
||||
if (this._height === undefined) {
|
||||
this._height = Services.prefs.getIntPref("devtools.debugger.ui.height");
|
||||
}
|
||||
return this._height;
|
||||
@ -609,9 +203,7 @@ let DebuggerUIPreferences = {
|
||||
|
||||
/**
|
||||
* Sets the preferred height of the debugger pane.
|
||||
*
|
||||
* @param number value
|
||||
* The new height.
|
||||
*/
|
||||
set height(value) {
|
||||
Services.prefs.setIntPref("devtools.debugger.ui.height", value);
|
||||
|
1282
browser/devtools/debugger/debugger-controller.js
Normal file
@ -1,696 +0,0 @@
|
||||
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Panos Astithas <past@mozilla.com>
|
||||
* Victor Porof <vporof@mozilla.com>
|
||||
* Mihai Sucan <mihai.sucan@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource:///modules/source-editor.jsm");
|
||||
|
||||
var gInitialized = false;
|
||||
var gClient = null;
|
||||
var gTabClient = null;
|
||||
|
||||
|
||||
function initDebugger()
|
||||
{
|
||||
window.removeEventListener("DOMContentLoaded", initDebugger, false);
|
||||
if (gInitialized) {
|
||||
return;
|
||||
}
|
||||
gInitialized = true;
|
||||
|
||||
DebuggerView.Stackframes.initialize();
|
||||
DebuggerView.Properties.initialize();
|
||||
DebuggerView.Scripts.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by chrome to set up a debugging session.
|
||||
*
|
||||
* @param DebuggerClient aClient
|
||||
* The debugger client.
|
||||
* @param object aTabGrip
|
||||
* The remote protocol grip of the tab.
|
||||
*/
|
||||
function startDebuggingTab(aClient, aTabGrip)
|
||||
{
|
||||
gClient = aClient;
|
||||
|
||||
gClient.attachTab(aTabGrip.actor, function(aResponse, aTabClient) {
|
||||
if (aTabClient) {
|
||||
gTabClient = aTabClient;
|
||||
gClient.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) {
|
||||
if (!aThreadClient) {
|
||||
Components.utils.reportError("Couldn't attach to thread: " +
|
||||
aResponse.error);
|
||||
return;
|
||||
}
|
||||
ThreadState.connect(aThreadClient, function() {
|
||||
StackFrames.connect(aThreadClient, function() {
|
||||
SourceScripts.connect(aThreadClient, function() {
|
||||
aThreadClient.resume();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function shutdownDebugger()
|
||||
{
|
||||
window.removeEventListener("unload", shutdownDebugger, false);
|
||||
|
||||
SourceScripts.disconnect();
|
||||
StackFrames.disconnect();
|
||||
ThreadState.disconnect();
|
||||
ThreadState.activeThread = false;
|
||||
|
||||
DebuggerView.Stackframes.destroy();
|
||||
DebuggerView.Properties.destroy();
|
||||
DebuggerView.Scripts.destroy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ThreadState keeps the UI up to date with the state of the
|
||||
* thread (paused/attached/etc.).
|
||||
*/
|
||||
var ThreadState = {
|
||||
activeThread: null,
|
||||
|
||||
/**
|
||||
* Connect to a thread client.
|
||||
* @param object aThreadClient
|
||||
* The thread client.
|
||||
* @param function aCallback
|
||||
* The next function in the initialization sequence.
|
||||
*/
|
||||
connect: function TS_connect(aThreadClient, aCallback) {
|
||||
this.activeThread = aThreadClient;
|
||||
aThreadClient.addListener("paused", ThreadState.update);
|
||||
aThreadClient.addListener("resumed", ThreadState.update);
|
||||
aThreadClient.addListener("detached", ThreadState.update);
|
||||
this.update();
|
||||
aCallback && aCallback();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the UI after a thread state change.
|
||||
*/
|
||||
update: function TS_update(aEvent) {
|
||||
DebuggerView.Stackframes.updateState(this.activeThread.state);
|
||||
if (aEvent == "detached") {
|
||||
ThreadState.activeThread = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect from the client.
|
||||
*/
|
||||
disconnect: function TS_disconnect() {
|
||||
this.activeThread.removeListener("paused", ThreadState.update);
|
||||
this.activeThread.removeListener("resumed", ThreadState.update);
|
||||
this.activeThread.removeListener("detached", ThreadState.update);
|
||||
}
|
||||
};
|
||||
|
||||
ThreadState.update = ThreadState.update.bind(ThreadState);
|
||||
|
||||
/**
|
||||
* Keeps the stack frame list up-to-date, using the thread client's
|
||||
* stack frame cache.
|
||||
*/
|
||||
var StackFrames = {
|
||||
pageSize: 25,
|
||||
activeThread: null,
|
||||
selectedFrame: null,
|
||||
|
||||
/**
|
||||
* Watch a given thread client.
|
||||
* @param object aThreadClient
|
||||
* The thread client.
|
||||
* @param function aCallback
|
||||
* The next function in the initialization sequence.
|
||||
*/
|
||||
connect: function SF_connect(aThreadClient, aCallback) {
|
||||
DebuggerView.Stackframes.addClickListener(this.onClick);
|
||||
|
||||
this.activeThread = aThreadClient;
|
||||
aThreadClient.addListener("paused", this.onPaused);
|
||||
aThreadClient.addListener("resumed", this.onResume);
|
||||
aThreadClient.addListener("framesadded", this.onFrames);
|
||||
aThreadClient.addListener("framescleared", this.onFramesCleared);
|
||||
this.onFramesCleared();
|
||||
aCallback && aCallback();
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect from the client.
|
||||
*/
|
||||
disconnect: function TS_disconnect() {
|
||||
this.activeThread.removeListener("paused", this.onPaused);
|
||||
this.activeThread.removeListener("resumed", this.onResume);
|
||||
this.activeThread.removeListener("framesadded", this.onFrames);
|
||||
this.activeThread.removeListener("framescleared", this.onFramesCleared);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the thread client's paused notification.
|
||||
*/
|
||||
onPaused: function SF_onPaused() {
|
||||
this.activeThread.fillFrames(this.pageSize);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the thread client's resumed notification.
|
||||
*/
|
||||
onResume: function SF_onResume() {
|
||||
window.editor.setDebugLocation(-1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the thread client's framesadded notification.
|
||||
*/
|
||||
onFrames: function SF_onFrames() {
|
||||
DebuggerView.Stackframes.empty();
|
||||
|
||||
for each (let frame in this.activeThread.cachedFrames) {
|
||||
this._addFramePanel(frame);
|
||||
}
|
||||
|
||||
if (this.activeThread.moreFrames) {
|
||||
DebuggerView.Stackframes.dirty = true;
|
||||
}
|
||||
|
||||
if (!this.selectedFrame) {
|
||||
this.selectFrame(0);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the thread client's framescleared notification.
|
||||
*/
|
||||
onFramesCleared: function SF_onFramesCleared() {
|
||||
DebuggerView.Stackframes.emptyText();
|
||||
this.selectedFrame = null;
|
||||
// Clear the properties as well.
|
||||
DebuggerView.Properties.localScope.empty();
|
||||
DebuggerView.Properties.globalScope.empty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for clicks on stack frames.
|
||||
*/
|
||||
onClick: function SF_onClick(aEvent) {
|
||||
let target = aEvent.target;
|
||||
while (target) {
|
||||
if (target.stackFrame) {
|
||||
this.selectFrame(target.stackFrame.depth);
|
||||
return;
|
||||
}
|
||||
target = target.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Marks the stack frame in the specified depth as selected and updates the
|
||||
* properties view with the stack frame's data.
|
||||
*
|
||||
* @param number aDepth
|
||||
* The depth of the frame in the stack.
|
||||
*/
|
||||
selectFrame: function SF_selectFrame(aDepth) {
|
||||
if (this.selectedFrame !== null) {
|
||||
DebuggerView.Stackframes.highlightFrame(this.selectedFrame, false);
|
||||
}
|
||||
|
||||
this.selectedFrame = aDepth;
|
||||
if (aDepth !== null) {
|
||||
DebuggerView.Stackframes.highlightFrame(this.selectedFrame, true);
|
||||
}
|
||||
|
||||
let frame = this.activeThread.cachedFrames[aDepth];
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the editor's caret to the proper line.
|
||||
if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) {
|
||||
window.editor.setCaretPosition(frame.where.line - 1);
|
||||
window.editor.setDebugLocation(frame.where.line - 1);
|
||||
} else if (DebuggerView.Scripts.contains(frame.where.url)) {
|
||||
DebuggerView.Scripts.selectScript(frame.where.url);
|
||||
window.editor.setCaretPosition(frame.where.line - 1);
|
||||
} else {
|
||||
window.editor.setDebugLocation(-1);
|
||||
}
|
||||
|
||||
// Display the local variables.
|
||||
let localScope = DebuggerView.Properties.localScope;
|
||||
localScope.empty();
|
||||
// Add "this".
|
||||
if (frame["this"]) {
|
||||
let thisVar = localScope.addVar("this");
|
||||
thisVar.setGrip({ "type": frame["this"].type,
|
||||
"class": frame["this"].class });
|
||||
this._addExpander(thisVar, frame["this"]);
|
||||
}
|
||||
|
||||
if (frame.arguments && frame.arguments.length > 0) {
|
||||
// Add "arguments".
|
||||
let argsVar = localScope.addVar("arguments");
|
||||
argsVar.setGrip({ "type": "object", "class": "Arguments" });
|
||||
this._addExpander(argsVar, frame.arguments);
|
||||
|
||||
// Add variables for every argument.
|
||||
let objClient = this.activeThread.pauseGrip(frame.callee);
|
||||
objClient.getSignature(function SF_getSignature(aResponse) {
|
||||
for (let i = 0; i < aResponse.parameters.length; i++) {
|
||||
let param = aResponse.parameters[i];
|
||||
let paramVar = localScope.addVar(param);
|
||||
let paramVal = frame.arguments[i];
|
||||
paramVar.setGrip(paramVal);
|
||||
this._addExpander(paramVar, paramVal);
|
||||
}
|
||||
// Signal that call parameters have been fetched.
|
||||
let evt = document.createEvent("Event");
|
||||
evt.initEvent("Debugger:FetchedParameters", true, false);
|
||||
document.documentElement.dispatchEvent(evt);
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the source editor current debug location based on the selected frame
|
||||
* and script.
|
||||
*/
|
||||
updateEditor: function SF_updateEditor() {
|
||||
if (this.selectedFrame === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let frame = this.activeThread.cachedFrames[this.selectedFrame];
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the editor's caret to the proper line.
|
||||
if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) {
|
||||
window.editor.setDebugLocation(frame.where.line - 1);
|
||||
} else {
|
||||
window.editor.setDebugLocation(-1);
|
||||
}
|
||||
},
|
||||
|
||||
_addExpander: function SF_addExpander(aVar, aObject) {
|
||||
// No need for expansion for null and undefined values, but we do need them
|
||||
// for frame.arguments which is a regular array.
|
||||
if (!aObject || typeof aObject != "object" ||
|
||||
(aObject.type != "object" && !Array.isArray(aObject))) {
|
||||
return;
|
||||
}
|
||||
// Add a dummy property to force the twisty to show up.
|
||||
aVar.addProperties({ " ": { value: " " }});
|
||||
aVar.onexpand = this._addVarProperties.bind(this, aVar, aObject);
|
||||
},
|
||||
|
||||
_addVarProperties: function SF_addVarProperties(aVar, aObject) {
|
||||
// Retrieve the properties only once.
|
||||
if (aVar.fetched) {
|
||||
return;
|
||||
}
|
||||
// Clear the placeholder property put in place to display the twisty.
|
||||
aVar.empty();
|
||||
|
||||
// For arrays we have to construct a grip-like object to pass into
|
||||
// addProperties.
|
||||
if (Array.isArray(aObject)) {
|
||||
let properties = { length: { writable: true, value: aObject.length } };
|
||||
for (let i = 0; i < aObject.length; i++) {
|
||||
properties[i + ""] = { value: aObject[i] };
|
||||
}
|
||||
aVar.addProperties(properties);
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
for (let i = 0; i < aObject.length; i++) {
|
||||
this._addExpander(aVar[i + ""], aObject[i]);
|
||||
}
|
||||
aVar.fetched = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let objClient = this.activeThread.pauseGrip(aObject);
|
||||
objClient.getPrototypeAndProperties(function SF_onProtoAndProps(aResponse) {
|
||||
// Add __proto__.
|
||||
if (aResponse.prototype.type != "null") {
|
||||
let properties = {};
|
||||
properties["__proto__ "] = { value: aResponse.prototype };
|
||||
aVar.addProperties(properties);
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
this._addExpander(aVar["__proto__ "], aResponse.prototype);
|
||||
}
|
||||
|
||||
// Sort the rest of the properties before adding them, for better UX.
|
||||
let properties = {};
|
||||
for each (let prop in Object.keys(aResponse.ownProperties).sort()) {
|
||||
properties[prop] = aResponse.ownProperties[prop];
|
||||
}
|
||||
aVar.addProperties(properties);
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
for (let prop in aResponse.ownProperties) {
|
||||
this._addExpander(aVar[prop], aResponse.ownProperties[prop].value);
|
||||
}
|
||||
|
||||
aVar.fetched = true;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the specified stack frame to the list.
|
||||
*
|
||||
* @param Debugger.Frame aFrame
|
||||
* The new frame to add.
|
||||
*/
|
||||
_addFramePanel: function SF_addFramePanel(aFrame) {
|
||||
let depth = aFrame.depth;
|
||||
let label = SourceScripts._getScriptLabel(aFrame.where.url);
|
||||
|
||||
let startText = this._frameTitle(aFrame);
|
||||
let endText = label + ":" + aFrame.where.line;
|
||||
|
||||
let panel = DebuggerView.Stackframes.addFrame(depth, startText, endText);
|
||||
|
||||
if (panel) {
|
||||
panel.stackFrame = aFrame;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads more stack frames from the debugger server cache.
|
||||
*/
|
||||
_addMoreFrames: function SF_addMoreFrames() {
|
||||
this.activeThread.fillFrames(
|
||||
this.activeThread.cachedFrames.length + this.pageSize);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a textual representation for the stack frame specified, for
|
||||
* displaying in the stack frame list.
|
||||
*
|
||||
* @param Debugger.Frame aFrame
|
||||
* The stack frame to label.
|
||||
*/
|
||||
_frameTitle: function SF_frameTitle(aFrame) {
|
||||
if (aFrame.type == "call") {
|
||||
return aFrame["calleeName"] ? aFrame["calleeName"] : "(anonymous)";
|
||||
}
|
||||
|
||||
return "(" + aFrame.type + ")";
|
||||
}
|
||||
};
|
||||
|
||||
StackFrames.onPaused = StackFrames.onPaused.bind(StackFrames);
|
||||
StackFrames.onResume = StackFrames.onResume.bind(StackFrames);
|
||||
StackFrames.onFrames = StackFrames.onFrames.bind(StackFrames);
|
||||
StackFrames.onFramesCleared = StackFrames.onFramesCleared.bind(StackFrames);
|
||||
StackFrames.onClick = StackFrames.onClick.bind(StackFrames);
|
||||
|
||||
/**
|
||||
* Keeps the source script list up-to-date, using the thread client's
|
||||
* source script cache.
|
||||
*/
|
||||
var SourceScripts = {
|
||||
pageSize: 25,
|
||||
activeThread: null,
|
||||
_labelsCache: null,
|
||||
|
||||
/**
|
||||
* Watch a given thread client.
|
||||
* @param object aThreadClient
|
||||
* The thread client.
|
||||
* @param function aCallback
|
||||
* The next function in the initialization sequence.
|
||||
*/
|
||||
connect: function SS_connect(aThreadClient, aCallback) {
|
||||
DebuggerView.Scripts.addChangeListener(this.onChange);
|
||||
|
||||
this.activeThread = aThreadClient;
|
||||
aThreadClient.addListener("scriptsadded", this.onScripts);
|
||||
aThreadClient.addListener("scriptscleared", this.onScriptsCleared);
|
||||
this.clearLabelsCache();
|
||||
this.onScriptsCleared();
|
||||
// Retrieve the list of scripts known to the server from before the client
|
||||
// was ready to handle new script notifications.
|
||||
this.activeThread.fillScripts();
|
||||
aCallback && aCallback();
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect from the client.
|
||||
*/
|
||||
disconnect: function TS_disconnect() {
|
||||
this.activeThread.removeListener("scriptsadded", this.onScripts);
|
||||
this.activeThread.removeListener("scriptscleared", this.onScriptsCleared);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the debugger client's unsolicited newScript notification.
|
||||
*/
|
||||
onNewScript: function SS_onNewScript(aNotification, aPacket) {
|
||||
this._addScript({ url: aPacket.url, startLine: aPacket.startLine });
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the thread client's scriptsadded notification.
|
||||
*/
|
||||
onScripts: function SS_onScripts() {
|
||||
for each (let script in this.activeThread.cachedScripts) {
|
||||
this._addScript(script);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the thread client's scriptscleared notification.
|
||||
*/
|
||||
onScriptsCleared: function SS_onScriptsCleared() {
|
||||
DebuggerView.Scripts.empty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for changes on the selected source script.
|
||||
*/
|
||||
onChange: function SS_onChange(aEvent) {
|
||||
let scripts = aEvent.target;
|
||||
if (!scripts.selectedItem) {
|
||||
return;
|
||||
}
|
||||
let script = scripts.selectedItem.getUserData("sourceScript");
|
||||
this.showScript(script);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the proper editor mode (JS or HTML) according to the specified
|
||||
* content type, or by determining the type from the URL.
|
||||
*
|
||||
* @param string aUrl
|
||||
* The script URL.
|
||||
* @param string aContentType [optional]
|
||||
* The script content type.
|
||||
*/
|
||||
setEditorMode: function SS_setEditorMode(aUrl, aContentType) {
|
||||
if (aContentType) {
|
||||
if (/javascript/.test(aContentType)) {
|
||||
window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
|
||||
} else {
|
||||
window.editor.setMode(SourceEditor.MODES.HTML);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._trimUrlQuery(aUrl).slice(-3) == ".js") {
|
||||
window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
|
||||
} else {
|
||||
window.editor.setMode(SourceEditor.MODES.HTML);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Trims the query part of a url string, if necessary.
|
||||
*
|
||||
* @param string aUrl
|
||||
* The script url.
|
||||
* @return string
|
||||
*/
|
||||
_trimUrlQuery: function SS_trimUrlQuery(aUrl) {
|
||||
let q = aUrl.indexOf('?');
|
||||
if (q > -1) {
|
||||
return aUrl.slice(0, q);
|
||||
}
|
||||
return aUrl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a unique, simplified label from a script url.
|
||||
* ex: a). ici://some.address.com/random/subrandom/
|
||||
* b). ni://another.address.org/random/subrandom/page.html
|
||||
* c). san://interesting.address.gro/random/script.js
|
||||
* d). si://interesting.address.moc/random/another/script.js
|
||||
* =>
|
||||
* a). subrandom/
|
||||
* b). page.html
|
||||
* c). script.js
|
||||
* d). another/script.js
|
||||
*
|
||||
* @param string aUrl
|
||||
* The script url.
|
||||
* @param string aHref
|
||||
* The content location href to be used. If unspecified, it will
|
||||
* defalult to debugged panrent window location.
|
||||
* @return string
|
||||
* The simplified label.
|
||||
*/
|
||||
_getScriptLabel: function SS_getScriptLabel(aUrl, aHref) {
|
||||
let url = this._trimUrlQuery(aUrl);
|
||||
|
||||
if (this._labelsCache[url]) {
|
||||
return this._labelsCache[url];
|
||||
}
|
||||
|
||||
let href = aHref || window.parent.content.location.href;
|
||||
let pathElements = url.split("/");
|
||||
let label = pathElements.pop() || (pathElements.pop() + "/");
|
||||
|
||||
// if the label as a leaf name is alreay present in the scripts list
|
||||
if (DebuggerView.Scripts.containsLabel(label)) {
|
||||
label = url.replace(href.substring(0, href.lastIndexOf("/") + 1), "");
|
||||
|
||||
// if the path/to/script is exactly the same, we're in different domains
|
||||
if (DebuggerView.Scripts.containsLabel(label)) {
|
||||
label = url;
|
||||
}
|
||||
}
|
||||
|
||||
return this._labelsCache[url] = label;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the labels cache, populated by SS_getScriptLabel().
|
||||
* This should be done every time the content location changes.
|
||||
*/
|
||||
clearLabelsCache: function SS_clearLabelsCache() {
|
||||
this._labelsCache = {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the specified script to the list and display it in the editor if the
|
||||
* editor is empty.
|
||||
*/
|
||||
_addScript: function SS_addScript(aScript) {
|
||||
DebuggerView.Scripts.addScript(this._getScriptLabel(aScript.url), aScript);
|
||||
|
||||
if (window.editor.getCharCount() == 0) {
|
||||
this.showScript(aScript);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the editor with the script text if available, otherwise fire an event
|
||||
* to load and display the script text.
|
||||
*
|
||||
* @param object aScript
|
||||
* The script object coming from the active thread.
|
||||
* @param object [aOptions]
|
||||
* Additional options for showing the script (optional). Supported
|
||||
* options:
|
||||
* - targetLine: place the editor at the given line number.
|
||||
*/
|
||||
showScript: function SS_showScript(aScript, aOptions) {
|
||||
if (!aScript.loaded) {
|
||||
// Notify the chrome code that we need to load a script file.
|
||||
var evt = document.createEvent("CustomEvent");
|
||||
evt.initCustomEvent("Debugger:LoadSource", true, false,
|
||||
{url: aScript.url, options: aOptions});
|
||||
document.documentElement.dispatchEvent(evt);
|
||||
window.editor.setMode(SourceEditor.MODES.TEXT);
|
||||
window.editor.setText(DebuggerView.getStr("loadingText"));
|
||||
window.editor.resetUndo();
|
||||
} else {
|
||||
this._onShowScript(aScript, aOptions);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the script source once it loads.
|
||||
*
|
||||
* @private
|
||||
* @param object aScript
|
||||
* The script object coming from the active thread.
|
||||
* @param object [aOptions]
|
||||
* Additional options for showing the script (optional). Supported
|
||||
* options:
|
||||
* - targetLine: place the editor at the given line number.
|
||||
*/
|
||||
_onShowScript: function SS__onShowScript(aScript, aOptions) {
|
||||
aOptions = aOptions || {};
|
||||
this.setEditorMode(aScript.url, aScript.contentType);
|
||||
window.editor.setText(aScript.text);
|
||||
window.updateEditorBreakpoints();
|
||||
StackFrames.updateEditor();
|
||||
if (aOptions.targetLine) {
|
||||
window.editor.setCaretPosition(aOptions.targetLine - 1);
|
||||
}
|
||||
window.editor.resetUndo();
|
||||
|
||||
// Notify the chrome code that we shown script file.
|
||||
let evt = document.createEvent("CustomEvent");
|
||||
evt.initCustomEvent("Debugger:ScriptShown", true, false,
|
||||
{url: aScript.url});
|
||||
document.documentElement.dispatchEvent(evt);
|
||||
},
|
||||
};
|
||||
|
||||
SourceScripts.onScripts = SourceScripts.onScripts.bind(SourceScripts);
|
||||
SourceScripts.onNewScript = SourceScripts.onNewScript.bind(SourceScripts);
|
||||
SourceScripts.onScriptsCleared = SourceScripts.onScriptsCleared.bind(SourceScripts);
|
||||
SourceScripts.onChange = SourceScripts.onChange.bind(SourceScripts);
|
||||
|
||||
window.addEventListener("DOMContentLoaded", initDebugger, false);
|
||||
window.addEventListener("unload", shutdownDebugger, false);
|
@ -47,54 +47,58 @@
|
||||
]>
|
||||
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
|
||||
<?xul-overlay href="chrome://browser/content/source-editor-overlay.xul"?>
|
||||
|
||||
<xul:window xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<xul:script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
<xul:script type="text/javascript" src="debugger.js"/>
|
||||
<xul:script type="text/javascript" src="debugger-view.js"/>
|
||||
<xul:popupset id="debugger-popups">
|
||||
<xul:menupopup id="sourceEditorContextMenu"
|
||||
onpopupshowing="goUpdateSourceEditorMenuItems()">
|
||||
<xul:menuitem id="se-cMenu-copy"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem id="se-cMenu-selectAll"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem id="se-cMenu-find"/>
|
||||
<xul:menuitem id="se-cMenu-findAgain"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem id="se-cMenu-gotoLine"/>
|
||||
</xul:menupopup>
|
||||
</xul:popupset>
|
||||
<xul:commandset id="editMenuCommands"/>
|
||||
<xul:commandset id="sourceEditorCommands"/>
|
||||
<xul:keyset id="sourceEditorKeys"/>
|
||||
|
||||
<div id="body" class="vbox flex">
|
||||
<xul:toolbar id="dbg-toolbar">
|
||||
<xul:button id="close">&debuggerUI.closeButton;</xul:button>
|
||||
<xul:button id="resume"/>
|
||||
<xul:button id="step-over">&debuggerUI.stepOverButton;</xul:button>
|
||||
<xul:button id="step-in">&debuggerUI.stepInButton;</xul:button>
|
||||
<xul:button id="step-out">&debuggerUI.stepOutButton;</xul:button>
|
||||
<xul:menulist id="scripts"/>
|
||||
</xul:toolbar>
|
||||
<div id="dbg-content" class="hbox flex">
|
||||
<div id="stack" class="vbox">
|
||||
<div class="title unselectable">&debuggerUI.stackTitle;</div>
|
||||
<div id="stackframes" class="vbox flex"></div>
|
||||
</div>
|
||||
<div id="script" class="vbox flex">
|
||||
<div class="title unselectable">&debuggerUI.scriptTitle;</div>
|
||||
<div id="editor" class="vbox flex"></div>
|
||||
</div>
|
||||
<div id="properties" class="vbox">
|
||||
<div class="title unselectable">&debuggerUI.propertiesTitle;</div>
|
||||
<div id="variables" class="vbox flex"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dbg-statusbar">
|
||||
<span id="status"></span>
|
||||
</div>
|
||||
<xul:script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
<xul:script type="text/javascript" src="debugger-controller.js"/>
|
||||
<xul:script type="text/javascript" src="debugger-view.js"/>
|
||||
|
||||
<xul:popupset id="debugger-popups">
|
||||
<xul:menupopup id="sourceEditorContextMenu"
|
||||
onpopupshowing="goUpdateSourceEditorMenuItems()">
|
||||
<xul:menuitem id="se-cMenu-copy"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem id="se-cMenu-selectAll"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem id="se-cMenu-find"/>
|
||||
<xul:menuitem id="se-cMenu-findAgain"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem id="se-cMenu-gotoLine"/>
|
||||
</xul:menupopup>
|
||||
</xul:popupset>
|
||||
|
||||
<xul:commandset id="editMenuCommands"/>
|
||||
<xul:commandset id="sourceEditorCommands"/>
|
||||
<xul:keyset id="sourceEditorKeys"/>
|
||||
|
||||
<div id="body" class="vbox flex">
|
||||
<xul:toolbar id="dbg-toolbar">
|
||||
<xul:button id="close">&debuggerUI.closeButton;</xul:button>
|
||||
<xul:button id="resume"/>
|
||||
<xul:button id="step-over">&debuggerUI.stepOverButton;</xul:button>
|
||||
<xul:button id="step-in">&debuggerUI.stepInButton;</xul:button>
|
||||
<xul:button id="step-out">&debuggerUI.stepOutButton;</xul:button>
|
||||
<xul:menulist id="scripts"/>
|
||||
</xul:toolbar>
|
||||
<div id="dbg-content" class="hbox flex">
|
||||
<div id="stack" class="vbox">
|
||||
<div class="title unselectable">&debuggerUI.stackTitle;</div>
|
||||
<div id="stackframes" class="vbox flex"></div>
|
||||
</div>
|
||||
<div id="script" class="vbox flex">
|
||||
<div class="title unselectable">&debuggerUI.scriptTitle;</div>
|
||||
<div id="editor" class="vbox flex"></div>
|
||||
</div>
|
||||
<div id="properties" class="vbox">
|
||||
<div class="title unselectable">&debuggerUI.propertiesTitle;</div>
|
||||
<div id="variables" class="vbox flex"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dbg-statusbar">
|
||||
<span id="status"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</xul:window>
|
||||
|
@ -29,7 +29,7 @@ function test()
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.debuggerWindow;
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
framesAdded = true;
|
||||
runTest();
|
||||
});
|
||||
@ -57,7 +57,7 @@ function test()
|
||||
{
|
||||
gScripts = gDebugger.DebuggerView.Scripts;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(gScripts._scripts.itemCount, 2, "Found the expected number of scripts.");
|
||||
@ -66,7 +66,7 @@ function test()
|
||||
|
||||
isnot(gEditor.getText().indexOf("debugger"), -1,
|
||||
"The correct script was loaded initially.");
|
||||
isnot(gScripts.selected, gScripts.scriptLocations()[0],
|
||||
isnot(gScripts.selected, gScripts.scriptLocations[0],
|
||||
"the correct script is selected");
|
||||
|
||||
gBreakpoints = gPane.breakpoints;
|
||||
@ -161,7 +161,7 @@ function test()
|
||||
ok(!gPane.getBreakpoint(gScripts.selected, 6),
|
||||
"getBreakpoint(selectedScript, 6) returns no breakpoint");
|
||||
|
||||
let script0 = gScripts.scriptLocations()[0];
|
||||
let script0 = gScripts.scriptLocations[0];
|
||||
isnot(script0, gScripts.selected,
|
||||
"first script location is not the currently selected script");
|
||||
|
||||
@ -187,7 +187,7 @@ function test()
|
||||
|
||||
ok(aBreakpointClient, "breakpoint2 added, client received");
|
||||
ok(!aResponseError, "breakpoint2 added without errors");
|
||||
is(aBreakpointClient.location.url, gScripts.scriptLocations()[0],
|
||||
is(aBreakpointClient.location.url, gScripts.scriptLocations[0],
|
||||
"breakpoint2 client url is correct");
|
||||
is(aBreakpointClient.location.line, 5,
|
||||
"breakpoint2 client line is correct");
|
||||
@ -196,7 +196,7 @@ function test()
|
||||
ok(aBreakpointClient.actor in gBreakpoints,
|
||||
"breakpoint2 client found in the list of debugger breakpoints");
|
||||
is(Object.keys(gBreakpoints).length, 1, "one breakpoint in the debugger");
|
||||
is(gPane.getBreakpoint(gScripts.scriptLocations()[0], 5), aBreakpointClient,
|
||||
is(gPane.getBreakpoint(gScripts.scriptLocations[0], 5), aBreakpointClient,
|
||||
"getBreakpoint(scriptLocations[0], 5) returns the correct breakpoint");
|
||||
|
||||
// remove the trap listener
|
||||
@ -211,7 +211,7 @@ function test()
|
||||
info("switch to the second script");
|
||||
|
||||
gScripts._scripts.selectedIndex = 0;
|
||||
gDebugger.SourceScripts.onChange({ target: gScripts._scripts });
|
||||
gDebugger.DebuggerController.SourceScripts.onChange({ target: gScripts._scripts });
|
||||
});
|
||||
}
|
||||
|
||||
@ -267,13 +267,13 @@ function test()
|
||||
is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct");
|
||||
|
||||
executeSoon(function() {
|
||||
gDebugger.StackFrames.activeThread.resume(finish);
|
||||
gDebugger.DebuggerController.activeThread.resume(finish);
|
||||
});
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger");
|
||||
ok(!gPane.getBreakpoint(gScripts.scriptLocations()[0], 5),
|
||||
ok(!gPane.getBreakpoint(gScripts.scriptLocations[0], 5),
|
||||
"getBreakpoint(scriptLocations[0], 5) returns no breakpoint");
|
||||
|
||||
removeTab(gTab);
|
||||
|
@ -28,7 +28,7 @@ function test()
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.debuggerWindow;
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
framesAdded = true;
|
||||
runTest();
|
||||
});
|
||||
@ -55,7 +55,7 @@ function test()
|
||||
{
|
||||
let scripts = gDebugger.DebuggerView.Scripts._scripts;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(scripts.itemCount, 2, "Found the expected number of scripts.");
|
||||
@ -107,7 +107,7 @@ function test()
|
||||
|
||||
executeSoon(function() {
|
||||
contextMenu.hidePopup();
|
||||
gDebugger.StackFrames.activeThread.resume(finish);
|
||||
gDebugger.DebuggerController.activeThread.resume(finish);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,9 @@ function test() {
|
||||
}
|
||||
|
||||
function testCleanExit() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
is(gDebugger.StackFrames.activeThread.paused, true,
|
||||
is(gDebugger.DebuggerController.activeThread.paused, true,
|
||||
"Should be paused after the debugger statement.");
|
||||
|
||||
closeDebuggerAndFinish(gTab);
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head><title>Browser Debugger Test Tab</title>
|
||||
<head><meta charset='utf-8'/><title>Browser Debugger Test Tab</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript">
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head><title>Browser Debugger Test Tab</title>
|
||||
<head><meta charset='utf-8'/><title>Browser Debugger Test Tab</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript">
|
||||
|
@ -22,12 +22,12 @@ function test() {
|
||||
}
|
||||
|
||||
function testAnonCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
|
||||
@ -44,7 +44,7 @@ function testAnonCall() {
|
||||
}
|
||||
|
||||
function resumeAndFinish() {
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gDebuggee = null;
|
||||
|
@ -1,6 +1,7 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Debugger Function Call Parameter Test</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head><title>Browser Debugger IFrame Test Tab</title>
|
||||
<head><meta charset='utf-8'/><title>Browser Debugger IFrame Test Tab</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
|
@ -16,22 +16,22 @@ function test() {
|
||||
gPane = aPane;
|
||||
let gDebugger = gPane.debuggerWindow;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.paused, false,
|
||||
is(gDebugger.DebuggerController.activeThread.paused, false,
|
||||
"Should be running after debug_tab_pane.");
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
let childNodes = frames.childNodes;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.paused, true,
|
||||
is(gDebugger.DebuggerController.activeThread.paused, true,
|
||||
"Should be paused after an interrupt request.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 1,
|
||||
"Should have one frame in the stack.");
|
||||
|
||||
gPane.activeThread.addOneTimeListener("resumed", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("resumed", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
}}, 0);
|
||||
|
@ -24,13 +24,13 @@ function test()
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({
|
||||
run: function() {
|
||||
var frames = gDebugger.DebuggerView.Stackframes._frames,
|
||||
var frames = gDebugger.DebuggerView.StackFrames._frames,
|
||||
childNodes = frames.childNodes;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 1,
|
||||
@ -49,10 +49,10 @@ function testSimpleCall() {
|
||||
|
||||
function testLocationChange()
|
||||
{
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gPane._client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
|
||||
ok(true, "tabNavigated event was fired.");
|
||||
gPane._client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
|
||||
gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
|
||||
ok(true, "Successfully reattached to the tab again.");
|
||||
|
||||
closeDebuggerAndFinish(gTab);
|
||||
|
@ -22,19 +22,22 @@ function test() {
|
||||
ok(DebuggerUI.preferences.height,
|
||||
"The debugger preferences should have a saved height value.");
|
||||
|
||||
is(DebuggerUI.preferences.height, pane.frame.height,
|
||||
is(DebuggerUI.preferences.height, pane._frame.height,
|
||||
"The debugger pane height should be the same as the preferred value.");
|
||||
|
||||
pane.frame.height = someHeight;
|
||||
pane._frame.height = someHeight;
|
||||
ok(DebuggerUI.preferences.height !== someHeight,
|
||||
"Height preferences shouldn't have been updated yet.");
|
||||
|
||||
pane.onConnected = function() {
|
||||
pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
|
||||
pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
|
||||
|
||||
removeTab(tab1);
|
||||
finish();
|
||||
|
||||
is(DebuggerUI.preferences.height, someHeight,
|
||||
"Height preferences should have been updated by now.");
|
||||
};
|
||||
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
@ -19,23 +19,23 @@ function test() {
|
||||
}
|
||||
|
||||
function testPause() {
|
||||
is(gDebugger.StackFrames.activeThread.paused, false,
|
||||
is(gDebugger.DebuggerController.activeThread.paused, false,
|
||||
"Should be running after debug_tab_pane.");
|
||||
|
||||
let button = gDebugger.document.getElementById("resume");
|
||||
is(button.label, gDebugger.DebuggerView.getStr("pauseLabel"),
|
||||
is(button.label, gDebugger.L10N.getStr("pauseLabel"),
|
||||
"Button label should be pause when running.");
|
||||
|
||||
gPane.activeThread.addOneTimeListener("paused", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
let childNodes = frames.childNodes;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.paused, true,
|
||||
is(gDebugger.DebuggerController.activeThread.paused, true,
|
||||
"Should be paused after an interrupt request.");
|
||||
|
||||
is(button.label, gDebugger.DebuggerView.getStr("resumeLabel"),
|
||||
is(button.label, gDebugger.L10N.getStr("resumeLabel"),
|
||||
"Button label should be resume when paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
|
||||
@ -51,14 +51,14 @@ function testPause() {
|
||||
}
|
||||
|
||||
function testResume() {
|
||||
gPane.activeThread.addOneTimeListener("resumed", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("resumed", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.paused, false,
|
||||
is(gDebugger.DebuggerController.activeThread.paused, false,
|
||||
"Should be paused after an interrupt request.");
|
||||
|
||||
let button = gDebugger.document.getElementById("resume");
|
||||
is(button.label, gDebugger.DebuggerView.getStr("pauseLabel"),
|
||||
is(button.label, gDebugger.L10N.getStr("pauseLabel"),
|
||||
"Button label should be pause when running.");
|
||||
|
||||
closeDebuggerAndFinish(gTab);
|
||||
|
@ -20,7 +20,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let globalScope = gDebugger.DebuggerView.Properties._globalScope;
|
||||
@ -82,10 +82,10 @@ function testSimpleCall() {
|
||||
}
|
||||
|
||||
function resumeAndFinish() {
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
let vs = gDebugger.DebuggerView.Scripts;
|
||||
let ss = gDebugger.SourceScripts;
|
||||
ss.onScriptsCleared();
|
||||
let ss = gDebugger.DebuggerController.SourceScripts;
|
||||
ss._onScriptsCleared();
|
||||
|
||||
is(ss._trimUrlQuery("a/b/c.d?test=1&random=4"), "a/b/c.d",
|
||||
"Trimming the url query isn't done properly.");
|
||||
|
@ -20,7 +20,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let testScope = gDebugger.DebuggerView.Properties._addScope("test");
|
||||
@ -116,7 +116,7 @@ function testSimpleCall() {
|
||||
ok(!testScope.expanded,
|
||||
"Clicking again the testScope tilte should collapse it.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}}, 0);
|
||||
|
@ -20,7 +20,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let testScope = gDebugger.DebuggerView.Properties._addScope("test");
|
||||
@ -118,7 +118,7 @@ function testSimpleCall() {
|
||||
is(gDebugger.DebuggerView.Properties._vars.childNodes.length, 4,
|
||||
"The scope should have been removed from the parent container tree.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}}, 0);
|
||||
|
@ -20,7 +20,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let testScope = gDebugger.DebuggerView.Properties._addScope("test");
|
||||
@ -73,7 +73,7 @@ function testSimpleCall() {
|
||||
is(testScope.querySelector(".details").childNodes.length, 0,
|
||||
"The var should have been removed from the parent container tree.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}}, 0);
|
||||
|
@ -20,7 +20,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let testScope = gDebugger.DebuggerView.Properties._addScope("test");
|
||||
@ -81,7 +81,7 @@ function testSimpleCall() {
|
||||
is(testScope.querySelector(".details").childNodes.length, 0,
|
||||
"The var should have been removed from the parent container tree.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}}, 0);
|
||||
|
@ -20,7 +20,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let globalScope = gDebugger.DebuggerView.Properties.globalScope;
|
||||
@ -117,7 +117,7 @@ function testSimpleCall() {
|
||||
is(localVar5.querySelector(".info").textContent, "[object Object]",
|
||||
"The grip information for the localVar5 wasn't set correctly.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}}, 0);
|
||||
|
@ -35,7 +35,7 @@ function testFrameParameters()
|
||||
|
||||
dump("After currentThread.dispatch!\n");
|
||||
|
||||
var frames = gDebugger.DebuggerView.Stackframes._frames,
|
||||
var frames = gDebugger.DebuggerView.StackFrames._frames,
|
||||
childNodes = frames.childNodes,
|
||||
localScope = gDebugger.DebuggerView.Properties.localScope,
|
||||
localNodes = localScope.querySelector(".details").childNodes;
|
||||
@ -46,7 +46,7 @@ function testFrameParameters()
|
||||
dump("localScope - " + localScope.constructor + "\n");
|
||||
dump("localNodes - " + localNodes.constructor + "\n");
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
|
||||
@ -89,9 +89,9 @@ function testFrameParameters()
|
||||
}
|
||||
|
||||
function resumeAndFinish() {
|
||||
gPane.activeThread.addOneTimeListener("framescleared", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
var frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
var frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
|
||||
"Should have no frames.");
|
||||
@ -100,7 +100,7 @@ function resumeAndFinish() {
|
||||
}}, 0);
|
||||
});
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume();
|
||||
gDebugger.DebuggerController.activeThread.resume();
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
|
@ -35,7 +35,7 @@ function testFrameParameters()
|
||||
|
||||
dump("After currentThread.dispatch!\n");
|
||||
|
||||
var frames = gDebugger.DebuggerView.Stackframes._frames,
|
||||
var frames = gDebugger.DebuggerView.StackFrames._frames,
|
||||
localScope = gDebugger.DebuggerView.Properties.localScope,
|
||||
localNodes = localScope.querySelector(".details").childNodes;
|
||||
|
||||
@ -44,7 +44,7 @@ function testFrameParameters()
|
||||
dump("localScope - " + localScope.constructor + "\n");
|
||||
dump("localNodes - " + localNodes.constructor + "\n");
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
|
||||
@ -104,9 +104,9 @@ function testFrameParameters()
|
||||
}
|
||||
|
||||
function resumeAndFinish() {
|
||||
gPane.activeThread.addOneTimeListener("framescleared", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
var frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
var frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
|
||||
"Should have no frames.");
|
||||
@ -115,7 +115,7 @@ function resumeAndFinish() {
|
||||
}}, 0);
|
||||
});
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume();
|
||||
gDebugger.DebuggerController.activeThread.resume();
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
|
@ -1,12 +1,13 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Browser Debugger Script Switching Test</title>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Browser Debugger Script Switching Test</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript" src="test-script-switching-01.js"></script>
|
||||
<script type="text/javascript" src="test-script-switching-02.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -29,7 +29,7 @@ function test()
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.debuggerWindow;
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
framesAdded = true;
|
||||
runTest();
|
||||
});
|
||||
@ -57,7 +57,7 @@ function test()
|
||||
function testScriptsDisplay() {
|
||||
gScripts = gDebugger.DebuggerView.Scripts._scripts;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(gScripts.itemCount, 2, "Found the expected number of scripts.");
|
||||
@ -79,6 +79,8 @@ function testScriptsDisplay() {
|
||||
ok(gDebugger.DebuggerView.Scripts.containsLabel(
|
||||
label2), "Second script label is incorrect.");
|
||||
|
||||
dump("Debugger editor text:\n" + gDebugger.editor.getText() + "\n");
|
||||
|
||||
ok(gDebugger.editor.getText().search(/debugger/) != -1,
|
||||
"The correct script was loaded initially.");
|
||||
|
||||
@ -98,6 +100,8 @@ function testScriptsDisplay() {
|
||||
|
||||
function testSwitchPaused()
|
||||
{
|
||||
dump("Debugger editor text:\n" + gDebugger.editor.getText() + "\n");
|
||||
|
||||
ok(gDebugger.editor.getText().search(/debugger/) == -1,
|
||||
"The second script is no longer displayed.");
|
||||
|
||||
@ -107,7 +111,7 @@ function testSwitchPaused()
|
||||
is(gDebugger.editor.getDebugLocation(), -1,
|
||||
"editor debugger location has been cleared.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
|
||||
let url = aEvent.detail.url;
|
||||
if (url.indexOf("-02.js") != -1) {
|
||||
|
@ -31,11 +31,11 @@ function test()
|
||||
}
|
||||
|
||||
function testSelectLine() {
|
||||
gPane.activeThread.addOneTimeListener("scriptsadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("scriptsadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
gScripts = gDebugger.DebuggerView.Scripts._scripts;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(gScripts.itemCount, 2, "Found the expected number of scripts.");
|
||||
@ -67,7 +67,7 @@ function testSelectLine() {
|
||||
is(gDebugger.editor.getCaretPosition().line, 4,
|
||||
"The correct line is selected.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
});
|
||||
|
@ -21,13 +21,13 @@ function test() {
|
||||
}
|
||||
|
||||
function testSimpleCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
let childNodes = frames.childNodes;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 1,
|
||||
@ -36,7 +36,7 @@ function testSimpleCall() {
|
||||
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
|
||||
"All children should be frames.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}}, 0);
|
||||
|
@ -21,13 +21,13 @@ function test() {
|
||||
}
|
||||
|
||||
function testEvalCall() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
let childNodes = frames.childNodes;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 2,
|
||||
@ -67,7 +67,7 @@ function testEvalCall() {
|
||||
ok(!frames.querySelector("#stackframe-1").classList.contains("selected"),
|
||||
"Second frame should not be selected after click inside the first frame.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}}, 0);
|
||||
|
@ -21,13 +21,13 @@ function test() {
|
||||
}
|
||||
|
||||
function testRecurse() {
|
||||
gDebuggee.gRecurseLimit = (gDebugger.StackFrames.pageSize * 2) + 1;
|
||||
gDebuggee.gRecurseLimit = (gDebugger.DebuggerController.StackFrames.pageSize * 2) + 1;
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let pageSize = gDebugger.StackFrames.pageSize;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
let pageSize = gDebugger.DebuggerController.StackFrames.pageSize;
|
||||
let recurseLimit = gDebuggee.gRecurseLimit;
|
||||
let childNodes = frames.childNodes;
|
||||
|
||||
@ -38,16 +38,16 @@ function testRecurse() {
|
||||
"All children should be frames.");
|
||||
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, pageSize * 2,
|
||||
"Should now have twice the max limit of frames.");
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, recurseLimit,
|
||||
"Should have reached the recurse limit.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
});
|
||||
|
@ -21,13 +21,13 @@ function test() {
|
||||
}
|
||||
|
||||
function testEvalCallResume() {
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
Services.tm.currentThread.dispatch({ run: function() {
|
||||
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
let childNodes = frames.childNodes;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 2,
|
||||
@ -37,7 +37,7 @@ function testEvalCallResume() {
|
||||
"All children should be frames.");
|
||||
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framescleared", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
|
||||
"Should have no frames after resume");
|
||||
@ -51,7 +51,7 @@ function testEvalCallResume() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
|
||||
gPane.activeThread.resume();
|
||||
gDebugger.DebuggerController.activeThread.resume();
|
||||
}}, 0);
|
||||
});
|
||||
|
||||
|
@ -24,7 +24,7 @@ function test() {
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.debuggerWindow;
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
framesAdded = true;
|
||||
runTest();
|
||||
});
|
||||
@ -51,7 +51,7 @@ function test() {
|
||||
|
||||
function testRecurse()
|
||||
{
|
||||
let frames = gDebugger.DebuggerView.Stackframes._frames;
|
||||
let frames = gDebugger.DebuggerView.StackFrames._frames;
|
||||
let childNodes = frames.childNodes;
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 4,
|
||||
@ -95,7 +95,7 @@ function testRecurse()
|
||||
is(gDebugger.editor.getDebugLocation(), 5,
|
||||
"editor debugger location is correct (frame 0 again).");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
is(gDebugger.editor.getDebugLocation(), -1,
|
||||
"editor debugger location is correct after resume.");
|
||||
closeDebuggerAndFinish(gTab);
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head><title>Browser Debugger Test Tab</title>
|
||||
<head><meta charset='utf-8'/><title>Browser Debugger Test Tab</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript">
|
||||
|
@ -1,10 +1,11 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Browser Debugger Test Tab</title>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Browser Debugger Test Tab</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,10 +1,11 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Browser Debugger Test Tab 2</title>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Browser Debugger Test Tab 2</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,12 +1,13 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Browser Debugger Update Editor Mode Test</title>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Browser Debugger Update Editor Mode Test</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript" src="test-script-switching-01.js?q=a"></script>
|
||||
<script type="text/javascript" src="test-editor-mode?a=b"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -29,7 +29,7 @@ function test()
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.debuggerWindow;
|
||||
|
||||
gPane.activeThread.addOneTimeListener("framesadded", function() {
|
||||
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
|
||||
framesAdded = true;
|
||||
runTest();
|
||||
});
|
||||
@ -56,7 +56,7 @@ function test()
|
||||
function testScriptsDisplay() {
|
||||
gScripts = gDebugger.DebuggerView.Scripts._scripts;
|
||||
|
||||
is(gDebugger.StackFrames.activeThread.state, "paused",
|
||||
is(gDebugger.DebuggerController.activeThread.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
|
||||
is(gScripts.itemCount, 2, "Found the expected number of scripts.");
|
||||
@ -90,7 +90,7 @@ function testSwitchPaused()
|
||||
is(gDebugger.editor.getMode(), SourceEditor.MODES.JAVASCRIPT,
|
||||
"Found the expected editor mode.");
|
||||
|
||||
gDebugger.StackFrames.activeThread.resume(function() {
|
||||
gDebugger.DebuggerController.activeThread.resume(function() {
|
||||
closeDebuggerAndFinish(gTab);
|
||||
});
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ function removeTab(aTab) {
|
||||
}
|
||||
|
||||
function closeDebuggerAndFinish(aTab) {
|
||||
DebuggerUI.aWindow.addEventListener("Debugger:Shutdown", function cleanup() {
|
||||
DebuggerUI.aWindow.removeEventListener("Debugger:Shutdown", cleanup, false);
|
||||
DebuggerUI.chromeWindow.addEventListener("Debugger:Shutdown", function cleanup() {
|
||||
DebuggerUI.chromeWindow.removeEventListener("Debugger:Shutdown", cleanup, false);
|
||||
finish();
|
||||
}, false);
|
||||
DebuggerUI.getDebugger(aTab).close();
|
||||
@ -96,12 +96,13 @@ function debug_tab_pane(aURL, aOnDebugging)
|
||||
let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
|
||||
|
||||
let pane = DebuggerUI.toggleDebugger();
|
||||
pane.onConnected = function() {
|
||||
pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
|
||||
pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
|
||||
|
||||
// Wait for the initial resume...
|
||||
pane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
|
||||
delete pane.onConnected;
|
||||
aOnDebugging(tab, debuggee, pane);
|
||||
});
|
||||
};
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
@ -77,7 +78,7 @@ const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
|
||||
*
|
||||
* // Constructor and destructor.
|
||||
* // @param aWindow - browser.xul window.
|
||||
* Highlighter(aWindow);
|
||||
* Highlighter(aWindow);
|
||||
* void destroy();
|
||||
*
|
||||
* // Highlight a node.
|
||||
@ -253,7 +254,7 @@ Highlighter.prototype = {
|
||||
* @param aPseudo - The pseudo-class to toggle, e.g. ":hover".
|
||||
*/
|
||||
pseudoClassLockToggled: function Highlighter_pseudoClassLockToggled(aPseudo)
|
||||
{
|
||||
{
|
||||
this.emitEvent("pseudoclasstoggled", [aPseudo]);
|
||||
this.updateInfobar();
|
||||
},
|
||||
@ -430,9 +431,14 @@ Highlighter.prototype = {
|
||||
* <box id="highlighter-nodeinfobar-container">
|
||||
* <box id="Highlighter-nodeinfobar-arrow-top"/>
|
||||
* <hbox id="highlighter-nodeinfobar">
|
||||
* <xhtml:span id="highlighter-nodeinfobar-tagname"/>
|
||||
* <xhtml:span id="highlighter-nodeinfobar-id"/>
|
||||
* <xhtml:span id="highlighter-nodeinfobar-classes"/>
|
||||
* <toolbarbutton class="highlighter-nodeinfobar-button" id="highlighter-nodeinfobar-inspectbutton"/>
|
||||
* <hbox id="highlighter-nodeinfobar-text">
|
||||
* <xhtml:span id="highlighter-nodeinfobar-tagname"/>
|
||||
* <xhtml:span id="highlighter-nodeinfobar-id"/>
|
||||
* <xhtml:span id="highlighter-nodeinfobar-classes"/>
|
||||
* <xhtml:span id="highlighter-nodeinfobar-pseudo-classes"/>
|
||||
* </hbox>
|
||||
* <toolbarbutton class="highlighter-nodeinfobar-button" id="highlighter-nodeinfobar-menu"/>
|
||||
* </hbox>
|
||||
* <box id="Highlighter-nodeinfobar-arrow-bottom"/>
|
||||
* </box>
|
||||
@ -466,17 +472,52 @@ Highlighter.prototype = {
|
||||
|
||||
let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||
classesBox.id = "highlighter-nodeinfobar-classes";
|
||||
|
||||
|
||||
let pseudoClassesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||
pseudoClassesBox.id = "highlighter-nodeinfobar-pseudo-classes";
|
||||
|
||||
|
||||
// Add some content to force a better boundingClientRect down below.
|
||||
pseudoClassesBox.textContent = " ";
|
||||
|
||||
nodeInfobar.appendChild(tagNameLabel);
|
||||
nodeInfobar.appendChild(idLabel);
|
||||
nodeInfobar.appendChild(classesBox);
|
||||
nodeInfobar.appendChild(pseudoClassesBox);
|
||||
// Create buttons
|
||||
|
||||
let inspect = this.chromeDoc.createElement("toolbarbutton");
|
||||
inspect.id = "highlighter-nodeinfobar-inspectbutton";
|
||||
inspect.className = "highlighter-nodeinfobar-button"
|
||||
let toolbarInspectButton =
|
||||
this.chromeDoc.getElementById("inspector-inspect-toolbutton");
|
||||
inspect.setAttribute("tooltiptext",
|
||||
toolbarInspectButton.getAttribute("tooltiptext"));
|
||||
inspect.setAttribute("command", "Inspector:Inspect");
|
||||
|
||||
let nodemenu = this.chromeDoc.createElement("toolbarbutton");
|
||||
nodemenu.setAttribute("type", "menu");
|
||||
nodemenu.id = "highlighter-nodeinfobar-menu";
|
||||
nodemenu.className = "highlighter-nodeinfobar-button"
|
||||
nodemenu.setAttribute("tooltiptext",
|
||||
this.strings.GetStringFromName("nodeMenu.tooltiptext"));
|
||||
|
||||
let menu = this.chromeDoc.getElementById("inspector-node-popup");
|
||||
menu = menu.cloneNode(true);
|
||||
menu.id = "highlighter-node-menu";
|
||||
|
||||
nodemenu.appendChild(menu);
|
||||
|
||||
// <hbox id="highlighter-nodeinfobar-text"/>
|
||||
let texthbox = this.chromeDoc.createElement("hbox");
|
||||
texthbox.id = "highlighter-nodeinfobar-text";
|
||||
texthbox.setAttribute("align", "center");
|
||||
texthbox.setAttribute("flex", "1");
|
||||
|
||||
texthbox.appendChild(tagNameLabel);
|
||||
texthbox.appendChild(idLabel);
|
||||
texthbox.appendChild(classesBox);
|
||||
texthbox.appendChild(pseudoClassesBox);
|
||||
|
||||
nodeInfobar.appendChild(inspect);
|
||||
nodeInfobar.appendChild(texthbox);
|
||||
nodeInfobar.appendChild(nodemenu);
|
||||
|
||||
container.appendChild(arrowBoxTop);
|
||||
container.appendChild(nodeInfobar);
|
||||
container.appendChild(arrowBoxBottom);
|
||||
@ -511,13 +552,13 @@ Highlighter.prototype = {
|
||||
|
||||
let popupSet = this.chromeDoc.getElementById("mainPopupSet");
|
||||
popupSet.appendChild(menu);
|
||||
|
||||
|
||||
let fragment = this.buildPseudoClassMenu();
|
||||
menu.appendChild(fragment);
|
||||
|
||||
menu.openPopup(this.nodeInfo.pseudoClassesBox, "end_before", 0, 0, true, false);
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the menuitems for toggling the selection's pseudo-class state
|
||||
*
|
||||
@ -887,3 +928,9 @@ Highlighter.prototype = {
|
||||
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
|
||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(Highlighter.prototype, "strings",
|
||||
function () {
|
||||
return Services.strings.createBundle(
|
||||
"chrome://browser/locale/devtools/inspector.properties");
|
||||
});
|
||||
|
@ -28,6 +28,7 @@
|
||||
* Kyle Simpson <ksimpson@mozilla.com>
|
||||
* Johan Charlez <johan.charlez@gmail.com>
|
||||
* Mike Ratcliffe <mratcliffe@mozilla.com>
|
||||
* Murali S R <murali.sr92@yahoo.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -1926,6 +1927,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
}
|
||||
this.menu.appendChild(fragment);
|
||||
this.menu.openPopup(aButton, "before_start", 0, 0, true, false);
|
||||
aButton.setAttribute("siblings-menu-open", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1936,7 +1938,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
*/
|
||||
handleEvent: function BC_handleEvent(aEvent)
|
||||
{
|
||||
if (aEvent.type == "mousedown") {
|
||||
if (aEvent.type == "mousedown" && aEvent.button == 0) {
|
||||
// on Click and Hold, open the Siblings menu
|
||||
|
||||
let timer;
|
||||
@ -1948,7 +1950,6 @@ HTMLBreadcrumbs.prototype = {
|
||||
let target = aEvent.originalTarget;
|
||||
if (target.tagName == "button") {
|
||||
target.onBreadcrumbsHold();
|
||||
target.setAttribute("siblings-menu-open", "true");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,5 @@ browser.jar:
|
||||
* content/browser/source-editor-overlay.xul (sourceeditor/source-editor-overlay.xul)
|
||||
* content/browser/debugger.xul (debugger/debugger.xul)
|
||||
content/browser/debugger.css (debugger/debugger.css)
|
||||
content/browser/debugger.js (debugger/debugger.js)
|
||||
content/browser/debugger-controller.js (debugger/debugger-controller.js)
|
||||
content/browser/debugger-view.js (debugger/debugger-view.js)
|
||||
|
||||
|
@ -229,8 +229,8 @@ gcli.addCommand({
|
||||
let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
|
||||
let files = [];
|
||||
if (dbg) {
|
||||
let scriptsView = dbg.frame.contentWindow.DebuggerView.Scripts;
|
||||
for each (let script in scriptsView.scriptLocations()) {
|
||||
let scriptsView = dbg.debuggerWindow.DebuggerView.Scripts;
|
||||
for each (let script in scriptsView.scriptLocations) {
|
||||
files.push(script);
|
||||
}
|
||||
}
|
||||
|
@ -69,11 +69,12 @@ function testCreateCommands() {
|
||||
is(requisition.getStatus().toString(), "ERROR", "break add line is ERROR");
|
||||
|
||||
let pane = DebuggerUI.toggleDebugger();
|
||||
pane.onConnected = function test_onConnected(aPane) {
|
||||
pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
|
||||
pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
|
||||
|
||||
// Wait for the initial resume.
|
||||
aPane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
|
||||
delete aPane.onConnected;
|
||||
aPane.debuggerWindow.gClient.activeThread.addOneTimeListener("framesadded", function() {
|
||||
pane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
|
||||
pane.debuggerWindow.gClient.activeThread.addOneTimeListener("framesadded", function() {
|
||||
type("break add line " + TEST_URI + " " + content.wrappedJSObject.line0);
|
||||
is(requisition.getStatus().toString(), "VALID", "break add line is VALID");
|
||||
requisition.exec();
|
||||
@ -82,7 +83,7 @@ function testCreateCommands() {
|
||||
is(requisition.getStatus().toString(), "VALID", "break list is VALID");
|
||||
requisition.exec();
|
||||
|
||||
aPane.debuggerWindow.gClient.activeThread.resume(function() {
|
||||
pane.debuggerWindow.gClient.activeThread.resume(function() {
|
||||
type("break del 0");
|
||||
is(requisition.getStatus().toString(), "VALID", "break del 0 is VALID");
|
||||
requisition.exec();
|
||||
@ -94,7 +95,7 @@ function testCreateCommands() {
|
||||
// Trigger newScript notifications using eval.
|
||||
content.wrappedJSObject.firstCall();
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
function type(command) {
|
||||
|
@ -32,3 +32,9 @@ ruleView.tooltiptext=View and Edit CSS
|
||||
# "Return" key # changes that state. %S is the keyboard shortcut (VK_RETURN in
|
||||
# chrome://global/locale/keys.properties).
|
||||
inspectButton.tooltiptext=Select element with mouse (%S)
|
||||
|
||||
|
||||
# LOCALIZATION NOTE (nodeMenu.tooltiptext)
|
||||
# This menu appears in the Infobar (on top of the highlighted node) once
|
||||
# the node is selected.
|
||||
nodeMenu.tooltiptext=Node operations
|
||||
|
@ -126,8 +126,8 @@ let webappsUI = {
|
||||
let message = bundle.getFormattedString("webapps.requestInstall",
|
||||
[manifest.name, host], 2);
|
||||
|
||||
aWindow.PopupNotifications.show(aBrowser, "webapps-install", message, "webapps-notification-icon",
|
||||
mainAction, null, { popupIconURL: manifest.iconURLForSize(64) });
|
||||
aWindow.PopupNotifications.show(aBrowser, "webapps-install", message,
|
||||
"webapps-notification-icon", mainAction);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2027,8 +2027,21 @@ panel[dimmed="true"] {
|
||||
|
||||
/* Highlighter - Node Infobar */
|
||||
|
||||
#highlighter-nodeinfobar {
|
||||
color: hsl(200, 100%, 65%);
|
||||
border: 1px solid hsla(210, 19%, 63%, .5);
|
||||
border-radius: 3px;
|
||||
background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - text */
|
||||
|
||||
#highlighter-nodeinfobar-text {
|
||||
/* 100% - size of the buttons and margins */
|
||||
max-width: -moz-calc(100% - 2 * (26px + 6px));
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
html|*#highlighter-nodeinfobar-tagname {
|
||||
color: white;
|
||||
}
|
||||
@ -2041,16 +2054,52 @@ html|*#highlighter-nodeinfobar-pseudo-classes {
|
||||
color: hsl(20, 100%, 70%);
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
/* Highlighter - Node Infobar - buttons */
|
||||
|
||||
#highlighter-nodeinfobar {
|
||||
color: hsl(200, 100%, 65%);
|
||||
border: 1px solid hsla(210, 19%, 63%, .5);
|
||||
border-radius: 3px;
|
||||
padding: 8px 16px;
|
||||
background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
|
||||
.highlighter-nodeinfobar-button {
|
||||
-moz-appearance: none;
|
||||
border: 0 solid hsla(210,8%,5%,.45);
|
||||
padding: 0;
|
||||
width: 26px;
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-border-end-width: 1px;
|
||||
box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
-moz-margin-end: 6px;
|
||||
list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
|
||||
-moz-image-region: rect(0px 16px 16px 0px);
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton:-moz-locale-dir(rtl) {
|
||||
box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton:active:hover,
|
||||
#highlighter-nodeinfobar-container:not([locked]) > #highlighter-nodeinfobar > #highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu {
|
||||
-moz-border-start-width: 1px;
|
||||
box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
-moz-margin-start: 6px;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu:-moz-locale-dir(rtl) {
|
||||
box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
|
||||
-moz-box-align: center;
|
||||
-moz-margin-start: -1px;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
|
||||
.highlighter-nodeinfobar-arrow {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
BIN
browser/themes/gnomestripe/devtools/dropmarker.png
Normal file
After Width: | Height: | Size: 218 B |
@ -131,6 +131,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/itemArrow-rtl.png (devtools/itemArrow-rtl.png)
|
||||
skin/classic/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png)
|
||||
skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-16-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 475 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.9 KiB |
@ -2772,8 +2772,21 @@ panel[dimmed="true"] {
|
||||
|
||||
/* Highlighter - Node Infobar */
|
||||
|
||||
#highlighter-nodeinfobar {
|
||||
color: hsl(200, 100%, 65%);
|
||||
border: 1px solid hsla(210, 19%, 63%, .5);
|
||||
border-radius: 3px;
|
||||
background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - text */
|
||||
|
||||
#highlighter-nodeinfobar-text {
|
||||
/* 100% - size of the buttons + margins */
|
||||
max-width: -moz-calc(100% - 2 * (26px + 6px));
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
html|*#highlighter-nodeinfobar-tagname {
|
||||
color: white;
|
||||
}
|
||||
@ -2786,16 +2799,52 @@ html|*#highlighter-nodeinfobar-pseudo-classes {
|
||||
color: hsl(20, 100%, 70%);
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
/* Highlighter - Node Infobar - buttons */
|
||||
|
||||
#highlighter-nodeinfobar {
|
||||
color: hsl(200, 100%, 65%);
|
||||
border: 1px solid hsla(210, 19%, 63%, .5);
|
||||
border-radius: 3px;
|
||||
padding: 8px 16px;
|
||||
background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
|
||||
.highlighter-nodeinfobar-button {
|
||||
-moz-appearance: none;
|
||||
border: 0 solid hsla(210,8%,5%,.45);
|
||||
padding: 0;
|
||||
width: 26px;
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-border-end-width: 1px;
|
||||
box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
-moz-margin-end: 6px;
|
||||
list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
|
||||
-moz-image-region: rect(0px 16px 16px 0px);
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton:-moz-locale-dir(rtl) {
|
||||
box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton:active:hover,
|
||||
#highlighter-nodeinfobar-container:not([locked]) > #highlighter-nodeinfobar > #highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu {
|
||||
-moz-border-start-width: 1px;
|
||||
box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
-moz-margin-start: 6px;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu:-moz-locale-dir(rtl) {
|
||||
box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
|
||||
-moz-box-align: center;
|
||||
-moz-margin-start: -1px;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
|
||||
.highlighter-nodeinfobar-arrow {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
BIN
browser/themes/pinstripe/devtools/dropmarker.png
Normal file
After Width: | Height: | Size: 218 B |
@ -172,6 +172,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png)
|
||||
skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
|
||||
skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 348 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.9 KiB |
@ -2694,8 +2694,21 @@ panel[dimmed="true"] {
|
||||
|
||||
/* Highlighter - Node Infobar */
|
||||
|
||||
#highlighter-nodeinfobar {
|
||||
color: hsl(200, 100%, 65%);
|
||||
border: 1px solid hsla(210, 19%, 63%, .5);
|
||||
border-radius: 3px;
|
||||
background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - text */
|
||||
|
||||
#highlighter-nodeinfobar-text {
|
||||
/* 100% - size of the buttons and margins */
|
||||
max-width: -moz-calc(100% - 2 * (26px + 6px));
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
html|*#highlighter-nodeinfobar-tagname {
|
||||
color: white;
|
||||
}
|
||||
@ -2708,16 +2721,52 @@ html|*#highlighter-nodeinfobar-pseudo-classes {
|
||||
color: hsl(20, 100%, 70%);
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
/* Highlighter - Node Infobar - buttons */
|
||||
|
||||
#highlighter-nodeinfobar {
|
||||
color: hsl(200, 100%, 65%);
|
||||
border: 1px solid hsla(210, 19%, 63%, .5);
|
||||
border-radius: 3px;
|
||||
padding: 8px 16px;
|
||||
background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
|
||||
.highlighter-nodeinfobar-button {
|
||||
-moz-appearance: none;
|
||||
border: 0 solid hsla(210,8%,5%,.45);
|
||||
padding: 0;
|
||||
width: 26px;
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-border-end-width: 1px;
|
||||
box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
-moz-margin-end: 6px;
|
||||
list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
|
||||
-moz-image-region: rect(0px 16px 16px 0px);
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton:-moz-locale-dir(rtl) {
|
||||
box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-inspectbutton:active:hover,
|
||||
#highlighter-nodeinfobar-container:not([locked]) > #highlighter-nodeinfobar > #highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu {
|
||||
-moz-border-start-width: 1px;
|
||||
box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
-moz-margin-start: 6px;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu:-moz-locale-dir(rtl) {
|
||||
box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
|
||||
}
|
||||
|
||||
#highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
|
||||
-moz-box-align: center;
|
||||
-moz-margin-start: -1px;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
|
||||
.highlighter-nodeinfobar-arrow {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
BIN
browser/themes/winstripe/devtools/dropmarker.png
Normal file
After Width: | Height: | Size: 218 B |
@ -158,6 +158,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/itemArrow-rtl.png (devtools/itemArrow-rtl.png)
|
||||
skin/classic/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png)
|
||||
skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
@ -331,6 +332,7 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/itemArrow-rtl.png (devtools/itemArrow-rtl.png)
|
||||
skin/classic/aero/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png)
|
||||
skin/classic/aero/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/aero/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/aero/browser/sync-throbber.png
|
||||
skin/classic/aero/browser/sync-16.png
|
||||
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 355 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.9 KiB |
31
client.py
@ -7,6 +7,9 @@ NSS_DIRS = (('dbm', 'mozilla/dbm'),
|
||||
('security/dbm', 'mozilla/security/dbm'))
|
||||
NSSCKBI_DIRS = (('security/nss/lib/ckfw/builtins', 'mozilla/security/nss/lib/ckfw/builtins'),)
|
||||
LIBFFI_DIRS = (('js/ctypes/libffi', 'libffi'),)
|
||||
WEBIDLPARSER_DIR = 'dom/bindings/parser'
|
||||
WEBIDLPARSER_REPO = 'https://hg.mozilla.org/users/khuey_mozilla.com/webidl-parser'
|
||||
WEBIDLPARSER_EXCLUSIONS = ['.hgignore', '.gitignore', '.hg', 'ply']
|
||||
|
||||
CVSROOT_MOZILLA = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot'
|
||||
CVSROOT_LIBFFI = ':pserver:anoncvs@sources.redhat.com:/cvs/libffi'
|
||||
@ -15,6 +18,7 @@ import os
|
||||
import sys
|
||||
import datetime
|
||||
import shutil
|
||||
import glob
|
||||
from optparse import OptionParser
|
||||
from subprocess import check_call
|
||||
|
||||
@ -30,7 +34,6 @@ def do_hg_pull(dir, repository, hg):
|
||||
fulldir = os.path.join(topsrcdir, dir)
|
||||
# clone if the dir doesn't exist, pull if it does
|
||||
if not os.path.exists(fulldir):
|
||||
fulldir = os.path.join(topsrcdir, dir)
|
||||
check_call_noisy([hg, 'clone', repository, fulldir])
|
||||
else:
|
||||
cmd = [hg, 'pull', '-u', '-R', fulldir]
|
||||
@ -40,6 +43,25 @@ def do_hg_pull(dir, repository, hg):
|
||||
check_call([hg, 'parent', '-R', fulldir,
|
||||
'--template=Updated to revision {node}.\n'])
|
||||
|
||||
def do_hg_replace(dir, repository, tag, exclusions, hg):
|
||||
"""
|
||||
Replace the contents of dir with the contents of repository, except for
|
||||
files matching exclusions.
|
||||
"""
|
||||
fulldir = os.path.join(topsrcdir, dir)
|
||||
if os.path.exists(fulldir):
|
||||
shutil.rmtree(fulldir)
|
||||
|
||||
assert not os.path.exists(fulldir)
|
||||
check_call_noisy([hg, 'clone', '-u', tag, repository, fulldir])
|
||||
|
||||
for thing in exclusions:
|
||||
for excluded in glob.iglob(os.path.join(fulldir, thing)):
|
||||
if os.path.isdir(excluded):
|
||||
shutil.rmtree(excluded)
|
||||
else:
|
||||
os.remove(excluded)
|
||||
|
||||
def do_cvs_export(modules, tag, cvsroot, cvs):
|
||||
"""Check out a CVS directory without CVS metadata, using "export"
|
||||
modules is a list of directories to check out and the corresponding
|
||||
@ -60,7 +82,7 @@ def do_cvs_export(modules, tag, cvsroot, cvs):
|
||||
cwd=os.path.join(topsrcdir, parent))
|
||||
print "CVS export end: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname")
|
||||
o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname | update_webidlparser tagname")
|
||||
o.add_option("--skip-mozilla", dest="skip_mozilla",
|
||||
action="store_true", default=False,
|
||||
help="Obsolete")
|
||||
@ -69,6 +91,8 @@ o.add_option("--cvs", dest="cvs", default=os.environ.get('CVS', 'cvs'),
|
||||
help="The location of the cvs binary")
|
||||
o.add_option("--cvsroot", dest="cvsroot",
|
||||
help="The CVSROOT (default for mozilla checkouts: %s)" % CVSROOT_MOZILLA)
|
||||
o.add_option("--hg", dest="hg", default=os.environ.get('HG', 'hg'),
|
||||
help="The location of the hg binary")
|
||||
|
||||
try:
|
||||
options, args = o.parse_args()
|
||||
@ -104,6 +128,9 @@ elif action in ('update_libffi'):
|
||||
if not options.cvsroot:
|
||||
options.cvsroot = CVSROOT_LIBFFI
|
||||
do_cvs_export(LIBFFI_DIRS, tag, options.cvsroot, options.cvs)
|
||||
elif action in ('update_webidlparser'):
|
||||
tag, = args[1:]
|
||||
do_hg_replace(WEBIDLPARSER_DIR, WEBIDLPARSER_REPO, tag, WEBIDLPARSER_EXCLUSIONS, options.hg)
|
||||
else:
|
||||
o.print_help()
|
||||
sys.exit(2)
|
||||
|
@ -6367,7 +6367,7 @@ dnl files in the updater. The --enable-signmar option is for building
|
||||
dnl the signmar program.
|
||||
dnl ========================================================
|
||||
|
||||
MOZ_ARG_ENABLE_BOOL(sign-mar,
|
||||
MOZ_ARG_ENABLE_BOOL(signmar,
|
||||
[ --enable-signmar Enable building the signmar program],
|
||||
MOZ_ENABLE_SIGNMAR=1,
|
||||
MOZ_ENABLE_SIGNMAR= )
|
||||
|
@ -7894,8 +7894,7 @@ namespace
|
||||
{
|
||||
|
||||
// Callback used by CopyFavicon to inform the favicon service that one URI
|
||||
// (mNewURI) has the same favicon URI (OnFaviconDataAvailable's aFaviconURI) as
|
||||
// another.
|
||||
// (mNewURI) has the same favicon URI (OnComplete's aFaviconURI) as another.
|
||||
class nsCopyFaviconCallback : public nsIFaviconDataCallback
|
||||
{
|
||||
public:
|
||||
@ -7907,9 +7906,14 @@ public:
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
OnFaviconDataAvailable(nsIURI *aFaviconURI, PRUint32 aDataLen,
|
||||
const PRUint8 *aData, const nsACString &aMimeType)
|
||||
OnComplete(nsIURI *aFaviconURI, PRUint32 aDataLen,
|
||||
const PRUint8 *aData, const nsACString &aMimeType)
|
||||
{
|
||||
// Continue only if there is an associated favicon.
|
||||
if (!aFaviconURI) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(aDataLen == 0,
|
||||
"We weren't expecting the callback to deliver data.");
|
||||
nsCOMPtr<mozIAsyncFavicons> favSvc =
|
||||
|
@ -1605,6 +1605,10 @@ nsGlobalWindow::SetScriptContext(nsIScriptContext *aScriptContext)
|
||||
// should probably assert the context is clean???
|
||||
aScriptContext->WillInitializeContext();
|
||||
|
||||
// We need point the context to the global window before initializing it
|
||||
// so that it can make various decisions properly.
|
||||
aScriptContext->SetGlobalObject(this);
|
||||
|
||||
nsresult rv = aScriptContext->InitContext();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -1874,8 +1878,6 @@ NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder)
|
||||
nsresult
|
||||
nsGlobalWindow::CreateOuterObject(nsGlobalWindow* aNewInner)
|
||||
{
|
||||
mContext->SetGlobalObject(this);
|
||||
|
||||
JSContext* cx = mContext->GetNativeContext();
|
||||
|
||||
if (IsChromeWindow()) {
|
||||
@ -7173,6 +7175,9 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs,
|
||||
|
||||
*aRetVal = nsnull;
|
||||
|
||||
if (Preferences::GetBool("dom.disable_window_showModalDialog", false))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
// Before bringing up the window/dialog, unsuppress painting and flush
|
||||
// pending reflows.
|
||||
EnsureReflowFlushAndPaint();
|
||||
@ -8928,6 +8933,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
|
||||
|
||||
// success!
|
||||
|
||||
NS_ENSURE_TRUE(domReturn, NS_OK);
|
||||
domReturn.swap(*aReturn);
|
||||
|
||||
if (aDoJSFixups) {
|
||||
|
@ -945,7 +945,11 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
|
||||
else
|
||||
newDefaultJSOptions &= ~JSOPTION_STRICT;
|
||||
|
||||
nsIScriptGlobalObject *global = context->GetGlobalObject();
|
||||
// The vanilla GetGlobalObject returns null if a global isn't set up on
|
||||
// the context yet. We can sometimes be call midway through context init,
|
||||
// So ask for the member directly instead.
|
||||
nsIScriptGlobalObject *global = context->GetGlobalObjectRef();
|
||||
|
||||
// XXX should we check for sysprin instead of a chrome window, to make
|
||||
// XXX components be covered by the chrome pref instead of the content one?
|
||||
nsCOMPtr<nsIDOMWindow> contentWindow(do_QueryInterface(global));
|
||||
|
@ -135,6 +135,8 @@ public:
|
||||
JSObject** aFunctionObject);
|
||||
|
||||
virtual nsIScriptGlobalObject *GetGlobalObject();
|
||||
inline nsIScriptGlobalObject *GetGlobalObjectRef() { return mGlobalObjectRef; };
|
||||
|
||||
virtual JSContext* GetNativeContext();
|
||||
virtual JSObject* GetNativeGlobal();
|
||||
virtual nsresult CreateNativeGlobalForInner(
|
||||
|
@ -75,7 +75,7 @@ bindinggen_dependencies := \
|
||||
$(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
|
||||
$(webidl_base)/%.webidl \
|
||||
$(NULL)
|
||||
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) -I$(srcdir)/parser \
|
||||
$(srcdir)/BindingGen.py $(ACCESSOR_OPT) header \
|
||||
$(srcdir)/Bindings.conf $*Binding \
|
||||
@ -84,7 +84,7 @@ $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
|
||||
$(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \
|
||||
$(webidl_base)/%.webidl \
|
||||
$(NULL)
|
||||
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) -I$(srcdir)/parser \
|
||||
$(srcdir)/BindingGen.py $(ACCESSOR_OPT) cpp \
|
||||
$(srcdir)/Bindings.conf $*Binding \
|
||||
@ -109,7 +109,7 @@ $(CACHE_DIR)/.done:
|
||||
|
||||
ParserResults.pkl: $(globalgen_dependencies) \
|
||||
$(addprefix $(webidl_base)/, $(webidl_files))
|
||||
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) -I$(srcdir)/parser \
|
||||
$(srcdir)/GlobalGen.py $(ACCESSOR_OPT) $(srcdir)/Bindings.conf $(webidl_base) \
|
||||
--cachedir=$(CACHE_DIR) \
|
||||
|
@ -69,14 +69,16 @@ def parseInt(literal):
|
||||
# Magic for creating enums
|
||||
def M_add_class_attribs(attribs):
|
||||
def foo(name, bases, dict_):
|
||||
for v, k in attribs:
|
||||
for v, k in enumerate(attribs):
|
||||
dict_[k] = v
|
||||
assert 'length' not in dict_
|
||||
dict_['length'] = len(attribs)
|
||||
return type(name, bases, dict_)
|
||||
return foo
|
||||
|
||||
def enum(*names):
|
||||
class Foo(object):
|
||||
__metaclass__ = M_add_class_attribs(enumerate(names))
|
||||
__metaclass__ = M_add_class_attribs(names)
|
||||
def __setattr__(self, name, value): # this makes it read-only
|
||||
raise NotImplementedError
|
||||
return Foo()
|
||||
@ -93,9 +95,8 @@ class WebIDLError(Exception):
|
||||
self.location)
|
||||
|
||||
class Location(object):
|
||||
_line = None
|
||||
|
||||
def __init__(self, lexer, lineno, lexpos, filename):
|
||||
self._line = None
|
||||
self._lineno = lineno
|
||||
self._lexpos = lexpos
|
||||
self._lexdata = lexer.lexdata
|
||||
@ -105,36 +106,47 @@ class Location(object):
|
||||
return self._lexpos == other._lexpos and \
|
||||
self._file == other._file
|
||||
|
||||
def filename(self):
|
||||
return self._file
|
||||
|
||||
def resolve(self):
|
||||
if self._line:
|
||||
return
|
||||
|
||||
startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
|
||||
endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
|
||||
self._line = self._lexdata[startofline:endofline]
|
||||
if endofline != -1:
|
||||
self._line = self._lexdata[startofline:endofline]
|
||||
else:
|
||||
self._line = self._lexdata[startofline:]
|
||||
self._colno = self._lexpos - startofline
|
||||
|
||||
def pointerline(self):
|
||||
def i():
|
||||
for i in xrange(0, self._colno):
|
||||
yield " "
|
||||
yield "^"
|
||||
|
||||
return "".join(i())
|
||||
|
||||
def get(self):
|
||||
self.resolve()
|
||||
return "%s line %s:%s" % (self._file, self._lineno, self._colno)
|
||||
|
||||
def _pointerline(self):
|
||||
return " " * self._colno + "^"
|
||||
|
||||
def __str__(self):
|
||||
self.resolve()
|
||||
return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
|
||||
self._line, self.pointerline())
|
||||
self._line, self._pointerline())
|
||||
|
||||
class BuiltinLocation(object):
|
||||
def __init__(self, text):
|
||||
self.msg = text
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, BuiltinLocation) and \
|
||||
self.msg == other.msg
|
||||
|
||||
def filename(self):
|
||||
return '<builtin>'
|
||||
|
||||
def resolve(self):
|
||||
pass
|
||||
|
||||
def get(self):
|
||||
return self.msg
|
||||
|
||||
@ -150,7 +162,7 @@ class IDLObject(object):
|
||||
self.userData = dict()
|
||||
|
||||
def filename(self):
|
||||
return self.location._file
|
||||
return self.location.filename()
|
||||
|
||||
def isInterface(self):
|
||||
return False
|
||||
@ -198,6 +210,11 @@ class IDLScope(IDLObject):
|
||||
return "::"
|
||||
|
||||
def ensureUnique(self, identifier, object):
|
||||
"""
|
||||
Ensure that there is at most one 'identifier' in scope ('self').
|
||||
Note that object can be None. This occurs if we end up here for an
|
||||
interface type we haven't seen yet.
|
||||
"""
|
||||
assert isinstance(identifier, IDLUnresolvedIdentifier)
|
||||
assert not object or isinstance(object, IDLObjectWithIdentifier)
|
||||
assert not object or object.identifier == identifier
|
||||
@ -300,6 +317,9 @@ class IDLUnresolvedIdentifier(IDLObject):
|
||||
object.identifier = identifier
|
||||
return identifier
|
||||
|
||||
def finish(self):
|
||||
assert False # Should replace with a resolved identifier first.
|
||||
|
||||
class IDLObjectWithIdentifier(IDLObject):
|
||||
def __init__(self, location, parentScope, identifier):
|
||||
IDLObject.__init__(self, location)
|
||||
@ -368,9 +388,8 @@ class IDLInterface(IDLObjectWithScope):
|
||||
|
||||
self.parent = parent
|
||||
self._callback = False
|
||||
|
||||
self._finished = False
|
||||
self.members = list(members) # clone the list
|
||||
assert iter(self.members) # Assert it's iterable
|
||||
|
||||
IDLObjectWithScope.__init__(self, location, parentScope, name)
|
||||
|
||||
@ -404,7 +423,7 @@ class IDLInterface(IDLObjectWithScope):
|
||||
return retval
|
||||
|
||||
def finish(self, scope):
|
||||
if hasattr(self, "_finished"):
|
||||
if self._finished:
|
||||
return
|
||||
|
||||
self._finished = True
|
||||
@ -416,7 +435,6 @@ class IDLInterface(IDLObjectWithScope):
|
||||
self.parent = parent
|
||||
|
||||
assert iter(self.members)
|
||||
members = None
|
||||
|
||||
if self.parent:
|
||||
self.parent.finish(scope)
|
||||
@ -427,19 +445,6 @@ class IDLInterface(IDLObjectWithScope):
|
||||
else:
|
||||
members = list(self.members)
|
||||
|
||||
SpecialType = enum(
|
||||
'NamedGetter',
|
||||
'NamedSetter',
|
||||
'NamedCreator',
|
||||
'NamedDeleter',
|
||||
'IndexedGetter',
|
||||
'IndexedSetter',
|
||||
'IndexedCreator',
|
||||
'IndexedDeleter'
|
||||
)
|
||||
|
||||
specialMembersSeen = [False for i in range(8)]
|
||||
|
||||
def memberNotOnParentChain(member, iface):
|
||||
assert iface
|
||||
|
||||
@ -451,59 +456,39 @@ class IDLInterface(IDLObjectWithScope):
|
||||
return False
|
||||
return memberNotOnParentChain(member, iface.parent)
|
||||
|
||||
# Ensure that there's at most one of each {named,indexed}
|
||||
# {getter,setter,creator,deleter}.
|
||||
specialMembersSeen = set()
|
||||
for member in members:
|
||||
if memberNotOnParentChain(member, self):
|
||||
member.resolve(self)
|
||||
|
||||
if member.tag == IDLInterfaceMember.Tags.Method:
|
||||
if member.isGetter():
|
||||
if member.isNamed():
|
||||
if specialMembersSeen[SpecialType.NamedGetter]:
|
||||
raise WebIDLError("Multiple named getters on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.NamedGetter] = True
|
||||
else:
|
||||
assert member.isIndexed()
|
||||
if specialMembersSeen[SpecialType.IndexedGetter]:
|
||||
raise WebIDLError("Multiple indexed getters on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.IndexedGetter] = True
|
||||
if member.isSetter():
|
||||
if member.isNamed():
|
||||
if specialMembersSeen[SpecialType.NamedSetter]:
|
||||
raise WebIDLError("Multiple named setters on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.NamedSetter] = True
|
||||
else:
|
||||
assert member.isIndexed()
|
||||
if specialMembersSeen[SpecialType.IndexedSetter]:
|
||||
raise WebIDLError("Multiple indexed setters on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.IndexedSetter] = True
|
||||
if member.isCreator():
|
||||
if member.isNamed():
|
||||
if specialMembersSeen[SpecialType.NamedCreator]:
|
||||
raise WebIDLError("Multiple named creators on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.NamedCreator] = True
|
||||
else:
|
||||
assert member.isIndexed()
|
||||
if specialMembersSeen[SpecialType.IndexedCreator]:
|
||||
raise WebIDLError("Multiple indexed creators on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.IndexedCreator] = True
|
||||
if member.isDeleter():
|
||||
if member.isNamed():
|
||||
if specialMembersSeen[SpecialType.NamedDeleter]:
|
||||
raise WebIDLError("Multiple named deleters on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.NamedDeleter] = True
|
||||
else:
|
||||
assert member.isIndexed()
|
||||
if specialMembersSeen[SpecialType.IndexedDeleter]:
|
||||
raise WebIDLError("Multiple indexed Deleters on %s" % (self),
|
||||
self.location)
|
||||
specialMembersSeen[SpecialType.IndexedDeleter] = True
|
||||
|
||||
if member.tag != IDLInterfaceMember.Tags.Method:
|
||||
continue
|
||||
|
||||
if member.isGetter():
|
||||
memberType = "getters"
|
||||
elif member.isSetter():
|
||||
memberType = "setters"
|
||||
elif member.isCreator():
|
||||
memberType = "creators"
|
||||
elif member.isDeleter():
|
||||
memberType = "deleters"
|
||||
else:
|
||||
continue
|
||||
|
||||
if member.isNamed():
|
||||
memberType = "named " + memberType
|
||||
elif member.isIndexed():
|
||||
memberType = "indexed " + memberType
|
||||
else:
|
||||
continue
|
||||
|
||||
if memberType in specialMembersSeen:
|
||||
raise WebIDLError("Multiple " + memberType + " on %s" % (self),
|
||||
self.location)
|
||||
|
||||
specialMembersSeen.add(memberType)
|
||||
|
||||
for member in self.members:
|
||||
member.finish(scope)
|
||||
@ -529,7 +514,7 @@ class IDLInterface(IDLObjectWithScope):
|
||||
return depth
|
||||
|
||||
def hasConstants(self):
|
||||
return reduce(lambda b, m: b or m.isConst(), self.members, False)
|
||||
return any(m.isConst() for m in self.members)
|
||||
|
||||
def hasInterfaceObject(self):
|
||||
if self.isCallback():
|
||||
@ -567,10 +552,7 @@ class IDLInterface(IDLObjectWithScope):
|
||||
identifier = IDLUnresolvedIdentifier(self.location, "constructor",
|
||||
allowForbidden=True)
|
||||
|
||||
method = IDLMethod(self.location, identifier, retType, args,
|
||||
False, False, False, False, False, False,
|
||||
False, False)
|
||||
|
||||
method = IDLMethod(self.location, identifier, retType, args)
|
||||
method.resolve(self)
|
||||
|
||||
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
|
||||
@ -763,6 +745,12 @@ class IDLNullableType(IDLType):
|
||||
def isString(self):
|
||||
return self.inner.isString()
|
||||
|
||||
def isFloat(self):
|
||||
return self.inner.isFloat()
|
||||
|
||||
def isInteger(self):
|
||||
return self.inner.isInteger()
|
||||
|
||||
def isVoid(self):
|
||||
return False
|
||||
|
||||
@ -772,6 +760,9 @@ class IDLNullableType(IDLType):
|
||||
def isArray(self):
|
||||
return self.inner.isArray()
|
||||
|
||||
def isArrayBuffer(self):
|
||||
return self.inner.isArrayBuffer()
|
||||
|
||||
def isDictionary(self):
|
||||
return self.inner.isDictionary()
|
||||
|
||||
@ -797,7 +788,7 @@ class IDLNullableType(IDLType):
|
||||
return self
|
||||
|
||||
def unroll(self):
|
||||
return self.inner
|
||||
return self.inner.unroll()
|
||||
|
||||
def isDistinguishableFrom(self, other):
|
||||
if other.nullable():
|
||||
@ -835,18 +826,19 @@ class IDLSequenceType(IDLType):
|
||||
return True
|
||||
|
||||
def isArray(self):
|
||||
return self.inner.isArray()
|
||||
return False
|
||||
|
||||
def isDictionary(self):
|
||||
return self.inner.isDictionary()
|
||||
return False
|
||||
|
||||
def isInterface(self):
|
||||
return self.inner.isInterface()
|
||||
return False
|
||||
|
||||
def isEnum(self):
|
||||
return self.inner.isEnum();
|
||||
return False
|
||||
|
||||
def tag(self):
|
||||
# XXXkhuey this is probably wrong.
|
||||
return self.inner.tag()
|
||||
|
||||
def resolveType(self, parentScope):
|
||||
@ -858,10 +850,11 @@ class IDLSequenceType(IDLType):
|
||||
|
||||
def complete(self, scope):
|
||||
self.inner = self.inner.complete(scope)
|
||||
self.name = self.inner.name
|
||||
return self
|
||||
|
||||
def unroll(self):
|
||||
return self.inner
|
||||
return self.inner.unroll()
|
||||
|
||||
def isDistinguishableFrom(self, other):
|
||||
return (other.isPrimitive() or other.isString() or other.isEnum() or
|
||||
@ -895,32 +888,33 @@ class IDLArrayType(IDLType):
|
||||
return False
|
||||
|
||||
def isPrimitive(self):
|
||||
return self.inner.isPrimitive()
|
||||
return False
|
||||
|
||||
def isString(self):
|
||||
return self.inner.isString()
|
||||
return False
|
||||
|
||||
def isVoid(self):
|
||||
return False
|
||||
|
||||
def isSequence(self):
|
||||
assert not self.inner.isSequence()
|
||||
return self.inner.isSequence()
|
||||
return False
|
||||
|
||||
def isArray(self):
|
||||
return True
|
||||
|
||||
def isDictionary(self):
|
||||
assert not self.inner.isDictionary()
|
||||
return self.inner.isDictionary()
|
||||
return False
|
||||
|
||||
def isInterface(self):
|
||||
return self.inner.isInterface()
|
||||
return False
|
||||
|
||||
def isEnum(self):
|
||||
return self.inner.isEnum()
|
||||
return False
|
||||
|
||||
def tag(self):
|
||||
# XXXkhuey this is probably wrong.
|
||||
return self.inner.tag()
|
||||
|
||||
def resolveType(self, parentScope):
|
||||
@ -932,10 +926,11 @@ class IDLArrayType(IDLType):
|
||||
|
||||
def complete(self, scope):
|
||||
self.inner = self.inner.complete(scope)
|
||||
self.name = self.inner.name
|
||||
return self
|
||||
|
||||
def unroll(self):
|
||||
return self.inner
|
||||
return self.inner.unroll()
|
||||
|
||||
def isDistinguishableFrom(self, other):
|
||||
return (other.isPrimitive() or other.isString() or other.isEnum() or
|
||||
@ -995,7 +990,7 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
|
||||
return self.inner.tag()
|
||||
|
||||
def unroll(self):
|
||||
return self.inner
|
||||
return self.inner.unroll()
|
||||
|
||||
def isDistinguishableFrom(self, other):
|
||||
return self.inner.isDistinguishableFrom(other)
|
||||
@ -1041,6 +1036,10 @@ class IDLWrapperType(IDLType):
|
||||
def isEnum(self):
|
||||
return isinstance(self.inner, IDLEnum)
|
||||
|
||||
def resolveType(self, parentScope):
|
||||
assert isinstance(parentScope, IDLScope)
|
||||
self.inner.resolve(parentScope)
|
||||
|
||||
def isComplete(self):
|
||||
return True
|
||||
|
||||
@ -1122,32 +1121,32 @@ class IDLBuiltinType(IDLType):
|
||||
def __init__(self, location, name, type):
|
||||
IDLType.__init__(self, location, name)
|
||||
self.builtin = True
|
||||
self.type = type
|
||||
self._typeTag = type
|
||||
|
||||
def isPrimitive(self):
|
||||
return self.type <= IDLBuiltinType.Types.double
|
||||
return self._typeTag <= IDLBuiltinType.Types.double
|
||||
|
||||
def isString(self):
|
||||
return self.type == IDLBuiltinType.Types.domstring
|
||||
return self._typeTag == IDLBuiltinType.Types.domstring
|
||||
|
||||
def isInteger(self):
|
||||
return self.type <= IDLBuiltinType.Types.unsigned_long_long
|
||||
return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
|
||||
|
||||
def isArrayBuffer(self):
|
||||
return self.type == IDLBuiltinType.Types.ArrayBuffer
|
||||
return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
|
||||
|
||||
def isInterface(self):
|
||||
# ArrayBuffers are interface types per the TypedArray spec,
|
||||
# but we handle them as builtins because SpiderMonkey implements
|
||||
# ArrayBuffers.
|
||||
return self.type == IDLBuiltinType.Types.ArrayBuffer
|
||||
return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
|
||||
|
||||
def isFloat(self):
|
||||
return self.type == IDLBuiltinType.Types.float or \
|
||||
self.type == IDLBuiltinType.Types.double
|
||||
return self._typeTag == IDLBuiltinType.Types.float or \
|
||||
self._typeTag == IDLBuiltinType.Types.double
|
||||
|
||||
def tag(self):
|
||||
return IDLBuiltinType.TagLookup[self.type]
|
||||
return IDLBuiltinType.TagLookup[self._typeTag]
|
||||
|
||||
def isDistinguishableFrom(self, other):
|
||||
if self.isPrimitive() or self.isString():
|
||||
@ -1280,7 +1279,7 @@ class IDLValue(IDLObject):
|
||||
|
||||
# We're both integer types. See if we fit.
|
||||
|
||||
(min, max) = integerTypeSizes[type.type]
|
||||
(min, max) = integerTypeSizes[type._typeTag]
|
||||
if self.value <= max and self.value >= min:
|
||||
# Promote
|
||||
return IDLValue(self.location, type, self.value)
|
||||
@ -1492,8 +1491,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
)
|
||||
|
||||
def __init__(self, location, identifier, returnType, arguments,
|
||||
static, getter, setter, creator, deleter, specialType, legacycaller,
|
||||
stringifier):
|
||||
static=False, getter=False, setter=False, creator=False,
|
||||
deleter=False, specialType=NamedOrIndexed.Neither,
|
||||
legacycaller=False, stringifier=False):
|
||||
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
|
||||
IDLInterfaceMember.__init__(self, location, identifier,
|
||||
IDLInterfaceMember.Tags.Method)
|
||||
|
||||
@ -1678,6 +1679,7 @@ class Tokenizer(object):
|
||||
def t_INTEGER(self, t):
|
||||
r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
|
||||
try:
|
||||
# Can't use int(), because that doesn't handle octal properly.
|
||||
t.value = parseInt(t.value)
|
||||
except:
|
||||
raise WebIDLError("Invalid integer literal",
|
||||
@ -2261,8 +2263,9 @@ class Parser(Tokenizer):
|
||||
"legacycaller" if legacycaller else ""), allowDoubleUnderscore=True)
|
||||
|
||||
method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
|
||||
static, getter, setter, creator, deleter, specialType,
|
||||
legacycaller, False)
|
||||
static=static, getter=getter, setter=setter, creator=creator,
|
||||
deleter=deleter, specialType=specialType,
|
||||
legacycaller=legacycaller, stringifier=False)
|
||||
p[0] = method
|
||||
|
||||
def p_QualifiersStatic(self, p):
|
||||
@ -2861,7 +2864,14 @@ class Parser(Tokenizer):
|
||||
for production in self._productions:
|
||||
production.finish(self.globalScope())
|
||||
|
||||
return set(self._productions)
|
||||
# De-duplicate self._productions, without modifying its order.
|
||||
seen = set()
|
||||
result = []
|
||||
for p in self._productions:
|
||||
if p not in seen:
|
||||
seen.add(p)
|
||||
result.append(p)
|
||||
return result
|
||||
|
||||
def reset(self):
|
||||
return Parser()
|
||||
|
@ -1 +0,0 @@
|
||||
__all__ = ['WebIDL']
|
@ -37,36 +37,76 @@
|
||||
|
||||
import os, sys
|
||||
import glob
|
||||
import optparse
|
||||
import traceback
|
||||
import WebIDL
|
||||
|
||||
class TestHarness(object):
|
||||
def __init__(self, test, verbose):
|
||||
self.test = test
|
||||
self.verbose = verbose
|
||||
self.printed_intro = False
|
||||
|
||||
def start(self):
|
||||
if self.verbose:
|
||||
self.maybe_print_intro()
|
||||
|
||||
def finish(self):
|
||||
if self.verbose or self.printed_intro:
|
||||
print "Finished test %s" % self.test
|
||||
|
||||
def maybe_print_intro(self):
|
||||
if not self.printed_intro:
|
||||
print "Starting test %s" % self.test
|
||||
self.printed_intro = True
|
||||
|
||||
def test_pass(self, msg):
|
||||
if self.verbose:
|
||||
print "TEST-PASS | %s" % msg
|
||||
|
||||
def test_fail(self, msg):
|
||||
self.maybe_print_intro()
|
||||
print "TEST-UNEXPECTED-FAIL | %s" % msg
|
||||
|
||||
def ok(self, condition, msg):
|
||||
if condition:
|
||||
print "TEST-PASS | %s" % msg
|
||||
self.test_pass(msg)
|
||||
else:
|
||||
print "TEST-UNEXPECTED-FAIL | %s" % msg
|
||||
self.test_fail(msg)
|
||||
|
||||
def check(self, a, b, msg):
|
||||
if a == b:
|
||||
print "TEST-PASS | %s" % msg
|
||||
self.test_pass(msg)
|
||||
else:
|
||||
print "TEST-UNEXPECTED-FAIL | %s" % msg
|
||||
self.test_fail(msg)
|
||||
print "\tGot %s expected %s" % (a, b)
|
||||
|
||||
def run_tests():
|
||||
harness = TestHarness()
|
||||
def run_tests(tests, verbose):
|
||||
testdir = os.path.join(os.path.dirname(__file__), 'tests')
|
||||
if not tests:
|
||||
tests = glob.iglob(os.path.join(testdir, "*.py"))
|
||||
sys.path.append(testdir)
|
||||
|
||||
tests = glob.iglob("tests/*.py")
|
||||
sys.path.append("./tests")
|
||||
for test in tests:
|
||||
(testpath, ext) = os.path.splitext(os.path.basename(test))
|
||||
_test = __import__(testpath, globals(), locals(), ['WebIDLTest'])
|
||||
#try:
|
||||
_test.WebIDLTest.__call__(WebIDL.Parser(), harness)
|
||||
#except:
|
||||
# print "TEST-UNEXPECTED-FAIL | Unhandled exception in Test %s" % testpath
|
||||
# print sys.exc_info()[0]
|
||||
print "Test %s Complete\n" % testpath
|
||||
|
||||
harness = TestHarness(test, verbose)
|
||||
harness.start()
|
||||
try:
|
||||
_test.WebIDLTest.__call__(WebIDL.Parser(), harness)
|
||||
except:
|
||||
print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s" % testpath
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
harness.finish()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests()
|
||||
usage = """%prog [OPTIONS] [TESTS]
|
||||
Where TESTS are relative to the tests directory."""
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
|
||||
help="Don't print passing tests.")
|
||||
options, tests = parser.parse_args()
|
||||
|
||||
run_tests(tests, verbose=options.verbose)
|
||||
|
13
dom/bindings/parser/tests/test_array_of_interface.py
Normal file
@ -0,0 +1,13 @@
|
||||
import WebIDL
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
parser.parse("""
|
||||
interface A {
|
||||
attribute long a;
|
||||
};
|
||||
|
||||
interface B {
|
||||
attribute A[] b;
|
||||
};
|
||||
""");
|
||||
parser.finish()
|
11
dom/bindings/parser/tests/test_builtin_filename.py
Normal file
@ -0,0 +1,11 @@
|
||||
import WebIDL
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
parser.parse("""
|
||||
interface Test {
|
||||
attribute long b;
|
||||
};
|
||||
""");
|
||||
|
||||
attr = parser.finish()[0].members[0]
|
||||
harness.check(attr.type.filename(), '<builtin>', 'Filename on builtin type')
|
@ -62,14 +62,14 @@ def WebIDLTest(parser, harness):
|
||||
"Should be an IDLInterface")
|
||||
|
||||
checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
|
||||
"constructor", [("TestConstructorNoArgs", [])])
|
||||
"constructor", [("TestConstructorNoArgs (Wrapper)", [])])
|
||||
checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
|
||||
"constructor",
|
||||
[("TestConstructorWithArgs",
|
||||
[("TestConstructorWithArgs (Wrapper)",
|
||||
[("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
|
||||
checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
|
||||
"constructor",
|
||||
[("TestConstructorOverloads",
|
||||
[("TestConstructorOverloads (Wrapper)",
|
||||
[("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
|
||||
("TestConstructorOverloads",
|
||||
("TestConstructorOverloads (Wrapper)",
|
||||
[("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
|
||||
|
15
dom/bindings/parser/tests/test_deduplicate.py
Normal file
@ -0,0 +1,15 @@
|
||||
import WebIDL
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
parser.parse("""
|
||||
interface Foo;
|
||||
interface Bar;
|
||||
interface Foo;
|
||||
""");
|
||||
|
||||
results = parser.finish()
|
||||
|
||||
# There should be no duplicate interfaces in the result.
|
||||
expectedNames = sorted(['Foo', 'Bar'])
|
||||
actualNames = sorted(map(lambda iface: iface.identifier.name, results))
|
||||
harness.check(actualNames, expectedNames, "Parser shouldn't output duplicate names.")
|
@ -47,7 +47,7 @@ def WebIDLTest(parser, harness):
|
||||
harness.check(len(signatures), 1, "Expect one signature")
|
||||
|
||||
(returnType, arguments) = signatures[0]
|
||||
harness.check(str(returnType), "TestEnum", "Method type is the correct name")
|
||||
harness.check(str(returnType), "TestEnum (Wrapper)", "Method type is the correct name")
|
||||
harness.check(len(arguments), 1, "Method has the right number of arguments")
|
||||
arg = arguments[0]
|
||||
harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
|
||||
@ -58,4 +58,4 @@ def WebIDLTest(parser, harness):
|
||||
"Attr has correct QName")
|
||||
harness.check(attr.identifier.name, "foo", "Attr has correct name")
|
||||
|
||||
harness.check(str(attr.type), "TestEnum", "Attr type is the correct name")
|
||||
harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")
|
||||
|
20
dom/bindings/parser/tests/test_error_colno.py
Normal file
@ -0,0 +1,20 @@
|
||||
import WebIDL
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
# Check that error messages put the '^' in the right place.
|
||||
|
||||
threw = False
|
||||
input = 'interface ?'
|
||||
try:
|
||||
parser.parse(input)
|
||||
results = parser.finish()
|
||||
except WebIDL.WebIDLError as e:
|
||||
threw = True
|
||||
lines = str(e).split('\n')
|
||||
|
||||
harness.check(len(lines), 3, 'Expected number of lines in error message')
|
||||
harness.check(lines[1], input, 'Second line shows error')
|
||||
harness.check(lines[2], ' ' * (len(input) - 1) + '^',
|
||||
'Correct column pointer in error message')
|
||||
|
||||
harness.ok(threw, "Should have thrown.")
|
134
dom/bindings/parser/tests/test_nullable_equivalency.py
Normal file
@ -0,0 +1,134 @@
|
||||
import WebIDL
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
parser.parse("""
|
||||
interface TestNullableEquivalency1 {
|
||||
attribute long a;
|
||||
attribute long? b;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency2 {
|
||||
attribute ArrayBuffer a;
|
||||
attribute ArrayBuffer? b;
|
||||
};
|
||||
|
||||
/* Not implemented */
|
||||
/*dictionary TestNullableEquivalency3Dict {
|
||||
long foo = 42;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency3 {
|
||||
attribute Test3Dict a;
|
||||
attribute Test3Dict? b;
|
||||
};*/
|
||||
|
||||
enum TestNullableEquivalency4Enum {
|
||||
"Foo",
|
||||
"Bar"
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency4 {
|
||||
attribute TestNullableEquivalency4Enum a;
|
||||
attribute TestNullableEquivalency4Enum? b;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency5 {
|
||||
attribute TestNullableEquivalency4 a;
|
||||
attribute TestNullableEquivalency4? b;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency6 {
|
||||
attribute boolean a;
|
||||
attribute boolean? b;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency7 {
|
||||
attribute DOMString a;
|
||||
attribute DOMString? b;
|
||||
};
|
||||
|
||||
/* Not implemented. */
|
||||
/*interface TestNullableEquivalency8 {
|
||||
attribute float a;
|
||||
attribute float? b;
|
||||
};*/
|
||||
|
||||
interface TestNullableEquivalency8 {
|
||||
attribute double a;
|
||||
attribute double? b;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency9 {
|
||||
attribute object a;
|
||||
attribute object? b;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency10 {
|
||||
attribute double[] a;
|
||||
attribute double[]? b;
|
||||
};
|
||||
|
||||
interface TestNullableEquivalency11 {
|
||||
attribute TestNullableEquivalency9[] a;
|
||||
attribute TestNullableEquivalency9[]? b;
|
||||
};
|
||||
""")
|
||||
|
||||
for decl in parser.finish():
|
||||
if decl.isInterface():
|
||||
checkEquivalent(decl, harness)
|
||||
|
||||
def checkEquivalent(iface, harness):
|
||||
type1 = iface.members[0].type
|
||||
type2 = iface.members[1].type
|
||||
|
||||
harness.check(type1.nullable(), False, 'attr1 should not be nullable')
|
||||
harness.check(type2.nullable(), True, 'attr2 should be nullable')
|
||||
|
||||
# We don't know about type1, but type2, the nullable type, definitely
|
||||
# shouldn't be builtin.
|
||||
harness.check(type2.builtin, False, 'attr2 should not be builtin')
|
||||
|
||||
# Ensure that all attributes of type2 match those in type1, except for:
|
||||
# - names on an ignore list,
|
||||
# - names beginning with '_',
|
||||
# - functions which throw when called with no args, and
|
||||
# - class-level non-callables ("static variables").
|
||||
#
|
||||
# Yes, this is an ugly, fragile hack. But it finds bugs...
|
||||
for attr in dir(type1):
|
||||
if attr.startswith('_') or \
|
||||
attr in ['nullable', 'builtin', 'filename', 'location',
|
||||
'inner', 'QName'] or \
|
||||
(hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
|
||||
continue
|
||||
|
||||
a1 = getattr(type1, attr)
|
||||
|
||||
if callable(a1):
|
||||
try:
|
||||
v1 = a1()
|
||||
except:
|
||||
# Can't call a1 with no args, so skip this attriute.
|
||||
continue
|
||||
|
||||
try:
|
||||
a2 = getattr(type2, attr)
|
||||
except:
|
||||
harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
|
||||
continue
|
||||
|
||||
if not callable(a2):
|
||||
harness.ok(False, "%s attribute on type %s in %s wasn't callable" % (attr, type2, iface))
|
||||
continue
|
||||
|
||||
v2 = a2()
|
||||
harness.check(v2, v1, '%s method return value' % attr)
|
||||
else:
|
||||
try:
|
||||
a2 = getattr(type2, attr)
|
||||
except:
|
||||
harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
|
||||
continue
|
||||
|
||||
harness.check(a2, a1, '%s attribute should match' % attr)
|