Merge fx-team to m-c. a=merge

CLOSED TREE
This commit is contained in:
Ryan VanderMeulen 2015-08-28 15:33:57 -04:00
commit 5d40e3bba8
416 changed files with 1431 additions and 312 deletions

View File

@ -1,10 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
add_task(function*() {
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: "http://example.com/",
url: "http://bug1105244.example.com/",
title: "test" });
registerCleanupFunction(function* () {
@ -38,16 +34,16 @@ function sendDelete() {
}
function* testDelete() {
yield promiseAutocompleteResultPopup("exam");
yield promiseAutocompleteResultPopup("bug1105244");
// move to the start.
sendHome();
// delete the first few chars - each delete should operate on the input field.
sendDelete();
Assert.equal(gURLBar.inputField.value, "xam");
Assert.equal(gURLBar.inputField.value, "ug1105244");
yield promisePopupShown(gURLBar.popup);
sendDelete();
Assert.equal(gURLBar.inputField.value, "am");
Assert.equal(gURLBar.inputField.value, "g1105244");
}

View File

@ -8,16 +8,13 @@ add_task(function* () {
const BOOL = ctypes.bool;
const LPCTSTR = ctypes.char16_t.ptr;
const DWORD = ctypes.uint32_t;
const LPDWORD = DWORD.ptr;
let wininet = ctypes.open("Wininet");
do_register_cleanup(() => {
try {
wininet.close();
} catch (ex) {}
});
/*
BOOL InternetSetCookie(
BOOL InternetSetCookieW(
_In_ LPCTSTR lpszUrl,
_In_ LPCTSTR lpszCookieName,
_In_ LPCTSTR lpszCookieData
@ -30,24 +27,69 @@ add_task(function* () {
LPCTSTR,
LPCTSTR);
let expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
/*
BOOL InternetGetCookieW(
_In_ LPCTSTR lpszUrl,
_In_ LPCTSTR lpszCookieName,
_Out_ LPCTSTR lpszCookieData,
_Inout_ LPDWORD lpdwSize
);
*/
let getIECookie = wininet.declare("InternetGetCookieW",
ctypes.default_abi,
BOOL,
LPCTSTR,
LPCTSTR,
LPCTSTR,
LPDWORD);
// We need to randomize the cookie to avoid clashing with other cookies
// that might have been set by previous tests and not properly cleared.
let date = (new Date()).getDate();
const COOKIE = {
host: "mycookietest.com",
get host() {
return new URL(this.href).host;
},
href: `http://mycookietest.${Math.random()}.com`,
name: "testcookie",
value: "testvalue",
expiry
expiry: new Date(new Date().setDate(date + 2))
};
let data = ctypes.char16_t.array()(256);
let sizeRef = DWORD(256).address();
// Sanity check.
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0,
"There are no cookies initially");
do_register_cleanup(() => {
// Remove the cookie.
try {
let expired = new Date(new Date().setDate(date - 2));
let rv = setIECookie(COOKIE.href, COOKIE.name,
`; expires=${expired.toUTCString()}`);
Assert.ok(rv, "Expired the IE cookie");
Assert.ok(!getIECookie(COOKIE.href, COOKIE.name, data, sizeRef),
"The cookie has been properly removed");
} catch (ex) {}
// Close the library.
try {
wininet.close();
} catch (ex) {}
});
// Create the persistent cookie in IE.
let value = COOKIE.name + " = " + COOKIE.value +"; expires = " +
COOKIE.expiry.toUTCString();
let rv = setIECookie(new URL("http://" + COOKIE.host).href, null, value);
Assert.ok(rv, "Added a persistent IE cookie");
let value = `${COOKIE.value}; expires=${COOKIE.expiry.toUTCString()}`;
let rv = setIECookie(COOKIE.href, COOKIE.name, value);
Assert.ok(rv, "Added a persistent IE cookie: " + value);
// Sanity check the cookie has been created.
Assert.ok(getIECookie(COOKIE.href, COOKIE.name, data, sizeRef),
"Found the added persistent IE cookie");
do_print("Found cookie: " + data.readString());
Assert.equal(data.readString(), `${COOKIE.name}=${COOKIE.value}`,
"Found the expected cookie");
// Sanity check that there are no cookies.
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0,
"There are no cookies initially");
// Migrate cookies.
yield promiseMigration(migrator, MigrationUtils.resourceTypes.COOKIES);

View File

@ -31,17 +31,11 @@ Cu.import("resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTransactions",
"resource://gre/modules/PlacesTransactions.jsm");
#ifdef MOZ_SERVICES_CLOUDSYNC
XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
"resource://gre/modules/CloudSync.jsm");
#else
let CloudSync = null;
#endif
#ifdef MOZ_SERVICES_SYNC
XPCOMUtils.defineLazyModuleGetter(this, "Weave",
"resource://services-sync/main.js");
#endif
// copied from utilityOverlay.js
const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";

View File

@ -2,6 +2,7 @@
* 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/. */
Components.utils.import("resource://gre/modules/AppConstants.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
@ -355,12 +356,12 @@ PlacesViewBase.prototype = {
PlacesUtils.livemarks.getLivemark({ id: itemId })
.then(aLivemark => {
element.setAttribute("livemark", "true");
#ifdef XP_MACOSX
// OS X native menubar doesn't track list-style-images since
// it doesn't have a frame (bug 733415). Thus enforce updating.
element.setAttribute("image", "");
element.removeAttribute("image");
#endif
if (AppConstants.platform === "macosx") {
// OS X native menubar doesn't track list-style-images since
// it doesn't have a frame (bug 733415). Thus enforce updating.
element.setAttribute("image", "");
element.removeAttribute("image");
}
this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
}, () => undefined);
}
@ -540,12 +541,12 @@ PlacesViewBase.prototype = {
let menu = elt.parentNode;
if (!menu.hasAttribute("livemark")) {
menu.setAttribute("livemark", "true");
#ifdef XP_MACOSX
// OS X native menubar doesn't track list-style-images since
// it doesn't have a frame (bug 733415). Thus enforce updating.
menu.setAttribute("image", "");
menu.removeAttribute("image");
#endif
if (AppConstants.platform === "macosx") {
// OS X native menubar doesn't track list-style-images since
// it doesn't have a frame (bug 733415). Thus enforce updating.
menu.setAttribute("image", "");
menu.removeAttribute("image");
}
}
PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
@ -1774,15 +1775,15 @@ function PlacesMenu(aPopupShowingEvent, aPlace, aOptions) {
this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
this._addEventListeners(window, ["unload"], false);
#ifdef XP_MACOSX
// Must walk up to support views in sub-menus, like Bookmarks Toolbar menu.
for (let elt = this._viewElt.parentNode; elt; elt = elt.parentNode) {
if (elt.localName == "menubar") {
this._nativeView = true;
break;
if (AppConstants.platform === "macosx") {
// Must walk up to support views in sub-menus, like Bookmarks Toolbar menu.
for (let elt = this._viewElt.parentNode; elt; elt = elt.parentNode) {
if (elt.localName == "menubar") {
this._nativeView = true;
break;
}
}
}
#endif
PlacesViewBase.call(this, aPlace, aOptions);
this._onPopupShowing(aPopupShowingEvent);

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<!-- 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/. -->
<bindings id="placesMenuBindings"
xmlns="http://www.mozilla.org/xbl"
@ -26,6 +26,10 @@
<implementation>
<field name="AppConstants" readonly="true">
(Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
</field>
<field name="_indicatorBar">
document.getAnonymousElementByAttribute(this, "class",
"menupopup-drop-indicator-bar");
@ -304,18 +308,18 @@
if (elt.parentNode != this)
return;
#ifdef XP_MACOSX
// XXX: The following check is a temporary hack until bug 420033 is
// resolved.
let parentElt = elt.parent;
while (parentElt) {
if (parentElt.id == "bookmarksMenuPopup" ||
parentElt.id == "goPopup")
return;
if (this.AppConstants.platform === "macosx") {
// XXX: The following check is a temporary hack until bug 420033 is
// resolved.
let parentElt = elt.parent;
while (parentElt) {
if (parentElt.id == "bookmarksMenuPopup" ||
parentElt.id == "goPopup")
return;
parentElt = parentElt.parentNode;
parentElt = parentElt.parentNode;
}
}
#endif
if (window.XULBrowserWindow) {
let elt = event.target;

View File

@ -3,6 +3,7 @@
* 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/. */
Components.utils.import("resource://gre/modules/AppConstants.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MigrationUtils",
@ -113,20 +114,21 @@ var PlacesOrganizer = {
PlacesSearchBox.init();
window.addEventListener("AppCommand", this, true);
#ifdef XP_MACOSX
// 1. Map Edit->Find command to OrganizerCommand_find:all. Need to map
// both the menuitem and the Find key.
var findMenuItem = document.getElementById("menu_find");
findMenuItem.setAttribute("command", "OrganizerCommand_find:all");
var findKey = document.getElementById("key_find");
findKey.setAttribute("command", "OrganizerCommand_find:all");
// 2. Disable some keybindings from browser.xul
var elements = ["cmd_handleBackspace", "cmd_handleShiftBackspace"];
for (var i=0; i < elements.length; i++) {
document.getElementById(elements[i]).setAttribute("disabled", "true");
if (AppConstants.platform === "macosx") {
// 1. Map Edit->Find command to OrganizerCommand_find:all. Need to map
// both the menuitem and the Find key.
let findMenuItem = document.getElementById("menu_find");
findMenuItem.setAttribute("command", "OrganizerCommand_find:all");
let findKey = document.getElementById("key_find");
findKey.setAttribute("command", "OrganizerCommand_find:all");
// 2. Disable some keybindings from browser.xul
let elements = ["cmd_handleBackspace", "cmd_handleShiftBackspace"];
for (let i = 0; i < elements.length; i++) {
document.getElementById(elements[i]).setAttribute("disabled", "true");
}
}
#endif
// remove the "Properties" context-menu item, we've our own details pane
document.getElementById("placesContext")

View File

@ -1,7 +1,8 @@
# -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
/* 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/. */
Components.utils.import("resource://gre/modules/AppConstants.jsm");
var SidebarUtils = {
handleTreeClick: function SU_handleTreeClick(aTree, aEvent, aGutterSelect) {
@ -30,12 +31,9 @@ var SidebarUtils = {
mouseInGutter = aEvent.clientX < rect.x;
}
#ifdef XP_MACOSX
var modifKey = aEvent.metaKey || aEvent.shiftKey;
#else
var modifKey = aEvent.ctrlKey || aEvent.shiftKey;
#endif
var metaKey = AppConstants.platform === "macosx" ? aEvent.metaKey
: aEvent.ctrlKey;
var modifKey = metaKey || aEvent.shiftKey;
var isContainer = tbo.view.isContainer(cell.row);
var openInTabs = isContainer &&
(aEvent.button == 1 ||

View File

@ -8,17 +8,17 @@ browser.jar:
# attributes separately
content/browser/places/bookmarkProperties2.xul (content/bookmarkProperties.xul)
* content/browser/places/places.xul (content/places.xul)
* content/browser/places/places.js (content/places.js)
content/browser/places/places.js (content/places.js)
content/browser/places/places.css (content/places.css)
content/browser/places/organizer.css (content/organizer.css)
content/browser/places/bookmarkProperties.xul (content/bookmarkProperties.xul)
content/browser/places/bookmarkProperties.js (content/bookmarkProperties.js)
content/browser/places/placesOverlay.xul (content/placesOverlay.xul)
* content/browser/places/menu.xml (content/menu.xml)
content/browser/places/menu.xml (content/menu.xml)
content/browser/places/tree.xml (content/tree.xml)
content/browser/places/controller.js (content/controller.js)
content/browser/places/treeView.js (content/treeView.js)
* content/browser/places/browserPlacesViews.js (content/browserPlacesViews.js)
content/browser/places/browserPlacesViews.js (content/browserPlacesViews.js)
# keep the Places version of the history sidebar at history/history-panel.xul
# to prevent having to worry about between versions of the browser
* content/browser/history/history-panel.xul (content/history-panel.xul)
@ -26,7 +26,7 @@ browser.jar:
# ditto for the bookmarks sidebar
content/browser/bookmarks/bookmarksPanel.xul (content/bookmarksPanel.xul)
content/browser/bookmarks/bookmarksPanel.js (content/bookmarksPanel.js)
* content/browser/bookmarks/sidebarUtils.js (content/sidebarUtils.js)
content/browser/bookmarks/sidebarUtils.js (content/sidebarUtils.js)
content/browser/places/moveBookmarks.xul (content/moveBookmarks.xul)
content/browser/places/moveBookmarks.js (content/moveBookmarks.js)
content/browser/places/editBookmarkOverlay.xul (content/editBookmarkOverlay.xul)

View File

@ -10,7 +10,7 @@ BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
JAR_MANIFESTS += ['jar.mn']
EXTRA_PP_JS_MODULES += [
EXTRA_JS_MODULES += [
'PlacesUIUtils.jsm',
]

View File

@ -0,0 +1,7 @@
/* 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";
exports.UPDATE_EVENT_BREAKPOINTS = 'UPDATE_EVENT_BREAKPOINTS';
exports.FETCH_EVENT_LISTENERS = 'FETCH_EVENT_LISTENERS';

View File

@ -0,0 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const constants = require('./constants');
// No global actions right now, but I'm sure there will be soon.
module.exports = {};

View File

@ -0,0 +1,147 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const constants = require('../constants');
const promise = require('promise');
const { rdpInvoke, asPaused } = require('../utils');
const { reportException } = require("devtools/toolkit/DevToolsUtils");
const FETCH_EVENT_LISTENERS_DELAY = 200; // ms
const initialState = {
activeEventNames: [],
listeners: [],
fetchingListeners: false,
};
function update(state = initialState, action, emitChange) {
switch(action.type) {
case constants.UPDATE_EVENT_BREAKPOINTS:
state.activeEventNames = action.eventNames;
emitChange('activeEventNames', state.activeEventNames);
break;
case constants.FETCH_EVENT_LISTENERS:
if (action.status === "begin") {
state.fetchingListeners = true;
}
else if (action.status === "done") {
state.fetchingListeners = false;
state.listeners = action.listeners;
emitChange('listeners', state.listeners);
}
break;
}
return state;
};
function fetchEventListeners() {
return (dispatch, getState) => {
// Make sure we're not sending a batch of closely repeated requests.
// This can easily happen whenever new sources are fetched.
setNamedTimeout("event-listeners-fetch", FETCH_EVENT_LISTENERS_DELAY, () => {
// In case there is still a request of listeners going on (it
// takes several RDP round trips right now), make sure we wait
// on a currently running request
if (getState().eventListeners.fetchingListeners) {
dispatch({
type: services.WAIT_UNTIL,
predicate: action => (
action.type === constants.FETCH_EVENT_LISTENERS &&
action.status === "done"
),
run: dispatch => dispatch(fetchEventListeners())
});
return;
}
dispatch({
type: constants.FETCH_EVENT_LISTENERS,
status: "begin"
});
asPaused(gThreadClient, _getListeners).then(listeners => {
// Notify that event listeners were fetched and shown in the view,
// and callback to resume the active thread if necessary.
window.emit(EVENTS.EVENT_LISTENERS_FETCHED);
dispatch({
type: constants.FETCH_EVENT_LISTENERS,
status: "done",
listeners: listeners
});
});
});
};
}
const _getListeners = Task.async(function*() {
const response = yield rdpInvoke(gThreadClient, gThreadClient.eventListeners);
// Make sure all the listeners are sorted by the event type, since
// they're not guaranteed to be clustered together.
response.listeners.sort((a, b) => a.type > b.type ? 1 : -1);
// Add all the listeners in the debugger view event linsteners container.
let fetchedDefinitions = new Map();
let listeners = [];
for (let listener of response.listeners) {
let definitionSite;
if (fetchedDefinitions.has(listener.function.actor)) {
definitionSite = fetchedDefinitions.get(listener.function.actor);
} else if (listener.function.class == "Function") {
definitionSite = yield _getDefinitionSite(listener.function);
if (!definitionSite) {
// We don't know where this listener comes from so don't show it in
// the UI as breaking on it doesn't work (bug 942899).
continue;
}
fetchedDefinitions.set(listener.function.actor, definitionSite);
}
listener.function.url = definitionSite;
listeners.push(listener);
}
fetchedDefinitions.clear();
return listeners;
});
const _getDefinitionSite = Task.async(function*(aFunction) {
const grip = gThreadClient.pauseGrip(aFunction);
let response;
try {
response = yield rdpInvoke(grip, grip.getDefinitionSite);
}
catch(e) {
// Don't make this error fatal, because it would break the entire events pane.
reportException("_getDefinitionSite", e);
return null;
}
return response.source.url;
});
function updateEventBreakpoints(eventNames) {
return dispatch => {
setNamedTimeout("event-breakpoints-update", 0, () => {
gThreadClient.pauseOnDOMEvents(eventNames, function() {
// Notify that event breakpoints were added/removed on the server.
window.emit(EVENTS.EVENT_BREAKPOINTS_UPDATED);
dispatch({
type: constants.UPDATE_EVENT_BREAKPOINTS,
eventNames: eventNames
});
});
});
}
}
module.exports = {
update: update,
actions: { updateEventBreakpoints, fetchEventListeners }
}

View File

@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const eventListeners = require('./event-listeners');
module.exports = { eventListeners };

View File

@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { promiseInvoke } = require("devtools/async-utils");
const { reportException } = require("devtools/toolkit/DevToolsUtils");
function rdpInvoke(client, method, ...args) {
return promiseInvoke(client, method, ...args)
.then((packet) => {
let { error, message } = packet;
if (error) {
throw new Error(error + ": " + message);
}
return packet;
});
}
function asPaused(client, func) {
if (client.state != "paused") {
return Task.spawn(function*() {
yield rdpInvoke(client, client.interrupt);
let result;
try {
result = yield func();
}
catch(e) {
// Try to put the debugger back in a working state by resuming
// it
yield rdpInvoke(client, client.resume);
throw e;
}
yield rdpInvoke(client, client.resume);
return result;
});
} else {
return func();
}
}
module.exports = { rdpInvoke, asPaused };

View File

@ -3,14 +3,24 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const actions = require('../stores/event-listeners').actions;
const bindActionCreators = require('devtools/shared/fluxify/bindActionCreators');
/**
* Functions handling the event listeners UI.
*/
function EventListenersView(DebuggerController) {
function EventListenersView(dispatcher, DebuggerController) {
dumpn("EventListenersView was instantiated");
this.actions = bindActionCreators(actions, dispatcher.dispatch);
this.getState = () => dispatcher.getState().eventListeners;
this.Breakpoints = DebuggerController.Breakpoints;
dispatcher.onChange({
"eventListeners": { "listeners": this.renderListeners }
}, this);
this._onCheck = this._onCheck.bind(this);
this._onClick = this._onClick.bind(this);
}
@ -47,6 +57,15 @@ EventListenersView.prototype = Heritage.extend(WidgetMethods, {
this.widget.removeEventListener("click", this._onClick, false);
},
renderListeners: function(listeners) {
listeners.forEach(listener => {
this.addListener(listener, { staged: true });
});
// Flushes all the prepared events into the event listeners container.
this.commit();
},
/**
* Adds an event to this event listeners container.
*
@ -142,12 +161,12 @@ EventListenersView.prototype = Heritage.extend(WidgetMethods, {
}
// Create the element node for the event listener item.
let itemView = this._createItemView(type, selector, url);
const itemView = this._createItemView(type, selector, url);
// Event breakpoints survive target navigations. Make sure the newly
// inserted event item is correctly checked.
let checkboxState =
this.Breakpoints.DOM.activeEventNames.indexOf(type) != -1;
const activeEventNames = this.getState().activeEventNames;
const checkboxState = activeEventNames.indexOf(type) != -1;
// Append an event listener item to this container.
this.push([itemView.container], {
@ -241,7 +260,8 @@ EventListenersView.prototype = Heritage.extend(WidgetMethods, {
_onCheck: function({ detail: { description, checked }, target }) {
if (description == "item") {
this.getItemForElement(target).attachment.checkboxState = checked;
this.Breakpoints.DOM.scheduleEventBreakpointsUpdate();
this.actions.updateEventBreakpoints(this.getCheckedEvents());
return;
}
@ -271,4 +291,4 @@ EventListenersView.prototype = Heritage.extend(WidgetMethods, {
_inNativeCodeString: ""
});
DebuggerView.EventListeners = new EventListenersView(DebuggerController);
module.exports = EventListenersView;

View File

@ -11,7 +11,6 @@ const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
const NEW_SOURCE_IGNORED_URLS = ["debugger eval code", "XStringBundle"];
const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms
const FETCH_EVENT_LISTENERS_DELAY = 200; // ms
const FRAME_STEP_CLEAR_DELAY = 100; // ms
const CALL_STACK_PAGE_SIZE = 25; // frames
@ -103,9 +102,11 @@ Cu.import("resource:///modules/devtools/VariablesView.jsm");
Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
Cu.import("resource:///modules/devtools/shared/browser-loader.js");
const require = BrowserLoader("resource:///modules/devtools/debugger/", this).require;
const {TargetFactory} = require("devtools/framework/target");
const {Toolbox} = require("devtools/framework/toolbox")
const {Toolbox} = require("devtools/framework/toolbox");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
const promise = require("devtools/toolkit/deprecated-sync-thenables");
const Editor = require("devtools/sourceeditor/editor");
@ -320,7 +321,9 @@ let DebuggerController = {
this.SourceScripts.connect();
if (aThreadClient.paused) {
aThreadClient.resume(this._ensureResumptionOrder);
aThreadClient.resume(res => {
this._ensureResumptionOrder(res)
});
}
deferred.resolve();
@ -1274,7 +1277,7 @@ SourceScripts.prototype = {
// Make sure the events listeners are up to date.
if (DebuggerView.instrumentsPaneTab == "events-tab") {
DebuggerController.Breakpoints.DOM.scheduleEventListenersFetch();
dispatcher.dispatch(actions.fetchEventListeners());
}
// Signal that a new source has been added.
@ -1534,151 +1537,6 @@ SourceScripts.prototype = {
}
};
/**
* Handles breaking on event listeners in the currently debugged target.
*/
function EventListeners() {
}
EventListeners.prototype = {
/**
* A list of event names on which the debuggee will automatically pause
* when invoked.
*/
activeEventNames: [],
/**
* Updates the list of events types with listeners that, when invoked,
* will automatically pause the debuggee. The respective events are
* retrieved from the UI.
*/
scheduleEventBreakpointsUpdate: function() {
// Make sure we're not sending a batch of closely repeated requests.
// This can easily happen when toggling all events of a certain type.
setNamedTimeout("event-breakpoints-update", 0, () => {
this.activeEventNames = DebuggerView.EventListeners.getCheckedEvents();
gThreadClient.pauseOnDOMEvents(this.activeEventNames);
// Notify that event breakpoints were added/removed on the server.
window.emit(EVENTS.EVENT_BREAKPOINTS_UPDATED);
});
},
/**
* Schedules fetching the currently attached event listeners from the debugee.
*/
scheduleEventListenersFetch: function() {
// Make sure we're not sending a batch of closely repeated requests.
// This can easily happen whenever new sources are fetched.
setNamedTimeout("event-listeners-fetch", FETCH_EVENT_LISTENERS_DELAY, () => {
if (gThreadClient.state != "paused") {
gThreadClient.interrupt(() => this._getListeners(() => gThreadClient.resume()));
} else {
this._getListeners();
}
});
},
/**
* A semaphore that is used to ensure only a single protocol request for event
* listeners will be ongoing at any given time.
*/
_parsingListeners: false,
/**
* A flag the indicates whether a new request to fetch updated event listeners
* has arrived, while another one was in progress.
*/
_eventListenersUpdateNeeded: false,
/**
* Fetches the currently attached event listeners from the debugee.
* The thread client state is assumed to be "paused".
*
* @param function aCallback
* Invoked once the event listeners are fetched and displayed.
*/
_getListeners: function(aCallback) {
// Don't make a new request if one is still ongoing, but schedule one for
// later.
if (this._parsingListeners) {
this._eventListenersUpdateNeeded = true;
return;
}
this._parsingListeners = true;
gThreadClient.eventListeners(Task.async(function*(aResponse) {
if (aResponse.error) {
throw "Error getting event listeners: " + aResponse.message;
}
// Make sure all the listeners are sorted by the event type, since
// they're not guaranteed to be clustered together.
aResponse.listeners.sort((a, b) => a.type > b.type ? 1 : -1);
// Add all the listeners in the debugger view event linsteners container.
let fetchedDefinitions = new Map();
for (let listener of aResponse.listeners) {
let definitionSite;
if (fetchedDefinitions.has(listener.function.actor)) {
definitionSite = fetchedDefinitions.get(listener.function.actor);
} else if (listener.function.class == "Function") {
definitionSite = yield this._getDefinitionSite(listener.function);
if (!definitionSite) {
// We don't know where this listener comes from so don't show it in
// the UI as breaking on it doesn't work (bug 942899).
continue;
}
fetchedDefinitions.set(listener.function.actor, definitionSite);
}
listener.function.url = definitionSite;
DebuggerView.EventListeners.addListener(listener, { staged: true });
}
fetchedDefinitions.clear();
// Flushes all the prepared events into the event listeners container.
DebuggerView.EventListeners.commit();
// Now that we are done, schedule a new update if necessary.
this._parsingListeners = false;
if (this._eventListenersUpdateNeeded) {
this._eventListenersUpdateNeeded = false;
this.scheduleEventListenersFetch();
}
// Notify that event listeners were fetched and shown in the view,
// and callback to resume the active thread if necessary.
window.emit(EVENTS.EVENT_LISTENERS_FETCHED);
aCallback && aCallback();
}.bind(this)));
},
/**
* Gets a function's source-mapped definiton site.
*
* @param object aFunction
* The grip of the function to get the definition site for.
* @return object
* A promise that is resolved with the function's owner source url.
*/
_getDefinitionSite: function(aFunction) {
let deferred = promise.defer();
gThreadClient.pauseGrip(aFunction).getDefinitionSite(aResponse => {
if (aResponse.error) {
// Don't make this error fatal, because it would break the entire events pane.
const msg = "Error getting function definition site: " + aResponse.message;
DevToolsUtils.reportException("_getDefinitionSite", msg);
deferred.resolve(null);
} else {
deferred.resolve(aResponse.source.url);
}
});
return deferred.promise;
}
};
/**
* Handles all the breakpoints in the current debugger.
*/
@ -2205,7 +2063,6 @@ DebuggerController.ThreadState = new ThreadState();
DebuggerController.StackFrames = new StackFrames();
DebuggerController.SourceScripts = new SourceScripts();
DebuggerController.Breakpoints = new Breakpoints();
DebuggerController.Breakpoints.DOM = new EventListeners();
/**
* Export some properties to the global scope for easier access.

View File

@ -33,6 +33,17 @@ const TOOLBAR_ORDER_POPUP_POSITION = "topcenter bottomleft";
const PROMISE_DEBUGGER_URL =
"chrome://browser/content/devtools/promisedebugger/promise-debugger.xhtml";
const createDispatcher = require('devtools/shared/create-dispatcher')();
const stores = require('./content/stores/index');
const dispatcher = createDispatcher(stores);
const waitUntilService = require('devtools/shared/fluxify/waitUntilService');
const services = {
WAIT_UNTIL: waitUntilService.name
};
const EventListenersView = require('./content/views/event-listeners-view');
const actions = require('./content/stores/event-listeners').actions;
/**
* Object defining the debugger view components.
*/
@ -598,7 +609,7 @@ let DebuggerView = {
*/
_onInstrumentsPaneTabSelect: function() {
if (this._instrumentsPane.selectedTab.id == "events-tab") {
DebuggerController.Breakpoints.DOM.scheduleEventListenersFetch();
dispatcher.dispatch(actions.fetchEventListeners());
}
},
@ -859,3 +870,5 @@ ResultsPanelContainer.prototype = Heritage.extend(WidgetMethods, {
left: 0,
top: 0
});
DebuggerView.EventListeners = new EventListenersView(dispatcher, DebuggerController);

View File

@ -8,4 +8,19 @@ EXTRA_JS_MODULES.devtools.debugger += [
'panel.js'
]
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
EXTRA_JS_MODULES.devtools.debugger.content += [
'content/constants.js',
'content/globalActions.js',
'content/utils.js'
]
EXTRA_JS_MODULES.devtools.debugger.content.views += [
'content/views/event-listeners-view.js'
]
EXTRA_JS_MODULES.devtools.debugger.content.stores += [
'content/stores/event-listeners.js',
'content/stores/index.js'
]
BROWSER_CHROME_MANIFESTS += ['test/mochitest/browser.ini']

View File

@ -13,6 +13,8 @@ function test() {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gEvents = gView.EventListeners;
let gDispatcher = gDebugger.dispatcher;
let constants = gDebugger.require('./content/constants');
Task.spawn(function*() {
yield waitForSourceShown(aPanel, ".html");
@ -24,7 +26,7 @@ function test() {
function testFetchOnFocus() {
return Task.spawn(function*() {
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
is(gView.instrumentsPaneHidden, false,
@ -43,7 +45,7 @@ function test() {
function testFetchOnReloadWhenFocused() {
return Task.spawn(function*() {
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
let reloading = once(gDebugger.gTarget, "will-navigate");
let reloaded = waitForSourcesAfterReload();
@ -74,11 +76,20 @@ function test() {
function testFetchOnReloadWhenNotFocused() {
return Task.spawn(function*() {
gDebugger.on(gDebugger.EVENTS.EVENT_LISTENERS_FETCHED, () => {
ok(false, "Shouldn't have fetched any event listeners.");
});
gDebugger.on(gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED, () => {
ok(false, "Shouldn't have updated any event breakpoints.");
gDispatcher.dispatch({
type: gDebugger.services.WAIT_UNTIL,
predicate: action => {
return (action.type === constants.FETCH_EVENT_LISTENERS ||
action.type === constants.UPDATE_EVENT_BREAKPOINTS);
},
run: (dispatch, getState, action) => {
if(action.type === constants.FETCH_EVENT_LISTENERS) {
ok(false, "Shouldn't have fetched any event listeners.");
}
else if(action.type === constants.UPDATE_EVENT_BREAKPOINTS) {
ok(false, "Shouldn't have updated any event breakpoints.");
}
}
});
gView.toggleInstrumentsPane({ visible: true, animated: false }, 0);

View File

@ -12,11 +12,13 @@ function test() {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gEvents = gView.EventListeners;
let gDispatcher = gDebugger.dispatcher;
let constants = gDebugger.require('./content/constants');
Task.spawn(function*() {
yield waitForSourceShown(aPanel, ".html");
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
yield fetched;
@ -44,7 +46,7 @@ function test() {
is(gEvents.getCheckedEvents().toString(), "",
"The getCheckedEvents() method returns the correct stuff.");
yield ensureThreadClientState(aPanel, "resumed");
yield ensureThreadClientState(aPanel, "attached");
yield closeDebuggerAndFinish(aPanel);
});

View File

@ -14,12 +14,14 @@ function test() {
let gView = gDebugger.DebuggerView;
let gController = gDebugger.DebuggerController
let gEvents = gView.EventListeners;
let gBreakpoints = gController.Breakpoints;
let gDispatcher = gDebugger.dispatcher;
let getState = gDispatcher.getState;
let constants = gDebugger.require('./content/constants');
Task.spawn(function*() {
yield waitForSourceShown(aPanel, ".html");
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
yield fetched;
@ -32,7 +34,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
let updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
yield updated;
@ -45,7 +47,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "change");
updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
yield updated;
@ -58,7 +60,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
yield ensureThreadClientState(aPanel, "resumed");
yield ensureThreadClientState(aPanel, "attached");
yield closeDebuggerAndFinish(aPanel);
});
@ -90,7 +92,7 @@ function test() {
"The getAllEvents() method returns the correct stuff.");
is(gEvents.getCheckedEvents().toString(), checked,
"The getCheckedEvents() method returns the correct stuff.");
is(gBreakpoints.DOM.activeEventNames.toString(), checked,
is(getState().eventListeners.activeEventNames.toString(), checked,
"The correct event names are listed as being active breakpoints.");
}
});

View File

@ -15,12 +15,14 @@ function test() {
let gView = gDebugger.DebuggerView;
let gController = gDebugger.DebuggerController
let gEvents = gView.EventListeners;
let gBreakpoints = gController.Breakpoints;
let gDispatcher = gDebugger.dispatcher;
let getState = gDispatcher.getState;
let constants = gDebugger.require('./content/constants');
Task.spawn(function*() {
yield waitForSourceShown(aPanel, ".html");
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
yield fetched;
@ -33,7 +35,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
let updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("interactionEvents"), gDebugger);
yield updated;
@ -46,7 +48,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "change");
updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("interactionEvents"), gDebugger);
yield updated;
@ -59,7 +61,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("keyboardEvents"), gDebugger);
yield updated;
@ -72,7 +74,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "keydown,keyup");
updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getGroupCheckboxNode("keyboardEvents"), gDebugger);
yield updated;
@ -85,7 +87,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
yield ensureThreadClientState(aPanel, "resumed");
yield ensureThreadClientState(aPanel, "attached");
yield closeDebuggerAndFinish(aPanel);
});
@ -117,7 +119,7 @@ function test() {
"The getAllEvents() method returns the correct stuff.");
is(gEvents.getCheckedEvents().toString(), checked,
"The getCheckedEvents() method returns the correct stuff.");
is(gBreakpoints.DOM.activeEventNames.toString(), checked,
is(getState().eventListeners.activeEventNames.toString(), checked,
"The correct event names are listed as being active breakpoints.");
}
});

View File

@ -15,11 +15,14 @@ function test() {
let gController = gDebugger.DebuggerController
let gEvents = gView.EventListeners;
let gBreakpoints = gController.Breakpoints;
let gDispatcher = gDebugger.dispatcher;
let getState = gDispatcher.getState;
let constants = gDebugger.require('./content/constants');
Task.spawn(function*() {
yield waitForSourceShown(aPanel, ".html");
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
yield fetched;
@ -32,7 +35,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
let updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(1), gDebugger);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(2), gDebugger);
@ -47,7 +50,8 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "change,click,keydown");
yield reloadActiveTab(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
reload(aPanel);
yield afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
testEventItem(0, true);
testEventItem(1, true);
@ -58,7 +62,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "change,click,keydown");
updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(0), gDebugger);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(1), gDebugger);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(2), gDebugger);
@ -73,7 +77,8 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
yield reloadActiveTab(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
reload(aPanel);
yield afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
testEventItem(0, false);
testEventItem(1, false);
@ -84,7 +89,7 @@ function test() {
testEventGroup("mouseEvents", false);
testEventArrays("change,click,keydown,keyup", "");
yield ensureThreadClientState(aPanel, "resumed");
yield ensureThreadClientState(aPanel, "attached");
yield closeDebuggerAndFinish(aPanel);
});
@ -115,9 +120,9 @@ function test() {
is(gEvents.getAllEvents().toString(), all,
"The getAllEvents() method returns the correct stuff.");
is(gEvents.getCheckedEvents().toString(), checked,
"The getCheckedEvents() method returns the correct stuff.");
is(gBreakpoints.DOM.activeEventNames.toString(), checked,
"The correct event names are listed as being active breakpoints.");
"The getCheckedEvents() method returns the correct stuff.");
is(getState().eventListeners.activeEventNames.toString(), checked,
"The correct event names are listed as being active breakpoints.");
}
});
}

View File

@ -13,25 +13,28 @@ function test() {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gEvents = gView.EventListeners;
let gDispatcher = gDebugger.dispatcher;
let getState = gDispatcher.getState;
let constants = gDebugger.require('./content/constants');
Task.spawn(function*() {
yield waitForSourceShown(aPanel, ".html");
yield callInTab(gTab, "addBodyClickEventListener");
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
let fetched = afterDispatch(gDispatcher, constants.FETCH_EVENT_LISTENERS);
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
yield fetched;
yield ensureThreadClientState(aPanel, "resumed");
yield ensureThreadClientState(aPanel, "attached");
is(gView.instrumentsPaneHidden, false,
"The instruments pane should be visible.");
is(gView.instrumentsPaneTab, "events-tab",
"The events tab should be selected.");
let updated = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_BREAKPOINTS_UPDATED);
let updated = afterDispatch(gDispatcher, constants.UPDATE_EVENT_BREAKPOINTS);
EventUtils.sendMouseEvent({ type: "click" }, getItemCheckboxNode(1), gDebugger);
yield updated;
yield ensureThreadClientState(aPanel, "resumed");
yield ensureThreadClientState(aPanel, "attached");
let paused = waitForCaretAndScopes(aPanel, 48);
generateMouseClickInTab(gTab, "content.document.body");

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