Merge m-c to s-c.

This commit is contained in:
Richard Newman 2012-04-14 01:44:24 -07:00
commit 6302cbc2a6
286 changed files with 10629 additions and 9790 deletions

View File

@ -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])

View File

@ -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);

View File

@ -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];

View File

@ -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()

View 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
};

View File

@ -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);

View File

@ -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

View File

@ -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');"

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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 \

View File

@ -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;
}

View File

@ -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();
});
}

View File

@ -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

View File

@ -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));
},
/**

View File

@ -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;

View File

@ -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.

View File

@ -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();

View File

@ -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) {

View File

@ -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() {

View File

@ -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));

View File

@ -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);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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);

View File

@ -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>

View File

@ -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);

View File

@ -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);
});
}

View File

@ -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);

View File

@ -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">

View File

@ -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">

View File

@ -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;

View File

@ -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/ -->

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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);
});
}

View File

@ -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);

View File

@ -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.");

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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() {

View File

@ -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() {

View File

@ -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>

View File

@ -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) {

View File

@ -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);
});
});

View File

@ -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);

View File

@ -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);

View File

@ -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);
});
});

View File

@ -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);
});

View File

@ -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);

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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 = "&nbsp;";
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");
});

View File

@ -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");
}
}

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -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)

View File

@ -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= )

View File

@ -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 =

View File

@ -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) {

View File

@ -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));

View File

@ -135,6 +135,8 @@ public:
JSObject** aFunctionObject);
virtual nsIScriptGlobalObject *GetGlobalObject();
inline nsIScriptGlobalObject *GetGlobalObjectRef() { return mGlobalObjectRef; };
virtual JSContext* GetNativeContext();
virtual JSObject* GetNativeGlobal();
virtual nsresult CreateNativeGlobalForInner(

View File

@ -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) \

View File

@ -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()

View File

@ -1 +0,0 @@
__all__ = ['WebIDL']

View File

@ -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)

View 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()

View 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')

View File

@ -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)])])

View 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.")

View File

@ -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")

View 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.")

View 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)

Some files were not shown because too many files have changed in this diff Show More