зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
0a3b9074de
|
@ -9,10 +9,10 @@
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||||
"resource://gre/modules/ExtensionParent.jsm");
|
"resource://gre/modules/ExtensionParent.jsm");
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
extensionStylesheets,
|
|
||||||
promiseEvent,
|
promiseEvent,
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ function getBrowser(sidebar) {
|
||||||
"chrome://extensions/content/ext-browser-content.js", false);
|
"chrome://extensions/content/ext-browser-content.js", false);
|
||||||
|
|
||||||
browser.messageManager.sendAsyncMessage("Extension:InitBrowser", {
|
browser.messageManager.sendAsyncMessage("Extension:InitBrowser", {
|
||||||
stylesheets: extensionStylesheets,
|
stylesheets: ExtensionParent.extensionStylesheets,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return browser;
|
return browser;
|
||||||
|
|
|
@ -24,7 +24,6 @@ Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
DefaultWeakMap,
|
DefaultWeakMap,
|
||||||
extensionStylesheets,
|
|
||||||
promiseEvent,
|
promiseEvent,
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
@ -160,7 +159,7 @@ class BasePopup {
|
||||||
let sheets = [];
|
let sheets = [];
|
||||||
|
|
||||||
if (this.browserStyle) {
|
if (this.browserStyle) {
|
||||||
sheets.push(...extensionStylesheets);
|
sheets.push(...ExtensionParent.extensionStylesheets);
|
||||||
}
|
}
|
||||||
if (!this.fixedWidth) {
|
if (!this.fixedWidth) {
|
||||||
sheets.push(...standaloneStylesheets);
|
sheets.push(...standaloneStylesheets);
|
||||||
|
|
|
@ -19,9 +19,14 @@ Cu.import("resource://gre/modules/EventEmitter.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
DefaultWeakMap,
|
DefaultWeakMap,
|
||||||
IconDetails,
|
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||||
|
|
||||||
|
var {
|
||||||
|
IconDetails,
|
||||||
|
} = ExtensionParent;
|
||||||
|
|
||||||
const POPUP_PRELOAD_TIMEOUT_MS = 200;
|
const POPUP_PRELOAD_TIMEOUT_MS = 200;
|
||||||
|
|
||||||
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
/* vim: set sts=2 sw=2 et tw=80: */
|
/* vim: set sts=2 sw=2 et tw=80: */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var {
|
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||||
PlatformInfo,
|
"resource://gre/modules/ExtensionParent.jsm");
|
||||||
} = ExtensionUtils;
|
|
||||||
|
|
||||||
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
|
||||||
|
@ -71,6 +70,7 @@ this.commands = class extends ExtensionAPI {
|
||||||
let commands = new Map();
|
let commands = new Map();
|
||||||
// For Windows, chrome.runtime expects 'win' while chrome.commands
|
// For Windows, chrome.runtime expects 'win' while chrome.commands
|
||||||
// expects 'windows'. We can special case this for now.
|
// expects 'windows'. We can special case this for now.
|
||||||
|
let {PlatformInfo} = ExtensionParent;
|
||||||
let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
|
let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
|
||||||
for (let [name, command] of Object.entries(manifest.commands)) {
|
for (let [name, command] of Object.entries(manifest.commands)) {
|
||||||
let suggested_key = command.suggested_key || {};
|
let suggested_key = command.suggested_key || {};
|
||||||
|
|
|
@ -9,9 +9,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||||
|
|
||||||
var {
|
var {
|
||||||
ExtensionError,
|
ExtensionError,
|
||||||
IconDetails,
|
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||||
|
|
||||||
|
var {
|
||||||
|
IconDetails,
|
||||||
|
} = ExtensionParent;
|
||||||
|
|
||||||
const ACTION_MENU_TOP_LEVEL_LIMIT = 6;
|
const ACTION_MENU_TOP_LEVEL_LIMIT = 6;
|
||||||
|
|
||||||
// Map[Extension -> Map[ID -> MenuItem]]
|
// Map[Extension -> Map[ID -> MenuItem]]
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
var {
|
var {
|
||||||
SpreadArgs,
|
SpreadArgs,
|
||||||
} = ExtensionUtils;
|
} = ExtensionCommon;
|
||||||
|
|
||||||
this.devtools_inspectedWindow = class extends ExtensionAPI {
|
this.devtools_inspectedWindow = class extends ExtensionAPI {
|
||||||
getAPI(context) {
|
getAPI(context) {
|
||||||
|
|
|
@ -8,11 +8,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||||
"resource:///modules/E10SUtils.jsm");
|
"resource:///modules/E10SUtils.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
|
IconDetails,
|
||||||
watchExtensionProxyContextLoad,
|
watchExtensionProxyContextLoad,
|
||||||
} = ExtensionParent;
|
} = ExtensionParent;
|
||||||
|
|
||||||
var {
|
var {
|
||||||
IconDetails,
|
|
||||||
promiseEvent,
|
promiseEvent,
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelPopup",
|
||||||
|
|
||||||
var {
|
var {
|
||||||
DefaultWeakMap,
|
DefaultWeakMap,
|
||||||
IconDetails,
|
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||||
|
|
||||||
|
var {
|
||||||
|
IconDetails,
|
||||||
|
} = ExtensionParent;
|
||||||
|
|
||||||
// WeakMap[Extension -> PageAction]
|
// WeakMap[Extension -> PageAction]
|
||||||
let pageActionMap = new WeakMap();
|
let pageActionMap = new WeakMap();
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,16 @@ XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||||
"resource://gre/modules/Services.jsm");
|
"resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
ExtensionError,
|
ExtensionError,
|
||||||
IconDetails,
|
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
var {
|
||||||
|
IconDetails,
|
||||||
|
} = ExtensionParent;
|
||||||
|
|
||||||
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
|
||||||
// WeakMap[Extension -> SidebarAction]
|
// WeakMap[Extension -> SidebarAction]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
||||||
|
|
||||||
Current extension version is: 1.8.398
|
Current extension version is: 1.8.423
|
||||||
|
|
||||||
Taken from upstream commit: 96377832
|
Taken from upstream commit: 8654635b
|
||||||
|
|
|
@ -144,7 +144,7 @@ Factory.prototype = {
|
||||||
registrar.unregisterFactory(this._classID2, this._factory);
|
registrar.unregisterFactory(this._classID2, this._factory);
|
||||||
}
|
}
|
||||||
this._factory = null;
|
this._factory = null;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var PdfJs = {
|
var PdfJs = {
|
||||||
|
@ -344,6 +344,6 @@ var PdfJs = {
|
||||||
delete this._pdfStreamConverterFactory;
|
delete this._pdfStreamConverterFactory;
|
||||||
|
|
||||||
this._registered = false;
|
this._registered = false;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -65,5 +65,5 @@ this.PdfJsTelemetry = {
|
||||||
onTimeToView(ms) {
|
onTimeToView(ms) {
|
||||||
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_TIME_TO_VIEW_MS");
|
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_TIME_TO_VIEW_MS");
|
||||||
histogram.add(ms);
|
histogram.add(ms);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -201,7 +201,7 @@ PdfDataListener.prototype = {
|
||||||
if (this.errorCode) {
|
if (this.errorCode) {
|
||||||
value(null, this.errorCode);
|
value(null, this.errorCode);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -216,7 +216,7 @@ class ChromeActions {
|
||||||
firstPageInfo: false,
|
firstPageInfo: false,
|
||||||
streamTypesUsed: [],
|
streamTypesUsed: [],
|
||||||
fontTypesUsed: [],
|
fontTypesUsed: [],
|
||||||
startAt: Date.now()
|
startAt: Date.now(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ class ChromeActions {
|
||||||
onDataAvailable(aRequest, aContext, aDataInputStream, aOffset, aCount) {
|
onDataAvailable(aRequest, aContext, aDataInputStream, aOffset, aCount) {
|
||||||
this.extListener.onDataAvailable(aRequest, aContext, aDataInputStream,
|
this.extListener.onDataAvailable(aRequest, aContext, aDataInputStream,
|
||||||
aOffset, aCount);
|
aOffset, aCount);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
channel.asyncOpen2(listener);
|
channel.asyncOpen2(listener);
|
||||||
|
@ -573,7 +573,7 @@ class RangedChromeActions extends ChromeActions {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.headers[aHeader] = aValue;
|
this.headers[aHeader] = aValue;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if (originalRequest.visitRequestHeaders) {
|
if (originalRequest.visitRequestHeaders) {
|
||||||
originalRequest.visitRequestHeaders(httpHeaderVisitor);
|
originalRequest.visitRequestHeaders(httpHeaderVisitor);
|
||||||
|
@ -670,7 +670,7 @@ class RangedChromeActions extends ChromeActions {
|
||||||
pdfjsLoadAction: "rangeProgress",
|
pdfjsLoadAction: "rangeProgress",
|
||||||
loaded: evt.loaded,
|
loaded: evt.loaded,
|
||||||
}, "*");
|
}, "*");
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,7 +763,8 @@ class RequestListener {
|
||||||
response = function sendResponse(aResponse) {
|
response = function sendResponse(aResponse) {
|
||||||
try {
|
try {
|
||||||
var listener = doc.createEvent("CustomEvent");
|
var listener = doc.createEvent("CustomEvent");
|
||||||
let detail = Cu.cloneInto({ response: aResponse }, doc.defaultView);
|
let detail = Cu.cloneInto({ response: aResponse, },
|
||||||
|
doc.defaultView);
|
||||||
listener.initCustomEvent("pdf.js.response", true, false, detail);
|
listener.initCustomEvent("pdf.js.response", true, false, detail);
|
||||||
return message.dispatchEvent(listener);
|
return message.dispatchEvent(listener);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -996,7 +997,7 @@ PdfStreamConverter.prototype = {
|
||||||
domWindow.frameElement.className === "previewPluginContentFrame";
|
domWindow.frameElement.className === "previewPluginContentFrame";
|
||||||
PdfJsTelemetry.onEmbed(isObjectEmbed);
|
PdfJsTelemetry.onEmbed(isObjectEmbed);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keep the URL the same so the browser sees it as the same.
|
// Keep the URL the same so the browser sees it as the same.
|
||||||
|
@ -1031,6 +1032,6 @@ PdfStreamConverter.prototype = {
|
||||||
}
|
}
|
||||||
delete this.dataListener;
|
delete this.dataListener;
|
||||||
delete this.binaryStream;
|
delete this.binaryStream;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ var PdfjsChromeUtils = {
|
||||||
query: aEvent.detail.query,
|
query: aEvent.detail.query,
|
||||||
caseSensitive: aEvent.detail.caseSensitive,
|
caseSensitive: aEvent.detail.caseSensitive,
|
||||||
highlightAll: aEvent.detail.highlightAll,
|
highlightAll: aEvent.detail.highlightAll,
|
||||||
findPrevious: aEvent.detail.findPrevious
|
findPrevious: aEvent.detail.findPrevious,
|
||||||
};
|
};
|
||||||
|
|
||||||
let browser = aEvent.currentTarget.browser;
|
let browser = aEvent.currentTarget.browser;
|
||||||
|
@ -329,7 +329,7 @@ var PdfjsChromeUtils = {
|
||||||
callback() {
|
callback() {
|
||||||
messageSent = true;
|
messageSent = true;
|
||||||
sendMessage(true);
|
sendMessage(true);
|
||||||
}
|
},
|
||||||
}];
|
}];
|
||||||
notificationBox.appendNotification(data.message, "pdfjs-fallback", null,
|
notificationBox.appendNotification(data.message, "pdfjs-fallback", null,
|
||||||
notificationBox.PRIORITY_INFO_LOW,
|
notificationBox.PRIORITY_INFO_LOW,
|
||||||
|
@ -347,6 +347,6 @@ var PdfjsChromeUtils = {
|
||||||
}
|
}
|
||||||
sendMessage(false);
|
sendMessage(false);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -65,35 +65,35 @@ var PdfjsContentUtils = {
|
||||||
|
|
||||||
clearUserPref(aPrefName) {
|
clearUserPref(aPrefName) {
|
||||||
this._mm.sendSyncMessage("PDFJS:Parent:clearUserPref", {
|
this._mm.sendSyncMessage("PDFJS:Parent:clearUserPref", {
|
||||||
name: aPrefName
|
name: aPrefName,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setIntPref(aPrefName, aPrefValue) {
|
setIntPref(aPrefName, aPrefValue) {
|
||||||
this._mm.sendSyncMessage("PDFJS:Parent:setIntPref", {
|
this._mm.sendSyncMessage("PDFJS:Parent:setIntPref", {
|
||||||
name: aPrefName,
|
name: aPrefName,
|
||||||
value: aPrefValue
|
value: aPrefValue,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setBoolPref(aPrefName, aPrefValue) {
|
setBoolPref(aPrefName, aPrefValue) {
|
||||||
this._mm.sendSyncMessage("PDFJS:Parent:setBoolPref", {
|
this._mm.sendSyncMessage("PDFJS:Parent:setBoolPref", {
|
||||||
name: aPrefName,
|
name: aPrefName,
|
||||||
value: aPrefValue
|
value: aPrefValue,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setCharPref(aPrefName, aPrefValue) {
|
setCharPref(aPrefName, aPrefValue) {
|
||||||
this._mm.sendSyncMessage("PDFJS:Parent:setCharPref", {
|
this._mm.sendSyncMessage("PDFJS:Parent:setCharPref", {
|
||||||
name: aPrefName,
|
name: aPrefName,
|
||||||
value: aPrefValue
|
value: aPrefValue,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setStringPref(aPrefName, aPrefValue) {
|
setStringPref(aPrefName, aPrefValue) {
|
||||||
this._mm.sendSyncMessage("PDFJS:Parent:setStringPref", {
|
this._mm.sendSyncMessage("PDFJS:Parent:setStringPref", {
|
||||||
name: aPrefName,
|
name: aPrefName,
|
||||||
value: aPrefValue
|
value: aPrefValue,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -140,6 +140,6 @@ var PdfjsContentUtils = {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -119,7 +119,7 @@ var FontInspector = (function FontInspectorClosure() {
|
||||||
download.href = url[1];
|
download.href = url[1];
|
||||||
} else if (fontObj.data) {
|
} else if (fontObj.data) {
|
||||||
url = URL.createObjectURL(new Blob([fontObj.data], {
|
url = URL.createObjectURL(new Blob([fontObj.data], {
|
||||||
type: fontObj.mimeType
|
type: fontObj.mimeType,
|
||||||
}));
|
}));
|
||||||
download.href = url;
|
download.href = url;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ var FontInspector = (function FontInspectorClosure() {
|
||||||
resetSelection();
|
resetSelection();
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ var StepperManager = (function StepperManagerClosure() {
|
||||||
saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
|
saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
|
||||||
breakPoints[pageIndex] = bps;
|
breakPoints[pageIndex] = bps;
|
||||||
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
|
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -433,7 +433,7 @@ var Stepper = (function StepperClosure() {
|
||||||
row.style.backgroundColor = null;
|
row.style.backgroundColor = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
return Stepper;
|
return Stepper;
|
||||||
})();
|
})();
|
||||||
|
@ -497,7 +497,7 @@ var Stats = (function Stats() {
|
||||||
cleanup() {
|
cleanup() {
|
||||||
stats = [];
|
stats = [];
|
||||||
clear(this.panel);
|
clear(this.panel);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -615,6 +615,6 @@ window.PDFBug = (function PDFBugClosure() {
|
||||||
tools[j].panel.setAttribute('hidden', 'true');
|
tools[j].panel.setAttribute('hidden', 'true');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Small subset of the webL10n API by Fabien Cazenave for pdf.js extension.
|
|
||||||
(function(window) {
|
|
||||||
var gLanguage = "";
|
|
||||||
var gExternalLocalizerServices = null;
|
|
||||||
var gReadyState = "loading";
|
|
||||||
|
|
||||||
// fetch an l10n objects
|
|
||||||
function getL10nData(key) {
|
|
||||||
var response = gExternalLocalizerServices.getStrings(key);
|
|
||||||
var data = JSON.parse(response);
|
|
||||||
if (!data) {
|
|
||||||
console.warn("[l10n] #" + key + " missing for [" + gLanguage + "]");
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace {{arguments}} with their values
|
|
||||||
function substArguments(text, args) {
|
|
||||||
if (!args) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
return text.replace(/\{\{\s*(\w+)\s*\}\}/g, function(all, name) {
|
|
||||||
return (name in args ? args[name] : "{{" + name + "}}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate a string
|
|
||||||
function translateString(key, args, fallback) {
|
|
||||||
var i = key.lastIndexOf(".");
|
|
||||||
var name, property;
|
|
||||||
if (i >= 0) {
|
|
||||||
name = key.substring(0, i);
|
|
||||||
property = key.substring(i + 1);
|
|
||||||
} else {
|
|
||||||
name = key;
|
|
||||||
property = "textContent";
|
|
||||||
}
|
|
||||||
var data = getL10nData(name);
|
|
||||||
var value = (data && data[property]) || fallback;
|
|
||||||
if (!value) {
|
|
||||||
return "{{" + key + "}}";
|
|
||||||
}
|
|
||||||
return substArguments(value, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate an HTML element
|
|
||||||
function translateElement(element) {
|
|
||||||
if (!element || !element.dataset) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the related l10n object
|
|
||||||
var key = element.dataset.l10nId;
|
|
||||||
var data = getL10nData(key);
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get arguments (if any)
|
|
||||||
// TODO: more flexible parser?
|
|
||||||
var args;
|
|
||||||
if (element.dataset.l10nArgs) {
|
|
||||||
try {
|
|
||||||
args = JSON.parse(element.dataset.l10nArgs);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("[l10n] could not parse arguments for #" + key + "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate element
|
|
||||||
// TODO: security check?
|
|
||||||
for (var k in data) {
|
|
||||||
element[k] = substArguments(data[k], args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// translate an HTML subtree
|
|
||||||
function translateFragment(element) {
|
|
||||||
element = element || document.querySelector("html");
|
|
||||||
|
|
||||||
// check all translatable children (= w/ a `data-l10n-id' attribute)
|
|
||||||
var children = element.querySelectorAll("*[data-l10n-id]");
|
|
||||||
var elementCount = children.length;
|
|
||||||
for (var i = 0; i < elementCount; i++) {
|
|
||||||
translateElement(children[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate element itself if necessary
|
|
||||||
if (element.dataset.l10nId) {
|
|
||||||
translateElement(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function translateDocument() {
|
|
||||||
gLanguage = gExternalLocalizerServices.getLocale();
|
|
||||||
|
|
||||||
translateFragment();
|
|
||||||
|
|
||||||
gReadyState = "complete";
|
|
||||||
|
|
||||||
// fire a 'localized' DOM event
|
|
||||||
var evtObject = document.createEvent("Event");
|
|
||||||
evtObject.initEvent("localized", false, false);
|
|
||||||
evtObject.language = gLanguage;
|
|
||||||
window.dispatchEvent(evtObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", function() {
|
|
||||||
if (gExternalLocalizerServices) {
|
|
||||||
translateDocument();
|
|
||||||
}
|
|
||||||
// ... else see setExternalLocalizerServices below
|
|
||||||
});
|
|
||||||
|
|
||||||
// Public API
|
|
||||||
document.mozL10n = {
|
|
||||||
// get a localized string
|
|
||||||
get: translateString,
|
|
||||||
|
|
||||||
// get the document language
|
|
||||||
getLanguage() {
|
|
||||||
return gLanguage;
|
|
||||||
},
|
|
||||||
|
|
||||||
// get the direction (ltr|rtl) of the current language
|
|
||||||
getDirection() {
|
|
||||||
// http://www.w3.org/International/questions/qa-scripts
|
|
||||||
// Arabic, Hebrew, Farsi, Pashto, Urdu
|
|
||||||
var rtlList = ["ar", "he", "fa", "ps", "ur"];
|
|
||||||
|
|
||||||
// use the short language code for "full" codes like 'ar-sa' (issue 5440)
|
|
||||||
var shortCode = gLanguage.split("-")[0];
|
|
||||||
|
|
||||||
return (rtlList.indexOf(shortCode) >= 0) ? "rtl" : "ltr";
|
|
||||||
},
|
|
||||||
|
|
||||||
getReadyState() {
|
|
||||||
return gReadyState;
|
|
||||||
},
|
|
||||||
|
|
||||||
setExternalLocalizerServices(externalLocalizerServices) {
|
|
||||||
gExternalLocalizerServices = externalLocalizerServices;
|
|
||||||
|
|
||||||
// ... in case if we missed DOMContentLoaded above.
|
|
||||||
if (window.document.readyState === "interactive" ||
|
|
||||||
window.document.readyState === "complete") {
|
|
||||||
translateDocument();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// translate an element or document fragment
|
|
||||||
translate: translateFragment
|
|
||||||
};
|
|
||||||
})(this);
|
|
|
@ -28,7 +28,6 @@ See https://github.com/adobe-type-tools/cmap-resources
|
||||||
|
|
||||||
<!-- This snippet is used in the Firefox extension (included from viewer.html) -->
|
<!-- This snippet is used in the Firefox extension (included from viewer.html) -->
|
||||||
<base href="resource://pdf.js/web/">
|
<base href="resource://pdf.js/web/">
|
||||||
<script src="l10n.js"></script>
|
|
||||||
<script src="../build/pdf.js"></script>
|
<script src="../build/pdf.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,7 +36,6 @@ See https://github.com/adobe-type-tools/cmap-resources
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="viewer.js"></script>
|
<script src="viewer.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,9 +8,6 @@ Library('icuuc')
|
||||||
FINAL_LIBRARY = 'icu'
|
FINAL_LIBRARY = 'icu'
|
||||||
|
|
||||||
DEFINES['U_COMMON_IMPLEMENTATION'] = True
|
DEFINES['U_COMMON_IMPLEMENTATION'] = True
|
||||||
# This normally gets defined in the SDK but our WINVER is too low.
|
|
||||||
#FIXME: should probably stop including mozilla-config.h
|
|
||||||
DEFINES['LOCALE_SNAME'] = 0x5c
|
|
||||||
|
|
||||||
LOCAL_INCLUDES += ['/intl/icu/source/i18n']
|
LOCAL_INCLUDES += ['/intl/icu/source/i18n']
|
||||||
|
|
||||||
|
|
|
@ -330,6 +330,7 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
||||||
nsIFrame* targetFrame = target->GetPrimaryFrame();
|
nsIFrame* targetFrame = target->GetPrimaryFrame();
|
||||||
nsRect targetRect;
|
nsRect targetRect;
|
||||||
Maybe<nsRect> intersectionRect;
|
Maybe<nsRect> intersectionRect;
|
||||||
|
bool isSameDoc = root && root->GetComposedDoc() == target->GetComposedDoc();
|
||||||
|
|
||||||
if (rootFrame && targetFrame) {
|
if (rootFrame && targetFrame) {
|
||||||
// If mRoot is set we are testing intersection with a container element
|
// If mRoot is set we are testing intersection with a container element
|
||||||
|
@ -338,7 +339,7 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
||||||
// Skip further processing of this target if it is not in the same
|
// Skip further processing of this target if it is not in the same
|
||||||
// Document as the intersection root, e.g. if root is an element of
|
// Document as the intersection root, e.g. if root is an element of
|
||||||
// the main document and target an element from an embedded iframe.
|
// the main document and target an element from an embedded iframe.
|
||||||
if (target->GetComposedDoc() != root->GetComposedDoc()) {
|
if (!isSameDoc) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Skip further processing of this target if is not a descendant of the
|
// Skip further processing of this target if is not a descendant of the
|
||||||
|
@ -410,12 +411,12 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
||||||
intersectionRectRelativeToRoot,
|
intersectionRectRelativeToRoot,
|
||||||
rootIntersectionRect
|
rootIntersectionRect
|
||||||
);
|
);
|
||||||
if (intersectionRect.isSome()) {
|
if (intersectionRect.isSome() && !isSameDoc) {
|
||||||
intersectionRect = Some(nsLayoutUtils::TransformFrameRectToAncestor(
|
nsRect rect = intersectionRect.value();
|
||||||
nsLayoutUtils::GetContainingBlockForClientRect(rootFrame),
|
nsPresContext* presContext = targetFrame->PresContext();
|
||||||
intersectionRect.value(),
|
nsLayoutUtils::TransformRect(rootFrame,
|
||||||
targetFrame->PresContext()->PresShell()->GetRootScrollFrame()
|
presContext->PresShell()->GetRootScrollFrame(), rect);
|
||||||
));
|
intersectionRect = Some(rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "mozilla/dom/StructuredCloneBlob.h"
|
||||||
|
|
||||||
|
#include "js/StructuredClone.h"
|
||||||
|
#include "js/Utility.h"
|
||||||
|
#include "jswrapper.h"
|
||||||
|
|
||||||
|
#include "xpcpublic.h"
|
||||||
|
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
#include "mozilla/UniquePtr.h"
|
||||||
|
#include "mozilla/dom/StructuredCloneTags.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
StructuredCloneBlob::StructuredCloneBlob()
|
||||||
|
: StructuredCloneHolder(CloningSupported, TransferringNotSupported,
|
||||||
|
StructuredCloneScope::DifferentProcess)
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
/* static */ already_AddRefed<StructuredCloneBlob>
|
||||||
|
StructuredCloneBlob::Constructor(GlobalObject& aGlobal, JS::HandleValue aValue,
|
||||||
|
JS::HandleObject aTargetGlobal,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
JSContext* cx = aGlobal.Context();
|
||||||
|
|
||||||
|
RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob();
|
||||||
|
|
||||||
|
Maybe<JSAutoCompartment> ac;
|
||||||
|
JS::RootedValue value(cx, aValue);
|
||||||
|
|
||||||
|
if (aTargetGlobal) {
|
||||||
|
ac.emplace(cx, aTargetGlobal);
|
||||||
|
|
||||||
|
if (!JS_WrapValue(cx, &value)) {
|
||||||
|
aRv.NoteJSContextException(cx);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else if (value.isObject()) {
|
||||||
|
JS::RootedObject obj(cx, js::CheckedUnwrap(&value.toObject()));
|
||||||
|
if (!obj) {
|
||||||
|
js::ReportAccessDenied(cx);
|
||||||
|
aRv.NoteJSContextException(cx);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ac.emplace(cx, obj);
|
||||||
|
value = JS::ObjectValue(*obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder->Write(cx, value, aRv);
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return holder.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StructuredCloneBlob::Deserialize(JSContext* aCx, JS::HandleObject aTargetScope,
|
||||||
|
JS::MutableHandleValue aResult, ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
JS::RootedObject scope(aCx, js::CheckedUnwrap(aTargetScope));
|
||||||
|
if (!scope) {
|
||||||
|
js::ReportAccessDenied(aCx);
|
||||||
|
aRv.NoteJSContextException(aCx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JSAutoCompartment ac(aCx, scope);
|
||||||
|
|
||||||
|
Read(xpc::NativeGlobal(scope), aCx, aResult, aRv);
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!JS_WrapValue(aCx, aResult)) {
|
||||||
|
aResult.set(JS::UndefinedValue());
|
||||||
|
aRv.NoteJSContextException(aCx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* static */ JSObject*
|
||||||
|
StructuredCloneBlob::ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader)
|
||||||
|
{
|
||||||
|
JS::RootedObject obj(aCx);
|
||||||
|
{
|
||||||
|
RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob();
|
||||||
|
|
||||||
|
if (!holder->ReadStructuredCloneInternal(aCx, aReader) ||
|
||||||
|
!holder->WrapObject(aCx, nullptr, &obj)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StructuredCloneBlob::ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader)
|
||||||
|
{
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t version;
|
||||||
|
if (!JS_ReadUint32Pair(aReader, &length, &version)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSStructuredCloneData data(length, length, 4096);
|
||||||
|
if (!JS_ReadBytes(aReader, data.Start(), length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope,
|
||||||
|
&StructuredCloneHolder::sCallbacks,
|
||||||
|
this);
|
||||||
|
mBuffer->adopt(Move(data), version, &StructuredCloneHolder::sCallbacks);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StructuredCloneBlob::WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter)
|
||||||
|
{
|
||||||
|
auto& data = mBuffer->data();
|
||||||
|
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_STRUCTURED_CLONE_HOLDER, 0) ||
|
||||||
|
!JS_WriteUint32Pair(aWriter, data.Size(), JS_STRUCTURED_CLONE_VERSION)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = data.Iter();
|
||||||
|
while (!iter.Done()) {
|
||||||
|
if (!JS_WriteBytes(aWriter, iter.Data(), iter.RemainingInSegment())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
iter.Advance(data, iter.RemainingInSegment());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StructuredCloneBlob::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto, JS::MutableHandleObject aResult)
|
||||||
|
{
|
||||||
|
return StructuredCloneHolderBinding::Wrap(aCx, this, aGivenProto, aResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_StructuredCloneBlob_h
|
||||||
|
#define mozilla_dom_StructuredCloneBlob_h
|
||||||
|
|
||||||
|
#include "mozilla/dom/BindingDeclarations.h"
|
||||||
|
#include "mozilla/dom/StructuredCloneHolder.h"
|
||||||
|
#include "mozilla/dom/StructuredCloneHolderBinding.h"
|
||||||
|
#include "mozilla/RefCounted.h"
|
||||||
|
|
||||||
|
#include "jsapi.h"
|
||||||
|
|
||||||
|
#include "nsISupports.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class StructuredCloneBlob : public StructuredCloneHolder
|
||||||
|
, public RefCounted<StructuredCloneBlob>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MOZ_DECLARE_REFCOUNTED_TYPENAME(StructuredCloneBlob)
|
||||||
|
|
||||||
|
explicit StructuredCloneBlob();
|
||||||
|
|
||||||
|
static JSObject* ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader);
|
||||||
|
bool WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter);
|
||||||
|
|
||||||
|
static already_AddRefed<StructuredCloneBlob>
|
||||||
|
Constructor(GlobalObject& aGlobal, JS::HandleValue aValue, JS::HandleObject aTargetGlobal, ErrorResult& aRv);
|
||||||
|
|
||||||
|
void Deserialize(JSContext* aCx, JS::HandleObject aTargetScope,
|
||||||
|
JS::MutableHandleValue aResult, ErrorResult& aRv);
|
||||||
|
|
||||||
|
nsISupports* GetParentObject() const { return nullptr; }
|
||||||
|
JSObject* GetWrapper() const { return nullptr; }
|
||||||
|
|
||||||
|
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandleObject aResult);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <typename T, detail::RefCountAtomicity>
|
||||||
|
friend class detail::RefCounted;
|
||||||
|
|
||||||
|
~StructuredCloneBlob() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_StructuredCloneBlob_h
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "mozilla/AutoRestore.h"
|
#include "mozilla/AutoRestore.h"
|
||||||
#include "mozilla/dom/BlobBinding.h"
|
#include "mozilla/dom/BlobBinding.h"
|
||||||
#include "mozilla/dom/CryptoKey.h"
|
#include "mozilla/dom/CryptoKey.h"
|
||||||
|
#include "mozilla/dom/StructuredCloneBlob.h"
|
||||||
#include "mozilla/dom/Directory.h"
|
#include "mozilla/dom/Directory.h"
|
||||||
#include "mozilla/dom/DirectoryBinding.h"
|
#include "mozilla/dom/DirectoryBinding.h"
|
||||||
#include "mozilla/dom/File.h"
|
#include "mozilla/dom/File.h"
|
||||||
|
@ -357,6 +358,10 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
|
||||||
return ReadStructuredCloneImageData(aCx, aReader);
|
return ReadStructuredCloneImageData(aCx, aReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) {
|
||||||
|
return StructuredCloneBlob::ReadStructuredClone(aCx, aReader);
|
||||||
|
}
|
||||||
|
|
||||||
if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS) {
|
if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS) {
|
||||||
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||||
if (!global) {
|
if (!global) {
|
||||||
|
@ -453,6 +458,14 @@ StructuredCloneHolder::WriteFullySerializableObjects(JSContext* aCx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if this is a StructuredCloneBlob object.
|
||||||
|
{
|
||||||
|
StructuredCloneBlob* holder = nullptr;
|
||||||
|
if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, aObj, holder))) {
|
||||||
|
return holder->WriteStructuredClone(aCx, aWriter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle URLSearchParams cloning
|
// Handle URLSearchParams cloning
|
||||||
{
|
{
|
||||||
URLSearchParams* usp = nullptr;
|
URLSearchParams* usp = nullptr;
|
||||||
|
|
|
@ -62,6 +62,8 @@ enum StructuredCloneTags {
|
||||||
|
|
||||||
SCTAG_DOM_INPUTSTREAM,
|
SCTAG_DOM_INPUTSTREAM,
|
||||||
|
|
||||||
|
SCTAG_DOM_STRUCTURED_CLONE_HOLDER,
|
||||||
|
|
||||||
// When adding a new tag for IDB, please don't add it to the end of the list!
|
// When adding a new tag for IDB, please don't add it to the end of the list!
|
||||||
// Tags that are supported by IDB must not ever change. See the static assert
|
// Tags that are supported by IDB must not ever change. See the static assert
|
||||||
// in IDBObjectStore.cpp, method CommonStructuredCloneReadCallback.
|
// in IDBObjectStore.cpp, method CommonStructuredCloneReadCallback.
|
||||||
|
|
|
@ -196,6 +196,7 @@ EXPORTS.mozilla.dom += [
|
||||||
'SameProcessMessageQueue.h',
|
'SameProcessMessageQueue.h',
|
||||||
'ScreenOrientation.h',
|
'ScreenOrientation.h',
|
||||||
'ShadowRoot.h',
|
'ShadowRoot.h',
|
||||||
|
'StructuredCloneBlob.h',
|
||||||
'StructuredCloneHolder.h',
|
'StructuredCloneHolder.h',
|
||||||
'StructuredCloneTags.h',
|
'StructuredCloneTags.h',
|
||||||
'StyleSheetList.h',
|
'StyleSheetList.h',
|
||||||
|
@ -338,6 +339,7 @@ UNIFIED_SOURCES += [
|
||||||
'SameProcessMessageQueue.cpp',
|
'SameProcessMessageQueue.cpp',
|
||||||
'ScreenOrientation.cpp',
|
'ScreenOrientation.cpp',
|
||||||
'ShadowRoot.cpp',
|
'ShadowRoot.cpp',
|
||||||
|
'StructuredCloneBlob.cpp',
|
||||||
'StructuredCloneHolder.cpp',
|
'StructuredCloneHolder.cpp',
|
||||||
'StyleSheetList.cpp',
|
'StyleSheetList.cpp',
|
||||||
'SubtleCrypto.cpp',
|
'SubtleCrypto.cpp',
|
||||||
|
|
|
@ -3893,6 +3893,10 @@ nsDOMWindowUtils::GetContentAPZTestData(JSContext* aContext,
|
||||||
if (!clm->GetAPZTestData().ToJS(aOutContentTestData, aContext)) {
|
if (!clm->GetAPZTestData().ToJS(aOutContentTestData, aContext)) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
} else if (WebRenderLayerManager* wrlm = lm->AsWebRenderLayerManager()) {
|
||||||
|
if (!wrlm->GetAPZTestData().ToJS(aOutContentTestData, aContext)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3908,13 +3912,20 @@ nsDOMWindowUtils::GetCompositorAPZTestData(JSContext* aContext,
|
||||||
if (!lm) {
|
if (!lm) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
APZTestData compositorSideData;
|
||||||
if (ClientLayerManager* clm = lm->AsClientLayerManager()) {
|
if (ClientLayerManager* clm = lm->AsClientLayerManager()) {
|
||||||
APZTestData compositorSideData;
|
|
||||||
clm->GetCompositorSideAPZTestData(&compositorSideData);
|
clm->GetCompositorSideAPZTestData(&compositorSideData);
|
||||||
if (!compositorSideData.ToJS(aOutCompositorTestData, aContext)) {
|
} else if (WebRenderLayerManager* wrlm = lm->AsWebRenderLayerManager()) {
|
||||||
|
if (!wrlm->WrBridge()) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
if (!wrlm->WrBridge()->SendGetAPZTestData(&compositorSideData)) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!compositorSideData.ToJS(aOutCompositorTestData, aContext)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
const global = this;
|
||||||
|
|
||||||
|
add_task(async function test_structuredCloneHolder() {
|
||||||
|
let principal = Services.scriptSecurityManager.createCodebasePrincipal(
|
||||||
|
Services.io.newURI("http://example.com/"), {});
|
||||||
|
|
||||||
|
let sandbox = Cu.Sandbox(principal);
|
||||||
|
|
||||||
|
const obj = {foo: [{bar: "baz"}]};
|
||||||
|
|
||||||
|
let holder = new StructuredCloneHolder(obj);
|
||||||
|
|
||||||
|
|
||||||
|
// Test same-compartment deserialization
|
||||||
|
|
||||||
|
let res = holder.deserialize(global);
|
||||||
|
|
||||||
|
notEqual(res, obj, "Deserialized result is a different object from the original");
|
||||||
|
|
||||||
|
deepEqual(res, obj, "Deserialized result is deeply equivalent to the original");
|
||||||
|
|
||||||
|
equal(Cu.getObjectPrincipal(res), Cu.getObjectPrincipal(global),
|
||||||
|
"Deserialized result has the correct principal");
|
||||||
|
|
||||||
|
|
||||||
|
// Test non-object-value round-trip.
|
||||||
|
|
||||||
|
equal(new StructuredCloneHolder("foo").deserialize(global), "foo",
|
||||||
|
"Round-tripping non-object values works as expected");
|
||||||
|
|
||||||
|
|
||||||
|
// Test cross-compartment deserialization
|
||||||
|
|
||||||
|
res = holder.deserialize(sandbox);
|
||||||
|
|
||||||
|
notEqual(res, obj, "Cross-compartment-deserialized result is a different object from the original");
|
||||||
|
|
||||||
|
deepEqual(res, obj, "Cross-compartment-deserialized result is deeply equivalent to the original");
|
||||||
|
|
||||||
|
equal(Cu.getObjectPrincipal(res), principal,
|
||||||
|
"Cross-compartment-deserialized result has the correct principal");
|
||||||
|
|
||||||
|
|
||||||
|
// Test message manager transportability
|
||||||
|
|
||||||
|
const MSG = "StructuredCloneHolder";
|
||||||
|
|
||||||
|
let resultPromise = new Promise(resolve => {
|
||||||
|
Services.ppmm.addMessageListener(MSG, resolve);
|
||||||
|
});
|
||||||
|
|
||||||
|
Services.cpmm.sendAsyncMessage(MSG, holder);
|
||||||
|
|
||||||
|
res = await resultPromise;
|
||||||
|
|
||||||
|
ok(res.data instanceof StructuredCloneHolder,
|
||||||
|
"Sending structured clone holders through message managers works as expected");
|
||||||
|
|
||||||
|
deepEqual(res.data.deserialize(global), obj,
|
||||||
|
"Sending structured clone holders through message managers works as expected");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test that X-rays passed to an exported function are serialized
|
||||||
|
// through their exported wrappers.
|
||||||
|
add_task(async function test_structuredCloneHolder_xray() {
|
||||||
|
let principal = Services.scriptSecurityManager.createCodebasePrincipal(
|
||||||
|
Services.io.newURI("http://example.com/"), {});
|
||||||
|
|
||||||
|
let sandbox1 = Cu.Sandbox(principal, {wantXrays: true});
|
||||||
|
|
||||||
|
let sandbox2 = Cu.Sandbox(principal, {wantXrays: true});
|
||||||
|
Cu.evalInSandbox(`this.x = {y: "z", get z() { return "q" }}`, sandbox2);
|
||||||
|
|
||||||
|
sandbox1.x = sandbox2.x;
|
||||||
|
|
||||||
|
let holder;
|
||||||
|
Cu.exportFunction(function serialize(val) {
|
||||||
|
holder = new StructuredCloneHolder(val, sandbox1);
|
||||||
|
}, sandbox1, {defineAs: "serialize"});
|
||||||
|
|
||||||
|
Cu.evalInSandbox(`serialize(x)`, sandbox1);
|
||||||
|
|
||||||
|
const obj = {y: "z"};
|
||||||
|
|
||||||
|
let res = holder.deserialize(global);
|
||||||
|
|
||||||
|
deepEqual(res, obj, "Deserialized result is deeply equivalent to the expected object");
|
||||||
|
deepEqual(res, sandbox2.x, "Deserialized result is deeply equivalent to the X-ray-wrapped object");
|
||||||
|
|
||||||
|
equal(Cu.getObjectPrincipal(res), Cu.getObjectPrincipal(global),
|
||||||
|
"Deserialized result has the correct principal");
|
||||||
|
});
|
|
@ -38,6 +38,7 @@ head = head_xml.js
|
||||||
head = head_xml.js
|
head = head_xml.js
|
||||||
[test_range.js]
|
[test_range.js]
|
||||||
head = head_xml.js
|
head = head_xml.js
|
||||||
|
[test_structuredcloneholder.js]
|
||||||
[test_thirdpartyutil.js]
|
[test_thirdpartyutil.js]
|
||||||
[test_treewalker.js]
|
[test_treewalker.js]
|
||||||
head = head_xml.js
|
head = head_xml.js
|
||||||
|
|
|
@ -798,6 +798,11 @@ DOMInterfaces = {
|
||||||
'implicitJSContext': [ 'close' ],
|
'implicitJSContext': [ 'close' ],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'StructuredCloneHolder': {
|
||||||
|
'nativeType': 'mozilla::dom::StructuredCloneBlob',
|
||||||
|
'wrapperCache': False,
|
||||||
|
},
|
||||||
|
|
||||||
'StyleSheet': {
|
'StyleSheet': {
|
||||||
'nativeType': 'mozilla::StyleSheet',
|
'nativeType': 'mozilla::StyleSheet',
|
||||||
'headerFile': 'mozilla/StyleSheetInlines.h',
|
'headerFile': 'mozilla/StyleSheetInlines.h',
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A holder for structured-clonable data which can itself be cloned with
|
||||||
|
* little overhead, and deserialized into an arbitrary global.
|
||||||
|
*/
|
||||||
|
[ChromeOnly, Exposed=(Window,System,Worker),
|
||||||
|
/**
|
||||||
|
* Serializes the given value to an opaque structured clone blob, and
|
||||||
|
* returns the result.
|
||||||
|
*
|
||||||
|
* The serialization happens in the compartment of the given global or, if no
|
||||||
|
* global is provided, the compartment of the data value.
|
||||||
|
*/
|
||||||
|
Constructor(any data, optional object? global = null)]
|
||||||
|
interface StructuredCloneHolder {
|
||||||
|
/**
|
||||||
|
* Deserializes the structured clone data in the scope of the given global,
|
||||||
|
* and returns the result.
|
||||||
|
*/
|
||||||
|
[Throws]
|
||||||
|
any deserialize(object global);
|
||||||
|
};
|
|
@ -790,6 +790,7 @@ WEBIDL_FILES = [
|
||||||
'StorageEvent.webidl',
|
'StorageEvent.webidl',
|
||||||
'StorageManager.webidl',
|
'StorageManager.webidl',
|
||||||
'StorageType.webidl',
|
'StorageType.webidl',
|
||||||
|
'StructuredCloneHolder.webidl',
|
||||||
'StyleSheet.webidl',
|
'StyleSheet.webidl',
|
||||||
'StyleSheetList.webidl',
|
'StyleSheetList.webidl',
|
||||||
'SubtleCrypto.webidl',
|
'SubtleCrypto.webidl',
|
||||||
|
|
|
@ -1334,9 +1334,10 @@ CompositorBridgeParent::FlushApzRepaints(const uint64_t& aLayersId)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CompositorBridgeParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
CompositorBridgeParent::GetAPZTestData(const uint64_t& aLayersId,
|
||||||
APZTestData* aOutData)
|
APZTestData* aOutData)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(aLayersId == 0 || aLayersId == mRootLayerTreeID);
|
||||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||||
*aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
|
*aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ public:
|
||||||
virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
|
virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
|
||||||
virtual CompositorAnimationStorage* GetAnimationStorage(const uint64_t& aId) { return nullptr; }
|
virtual CompositorAnimationStorage* GetAnimationStorage(const uint64_t& aId) { return nullptr; }
|
||||||
virtual void FlushApzRepaints(const uint64_t& aLayersId) = 0;
|
virtual void FlushApzRepaints(const uint64_t& aLayersId) = 0;
|
||||||
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
virtual void GetAPZTestData(const uint64_t& aLayersId,
|
||||||
APZTestData* aOutData) { }
|
APZTestData* aOutData) { }
|
||||||
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
||||||
const uint64_t& aInputBlockId,
|
const uint64_t& aInputBlockId,
|
||||||
|
@ -232,7 +232,7 @@ public:
|
||||||
override;
|
override;
|
||||||
virtual CompositorAnimationStorage* GetAnimationStorage(const uint64_t& aId) override;
|
virtual CompositorAnimationStorage* GetAnimationStorage(const uint64_t& aId) override;
|
||||||
virtual void FlushApzRepaints(const uint64_t& aLayersId) override;
|
virtual void FlushApzRepaints(const uint64_t& aLayersId) override;
|
||||||
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
virtual void GetAPZTestData(const uint64_t& aLayersId,
|
||||||
APZTestData* aOutData) override;
|
APZTestData* aOutData) override;
|
||||||
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
||||||
const uint64_t& aInputBlockId,
|
const uint64_t& aInputBlockId,
|
||||||
|
|
|
@ -437,13 +437,12 @@ CrossProcessCompositorBridgeParent::FlushApzRepaints(const uint64_t& aLayersId)
|
||||||
|
|
||||||
void
|
void
|
||||||
CrossProcessCompositorBridgeParent::GetAPZTestData(
|
CrossProcessCompositorBridgeParent::GetAPZTestData(
|
||||||
const LayerTransactionParent* aLayerTree,
|
const uint64_t& aLayersId,
|
||||||
APZTestData* aOutData)
|
APZTestData* aOutData)
|
||||||
{
|
{
|
||||||
uint64_t id = aLayerTree->GetId();
|
MOZ_ASSERT(aLayersId != 0);
|
||||||
MOZ_ASSERT(id != 0);
|
|
||||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||||
*aOutData = sIndirectLayerTrees[id].mApzTestData;
|
*aOutData = sIndirectLayerTrees[aLayersId].mApzTestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -105,7 +105,7 @@ public:
|
||||||
virtual CompositorAnimationStorage*
|
virtual CompositorAnimationStorage*
|
||||||
GetAnimationStorage(const uint64_t& aId) override;
|
GetAnimationStorage(const uint64_t& aId) override;
|
||||||
virtual void FlushApzRepaints(const uint64_t& aLayersId) override;
|
virtual void FlushApzRepaints(const uint64_t& aLayersId) override;
|
||||||
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
virtual void GetAPZTestData(const uint64_t& aLayersId,
|
||||||
APZTestData* aOutData) override;
|
APZTestData* aOutData) override;
|
||||||
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
||||||
const uint64_t& aInputBlockId,
|
const uint64_t& aInputBlockId,
|
||||||
|
|
|
@ -849,7 +849,7 @@ LayerTransactionParent::RecvFlushApzRepaints()
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData)
|
LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData)
|
||||||
{
|
{
|
||||||
mCompositorBridge->GetAPZTestData(this, aOutData);
|
mCompositorBridge->GetAPZTestData(GetId(), aOutData);
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ include WebRenderMessages;
|
||||||
include protocol PCompositorBridge;
|
include protocol PCompositorBridge;
|
||||||
include protocol PTexture;
|
include protocol PTexture;
|
||||||
|
|
||||||
|
using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
|
||||||
using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
|
using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
|
||||||
using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
|
using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
|
||||||
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
|
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
|
||||||
|
@ -76,6 +77,7 @@ parent:
|
||||||
sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
|
sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
|
||||||
sync SetAsyncZoom(ViewID scrollId, float zoom);
|
sync SetAsyncZoom(ViewID scrollId, float zoom);
|
||||||
async FlushApzRepaints();
|
async FlushApzRepaints();
|
||||||
|
sync GetAPZTestData() returns (APZTestData data);
|
||||||
|
|
||||||
async Shutdown();
|
async Shutdown();
|
||||||
child:
|
child:
|
||||||
|
|
|
@ -371,7 +371,7 @@ WebRenderBridgeParent::UpdateAPZ()
|
||||||
if (RefPtr<APZCTreeManager> apzc = cbp->GetAPZCTreeManager()) {
|
if (RefPtr<APZCTreeManager> apzc = cbp->GetAPZCTreeManager()) {
|
||||||
apzc->UpdateHitTestingTree(rootLayersId, rootWrbp->GetScrollData(),
|
apzc->UpdateHitTestingTree(rootLayersId, rootWrbp->GetScrollData(),
|
||||||
mScrollData.IsFirstPaint(), GetLayersId(),
|
mScrollData.IsFirstPaint(), GetLayersId(),
|
||||||
/* TODO: propagate paint sequence number */ 0);
|
mScrollData.GetPaintSequenceNumber());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +413,9 @@ WebRenderBridgeParent::RecvDPEnd(const gfx::IntSize& aSize,
|
||||||
const WrBuiltDisplayListDescriptor& dlDesc,
|
const WrBuiltDisplayListDescriptor& dlDesc,
|
||||||
const WebRenderScrollData& aScrollData)
|
const WebRenderScrollData& aScrollData)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
||||||
aContentSize, dl, dlDesc, aScrollData);
|
aContentSize, dl, dlDesc, aScrollData);
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
|
@ -429,6 +432,9 @@ WebRenderBridgeParent::RecvDPSyncEnd(const gfx::IntSize &aSize,
|
||||||
const WrBuiltDisplayListDescriptor& dlDesc,
|
const WrBuiltDisplayListDescriptor& dlDesc,
|
||||||
const WebRenderScrollData& aScrollData)
|
const WebRenderScrollData& aScrollData)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
||||||
aContentSize, dl, dlDesc, aScrollData);
|
aContentSize, dl, dlDesc, aScrollData);
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
|
@ -695,6 +701,9 @@ WebRenderBridgeParent::RecvRemoveExternalImageId(const ExternalImageId& aImageId
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
|
WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
mChildLayerObserverEpoch = aLayerObserverEpoch;
|
mChildLayerObserverEpoch = aLayerObserverEpoch;
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
@ -702,6 +711,9 @@ WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverE
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvClearCachedResources()
|
WebRenderBridgeParent::RecvClearCachedResources()
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), false);
|
mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), false);
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
@ -732,6 +744,9 @@ mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
|
WebRenderBridgeParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
|
||||||
nsTArray<ScrollableLayerGuid>&& aTargets)
|
nsTArray<ScrollableLayerGuid>&& aTargets)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
mCompositorBridge->SetConfirmedTargetAPZC(GetLayersId(), aBlockId, aTargets);
|
mCompositorBridge->SetConfirmedTargetAPZC(GetLayersId(), aBlockId, aTargets);
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
@ -741,6 +756,9 @@ WebRenderBridgeParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScr
|
||||||
const float& aX,
|
const float& aX,
|
||||||
const float& aY)
|
const float& aY)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aScrollId);
|
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aScrollId);
|
||||||
if (!apzc) {
|
if (!apzc) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
@ -753,6 +771,9 @@ mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
WebRenderBridgeParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
||||||
const float& aZoom)
|
const float& aZoom)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aScrollId);
|
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aScrollId);
|
||||||
if (!apzc) {
|
if (!apzc) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
@ -764,10 +785,20 @@ WebRenderBridgeParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvFlushApzRepaints()
|
WebRenderBridgeParent::RecvFlushApzRepaints()
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
mCompositorBridge->FlushApzRepaints(GetLayersId());
|
mCompositorBridge->FlushApzRepaints(GetLayersId());
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
WebRenderBridgeParent::RecvGetAPZTestData(APZTestData* aOutData)
|
||||||
|
{
|
||||||
|
mCompositorBridge->GetAPZTestData(GetLayersId(), aOutData);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
|
WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||||
{
|
{
|
||||||
|
@ -1067,6 +1098,9 @@ mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle,
|
WebRenderBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle,
|
||||||
const TextureInfo& aInfo)
|
const TextureInfo& aInfo)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
if (!AddCompositable(aHandle, aInfo)) {
|
if (!AddCompositable(aHandle, aInfo)) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
}
|
}
|
||||||
|
@ -1076,6 +1110,9 @@ WebRenderBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle,
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle)
|
WebRenderBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
ReleaseCompositable(aHandle);
|
ReleaseCompositable(aHandle);
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
@ -1083,6 +1120,9 @@ WebRenderBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
WebRenderBridgeParent::RecvInitReadLocks(ReadLockArray&& aReadLocks)
|
WebRenderBridgeParent::RecvInitReadLocks(ReadLockArray&& aReadLocks)
|
||||||
{
|
{
|
||||||
|
if (mDestroyed) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
if (!AddReadLocks(Move(aReadLocks))) {
|
if (!AddReadLocks(Move(aReadLocks))) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ public:
|
||||||
mozilla::ipc::IPCResult RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
mozilla::ipc::IPCResult RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
||||||
const float& aZoom) override;
|
const float& aZoom) override;
|
||||||
mozilla::ipc::IPCResult RecvFlushApzRepaints() override;
|
mozilla::ipc::IPCResult RecvFlushApzRepaints() override;
|
||||||
|
mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* data) override;
|
||||||
|
|
||||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||||
void SetWebRenderProfilerEnabled(bool aEnabled);
|
void SetWebRenderProfilerEnabled(bool aEnabled);
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace layers {
|
||||||
WebRenderCompositableHolder::AsyncImagePipelineHolder::AsyncImagePipelineHolder()
|
WebRenderCompositableHolder::AsyncImagePipelineHolder::AsyncImagePipelineHolder()
|
||||||
: mInitialised(false)
|
: mInitialised(false)
|
||||||
, mIsChanged(false)
|
, mIsChanged(false)
|
||||||
|
, mUseExternalImage(false)
|
||||||
, mFilter(WrImageRendering::Auto)
|
, mFilter(WrImageRendering::Auto)
|
||||||
, mMixBlendMode(WrMixBlendMode::Normal)
|
, mMixBlendMode(WrMixBlendMode::Normal)
|
||||||
{}
|
{}
|
||||||
|
@ -293,6 +294,7 @@ WebRenderCompositableHolder::UpdateImageKeys(wr::WebRenderAPI* aApi,
|
||||||
if (texture == aHolder->mCurrentTexture) {
|
if (texture == aHolder->mCurrentTexture) {
|
||||||
// Reuse previous ImageKeys.
|
// Reuse previous ImageKeys.
|
||||||
aKeys.AppendElements(aHolder->mKeys);
|
aKeys.AppendElements(aHolder->mKeys);
|
||||||
|
aUseExternalImage = aHolder->mUseExternalImage;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +308,7 @@ WebRenderCompositableHolder::UpdateImageKeys(wr::WebRenderAPI* aApi,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
aUseExternalImage = GetImageKeyForTextureHost(aApi, texture, aKeys);
|
aUseExternalImage = aHolder->mUseExternalImage = GetImageKeyForTextureHost(aApi, texture, aKeys);
|
||||||
MOZ_ASSERT(!aKeys.IsEmpty());
|
MOZ_ASSERT(!aKeys.IsEmpty());
|
||||||
aHolder->mKeys.AppendElements(aKeys);
|
aHolder->mKeys.AppendElements(aKeys);
|
||||||
aHolder->mCurrentTexture = texture;
|
aHolder->mCurrentTexture = texture;
|
||||||
|
|
|
@ -122,6 +122,7 @@ private:
|
||||||
|
|
||||||
bool mInitialised;
|
bool mInitialised;
|
||||||
bool mIsChanged;
|
bool mIsChanged;
|
||||||
|
bool mUseExternalImage;
|
||||||
LayerRect mScBounds;
|
LayerRect mScBounds;
|
||||||
gfx::Matrix4x4 mScTransform;
|
gfx::Matrix4x4 mScTransform;
|
||||||
gfx::MaybeIntSize mScaleToSize;
|
gfx::MaybeIntSize mScaleToSize;
|
||||||
|
|
|
@ -32,6 +32,7 @@ WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
|
||||||
, mNeedsComposite(false)
|
, mNeedsComposite(false)
|
||||||
, mIsFirstPaint(false)
|
, mIsFirstPaint(false)
|
||||||
, mTarget(nullptr)
|
, mTarget(nullptr)
|
||||||
|
, mPaintSequenceNumber(0)
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(WebRenderLayerManager);
|
MOZ_COUNT_CTOR(WebRenderLayerManager);
|
||||||
}
|
}
|
||||||
|
@ -124,6 +125,13 @@ WebRenderLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
|
||||||
bool
|
bool
|
||||||
WebRenderLayerManager::BeginTransaction()
|
WebRenderLayerManager::BeginTransaction()
|
||||||
{
|
{
|
||||||
|
// Increment the paint sequence number even if test logging isn't
|
||||||
|
// enabled in this process; it may be enabled in the parent process,
|
||||||
|
// and the parent process expects unique sequence numbers.
|
||||||
|
++mPaintSequenceNumber;
|
||||||
|
if (gfxPrefs::APZTestLoggingEnabled()) {
|
||||||
|
mApzTestData.StartNewPaint(mPaintSequenceNumber);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +217,7 @@ WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback
|
||||||
scrollData.SetIsFirstPaint();
|
scrollData.SetIsFirstPaint();
|
||||||
mIsFirstPaint = false;
|
mIsFirstPaint = false;
|
||||||
}
|
}
|
||||||
|
scrollData.SetPaintSequenceNumber(mPaintSequenceNumber);
|
||||||
if (mRoot) {
|
if (mRoot) {
|
||||||
PopulateScrollData(scrollData, mRoot.get());
|
PopulateScrollData(scrollData, mRoot.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "Layers.h"
|
#include "Layers.h"
|
||||||
#include "mozilla/MozPromise.h"
|
#include "mozilla/MozPromise.h"
|
||||||
|
#include "mozilla/layers/APZTestData.h"
|
||||||
#include "mozilla/layers/TransactionIdAllocator.h"
|
#include "mozilla/layers/TransactionIdAllocator.h"
|
||||||
#include "mozilla/webrender/WebRenderTypes.h"
|
#include "mozilla/webrender/WebRenderTypes.h"
|
||||||
|
|
||||||
|
@ -126,6 +127,16 @@ public:
|
||||||
void SetTransactionIncomplete() { mTransactionIncomplete = true; }
|
void SetTransactionIncomplete() { mTransactionIncomplete = true; }
|
||||||
bool IsMutatedLayer(Layer* aLayer);
|
bool IsMutatedLayer(Layer* aLayer);
|
||||||
|
|
||||||
|
// See equivalent function in ClientLayerManager
|
||||||
|
void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
|
||||||
|
const std::string& aKey,
|
||||||
|
const std::string& aValue) {
|
||||||
|
mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
|
||||||
|
}
|
||||||
|
// See equivalent function in ClientLayerManager
|
||||||
|
const APZTestData& GetAPZTestData() const
|
||||||
|
{ return mApzTestData; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Take a snapshot of the parent context, and copy
|
* Take a snapshot of the parent context, and copy
|
||||||
|
@ -178,6 +189,11 @@ private:
|
||||||
// being drawn to the default target, and then copy those pixels
|
// being drawn to the default target, and then copy those pixels
|
||||||
// back to mTarget.
|
// back to mTarget.
|
||||||
RefPtr<gfxContext> mTarget;
|
RefPtr<gfxContext> mTarget;
|
||||||
|
|
||||||
|
// See equivalent field in ClientLayerManager
|
||||||
|
uint32_t mPaintSequenceNumber;
|
||||||
|
// See equivalent field in ClientLayerManager
|
||||||
|
APZTestData mApzTestData;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
|
|
|
@ -80,6 +80,7 @@ WebRenderLayerScrollData::GetTransformTyped() const
|
||||||
|
|
||||||
WebRenderScrollData::WebRenderScrollData()
|
WebRenderScrollData::WebRenderScrollData()
|
||||||
: mIsFirstPaint(false)
|
: mIsFirstPaint(false)
|
||||||
|
, mPaintSequenceNumber(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,5 +152,17 @@ WebRenderScrollData::IsFirstPaint() const
|
||||||
return mIsFirstPaint;
|
return mIsFirstPaint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WebRenderScrollData::SetPaintSequenceNumber(uint32_t aPaintSequenceNumber)
|
||||||
|
{
|
||||||
|
mPaintSequenceNumber = aPaintSequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
WebRenderScrollData::GetPaintSequenceNumber() const
|
||||||
|
{
|
||||||
|
return mPaintSequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -121,6 +121,8 @@ public:
|
||||||
|
|
||||||
void SetIsFirstPaint();
|
void SetIsFirstPaint();
|
||||||
bool IsFirstPaint() const;
|
bool IsFirstPaint() const;
|
||||||
|
void SetPaintSequenceNumber(uint32_t aPaintSequenceNumber);
|
||||||
|
uint32_t GetPaintSequenceNumber() const;
|
||||||
|
|
||||||
friend struct IPC::ParamTraits<WebRenderScrollData>;
|
friend struct IPC::ParamTraits<WebRenderScrollData>;
|
||||||
|
|
||||||
|
@ -146,6 +148,7 @@ private:
|
||||||
nsTArray<WebRenderLayerScrollData> mLayerScrollData;
|
nsTArray<WebRenderLayerScrollData> mLayerScrollData;
|
||||||
|
|
||||||
bool mIsFirstPaint;
|
bool mIsFirstPaint;
|
||||||
|
uint32_t mPaintSequenceNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
|
@ -219,6 +222,7 @@ struct ParamTraits<mozilla::layers::WebRenderScrollData>
|
||||||
WriteParam(aMsg, aParam.mScrollMetadatas);
|
WriteParam(aMsg, aParam.mScrollMetadatas);
|
||||||
WriteParam(aMsg, aParam.mLayerScrollData);
|
WriteParam(aMsg, aParam.mLayerScrollData);
|
||||||
WriteParam(aMsg, aParam.mIsFirstPaint);
|
WriteParam(aMsg, aParam.mIsFirstPaint);
|
||||||
|
WriteParam(aMsg, aParam.mPaintSequenceNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -226,7 +230,8 @@ struct ParamTraits<mozilla::layers::WebRenderScrollData>
|
||||||
{
|
{
|
||||||
return ReadParam(aMsg, aIter, &aResult->mScrollMetadatas)
|
return ReadParam(aMsg, aIter, &aResult->mScrollMetadatas)
|
||||||
&& ReadParam(aMsg, aIter, &aResult->mLayerScrollData)
|
&& ReadParam(aMsg, aIter, &aResult->mLayerScrollData)
|
||||||
&& ReadParam(aMsg, aIter, &aResult->mIsFirstPaint);
|
&& ReadParam(aMsg, aIter, &aResult->mIsFirstPaint)
|
||||||
|
&& ReadParam(aMsg, aIter, &aResult->mPaintSequenceNumber);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1048,6 +1048,8 @@ description =
|
||||||
description = test only
|
description = test only
|
||||||
[PWebRenderBridge::SetAsyncZoom]
|
[PWebRenderBridge::SetAsyncZoom]
|
||||||
description = test only
|
description = test only
|
||||||
|
[PWebRenderBridge::GetAPZTestData]
|
||||||
|
description = test only
|
||||||
[PHal::GetCurrentBatteryInformation]
|
[PHal::GetCurrentBatteryInformation]
|
||||||
description =
|
description =
|
||||||
[PHal::GetCurrentNetworkInformation]
|
[PHal::GetCurrentNetworkInformation]
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// |jit-test| allow-oom
|
|
||||||
if (getBuildConfiguration().debug === true)
|
if (getBuildConfiguration().debug === true)
|
||||||
quit(0);
|
quit(0);
|
||||||
function f(){};
|
function f(){};
|
||||||
|
@ -9,4 +8,4 @@ try {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ex = e;
|
ex = e;
|
||||||
}
|
}
|
||||||
assertEq(ex instanceof InternalError, true);
|
assertEq(ex === "out of memory" || (ex instanceof InternalError), true);
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ArrayObject : public NativeObject
|
||||||
|
|
||||||
void setNonWritableLength(JSContext* cx) {
|
void setNonWritableLength(JSContext* cx) {
|
||||||
if (getElementsHeader()->numShiftedElements() > 0)
|
if (getElementsHeader()->numShiftedElements() > 0)
|
||||||
unshiftElements();
|
moveShiftedElements();
|
||||||
|
|
||||||
// When an array's length becomes non-writable, writes to indexes
|
// When an array's length becomes non-writable, writes to indexes
|
||||||
// greater than or equal to the length don't change the array. We
|
// greater than or equal to the length don't change the array. We
|
||||||
|
|
|
@ -185,7 +185,7 @@ NativeObject::tryShiftDenseElements(uint32_t count)
|
||||||
MOZ_ASSERT(count < header->initializedLength);
|
MOZ_ASSERT(count < header->initializedLength);
|
||||||
|
|
||||||
if (MOZ_UNLIKELY(header->numShiftedElements() + count > ObjectElements::MaxShiftedElements)) {
|
if (MOZ_UNLIKELY(header->numShiftedElements() + count > ObjectElements::MaxShiftedElements)) {
|
||||||
unshiftElements();
|
moveShiftedElements();
|
||||||
header = getElementsHeader();
|
header = getElementsHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ ObjectElements::FreezeElements(JSContext* cx, HandleNativeObject obj)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (obj->getElementsHeader()->numShiftedElements() > 0)
|
if (obj->getElementsHeader()->numShiftedElements() > 0)
|
||||||
obj->unshiftElements();
|
obj->moveShiftedElements();
|
||||||
|
|
||||||
ObjectElements* header = obj->getElementsHeader();
|
ObjectElements* header = obj->getElementsHeader();
|
||||||
|
|
||||||
|
@ -707,7 +707,7 @@ NativeObject::maybeDensifySparseElements(JSContext* cx, HandleNativeObject obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NativeObject::unshiftElements()
|
NativeObject::moveShiftedElements()
|
||||||
{
|
{
|
||||||
ObjectElements* header = getElementsHeader();
|
ObjectElements* header = getElementsHeader();
|
||||||
uint32_t numShifted = header->numShiftedElements();
|
uint32_t numShifted = header->numShiftedElements();
|
||||||
|
@ -723,7 +723,7 @@ NativeObject::unshiftElements()
|
||||||
elements_ = newHeader->elements();
|
elements_ = newHeader->elements();
|
||||||
|
|
||||||
// To move the elements, temporarily update initializedLength to include
|
// To move the elements, temporarily update initializedLength to include
|
||||||
// both shifted and unshifted elements.
|
// the shifted elements.
|
||||||
newHeader->initializedLength += numShifted;
|
newHeader->initializedLength += numShifted;
|
||||||
|
|
||||||
// Move the elements. Initialize to |undefined| to ensure pre-barriers
|
// Move the elements. Initialize to |undefined| to ensure pre-barriers
|
||||||
|
@ -739,14 +739,14 @@ NativeObject::unshiftElements()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NativeObject::maybeUnshiftElements()
|
NativeObject::maybeMoveShiftedElements()
|
||||||
{
|
{
|
||||||
ObjectElements* header = getElementsHeader();
|
ObjectElements* header = getElementsHeader();
|
||||||
MOZ_ASSERT(header->numShiftedElements() > 0);
|
MOZ_ASSERT(header->numShiftedElements() > 0);
|
||||||
|
|
||||||
// Unshift if less than a third of the allocated space is in use.
|
// Move the elements if less than a third of the allocated space is in use.
|
||||||
if (header->capacity < header->numAllocatedElements() / 3)
|
if (header->capacity < header->numAllocatedElements() / 3)
|
||||||
unshiftElements();
|
moveShiftedElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a requested capacity (in elements) and (potentially) the length of an
|
// Given a requested capacity (in elements) and (potentially) the length of an
|
||||||
|
@ -847,22 +847,31 @@ NativeObject::growElements(JSContext* cx, uint32_t reqCapacity)
|
||||||
if (denseElementsAreCopyOnWrite())
|
if (denseElementsAreCopyOnWrite())
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
|
|
||||||
// If there are shifted elements, consider unshifting them first. If we
|
// If there are shifted elements, consider moving them first. If we don't
|
||||||
// don't unshift here, the code below will include the shifted elements in
|
// move them here, the code below will include the shifted elements in the
|
||||||
// the resize.
|
// resize.
|
||||||
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||||
if (numShifted > 0) {
|
if (numShifted > 0) {
|
||||||
maybeUnshiftElements();
|
// If the number of elements is small, it's cheaper to just move them as
|
||||||
|
// it may avoid a malloc/realloc. Note that there's no technical reason
|
||||||
|
// for using this particular value, but it works well in real-world use
|
||||||
|
// cases.
|
||||||
|
static const size_t MaxElementsToMoveEagerly = 20;
|
||||||
|
|
||||||
|
if (getElementsHeader()->initializedLength <= MaxElementsToMoveEagerly)
|
||||||
|
moveShiftedElements();
|
||||||
|
else
|
||||||
|
maybeMoveShiftedElements();
|
||||||
if (getDenseCapacity() >= reqCapacity)
|
if (getDenseCapacity() >= reqCapacity)
|
||||||
return true;
|
return true;
|
||||||
numShifted = getElementsHeader()->numShiftedElements();
|
numShifted = getElementsHeader()->numShiftedElements();
|
||||||
|
|
||||||
// Ensure |reqCapacity + numShifted| below won't overflow by forcing an
|
// If |reqCapacity + numShifted| overflows, we just move all shifted
|
||||||
// unshift in that case.
|
// elements to avoid the problem.
|
||||||
CheckedInt<uint32_t> checkedReqCapacity(reqCapacity);
|
CheckedInt<uint32_t> checkedReqCapacity(reqCapacity);
|
||||||
checkedReqCapacity += numShifted;
|
checkedReqCapacity += numShifted;
|
||||||
if (MOZ_UNLIKELY(!checkedReqCapacity.isValid())) {
|
if (MOZ_UNLIKELY(!checkedReqCapacity.isValid())) {
|
||||||
unshiftElements();
|
moveShiftedElements();
|
||||||
numShifted = 0;
|
numShifted = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -932,10 +941,10 @@ NativeObject::shrinkElements(JSContext* cx, uint32_t reqCapacity)
|
||||||
if (!hasDynamicElements())
|
if (!hasDynamicElements())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If we have shifted elements, consider unshifting them.
|
// If we have shifted elements, consider moving them.
|
||||||
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||||
if (numShifted > 0) {
|
if (numShifted > 0) {
|
||||||
maybeUnshiftElements();
|
maybeMoveShiftedElements();
|
||||||
numShifted = getElementsHeader()->numShiftedElements();
|
numShifted = getElementsHeader()->numShiftedElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
|
||||||
* to the next element and moving the ObjectElements header in memory (so it's
|
* to the next element and moving the ObjectElements header in memory (so it's
|
||||||
* stored where the shifted Value used to be).
|
* stored where the shifted Value used to be).
|
||||||
*
|
*
|
||||||
* Shifted elements can be unshifted when we grow the array, when the array is
|
* Shifted elements can be moved when we grow the array, when the array is
|
||||||
* frozen (for simplicity, shifted elements are not supported on objects that
|
* frozen (for simplicity, shifted elements are not supported on objects that
|
||||||
* are frozen, have copy-on-write elements, or on arrays with non-writable
|
* are frozen, have copy-on-write elements, or on arrays with non-writable
|
||||||
* length).
|
* length).
|
||||||
|
@ -205,7 +205,7 @@ class ObjectElements
|
||||||
};
|
};
|
||||||
|
|
||||||
// The flags word stores both the flags and the number of shifted elements.
|
// The flags word stores both the flags and the number of shifted elements.
|
||||||
// Allow shifting 2047 elements before unshifting.
|
// Allow shifting 2047 elements before actually moving the elements.
|
||||||
static const size_t NumShiftedElementsBits = 11;
|
static const size_t NumShiftedElementsBits = 11;
|
||||||
static const size_t MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
|
static const size_t MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
|
||||||
static const size_t NumShiftedElementsShift = 32 - NumShiftedElementsBits;
|
static const size_t NumShiftedElementsShift = 32 - NumShiftedElementsBits;
|
||||||
|
@ -1074,11 +1074,12 @@ class NativeObject : public ShapedObject
|
||||||
// Try to shift |count| dense elements, see the "Shifted elements" comment.
|
// Try to shift |count| dense elements, see the "Shifted elements" comment.
|
||||||
inline bool tryShiftDenseElements(uint32_t count);
|
inline bool tryShiftDenseElements(uint32_t count);
|
||||||
|
|
||||||
// Unshift all shifted elements so that numShiftedElements is 0.
|
// Move the elements header and all shifted elements to the start of the
|
||||||
void unshiftElements();
|
// allocated elements space, so that numShiftedElements is 0 afterwards.
|
||||||
|
void moveShiftedElements();
|
||||||
|
|
||||||
// If this object has many shifted elements, unshift them.
|
// If this object has many shifted elements call moveShiftedElements.
|
||||||
void maybeUnshiftElements();
|
void maybeMoveShiftedElements();
|
||||||
|
|
||||||
static bool goodElementsAllocationAmount(JSContext* cx, uint32_t reqAllocated,
|
static bool goodElementsAllocationAmount(JSContext* cx, uint32_t reqAllocated,
|
||||||
uint32_t length, uint32_t* goodAmount);
|
uint32_t length, uint32_t* goodAmount);
|
||||||
|
|
|
@ -20,6 +20,22 @@
|
||||||
#include <prio.h>
|
#include <prio.h>
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
// A specialization of GenericErrorResult which auto-converts to a nsresult.
|
||||||
|
// This should be removed when bug 1366511 is fixed.
|
||||||
|
template <>
|
||||||
|
class MOZ_MUST_USE_TYPE GenericErrorResult<nsresult>
|
||||||
|
{
|
||||||
|
nsresult mErrorValue;
|
||||||
|
|
||||||
|
template<typename V, typename E2> friend class Result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {}
|
||||||
|
|
||||||
|
operator nsresult() { return mErrorValue; }
|
||||||
|
};
|
||||||
|
|
||||||
namespace loader {
|
namespace loader {
|
||||||
|
|
||||||
using mozilla::dom::AutoJSAPI;
|
using mozilla::dom::AutoJSAPI;
|
||||||
|
|
|
@ -39,6 +39,9 @@
|
||||||
#include "nsXULAppAPI.h"
|
#include "nsXULAppAPI.h"
|
||||||
#include "WrapperFactory.h"
|
#include "WrapperFactory.h"
|
||||||
|
|
||||||
|
#include "AutoMemMap.h"
|
||||||
|
#include "ScriptPreloader-inl.h"
|
||||||
|
|
||||||
#include "mozilla/AddonPathService.h"
|
#include "mozilla/AddonPathService.h"
|
||||||
#include "mozilla/scache/StartupCache.h"
|
#include "mozilla/scache/StartupCache.h"
|
||||||
#include "mozilla/scache/StartupCacheUtils.h"
|
#include "mozilla/scache/StartupCacheUtils.h"
|
||||||
|
@ -51,6 +54,7 @@
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::scache;
|
using namespace mozilla::scache;
|
||||||
|
using namespace mozilla::loader;
|
||||||
using namespace xpc;
|
using namespace xpc;
|
||||||
using namespace JS;
|
using namespace JS;
|
||||||
|
|
||||||
|
@ -506,25 +510,6 @@ mozJSComponentLoader::CreateLoaderGlobal(JSContext* aCx,
|
||||||
aGlobal.set(global);
|
aGlobal.set(global);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some stack based classes for cleaning up on early return
|
|
||||||
class FileAutoCloser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit FileAutoCloser(PRFileDesc* file) : mFile(file) {}
|
|
||||||
~FileAutoCloser() { PR_Close(mFile); }
|
|
||||||
private:
|
|
||||||
PRFileDesc* mFile;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FileMapAutoCloser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit FileMapAutoCloser(PRFileMap* map) : mMap(map) {}
|
|
||||||
~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
|
|
||||||
private:
|
|
||||||
PRFileMap* mMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
|
mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
|
||||||
nsIFile* aComponentFile,
|
nsIFile* aComponentFile,
|
||||||
|
@ -671,54 +656,13 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
||||||
.setSourceIsLazy(!!cache);
|
.setSourceIsLazy(!!cache);
|
||||||
|
|
||||||
if (realFile) {
|
if (realFile) {
|
||||||
int64_t fileSize;
|
AutoMemMap map;
|
||||||
rv = aComponentFile->GetFileSize(&fileSize);
|
MOZ_TRY(map.init(aComponentFile));
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t maxSize = UINT32_MAX;
|
|
||||||
if (fileSize > maxSize) {
|
|
||||||
NS_ERROR("file too large");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PRFileDesc* fileHandle;
|
|
||||||
rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
return NS_ERROR_FILE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the file is closed, no matter how we return.
|
|
||||||
FileAutoCloser fileCloser(fileHandle);
|
|
||||||
|
|
||||||
// We don't provide the file size here. If we did, PR_CreateFileMap
|
|
||||||
// would simply stat() the file to verify that the size we provided
|
|
||||||
// didn't require extending the file. We know that the file doesn't
|
|
||||||
// need to be extended, so skip the extra work by not providing the
|
|
||||||
// size.
|
|
||||||
PRFileMap* map = PR_CreateFileMap(fileHandle, 0, PR_PROT_READONLY);
|
|
||||||
if (!map) {
|
|
||||||
NS_ERROR("Failed to create file map");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the file map is closed, no matter how we return.
|
|
||||||
FileMapAutoCloser mapCloser(map);
|
|
||||||
|
|
||||||
uint32_t fileSize32 = fileSize;
|
|
||||||
|
|
||||||
char* buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
|
|
||||||
if (!buf) {
|
|
||||||
NS_WARNING("Failed to map file");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: exceptions will get handled further down;
|
// Note: exceptions will get handled further down;
|
||||||
// don't early return for them here.
|
// don't early return for them here.
|
||||||
Compile(cx, options, buf, fileSize32, &script);
|
auto buf = map.get<char>();
|
||||||
|
Compile(cx, options, buf.get(), map.size(), &script);
|
||||||
PR_MemUnmap(buf, fileSize32);
|
|
||||||
} else {
|
} else {
|
||||||
rv = aInfo.EnsureScriptChannel();
|
rv = aInfo.EnsureScriptChannel();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
|
@ -9,9 +9,14 @@ add_task(function* test() {
|
||||||
const url = "http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_deadObjectOnUnload.html";
|
const url = "http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_deadObjectOnUnload.html";
|
||||||
let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||||
let browser = gBrowser.selectedBrowser;
|
let browser = gBrowser.selectedBrowser;
|
||||||
let contentDocDead = yield ContentTask.spawn(browser,{}, function*(browser){
|
let innerWindowId = browser.innerWindowID;
|
||||||
|
let contentDocDead = yield ContentTask.spawn(browser,{innerWindowId}, function*(args){
|
||||||
let doc = content.document;
|
let doc = content.document;
|
||||||
let promise = ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", true);
|
let {TestUtils} = Components.utils.import("resource://testing-common/TestUtils.jsm", {});
|
||||||
|
let promise = TestUtils.topicObserved("inner-window-destroyed", (subject, data) => {
|
||||||
|
let id = subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data;
|
||||||
|
return id == args.innerWindowId;
|
||||||
|
});
|
||||||
content.location = "about:home";
|
content.location = "about:home";
|
||||||
yield promise;
|
yield promise;
|
||||||
return Components.utils.isDeadWrapper(doc);
|
return Components.utils.isDeadWrapper(doc);
|
||||||
|
|
|
@ -122,6 +122,7 @@
|
||||||
#include "RegionBuilder.h"
|
#include "RegionBuilder.h"
|
||||||
#include "SVGSVGElement.h"
|
#include "SVGSVGElement.h"
|
||||||
#include "DisplayItemClip.h"
|
#include "DisplayItemClip.h"
|
||||||
|
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||||
|
|
||||||
#ifdef MOZ_XUL
|
#ifdef MOZ_XUL
|
||||||
#include "nsXULPopupManager.h"
|
#include "nsXULPopupManager.h"
|
||||||
|
@ -8508,6 +8509,8 @@ nsLayoutUtils::DoLogTestDataForPaint(LayerManager* aManager,
|
||||||
{
|
{
|
||||||
if (ClientLayerManager* mgr = aManager->AsClientLayerManager()) {
|
if (ClientLayerManager* mgr = aManager->AsClientLayerManager()) {
|
||||||
mgr->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
|
mgr->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
|
||||||
|
} else if (WebRenderLayerManager* wrlm = aManager->AsWebRenderLayerManager()) {
|
||||||
|
wrlm->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
|
XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
|
||||||
"resource://gre/modules/PageActions.jsm");
|
"resource://gre/modules/PageActions.jsm");
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
IconDetails,
|
IconDetails,
|
||||||
} = ExtensionUtils;
|
} = ExtensionParent;
|
||||||
|
|
||||||
// WeakMap[Extension -> PageAction]
|
// WeakMap[Extension -> PageAction]
|
||||||
var pageActionMap = new WeakMap();
|
var pageActionMap = new WeakMap();
|
||||||
|
|
|
@ -5,13 +5,11 @@
|
||||||
|
|
||||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||||
Cu.import('resource://gre/modules/Services.jsm');
|
Cu.import('resource://gre/modules/Services.jsm');
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
let { PlatformInfo } = ExtensionUtils;
|
if (AppConstants.platform == "android" && !Services.prefs.getBoolPref("network.mdns.use_js_fallback")) {
|
||||||
|
|
||||||
if (PlatformInfo.os == "android" && !Services.prefs.getBoolPref("network.mdns.use_js_fallback")) {
|
|
||||||
Cu.import("resource://gre/modules/MulticastDNSAndroid.jsm");
|
Cu.import("resource://gre/modules/MulticastDNSAndroid.jsm");
|
||||||
} else {
|
} else {
|
||||||
Cu.import("resource://gre/modules/MulticastDNS.jsm");
|
Cu.import("resource://gre/modules/MulticastDNS.jsm");
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
[display-none.html]
|
[display-none.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
[iframe-no-root.html]
|
[iframe-no-root.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
[multiple-thresholds.html]
|
[multiple-thresholds.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
[root-margin.html]
|
[root-margin.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
[same-document-no-root.html]
|
[same-document-no-root.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
[same-document-zero-size-target.html]
|
[same-document-zero-size-target.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
[shadow-content.html]
|
[shadow-content.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
prefs: [dom.webcomponents.enabled:true]
|
prefs: [dom.webcomponents.enabled:true]
|
||||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ module.exports = {
|
||||||
"Ci": true,
|
"Ci": true,
|
||||||
"Cr": true,
|
"Cr": true,
|
||||||
"Cu": true,
|
"Cu": true,
|
||||||
|
"StructuredCloneHolder": false,
|
||||||
"TextDecoder": false,
|
"TextDecoder": false,
|
||||||
"TextEncoder": false,
|
"TextEncoder": false,
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ module.exports = {
|
||||||
"AppConstants": true,
|
"AppConstants": true,
|
||||||
"Extension": true,
|
"Extension": true,
|
||||||
"ExtensionAPI": true,
|
"ExtensionAPI": true,
|
||||||
|
"ExtensionCommon": true,
|
||||||
"ExtensionManagement": true,
|
"ExtensionManagement": true,
|
||||||
"ExtensionUtils": true,
|
"ExtensionUtils": true,
|
||||||
"extensions": true,
|
"extensions": true,
|
||||||
|
|
|
@ -97,13 +97,12 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
|
||||||
var {
|
var {
|
||||||
GlobalManager,
|
GlobalManager,
|
||||||
ParentAPIManager,
|
ParentAPIManager,
|
||||||
|
StartupCache,
|
||||||
apiManager: Management,
|
apiManager: Management,
|
||||||
} = ExtensionParent;
|
} = ExtensionParent;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
classifyPermission,
|
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
StartupCache,
|
|
||||||
getUniqueId,
|
getUniqueId,
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
@ -145,6 +144,29 @@ function validateThemeManifest(manifestProperties) {
|
||||||
return invalidProps;
|
return invalidProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classify an individual permission from a webextension manifest
|
||||||
|
* as a host/origin permission, an api permission, or a regular permission.
|
||||||
|
*
|
||||||
|
* @param {string} perm The permission string to classify
|
||||||
|
*
|
||||||
|
* @returns {object}
|
||||||
|
* An object with exactly one of the following properties:
|
||||||
|
* "origin" to indicate this is a host/origin permission.
|
||||||
|
* "api" to indicate this is an api permission
|
||||||
|
* (as used for webextensions experiments).
|
||||||
|
* "permission" to indicate this is a regular permission.
|
||||||
|
*/
|
||||||
|
function classifyPermission(perm) {
|
||||||
|
let match = /^(\w+)(?:\.(\w+)(?:\.\w+)*)?$/.exec(perm);
|
||||||
|
if (!match) {
|
||||||
|
return {origin: perm};
|
||||||
|
} else if (match[1] == "experiments" && match[2]) {
|
||||||
|
return {api: match[2]};
|
||||||
|
}
|
||||||
|
return {permission: perm};
|
||||||
|
}
|
||||||
|
|
||||||
const LOGGER_ID_BASE = "addons.webextension.";
|
const LOGGER_ID_BASE = "addons.webextension.";
|
||||||
const UUID_MAP_PREF = "extensions.webextensions.uuids";
|
const UUID_MAP_PREF = "extensions.webextensions.uuids";
|
||||||
const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall";
|
const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall";
|
||||||
|
|
|
@ -40,22 +40,41 @@ const {
|
||||||
DefaultMap,
|
DefaultMap,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
LimitedSet,
|
LimitedSet,
|
||||||
SpreadArgs,
|
|
||||||
defineLazyGetter,
|
defineLazyGetter,
|
||||||
getMessageManager,
|
getMessageManager,
|
||||||
getUniqueId,
|
getUniqueId,
|
||||||
injectAPI,
|
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
LocalAPIImplementation,
|
LocalAPIImplementation,
|
||||||
LocaleData,
|
LocaleData,
|
||||||
|
NoCloneSpreadArgs,
|
||||||
SchemaAPIInterface,
|
SchemaAPIInterface,
|
||||||
SingletonEventManager,
|
SingletonEventManager,
|
||||||
} = ExtensionCommon;
|
} = ExtensionCommon;
|
||||||
|
|
||||||
const isContentProcess = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
const isContentProcess = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
||||||
|
|
||||||
|
// Copy an API object from |source| into the scope |dest|.
|
||||||
|
function injectAPI(source, dest) {
|
||||||
|
for (let prop in source) {
|
||||||
|
// Skip names prefixed with '_'.
|
||||||
|
if (prop[0] == "_") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let desc = Object.getOwnPropertyDescriptor(source, prop);
|
||||||
|
if (typeof(desc.value) == "function") {
|
||||||
|
Cu.exportFunction(desc.value, dest, {defineAs: prop});
|
||||||
|
} else if (typeof(desc.value) == "object") {
|
||||||
|
let obj = Cu.createObjectIn(dest, {defineAs: prop});
|
||||||
|
injectAPI(desc.value, obj);
|
||||||
|
} else {
|
||||||
|
Object.defineProperty(dest, prop, desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction for a Port object in the extension API.
|
* Abstraction for a Port object in the extension API.
|
||||||
*
|
*
|
||||||
|
@ -115,15 +134,16 @@ class Port {
|
||||||
},
|
},
|
||||||
|
|
||||||
onDisconnect: new SingletonEventManager(this.context, "Port.onDisconnect", fire => {
|
onDisconnect: new SingletonEventManager(this.context, "Port.onDisconnect", fire => {
|
||||||
return this.registerOnDisconnect(error => {
|
return this.registerOnDisconnect(holder => {
|
||||||
|
let error = holder.deserialize(this.context.cloneScope);
|
||||||
portError = error && this.context.normalizeError(error);
|
portError = error && this.context.normalizeError(error);
|
||||||
fire.asyncWithoutClone(portObj);
|
fire.asyncWithoutClone(portObj);
|
||||||
});
|
});
|
||||||
}).api(),
|
}).api(),
|
||||||
|
|
||||||
onMessage: new SingletonEventManager(this.context, "Port.onMessage", fire => {
|
onMessage: new SingletonEventManager(this.context, "Port.onMessage", fire => {
|
||||||
return this.registerOnMessage(msg => {
|
return this.registerOnMessage(holder => {
|
||||||
msg = Cu.cloneInto(msg, this.context.cloneScope);
|
let msg = holder.deserialize(this.context.cloneScope);
|
||||||
fire.asyncWithoutClone(msg, portObj);
|
fire.asyncWithoutClone(msg, portObj);
|
||||||
});
|
});
|
||||||
}).api(),
|
}).api(),
|
||||||
|
@ -202,7 +222,9 @@ class Port {
|
||||||
responseType: MessageChannel.RESPONSE_NONE,
|
responseType: MessageChannel.RESPONSE_NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.context.sendMessage(this.senderMM, message, data, options);
|
let holder = new StructuredCloneHolder(data);
|
||||||
|
|
||||||
|
return this.context.sendMessage(this.senderMM, message, holder, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDisconnection() {
|
handleDisconnection() {
|
||||||
|
@ -307,7 +329,9 @@ class Messenger {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(messageManager, msg, recipient, responseCallback) {
|
sendMessage(messageManager, msg, recipient, responseCallback) {
|
||||||
let promise = this._sendMessage(messageManager, "Extension:Message", msg, recipient)
|
let holder = new StructuredCloneHolder(msg);
|
||||||
|
|
||||||
|
let promise = this._sendMessage(messageManager, "Extension:Message", holder, recipient)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.result == MessageChannel.RESULT_NO_HANDLER) {
|
if (error.result == MessageChannel.RESULT_NO_HANDLER) {
|
||||||
return Promise.reject({message: "Could not establish connection. Receiving end does not exist."});
|
return Promise.reject({message: "Could not establish connection. Receiving end does not exist."});
|
||||||
|
@ -336,7 +360,7 @@ class Messenger {
|
||||||
filter(sender, recipient));
|
filter(sender, recipient));
|
||||||
},
|
},
|
||||||
|
|
||||||
receiveMessage: ({target, data: message, sender, recipient}) => {
|
receiveMessage: ({target, data: holder, sender, recipient}) => {
|
||||||
if (!this.context.active) {
|
if (!this.context.active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +374,7 @@ class Messenger {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
message = Cu.cloneInto(message, this.context.cloneScope);
|
let message = holder.deserialize(this.context.cloneScope);
|
||||||
sender = Cu.cloneInto(sender, this.context.cloneScope);
|
sender = Cu.cloneInto(sender, this.context.cloneScope);
|
||||||
sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope);
|
sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope);
|
||||||
|
|
||||||
|
@ -393,7 +417,7 @@ class Messenger {
|
||||||
} else if (error.result === MessageChannel.RESULT_DISCONNECTED) {
|
} else if (error.result === MessageChannel.RESULT_DISCONNECTED) {
|
||||||
error = null;
|
error = null;
|
||||||
}
|
}
|
||||||
port.disconnectByOtherEnd(error);
|
port.disconnectByOtherEnd(new StructuredCloneHolder(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
return port.api();
|
return port.api();
|
||||||
|
@ -734,7 +758,9 @@ class ChildAPIManager {
|
||||||
let listener = map.ids.get(data.listenerId);
|
let listener = map.ids.get(data.listenerId);
|
||||||
|
|
||||||
if (listener) {
|
if (listener) {
|
||||||
return this.context.runSafe(listener, ...data.args);
|
let args = data.args.deserialize(this.context.cloneScope);
|
||||||
|
|
||||||
|
return this.context.runSafeWithoutClone(listener, ...args);
|
||||||
}
|
}
|
||||||
if (!map.removedIds.has(data.listenerId)) {
|
if (!map.removedIds.has(data.listenerId)) {
|
||||||
Services.console.logStringMessage(
|
Services.console.logStringMessage(
|
||||||
|
@ -747,7 +773,9 @@ class ChildAPIManager {
|
||||||
if ("error" in data) {
|
if ("error" in data) {
|
||||||
deferred.reject(data.error);
|
deferred.reject(data.error);
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve(new SpreadArgs(data.result));
|
let result = data.result.deserialize(this.context.cloneScope);
|
||||||
|
|
||||||
|
deferred.resolve(new NoCloneSpreadArgs(result));
|
||||||
}
|
}
|
||||||
this.callPromises.delete(data.callId);
|
this.callPromises.delete(data.callId);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -29,6 +29,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||||
"resource://gre/modules/Schemas.jsm");
|
"resource://gre/modules/Schemas.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
|
||||||
|
"@mozilla.org/content/style-sheet-service;1",
|
||||||
|
"nsIStyleSheetService");
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
|
@ -36,7 +40,6 @@ var {
|
||||||
DefaultWeakMap,
|
DefaultWeakMap,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
ExtensionError,
|
ExtensionError,
|
||||||
SpreadArgs,
|
|
||||||
defineLazyGetter,
|
defineLazyGetter,
|
||||||
getConsole,
|
getConsole,
|
||||||
getInnerWindowID,
|
getInnerWindowID,
|
||||||
|
@ -48,6 +51,39 @@ var {
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "console", getConsole);
|
XPCOMUtils.defineLazyGetter(this, "console", getConsole);
|
||||||
|
|
||||||
|
var ExtensionCommon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sentinel class to indicate that an array of values should be
|
||||||
|
* treated as an array when used as a promise resolution value, but as a
|
||||||
|
* spread expression (...args) when passed to a callback.
|
||||||
|
*/
|
||||||
|
class SpreadArgs extends Array {
|
||||||
|
constructor(args) {
|
||||||
|
super();
|
||||||
|
this.push(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like SpreadArgs, but also indicates that the array values already
|
||||||
|
* belong to the target compartment, and should not be cloned before
|
||||||
|
* being passed.
|
||||||
|
*
|
||||||
|
* The `unwrappedValues` property contains an Array object which belongs
|
||||||
|
* to the target compartment, and contains the same unwrapped values
|
||||||
|
* passed the NoCloneSpreadArgs constructor.
|
||||||
|
*/
|
||||||
|
class NoCloneSpreadArgs {
|
||||||
|
constructor(args) {
|
||||||
|
this.unwrappedValues = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this.unwrappedValues[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class BaseContext {
|
class BaseContext {
|
||||||
constructor(envType, extension) {
|
constructor(envType, extension) {
|
||||||
this.envType = envType;
|
this.envType = envType;
|
||||||
|
@ -311,6 +347,8 @@ class BaseContext {
|
||||||
dump(`Promise resolved after context unloaded\n`);
|
dump(`Promise resolved after context unloaded\n`);
|
||||||
} else if (!this.active) {
|
} else if (!this.active) {
|
||||||
dump(`Promise resolved while context is inactive\n`);
|
dump(`Promise resolved while context is inactive\n`);
|
||||||
|
} else if (args instanceof NoCloneSpreadArgs) {
|
||||||
|
this.runSafeWithoutClone(callback, ...args.unwrappedValues);
|
||||||
} else if (args instanceof SpreadArgs) {
|
} else if (args instanceof SpreadArgs) {
|
||||||
runSafe(callback, ...args);
|
runSafe(callback, ...args);
|
||||||
} else {
|
} else {
|
||||||
|
@ -336,6 +374,9 @@ class BaseContext {
|
||||||
dump(`Promise resolved after context unloaded\n`);
|
dump(`Promise resolved after context unloaded\n`);
|
||||||
} else if (!this.active) {
|
} else if (!this.active) {
|
||||||
dump(`Promise resolved while context is inactive\n`);
|
dump(`Promise resolved while context is inactive\n`);
|
||||||
|
} else if (value instanceof NoCloneSpreadArgs) {
|
||||||
|
let values = value.unwrappedValues;
|
||||||
|
this.runSafeWithoutClone(resolve, values.length == 1 ? values[0] : values);
|
||||||
} else if (value instanceof SpreadArgs) {
|
} else if (value instanceof SpreadArgs) {
|
||||||
runSafe(resolve, value.length == 1 ? value[0] : value);
|
runSafe(resolve, value.length == 1 ? value[0] : value);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1067,7 +1108,7 @@ class SchemaAPIManager extends EventEmitter {
|
||||||
sandboxName: `Namespace of ext-*.js scripts for ${this.processType}`,
|
sandboxName: `Namespace of ext-*.js scripts for ${this.processType}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(global, {global, Cc, Ci, Cu, Cr, XPCOMUtils, ChromeWorker, MatchPattern, MatchPatternSet, extensions: this});
|
Object.assign(global, {global, Cc, Ci, Cu, Cr, XPCOMUtils, ChromeWorker, ExtensionCommon, MatchPattern, MatchPatternSet, extensions: this});
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/AppConstants.jsm", global);
|
Cu.import("resource://gre/modules/AppConstants.jsm", global);
|
||||||
Cu.import("resource://gre/modules/ExtensionAPI.jsm", global);
|
Cu.import("resource://gre/modules/ExtensionAPI.jsm", global);
|
||||||
|
@ -1443,13 +1484,44 @@ SingletonEventManager.prototype = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Simple API for event listeners where events never fire.
|
||||||
|
function ignoreEvent(context, name) {
|
||||||
|
return {
|
||||||
|
addListener: function(callback) {
|
||||||
|
let id = context.extension.id;
|
||||||
|
let frame = Components.stack.caller;
|
||||||
|
let msg = `In add-on ${id}, attempting to use listener "${name}", which is unimplemented.`;
|
||||||
|
let scriptError = Cc["@mozilla.org/scripterror;1"]
|
||||||
|
.createInstance(Ci.nsIScriptError);
|
||||||
|
scriptError.init(msg, frame.filename, null, frame.lineNumber,
|
||||||
|
frame.columnNumber, Ci.nsIScriptError.warningFlag,
|
||||||
|
"content javascript");
|
||||||
|
let consoleService = Cc["@mozilla.org/consoleservice;1"]
|
||||||
|
.getService(Ci.nsIConsoleService);
|
||||||
|
consoleService.logMessage(scriptError);
|
||||||
|
},
|
||||||
|
removeListener: function(callback) {},
|
||||||
|
hasListener: function(callback) {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const ExtensionCommon = {
|
|
||||||
|
const stylesheetMap = new DefaultMap(url => {
|
||||||
|
let uri = Services.io.newURI(url);
|
||||||
|
return styleSheetService.preloadSheet(uri, styleSheetService.AGENT_SHEET);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ExtensionCommon = {
|
||||||
BaseContext,
|
BaseContext,
|
||||||
CanOfAPIs,
|
CanOfAPIs,
|
||||||
LocalAPIImplementation,
|
LocalAPIImplementation,
|
||||||
LocaleData,
|
LocaleData,
|
||||||
|
NoCloneSpreadArgs,
|
||||||
SchemaAPIInterface,
|
SchemaAPIInterface,
|
||||||
SchemaAPIManager,
|
SchemaAPIManager,
|
||||||
SingletonEventManager,
|
SingletonEventManager,
|
||||||
|
SpreadArgs,
|
||||||
|
ignoreEvent,
|
||||||
|
stylesheetMap,
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||||
"resource://gre/modules/AppConstants.jsm");
|
"resource://gre/modules/AppConstants.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||||
"resource:///modules/E10SUtils.jsm");
|
"resource:///modules/E10SUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "IndexedDB",
|
||||||
|
"resource://gre/modules/IndexedDB.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||||
"resource://gre/modules/MessageChannel.jsm");
|
"resource://gre/modules/MessageChannel.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
|
XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
|
||||||
|
@ -46,12 +48,13 @@ var {
|
||||||
BaseContext,
|
BaseContext,
|
||||||
CanOfAPIs,
|
CanOfAPIs,
|
||||||
SchemaAPIManager,
|
SchemaAPIManager,
|
||||||
|
SpreadArgs,
|
||||||
} = ExtensionCommon;
|
} = ExtensionCommon;
|
||||||
|
|
||||||
var {
|
var {
|
||||||
DefaultWeakMap,
|
DefaultWeakMap,
|
||||||
|
ExtensionError,
|
||||||
MessageManagerProxy,
|
MessageManagerProxy,
|
||||||
SpreadArgs,
|
|
||||||
defineLazyGetter,
|
defineLazyGetter,
|
||||||
promiseDocumentLoaded,
|
promiseDocumentLoaded,
|
||||||
promiseEvent,
|
promiseEvent,
|
||||||
|
@ -619,7 +622,9 @@ ParentAPIManager = {
|
||||||
result.then(result => {
|
result.then(result => {
|
||||||
result = result instanceof SpreadArgs ? [...result] : [result];
|
result = result instanceof SpreadArgs ? [...result] : [result];
|
||||||
|
|
||||||
reply({result});
|
let holder = new StructuredCloneHolder(result);
|
||||||
|
|
||||||
|
reply({result: holder});
|
||||||
}, error => {
|
}, error => {
|
||||||
error = context.normalizeError(error);
|
error = context.normalizeError(error);
|
||||||
reply({error: {message: error.message, fileName: error.fileName}});
|
reply({error: {message: error.message, fileName: error.fileName}});
|
||||||
|
@ -651,7 +656,7 @@ ParentAPIManager = {
|
||||||
childId,
|
childId,
|
||||||
listenerId: data.listenerId,
|
listenerId: data.listenerId,
|
||||||
path: data.path,
|
path: data.path,
|
||||||
args: listenerArgs,
|
args: new StructuredCloneHolder(listenerArgs),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
recipient: {childId},
|
recipient: {childId},
|
||||||
|
@ -1070,11 +1075,305 @@ function extensionNameFromURI(uri) {
|
||||||
return GlobalManager.getExtension(id).name;
|
return GlobalManager.getExtension(id).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExtensionParent = {
|
const INTEGER = /^[1-9]\d*$/;
|
||||||
|
|
||||||
|
// Manages icon details for toolbar buttons in the |pageAction| and
|
||||||
|
// |browserAction| APIs.
|
||||||
|
let IconDetails = {
|
||||||
|
// WeakMap<Extension -> Map<url-string -> object>>
|
||||||
|
iconCache: new DefaultWeakMap(() => new Map()),
|
||||||
|
|
||||||
|
// Normalizes the various acceptable input formats into an object
|
||||||
|
// with icon size as key and icon URL as value.
|
||||||
|
//
|
||||||
|
// If a context is specified (function is called from an extension):
|
||||||
|
// Throws an error if an invalid icon size was provided or the
|
||||||
|
// extension is not allowed to load the specified resources.
|
||||||
|
//
|
||||||
|
// If no context is specified, instead of throwing an error, this
|
||||||
|
// function simply logs a warning message.
|
||||||
|
normalize(details, extension, context = null) {
|
||||||
|
if (!details.imageData && typeof details.path === "string") {
|
||||||
|
let icons = this.iconCache.get(extension);
|
||||||
|
|
||||||
|
let baseURI = context ? context.uri : extension.baseURI;
|
||||||
|
let url = baseURI.resolve(details.path);
|
||||||
|
|
||||||
|
let icon = icons.get(url);
|
||||||
|
if (!icon) {
|
||||||
|
icon = this._normalize(details, extension, context);
|
||||||
|
icons.set(url, icon);
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._normalize(details, extension, context);
|
||||||
|
},
|
||||||
|
|
||||||
|
_normalize(details, extension, context = null) {
|
||||||
|
let result = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (details.imageData) {
|
||||||
|
let imageData = details.imageData;
|
||||||
|
|
||||||
|
if (typeof imageData == "string") {
|
||||||
|
imageData = {"19": imageData};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let size of Object.keys(imageData)) {
|
||||||
|
if (!INTEGER.test(size)) {
|
||||||
|
throw new ExtensionError(`Invalid icon size ${size}, must be an integer`);
|
||||||
|
}
|
||||||
|
result[size] = imageData[size];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.path) {
|
||||||
|
let path = details.path;
|
||||||
|
if (typeof path != "object") {
|
||||||
|
path = {"19": path};
|
||||||
|
}
|
||||||
|
|
||||||
|
let baseURI = context ? context.uri : extension.baseURI;
|
||||||
|
|
||||||
|
for (let size of Object.keys(path)) {
|
||||||
|
if (!INTEGER.test(size)) {
|
||||||
|
throw new ExtensionError(`Invalid icon size ${size}, must be an integer`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = baseURI.resolve(path[size]);
|
||||||
|
|
||||||
|
// The Chrome documentation specifies these parameters as
|
||||||
|
// relative paths. We currently accept absolute URLs as well,
|
||||||
|
// which means we need to check that the extension is allowed
|
||||||
|
// to load them. This will throw an error if it's not allowed.
|
||||||
|
try {
|
||||||
|
Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(
|
||||||
|
extension.principal, url,
|
||||||
|
Services.scriptSecurityManager.DISALLOW_SCRIPT);
|
||||||
|
} catch (e) {
|
||||||
|
throw new ExtensionError(`Illegal URL ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
result[size] = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Function is called from extension code, delegate error.
|
||||||
|
if (context) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
// If there's no context, it's because we're handling this
|
||||||
|
// as a manifest directive. Log a warning rather than
|
||||||
|
// raising an error.
|
||||||
|
extension.manifestError(`Invalid icon data: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Returns the appropriate icon URL for the given icons object and the
|
||||||
|
// screen resolution of the given window.
|
||||||
|
getPreferredIcon(icons, extension = null, size = 16) {
|
||||||
|
const DEFAULT = "chrome://browser/content/extension.svg";
|
||||||
|
|
||||||
|
let bestSize = null;
|
||||||
|
if (icons[size]) {
|
||||||
|
bestSize = size;
|
||||||
|
} else if (icons[2 * size]) {
|
||||||
|
bestSize = 2 * size;
|
||||||
|
} else {
|
||||||
|
let sizes = Object.keys(icons)
|
||||||
|
.map(key => parseInt(key, 10))
|
||||||
|
.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
bestSize = sizes.find(candidate => candidate > size) || sizes.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestSize) {
|
||||||
|
return {size: bestSize, icon: icons[bestSize]};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {size, icon: DEFAULT};
|
||||||
|
},
|
||||||
|
|
||||||
|
convertImageURLToDataURL(imageURL, contentWindow, browserWindow, size = 18) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let image = new contentWindow.Image();
|
||||||
|
image.onload = function() {
|
||||||
|
let canvas = contentWindow.document.createElement("canvas");
|
||||||
|
let ctx = canvas.getContext("2d");
|
||||||
|
let dSize = size * browserWindow.devicePixelRatio;
|
||||||
|
|
||||||
|
// Scales the image while maintaing width to height ratio.
|
||||||
|
// If the width and height differ, the image is centered using the
|
||||||
|
// smaller of the two dimensions.
|
||||||
|
let dWidth, dHeight, dx, dy;
|
||||||
|
if (this.width > this.height) {
|
||||||
|
dWidth = dSize;
|
||||||
|
dHeight = image.height * (dSize / image.width);
|
||||||
|
dx = 0;
|
||||||
|
dy = (dSize - dHeight) / 2;
|
||||||
|
} else {
|
||||||
|
dWidth = image.width * (dSize / image.height);
|
||||||
|
dHeight = dSize;
|
||||||
|
dx = (dSize - dWidth) / 2;
|
||||||
|
dy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = dSize;
|
||||||
|
canvas.height = dSize;
|
||||||
|
ctx.drawImage(this, 0, 0, this.width, this.height, dx, dy, dWidth, dHeight);
|
||||||
|
resolve(canvas.toDataURL("image/png"));
|
||||||
|
};
|
||||||
|
image.onerror = reject;
|
||||||
|
image.src = imageURL;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// These URLs should already be properly escaped, but make doubly sure CSS
|
||||||
|
// string escape characters are escaped here, since they could lead to a
|
||||||
|
// sandbox break.
|
||||||
|
escapeUrl(url) {
|
||||||
|
return url.replace(/[\\\s"]/g, encodeURIComponent);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let StartupCache = {
|
||||||
|
DB_NAME: "ExtensionStartupCache",
|
||||||
|
|
||||||
|
SCHEMA_VERSION: 2,
|
||||||
|
|
||||||
|
STORE_NAMES: Object.freeze(["locales", "manifests", "schemas"]),
|
||||||
|
|
||||||
|
dbPromise: null,
|
||||||
|
|
||||||
|
initDB(db) {
|
||||||
|
for (let name of StartupCache.STORE_NAMES) {
|
||||||
|
try {
|
||||||
|
db.deleteObjectStore(name);
|
||||||
|
} catch (e) {
|
||||||
|
// Don't worry if the store doesn't already exist.
|
||||||
|
}
|
||||||
|
db.createObjectStore(name, {keyPath: "key"});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clearAddonData(id) {
|
||||||
|
let range = IDBKeyRange.bound([id], [id, "\uFFFF"]);
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
this.locales.delete(range),
|
||||||
|
this.manifests.delete(range),
|
||||||
|
]).catch(e => {
|
||||||
|
// Ignore the error. It happens when we try to flush the add-on
|
||||||
|
// data after the AddonManager has flushed the entire startup cache.
|
||||||
|
this.dbPromise = this.reallyOpen(true).catch(e => {});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async reallyOpen(invalidate = false) {
|
||||||
|
if (this.dbPromise) {
|
||||||
|
let db = await this.dbPromise;
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidate) {
|
||||||
|
IndexedDB.deleteDatabase(this.DB_NAME, {storage: "persistent"});
|
||||||
|
}
|
||||||
|
|
||||||
|
return IndexedDB.open(this.DB_NAME,
|
||||||
|
{storage: "persistent", version: this.SCHEMA_VERSION},
|
||||||
|
db => this.initDB(db));
|
||||||
|
},
|
||||||
|
|
||||||
|
async open() {
|
||||||
|
if (!this.dbPromise) {
|
||||||
|
this.dbPromise = this.reallyOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dbPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
observe(subject, topic, data) {
|
||||||
|
if (topic === "startupcache-invalidate") {
|
||||||
|
this.dbPromise = this.reallyOpen(true).catch(e => {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Services.obs.addObserver(StartupCache, "startupcache-invalidate");
|
||||||
|
|
||||||
|
class CacheStore {
|
||||||
|
constructor(storeName) {
|
||||||
|
this.storeName = storeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(key, createFunc) {
|
||||||
|
let db;
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
db = await StartupCache.open();
|
||||||
|
|
||||||
|
result = await db.objectStore(this.storeName)
|
||||||
|
.get(key);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
|
||||||
|
return createFunc(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result === undefined) {
|
||||||
|
let value = await createFunc(key);
|
||||||
|
result = {key, value};
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.objectStore(this.storeName, "readwrite")
|
||||||
|
.put(result);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result && result.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll() {
|
||||||
|
let result = new Map();
|
||||||
|
try {
|
||||||
|
let db = await StartupCache.open();
|
||||||
|
|
||||||
|
let results = await db.objectStore(this.storeName)
|
||||||
|
.getAll();
|
||||||
|
for (let {key, value} of results) {
|
||||||
|
result.set(key, value);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(key) {
|
||||||
|
let db = await StartupCache.open();
|
||||||
|
|
||||||
|
return db.objectStore(this.storeName, "readwrite").delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let name of StartupCache.STORE_NAMES) {
|
||||||
|
StartupCache[name] = new CacheStore(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ExtensionParent = {
|
||||||
extensionNameFromURI,
|
extensionNameFromURI,
|
||||||
GlobalManager,
|
GlobalManager,
|
||||||
HiddenExtensionPage,
|
HiddenExtensionPage,
|
||||||
|
IconDetails,
|
||||||
ParentAPIManager,
|
ParentAPIManager,
|
||||||
|
StartupCache,
|
||||||
WebExtensionPolicy,
|
WebExtensionPolicy,
|
||||||
apiManager,
|
apiManager,
|
||||||
get baseManifestProperties() {
|
get baseManifestProperties() {
|
||||||
|
@ -1095,3 +1394,39 @@ const ExtensionParent = {
|
||||||
watchExtensionProxyContextLoad,
|
watchExtensionProxyContextLoad,
|
||||||
DebugUtils,
|
DebugUtils,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(ExtensionParent, "PlatformInfo", () => {
|
||||||
|
return Object.freeze({
|
||||||
|
os: (function() {
|
||||||
|
let os = AppConstants.platform;
|
||||||
|
if (os == "macosx") {
|
||||||
|
os = "mac";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
})(),
|
||||||
|
arch: (function() {
|
||||||
|
let abi = Services.appinfo.XPCOMABI;
|
||||||
|
let [arch] = abi.split("-");
|
||||||
|
if (arch == "x86") {
|
||||||
|
arch = "x86-32";
|
||||||
|
} else if (arch == "x86_64") {
|
||||||
|
arch = "x86-64";
|
||||||
|
}
|
||||||
|
return arch;
|
||||||
|
})(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the browser_style stylesheets needed for extension popups and sidebars.
|
||||||
|
* @returns {Array<string>} an array of stylesheets needed for the current platform.
|
||||||
|
*/
|
||||||
|
XPCOMUtils.defineLazyGetter(ExtensionParent, "extensionStylesheets", () => {
|
||||||
|
let stylesheets = ["chrome://browser/content/extension.css"];
|
||||||
|
|
||||||
|
if (AppConstants.platform === "macosx") {
|
||||||
|
stylesheets.push("chrome://browser/content/extension-mac.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
return stylesheets;
|
||||||
|
});
|
||||||
|
|
|
@ -11,29 +11,13 @@ const Cc = Components.classes;
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
const Cr = Components.results;
|
const Cr = Components.results;
|
||||||
|
|
||||||
const INTEGER = /^[1-9]\d*$/;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
|
||||||
"resource://gre/modules/AddonManager.jsm");
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
|
||||||
"resource://gre/modules/AppConstants.jsm");
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
|
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
|
||||||
"resource://gre/modules/Console.jsm");
|
"resource://gre/modules/Console.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "IndexedDB",
|
|
||||||
"resource://gre/modules/IndexedDB.jsm");
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||||
"resource://gre/modules/MessageChannel.jsm");
|
"resource://gre/modules/MessageChannel.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
|
||||||
"resource://gre/modules/Preferences.jsm");
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
|
||||||
"resource://gre/modules/Schemas.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
|
|
||||||
"@mozilla.org/content/style-sheet-service;1",
|
|
||||||
"nsIStyleSheetService");
|
|
||||||
|
|
||||||
function getConsole() {
|
function getConsole() {
|
||||||
return new ConsoleAPI({
|
return new ConsoleAPI({
|
||||||
|
@ -51,134 +35,6 @@ function getUniqueId() {
|
||||||
return `${nextId++}-${uniqueProcessID}`;
|
return `${nextId++}-${uniqueProcessID}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let StartupCache = {
|
|
||||||
DB_NAME: "ExtensionStartupCache",
|
|
||||||
|
|
||||||
SCHEMA_VERSION: 2,
|
|
||||||
|
|
||||||
STORE_NAMES: Object.freeze(["locales", "manifests", "schemas"]),
|
|
||||||
|
|
||||||
dbPromise: null,
|
|
||||||
|
|
||||||
initDB(db) {
|
|
||||||
for (let name of StartupCache.STORE_NAMES) {
|
|
||||||
try {
|
|
||||||
db.deleteObjectStore(name);
|
|
||||||
} catch (e) {
|
|
||||||
// Don't worry if the store doesn't already exist.
|
|
||||||
}
|
|
||||||
db.createObjectStore(name, {keyPath: "key"});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clearAddonData(id) {
|
|
||||||
let range = IDBKeyRange.bound([id], [id, "\uFFFF"]);
|
|
||||||
|
|
||||||
return Promise.all([
|
|
||||||
this.locales.delete(range),
|
|
||||||
this.manifests.delete(range),
|
|
||||||
]).catch(e => {
|
|
||||||
// Ignore the error. It happens when we try to flush the add-on
|
|
||||||
// data after the AddonManager has flushed the entire startup cache.
|
|
||||||
this.dbPromise = this.reallyOpen(true).catch(e => {});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async reallyOpen(invalidate = false) {
|
|
||||||
if (this.dbPromise) {
|
|
||||||
let db = await this.dbPromise;
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalidate) {
|
|
||||||
if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT) {
|
|
||||||
IndexedDB.deleteDatabase(this.DB_NAME, {storage: "persistent"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return IndexedDB.open(this.DB_NAME,
|
|
||||||
{storage: "persistent", version: this.SCHEMA_VERSION},
|
|
||||||
db => this.initDB(db));
|
|
||||||
},
|
|
||||||
|
|
||||||
async open() {
|
|
||||||
if (!this.dbPromise) {
|
|
||||||
this.dbPromise = this.reallyOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dbPromise;
|
|
||||||
},
|
|
||||||
|
|
||||||
observe(subject, topic, data) {
|
|
||||||
if (topic === "startupcache-invalidate") {
|
|
||||||
this.dbPromise = this.reallyOpen(true).catch(e => {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Services.obs.addObserver(StartupCache, "startupcache-invalidate");
|
|
||||||
|
|
||||||
class CacheStore {
|
|
||||||
constructor(storeName) {
|
|
||||||
this.storeName = storeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get(key, createFunc) {
|
|
||||||
let db;
|
|
||||||
let result;
|
|
||||||
try {
|
|
||||||
db = await StartupCache.open();
|
|
||||||
|
|
||||||
result = await db.objectStore(this.storeName)
|
|
||||||
.get(key);
|
|
||||||
} catch (e) {
|
|
||||||
Cu.reportError(e);
|
|
||||||
|
|
||||||
return createFunc(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result === undefined) {
|
|
||||||
let value = await createFunc(key);
|
|
||||||
result = {key, value};
|
|
||||||
|
|
||||||
try {
|
|
||||||
db.objectStore(this.storeName, "readwrite")
|
|
||||||
.put(result);
|
|
||||||
} catch (e) {
|
|
||||||
Cu.reportError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result && result.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAll() {
|
|
||||||
let result = new Map();
|
|
||||||
try {
|
|
||||||
let db = await StartupCache.open();
|
|
||||||
|
|
||||||
let results = await db.objectStore(this.storeName)
|
|
||||||
.getAll();
|
|
||||||
for (let {key, value} of results) {
|
|
||||||
result.set(key, value);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
Cu.reportError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete(key) {
|
|
||||||
let db = await StartupCache.open();
|
|
||||||
|
|
||||||
return db.objectStore(this.storeName, "readwrite").delete(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let name of StartupCache.STORE_NAMES) {
|
|
||||||
StartupCache[name] = new CacheStore(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Error subclass for which complete error messages are always passed
|
* An Error subclass for which complete error messages are always passed
|
||||||
|
@ -251,21 +107,6 @@ function instanceOf(value, type) {
|
||||||
return {}.toString.call(value) == `[object ${type}]`;
|
return {}.toString.call(value) == `[object ${type}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extend the object |obj| with the property descriptors of each object in
|
|
||||||
// |args|.
|
|
||||||
function extend(obj, ...args) {
|
|
||||||
for (let arg of args) {
|
|
||||||
let props = [...Object.getOwnPropertyNames(arg),
|
|
||||||
...Object.getOwnPropertySymbols(arg)];
|
|
||||||
for (let prop of props) {
|
|
||||||
let descriptor = Object.getOwnPropertyDescriptor(arg, prop);
|
|
||||||
Object.defineProperty(obj, prop, descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to a WeakMap, but creates a new key with the given
|
* Similar to a WeakMap, but creates a new key with the given
|
||||||
* constructor if one is not present.
|
* constructor if one is not present.
|
||||||
|
@ -308,176 +149,6 @@ function getInnerWindowID(window) {
|
||||||
return getWinUtils(window).currentInnerWindowID;
|
return getWinUtils(window).currentInnerWindowID;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpreadArgs extends Array {
|
|
||||||
constructor(args) {
|
|
||||||
super();
|
|
||||||
this.push(...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manages icon details for toolbar buttons in the |pageAction| and
|
|
||||||
// |browserAction| APIs.
|
|
||||||
let IconDetails = {
|
|
||||||
// WeakMap<Extension -> Map<url-string -> object>>
|
|
||||||
iconCache: new DefaultWeakMap(() => new Map()),
|
|
||||||
|
|
||||||
// Normalizes the various acceptable input formats into an object
|
|
||||||
// with icon size as key and icon URL as value.
|
|
||||||
//
|
|
||||||
// If a context is specified (function is called from an extension):
|
|
||||||
// Throws an error if an invalid icon size was provided or the
|
|
||||||
// extension is not allowed to load the specified resources.
|
|
||||||
//
|
|
||||||
// If no context is specified, instead of throwing an error, this
|
|
||||||
// function simply logs a warning message.
|
|
||||||
normalize(details, extension, context = null) {
|
|
||||||
if (!details.imageData && typeof details.path === "string") {
|
|
||||||
let icons = this.iconCache.get(extension);
|
|
||||||
|
|
||||||
let baseURI = context ? context.uri : extension.baseURI;
|
|
||||||
let url = baseURI.resolve(details.path);
|
|
||||||
|
|
||||||
let icon = icons.get(url);
|
|
||||||
if (!icon) {
|
|
||||||
icon = this._normalize(details, extension, context);
|
|
||||||
icons.set(url, icon);
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._normalize(details, extension, context);
|
|
||||||
},
|
|
||||||
|
|
||||||
_normalize(details, extension, context = null) {
|
|
||||||
let result = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (details.imageData) {
|
|
||||||
let imageData = details.imageData;
|
|
||||||
|
|
||||||
if (typeof imageData == "string") {
|
|
||||||
imageData = {"19": imageData};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let size of Object.keys(imageData)) {
|
|
||||||
if (!INTEGER.test(size)) {
|
|
||||||
throw new ExtensionError(`Invalid icon size ${size}, must be an integer`);
|
|
||||||
}
|
|
||||||
result[size] = imageData[size];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (details.path) {
|
|
||||||
let path = details.path;
|
|
||||||
if (typeof path != "object") {
|
|
||||||
path = {"19": path};
|
|
||||||
}
|
|
||||||
|
|
||||||
let baseURI = context ? context.uri : extension.baseURI;
|
|
||||||
|
|
||||||
for (let size of Object.keys(path)) {
|
|
||||||
if (!INTEGER.test(size)) {
|
|
||||||
throw new ExtensionError(`Invalid icon size ${size}, must be an integer`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = baseURI.resolve(path[size]);
|
|
||||||
|
|
||||||
// The Chrome documentation specifies these parameters as
|
|
||||||
// relative paths. We currently accept absolute URLs as well,
|
|
||||||
// which means we need to check that the extension is allowed
|
|
||||||
// to load them. This will throw an error if it's not allowed.
|
|
||||||
try {
|
|
||||||
Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(
|
|
||||||
extension.principal, url,
|
|
||||||
Services.scriptSecurityManager.DISALLOW_SCRIPT);
|
|
||||||
} catch (e) {
|
|
||||||
throw new ExtensionError(`Illegal URL ${url}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
result[size] = url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Function is called from extension code, delegate error.
|
|
||||||
if (context) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
// If there's no context, it's because we're handling this
|
|
||||||
// as a manifest directive. Log a warning rather than
|
|
||||||
// raising an error.
|
|
||||||
extension.manifestError(`Invalid icon data: ${e}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Returns the appropriate icon URL for the given icons object and the
|
|
||||||
// screen resolution of the given window.
|
|
||||||
getPreferredIcon(icons, extension = null, size = 16) {
|
|
||||||
const DEFAULT = "chrome://browser/content/extension.svg";
|
|
||||||
|
|
||||||
let bestSize = null;
|
|
||||||
if (icons[size]) {
|
|
||||||
bestSize = size;
|
|
||||||
} else if (icons[2 * size]) {
|
|
||||||
bestSize = 2 * size;
|
|
||||||
} else {
|
|
||||||
let sizes = Object.keys(icons)
|
|
||||||
.map(key => parseInt(key, 10))
|
|
||||||
.sort((a, b) => a - b);
|
|
||||||
|
|
||||||
bestSize = sizes.find(candidate => candidate > size) || sizes.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestSize) {
|
|
||||||
return {size: bestSize, icon: icons[bestSize]};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {size, icon: DEFAULT};
|
|
||||||
},
|
|
||||||
|
|
||||||
convertImageURLToDataURL(imageURL, contentWindow, browserWindow, size = 18) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let image = new contentWindow.Image();
|
|
||||||
image.onload = function() {
|
|
||||||
let canvas = contentWindow.document.createElement("canvas");
|
|
||||||
let ctx = canvas.getContext("2d");
|
|
||||||
let dSize = size * browserWindow.devicePixelRatio;
|
|
||||||
|
|
||||||
// Scales the image while maintaing width to height ratio.
|
|
||||||
// If the width and height differ, the image is centered using the
|
|
||||||
// smaller of the two dimensions.
|
|
||||||
let dWidth, dHeight, dx, dy;
|
|
||||||
if (this.width > this.height) {
|
|
||||||
dWidth = dSize;
|
|
||||||
dHeight = image.height * (dSize / image.width);
|
|
||||||
dx = 0;
|
|
||||||
dy = (dSize - dHeight) / 2;
|
|
||||||
} else {
|
|
||||||
dWidth = image.width * (dSize / image.height);
|
|
||||||
dHeight = dSize;
|
|
||||||
dx = (dSize - dWidth) / 2;
|
|
||||||
dy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.width = dSize;
|
|
||||||
canvas.height = dSize;
|
|
||||||
ctx.drawImage(this, 0, 0, this.width, this.height, dx, dy, dWidth, dHeight);
|
|
||||||
resolve(canvas.toDataURL("image/png"));
|
|
||||||
};
|
|
||||||
image.onerror = reject;
|
|
||||||
image.src = imageURL;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// These URLs should already be properly escaped, but make doubly sure CSS
|
|
||||||
// string escape characters are escaped here, since they could lead to a
|
|
||||||
// sandbox break.
|
|
||||||
escapeUrl(url) {
|
|
||||||
return url.replace(/[\\\s"]/g, encodeURIComponent);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const LISTENERS = Symbol("listeners");
|
const LISTENERS = Symbol("listeners");
|
||||||
const ONCE_MAP = Symbol("onceMap");
|
const ONCE_MAP = Symbol("onceMap");
|
||||||
|
|
||||||
|
@ -571,47 +242,6 @@ class EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple API for event listeners where events never fire.
|
|
||||||
function ignoreEvent(context, name) {
|
|
||||||
return {
|
|
||||||
addListener: function(callback) {
|
|
||||||
let id = context.extension.id;
|
|
||||||
let frame = Components.stack.caller;
|
|
||||||
let msg = `In add-on ${id}, attempting to use listener "${name}", which is unimplemented.`;
|
|
||||||
let scriptError = Cc["@mozilla.org/scripterror;1"]
|
|
||||||
.createInstance(Ci.nsIScriptError);
|
|
||||||
scriptError.init(msg, frame.filename, null, frame.lineNumber,
|
|
||||||
frame.columnNumber, Ci.nsIScriptError.warningFlag,
|
|
||||||
"content javascript");
|
|
||||||
let consoleService = Cc["@mozilla.org/consoleservice;1"]
|
|
||||||
.getService(Ci.nsIConsoleService);
|
|
||||||
consoleService.logMessage(scriptError);
|
|
||||||
},
|
|
||||||
removeListener: function(callback) {},
|
|
||||||
hasListener: function(callback) {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy an API object from |source| into the scope |dest|.
|
|
||||||
function injectAPI(source, dest) {
|
|
||||||
for (let prop in source) {
|
|
||||||
// Skip names prefixed with '_'.
|
|
||||||
if (prop[0] == "_") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let desc = Object.getOwnPropertyDescriptor(source, prop);
|
|
||||||
if (typeof(desc.value) == "function") {
|
|
||||||
Cu.exportFunction(desc.value, dest, {defineAs: prop});
|
|
||||||
} else if (typeof(desc.value) == "object") {
|
|
||||||
let obj = Cu.createObjectIn(dest, {defineAs: prop});
|
|
||||||
injectAPI(desc.value, obj);
|
|
||||||
} else {
|
|
||||||
Object.defineProperty(dest, prop, desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set with a limited number of slots, which flushes older entries as
|
* A set with a limited number of slots, which flushes older entries as
|
||||||
* newer ones are added.
|
* newer ones are added.
|
||||||
|
@ -744,28 +374,6 @@ function flushJarCache(jarPath) {
|
||||||
Services.obs.notifyObservers(null, "flush-cache-entry", jarPath);
|
Services.obs.notifyObservers(null, "flush-cache-entry", jarPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function PlatformInfo() {
|
|
||||||
return Object.freeze({
|
|
||||||
os: (function() {
|
|
||||||
let os = AppConstants.platform;
|
|
||||||
if (os == "macosx") {
|
|
||||||
os = "mac";
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
})(),
|
|
||||||
arch: (function() {
|
|
||||||
let abi = Services.appinfo.XPCOMABI;
|
|
||||||
let [arch] = abi.split("-");
|
|
||||||
if (arch == "x86") {
|
|
||||||
arch = "x86-32";
|
|
||||||
} else if (arch == "x86_64") {
|
|
||||||
arch = "x86-64";
|
|
||||||
}
|
|
||||||
return arch;
|
|
||||||
})(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert any of several different representations of a date/time to a Date object.
|
* Convert any of several different representations of a date/time to a Date object.
|
||||||
* Accepts several formats:
|
* Accepts several formats:
|
||||||
|
@ -784,25 +392,6 @@ function normalizeTime(date) {
|
||||||
? parseInt(date, 10) : date);
|
? parseInt(date, 10) : date);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stylesheetMap = new DefaultMap(url => {
|
|
||||||
let uri = Services.io.newURI(url);
|
|
||||||
return styleSheetService.preloadSheet(uri, styleSheetService.AGENT_SHEET);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retreives the browser_style stylesheets needed for extension popups and sidebars.
|
|
||||||
* @returns {Array<string>} an array of stylesheets needed for the current platform.
|
|
||||||
*/
|
|
||||||
function extensionStylesheets() {
|
|
||||||
let stylesheets = ["chrome://browser/content/extension.css"];
|
|
||||||
|
|
||||||
if (AppConstants.platform === "macosx") {
|
|
||||||
stylesheets.push("chrome://browser/content/extension-mac.css");
|
|
||||||
}
|
|
||||||
|
|
||||||
return stylesheets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a lazy getter for the given property on the given object. The
|
* Defines a lazy getter for the given property on the given object. The
|
||||||
* first time the property is accessed, the return value of the getter
|
* first time the property is accessed, the return value of the getter
|
||||||
|
@ -844,26 +433,6 @@ function defineLazyGetter(object, prop, getter) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function findPathInObject(obj, path, printErrors = true) {
|
|
||||||
let parent;
|
|
||||||
for (let elt of path.split(".")) {
|
|
||||||
if (!obj || !(elt in obj)) {
|
|
||||||
if (printErrors) {
|
|
||||||
Cu.reportError(`WebExtension API ${path} not found (it may be unimplemented by Firefox).`);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent = obj;
|
|
||||||
obj = obj[elt];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof obj === "function") {
|
|
||||||
return obj.bind(parent);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Acts as a proxy for a message manager or message manager owner, and
|
* Acts as a proxy for a message manager or message manager owner, and
|
||||||
* tracks docShell swaps so that messages are always sent to the same
|
* tracks docShell swaps so that messages are always sent to the same
|
||||||
|
@ -1041,34 +610,8 @@ class MessageManagerProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Classify an individual permission from a webextension manifest
|
|
||||||
* as a host/origin permission, an api permission, or a regular permission.
|
|
||||||
*
|
|
||||||
* @param {string} perm The permission string to classify
|
|
||||||
*
|
|
||||||
* @returns {object}
|
|
||||||
* An object with exactly one of the following properties:
|
|
||||||
* "origin" to indicate this is a host/origin permission.
|
|
||||||
* "api" to indicate this is an api permission
|
|
||||||
* (as used for webextensions experiments).
|
|
||||||
* "permission" to indicate this is a regular permission.
|
|
||||||
*/
|
|
||||||
function classifyPermission(perm) {
|
|
||||||
let match = /^(\w+)(?:\.(\w+)(?:\.\w+)*)?$/.exec(perm);
|
|
||||||
if (!match) {
|
|
||||||
return {origin: perm};
|
|
||||||
} else if (match[1] == "experiments" && match[2]) {
|
|
||||||
return {api: match[2]};
|
|
||||||
}
|
|
||||||
return {permission: perm};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ExtensionUtils = {
|
this.ExtensionUtils = {
|
||||||
classifyPermission,
|
|
||||||
defineLazyGetter,
|
defineLazyGetter,
|
||||||
extend,
|
|
||||||
findPathInObject,
|
|
||||||
flushJarCache,
|
flushJarCache,
|
||||||
getConsole,
|
getConsole,
|
||||||
getInnerWindowID,
|
getInnerWindowID,
|
||||||
|
@ -1076,8 +619,6 @@ this.ExtensionUtils = {
|
||||||
getUniqueId,
|
getUniqueId,
|
||||||
filterStack,
|
filterStack,
|
||||||
getWinUtils,
|
getWinUtils,
|
||||||
ignoreEvent,
|
|
||||||
injectAPI,
|
|
||||||
instanceOf,
|
instanceOf,
|
||||||
normalizeTime,
|
normalizeTime,
|
||||||
promiseDocumentLoaded,
|
promiseDocumentLoaded,
|
||||||
|
@ -1088,17 +629,10 @@ this.ExtensionUtils = {
|
||||||
runSafeSync,
|
runSafeSync,
|
||||||
runSafeSyncWithoutClone,
|
runSafeSyncWithoutClone,
|
||||||
runSafeWithoutClone,
|
runSafeWithoutClone,
|
||||||
stylesheetMap,
|
|
||||||
DefaultMap,
|
DefaultMap,
|
||||||
DefaultWeakMap,
|
DefaultWeakMap,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
ExtensionError,
|
ExtensionError,
|
||||||
IconDetails,
|
|
||||||
LimitedSet,
|
LimitedSet,
|
||||||
MessageManagerProxy,
|
MessageManagerProxy,
|
||||||
SpreadArgs,
|
|
||||||
StartupCache,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this.ExtensionUtils, "extensionStylesheets", extensionStylesheets);
|
|
||||||
XPCOMUtils.defineLazyGetter(this.ExtensionUtils, "PlatformInfo", PlatformInfo);
|
|
||||||
|
|
|
@ -59,6 +59,8 @@ const PREF_MAX_WRITE = "webextensions.native-messaging.max-output-message-bytes"
|
||||||
|
|
||||||
const REGPATH = "Software\\Mozilla\\NativeMessagingHosts";
|
const REGPATH = "Software\\Mozilla\\NativeMessagingHosts";
|
||||||
|
|
||||||
|
const global = this;
|
||||||
|
|
||||||
this.HostManifestManager = {
|
this.HostManifestManager = {
|
||||||
_initializePromise: null,
|
_initializePromise: null,
|
||||||
_lookup: null,
|
_lookup: null,
|
||||||
|
@ -236,7 +238,7 @@ this.NativeApp = class extends EventEmitter {
|
||||||
app.on("message", (what, msg) => port.postMessage(msg));
|
app.on("message", (what, msg) => port.postMessage(msg));
|
||||||
/* eslint-enable mozilla/balanced-listeners */
|
/* eslint-enable mozilla/balanced-listeners */
|
||||||
|
|
||||||
port.registerOnMessage(msg => app.send(msg));
|
port.registerOnMessage(holder => app.send(holder));
|
||||||
port.registerOnDisconnect(msg => app.close());
|
port.registerOnDisconnect(msg => app.close());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,10 +336,11 @@ this.NativeApp = class extends EventEmitter {
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
send(msg) {
|
send(holder) {
|
||||||
if (this._isDisconnected) {
|
if (this._isDisconnected) {
|
||||||
throw new this.context.cloneScope.Error("Attempt to postMessage on disconnected port");
|
throw new this.context.cloneScope.Error("Attempt to postMessage on disconnected port");
|
||||||
}
|
}
|
||||||
|
let msg = holder.deserialize(global);
|
||||||
if (Cu.getClassName(msg, true) != "ArrayBuffer") {
|
if (Cu.getClassName(msg, true) != "ArrayBuffer") {
|
||||||
// This error cannot be triggered by extensions; it indicates an error in
|
// This error cannot be triggered by extensions; it indicates an error in
|
||||||
// our implementation.
|
// our implementation.
|
||||||
|
@ -412,14 +415,14 @@ this.NativeApp = class extends EventEmitter {
|
||||||
this._cleanup();
|
this._cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(msg) {
|
sendMessage(holder) {
|
||||||
let responsePromise = new Promise((resolve, reject) => {
|
let responsePromise = new Promise((resolve, reject) => {
|
||||||
this.once("message", (what, msg) => { resolve(msg); });
|
this.once("message", (what, msg) => { resolve(msg); });
|
||||||
this.once("disconnect", (what, err) => { reject(err); });
|
this.once("disconnect", (what, err) => { reject(err); });
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = this.startupPromise.then(() => {
|
let result = this.startupPromise.then(() => {
|
||||||
this.send(msg);
|
this.send(holder);
|
||||||
return responsePromise;
|
return responsePromise;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,16 +21,19 @@ Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||||
var {
|
var {
|
||||||
DefaultMap,
|
DefaultMap,
|
||||||
DefaultWeakMap,
|
DefaultWeakMap,
|
||||||
StartupCache,
|
|
||||||
instanceOf,
|
instanceOf,
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||||
|
"resource://gre/modules/ExtensionParent.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||||
"resource://gre/modules/NetUtil.jsm");
|
"resource://gre/modules/NetUtil.jsm");
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "contentPolicyService",
|
XPCOMUtils.defineLazyServiceGetter(this, "contentPolicyService",
|
||||||
"@mozilla.org/addons/content-policy;1",
|
"@mozilla.org/addons/content-policy;1",
|
||||||
"nsIAddonContentPolicy");
|
"nsIAddonContentPolicy");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "StartupCache", () => ExtensionParent.StartupCache);
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["Schemas"];
|
this.EXPORTED_SYMBOLS = ["Schemas"];
|
||||||
|
|
||||||
const {DEBUG} = AppConstants;
|
const {DEBUG} = AppConstants;
|
||||||
|
|
|
@ -9,6 +9,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "clearTimeout",
|
XPCOMUtils.defineLazyModuleGetter(this, "clearTimeout",
|
||||||
"resource://gre/modules/Timer.jsm");
|
"resource://gre/modules/Timer.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionCommon",
|
||||||
|
"resource://gre/modules/ExtensionCommon.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||||
"resource://gre/modules/NetUtil.jsm");
|
"resource://gre/modules/NetUtil.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "require",
|
XPCOMUtils.defineLazyModuleGetter(this, "require",
|
||||||
|
@ -19,7 +21,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
|
||||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||||
const {
|
const {
|
||||||
getWinUtils,
|
getWinUtils,
|
||||||
stylesheetMap,
|
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
/* eslint-env mozilla/frame-script */
|
/* eslint-env mozilla/frame-script */
|
||||||
|
@ -124,7 +125,7 @@ const BrowserListener = {
|
||||||
let winUtils = getWinUtils(content);
|
let winUtils = getWinUtils(content);
|
||||||
|
|
||||||
for (let url of this.stylesheets) {
|
for (let url of this.stylesheets) {
|
||||||
winUtils.addSheet(stylesheetMap.get(url), winUtils.AGENT_SHEET);
|
winUtils.addSheet(ExtensionCommon.stylesheetMap.get(url), winUtils.AGENT_SHEET);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||||
|
"resource://gre/modules/AppConstants.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||||
"resource://gre/modules/Downloads.jsm");
|
"resource://gre/modules/Downloads.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
|
XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
|
||||||
|
@ -14,11 +16,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
|
||||||
"resource://gre/modules/EventEmitter.jsm");
|
"resource://gre/modules/EventEmitter.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
ignoreEvent,
|
|
||||||
normalizeTime,
|
normalizeTime,
|
||||||
PlatformInfo,
|
|
||||||
} = ExtensionUtils;
|
} = ExtensionUtils;
|
||||||
|
|
||||||
|
var {
|
||||||
|
ignoreEvent,
|
||||||
|
} = ExtensionCommon;
|
||||||
|
|
||||||
const DOWNLOAD_ITEM_FIELDS = ["id", "url", "referrer", "filename", "incognito",
|
const DOWNLOAD_ITEM_FIELDS = ["id", "url", "referrer", "filename", "incognito",
|
||||||
"danger", "mime", "startTime", "endTime",
|
"danger", "mime", "startTime", "endTime",
|
||||||
"estimatedEndTime", "state",
|
"estimatedEndTime", "state",
|
||||||
|
@ -392,7 +396,7 @@ this.downloads = class extends ExtensionAPI {
|
||||||
downloads: {
|
downloads: {
|
||||||
download(options) {
|
download(options) {
|
||||||
let {filename} = options;
|
let {filename} = options;
|
||||||
if (filename && PlatformInfo.os === "win") {
|
if (filename && AppConstants.platform === "win") {
|
||||||
// cross platform javascript code uses "/"
|
// cross platform javascript code uses "/"
|
||||||
filename = filename.replace(/\//g, "\\");
|
filename = filename.replace(/\//g, "\\");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
|
||||||
|
|
||||||
var {
|
var {
|
||||||
ignoreEvent,
|
ignoreEvent,
|
||||||
} = ExtensionUtils;
|
} = ExtensionCommon;
|
||||||
|
|
||||||
// WeakMap[Extension -> Map[id -> Notification]]
|
// WeakMap[Extension -> Map[id -> Notification]]
|
||||||
let notificationsMap = new WeakMap();
|
let notificationsMap = new WeakMap();
|
||||||
|
|
|
@ -6,6 +6,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
||||||
"resource://gre/modules/AddonManager.jsm");
|
"resource://gre/modules/AddonManager.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
|
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
|
||||||
"resource://gre/modules/Extension.jsm");
|
"resource://gre/modules/Extension.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||||
|
"resource://gre/modules/ExtensionParent.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||||
"resource://gre/modules/NetUtil.jsm");
|
"resource://gre/modules/NetUtil.jsm");
|
||||||
|
|
||||||
|
@ -100,7 +102,7 @@ this.runtime = class extends ExtensionAPI {
|
||||||
},
|
},
|
||||||
|
|
||||||
getPlatformInfo: function() {
|
getPlatformInfo: function() {
|
||||||
return Promise.resolve(ExtensionUtils.PlatformInfo);
|
return Promise.resolve(ExtensionParent.PlatformInfo);
|
||||||
},
|
},
|
||||||
|
|
||||||
openOptionsPage: function() {
|
openOptionsPage: function() {
|
||||||
|
|
|
@ -18,15 +18,9 @@ add_task(async function test_sendMessage_error() {
|
||||||
// because there is no onMessage listener.
|
// because there is no onMessage listener.
|
||||||
[[null, null, null], "Could not establish connection. Receiving end does not exist."],
|
[[null, null, null], "Could not establish connection. Receiving end does not exist."],
|
||||||
|
|
||||||
// Structural cloning doesn't work with DOM but we fall back
|
// Structured cloning doesn't work with DOM objects
|
||||||
// JSON serialization, so we don't expect another error.
|
[[null, location, null], "The object could not be cloned."],
|
||||||
[[null, location, null], "Could not establish connection. Receiving end does not exist."],
|
[[null, [circ, location], null], "The object could not be cloned."],
|
||||||
|
|
||||||
// Structured cloning supports cyclic self-references.
|
|
||||||
[[null, [circ, location], null], "cyclic object value"],
|
|
||||||
// JSON serialization does not support cyclic references.
|
|
||||||
[[null, circ, null], "Could not establish connection. Receiving end does not exist."],
|
|
||||||
// (the last two tests shows whether sendMessage is implemented as structured cloning).
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Repeat all tests with the undefined value instead of null.
|
// Repeat all tests with the undefined value instead of null.
|
||||||
|
|
|
@ -287,7 +287,7 @@ while True:
|
||||||
});
|
});
|
||||||
|
|
||||||
let buffer = NativeApp.encodeMessage(mockContext, MSG);
|
let buffer = NativeApp.encodeMessage(mockContext, MSG);
|
||||||
app.send(buffer);
|
app.send(new StructuredCloneHolder(buffer));
|
||||||
await recvPromise;
|
await recvPromise;
|
||||||
|
|
||||||
app._cleanup();
|
app._cleanup();
|
||||||
|
|
|
@ -74,8 +74,8 @@ const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml
|
||||||
var gViewDefault = "addons://discover/";
|
var gViewDefault = "addons://discover/";
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "extensionStylesheets", () => {
|
XPCOMUtils.defineLazyGetter(this, "extensionStylesheets", () => {
|
||||||
const {ExtensionUtils} = Cu.import("resource://gre/modules/ExtensionUtils.jsm", {});
|
const {ExtensionParent} = Cu.import("resource://gre/modules/ExtensionParent.jsm", {});
|
||||||
return ExtensionUtils.extensionStylesheets;
|
return ExtensionParent.extensionStylesheets;
|
||||||
});
|
});
|
||||||
|
|
||||||
var gStrings = {};
|
var gStrings = {};
|
||||||
|
|
|
@ -41,7 +41,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Locale",
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||||
"resource://gre/modules/FileUtils.jsm");
|
"resource://gre/modules/FileUtils.jsm");
|
||||||
XPCOMUtils.defineLazyGetter(this, "IconDetails", () => {
|
XPCOMUtils.defineLazyGetter(this, "IconDetails", () => {
|
||||||
return Cu.import("resource://gre/modules/ExtensionUtils.jsm", {}).ExtensionUtils.IconDetails;
|
return Cu.import("resource://gre/modules/ExtensionParent.jsm", {}).ExtensionParent.IconDetails;
|
||||||
});
|
});
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче