Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-03-06 16:25:02 -05:00
Родитель 010f9c04eb 8aaa845a81
Коммит d00595bc6b
531 изменённых файлов: 27121 добавлений и 11752 удалений

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

@ -1024,7 +1024,7 @@ pref("apz.fling_curve_function_y1", "0.0");
pref("apz.fling_curve_function_x2", "0.80");
pref("apz.fling_curve_function_y2", "1.0");
pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
pref("apz.fling_friction", "0.00238");
pref("apz.fling_friction", "0.0019");
pref("apz.max_velocity_inches_per_ms", "0.07");
// Tweak default displayport values to reduce the risk of running out of
@ -1039,9 +1039,9 @@ pref("apz.axis_lock.mode", 2);
// Overscroll-related settings
pref("apz.overscroll.enabled", true);
pref("apz.overscroll.stretch_factor", "0.15");
pref("apz.overscroll.spring_stiffness", "0.002");
pref("apz.overscroll.spring_friction", "0.02");
pref("apz.overscroll.stretch_factor", "0.35");
pref("apz.overscroll.spring_stiffness", "0.0018");
pref("apz.overscroll.spring_friction", "0.015");
pref("apz.overscroll.stop_distance_threshold", "5.0");
pref("apz.overscroll.stop_velocity_threshold", "0.01");

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

@ -178,6 +178,11 @@ let RemoteDebugger = {
let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
// Allow debugging of chrome for any process
if (!restrictPrivileges) {
DebuggerServer.allowChromeProcess = true;
}
/**
* Construct a root actor appropriate for use in a server running in B2G.
* The returned root actor respects the factories registered with

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

@ -89,7 +89,7 @@ let ReadingListUI = {
}
},
onReadingListPopupShowing(target) {
onReadingListPopupShowing: Task.async(function* (target) {
if (target.id == "BMB_readingListPopup") {
// Setting this class in the .xul file messes with the way
// browser-places.js inserts bookmarks in the menu.
@ -105,55 +105,56 @@ let ReadingListUI = {
if (insertPoint.classList.contains("subviewbutton"))
classList += " subviewbutton";
ReadingList.getItems().then(items => {
for (let item of items) {
let menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", item.title || item.url.spec);
menuitem.setAttribute("class", classList);
let hasItems = false;
yield ReadingList.forEachItem(item => {
hasItems = true;
let node = menuitem._placesNode = {
// Passing the PlacesUtils.nodeIsURI check is required for the
// onCommand handler to load our URI.
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_URI,
let menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", item.title || item.url);
menuitem.setAttribute("class", classList);
// makes PlacesUIUtils.canUserRemove return false.
// The context menu is broken without this.
parent: {type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER},
let node = menuitem._placesNode = {
// Passing the PlacesUtils.nodeIsURI check is required for the
// onCommand handler to load our URI.
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_URI,
// A -1 id makes this item a non-bookmark, which avoids calling
// PlacesUtils.annotations.itemHasAnnotation to check if the
// bookmark should be opened in the sidebar (this call fails for
// readinglist item, and breaks loading our URI).
itemId: -1,
// makes PlacesUIUtils.canUserRemove return false.
// The context menu is broken without this.
parent: {type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER},
// Used by the tooltip and onCommand handlers.
uri: item.url.spec,
// A -1 id makes this item a non-bookmark, which avoids calling
// PlacesUtils.annotations.itemHasAnnotation to check if the
// bookmark should be opened in the sidebar (this call fails for
// readinglist item, and breaks loading our URI).
itemId: -1,
// Used by the tooltip.
title: item.title
};
// Used by the tooltip and onCommand handlers.
uri: item.url,
Favicons.getFaviconURLForPage(item.url, uri => {
if (uri) {
menuitem.setAttribute("image",
Favicons.getFaviconLinkForIcon(uri).spec);
}
});
// Used by the tooltip.
title: item.title
};
target.insertBefore(menuitem, insertPoint);
}
Favicons.getFaviconURLForPage(item.uri, uri => {
if (uri) {
menuitem.setAttribute("image",
Favicons.getFaviconLinkForIcon(uri).spec);
}
});
if (!items.length) {
let menuitem = document.createElement("menuitem");
let bundle =
Services.strings.createBundle("chrome://browser/locale/places/places.properties");
menuitem.setAttribute("label", bundle.GetStringFromName("bookmarksMenuEmptyFolder"));
menuitem.setAttribute("class", "bookmark-item");
menuitem.setAttribute("disabled", true);
target.insertBefore(menuitem, insertPoint);
}
target.insertBefore(menuitem, insertPoint);
});
},
if (!hasItems) {
let menuitem = document.createElement("menuitem");
let bundle =
Services.strings.createBundle("chrome://browser/locale/places/places.properties");
menuitem.setAttribute("label", bundle.GetStringFromName("bookmarksMenuEmptyFolder"));
menuitem.setAttribute("class", "bookmark-item");
menuitem.setAttribute("disabled", true);
target.insertBefore(menuitem, insertPoint);
}
}),
/**
* Hide the ReadingList sidebar, if it is currently shown.

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

@ -637,8 +637,8 @@ SocialShare = {
// containing the open graph data.
let _dataFn;
if (!pageData || sharedURI == gBrowser.currentURI) {
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
messageManager.addMessageListener("PageMetadata:PageDataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("PageMetadata:PageDataResult", _dataFn);
let pageData = msg.json;
if (graphData) {
// overwrite data retreived from page with data given to us as a param
@ -648,17 +648,17 @@ SocialShare = {
}
this.sharePage(providerOrigin, pageData, target);
});
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetPageData");
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("PageMetadata:GetPageData");
return;
}
// if this is a share of a selected item, get any microdata
if (!pageData.microdata && target) {
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
messageManager.addMessageListener("PageMetadata:MicrodataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("PageMetadata:MicrodataResult", _dataFn);
pageData.microdata = msg.data;
this.sharePage(providerOrigin, pageData, target);
});
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetMicrodata", null, target);
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("PageMetadata:GetMicrodata", null, target);
return;
}
this.currentShare = pageData;

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

@ -32,6 +32,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AboutReader",
"resource://gre/modules/AboutReader.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
"resource://gre/modules/PageMetadata.jsm");
XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() {
let ssdp = Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm", {}).SimpleServiceDiscovery;
// Register targets
@ -153,6 +155,12 @@ let handleContentContextMenu = function (event) {
subject.wrappedJSObject = subject;
Services.obs.notifyObservers(subject, "content-contextmenu", null);
let doc = event.target.ownerDocument;
let docLocation = doc.location.href;
let charSet = doc.characterSet;
let baseURI = doc.baseURI;
let referrer = doc.referrer;
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
let editFlags = SpellCheckHelper.isEditable(event.target, content);
let spellInfo;
@ -163,9 +171,10 @@ let handleContentContextMenu = function (event) {
}
let customMenuItems = PageMenuChild.build(event.target);
let principal = event.target.ownerDocument.nodePrincipal;
let principal = doc.nodePrincipal;
sendSyncMessage("contextmenu",
{ editFlags, spellInfo, customMenuItems, addonInfo, principal },
{ editFlags, spellInfo, customMenuItems, addonInfo,
principal, docLocation, charSet, baseURI, referrer },
{ event, popupNode: event.target });
}
else {
@ -178,6 +187,10 @@ let handleContentContextMenu = function (event) {
popupNode: event.target,
browser: browser,
addonInfo: addonInfo,
documentURIObject: doc.documentURIObject,
docLocation: docLocation,
charSet: charSet,
referrer: referrer,
};
}
}
@ -1000,30 +1013,29 @@ addEventListener("pageshow", function(event) {
}
});
let SocialMessenger = {
let PageMetadataMessenger = {
init: function() {
addMessageListener("Social:GetPageData", this);
addMessageListener("Social:GetMicrodata", this);
XPCOMUtils.defineLazyGetter(this, "og", function() {
let tmp = {};
Cu.import("resource:///modules/Social.jsm", tmp);
return tmp.OpenGraphBuilder;
});
addMessageListener("PageMetadata:GetPageData", this);
addMessageListener("PageMetadata:GetMicrodata", this);
},
receiveMessage: function(aMessage) {
switch(aMessage.name) {
case "Social:GetPageData":
sendAsyncMessage("Social:PageDataResult", this.og.getData(content.document));
case "PageMetadata:GetPageData": {
let result = PageMetadata.getData(content.document);
sendAsyncMessage("PageMetadata:PageDataResult", result);
break;
case "Social:GetMicrodata":
}
case "PageMetadata:GetMicrodata": {
let target = aMessage.objects;
sendAsyncMessage("Social:PageDataResult", this.og.getMicrodata(content.document, target));
let result = PageMetadata.getMicrodata(content.document, target);
sendAsyncMessage("PageMetadata:MicrodataResult", result);
break;
}
}
}
}
SocialMessenger.init();
PageMetadataMessenger.init();
addEventListener("ActivateSocialFeature", function (aEvent) {
let document = content.document;

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

@ -576,6 +576,7 @@ nsContextMenu.prototype = {
this.linkURL = "";
this.linkURI = null;
this.linkProtocol = "";
this.linkHasNoReferrer = false;
this.onMathML = false;
this.inFrame = false;
this.inSrcdocFrame = false;
@ -738,6 +739,7 @@ nsContextMenu.prototype = {
this.linkProtocol = this.getLinkProtocol();
this.onMailtoLink = (this.linkProtocol == "mailto");
this.onSaveableLink = this.isLinkSaveable( this.link );
this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);
}
// Background image? Don't bother if we've already found a
@ -864,10 +866,10 @@ nsContextMenu.prototype = {
return aNode.spellcheck;
},
_openLinkInParameters : function (doc, extra) {
let params = { charset: doc.characterSet,
referrerURI: doc.documentURIObject,
noReferrer: BrowserUtils.linkHasNoReferrer(this.link) };
_openLinkInParameters : function (extra) {
let params = { charset: gContextMenuContentData.charSet,
referrerURI: gContextMenuContentData.documentURIObject,
noReferrer: this.linkHasNoReferrer };
for (let p in extra)
params[p] = extra[p];
return params;
@ -875,41 +877,38 @@ nsContextMenu.prototype = {
// Open linked-to URL in a new window.
openLink : function () {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this.principal);
openLinkIn(this.linkURL, "window", this._openLinkInParameters(doc));
openLinkIn(this.linkURL, "window", this._openLinkInParameters());
},
// Open linked-to URL in a new private window.
openLinkInPrivateWindow : function () {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this.principal);
openLinkIn(this.linkURL, "window",
this._openLinkInParameters(doc, { private: true }));
this._openLinkInParameters({ private: true }));
},
// Open linked-to URL in a new tab.
openLinkInTab: function() {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this.principal);
var referrerURI = doc.documentURIObject;
let referrerURI = gContextMenuContentData.documentURIObject;
// if the mixedContentChannel is present and the referring URI passes
// a same origin check with the target URI, we can preserve the users
// decision of disabling MCB on a page for it's child tabs.
var persistAllowMixedContentInChildTab = false;
let persistAllowMixedContentInChildTab = false;
if (this.browser.docShell && this.browser.docShell.mixedContentChannel) {
const sm = Services.scriptSecurityManager;
try {
var targetURI = this.linkURI;
let targetURI = this.linkURI;
sm.checkSameOriginURI(referrerURI, targetURI, false);
persistAllowMixedContentInChildTab = true;
}
catch (e) { }
}
let params = this._openLinkInParameters(doc, {
let params = this._openLinkInParameters({
allowMixedContent: persistAllowMixedContentInChildTab,
});
openLinkIn(this.linkURL, "tab", params);
@ -917,18 +916,15 @@ nsContextMenu.prototype = {
// open URL in current tab
openLinkInCurrent: function() {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, this.principal);
openLinkIn(this.linkURL, "current", this._openLinkInParameters(doc));
openLinkIn(this.linkURL, "current", this._openLinkInParameters());
},
// Open frame in a new tab.
openFrameInTab: function() {
var doc = this.target.ownerDocument;
var frameURL = doc.location.href;
var referrer = doc.referrer;
openLinkIn(frameURL, "tab",
{ charset: doc.characterSet,
let referrer = gContextMenuContentData.referrer;
openLinkIn(gContextMenuContentData.docLocation, "tab",
{ charset: gContextMenuContentData.charSet,
referrerURI: referrer ? makeURI(referrer) : null });
},
@ -939,25 +935,21 @@ nsContextMenu.prototype = {
// Open clicked-in frame in its own window.
openFrame: function() {
var doc = this.target.ownerDocument;
var frameURL = doc.location.href;
var referrer = doc.referrer;
openLinkIn(frameURL, "window",
{ charset: doc.characterSet,
let referrer = gContextMenuContentData.referrer;
openLinkIn(gContextMenuContentData.docLocation, "window",
{ charset: gContextMenuContentData.charSet,
referrerURI: referrer ? makeURI(referrer) : null });
},
// Open clicked-in frame in the same window.
showOnlyThisFrame: function() {
var doc = this.target.ownerDocument;
var frameURL = doc.location.href;
urlSecurityCheck(frameURL,
urlSecurityCheck(gContextMenuContentData.docLocation,
this.browser.contentPrincipal,
Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
var referrer = doc.referrer;
openUILinkIn(frameURL, "current", { disallowInheritPrincipal: true,
referrerURI: referrer ? makeURI(referrer) : null });
let referrer = gContextMenuContentData.referrer;
openUILinkIn(gContextMenuContentData.docLocation, "current",
{ disallowInheritPrincipal: true,
referrerURI: referrer ? makeURI(referrer) : null });
},
reload: function(event) {

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

@ -150,21 +150,21 @@
let URLTemplate = provider.markURL;
let _dataFn;
if (!pageData) {
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
messageManager.addMessageListener("PageMetadata:PageDataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("PageMetadata:PageDataResult", _dataFn);
this.loadPanel(msg.json, target);
});
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetPageData");
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("PageMetadata:GetPageData");
return;
}
// if this is a share of a selected item, get any microdata
if (!pageData.microdata && target) {
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
messageManager.addMessageListener("PageMetadata:MicrodataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("PageMetadata:MicrodataResult", _dataFn);
pageData.microdata = msg.data;
this.loadPanel(pageData, target);
});
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetMicrodata", null, target);
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("PageMetadata:GetMicrodata", null, target);
return;
}
this.pageData = pageData;

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

@ -3218,6 +3218,9 @@
let spellInfo = aMessage.data.spellInfo;
if (spellInfo)
spellInfo.target = aMessage.target.messageManager;
let documentURIObject = makeURI(aMessage.data.docLocation,
aMessage.data.charSet,
makeURI(aMessage.data.baseURI));
gContextMenuContentData = { isRemote: true,
event: aMessage.objects.event,
popupNode: aMessage.objects.popupNode,
@ -3226,7 +3229,11 @@
spellInfo: spellInfo,
principal: aMessage.data.principal,
customMenuItems: aMessage.data.customMenuItems,
addonInfo: aMessage.data.addonInfo };
addonInfo: aMessage.data.addonInfo,
documentURIObject: documentURIObject,
docLocation: aMessage.data.docLocation,
charSet: aMessage.data.charSet,
referrer: aMessage.data.referrer };
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
let event = gContextMenuContentData.event;
let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);

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

@ -12,6 +12,7 @@ support-files =
browser_bug678392-2.html
browser_bug970746.xhtml
browser_fxa_oauth.html
browser_fxa_profile_channel.html
browser_registerProtocolHandler_notification.html
browser_ssl_error_reports_content.js
browser_star_hsts.sjs
@ -319,6 +320,7 @@ skip-if = e10s
skip-if = buildapp == 'mulet' || e10s || os == "linux" # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly. Linux: Intermittent failures - bug 941575.
[browser_fxa_migrate.js]
[browser_fxa_oauth.js]
[browser_fxa_profile_channel.js]
[browser_gestureSupport.js]
skip-if = e10s # Bug 863514 - no gesture support.
[browser_getshortcutoruri.js]

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

@ -26,8 +26,7 @@ function test() {
cb();
});
let contextMenu = initContextMenu(contentBody);
contextMenu.viewBGImage();
doContextCommand(contentBody, "context-viewbgimage");
}
},
{
@ -45,8 +44,7 @@ function test() {
img.setAttribute("src", writeDomainURL);
doc.body.appendChild(img);
let contextMenu = initContextMenu(img);
contextMenu.viewMedia();
doContextCommand(img, "context-viewimage");
}
},
{
@ -65,8 +63,8 @@ function test() {
doc.body.appendChild(iframe);
iframe.addEventListener("load", function onload() {
let contextMenu = initContextMenu(iframe.contentDocument.body);
contextMenu.showOnlyThisFrame();
doContextCommand(iframe.contentDocument.body,
"context-showonlythisframe");
}, false);
}
}
@ -99,9 +97,8 @@ function test() {
doNext();
}
function initContextMenu(aNode) {
document.popupNode = aNode;
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let contextMenu = new nsContextMenu(contentAreaContextMenu);
return contextMenu;
function doContextCommand(aNode, aCmd) {
EventUtils.sendMouseEvent({ type: "contextmenu" }, aNode);
document.getElementById(aCmd).click();
document.getElementById("contentAreaContextMenu").hidePopup();
}

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

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>fxa_profile_channel_test</title>
</head>
<body>
<script>
window.onload = function(){
var event = new window.CustomEvent("WebChannelMessageToChrome", {
detail: {
id: "account_updates",
message: {
command: "profile:image:change",
data: {
uid: "abc123",
},
},
},
});
window.dispatchEvent(event);
};
</script>
</body>
</html>

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

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
return Components.utils.import("resource://gre/modules/FxAccountsCommon.js", {});
});
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsProfileChannel",
"resource://gre/modules/FxAccountsProfileChannel.jsm");
const HTTP_PATH = "http://example.com";
let gTests = [
{
desc: "FxA Profile Channel - should receive message about account updates",
run: function* () {
return new Promise(function(resolve, reject) {
let tabOpened = false;
let properUrl = "http://example.com/browser/browser/base/content/test/general/browser_fxa_profile_channel.html";
waitForTab(function (tab) {
Assert.ok("Tab successfully opened");
let match = gBrowser.currentURI.spec == properUrl;
Assert.ok(match);
tabOpened = true;
});
let client = new FxAccountsProfileChannel({
content_uri: HTTP_PATH,
});
makeObserver(FxAccountsCommon.ONPROFILE_IMAGE_CHANGE_NOTIFICATION, function (subject, topic, data) {
Assert.ok(tabOpened);
Assert.equal(data, "abc123");
resolve();
gBrowser.removeCurrentTab();
});
gBrowser.selectedTab = gBrowser.addTab(properUrl);
});
}
}
]; // gTests
function makeObserver(aObserveTopic, aObserveFunc) {
let callback = function (aSubject, aTopic, aData) {
if (aTopic == aObserveTopic) {
removeMe();
aObserveFunc(aSubject, aTopic, aData);
}
};
function removeMe() {
Services.obs.removeObserver(callback, aObserveTopic);
}
Services.obs.addObserver(callback, aObserveTopic, false);
return removeMe;
}
function waitForTab(aCallback) {
let container = gBrowser.tabContainer;
container.addEventListener("TabOpen", function tabOpener(event) {
container.removeEventListener("TabOpen", tabOpener, false);
gBrowser.addEventListener("load", function listener() {
gBrowser.removeEventListener("load", listener, true);
let tab = event.target;
aCallback(tab);
}, true);
}, false);
}
function test() {
waitForExplicitFinish();
Task.spawn(function () {
for (let test of gTests) {
info("Running: " + test.desc);
yield test.run();
}
}).then(finish, ex => {
Assert.ok(false, "Unexpected Exception: " + ex);
finish();
});
}

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

@ -414,29 +414,36 @@ function waitForDocLoadAndStopIt(aExpectedURL, aBrowser=gBrowser.selectedBrowser
* @return promise
*/
function waitForDocLoadComplete(aBrowser=gBrowser) {
let deferred = Promise.defer();
let progressListener = {
onStateChange: function (webProgress, req, flags, status) {
let docStop = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
Ci.nsIWebProgressListener.STATE_STOP;
info("Saw state " + flags.toString(16) + " and status " + status.toString(16));
return new Promise(resolve => {
let listener = {
onStateChange: function (webProgress, req, flags, status) {
let docStop = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
Ci.nsIWebProgressListener.STATE_STOP;
info("Saw state " + flags.toString(16) + " and status " + status.toString(16));
// When a load needs to be retargetted to a new process it is cancelled
// with NS_BINDING_ABORTED so ignore that case
if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
aBrowser.removeProgressListener(progressListener);
info("Browser loaded " + aBrowser.contentWindow.location);
deferred.resolve();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
aBrowser.addProgressListener(progressListener);
info("Waiting for browser load");
return deferred.promise;
// When a load needs to be retargetted to a new process it is cancelled
// with NS_BINDING_ABORTED so ignore that case
if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
aBrowser.removeProgressListener(this);
waitForDocLoadComplete.listeners.delete(this);
info("Browser loaded " + aBrowser.contentWindow.location);
resolve();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
aBrowser.addProgressListener(listener);
waitForDocLoadComplete.listeners.add(listener);
info("Waiting for browser load");
});
}
// Keep a set of progress listeners for waitForDocLoadComplete() to make sure
// they're not GC'ed before we saw the page load.
waitForDocLoadComplete.listeners = new Set();
registerCleanupFunction(() => waitForDocLoadComplete.listeners.clear());
let FullZoomHelper = {
selectTabAndWaitForLocationChange: function selectTabAndWaitForLocationChange(tab) {

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

@ -4,6 +4,9 @@
# 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/.
with Files('*'):
BUG_COMPONENT = ('Firefox', 'Downloads Panel')
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']

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

@ -166,7 +166,8 @@ loop.roomViews = (function(mozL10n) {
ActiveRoomStoreMixin,
sharedMixins.DocumentTitleMixin,
sharedMixins.MediaSetupMixin,
sharedMixins.RoomsAudioMixin
sharedMixins.RoomsAudioMixin,
sharedMixins.WindowCloseMixin
],
propTypes: {
@ -204,14 +205,11 @@ loop.roomViews = (function(mozL10n) {
* User clicked on the "Leave" button.
*/
leaveRoom: function() {
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
},
/**
* Closes the window if the cancel button is pressed in the generic failure view.
*/
closeWindow: function() {
window.close();
if (this.state.used) {
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
} else {
this.closeWindow();
}
},
/**
@ -255,15 +253,9 @@ loop.roomViews = (function(mozL10n) {
);
}
case ROOM_STATES.ENDED: {
if (this.state.used)
return React.createElement(sharedViews.FeedbackView, {
onAfterFeedbackReceived: this.closeWindow}
);
// In case the room was not used (no one was here), we
// bypass the feedback form.
this.closeWindow();
return null;
return React.createElement(sharedViews.FeedbackView, {
onAfterFeedbackReceived: this.closeWindow}
);
}
default: {
return (

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

@ -166,7 +166,8 @@ loop.roomViews = (function(mozL10n) {
ActiveRoomStoreMixin,
sharedMixins.DocumentTitleMixin,
sharedMixins.MediaSetupMixin,
sharedMixins.RoomsAudioMixin
sharedMixins.RoomsAudioMixin,
sharedMixins.WindowCloseMixin
],
propTypes: {
@ -204,14 +205,11 @@ loop.roomViews = (function(mozL10n) {
* User clicked on the "Leave" button.
*/
leaveRoom: function() {
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
},
/**
* Closes the window if the cancel button is pressed in the generic failure view.
*/
closeWindow: function() {
window.close();
if (this.state.used) {
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
} else {
this.closeWindow();
}
},
/**
@ -255,15 +253,9 @@ loop.roomViews = (function(mozL10n) {
/>;
}
case ROOM_STATES.ENDED: {
if (this.state.used)
return <sharedViews.FeedbackView
onAfterFeedbackReceived={this.closeWindow}
/>;
// In case the room was not used (no one was here), we
// bypass the feedback form.
this.closeWindow();
return null;
return <sharedViews.FeedbackView
onAfterFeedbackReceived={this.closeWindow}
/>;
}
default: {
return (

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

@ -497,13 +497,6 @@ loop.store.ActiveRoomStore = (function() {
windowUnload: function() {
this._leaveRoom(ROOM_STATES.CLOSING);
// If we're closing the window, then ensure the screensharing state
// is cleared. We don't do this on leave room, as we might still be
// sharing.
this._mozLoop.setScreenShareState(
this.getStoreState().windowId,
false);
if (!this._onUpdateListener) {
return;
}
@ -520,7 +513,7 @@ loop.store.ActiveRoomStore = (function() {
* Handles a room being left.
*/
leaveRoom: function() {
this._leaveRoom();
this._leaveRoom(ROOM_STATES.ENDED);
},
/**
@ -554,14 +547,24 @@ loop.store.ActiveRoomStore = (function() {
* Handles leaving a room. Clears any membership timeouts, then
* signals to the server the leave of the room.
*
* @param {ROOM_STATES} nextState Optional; the next state to switch to.
* Switches to READY if undefined.
* @param {ROOM_STATES} nextState The next state to switch to.
*/
_leaveRoom: function(nextState) {
if (loop.standaloneMedia) {
loop.standaloneMedia.multiplexGum.reset();
}
this._mozLoop.setScreenShareState(
this.getStoreState().windowId,
false);
if (this._browserSharingListener) {
// Remove the browser sharing listener as we don't need it now.
this._mozLoop.removeBrowserSharingListener(this._browserSharingListener);
this._browserSharingListener = null;
}
// We probably don't need to end screen share separately, but lets be safe.
this._sdkDriver.disconnectSession();
if (this._timeout) {
@ -577,7 +580,7 @@ loop.store.ActiveRoomStore = (function() {
this._storeState.sessionToken);
}
this.setStoreState({roomState: nextState || ROOM_STATES.ENDED});
this.setStoreState({roomState: nextState});
},
/**

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

@ -28,7 +28,7 @@ loop.store.ROOM_STATES = {
FAILED: "room-failed",
// The room is full
FULL: "room-full",
// The room conversation has ended
// The room conversation has ended, displays the feedback view.
ENDED: "room-ended",
// The window is closing
CLOSING: "room-closing"

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -22,6 +22,7 @@ describe("loop.roomViews", function () {
};
fakeWindow = {
close: sinon.stub(),
document: {},
navigator: {
mozLoop: fakeMozLoop
@ -290,6 +291,32 @@ describe("loop.roomViews", function () {
sinon.match.hasOwn("name", "setMute"));
});
it("should dispatch a `LeaveRoom` action when the hangup button is pressed and the room has been used", function() {
view = mountTestComponent();
view.setState({used: true});
var hangupBtn = view.getDOMNode().querySelector(".btn-hangup");
React.addons.TestUtils.Simulate.click(hangupBtn);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.LeaveRoom());
});
it("should close the window when the hangup button is pressed and the room has not been used", function() {
view = mountTestComponent();
view.setState({used: false});
var hangupBtn = view.getDOMNode().querySelector(".btn-hangup");
React.addons.TestUtils.Simulate.click(hangupBtn);
sinon.assert.calledOnce(fakeWindow.close);
});
describe("#componentWillUpdate", function() {
function expectActionDispatched(view) {
sinon.assert.calledOnce(dispatcher.dispatch);
@ -389,18 +416,6 @@ describe("loop.roomViews", function () {
TestUtils.findRenderedComponentWithType(view,
loop.shared.views.FeedbackView);
});
it("should NOT render the FeedbackView if the room has not been used",
function() {
activeRoomStore.setStoreState({
roomState: ROOM_STATES.ENDED,
used: false
});
view = mountTestComponent();
expect(view.getDOMNode()).eql(null);
});
});
describe("Mute", function() {

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

@ -142,6 +142,15 @@ describe("loop.store.ActiveRoomStore", function () {
sinon.assert.calledOnce(fakeMultiplexGum.reset);
});
it("should set screen sharing inactive", function() {
store.setStoreState({windowId: "1234"});
store.roomFailure({error: fakeError});
sinon.assert.calledOnce(fakeMozLoop.setScreenShareState);
sinon.assert.calledWithExactly(fakeMozLoop.setScreenShareState, "1234", false);
});
it("should disconnect from the servers via the sdk", function() {
store.roomFailure({error: fakeError});
@ -157,6 +166,18 @@ describe("loop.store.ActiveRoomStore", function () {
sinon.assert.calledOnce(clearTimeout);
});
it("should remove the sharing listener", function() {
// Setup the listener.
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
// Now simulate room failure.
store.roomFailure({error: fakeError});
sinon.assert.calledOnce(fakeMozLoop.removeBrowserSharingListener);
});
it("should call mozLoop.rooms.leave", function() {
store.roomFailure({error: fakeError});
@ -600,6 +621,15 @@ describe("loop.store.ActiveRoomStore", function () {
sinon.assert.calledOnce(fakeMultiplexGum.reset);
});
it("should set screen sharing inactive", function() {
store.setStoreState({windowId: "1234"});
store.connectionFailure(connectionFailureAction);
sinon.assert.calledOnce(fakeMozLoop.setScreenShareState);
sinon.assert.calledWithExactly(fakeMozLoop.setScreenShareState, "1234", false);
});
it("should disconnect from the servers via the sdk", function() {
store.connectionFailure(connectionFailureAction);
@ -623,6 +653,18 @@ describe("loop.store.ActiveRoomStore", function () {
"fakeToken", "1627384950");
});
it("should remove the sharing listener", function() {
// Setup the listener.
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
// Now simulate connection failure.
store.connectionFailure(connectionFailureAction);
sinon.assert.calledOnce(fakeMozLoop.removeBrowserSharingListener);
});
it("should set the state to `FAILED`", function() {
store.connectionFailure(connectionFailureAction);
@ -877,6 +919,18 @@ describe("loop.store.ActiveRoomStore", function () {
"fakeToken", "1627384950");
});
it("should remove the sharing listener", function() {
// Setup the listener.
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
// Now unload the window.
store.windowUnload();
sinon.assert.calledOnce(fakeMozLoop.removeBrowserSharingListener);
});
it("should set the state to CLOSING", function() {
store.windowUnload();
@ -922,6 +976,18 @@ describe("loop.store.ActiveRoomStore", function () {
"fakeToken", "1627384950");
});
it("should remove the sharing listener", function() {
// Setup the listener.
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
// Now leave the room.
store.leaveRoom();
sinon.assert.calledOnce(fakeMozLoop.removeBrowserSharingListener);
});
it("should set the state to ENDED", function() {
store.leaveRoom();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,332 @@
/* 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";
this.EXPORTED_SYMBOLS = [
"SQLiteStore",
];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReadingList",
"resource:///modules/readinglist/ReadingList.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
"resource://gre/modules/Sqlite.jsm");
/**
* A SQLite Reading List store backed by a database on disk. The database is
* created if it doesn't exist.
*
* @param pathRelativeToProfileDir The path of the database file relative to
* the profile directory.
*/
this.SQLiteStore = function SQLiteStore(pathRelativeToProfileDir) {
this.pathRelativeToProfileDir = pathRelativeToProfileDir;
this._ensureConnection(pathRelativeToProfileDir);
};
this.SQLiteStore.prototype = {
/**
* Yields the number of items in the store that match the given options.
*
* @param optsList A variable number of options objects that control the
* items that are matched. See Options Objects in ReadingList.jsm.
* @return Promise<number> The number of matching items in the store.
* Rejected with an Error on error.
*/
count: Task.async(function* (...optsList) {
let [sql, args] = sqlFromOptions(optsList);
let count = 0;
let conn = yield this._connectionPromise;
yield conn.executeCached(`
SELECT COUNT(*) AS count FROM items ${sql};
`, args, row => count = row.getResultByName("count"));
return count;
}),
/**
* Enumerates the items in the store that match the given options.
*
* @param callback Called for each item in the enumeration. It's passed a
* single object, an item.
* @param optsList A variable number of options objects that control the
* items that are matched. See Options Objects in ReadingList.jsm.
* @return Promise<null> Resolved when the enumeration completes. Rejected
* with an Error on error.
*/
forEachItem: Task.async(function* (callback, ...optsList) {
let [sql, args] = sqlFromOptions(optsList);
let colNames = ReadingList.ItemBasicPropertyNames;
let conn = yield this._connectionPromise;
yield conn.executeCached(`
SELECT ${colNames} FROM items ${sql};
`, args, row => callback(itemFromRow(row)));
}),
/**
* Adds an item to the store that isn't already present. See
* ReadingList.prototype.addItems.
*
* @param items A simple object representing an item.
* @return Promise<null> Resolved when the store is updated. Rejected with an
* Error on error.
*/
addItem: Task.async(function* (item) {
let colNames = [];
let paramNames = [];
for (let propName in item) {
colNames.push(propName);
paramNames.push(`:${propName}`);
}
let conn = yield this._connectionPromise;
yield conn.executeCached(`
INSERT INTO items (${colNames}) VALUES (${paramNames});
`, item);
}),
/**
* Updates the properties of an item that's already present in the store. See
* ReadingList.prototype.updateItem.
*
* @param item The item to update. It must have a `url`.
* @return Promise<null> Resolved when the store is updated. Rejected with an
* Error on error.
*/
updateItem: Task.async(function* (item) {
let assignments = [];
for (let propName in item) {
assignments.push(`${propName} = :${propName}`);
}
let conn = yield this._connectionPromise;
yield conn.executeCached(`
UPDATE items SET ${assignments} WHERE url = :url;
`, item);
}),
/**
* Deletes an item from the store.
*
* @param url The URL string of the item to delete.
* @return Promise<null> Resolved when the store is updated. Rejected with an
* Error on error.
*/
deleteItemByURL: Task.async(function* (url) {
let conn = yield this._connectionPromise;
yield conn.executeCached(`
DELETE FROM items WHERE url = :url;
`, { url: url });
}),
/**
* Call this when you're done with the store. Don't use it afterward.
*/
destroy: Task.async(function* () {
let conn = yield this._connectionPromise;
yield conn.close();
this._connectionPromise = Promise.reject("Store destroyed");
}),
/**
* Creates the database connection if it hasn't been created already.
*
* @param pathRelativeToProfileDir The path of the database file relative to
* the profile directory.
*/
_ensureConnection: Task.async(function* (pathRelativeToProfileDir) {
if (!this._connectionPromise) {
this._connectionPromise = Task.spawn(function* () {
let conn = yield Sqlite.openConnection({
path: pathRelativeToProfileDir,
sharedMemoryCache: false,
});
Sqlite.shutdown.addBlocker("readinglist/SQLiteStore: Destroy",
this.destroy.bind(this));
yield conn.execute(`
PRAGMA locking_mode = EXCLUSIVE;
`);
yield this._checkSchema(conn);
return conn;
}.bind(this));
}
}),
// Promise<Sqlite.OpenedConnection>
_connectionPromise: null,
// The current schema version.
_schemaVersion: 1,
_checkSchema: Task.async(function* (conn) {
let version = parseInt(yield conn.getSchemaVersion());
for (; version < this._schemaVersion; version++) {
let meth = `_migrateSchema${version}To${version + 1}`;
yield this[meth](conn);
}
yield conn.setSchemaVersion(this._schemaVersion);
}),
_migrateSchema0To1: Task.async(function* (conn) {
yield conn.execute(`
PRAGMA journal_mode = wal;
`);
// 524288 bytes = 512 KiB
yield conn.execute(`
PRAGMA journal_size_limit = 524288;
`);
yield conn.execute(`
CREATE TABLE items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guid TEXT UNIQUE,
url TEXT NOT NULL UNIQUE,
resolvedURL TEXT UNIQUE,
lastModified INTEGER,
title TEXT,
resolvedTitle TEXT,
excerpt TEXT,
status INTEGER,
favorite BOOLEAN,
isArticle BOOLEAN,
wordCount INTEGER,
unread BOOLEAN,
addedBy TEXT,
addedOn INTEGER,
storedOn INTEGER,
markedReadBy TEXT,
markedReadOn INTEGER,
readPosition INTEGER
);
`);
yield conn.execute(`
CREATE INDEX items_addedOn ON items (addedOn);
`);
yield conn.execute(`
CREATE INDEX items_unread ON items (unread);
`);
}),
};
/**
* Returns a simple object whose properties are the
* ReadingList.ItemBasicPropertyNames properties lifted from the given row.
*
* @param row A mozIStorageRow.
* @return The item.
*/
function itemFromRow(row) {
let item = {};
for (let name of ReadingList.ItemBasicPropertyNames) {
item[name] = row.getResultByName(name);
}
return item;
}
/**
* Returns the back part of a SELECT statement generated from the given list of
* options.
*
* @param optsList See Options Objects in ReadingList.jsm.
* @return An array [sql, args]. sql is a string of SQL. args is an object
* that contains arguments for all the parameters in sql.
*/
function sqlFromOptions(optsList) {
// We modify the options objects, which were passed in by the store client, so
// clone them first.
optsList = Cu.cloneInto(optsList, {}, { cloneFunctions: false });
let sort;
let sortDir;
let limit;
let offset;
for (let opts of optsList) {
if ("sort" in opts) {
sort = opts.sort;
delete opts.sort;
}
if ("descending" in opts) {
if (opts.descending) {
sortDir = "DESC";
}
delete opts.descending;
}
if ("limit" in opts) {
limit = opts.limit;
delete opts.limit;
}
if ("offset" in opts) {
offset = opts.offset;
delete opts.offset;
}
}
let fragments = [];
if (sort) {
sortDir = sortDir || "ASC";
fragments.push(`ORDER BY ${sort} ${sortDir}`);
}
if (limit) {
fragments.push(`LIMIT ${limit}`);
if (offset) {
fragments.push(`OFFSET ${offset}`);
}
}
let args = {};
function uniqueParamName(name) {
if (name in args) {
for (let i = 1; ; i++) {
let newName = `${name}_${i}`;
if (!(newName in args)) {
return newName;
}
}
}
return name;
}
// Build a WHERE clause for the remaining properties. Assume they all refer
// to columns. (If they don't, the SQL query will fail.)
let disjunctions = [];
for (let opts of optsList) {
let conjunctions = [];
for (let key in opts) {
if (Array.isArray(opts[key])) {
// Convert arrays to IN expressions. e.g., { guid: ['a', 'b', 'c'] }
// becomes "guid IN (:guid, :guid_1, :guid_2)". The guid_i arguments
// are added to opts.
let array = opts[key];
let params = [];
for (let i = 0; i < array.length; i++) {
let paramName = uniqueParamName(key);
params.push(`:${paramName}`);
args[paramName] = array[i];
}
conjunctions.push(`${key} IN (${params})`);
}
else {
let paramName = uniqueParamName(key);
conjunctions.push(`${key} = :${paramName}`);
args[paramName] = opts[key];
}
}
let conjunction = conjunctions.join(" AND ");
if (conjunction) {
disjunctions.push(`(${conjunction})`);
}
}
let disjunction = disjunctions.join(" OR ");
if (disjunction) {
let where = `WHERE ${disjunction}`;
fragments = [where].concat(fragments);
}
let sql = fragments.join(" ");
return [sql, args];
}

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

@ -6,6 +6,7 @@ JAR_MANIFESTS += ['jar.mn']
EXTRA_JS_MODULES.readinglist += [
'ReadingList.jsm',
'SQLiteStore.jsm',
]
TESTING_JS_MODULES += [

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

@ -7,6 +7,7 @@
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/readinglist/ReadingList.jsm");
@ -21,6 +22,12 @@ let RLSidebar = {
*/
list: null,
/**
* A promise that's resolved when building the initial list completes.
* @type {Promise}
*/
listPromise: null,
/**
* <template> element used for constructing list item elements.
* @type {Element}
@ -53,7 +60,7 @@ let RLSidebar = {
this.list.addEventListener("mousemove", event => this.onListMouseMove(event));
this.list.addEventListener("keydown", event => this.onListKeyDown(event), true);
this.ensureListItems();
this.listPromise = this.ensureListItems();
ReadingList.addListener(this);
let initEvent = new CustomEvent("Initialized", {bubbles: true});
@ -74,7 +81,7 @@ let RLSidebar = {
* TODO: We may not want to show this new item right now.
* TODO: We should guard against the list growing here.
*
* @param {Readinglist.Item} item - Item that was added.
* @param {ReadinglistItem} item - Item that was added.
*/
onItemAdded(item) {
log.trace(`onItemAdded: ${item}`);
@ -88,7 +95,7 @@ let RLSidebar = {
/**
* Handle an item being deleted from the ReadingList.
* @param {ReadingList.Item} item - Item that was deleted.
* @param {ReadingListItem} item - Item that was deleted.
*/
onItemDeleted(item) {
log.trace(`onItemDeleted: ${item}`);
@ -103,7 +110,7 @@ let RLSidebar = {
/**
* Handle an item in the ReadingList having any of its properties changed.
* @param {ReadingList.Item} item - Item that was updated.
* @param {ReadingListItem} item - Item that was updated.
*/
onItemUpdated(item) {
log.trace(`onItemUpdated: ${item}`);
@ -118,12 +125,12 @@ let RLSidebar = {
/**
* Update the element representing an item, ensuring it's in sync with the
* underlying data.
* @param {ReadingList.Item} item - Item to use as a source.
* @param {ReadingListItem} item - Item to use as a source.
* @param {Element} itemNode - Element to update.
*/
updateItem(item, itemNode) {
itemNode.setAttribute("id", "item-" + item.id);
itemNode.setAttribute("title", `${item.title}\n${item.url.spec}`);
itemNode.setAttribute("title", `${item.title}\n${item.url}`);
itemNode.querySelector(".item-title").textContent = item.title;
itemNode.querySelector(".item-domain").textContent = item.domain;
@ -132,18 +139,16 @@ let RLSidebar = {
/**
* Ensure that the list is populated with the correct items.
*/
ensureListItems() {
ReadingList.getItems().then(items => {
for (let item of items) {
// TODO: Should be batch inserting via DocumentFragment
try {
this.onItemAdded(item);
} catch (e) {
log.warn("Error adding item", e);
}
ensureListItems: Task.async(function* () {
yield ReadingList.forEachItem(item => {
// TODO: Should be batch inserting via DocumentFragment
try {
this.onItemAdded(item);
} catch (e) {
log.warn("Error adding item", e);
}
});
},
}),
/**
* Get the number of items currently displayed in the list.
@ -317,7 +322,7 @@ let RLSidebar = {
}
let item = this.getItemFromNode(itemNode);
this.openURL(item.url.spec, event);
this.openURL(item.url, event);
},
/**

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

@ -7,6 +7,7 @@ this.EXPORTED_SYMBOLS = [
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource:///modules/readinglist/ReadingList.jsm");
@ -41,6 +42,15 @@ SidebarUtils.prototype = {
return this.RLSidebar.list;
},
/**
* Opens the sidebar and waits until it finishes building its list.
* @return {Promise} Resolved when the sidebar's list is ready.
*/
showSidebar: Task.async(function* () {
yield this.window.ReadingListUI.showSidebar();
yield this.RLSidebar.listPromise;
}),
/**
* Check that the number of elements in the list matches the expected count.
* @param {number} count - Expected number of items.
@ -136,24 +146,18 @@ this.ReadingListTestUtils = {
}
return Promise.all(promises);
}
return new Promise(resolve => {
let item = new ReadingList.Item(data);
ReadingList._items.push(item);
ReadingList._notifyListeners("onItemAdded", item);
resolve(item);
});
return ReadingList.addItem(data);
},
/**
* Cleanup all data, resetting to a blank state.
*/
cleanup() {
return new Promise(resolve => {
ReadingList._items = [];
ReadingList._listeners.clear();
Preferences.reset(PREF_RL_ENABLED);
resolve();
});
},
cleanup: Task.async(function *() {
Preferences.reset(PREF_RL_ENABLED);
let items = [];
yield ReadingList.forEachItem(i => items.push(i));
for (let item of items) {
yield ReadingList.deleteItem(item);
}
}),
};

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

@ -10,7 +10,7 @@ add_task(function*() {
RLUtils.enabled = true;
yield ReadingListUI.showSidebar();
yield RLSidebarUtils.showSidebar();
let RLSidebar = RLSidebarUtils.RLSidebar;
let sidebarDoc = SidebarUI.browser.contentDocument;
Assert.equal(RLSidebar.numItems, 0, "Should start with no items");
@ -19,7 +19,6 @@ add_task(function*() {
info("Adding first item");
yield RLUtils.addItem({
id: "c3502a49-bcef-4a94-b222-d4834463de33",
url: "http://example.com/article1",
title: "Article 1",
});
@ -27,11 +26,9 @@ add_task(function*() {
info("Adding more items");
yield RLUtils.addItem([{
id: "e054f5b7-1f4f-463f-bb96-d64c02448c31",
url: "http://example.com/article2",
title: "Article 2",
}, {
id: "4207230b-2364-4e97-9587-01312b0ce4e6",
url: "http://example.com/article3",
title: "Article 3",
}]);
@ -42,12 +39,11 @@ add_task(function*() {
info("Adding another item");
yield RLUtils.addItem({
id: "dae0e855-607e-4df3-b27f-73a5e35c94fe",
url: "http://example.com/article4",
title: "Article 4",
});
info("Re-eopning sidebar");
yield ReadingListUI.showSidebar();
info("Re-opening sidebar");
yield RLSidebarUtils.showSidebar();
RLSidebarUtils.expectNumItems(4);
});

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

@ -23,62 +23,60 @@ add_task(function*() {
RLUtils.enabled = true;
let itemData = [{
id: "00bd24c7-3629-40b0-acde-37aa81768735",
url: "http://example.com/article1",
title: "Article 1",
}, {
id: "28bf7f19-cf94-4ceb-876a-ac1878342e0d",
url: "http://example.com/article2",
title: "Article 2",
}, {
id: "7e5064ea-f45d-4fc7-8d8c-c067b7781e78",
url: "http://example.com/article3",
title: "Article 3",
}, {
id: "8e72a472-8db8-4904-ba39-9672f029e2d0",
url: "http://example.com/article4",
title: "Article 4",
}, {
id: "8d332744-37bc-4a1a-a26b-e9953b9f7d91",
url: "http://example.com/article5",
title: "Article 5",
}];
info("Adding initial mock data");
yield RLUtils.addItem(itemData);
info("Fetching items");
let items = yield ReadingList.iterator({ sort: "url" }).items(itemData.length);
info("Opening sidebar");
yield ReadingListUI.showSidebar();
yield RLSidebarUtils.showSidebar();
RLSidebarUtils.expectNumItems(5);
RLSidebarUtils.expectSelectedId(null);
RLSidebarUtils.expectActiveId(null);
info("Mouse move over item 1");
yield mouseInteraction("mousemove", "SelectedItemChanged", RLSidebarUtils.list.children[0]);
RLSidebarUtils.expectSelectedId(itemData[0].id);
RLSidebarUtils.expectSelectedId(items[0].id);
RLSidebarUtils.expectActiveId(null);
info("Mouse move over item 2");
yield mouseInteraction("mousemove", "SelectedItemChanged", RLSidebarUtils.list.children[1]);
RLSidebarUtils.expectSelectedId(itemData[1].id);
RLSidebarUtils.expectSelectedId(items[1].id);
RLSidebarUtils.expectActiveId(null);
info("Mouse move over item 5");
yield mouseInteraction("mousemove", "SelectedItemChanged", RLSidebarUtils.list.children[4]);
RLSidebarUtils.expectSelectedId(itemData[4].id);
RLSidebarUtils.expectSelectedId(items[4].id);
RLSidebarUtils.expectActiveId(null);
info("Mouse move over item 1 again");
yield mouseInteraction("mousemove", "SelectedItemChanged", RLSidebarUtils.list.children[0]);
RLSidebarUtils.expectSelectedId(itemData[0].id);
RLSidebarUtils.expectSelectedId(items[0].id);
RLSidebarUtils.expectActiveId(null);
info("Mouse click on item 1");
yield mouseInteraction("click", "ActiveItemChanged", RLSidebarUtils.list.children[0]);
RLSidebarUtils.expectSelectedId(itemData[0].id);
RLSidebarUtils.expectActiveId(itemData[0].id);
RLSidebarUtils.expectSelectedId(items[0].id);
RLSidebarUtils.expectActiveId(items[0].id);
info("Mouse click on item 3");
yield mouseInteraction("click", "ActiveItemChanged", RLSidebarUtils.list.children[2]);
RLSidebarUtils.expectSelectedId(itemData[2].id);
RLSidebarUtils.expectActiveId(itemData[2].id);
RLSidebarUtils.expectSelectedId(items[2].id);
RLSidebarUtils.expectActiveId(items[2].id);
});

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

@ -0,0 +1,701 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gDBFile = do_get_profile();
Cu.import("resource:///modules/readinglist/ReadingList.jsm");
Cu.import("resource:///modules/readinglist/SQLiteStore.jsm");
Cu.import("resource://gre/modules/Sqlite.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
var gList;
var gItems;
function run_test() {
run_next_test();
}
add_task(function* prepare() {
gList = ReadingList;
Assert.ok(gList);
gDBFile.append(gList._store.pathRelativeToProfileDir);
do_register_cleanup(() => {
if (gDBFile.exists()) {
gDBFile.remove(true);
}
});
gItems = [];
for (let i = 0; i < 3; i++) {
gItems.push({
list: gList,
guid: `guid${i}`,
url: `http://example.com/${i}`,
resolvedURL: `http://example.com/resolved/${i}`,
title: `title ${i}`,
excerpt: `excerpt ${i}`,
unread: 0,
addedOn: Date.now(),
lastModified: Date.now(),
favorite: 0,
isArticle: 1,
storedOn: Date.now(),
});
}
for (let item of gItems) {
let addedItem = yield gList.addItem(item);
checkItems(addedItem, item);
}
});
add_task(function* item_properties() {
// get an item
let iter = gList.iterator({
sort: "guid",
});
let item = (yield iter.items(1))[0];
Assert.ok(item);
Assert.ok(item.uri);
Assert.ok(item.uri instanceof Ci.nsIURI);
Assert.equal(item.uri.spec, item.url);
Assert.ok(item.resolvedURI);
Assert.ok(item.resolvedURI instanceof Ci.nsIURI);
Assert.equal(item.resolvedURI.spec, item.resolvedURL);
Assert.ok(item.lastModified);
Assert.ok(item.lastModified instanceof Cu.getGlobalForObject(ReadingList).Date);
Assert.ok(item.addedOn);
Assert.ok(item.addedOn instanceof Cu.getGlobalForObject(ReadingList).Date);
Assert.ok(item.storedOn);
Assert.ok(item.storedOn instanceof Cu.getGlobalForObject(ReadingList).Date);
Assert.ok(typeof(item.favorite) == "boolean");
Assert.ok(typeof(item.isArticle) == "boolean");
Assert.ok(typeof(item.unread) == "boolean");
Assert.equal(item.domain, "example.com");
Assert.equal(item.id, hash(item.url));
});
add_task(function* constraints() {
// add an item again
let err = null;
try {
yield gList.addItem(gItems[0]);
}
catch (e) {
err = e;
}
checkError(err);
// add a new item with an existing guid
let item = kindOfClone(gItems[0]);
item.guid = gItems[0].guid;
err = null;
try {
yield gList.addItem(item);
}
catch (e) {
err = e;
}
checkError(err);
// add a new item with an existing url
item = kindOfClone(gItems[0]);
item.url = gItems[0].url;
err = null;
try {
yield gList.addItem(item);
}
catch (e) {
err = e;
}
checkError(err);
// update an item with an existing url
item.guid = gItems[1].guid;
err = null;
try {
yield gList.updateItem(item);
}
catch (e) {
err = e;
}
checkError(err);
// add a new item with an existing resolvedURL
item = kindOfClone(gItems[0]);
item.resolvedURL = gItems[0].resolvedURL;
err = null;
try {
yield gList.addItem(item);
}
catch (e) {
err = e;
}
checkError(err);
// update an item with an existing resolvedURL
item.url = gItems[1].url;
err = null;
try {
yield gList.updateItem(item);
}
catch (e) {
err = e;
}
checkError(err);
// add a new item with no guid, which is allowed
item = kindOfClone(gItems[0]);
delete item.guid;
err = null;
try {
yield gList.addItem(item);
}
catch (e) {
err = e;
}
Assert.ok(!err, err ? err.message : undefined);
let item1 = item;
// add a second item with no guid, which is allowed
item = kindOfClone(gItems[1]);
delete item.guid;
err = null;
try {
yield gList.addItem(item);
}
catch (e) {
err = e;
}
Assert.ok(!err, err ? err.message : undefined);
let item2 = item;
// Delete both items since other tests assume the store contains only gItems.
item1.list = gList;
item2.list = gList;
yield gList.deleteItem(item1);
yield gList.deleteItem(item2);
let items = [];
yield gList.forEachItem(i => items.push(i), { url: [item1.url, item2.url] });
Assert.equal(items.length, 0);
// add a new item with no url
item = kindOfClone(gItems[0]);
delete item.url;
err = null;
try {
yield gList.addItem(item);
}
catch (e) {
err = e;
}
checkError(err);
});
add_task(function* count() {
let count = yield gList.count();
Assert.equal(count, gItems.length);
count = yield gList.count({
guid: gItems[0].guid,
});
Assert.equal(count, 1);
});
add_task(function* forEachItem() {
// all items
let items = [];
yield gList.forEachItem(item => items.push(item), {
sort: "guid",
});
checkItems(items, gItems);
// first item
items = [];
yield gList.forEachItem(item => items.push(item), {
limit: 1,
sort: "guid",
});
checkItems(items, gItems.slice(0, 1));
// last item
items = [];
yield gList.forEachItem(item => items.push(item), {
limit: 1,
sort: "guid",
descending: true,
});
checkItems(items, gItems.slice(gItems.length - 1, gItems.length));
// match on a scalar property
items = [];
yield gList.forEachItem(item => items.push(item), {
guid: gItems[0].guid,
});
checkItems(items, gItems.slice(0, 1));
// match on an array
items = [];
yield gList.forEachItem(item => items.push(item), {
guid: gItems.map(i => i.guid),
sort: "guid",
});
checkItems(items, gItems);
// match on AND'ed properties
items = [];
yield gList.forEachItem(item => items.push(item), {
guid: gItems.map(i => i.guid),
title: gItems[0].title,
sort: "guid",
});
checkItems(items, [gItems[0]]);
// match on OR'ed properties
items = [];
yield gList.forEachItem(item => items.push(item), {
guid: gItems[1].guid,
sort: "guid",
}, {
guid: gItems[0].guid,
});
checkItems(items, [gItems[0], gItems[1]]);
// match on AND'ed and OR'ed properties
items = [];
yield gList.forEachItem(item => items.push(item), {
guid: gItems.map(i => i.guid),
title: gItems[1].title,
sort: "guid",
}, {
guid: gItems[0].guid,
});
checkItems(items, [gItems[0], gItems[1]]);
});
add_task(function* forEachItem_promises() {
// promises resolved immediately
let items = [];
yield gList.forEachItem(item => {
items.push(item);
return Promise.resolve();
}, {
sort: "guid",
});
checkItems(items, gItems);
// promises resolved after a delay
items = [];
let i = 0;
let promises = [];
yield gList.forEachItem(item => {
items.push(item);
// The previous promise should have been resolved by now.
if (i > 0) {
Assert.equal(promises[i - 1], null);
}
// Make a new promise that should continue iteration when resolved.
let this_i = i++;
let promise = new Promise(resolve => {
// Resolve the promise one second from now. The idea is that if
// forEachItem works correctly, then the callback should not be called
// again before the promise resolves -- before one second elapases.
// Maybe there's a better way to do this that doesn't hinge on timeouts.
setTimeout(() => {
promises[this_i] = null;
resolve();
}, 0);
});
promises.push(promise);
return promise;
}, {
sort: "guid",
});
checkItems(items, gItems);
});
add_task(function* iterator_forEach() {
// no limit
let items = [];
let iter = gList.iterator({
sort: "guid",
});
yield iter.forEach(item => items.push(item));
checkItems(items, gItems);
// limit one each time
items = [];
iter = gList.iterator({
sort: "guid",
});
for (let i = 0; i < gItems.length; i++) {
yield iter.forEach(item => items.push(item), 1);
checkItems(items, gItems.slice(0, i + 1));
}
yield iter.forEach(item => items.push(item), 100);
checkItems(items, gItems);
yield iter.forEach(item => items.push(item));
checkItems(items, gItems);
// match on a scalar property
items = [];
iter = gList.iterator({
sort: "guid",
guid: gItems[0].guid,
});
yield iter.forEach(item => items.push(item));
checkItems(items, [gItems[0]]);
// match on an array
items = [];
iter = gList.iterator({
sort: "guid",
guid: gItems.map(i => i.guid),
});
yield iter.forEach(item => items.push(item));
checkItems(items, gItems);
// match on AND'ed properties
items = [];
iter = gList.iterator({
sort: "guid",
guid: gItems.map(i => i.guid),
title: gItems[0].title,
});
yield iter.forEach(item => items.push(item));
checkItems(items, [gItems[0]]);
// match on OR'ed properties
items = [];
iter = gList.iterator({
sort: "guid",
guid: gItems[1].guid,
}, {
guid: gItems[0].guid,
});
yield iter.forEach(item => items.push(item));
checkItems(items, [gItems[0], gItems[1]]);
// match on AND'ed and OR'ed properties
items = [];
iter = gList.iterator({
sort: "guid",
guid: gItems.map(i => i.guid),
title: gItems[1].title,
}, {
guid: gItems[0].guid,
});
yield iter.forEach(item => items.push(item));
checkItems(items, [gItems[0], gItems[1]]);
});
add_task(function* iterator_items() {
// no limit
let iter = gList.iterator({
sort: "guid",
});
let items = yield iter.items(gItems.length);
checkItems(items, gItems);
items = yield iter.items(100);
checkItems(items, []);
// limit one each time
iter = gList.iterator({
sort: "guid",
});
for (let i = 0; i < gItems.length; i++) {
items = yield iter.items(1);
checkItems(items, gItems.slice(i, i + 1));
}
items = yield iter.items(100);
checkItems(items, []);
// match on a scalar property
iter = gList.iterator({
sort: "guid",
guid: gItems[0].guid,
});
items = yield iter.items(gItems.length);
checkItems(items, [gItems[0]]);
// match on an array
iter = gList.iterator({
sort: "guid",
guid: gItems.map(i => i.guid),
});
items = yield iter.items(gItems.length);
checkItems(items, gItems);
// match on AND'ed properties
iter = gList.iterator({
sort: "guid",
guid: gItems.map(i => i.guid),
title: gItems[0].title,
});
items = yield iter.items(gItems.length);
checkItems(items, [gItems[0]]);
// match on OR'ed properties
iter = gList.iterator({
sort: "guid",
guid: gItems[1].guid,
}, {
guid: gItems[0].guid,
});
items = yield iter.items(gItems.length);
checkItems(items, [gItems[0], gItems[1]]);
// match on AND'ed and OR'ed properties
iter = gList.iterator({
sort: "guid",
guid: gItems.map(i => i.guid),
title: gItems[1].title,
}, {
guid: gItems[0].guid,
});
items = yield iter.items(gItems.length);
checkItems(items, [gItems[0], gItems[1]]);
});
add_task(function* iterator_forEach_promise() {
// promises resolved immediately
let items = [];
let iter = gList.iterator({
sort: "guid",
});
yield iter.forEach(item => {
items.push(item);
return Promise.resolve();
});
checkItems(items, gItems);
// promises resolved after a delay
// See forEachItem_promises above for comments on this part.
items = [];
let i = 0;
let promises = [];
iter = gList.iterator({
sort: "guid",
});
yield iter.forEach(item => {
items.push(item);
if (i > 0) {
Assert.equal(promises[i - 1], null);
}
let this_i = i++;
let promise = new Promise(resolve => {
setTimeout(() => {
promises[this_i] = null;
resolve();
}, 0);
});
promises.push(promise);
return promise;
});
checkItems(items, gItems);
});
add_task(function* updateItem() {
// get an item
let items = [];
yield gList.forEachItem(i => items.push(i), {
guid: gItems[0].guid,
});
Assert.equal(items.length, 1);
let item = {
_properties: items[0]._properties,
list: items[0].list,
};
// update its title
let newTitle = "updateItem new title";
Assert.notEqual(item.title, newTitle);
item._properties.title = newTitle;
yield gList.updateItem(item);
// get the item again
items = [];
yield gList.forEachItem(i => items.push(i), {
guid: gItems[0].guid,
});
Assert.equal(items.length, 1);
item = items[0];
Assert.equal(item.title, newTitle);
});
add_task(function* item_setProperties() {
// get an item
let iter = gList.iterator({
sort: "guid",
});
let item = (yield iter.items(1))[0];
Assert.ok(item);
// item.setProperties(commit=false). After fetching the item again, its title
// should be the old title.
let oldTitle = item.title;
let newTitle = "item_setProperties title 1";
Assert.notEqual(oldTitle, newTitle);
item.setProperties({ title: newTitle }, false);
Assert.equal(item.title, newTitle);
iter = gList.iterator({
sort: "guid",
});
let sameItem = (yield iter.items(1))[0];
Assert.ok(item === sameItem);
Assert.equal(sameItem.title, oldTitle);
// item.setProperties(commit=true). After fetching the item again, its title
// should be the new title.
newTitle = "item_setProperties title 2";
item.setProperties({ title: newTitle }, true);
Assert.equal(item.title, newTitle);
iter = gList.iterator({
sort: "guid",
});
sameItem = (yield iter.items(1))[0];
Assert.ok(item === sameItem);
Assert.equal(sameItem.title, newTitle);
// Set item.title directly. After fetching the item again, its title should
// be the new title.
newTitle = "item_setProperties title 3";
item.title = newTitle;
Assert.equal(item.title, newTitle);
iter = gList.iterator({
sort: "guid",
});
sameItem = (yield iter.items(1))[0];
Assert.ok(item === sameItem);
Assert.equal(sameItem.title, newTitle);
});
add_task(function* listeners() {
// add an item
let resolve;
let listenerPromise = new Promise(r => resolve = r);
let listener = {
onItemAdded: resolve,
};
gList.addListener(listener);
let item = kindOfClone(gItems[0]);
let items = yield Promise.all([listenerPromise, gList.addItem(item)]);
Assert.ok(items[0]);
Assert.ok(items[0] === items[1]);
gList.removeListener(listener);
// update an item
listenerPromise = new Promise(r => resolve = r);
listener = {
onItemUpdated: resolve,
};
gList.addListener(listener);
items[0].title = "listeners new title";
let listenerItem = yield listenerPromise;
Assert.ok(listenerItem);
Assert.ok(listenerItem === items[0]);
gList.removeListener(listener);
// delete an item
listenerPromise = new Promise(r => resolve = r);
listener = {
onItemDeleted: resolve,
};
gList.addListener(listener);
items[0].delete();
listenerItem = yield listenerPromise;
Assert.ok(listenerItem);
Assert.ok(listenerItem === items[0]);
gList.removeListener(listener);
});
// This test deletes items so it should probably run last.
add_task(function* deleteItem() {
// delete first item with item.delete()
let iter = gList.iterator({
sort: "guid",
});
let item = (yield iter.items(1))[0];
Assert.ok(item);
item.delete();
gItems[0].list = null;
Assert.equal((yield gList.count()), gItems.length - 1);
let items = [];
yield gList.forEachItem(i => items.push(i), {
sort: "guid",
});
checkItems(items, gItems.slice(1));
// delete second item with list.deleteItem()
yield gList.deleteItem(gItems[1]);
gItems[1].list = null;
Assert.equal((yield gList.count()), gItems.length - 2);
items = [];
yield gList.forEachItem(i => items.push(i), {
sort: "guid",
});
checkItems(items, gItems.slice(2));
// delete third item with list.deleteItem()
yield gList.deleteItem(gItems[2]);
gItems[2].list = null;
Assert.equal((yield gList.count()), gItems.length - 3);
items = [];
yield gList.forEachItem(i => items.push(i), {
sort: "guid",
});
checkItems(items, gItems.slice(3));
});
function checkItems(actualItems, expectedItems) {
Assert.equal(actualItems.length, expectedItems.length);
for (let i = 0; i < expectedItems.length; i++) {
for (let prop in expectedItems[i]) {
if (prop != "list") {
Assert.ok(prop in actualItems[i]._properties, prop);
Assert.equal(actualItems[i]._properties[prop], expectedItems[i][prop]);
}
}
Assert.equal(actualItems[i].list, expectedItems[i].list);
}
}
function checkError(err) {
Assert.ok(err);
Assert.ok(err instanceof Cu.getGlobalForObject(Sqlite).Error);
}
function kindOfClone(item) {
let newItem = {};
for (let prop in item) {
newItem[prop] = item[prop];
if (typeof(newItem[prop]) == "string") {
newItem[prop] += " -- make this string different";
}
}
return newItem;
}
function hash(str) {
let hasher = Cc["@mozilla.org/security/hash;1"].
createInstance(Ci.nsICryptoHash);
hasher.init(Ci.nsICryptoHash.MD5);
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
stream.data = str;
hasher.updateFromStream(stream, -1);
let binaryStr = hasher.finish(false);
let hexStr =
[("0" + binaryStr.charCodeAt(i).toString(16)).slice(-2) for (i in hash)].
join("");
return hexStr;
}

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

@ -0,0 +1,314 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/readinglist/SQLiteStore.jsm");
Cu.import("resource://gre/modules/Sqlite.jsm");
var gStore;
var gItems;
function run_test() {
run_next_test();
}
add_task(function* prepare() {
let basename = "reading-list-test.sqlite";
let dbFile = do_get_profile();
dbFile.append(basename);
function removeDB() {
if (dbFile.exists()) {
dbFile.remove(true);
}
}
removeDB();
do_register_cleanup(removeDB);
gStore = new SQLiteStore(dbFile.path);
gItems = [];
for (let i = 0; i < 3; i++) {
gItems.push({
guid: `guid${i}`,
url: `http://example.com/${i}`,
resolvedURL: `http://example.com/resolved/${i}`,
title: `title ${i}`,
excerpt: `excerpt ${i}`,
unread: true,
addedOn: i,
});
}
for (let item of gItems) {
yield gStore.addItem(item);
}
});
add_task(function* constraints() {
// add an item again
let err = null;
try {
yield gStore.addItem(gItems[0]);
}
catch (e) {
err = e;
}
checkError(err, "UNIQUE constraint failed");
// add a new item with an existing guid
function kindOfClone(item) {
let newItem = {};
for (let prop in item) {
newItem[prop] = item[prop];
if (typeof(newItem[prop]) == "string") {
newItem[prop] += " -- make this string different";
}
}
return newItem;
}
let item = kindOfClone(gItems[0]);
item.guid = gItems[0].guid;
err = null;
try {
yield gStore.addItem(item);
}
catch (e) {
err = e;
}
checkError(err, "UNIQUE constraint failed: items.guid");
// add a new item with an existing url
item = kindOfClone(gItems[0]);
item.url = gItems[0].url;
err = null;
try {
yield gStore.addItem(item);
}
catch (e) {
err = e;
}
checkError(err, "UNIQUE constraint failed: items.url");
// update an item with an existing url
item.guid = gItems[1].guid;
err = null;
try {
yield gStore.updateItem(item);
}
catch (e) {
err = e;
}
// The failure actually happens on items.guid, not items.url, because the item
// is first looked up by url, and then its other properties are updated on the
// resulting row.
checkError(err, "UNIQUE constraint failed: items.guid");
// add a new item with an existing resolvedURL
item = kindOfClone(gItems[0]);
item.resolvedURL = gItems[0].resolvedURL;
err = null;
try {
yield gStore.addItem(item);
}
catch (e) {
err = e;
}
checkError(err, "UNIQUE constraint failed: items.resolvedURL");
// update an item with an existing resolvedURL
item.url = gItems[1].url;
err = null;
try {
yield gStore.updateItem(item);
}
catch (e) {
err = e;
}
checkError(err, "UNIQUE constraint failed: items.resolvedURL");
// add a new item with no guid, which is allowed
item = kindOfClone(gItems[0]);
delete item.guid;
err = null;
try {
yield gStore.addItem(item);
}
catch (e) {
err = e;
}
Assert.ok(!err, err ? err.message : undefined);
let url1 = item.url;
// add a second new item with no guid, which is allowed
item = kindOfClone(gItems[1]);
delete item.guid;
err = null;
try {
yield gStore.addItem(item);
}
catch (e) {
err = e;
}
Assert.ok(!err, err ? err.message : undefined);
let url2 = item.url;
// Delete both items since other tests assume the store contains only gItems.
yield gStore.deleteItemByURL(url1);
yield gStore.deleteItemByURL(url2);
let items = [];
yield gStore.forEachItem(i => items.push(i), { url: [url1, url2] });
Assert.equal(items.length, 0);
// add a new item with no url, which is not allowed
item = kindOfClone(gItems[0]);
delete item.url;
err = null;
try {
yield gStore.addItem(item);
}
catch (e) {
err = e;
}
checkError(err, "NOT NULL constraint failed: items.url");
});
add_task(function* count() {
let count = yield gStore.count();
Assert.equal(count, gItems.length);
count = yield gStore.count({
guid: gItems[0].guid,
});
Assert.equal(count, 1);
});
add_task(function* forEachItem() {
// all items
let items = [];
yield gStore.forEachItem(item => items.push(item), {
sort: "guid",
});
checkItems(items, gItems);
// first item
items = [];
yield gStore.forEachItem(item => items.push(item), {
limit: 1,
sort: "guid",
});
checkItems(items, gItems.slice(0, 1));
// last item
items = [];
yield gStore.forEachItem(item => items.push(item), {
limit: 1,
sort: "guid",
descending: true,
});
checkItems(items, gItems.slice(gItems.length - 1, gItems.length));
// match on a scalar property
items = [];
yield gStore.forEachItem(item => items.push(item), {
guid: gItems[0].guid,
});
checkItems(items, gItems.slice(0, 1));
// match on an array
items = [];
yield gStore.forEachItem(item => items.push(item), {
guid: gItems.map(i => i.guid),
sort: "guid",
});
checkItems(items, gItems);
// match on AND'ed properties
items = [];
yield gStore.forEachItem(item => items.push(item), {
guid: gItems.map(i => i.guid),
title: gItems[0].title,
sort: "guid",
});
checkItems(items, [gItems[0]]);
// match on OR'ed properties
items = [];
yield gStore.forEachItem(item => items.push(item), {
guid: gItems[1].guid,
sort: "guid",
}, {
guid: gItems[0].guid,
});
checkItems(items, [gItems[0], gItems[1]]);
// match on AND'ed and OR'ed properties
items = [];
yield gStore.forEachItem(item => items.push(item), {
guid: gItems.map(i => i.guid),
title: gItems[1].title,
sort: "guid",
}, {
guid: gItems[0].guid,
});
checkItems(items, [gItems[0], gItems[1]]);
});
add_task(function* updateItem() {
let newTitle = "a new title";
gItems[0].title = newTitle;
yield gStore.updateItem(gItems[0]);
let item;
yield gStore.forEachItem(i => item = i, {
guid: gItems[0].guid,
});
Assert.ok(item);
Assert.equal(item.title, gItems[0].title);
});
// This test deletes items so it should probably run last.
add_task(function* deleteItemByURL() {
// delete first item
yield gStore.deleteItemByURL(gItems[0].url);
Assert.equal((yield gStore.count()), gItems.length - 1);
let items = [];
yield gStore.forEachItem(i => items.push(i), {
sort: "guid",
});
checkItems(items, gItems.slice(1));
// delete second item
yield gStore.deleteItemByURL(gItems[1].url);
Assert.equal((yield gStore.count()), gItems.length - 2);
items = [];
yield gStore.forEachItem(i => items.push(i), {
sort: "guid",
});
checkItems(items, gItems.slice(2));
// delete third item
yield gStore.deleteItemByURL(gItems[2].url);
Assert.equal((yield gStore.count()), gItems.length - 3);
items = [];
yield gStore.forEachItem(i => items.push(i), {
sort: "guid",
});
checkItems(items, gItems.slice(3));
});
function checkItems(actualItems, expectedItems) {
Assert.equal(actualItems.length, expectedItems.length);
for (let i = 0; i < expectedItems.length; i++) {
for (let prop in expectedItems[i]) {
Assert.ok(prop in actualItems[i], prop);
Assert.equal(actualItems[i][prop], expectedItems[i][prop]);
}
}
}
function checkError(err, expectedMsgSubstring) {
Assert.ok(err);
Assert.ok(err instanceof Cu.getGlobalForObject(Sqlite).Error);
Assert.ok(err.message);
Assert.ok(err.message.indexOf(expectedMsgSubstring) >= 0, err.message);
}

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

@ -2,4 +2,6 @@
head = head.js
firefox-appdir = browser
;[test_ReadingList.js]
[test_scheduler.js]
;[test_SQLiteStore.js]

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

@ -206,7 +206,9 @@ let DebuggerController = {
if (target.isAddon) {
yield this._startAddonDebugging(actor);
} else if (target.chrome) {
} else if (!target.isTabActor) {
// Some actors like AddonActor or RootActor for chrome debugging
// do not support attach/detach and can be used directly
yield this._startChromeDebugging(chromeDebugger);
} else {
yield this._startDebuggingTab();

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

@ -23,6 +23,7 @@ function test() {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
@ -42,26 +43,26 @@ function test() {
}
function testChromeActor() {
gClient.listTabs(aResponse => {
ok(aResponse.chromeDebugger.contains("chromeDebugger"),
"Chrome debugger actor should identify itself accordingly.");
gClient.attachProcess().then(aResponse => {
gClient.addListener("newGlobal", onNewGlobal);
gClient.addListener("newSource", onNewSource);
gClient.attachThread(aResponse.chromeDebugger, (aResponse, aThreadClient) => {
gThreadClient = aThreadClient;
let actor = aResponse.form.actor;
gClient.attachTab(actor, (response, tabClient) => {
tabClient.attachThread(null, (aResponse, aThreadClient) => {
gThreadClient = aThreadClient;
if (aResponse.error) {
ok(false, "Couldn't attach to the chrome debugger.");
gAttached.reject();
} else {
ok(true, "Attached to the chrome debugger.");
gAttached.resolve();
if (aResponse.error) {
ok(false, "Couldn't attach to the chrome debugger.");
gAttached.reject();
} else {
ok(true, "Attached to the chrome debugger.");
gAttached.resolve();
// Ensure that a new chrome global will be created.
gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
}
// Ensure that a new chrome global will be created.
gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
}
});
});
});
}

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

@ -563,6 +563,7 @@ AddonDebugger.prototype = {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
this.frame = document.createElement("iframe");
this.frame.setAttribute("height", 400);
@ -581,7 +582,8 @@ AddonDebugger.prototype = {
let targetOptions = {
form: addonActor,
client: this.client,
chrome: true
chrome: true,
isTabActor: false
};
let toolboxOptions = {

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

@ -110,6 +110,7 @@ devtoolsCommandlineHandler.prototype = {
let debuggerServer = serverLoader.DebuggerServer;
debuggerServer.init();
debuggerServer.addBrowserActors();
debuggerServer.allowChromeProcess = true;
let listener = debuggerServer.createListener();
listener.portOrPath = portOrPath;

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

@ -116,29 +116,31 @@ BrowserToolboxProcess.prototype = {
* Initializes the debugger server.
*/
_initServer: function() {
if (this.debuggerServer) {
dumpn("The chrome toolbox server is already running.");
return;
}
dumpn("Initializing the chrome toolbox server.");
if (!this.loader) {
// Create a separate loader instance, so that we can be sure to receive a
// separate instance of the DebuggingServer from the rest of the devtools.
// This allows us to safely use the tools against even the actors and
// DebuggingServer itself, especially since we can mark this loader as
// invisible to the debugger (unlike the usual loader settings).
this.loader = new DevToolsLoader();
this.loader.invisibleToDebugger = true;
this.loader.main("devtools/server/main");
this.debuggerServer = this.loader.DebuggerServer;
dumpn("Created a separate loader instance for the DebuggerServer.");
// Create a separate loader instance, so that we can be sure to receive a
// separate instance of the DebuggingServer from the rest of the devtools.
// This allows us to safely use the tools against even the actors and
// DebuggingServer itself, especially since we can mark this loader as
// invisible to the debugger (unlike the usual loader settings).
this.loader = new DevToolsLoader();
this.loader.invisibleToDebugger = true;
this.loader.main("devtools/server/main");
this.debuggerServer = this.loader.DebuggerServer;
dumpn("Created a separate loader instance for the DebuggerServer.");
// Forward interesting events.
this.debuggerServer.on("connectionchange", this.emit.bind(this));
}
// Forward interesting events.
this.debuggerServer.on("connectionchange", this.emit.bind(this));
if (!this.debuggerServer.initialized) {
this.debuggerServer.init();
this.debuggerServer.addBrowserActors();
dumpn("initialized and added the browser actors for the DebuggerServer.");
}
this.debuggerServer.init();
this.debuggerServer.addBrowserActors();
this.debuggerServer.allowChromeProcess = true;
dumpn("initialized and added the browser actors for the DebuggerServer.");
let chromeDebuggingPort =
Services.prefs.getIntPref("devtools.debugger.chrome-debugging-port");

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

@ -129,11 +129,19 @@ let onConnectionReady = Task.async(function*(aType, aTraits) {
let gParent = document.getElementById("globalActors");
// Build the Remote Process button
if (Object.keys(globals).length > 1) {
// If Fx<37, tab actors were used to be exposed on RootActor
// but in Fx>=37, chrome is debuggable via attachProcess() and ChromeActor
if (globals.consoleActor || gClient.mainRoot.traits.allowChromeProcess) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(globals, true);
if (gClient.mainRoot.traits.allowChromeProcess) {
gClient.attachProcess()
.then(aResponse => {
openToolbox(aResponse.form, true);
});
} else if (globals.consoleActor) {
openToolbox(globals, true, "webconsole", false);
}
}
a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
a.className = "remote-process";
@ -162,7 +170,7 @@ let onConnectionReady = Task.async(function*(aType, aTraits) {
function buildAddonLink(addon, parent) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(addon, true, "jsdebugger");
openToolbox(addon, true, "jsdebugger", false);
}
a.textContent = addon.name;
@ -221,11 +229,12 @@ function handleConnectionTimeout() {
* The user clicked on one of the buttons.
* Opens the toolbox.
*/
function openToolbox(form, chrome=false, tool="webconsole") {
function openToolbox(form, chrome=false, tool="webconsole", isTabActor) {
let options = {
form: form,
client: gClient,
chrome: chrome
chrome: chrome,
isTabActor: isTabActor
};
devtools.TargetFactory.forRemoteTab(options).then((target) => {
let hostType = devtools.Toolbox.HostType.WINDOW;
@ -233,7 +242,7 @@ function openToolbox(form, chrome=false, tool="webconsole") {
toolbox.once("destroyed", function() {
gClient.close();
});
});
}, console.error.bind(console));
window.close();
});
}, console.error.bind(console));
}

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

@ -697,6 +697,7 @@ let gDevToolsBrowser = {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let transport = DebuggerServer.connectPipe();
let client = new DebuggerClient(transport);
@ -718,7 +719,8 @@ let gDevToolsBrowser = {
let options = {
form: response.form,
client: client,
chrome: true
chrome: true,
isTabActor: false
};
return devtools.TargetFactory.forRemoteTab(options);
})

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

@ -175,6 +175,8 @@ function TabTarget(tab) {
this._client = tab.client;
this._chrome = tab.chrome;
}
// Default isTabActor to true if not explicitely specified
this._isTabActor = typeof(tab.isTabActor) == "boolean" ? tab.isTabActor : true;
}
TabTarget.prototype = {
@ -315,10 +317,21 @@ TabTarget.prototype = {
return this._client;
},
// Tells us if we are debugging content document
// or if we are debugging chrome stuff.
// Allows to controls which features are available against
// a chrome or a content document.
get chrome() {
return this._chrome;
},
// Tells us if the related actor implements TabActor interface
// and requires to call `attach` request before being used
// and `detach` during cleanup
get isTabActor() {
return this._isTabActor;
},
get window() {
// XXX - this is a footgun for e10s - there .contentWindow will be null,
// and even though .contentWindowAsCPOW *might* work, it will not work
@ -436,12 +449,13 @@ TabTarget.prototype = {
attachTab();
});
});
} else if (!this.chrome) {
} else if (this.isTabActor) {
// In the remote debugging case, the protocol connection will have been
// already initialized in the connection screen code.
attachTab();
} else {
// Remote chrome debugging doesn't need anything at this point.
// AddonActor and chrome debugging on RootActor doesn't inherits from TabActor and
// doesn't need to be attached.
this._remote.resolve(null);
}

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

@ -1,10 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let { DebuggerServer } =
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let { DebuggerClient } =
Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
let { devtools } =
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
@ -12,28 +8,19 @@ let { devtools } =
function test() {
waitForExplicitFinish();
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
getChromeActors((client, response) => {
let options = {
form: response,
client: client,
chrome: true
};
var client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() => {
client.listTabs(response => {
let options = {
form: response,
client: client,
chrome: true
};
devtools.TargetFactory.forRemoteTab(options).then(target => {
target.on("close", () => {
ok(true, "Target was closed");
DebuggerServer.destroy();
finish();
});
client.close();
devtools.TargetFactory.forRemoteTab(options).then(target => {
target.on("close", () => {
ok(true, "Target was closed");
finish();
});
client.close();
});
});
}

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

@ -4,10 +4,6 @@
// Test support methods on Target, such as `hasActor`, `getActorDescription`,
// `actorHasMethod` and `getTrait`.
let { DebuggerServer } =
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let { DebuggerClient } =
Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
let { devtools } =
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let { Task } =
@ -60,29 +56,20 @@ function* testTarget (client, target) {
function test() {
waitForExplicitFinish();
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
getChromeActors((client, response) => {
let options = {
form: response,
client: client,
chrome: true
};
var client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() => {
client.listTabs(response => {
let options = {
form: response,
client: client,
chrome: true
};
devtools.TargetFactory.forRemoteTab(options).then(Task.async(testTarget).bind(null, client));
});
devtools.TargetFactory.forRemoteTab(options).then(Task.async(testTarget).bind(null, client));
});
}
function close (target, client) {
target.on("close", () => {
ok(true, "Target was closed");
DebuggerServer.destroy();
finish();
});
client.close();

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

@ -152,3 +152,26 @@ function toggleAllTools(state) {
}
}
}
function getChromeActors(callback)
{
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() => {
client.attachProcess().then(response => {
callback(client, response.form);
});
});
SimpleTest.registerCleanupFunction(() => {
DebuggerServer.destroy();
});
}

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

@ -38,10 +38,12 @@ let connect = Task.async(function*() {
if (addonID) {
gClient.listAddons(({addons}) => {
let addonActor = addons.filter(addon => addon.id === addonID).pop();
openToolbox(addonActor);
openToolbox({ form: addonActor, chrome: true, isTabActor: false });
});
} else {
gClient.listTabs(openToolbox);
gClient.attachProcess().then(aResponse => {
openToolbox({ form: aResponse.form, chrome: true });
});
}
});
});
@ -52,6 +54,7 @@ function setPrefDefaults() {
Services.prefs.setBoolPref("devtools.profiler.ui.show-platform-data", true);
Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", false);
Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
}
window.addEventListener("load", function() {
@ -65,11 +68,12 @@ function onCloseCommand(event) {
window.close();
}
function openToolbox(form) {
function openToolbox({ form, chrome, isTabActor }) {
let options = {
form: form,
client: gClient,
chrome: true
chrome: chrome,
isTabActor: isTabActor
};
devtools.TargetFactory.forRemoteTab(options).then(target => {
let frame = document.getElementById("toolbox-iframe");

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

@ -211,7 +211,9 @@ let NetMonitorController = {
let target = this._target;
let { client, form } = target;
if (target.chrome) {
// Some actors like AddonActor or RootActor for chrome debugging
// do not support attach/detach and can be used directly
if (!target.isTabActor) {
this._startChromeMonitoring(client, form.consoleActor, deferred.resolve);
} else {
this._startMonitoringTab(client, form, deferred.resolve);

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

@ -121,14 +121,9 @@ PerformanceActorsConnection.prototype = {
* Initializes a connection to the profiler actor.
*/
_connectProfilerActor: Task.async(function*() {
// Chrome debugging targets have already obtained a reference
// to the profiler actor.
if (this._target.chrome) {
this._profiler = this._target.form.profilerActor;
}
// When we are debugging content processes, we already have the tab
// specific one. Use it immediately.
else if (this._target.form && this._target.form.profilerActor) {
// Chrome and content process targets already have obtained a reference
// to the profiler tab actor. Use it immediately.
if (this._target.form && this._target.form.profilerActor) {
this._profiler = this._target.form.profilerActor;
}
// Check if we already have a grip to the `listTabs` response object

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

@ -40,6 +40,8 @@ function spawnTest () {
yield notified;
let firstInterval = OverviewView.getTimeInterval();
info("First interval start time: " + firstInterval.startTime);
info("First interval end time: " + firstInterval.endTime);
ok(firstInterval.startTime - 10 < Number.EPSILON,
"The interval's start time was properly set.");
ok(firstInterval.endTime - 20 < Number.EPSILON,
@ -56,6 +58,8 @@ function spawnTest () {
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, fail);
let secondInterval = OverviewView.getTimeInterval();
info("Second interval start time: " + secondInterval.startTime);
info("Second interval end time: " + secondInterval.endTime);
is(Math.round(secondInterval.startTime), 30,
"The interval's start time was properly set again.");
is(Math.round(secondInterval.endTime), 40,

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

@ -83,14 +83,9 @@ ProfilerConnection.prototype = {
// Local debugging needs to make the target remote.
yield this._target.makeRemote();
// Chrome debugging targets have already obtained a reference
// to the profiler actor.
if (this._target.chrome) {
this._profiler = this._target.form.profilerActor;
}
// Or when we are debugging content processes, we already have the tab
// specific one. Use it immediately.
else if (this._target.form && this._target.form.profilerActor) {
// Chrome and content process targets already have obtained a reference
// to the profiler tab actor. Use it immediately.
if (this._target.form && this._target.form.profilerActor) {
this._profiler = this._target.form.profilerActor;
yield this._registerEventNotifications();
}
@ -100,8 +95,9 @@ ProfilerConnection.prototype = {
this._profiler = this._target.root.profilerActor;
yield this._registerEventNotifications();
}
// Otherwise, call `listTabs`.
else {
// Otherwise, call `listTabs`, but ensure not trying to fetch tab actors
// for AddonTarget that are chrome, but do not expose profile at all.
else if (!this._target.chrome) {
this._profiler = (yield listTabs(this._client)).profilerActor;
yield this._registerEventNotifications();
}

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

@ -2152,16 +2152,17 @@ ScratchpadWindow.prototype = Heritage.extend(ScratchpadTab.prototype, {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() => {
client.listTabs(aResponse => {
client.attachProcess().then(aResponse => {
if (aResponse.error) {
reportError("listTabs", aResponse);
deferred.reject(aResponse);
}
else {
deferred.resolve({ form: aResponse, client: client });
deferred.resolve({ form: aResponse.form, client: client });
}
});
});

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

@ -224,7 +224,7 @@ FrameNode.prototype = {
if (uri) {
functionName = this.location.substring(0, firstParenIndex - 1);
fileName = (uri.fileName + (uri.ref ? "#" + uri.ref : "")) || "/";
hostName = uri.host;
hostName = url.indexOf("jar:") == 0 ? "" : uri.host;
} else {
functionName = this.location;
url = null;

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

@ -187,22 +187,16 @@ HUD_SERVICE.prototype =
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() =>
client.listTabs((aResponse) => {
// Add Global Process debugging...
let globals = Cu.cloneInto(aResponse, {});
delete globals.tabs;
delete globals.selected;
// ...only if there are appropriate actors (a 'from' property will
// always be there).
if (Object.keys(globals).length > 1) {
deferred.resolve({ form: globals, client: client, chrome: true });
} else {
deferred.reject("Global console not found!");
}
}));
client.connect(() => {
client.attachProcess().then(aResponse => {
// Set chrome:false in order to attach to the target
// (i.e. send an `attach` request to the chrome actor)
deferred.resolve({ form: aResponse.form, client: client, chrome: false });
}, deferred.reject);
});
return deferred.promise;
}
@ -210,13 +204,7 @@ HUD_SERVICE.prototype =
let target;
function getTarget(aConnection)
{
let options = {
form: aConnection.form,
client: aConnection.client,
chrome: true,
};
return devtools.TargetFactory.forRemoteTab(options);
return devtools.TargetFactory.forRemoteTab(aConnection);
}
function openWindow(aTarget)
@ -241,12 +229,12 @@ HUD_SERVICE.prototype =
}
connect().then(getTarget).then(openWindow).then((aWindow) => {
this.openBrowserConsole(target, aWindow, aWindow)
return this.openBrowserConsole(target, aWindow, aWindow)
.then((aBrowserConsole) => {
this._browserConsoleDefer.resolve(aBrowserConsole);
this._browserConsoleDefer = null;
})
}, console.error);
}, console.error.bind(console));
return this._browserConsoleDefer.promise;
},
@ -640,10 +628,13 @@ WebConsole.prototype = {
this._destroyer = promise.defer();
let popupset = this.mainPopupSet;
let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
for (let panel of panels) {
panel.hidePopup();
// The document may already be removed
if (this.chromeUtilsWindow && this.mainPopupSet) {
let popupset = this.mainPopupSet;
let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
for (let panel of panels) {
panel.hidePopup();
}
}
let onDestroy = function WC_onDestroyUI() {

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

@ -440,7 +440,11 @@ WebConsoleFrame.prototype = {
* @type boolean
*/
get persistLog() {
return Services.prefs.getBoolPref(PREF_PERSISTLOG);
// For the browser console, we receive tab navigation
// when the original top level window we attached to is closed,
// but we don't want to reset console history and just switch to
// the next available window.
return this.owner._browserConsole || Services.prefs.getBoolPref(PREF_PERSISTLOG);
},
/**
@ -3426,7 +3430,7 @@ JSTerm.prototype = {
let selectedNodeActor = null;
let inspectorSelection = this.hud.owner.getInspectorSelection();
if (inspectorSelection) {
if (inspectorSelection && inspectorSelection.nodeFront) {
selectedNodeActor = inspectorSelection.nodeFront.actorID;
}
@ -5095,7 +5099,7 @@ WebConsoleConnectionProxy.prototype = {
this.target.on("navigate", this._onTabNavigated);
this._consoleActor = this.target.form.consoleActor;
if (!this.target.chrome) {
if (this.target.isTabActor) {
let tab = this.target.form;
this.owner.onLocationChange(tab.url, tab.title);
}

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

@ -927,8 +927,10 @@ let UI = {
// We can't know for sure which one was used here, so reset the
// |toolboxPromise| since someone must be destroying it to reach here,
// and call our own close method.
this.toolboxPromise = null;
this._closeToolboxUI();
if (this.toolboxIframe && this.toolboxIframe.uid == json.uid) {
this.toolboxPromise = null;
this._closeToolboxUI();
}
break;
}
} catch(e) { console.error(e); }
@ -941,9 +943,9 @@ let UI = {
this.toolboxPromise = null;
return toolboxPromise.then(toolbox => {
return toolbox.destroy();
}).catch(console.error)
}).then(null, console.error)
.then(() => this._closeToolboxUI())
.catch(console.error);
.then(null, console.error);
}
return promise.resolve();
},
@ -966,9 +968,13 @@ let UI = {
let iframe = document.createElement("iframe");
iframe.id = "toolbox";
// Compute a uid on the iframe in order to identify toolbox iframe
// when receiving toolbox-close event
iframe.uid = new Date().getTime();
document.querySelector("notificationbox").insertBefore(iframe, splitter.nextSibling);
let host = devtools.Toolbox.HostType.CUSTOM;
let options = { customIframe: iframe, zoom: false };
let options = { customIframe: iframe, zoom: false, uid: iframe.uid };
this.toolboxIframe = iframe;
let height = Services.prefs.getIntPref("devtools.toolbox.footer.height");

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

@ -217,11 +217,25 @@ let AppManager = exports.AppManager = {
getTarget: function() {
if (this.selectedProject.type == "mainProcess") {
return devtools.TargetFactory.forRemoteTab({
form: this._listTabsResponse,
client: this.connection.client,
chrome: true
});
// Fx >=37 exposes a ChromeActor to debug the main process
if (this.connection.client.mainRoot.traits.allowChromeProcess) {
return this.connection.client.attachProcess()
.then(aResponse => {
return devtools.TargetFactory.forRemoteTab({
form: aResponse.form,
client: this.connection.client,
chrome: true
});
});
} else {
// Fx <37 exposes tab actors on the root actor
return devtools.TargetFactory.forRemoteTab({
form: this._listTabsResponse,
client: this.connection.client,
chrome: true,
isTabActor: false
});
}
}
if (this.selectedProject.type == "tab") {
@ -414,8 +428,12 @@ let AppManager = exports.AppManager = {
},
isMainProcessDebuggable: function() {
return this._listTabsResponse &&
this._listTabsResponse.consoleActor;
// Fx <37 exposes chrome tab actors on RootActor
// Fx >=37 exposes a dedicated actor via attachProcess request
return this.connection.client &&
this.connection.client.mainRoot.traits.allowChromeProcess ||
(this._listTabsResponse &&
this._listTabsResponse.consoleActor);
},
get deviceFront() {

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

@ -575,6 +575,7 @@ let gLocalRuntime = {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
connection.host = null; // Force Pipe transport
connection.port = null;
connection.connect();

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

@ -24,6 +24,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
"resource://gre/modules/SocialService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
"resource://gre/modules/PageMetadata.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
@ -31,9 +33,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "unescapeService",
"@mozilla.org/feed-unescapehtml;1",
"nsIScriptableUnescapeHTML");
function promiseSetAnnotation(aURI, providerList) {
let deferred = Promise.defer();
@ -528,180 +527,4 @@ this.OpenGraphBuilder = {
endpointURL = endpointURL + "?" + str.join("&");
return endpointURL;
},
getData: function(aDocument, target) {
let res = {
url: this._validateURL(aDocument, aDocument.documentURI),
title: aDocument.title,
previews: []
};
this._getMetaData(aDocument, res);
this._getLinkData(aDocument, res);
this._getPageData(aDocument, res);
res.microdata = this.getMicrodata(aDocument, target);
return res;
},
getMicrodata: function (aDocument, target) {
return getMicrodata(aDocument, target);
},
_getMetaData: function(aDocument, o) {
// query for standardized meta data
let els = aDocument.querySelectorAll("head > meta[property], head > meta[name]");
if (els.length < 1)
return;
let url;
for (let el of els) {
let value = el.getAttribute("content")
if (!value)
continue;
value = unescapeService.unescape(value.trim());
let key = el.getAttribute("property") || el.getAttribute("name");
if (!key)
continue;
// There are a wide array of possible meta tags, expressing articles,
// products, etc. so all meta tags are passed through but we touch up the
// most common attributes.
o[key] = value;
switch (key) {
case "title":
case "og:title":
o.title = value;
break;
case "description":
case "og:description":
o.description = value;
break;
case "og:site_name":
o.siteName = value;
break;
case "medium":
case "og:type":
o.medium = value;
break;
case "og:video":
url = this._validateURL(aDocument, value);
if (url)
o.source = url;
break;
case "og:url":
url = this._validateURL(aDocument, value);
if (url)
o.url = url;
break;
case "og:image":
url = this._validateURL(aDocument, value);
if (url)
o.previews.push(url);
break;
}
}
},
_getLinkData: function(aDocument, o) {
let els = aDocument.querySelectorAll("head > link[rel], head > link[id]");
for (let el of els) {
let url = el.getAttribute("href");
if (!url)
continue;
url = this._validateURL(aDocument, unescapeService.unescape(url.trim()));
switch (el.getAttribute("rel") || el.getAttribute("id")) {
case "shorturl":
case "shortlink":
o.shortUrl = url;
break;
case "canonicalurl":
case "canonical":
o.url = url;
break;
case "image_src":
o.previews.push(url);
break;
case "alternate":
// expressly for oembed support but we're liberal here and will let
// other alternate links through. oembed defines an href, supplied by
// the site, where you can fetch additional meta data about a page.
// We'll let the client fetch the oembed data themselves, but they
// need the data from this link.
if (!o.alternate)
o.alternate = [];
o.alternate.push({
"type": el.getAttribute("type"),
"href": el.getAttribute("href"),
"title": el.getAttribute("title")
})
}
}
},
// scrape through the page for data we want
_getPageData: function(aDocument, o) {
if (o.previews.length < 1)
o.previews = this._getImageUrls(aDocument);
},
_validateURL: function(aDocument, url) {
let docURI = Services.io.newURI(aDocument.documentURI, null, null);
let uri = Services.io.newURI(docURI.resolve(url), null, null);
if (["http", "https", "ftp", "ftps"].indexOf(uri.scheme) < 0)
return null;
uri.userPass = "";
return uri.spec;
},
_getImageUrls: function(aDocument) {
let l = [];
let els = aDocument.querySelectorAll("img");
for (let el of els) {
let src = el.getAttribute("src");
if (src) {
l.push(this._validateURL(aDocument, unescapeService.unescape(src)));
// we don't want a billion images
if (l.length > 5)
break;
}
}
return l;
}
};
// getMicrodata (and getObject) based on wg algorythm to convert microdata to json
// http://www.whatwg.org/specs/web-apps/current-work/multipage/microdata-2.html#json
function getMicrodata(document, target) {
function _getObject(item) {
let result = {};
if (item.itemType.length)
result.types = [i for (i of item.itemType)];
if (item.itemId)
result.itemId = item.itemid;
if (item.properties.length)
result.properties = {};
for (let elem of item.properties) {
let value;
if (elem.itemScope)
value = _getObject(elem);
else if (elem.itemValue)
value = elem.itemValue;
// handle mis-formatted microdata
else if (elem.hasAttribute("content"))
value = elem.getAttribute("content");
for (let prop of elem.itemProp) {
if (!result.properties[prop])
result.properties[prop] = [];
result.properties[prop].push(value);
}
}
return result;
}
let result = { items: [] };
let elms = target ? [target] : document.getItems();
for (let el of elms) {
if (el.itemScope)
result.items.push(_getObject(el));
}
return result;
}

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

@ -237,6 +237,8 @@ menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
menuitem.panel-subview-footer@menuStateHover@,
.subviewbutton.panel-subview-footer@buttonStateHover@,
.subviewbutton.panel-subview-footer@buttonStateActive@,
.subviewbutton@menuStateHover@ > .menu-accel-container,
.PanelUI-subView .subviewbutton[shortcut]@buttonStateHover@::after,
#BMB_bookmarksPopup .panel-subview-footer@menuStateHover@ > .menu-text {
background-color: Highlight;
color: highlighttext !important;

1
config/external/nss/nss.def поставляемый
Просмотреть файл

@ -525,7 +525,6 @@ SECKEY_ExtractPublicKey
SECKEY_GetPublicKeyType
SECKEY_ImportDERPublicKey
SECKEY_PublicKeyStrength
SECKEY_PublicKeyStrengthInBits
SECKEY_RSAPSSParamsTemplate DATA
SECKEY_SignatureLen
SECMIME_DecryptionAllowed

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

@ -858,7 +858,7 @@ Console::Assert(JSContext* aCx, bool aCondition,
METHOD(Count, "count")
void
Console::__noSuchMethod__()
Console::NoopMethod()
{
// Nothing to do.
}

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

@ -101,7 +101,7 @@ public:
Count(JSContext* aCx, const Sequence<JS::Value>& aData);
void
__noSuchMethod__();
NoopMethod();
private:
enum MethodName

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

@ -723,31 +723,5 @@ DOMException::Create(nsresult aRv)
return inst.forget();
}
bool
DOMException::Sanitize(JSContext* aCx,
JS::MutableHandle<JS::Value> aSanitizedValue)
{
nsRefPtr<DOMException> retval = this;
if (mLocation && !mLocation->CallerSubsumes(aCx)) {
nsString message;
GetMessageMoz(message);
nsString name;
GetName(name);
retval = new dom::DOMException(nsresult(Result()),
NS_ConvertUTF16toUTF8(message),
NS_ConvertUTF16toUTF8(name),
Code());
// Now it's possible that the stack on retval still starts with
// stuff aCx is not supposed to touch; it depends on what's on the
// stack right this second. Walk past all of that.
nsCOMPtr<nsIStackFrame> stack;
nsresult rv = retval->mLocation->GetSanitized(aCx, getter_AddRefs(stack));
NS_ENSURE_SUCCESS(rv, false);
retval->mLocation.swap(stack);
}
return ToJSValue(aCx, retval, aSanitizedValue);
}
} // namespace dom
} // namespace mozilla

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

@ -156,15 +156,6 @@ public:
static already_AddRefed<DOMException>
Create(nsresult aRv);
// Sanitize() is a workaround for the fact that DOMExceptions can leak stack
// information for the first stackframe to callers that should not have access
// to it. To prevent this, we check whether aCx subsumes our first stackframe
// and if not hand out a JS::Value for a clone of ourselves. Otherwise we
// hand out a JS::Value for ourselves.
//
// If the return value is false, an exception was thrown on aCx.
bool Sanitize(JSContext* aCx, JS::MutableHandle<JS::Value> aSanitizedValue);
protected:
virtual ~DOMException() {}

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

@ -797,6 +797,15 @@ Element::ScrollByNoFlush(int32_t aDx, int32_t aDy)
return (before != after);
}
void
Element::MozScrollSnap()
{
nsIScrollableFrame* sf = GetScrollFrame(nullptr, false);
if (sf) {
sf->ScrollSnap();
}
}
static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame)
{
if (!aFrame) {

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

@ -762,6 +762,7 @@ public:
void SetScrollLeft(int32_t aScrollLeft);
int32_t ScrollWidth();
int32_t ScrollHeight();
void MozScrollSnap();
int32_t ClientTop()
{
return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().y);

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

@ -257,11 +257,15 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(CSSFontFaceRule, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager, nsEventTargetSH,
NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager,
nsMessageManagerSH<nsEventTargetSH>,
DOM_DEFAULT_SCRIPTABLE_FLAGS |
nsIXPCScriptable::WANT_ENUMERATE |
nsIXPCScriptable::IS_GLOBAL_OBJECT)
NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentProcessMessageManager, nsDOMGenericSH,
NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentProcessMessageManager,
nsMessageManagerSH<nsDOMGenericSH>,
DOM_DEFAULT_SCRIPTABLE_FLAGS |
nsIXPCScriptable::WANT_ENUMERATE |
nsIXPCScriptable::IS_GLOBAL_OBJECT)
NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageBroadcaster, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
@ -2587,3 +2591,42 @@ nsNonDOMObjectSH::GetFlags(uint32_t *aFlags)
*aFlags = nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::SINGLETON_CLASSINFO;
return NS_OK;
}
// nsContentFrameMessageManagerSH
template<typename Super>
NS_IMETHODIMP
nsMessageManagerSH<Super>::Resolve(nsIXPConnectWrappedNative* wrapper,
JSContext* cx, JSObject* obj_,
jsid id_, bool* resolvedp,
bool* _retval)
{
JS::Rooted<JSObject*> obj(cx, obj_);
JS::Rooted<jsid> id(cx, id_);
*_retval = SystemGlobalResolve(cx, obj, id, resolvedp);
NS_ENSURE_TRUE(*_retval, NS_ERROR_FAILURE);
if (*resolvedp) {
return NS_OK;
}
return Super::Resolve(wrapper, cx, obj, id, resolvedp, _retval);
}
template<typename Super>
NS_IMETHODIMP
nsMessageManagerSH<Super>::Enumerate(nsIXPConnectWrappedNative* wrapper,
JSContext* cx, JSObject* obj_,
bool* _retval)
{
JS::Rooted<JSObject*> obj(cx, obj_);
*_retval = SystemGlobalEnumerate(cx, obj);
NS_ENSURE_TRUE(*_retval, NS_ERROR_FAILURE);
// Don't call up to our superclass, since neither nsDOMGenericSH nor
// nsEventTargetSH have WANT_ENUMERATE.
MOZ_ASSERT(!(this->GetScriptableFlags() & nsIXPCScriptable::WANT_ENUMERATE));
return NS_OK;
}

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

@ -313,4 +313,29 @@ public:
}
};
template<typename Super>
class nsMessageManagerSH : public Super
{
protected:
explicit nsMessageManagerSH(nsDOMClassInfoData* aData)
: Super(aData)
{
}
virtual ~nsMessageManagerSH()
{
}
public:
NS_IMETHOD Resolve(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
JSObject* obj_, jsid id_, bool* resolvedp,
bool* _retval) MOZ_OVERRIDE;
NS_IMETHOD Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
JSObject* obj_, bool* _retval) MOZ_OVERRIDE;
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
{
return new nsMessageManagerSH(aData);
}
};
#endif /* nsDOMClassInfo_h___ */

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

@ -185,7 +185,7 @@ nsFrameLoader::~nsFrameLoader()
if (mMessageManager) {
mMessageManager->Disconnect();
}
nsFrameLoader::Destroy();
MOZ_RELEASE_ASSERT(mDestroyCalled);
}
nsFrameLoader*
@ -2665,17 +2665,40 @@ nsFrameLoader::RequestNotifyAfterRemotePaint()
// If remote browsing (e10s), handle this with the TabParent.
if (mRemoteBrowser) {
unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint();
return NS_OK;
}
// If not remote browsing, directly use the document's window.
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mDocShell);
if (!window) {
NS_WARNING("Unable to get window for synchronous MozAfterRemotePaint event.");
return NS_OK;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeReady()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeReady() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
window->SetRequestNotifyAfterRemotePaint();
nsRefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeReady"),
true, false);
event->PostDOMEvent();
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeCleared()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeCleared() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
nsRefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeCleared"),
true, false);
event->PostDOMEvent();
return NS_OK;
}

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

@ -585,7 +585,7 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
// Make sure no actual window ends up with mWindowID == 0
mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
mMarkedCCGeneration(0), mSendAfterRemotePaint(false)
mMarkedCCGeneration(0)
{}
nsPIDOMWindow::~nsPIDOMWindow() {}
@ -3773,21 +3773,6 @@ nsPIDOMWindow::RefreshMediaElements()
service->RefreshAgentsVolume(this);
}
void
nsPIDOMWindow::SendAfterRemotePaintIfRequested()
{
if (!mSendAfterRemotePaint) {
return;
}
mSendAfterRemotePaint = false;
nsContentUtils::DispatchChromeEvent(GetExtantDoc(),
GetParentTarget(),
NS_LITERAL_STRING("MozAfterRemotePaint"),
false, false);
}
// nsISpeechSynthesisGetter
#ifdef MOZ_WEBSPEECH
@ -7355,6 +7340,16 @@ nsGlobalWindow::ScrollByPages(int32_t numPages,
}
}
void
nsGlobalWindow::MozScrollSnap()
{
FlushPendingNotifications(Flush_Layout);
nsIScrollableFrame *sf = GetScrollFrame();
if (sf) {
sf->ScrollSnap();
}
}
void
nsGlobalWindow::MozRequestOverfill(OverfillCallback& aCallback,
mozilla::ErrorResult& aError)

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

@ -670,6 +670,26 @@ public:
mCleanedUp);
}
bool
HadOriginalOpener() const
{
MOZ_ASSERT(IsOuterWindow());
return mHadOriginalOpener;
}
bool
IsTopLevelWindow()
{
MOZ_ASSERT(IsOuterWindow());
nsCOMPtr<nsIDOMWindow> parentWindow;
nsresult rv = GetScriptableTop(getter_AddRefs(parentWindow));
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return parentWindow == static_cast<nsIDOMWindow*>(this);
}
virtual void
FirePopupBlockedEvent(nsIDocument* aDoc,
nsIURI* aPopupURI,
@ -946,6 +966,7 @@ public:
const mozilla::dom::ScrollOptions& aOptions);
void ScrollByPages(int32_t numPages,
const mozilla::dom::ScrollOptions& aOptions);
void MozScrollSnap();
int32_t GetInnerWidth(mozilla::ErrorResult& aError);
void SetInnerWidth(int32_t aInnerWidth, mozilla::ErrorResult& aError);
int32_t GetInnerHeight(mozilla::ErrorResult& aError);

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

@ -16,7 +16,7 @@ interface nsIDOMElement;
interface nsITabParent;
interface nsILoadContext;
[scriptable, builtinclass, uuid(28b6b043-46ec-412f-9be9-db22938b0d6d)]
[scriptable, builtinclass, uuid(d24f9330-ae4e-11e4-ab27-0800200c9a66)]
interface nsIFrameLoader : nsISupports
{
/**
@ -121,6 +121,14 @@ interface nsIFrameLoader : nsISupports
*/
void requestNotifyAfterRemotePaint();
/**
* Request an event when the layer tree from the remote tab becomes
* available or unavailable. When this happens, a mozLayerTreeReady
* or mozLayerTreeCleared event is fired.
*/
void requestNotifyLayerTreeReady();
void requestNotifyLayerTreeCleared();
/**
* The default event mode automatically forwards the events
* handled in EventStateManager::HandleCrossProcessEvent to

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

@ -751,17 +751,6 @@ public:
return mMarkedCCGeneration;
}
// Sets the condition that we send an NS_AFTER_REMOTE_PAINT message just before the next
// composite. Used in non-e10s implementations.
void SetRequestNotifyAfterRemotePaint()
{
mSendAfterRemotePaint = true;
}
// Sends an NS_AFTER_REMOTE_PAINT message if requested by
// SetRequestNotifyAfterRemotePaint().
void SendAfterRemotePaintIfRequested();
protected:
// The nsPIDOMWindow constructor. The aOuterWindow argument should
// be null if and only if the created window itself is an outer
@ -854,10 +843,6 @@ protected:
bool mHasNotifiedGlobalCreated;
uint32_t mMarkedCCGeneration;
// If true, send an NS_AFTER_REMOTE_PAINT message before compositing in a
// non-e10s implementation.
bool mSendAfterRemotePaint;
};

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

@ -14,6 +14,7 @@ support-files =
file_bug990812-3.xul
file_bug990812-4.xul
file_bug990812-5.xul
file_bug1139964.xul
fileconstructor_file.png
frame_bug814638.xul
host_bug814638.xul
@ -55,6 +56,7 @@ skip-if = buildapp == 'mulet'
[test_bug914381.html]
[test_bug990812.xul]
[test_bug1063837.xul]
[test_bug1139964.xul]
[test_cpows.xul]
skip-if = buildapp == 'mulet'
[test_document_register.xul]

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

@ -0,0 +1,62 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1139964
-->
<window title="Mozilla Bug 1139964"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="run()">
<label value="Mozilla Bug 1139964"/>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
var Cc = Components.classes;
var Ci = Components.interfaces;
var ppm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
function ok(cond, msg) {
opener.wrappedJSObject.ok(cond, msg);
}
var msgName = "TEST:Global_has_Promise";
function mmScriptForPromiseTest() {
sendAsyncMessage("TEST:Global_has_Promise",
{
hasPromise: ("Promise" in this),
hasTextEncoder: ("TextEncoder" in this),
hasWindow: ("Window" in this),
});
}
function processListener(m) {
ppm.removeMessageListener(msgName, processListener);
ok(m.data.hasPromise, "ProcessGlobal should have Promise object in the global scope!");
ok(m.data.hasTextEncoder, "ProcessGlobal should have TextEncoder object in the global scope!");
ok(!m.data.hasWindow, "ProcessGlobal should not have Window object in the global scope!");
messageManager.addMessageListener(msgName, tabListener)
messageManager.loadFrameScript("data:,(" + mmScriptForPromiseTest.toString() + ")()", true);
}
function tabListener(m) {
messageManager.removeMessageListener(msgName, tabListener);
ok(m.data.hasPromise, "TabChildGlobal should have Promise object in the global scope!");
ok(m.data.hasTextEncoder, "TabChildGlobal should have TextEncoder object in the global scope!");
ok(!m.data.hasWindow, "TabChildGlobal should not have Window object in the global scope!");
opener.setTimeout("done()", 0);
window.close();
}
function run() {
ppm.addMessageListener(msgName, processListener)
ppm.loadProcessScript("data:,(" + mmScriptForPromiseTest.toString() + ")()", true);
}
]]></script>
<browser type="content" src="about:blank" id="ifr"/>
</window>

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

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1139964
-->
<window title="Mozilla Bug 1139964"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1139964"
target="_blank">Mozilla Bug 1139964</a>
</body>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
/** Test for Bug 1139964 **/
SimpleTest.waitForExplicitFinish();
function done() {
SimpleTest.finish();
}
addLoadEvent(function() {
window.open("file_bug1139964.xul", "", "chrome");
});
]]></script>
</window>

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

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1139667 - Test mapping of fetch() to connect-src</title>
</head>
<body>
<script type="text/javascript">
// Please note that file_csp_testserver.sjs?foo does not return a response.
// For testing purposes this is not necessary because we only want to check
// whether CSP allows or blocks the load.
fetch( "file_csp_testserver.sjs?foo");
</script>
</body>
</html>

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

@ -2,6 +2,7 @@
support-files =
file_base-uri.html
file_connect-src.html
file_connect-src-fetch.html
file_CSP.css
file_CSP.sjs
file_csp_allow_https_schemes.html

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

@ -1,7 +1,7 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1031530 - Test mapping of XMLHttpRequest to connect-src</title>
<title>Bug 1031530 and Bug 1139667 - Test mapping of XMLHttpRequest and fetch() to connect-src</title>
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
@ -16,7 +16,7 @@
/*
* Description of the test:
* We load a page with a given CSP and verify that XMLHttpRequests are correctly
* We load a page with a given CSP and verify that XMLHttpRequests and fetches are correctly
* evaluated through the "connect-src" directive. All XMLHttpRequests are served
* using http://mochi.test:8888, which allows the requests to succeed for the first
* two policies and to fail for the last policy. Please note that we have to add
@ -27,14 +27,32 @@ SimpleTest.waitForExplicitFinish();
var tests = [
{
file: "file_connect-src.html",
result : "allowed",
policy : "default-src 'none' script-src 'unsafe-inline'; connect-src http://mochi.test:8888"
},
{
file: "file_connect-src.html",
result : "allowed",
policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src *"
},
{
file: "file_connect-src.html",
result : "blocked",
policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src http://www.example.com"
},
{
file: "file_connect-src-fetch.html",
result : "allowed",
policy : "default-src 'none' script-src 'unsafe-inline'; connect-src http://mochi.test:8888"
},
{
file: "file_connect-src-fetch.html",
result : "allowed",
policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src *"
},
{
file: "file_connect-src-fetch.html",
result : "blocked",
policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src http://www.example.com"
}
@ -96,7 +114,7 @@ function loadNextTest() {
var src = "file_csp_testserver.sjs";
// append the file that should be served
src += "?file=" + escape("tests/dom/base/test/csp/file_connect-src.html");
src += "?file=" + escape("tests/dom/base/test/csp/" + tests[counter].file);
// append the CSP that should be used to serve the file
src += "&csp=" + escape(tests[counter].policy);
@ -104,7 +122,9 @@ function loadNextTest() {
}
// start running the tests
loadNextTest();
SpecialPowers.pushPrefEnv({"set": [
["dom.fetch.enabled", true]
]}, loadNextTest);
</script>
</body>

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

@ -42,6 +42,7 @@
#include "mozilla/dom/HTMLEmbedElementBinding.h"
#include "mozilla/dom/HTMLAppletElementBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ResolveSystemBinding.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "WorkerPrivate.h"
#include "nsDOMClassInfo.h"
@ -249,26 +250,6 @@ ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
nsresult rv =
UNWRAP_OBJECT(DOMException, &mJSException.toObject(), domException);
if (NS_SUCCEEDED(rv)) {
// We may have to create a new DOMException object, because the one we
// have has a stack that includes the chrome code that threw it, and in
// particular has the wrong file/line/column information.
JS::Rooted<JS::Value> reflector(aCx);
if (!domException->Sanitize(aCx, &reflector)) {
// Well, that threw _an_ exception. Let's forget ours. We can just
// unroot and not change the value, since mJSException is completely
// ignored if mResult is not NS_ERROR_DOM_JS_EXCEPTION and we plan to
// change mResult to a different value.
js::RemoveRawValueRoot(aCx, &mJSException);
// We no longer have a useful exception but we do want to signal that an
// error occured.
mResult = NS_ERROR_FAILURE;
// But do make sure to not ReportJSException here, since we don't have one.
return;
}
mJSException = reflector;
ReportJSException(aCx);
return;
}
@ -307,17 +288,6 @@ ErrorResult::StealJSException(JSContext* cx,
value.set(mJSException);
js::RemoveRawValueRoot(cx, &mJSException);
mResult = NS_OK;
if (value.isObject()) {
// If it's a DOMException we may need to sanitize it.
dom::DOMException* domException;
nsresult rv =
UNWRAP_OBJECT(DOMException, &value.toObject(), domException);
if (NS_SUCCEEDED(rv) && !domException->Sanitize(cx, value)) {
JS_GetPendingException(cx, value);
JS_ClearPendingException(cx);
}
}
}
void
@ -1813,8 +1783,8 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
// CloneExpandoChain() will use this property of |newobj| when it calls
// preserveWrapper() via attachExpandoObject() if |aObj| has expandos set, and
// preserveWrapper() will not do anything in this case. This is safe because
// if expandos are present then the wrapper will already already have been
// preserved called for this native.
// if expandos are present then the wrapper will already have been preserved
// for this native.
if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
return NS_ERROR_FAILURE;
}
@ -2328,12 +2298,20 @@ bool
ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
JS::Handle<jsid> aId, bool* aResolvedp)
{
MOZ_ASSERT(JS_IsGlobalObject(aObj),
"Should have a global here, since we plan to resolve standard "
"classes!");
return JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp);
}
bool
EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj)
{
MOZ_ASSERT(JS_IsGlobalObject(aObj),
"Should have a global here, since we plan to enumerate standard "
"classes!");
return JS_EnumerateStandardClasses(aCx, aObj);
}
@ -2727,5 +2705,28 @@ UnwrapArgImpl(JS::Handle<JSObject*> src,
return wrappedJS->QueryInterface(iid, ppArg);
}
bool
SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, bool* resolvedp)
{
if (!ResolveGlobal(cx, obj, id, resolvedp)) {
return false;
}
if (*resolvedp) {
return true;
}
return ResolveSystemBinding(cx, obj, id, resolvedp);
}
bool
SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj)
{
bool ignored = false;
return EnumerateGlobal(cx, obj) &&
ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
}
} // namespace dom
} // namespace mozilla

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

@ -3270,6 +3270,18 @@ GetErrorPrototype(JSContext* aCx, JS::Handle<JSObject*> aForObj)
return JS_GetErrorPrototype(aCx);
}
// Resolve an id on the given global object that wants to be included in
// Exposed=System webidl annotations. False return value means exception
// thrown.
bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, bool* resolvedp);
// Enumerate all ids on the given global object that wants to be included in
// Exposed=System webidl annotations. False return value means exception
// thrown.
bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj);
} // namespace dom
} // namespace mozilla

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

@ -1270,6 +1270,11 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::TextTrackRegion',
},
'WindowClient': {
'nativeType': 'mozilla::dom::workers::ServiceWorkerWindowClient',
'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerWindowClient.h',
},
'WebGLActiveInfo': {
'nativeType': 'mozilla::WebGLActiveInfo',
'headerFile': 'WebGLActiveInfo.h'

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

@ -107,8 +107,7 @@ public:
// StealJSException steals the JS Exception from the object. This method must
// be called only if IsJSException() returns true. This method also resets the
// ErrorCode() to NS_OK. The value will be ensured to be sanitized wrt to the
// current compartment of cx if it happens to be a DOMException.
// ErrorCode() to NS_OK.
void StealJSException(JSContext* cx, JS::MutableHandle<JS::Value> value);
void MOZ_ALWAYS_INLINE MightThrowJSException()

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

@ -307,9 +307,6 @@ public:
NS_IMETHOD GetName(nsAString& aFunction) MOZ_OVERRIDE;
NS_IMETHOD GetCaller(nsIStackFrame** aCaller) MOZ_OVERRIDE;
NS_IMETHOD GetFormattedStack(nsAString& aStack) MOZ_OVERRIDE;
virtual bool CallerSubsumes(JSContext* aCx) MOZ_OVERRIDE;
NS_IMETHOD GetSanitized(JSContext* aCx,
nsIStackFrame** aSanitized) MOZ_OVERRIDE;
protected:
virtual bool IsJSFrame() const MOZ_OVERRIDE {
@ -391,37 +388,68 @@ NS_IMETHODIMP JSStackFrame::GetLanguageName(nsACString& aLanguageName)
return NS_OK;
}
// Helper method to get the value of a stack property, if it's not already
// cached. This will make sure we skip the cache if the access is happening
// over Xrays.
//
// @argument aStack the stack we're working with; must be non-null.
// @argument aPropGetter the getter function to call.
// @argument aIsCached whether we've cached this property's value before.
//
// @argument [out] aCanCache whether the value can get cached.
// @argument [out] aUseCachedValue if true, just use the cached value.
// @argument [out] aValue the value we got from the stack.
template<typename ReturnType, typename GetterOutParamType>
static void
GetValueIfNotCached(JSContext* aCx, JSObject* aStack,
JS::SavedFrameResult (*aPropGetter)(JSContext*,
JS::Handle<JSObject*>,
GetterOutParamType),
bool aIsCached, bool* aCanCache, bool* aUseCachedValue,
ReturnType aValue)
{
MOZ_ASSERT(aStack);
JS::Rooted<JSObject*> stack(aCx, aStack);
// Allow caching if aCx and stack are same-compartment. Otherwise take the
// slow path.
*aCanCache = js::GetContextCompartment(aCx) == js::GetObjectCompartment(stack);
if (*aCanCache && aIsCached) {
*aUseCachedValue = true;
return;
}
*aUseCachedValue = false;
JS::ExposeObjectToActiveJS(stack);
aPropGetter(aCx, stack, aValue);
}
/* readonly attribute AString filename; */
NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
{
// We can get called after unlink; in that case we can't do much
// about producing a useful value.
if (!mFilenameInitialized && mStack) {
ThreadsafeAutoJSContext cx;
JS::Rooted<JSObject*> stack(cx, mStack);
JS::ExposeObjectToActiveJS(mStack);
JSAutoCompartment ac(cx, stack);
NS_ENSURE_TRUE(mStack, NS_ERROR_NOT_AVAILABLE);
ThreadsafeAutoJSContext cx;
JS::Rooted<JSString*> filename(cx);
bool canCache = false, useCachedValue = false;
GetValueIfNotCached(cx, mStack, JS::GetSavedFrameSource, mFilenameInitialized,
&canCache, &useCachedValue, &filename);
if (useCachedValue) {
return StackFrame::GetFilename(aFilename);
}
JS::Rooted<JS::Value> filenameVal(cx);
if (!JS_GetProperty(cx, stack, "source", &filenameVal)) {
return NS_ERROR_UNEXPECTED;
}
if (filenameVal.isNull()) {
filenameVal = JS_GetEmptyStringValue(cx);
}
MOZ_ASSERT(filenameVal.isString());
nsAutoJSString str;
if (!str.init(cx, filenameVal.toString())) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsAutoJSString str;
if (!str.init(cx, filename)) {
return NS_ERROR_OUT_OF_MEMORY;
}
aFilename = str;
if (canCache) {
mFilename = str;
mFilenameInitialized = true;
}
return StackFrame::GetFilename(aFilename);
return NS_OK;
}
NS_IMETHODIMP StackFrame::GetFilename(nsAString& aFilename)
@ -439,30 +467,34 @@ NS_IMETHODIMP StackFrame::GetFilename(nsAString& aFilename)
/* readonly attribute AString name; */
NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
{
// We can get called after unlink; in that case we can't do much
// about producing a useful value.
if (!mFunnameInitialized && mStack) {
ThreadsafeAutoJSContext cx;
JS::Rooted<JSObject*> stack(cx, mStack);
JS::ExposeObjectToActiveJS(mStack);
JSAutoCompartment ac(cx, stack);
JS::Rooted<JS::Value> nameVal(cx);
// functionDisplayName can be null
if (!JS_GetProperty(cx, stack, "functionDisplayName", &nameVal) ||
(!nameVal.isString() && !nameVal.isNull())) {
return NS_ERROR_UNEXPECTED;
}
if (nameVal.isString()) {
nsAutoJSString str;
if (!str.init(cx, nameVal.toString())) {
return NS_ERROR_OUT_OF_MEMORY;
}
mFunname = str;
NS_ENSURE_TRUE(mStack, NS_ERROR_NOT_AVAILABLE);
ThreadsafeAutoJSContext cx;
JS::Rooted<JSString*> name(cx);
bool canCache = false, useCachedValue = false;
GetValueIfNotCached(cx, mStack, JS::GetSavedFrameFunctionDisplayName,
mFunnameInitialized, &canCache, &useCachedValue,
&name);
if (useCachedValue) {
return StackFrame::GetName(aFunction);
}
if (name) {
nsAutoJSString str;
if (!str.init(cx, name)) {
return NS_ERROR_OUT_OF_MEMORY;
}
aFunction = str;
} else {
aFunction.SetIsVoid(true);
}
if (canCache) {
mFunname = aFunction;
mFunnameInitialized = true;
}
return StackFrame::GetName(aFunction);
return NS_OK;
}
NS_IMETHODIMP StackFrame::GetName(nsAString& aFunction)
@ -481,27 +513,25 @@ NS_IMETHODIMP StackFrame::GetName(nsAString& aFunction)
nsresult
JSStackFrame::GetLineno(int32_t* aLineNo)
{
// We can get called after unlink; in that case we can't do much
// about producing a useful value.
if (!mLinenoInitialized && mStack) {
ThreadsafeAutoJSContext cx;
JS::Rooted<JSObject*> stack(cx, mStack);
JS::ExposeObjectToActiveJS(mStack);
JSAutoCompartment ac(cx, stack);
JS::Rooted<JS::Value> lineVal(cx);
if (!JS_GetProperty(cx, stack, "line", &lineVal)) {
return NS_ERROR_UNEXPECTED;
}
if (lineVal.isNumber()) {
mLineno = lineVal.toNumber();
} else {
MOZ_ASSERT(lineVal.isNull());
mLineno = 0;
}
NS_ENSURE_TRUE(mStack, NS_ERROR_NOT_AVAILABLE);
ThreadsafeAutoJSContext cx;
uint32_t line;
bool canCache = false, useCachedValue = false;
GetValueIfNotCached(cx, mStack, JS::GetSavedFrameLine, mLinenoInitialized,
&canCache, &useCachedValue, &line);
if (useCachedValue) {
return StackFrame::GetLineno(aLineNo);
}
*aLineNo = line;
if (canCache) {
mLineno = line;
mLinenoInitialized = true;
}
return StackFrame::GetLineno(aLineNo);
return NS_OK;
}
/* readonly attribute int32_t lineNumber; */
@ -514,27 +544,25 @@ NS_IMETHODIMP StackFrame::GetLineNumber(int32_t* aLineNumber)
nsresult
JSStackFrame::GetColNo(int32_t* aColNo)
{
// We can get called after unlink; in that case we can't do much
// about producing a useful value.
if (!mColNoInitialized && mStack) {
ThreadsafeAutoJSContext cx;
JS::Rooted<JSObject*> stack(cx, mStack);
JS::ExposeObjectToActiveJS(mStack);
JSAutoCompartment ac(cx, stack);
JS::Rooted<JS::Value> colVal(cx);
if (!JS_GetProperty(cx, stack, "column", &colVal)) {
return NS_ERROR_UNEXPECTED;
}
if (colVal.isNumber()) {
mColNo = colVal.toNumber();
} else {
MOZ_ASSERT(colVal.isNull());
mColNo = 0;
}
NS_ENSURE_TRUE(mStack, NS_ERROR_NOT_AVAILABLE);
ThreadsafeAutoJSContext cx;
uint32_t col;
bool canCache = false, useCachedValue = false;
GetValueIfNotCached(cx, mStack, JS::GetSavedFrameColumn, mColNoInitialized,
&canCache, &useCachedValue, &col);
if (useCachedValue) {
return StackFrame::GetColNo(aColNo);
}
*aColNo = col;
if (canCache) {
mColNo = col;
mColNoInitialized = true;
}
return StackFrame::GetColNo(aColNo);
return NS_OK;
}
/* readonly attribute int32_t columnNumber; */
@ -550,63 +578,37 @@ NS_IMETHODIMP StackFrame::GetSourceLine(nsACString& aSourceLine)
return NS_OK;
}
/* [noscript] readonly attribute nsIStackFrame sanitized */
NS_IMETHODIMP StackFrame::GetSanitized(JSContext*, nsIStackFrame** aSanitized)
{
NS_ADDREF(*aSanitized = this);
return NS_OK;
}
/* [noscript] readonly attribute nsIStackFrame sanitized */
NS_IMETHODIMP JSStackFrame::GetSanitized(JSContext* aCx, nsIStackFrame** aSanitized)
{
// NB: Do _not_ enter the compartment of the SavedFrame object here, because
// we are checking against the caller's compartment's principals in
// GetFirstSubsumedSavedFrame.
JS::RootedObject savedFrame(aCx, mStack);
JS::ExposeObjectToActiveJS(mStack);
savedFrame = js::GetFirstSubsumedSavedFrame(aCx, savedFrame);
nsCOMPtr<nsIStackFrame> stackFrame;
if (savedFrame) {
stackFrame = new JSStackFrame(savedFrame);
} else {
stackFrame = new StackFrame();
}
stackFrame.forget(aSanitized);
return NS_OK;
}
/* readonly attribute nsIStackFrame caller; */
NS_IMETHODIMP JSStackFrame::GetCaller(nsIStackFrame** aCaller)
{
// We can get called after unlink; in that case we can't do much
// about producing a useful value.
if (!mCallerInitialized && mStack) {
ThreadsafeAutoJSContext cx;
JS::Rooted<JSObject*> stack(cx, mStack);
JS::ExposeObjectToActiveJS(mStack);
JSAutoCompartment ac(cx, stack);
JS::Rooted<JS::Value> callerVal(cx);
if (!JS_GetProperty(cx, stack, "parent", &callerVal) ||
!callerVal.isObjectOrNull()) {
return NS_ERROR_UNEXPECTED;
}
NS_ENSURE_TRUE(mStack, NS_ERROR_NOT_AVAILABLE);
ThreadsafeAutoJSContext cx;
JS::Rooted<JSObject*> callerObj(cx);
bool canCache = false, useCachedValue = false;
GetValueIfNotCached(cx, mStack, JS::GetSavedFrameParent, mCallerInitialized,
&canCache, &useCachedValue, &callerObj);
if (callerVal.isObject()) {
JS::Rooted<JSObject*> caller(cx, &callerVal.toObject());
mCaller = new JSStackFrame(caller);
} else {
// Do we really need this dummy frame? If so, we should document why... I
// guess for symmetry with the "nothing on the stack" case, which returns
// a single dummy frame?
mCaller = new StackFrame();
}
if (useCachedValue) {
return StackFrame::GetCaller(aCaller);
}
nsCOMPtr<nsIStackFrame> caller;
if (callerObj) {
caller = new JSStackFrame(callerObj);
} else {
// Do we really need this dummy frame? If so, we should document why... I
// guess for symmetry with the "nothing on the stack" case, which returns
// a single dummy frame?
caller = new StackFrame();
}
caller.forget(aCaller);
if (canCache) {
mCaller = *aCaller;
mCallerInitialized = true;
}
return StackFrame::GetCaller(aCaller);
return NS_OK;
}
NS_IMETHODIMP StackFrame::GetCaller(nsIStackFrame** aCaller)
@ -617,26 +619,42 @@ NS_IMETHODIMP StackFrame::GetCaller(nsIStackFrame** aCaller)
NS_IMETHODIMP JSStackFrame::GetFormattedStack(nsAString& aStack)
{
// We can get called after unlink; in that case we can't do much
// about producing a useful value.
if (!mFormattedStackInitialized && mStack) {
ThreadsafeAutoJSContext cx;
JS::Rooted<JS::Value> stack(cx, JS::ObjectValue(*mStack));
JS::ExposeObjectToActiveJS(mStack);
JSAutoCompartment ac(cx, mStack);
JS::Rooted<JSString*> formattedStack(cx, JS::ToString(cx, stack));
if (!formattedStack) {
return NS_ERROR_UNEXPECTED;
}
nsAutoJSString str;
if (!str.init(cx, formattedStack)) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ENSURE_TRUE(mStack, NS_ERROR_NOT_AVAILABLE);
// Sadly we can't use GetValueIfNotCached here, because our getter
// returns bool, not JS::SavedFrameResult. Maybe it's possible to
// make the templates more complicated to deal, but in the meantime
// let's just inline GetValueIfNotCached here.
ThreadsafeAutoJSContext cx;
// Allow caching if cx and stack are same-compartment. Otherwise take the
// slow path.
bool canCache =
js::GetContextCompartment(cx) == js::GetObjectCompartment(mStack);
if (canCache && mFormattedStackInitialized) {
aStack = mFormattedStack;
return NS_OK;
}
JS::ExposeObjectToActiveJS(mStack);
JS::Rooted<JSObject*> stack(cx, mStack);
JS::Rooted<JSString*> formattedStack(cx);
if (!JS::StringifySavedFrameStack(cx, stack, &formattedStack)) {
return NS_ERROR_UNEXPECTED;
}
nsAutoJSString str;
if (!str.init(cx, formattedStack)) {
return NS_ERROR_OUT_OF_MEMORY;
}
aStack = str;
if (canCache) {
mFormattedStack = str;
mFormattedStackInitialized = true;
}
aStack = mFormattedStack;
return NS_OK;
}
@ -681,37 +699,6 @@ NS_IMETHODIMP StackFrame::ToString(nsACString& _retval)
return NS_OK;
}
/* virtual */ bool
StackFrame::CallerSubsumes(JSContext* aCx)
{
return true;
}
/* virtual */ bool
JSStackFrame::CallerSubsumes(JSContext* aCx)
{
if (!NS_IsMainThread()) {
return true;
}
if (!mStack) {
// No problem here, there's no data to leak.
return true;
}
nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal();
JS::Rooted<JSObject*> unwrappedStack(aCx, js::CheckedUnwrap(mStack));
if (!unwrappedStack) {
// We can't leak data here either.
return true;
}
nsIPrincipal* stackPrincipal =
nsJSPrincipals::get(js::GetSavedFramePrincipals(unwrappedStack));
return callerPrincipal->SubsumesConsideringDomain(stackPrincipal);
}
/* static */ already_AddRefed<nsIStackFrame>
JSStackFrame::CreateStack(JSContext* aCx, int32_t aMaxDepth)
{

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

@ -42,20 +42,24 @@ if (!('BrowserElementIsPreloaded' in this)) {
}
}
if (docShell.asyncPanZoomEnabled === false) {
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanningAPZDisabled.js");
ContentPanningAPZDisabled.init();
}
if (Services.prefs.getIntPref("dom.w3c_touch_events.enabled") == 1) {
if (docShell.asyncPanZoomEnabled === false) {
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanningAPZDisabled.js");
ContentPanningAPZDisabled.init();
}
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js");
ContentPanning.init();
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js");
ContentPanning.init();
}
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js");
} else {
if (docShell.asyncPanZoomEnabled === false) {
ContentPanningAPZDisabled.init();
if (Services.prefs.getIntPref("dom.w3c_touch_events.enabled") == 1) {
if (docShell.asyncPanZoomEnabled === false) {
ContentPanningAPZDisabled.init();
}
ContentPanning.init();
}
ContentPanning.init();
}
var BrowserElementIsReady = true;

4
dom/cache/CacheStorageChild.cpp поставляемый
Просмотреть файл

@ -137,7 +137,9 @@ CacheStorageChild::RecvOpenResponse(const RequestId& aRequestId,
// the feature won't try to shutdown the actor until after we create the
// Cache DOM object in the listener's RecvOpenResponse() method. This
// is important because StartShutdown() expects a Cache object listener.
cacheChild->SetFeature(GetFeature());
if (cacheChild) {
cacheChild->SetFeature(GetFeature());
}
listener->RecvOpenResponse(aRequestId, aRv, cacheChild);
return true;

18
dom/cache/DBSchema.cpp поставляемый
Просмотреть файл

@ -339,7 +339,7 @@ DBSchema::CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
PCacheQueryParams params(false, false, false, false, false,
PCacheQueryParams params(false, false, false, false,
NS_LITERAL_STRING(""));
nsAutoTArray<EntryId, 256> matches;
nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
@ -675,13 +675,7 @@ DBSchema::QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
query.AppendLiteral("request_url");
}
if (aParams.prefixMatch()) {
query.AppendLiteral(" LIKE ?2 ESCAPE '\\'");
} else {
query.AppendLiteral("=?2");
}
query.AppendLiteral(" GROUP BY entries.id ORDER BY entries.id;");
query.AppendLiteral("=?2 GROUP BY entries.id ORDER BY entries.id;");
nsCOMPtr<mozIStorageStatement> state;
nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
@ -690,14 +684,6 @@ DBSchema::QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
rv = state->BindInt32Parameter(0, aCacheId);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (aParams.prefixMatch()) {
nsAutoString escapedUrlToMatch;
rv = state->EscapeStringForLIKE(urlToMatch, '\\', escapedUrlToMatch);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
urlToMatch = escapedUrlToMatch;
urlToMatch.AppendLiteral("%");
}
rv = state->BindStringParameter(1, urlToMatch);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }

1
dom/cache/PCacheTypes.ipdlh поставляемый
Просмотреть файл

@ -22,7 +22,6 @@ struct PCacheQueryParams
bool ignoreSearch;
bool ignoreMethod;
bool ignoreVary;
bool prefixMatch;
bool cacheNameSet;
nsString cacheName;
};

1
dom/cache/TypeUtils.cpp поставляемый
Просмотреть файл

@ -266,7 +266,6 @@ TypeUtils::ToPCacheQueryParams(PCacheQueryParams& aOut,
aOut.ignoreSearch() = aIn.mIgnoreSearch;
aOut.ignoreMethod() = aIn.mIgnoreMethod;
aOut.ignoreVary() = aIn.mIgnoreVary;
aOut.prefixMatch() = aIn.mPrefixMatch;
aOut.cacheNameSet() = aIn.mCacheName.WasPassed();
if (aOut.cacheNameSet()) {
aOut.cacheName() = aIn.mCacheName.Value();

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

@ -131,6 +131,26 @@ DragEvent::GetDataTransfer()
return dragEvent->dataTransfer;
}
// static
already_AddRefed<DragEvent>
DragEvent::Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const DragEventInit& aParam,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
nsRefPtr<DragEvent> e = new DragEvent(t, nullptr, nullptr);
bool trusted = e->Init(t);
aRv = e->InitDragEvent(aType, aParam.mBubbles, aParam.mCancelable,
aParam.mView, aParam.mDetail, aParam.mScreenX,
aParam.mScreenY, aParam.mClientX, aParam.mClientY,
aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey,
aParam.mMetaKey, aParam.mButton, aParam.mRelatedTarget,
aParam.mDataTransfer);
e->SetTrusted(trusted);
return e.forget();
}
} // namespace dom
} // namespace mozilla

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

@ -48,6 +48,11 @@ public:
DataTransfer* aDataTransfer,
ErrorResult& aError);
static already_AddRefed<DragEvent> Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const DragEventInit& aParam,
ErrorResult& aRv);
protected:
~DragEvent() {}
};

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

@ -2417,13 +2417,16 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
actualDevPixelScrollAmount.y = 0;
}
nsIScrollableFrame::ScrollSnapMode snapMode = nsIScrollableFrame::DISABLE_SNAP;
nsIAtom* origin = nullptr;
switch (aEvent->deltaMode) {
case nsIDOMWheelEvent::DOM_DELTA_LINE:
origin = nsGkAtoms::mouseWheel;
snapMode = nsIScrollableFrame::ENABLE_SNAP;
break;
case nsIDOMWheelEvent::DOM_DELTA_PAGE:
origin = nsGkAtoms::pages;
snapMode = nsIScrollableFrame::ENABLE_SNAP;
break;
case nsIDOMWheelEvent::DOM_DELTA_PIXEL:
origin = nsGkAtoms::pixels;
@ -2476,10 +2479,14 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
MOZ_CRASH("Invalid scrollType value comes");
}
nsIScrollableFrame::ScrollMomentum momentum =
aEvent->isMomentum ? nsIScrollableFrame::SYNTHESIZED_MOMENTUM_EVENT
: nsIScrollableFrame::NOT_MOMENTUM;
nsIntPoint overflow;
aScrollableFrame->ScrollBy(actualDevPixelScrollAmount,
nsIScrollableFrame::DEVICE_PIXELS,
mode, &overflow, origin, aEvent->isMomentum);
mode, &overflow, origin, momentum, snapMode);
if (!scrollFrameWeak.IsAlive()) {
// If the scroll causes changing the layout, we can think that the event
@ -2980,6 +2987,13 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
{
MOZ_ASSERT(aEvent->mFlags.mIsTrusted);
ScrollbarsForWheel::MayInactivate();
WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
nsIScrollableFrame* scrollTarget =
ComputeScrollTarget(aTargetFrame, wheelEvent,
COMPUTE_DEFAULT_ACTION_TARGET);
if (scrollTarget) {
scrollTarget->ScrollSnap();
}
}
break;
case NS_WHEEL_WHEEL:

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

@ -824,6 +824,21 @@ while (testWheelProps.length) {
}
}
// DragEvent
try {
e = new DragEvent();
} catch(exp) {
ex = true;
}
ok(ex, "DragEvent: First parameter is required!");
ex = false;
e = new DragEvent("hello");
is(e.type, "hello", "DragEvent: Wrong event type!");
document.dispatchEvent(e);
is(receivedEvent, e, "DragEvent: Wrong event!");
// TransitionEvent
e = new TransitionEvent("hello", { propertyName: "color", elapsedTime: 3.5, pseudoElement: "", foobar: "baz" })
is("propertyName" in e, true, "Transition events have propertyName property");

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

@ -177,7 +177,7 @@ public:
nsRefPtr<FetchDriver> fetch = new FetchDriver(mRequest, principal, loadGroup);
nsIDocument* doc = mResolver->GetWorkerPrivate()->GetDocument();
if (doc) {
fetch->SetReferrerPolicy(doc->GetReferrerPolicy());
fetch->SetDocument(doc);
}
nsresult rv = fetch->Fetch(mResolver);
@ -234,7 +234,7 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
nsRefPtr<FetchDriver> fetch =
new FetchDriver(r, doc->NodePrincipal(), loadGroup);
fetch->SetReferrerPolicy(doc->GetReferrerPolicy());
fetch->SetDocument(doc);
aRv = fetch->Fetch(resolver);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@ -315,7 +315,6 @@ public:
result.ThrowTypeError(MSG_FETCH_FAILED);
promise->MaybeReject(result);
}
return true;
}
};

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

@ -5,6 +5,7 @@
#include "mozilla/dom/FetchDriver.h"
#include "nsIDocument.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIHttpChannel.h"
@ -42,7 +43,6 @@ FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
, mLoadGroup(aLoadGroup)
, mRequest(aRequest)
, mFetchRecursionCount(0)
, mReferrerPolicy(net::RP_Default)
, mResponseAvailableCalled(false)
{
}
@ -99,11 +99,27 @@ FetchDriver::ContinueFetch(bool aCORSFlag)
return FailWithNetworkError();
}
// CSP/mixed content checks.
int16_t shouldLoad;
rv = NS_CheckContentLoadPolicy(mRequest->ContentPolicyType(),
requestURI,
mPrincipal,
mDocument,
// FIXME(nsm): Should MIME be extracted from
// Content-Type header?
EmptyCString(), /* mime guess */
nullptr, /* extra */
&shouldLoad,
nsContentUtils::GetContentPolicy(),
nsContentUtils::GetSecurityManager());
if (NS_WARN_IF(NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad))) {
// Disallowed by content policy.
return FailWithNetworkError();
}
// Begin Step 4 of the Fetch algorithm
// https://fetch.spec.whatwg.org/#fetching
// FIXME(nsm): Bug 1039846: Add CSP checks
nsAutoCString scheme;
rv = requestURI->GetScheme(scheme);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -289,7 +305,6 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
{
// Step 1. "Let response be null."
mResponse = nullptr;
nsresult rv;
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
@ -406,7 +421,12 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
return FailWithNetworkError();
}
rv = httpChan->SetReferrerWithPolicy(refURI, mReferrerPolicy);
net::ReferrerPolicy referrerPolicy = net::RP_Default;
if (mDocument) {
referrerPolicy = mDocument->GetReferrerPolicy();
}
rv = httpChan->SetReferrerWithPolicy(refURI, referrerPolicy);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
@ -869,5 +889,13 @@ FetchDriver::OnRedirectVerifyCallback(nsresult aResult)
mRedirectCallback = nullptr;
return NS_OK;
}
void
FetchDriver::SetDocument(nsIDocument* aDocument)
{
// Cannot set document after Fetch() has been called.
MOZ_ASSERT(mFetchRecursionCount == 0);
mDocument = aDocument;
}
} // namespace dom
} // namespace mozilla

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

@ -16,6 +16,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/net/ReferrerPolicy.h"
class nsIDocument;
class nsIOutputStream;
class nsILoadGroup;
class nsIPrincipal;
@ -58,12 +59,7 @@ public:
NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
void
SetReferrerPolicy(net::ReferrerPolicy aPolicy)
{
// Cannot set policy after Fetch() has been called.
MOZ_ASSERT(mFetchRecursionCount == 0);
mReferrerPolicy = aPolicy;
}
SetDocument(nsIDocument* aDocument);
private:
nsCOMPtr<nsIPrincipal> mPrincipal;
@ -76,8 +72,8 @@ private:
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
nsCOMPtr<nsIChannel> mOldRedirectChannel;
nsCOMPtr<nsIChannel> mNewRedirectChannel;
nsCOMPtr<nsIDocument> mDocument;
uint32_t mFetchRecursionCount;
net::ReferrerPolicy mReferrerPolicy;
DebugOnly<bool> mResponseAvailableCalled;

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

@ -561,8 +561,9 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
nsresult rv = blob->GetSize(&size);
if (NS_SUCCEEDED(rv)) {
AutoJSAPI jsapi;
jsapi.Init(mGlobal);
JS_updateMallocCounter(jsapi.cx(), size);
if (jsapi.Init(mGlobal)) {
JS_updateMallocCounter(jsapi.cx(), size);
}
}
nsRefPtr<File> newBlob = new File(mGlobal, blob->Impl());

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

@ -1544,11 +1544,10 @@ ContentChild::DeallocPNeckoChild(PNeckoChild* necko)
PPrintingChild*
ContentChild::AllocPPrintingChild()
{
// The ContentParent should never attempt to allocate the
// nsPrintingPromptServiceProxy, which implements PPrintingChild. Instead,
// the nsPrintingPromptServiceProxy service is requested and instantiated
// via XPCOM, and the constructor of nsPrintingPromptServiceProxy sets up
// the IPC connection.
// The ContentParent should never attempt to allocate the nsPrintingProxy,
// which implements PPrintingChild. Instead, the nsPrintingProxy service is
// requested and instantiated via XPCOM, and the constructor of
// nsPrintingProxy sets up the IPC connection.
NS_NOTREACHED("Should never get here!");
return nullptr;
}

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

@ -513,6 +513,7 @@ child:
// The following methods correspond to functions on the GeckoContentController
// interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
// in that file for these functions.
RequestFlingSnap(ViewID aScrollID, CSSPoint aDestination);
AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);

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

@ -2036,6 +2036,14 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
return TabChildBase::UpdateFrameHandler(aFrameMetrics);
}
bool
TabChild::RecvRequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
const mozilla::CSSPoint& aDestination)
{
APZCCallbackHelper::RequestFlingSnap(aScrollId, aDestination);
return true;
}
bool
TabChild::RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
const uint32_t& aScrollGeneration)

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

@ -319,6 +319,8 @@ public:
const ScreenOrientation& orientation,
const nsIntPoint& chromeDisp) MOZ_OVERRIDE;
virtual bool RecvUpdateFrame(const layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
virtual bool RecvRequestFlingSnap(const ViewID& aScrollId,
const CSSPoint& aDestination) MOZ_OVERRIDE;
virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,

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

@ -922,6 +922,15 @@ TabParent::UIResolutionChanged()
}
}
void
TabParent::RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
const mozilla::CSSPoint& aDestination)
{
if (!mIsDestroyed) {
unused << SendRequestFlingSnap(aScrollId, aDestination);
}
}
void
TabParent::AcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration)
{
@ -2620,6 +2629,86 @@ TabParent::GetTabId(uint64_t* aId)
return NS_OK;
}
class LayerTreeUpdateRunnable MOZ_FINAL
: public nsRunnable
{
uint64_t mLayersId;
bool mActive;
public:
explicit LayerTreeUpdateRunnable(uint64_t aLayersId, bool aActive)
: mLayersId(aLayersId), mActive(aActive) {}
private:
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
TabParent* tabParent = TabParent::GetTabParentFromLayersId(mLayersId);
if (tabParent) {
tabParent->LayerTreeUpdate(mActive);
}
return NS_OK;
}
};
// This observer runs on the compositor thread, so we dispatch a runnable to the
// main thread to actually dispatch the event.
class LayerTreeUpdateObserver : public CompositorUpdateObserver
{
virtual void ObserveUpdate(uint64_t aLayersId, bool aActive) {
nsRefPtr<LayerTreeUpdateRunnable> runnable = new LayerTreeUpdateRunnable(aLayersId, aActive);
NS_DispatchToMainThread(runnable);
}
};
bool
TabParent::RequestNotifyLayerTreeReady()
{
RenderFrameParent* frame = GetRenderFrame();
if (!frame) {
return false;
}
CompositorParent::RequestNotifyLayerTreeReady(frame->GetLayersId(),
new LayerTreeUpdateObserver());
return true;
}
bool
TabParent::RequestNotifyLayerTreeCleared()
{
RenderFrameParent* frame = GetRenderFrame();
if (!frame) {
return false;
}
CompositorParent::RequestNotifyLayerTreeCleared(frame->GetLayersId(),
new LayerTreeUpdateObserver());
return true;
}
bool
TabParent::LayerTreeUpdate(bool aActive)
{
nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
if (!target) {
NS_WARNING("Could not locate target for layer tree message.");
return true;
}
nsCOMPtr<nsIDOMEvent> event;
NS_NewDOMEvent(getter_AddRefs(event), mFrameElement, nullptr, nullptr);
if (aActive) {
event->InitEvent(NS_LITERAL_STRING("MozLayerTreeReady"), true, false);
} else {
event->InitEvent(NS_LITERAL_STRING("MozLayerTreeCleared"), true, false);
}
event->SetTrusted(true);
event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
bool dummy;
mFrameElement->DispatchEvent(event, &dummy);
return true;
}
bool
TabParent::RecvRemotePaintIsReady()
{

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

@ -245,6 +245,8 @@ public:
const nsIntPoint& chromeDisp);
void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
void UIResolutionChanged();
void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
const mozilla::CSSPoint& aDestination);
void AcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration);
void HandleDoubleTap(const CSSPoint& aPoint,
Modifiers aModifiers,
@ -372,6 +374,11 @@ public:
bool SendLoadRemoteScript(const nsString& aURL,
const bool& aRunInGlobalScope);
// See nsIFrameLoader requestNotifyLayerTreeReady.
bool RequestNotifyLayerTreeReady();
bool RequestNotifyLayerTreeCleared();
bool LayerTreeUpdate(bool aActive);
protected:
bool ReceiveMessage(const nsString& aMessage,
bool aSync,

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше