зеркало из 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",
|
||||
"resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
var {
|
||||
extensionStylesheets,
|
||||
promiseEvent,
|
||||
} = ExtensionUtils;
|
||||
|
||||
|
@ -61,7 +61,7 @@ function getBrowser(sidebar) {
|
|||
"chrome://extensions/content/ext-browser-content.js", false);
|
||||
|
||||
browser.messageManager.sendAsyncMessage("Extension:InitBrowser", {
|
||||
stylesheets: extensionStylesheets,
|
||||
stylesheets: ExtensionParent.extensionStylesheets,
|
||||
});
|
||||
}
|
||||
return browser;
|
||||
|
|
|
@ -24,7 +24,6 @@ Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
|||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
extensionStylesheets,
|
||||
promiseEvent,
|
||||
} = ExtensionUtils;
|
||||
|
||||
|
@ -160,7 +159,7 @@ class BasePopup {
|
|||
let sheets = [];
|
||||
|
||||
if (this.browserStyle) {
|
||||
sheets.push(...extensionStylesheets);
|
||||
sheets.push(...ExtensionParent.extensionStylesheets);
|
||||
}
|
||||
if (!this.fixedWidth) {
|
||||
sheets.push(...standaloneStylesheets);
|
||||
|
|
|
@ -19,9 +19,14 @@ Cu.import("resource://gre/modules/EventEmitter.jsm");
|
|||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
IconDetails,
|
||||
} = ExtensionUtils;
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
var {
|
||||
IconDetails,
|
||||
} = ExtensionParent;
|
||||
|
||||
const POPUP_PRELOAD_TIMEOUT_MS = 200;
|
||||
|
||||
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: */
|
||||
"use strict";
|
||||
|
||||
var {
|
||||
PlatformInfo,
|
||||
} = ExtensionUtils;
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||
"resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
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();
|
||||
// For Windows, chrome.runtime expects 'win' while chrome.commands
|
||||
// expects 'windows'. We can special case this for now.
|
||||
let {PlatformInfo} = ExtensionParent;
|
||||
let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
|
||||
for (let [name, command] of Object.entries(manifest.commands)) {
|
||||
let suggested_key = command.suggested_key || {};
|
||||
|
|
|
@ -9,9 +9,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
|||
|
||||
var {
|
||||
ExtensionError,
|
||||
IconDetails,
|
||||
} = ExtensionUtils;
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
var {
|
||||
IconDetails,
|
||||
} = ExtensionParent;
|
||||
|
||||
const ACTION_MENU_TOP_LEVEL_LIMIT = 6;
|
||||
|
||||
// Map[Extension -> Map[ID -> MenuItem]]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
var {
|
||||
SpreadArgs,
|
||||
} = ExtensionUtils;
|
||||
} = ExtensionCommon;
|
||||
|
||||
this.devtools_inspectedWindow = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
|
|
|
@ -8,11 +8,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
|||
"resource:///modules/E10SUtils.jsm");
|
||||
|
||||
var {
|
||||
IconDetails,
|
||||
watchExtensionProxyContextLoad,
|
||||
} = ExtensionParent;
|
||||
|
||||
var {
|
||||
IconDetails,
|
||||
promiseEvent,
|
||||
} = ExtensionUtils;
|
||||
|
||||
|
|
|
@ -8,9 +8,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelPopup",
|
|||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
IconDetails,
|
||||
} = ExtensionUtils;
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
var {
|
||||
IconDetails,
|
||||
} = ExtensionParent;
|
||||
|
||||
// WeakMap[Extension -> PageAction]
|
||||
let pageActionMap = new WeakMap();
|
||||
|
||||
|
|
|
@ -9,11 +9,16 @@ XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
var {
|
||||
ExtensionError,
|
||||
IconDetails,
|
||||
} = ExtensionUtils;
|
||||
|
||||
var {
|
||||
IconDetails,
|
||||
} = ExtensionParent;
|
||||
|
||||
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
// WeakMap[Extension -> SidebarAction]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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);
|
||||
}
|
||||
this._factory = null;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var PdfJs = {
|
||||
|
@ -344,6 +344,6 @@ var PdfJs = {
|
|||
delete this._pdfStreamConverterFactory;
|
||||
|
||||
this._registered = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -65,5 +65,5 @@ this.PdfJsTelemetry = {
|
|||
onTimeToView(ms) {
|
||||
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_TIME_TO_VIEW_MS");
|
||||
histogram.add(ms);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -201,7 +201,7 @@ PdfDataListener.prototype = {
|
|||
if (this.errorCode) {
|
||||
value(null, this.errorCode);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -216,7 +216,7 @@ class ChromeActions {
|
|||
firstPageInfo: false,
|
||||
streamTypesUsed: [],
|
||||
fontTypesUsed: [],
|
||||
startAt: Date.now()
|
||||
startAt: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -304,7 +304,7 @@ class ChromeActions {
|
|||
onDataAvailable(aRequest, aContext, aDataInputStream, aOffset, aCount) {
|
||||
this.extListener.onDataAvailable(aRequest, aContext, aDataInputStream,
|
||||
aOffset, aCount);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
channel.asyncOpen2(listener);
|
||||
|
@ -573,7 +573,7 @@ class RangedChromeActions extends ChromeActions {
|
|||
return;
|
||||
}
|
||||
this.headers[aHeader] = aValue;
|
||||
}
|
||||
},
|
||||
};
|
||||
if (originalRequest.visitRequestHeaders) {
|
||||
originalRequest.visitRequestHeaders(httpHeaderVisitor);
|
||||
|
@ -670,7 +670,7 @@ class RangedChromeActions extends ChromeActions {
|
|||
pdfjsLoadAction: "rangeProgress",
|
||||
loaded: evt.loaded,
|
||||
}, "*");
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -763,7 +763,8 @@ class RequestListener {
|
|||
response = function sendResponse(aResponse) {
|
||||
try {
|
||||
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);
|
||||
return message.dispatchEvent(listener);
|
||||
} catch (e) {
|
||||
|
@ -996,7 +997,7 @@ PdfStreamConverter.prototype = {
|
|||
domWindow.frameElement.className === "previewPluginContentFrame";
|
||||
PdfJsTelemetry.onEmbed(isObjectEmbed);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Keep the URL the same so the browser sees it as the same.
|
||||
|
@ -1031,6 +1032,6 @@ PdfStreamConverter.prototype = {
|
|||
}
|
||||
delete this.dataListener;
|
||||
delete this.binaryStream;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ var PdfjsChromeUtils = {
|
|||
query: aEvent.detail.query,
|
||||
caseSensitive: aEvent.detail.caseSensitive,
|
||||
highlightAll: aEvent.detail.highlightAll,
|
||||
findPrevious: aEvent.detail.findPrevious
|
||||
findPrevious: aEvent.detail.findPrevious,
|
||||
};
|
||||
|
||||
let browser = aEvent.currentTarget.browser;
|
||||
|
@ -329,7 +329,7 @@ var PdfjsChromeUtils = {
|
|||
callback() {
|
||||
messageSent = true;
|
||||
sendMessage(true);
|
||||
}
|
||||
},
|
||||
}];
|
||||
notificationBox.appendNotification(data.message, "pdfjs-fallback", null,
|
||||
notificationBox.PRIORITY_INFO_LOW,
|
||||
|
@ -347,6 +347,6 @@ var PdfjsChromeUtils = {
|
|||
}
|
||||
sendMessage(false);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -65,35 +65,35 @@ var PdfjsContentUtils = {
|
|||
|
||||
clearUserPref(aPrefName) {
|
||||
this._mm.sendSyncMessage("PDFJS:Parent:clearUserPref", {
|
||||
name: aPrefName
|
||||
name: aPrefName,
|
||||
});
|
||||
},
|
||||
|
||||
setIntPref(aPrefName, aPrefValue) {
|
||||
this._mm.sendSyncMessage("PDFJS:Parent:setIntPref", {
|
||||
name: aPrefName,
|
||||
value: aPrefValue
|
||||
value: aPrefValue,
|
||||
});
|
||||
},
|
||||
|
||||
setBoolPref(aPrefName, aPrefValue) {
|
||||
this._mm.sendSyncMessage("PDFJS:Parent:setBoolPref", {
|
||||
name: aPrefName,
|
||||
value: aPrefValue
|
||||
value: aPrefValue,
|
||||
});
|
||||
},
|
||||
|
||||
setCharPref(aPrefName, aPrefValue) {
|
||||
this._mm.sendSyncMessage("PDFJS:Parent:setCharPref", {
|
||||
name: aPrefName,
|
||||
value: aPrefValue
|
||||
value: aPrefValue,
|
||||
});
|
||||
},
|
||||
|
||||
setStringPref(aPrefName, aPrefValue) {
|
||||
this._mm.sendSyncMessage("PDFJS:Parent:setStringPref", {
|
||||
name: aPrefName,
|
||||
value: aPrefValue
|
||||
value: aPrefValue,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -140,6 +140,6 @@ var PdfjsContentUtils = {
|
|||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -119,7 +119,7 @@ var FontInspector = (function FontInspectorClosure() {
|
|||
download.href = url[1];
|
||||
} else if (fontObj.data) {
|
||||
url = URL.createObjectURL(new Blob([fontObj.data], {
|
||||
type: fontObj.mimeType
|
||||
type: fontObj.mimeType,
|
||||
}));
|
||||
download.href = url;
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ var FontInspector = (function FontInspectorClosure() {
|
|||
resetSelection();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -243,7 +243,7 @@ var StepperManager = (function StepperManagerClosure() {
|
|||
saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
|
||||
breakPoints[pageIndex] = bps;
|
||||
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
|
||||
}
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -433,7 +433,7 @@ var Stepper = (function StepperClosure() {
|
|||
row.style.backgroundColor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
return Stepper;
|
||||
})();
|
||||
|
@ -497,7 +497,7 @@ var Stats = (function Stats() {
|
|||
cleanup() {
|
||||
stats = [];
|
||||
clear(this.panel);
|
||||
}
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -615,6 +615,6 @@ window.PDFBug = (function PDFBugClosure() {
|
|||
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) -->
|
||||
<base href="resource://pdf.js/web/">
|
||||
<script src="l10n.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>
|
||||
|
||||
</head>
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,9 +8,6 @@ Library('icuuc')
|
|||
FINAL_LIBRARY = 'icu'
|
||||
|
||||
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']
|
||||
|
||||
|
|
|
@ -330,6 +330,7 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
|||
nsIFrame* targetFrame = target->GetPrimaryFrame();
|
||||
nsRect targetRect;
|
||||
Maybe<nsRect> intersectionRect;
|
||||
bool isSameDoc = root && root->GetComposedDoc() == target->GetComposedDoc();
|
||||
|
||||
if (rootFrame && targetFrame) {
|
||||
// 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
|
||||
// Document as the intersection root, e.g. if root is an element of
|
||||
// the main document and target an element from an embedded iframe.
|
||||
if (target->GetComposedDoc() != root->GetComposedDoc()) {
|
||||
if (!isSameDoc) {
|
||||
continue;
|
||||
}
|
||||
// Skip further processing of this target if is not a descendant of the
|
||||
|
@ -410,12 +411,12 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
|||
intersectionRectRelativeToRoot,
|
||||
rootIntersectionRect
|
||||
);
|
||||
if (intersectionRect.isSome()) {
|
||||
intersectionRect = Some(nsLayoutUtils::TransformFrameRectToAncestor(
|
||||
nsLayoutUtils::GetContainingBlockForClientRect(rootFrame),
|
||||
intersectionRect.value(),
|
||||
targetFrame->PresContext()->PresShell()->GetRootScrollFrame()
|
||||
));
|
||||
if (intersectionRect.isSome() && !isSameDoc) {
|
||||
nsRect rect = intersectionRect.value();
|
||||
nsPresContext* presContext = targetFrame->PresContext();
|
||||
nsLayoutUtils::TransformRect(rootFrame,
|
||||
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/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/CryptoKey.h"
|
||||
#include "mozilla/dom/StructuredCloneBlob.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/DirectoryBinding.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
|
@ -357,6 +358,10 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
|
|||
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) {
|
||||
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
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
|
||||
{
|
||||
URLSearchParams* usp = nullptr;
|
||||
|
|
|
@ -62,6 +62,8 @@ enum StructuredCloneTags {
|
|||
|
||||
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!
|
||||
// Tags that are supported by IDB must not ever change. See the static assert
|
||||
// in IDBObjectStore.cpp, method CommonStructuredCloneReadCallback.
|
||||
|
|
|
@ -196,6 +196,7 @@ EXPORTS.mozilla.dom += [
|
|||
'SameProcessMessageQueue.h',
|
||||
'ScreenOrientation.h',
|
||||
'ShadowRoot.h',
|
||||
'StructuredCloneBlob.h',
|
||||
'StructuredCloneHolder.h',
|
||||
'StructuredCloneTags.h',
|
||||
'StyleSheetList.h',
|
||||
|
@ -338,6 +339,7 @@ UNIFIED_SOURCES += [
|
|||
'SameProcessMessageQueue.cpp',
|
||||
'ScreenOrientation.cpp',
|
||||
'ShadowRoot.cpp',
|
||||
'StructuredCloneBlob.cpp',
|
||||
'StructuredCloneHolder.cpp',
|
||||
'StyleSheetList.cpp',
|
||||
'SubtleCrypto.cpp',
|
||||
|
|
|
@ -3893,6 +3893,10 @@ nsDOMWindowUtils::GetContentAPZTestData(JSContext* aContext,
|
|||
if (!clm->GetAPZTestData().ToJS(aOutContentTestData, aContext)) {
|
||||
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) {
|
||||
return NS_OK;
|
||||
}
|
||||
APZTestData compositorSideData;
|
||||
if (ClientLayerManager* clm = lm->AsClientLayerManager()) {
|
||||
APZTestData 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;
|
||||
}
|
||||
}
|
||||
if (!compositorSideData.ToJS(aOutCompositorTestData, aContext)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
[test_range.js]
|
||||
head = head_xml.js
|
||||
[test_structuredcloneholder.js]
|
||||
[test_thirdpartyutil.js]
|
||||
[test_treewalker.js]
|
||||
head = head_xml.js
|
||||
|
|
|
@ -798,6 +798,11 @@ DOMInterfaces = {
|
|||
'implicitJSContext': [ 'close' ],
|
||||
},
|
||||
|
||||
'StructuredCloneHolder': {
|
||||
'nativeType': 'mozilla::dom::StructuredCloneBlob',
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'StyleSheet': {
|
||||
'nativeType': 'mozilla::StyleSheet',
|
||||
'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',
|
||||
'StorageManager.webidl',
|
||||
'StorageType.webidl',
|
||||
'StructuredCloneHolder.webidl',
|
||||
'StyleSheet.webidl',
|
||||
'StyleSheetList.webidl',
|
||||
'SubtleCrypto.webidl',
|
||||
|
|
|
@ -1334,9 +1334,10 @@ CompositorBridgeParent::FlushApzRepaints(const uint64_t& aLayersId)
|
|||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
||||
CompositorBridgeParent::GetAPZTestData(const uint64_t& aLayersId,
|
||||
APZTestData* aOutData)
|
||||
{
|
||||
MOZ_ASSERT(aLayersId == 0 || aLayersId == mRootLayerTreeID);
|
||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||
*aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ public:
|
|||
virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
|
||||
virtual CompositorAnimationStorage* GetAnimationStorage(const uint64_t& aId) { return nullptr; }
|
||||
virtual void FlushApzRepaints(const uint64_t& aLayersId) = 0;
|
||||
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
||||
virtual void GetAPZTestData(const uint64_t& aLayersId,
|
||||
APZTestData* aOutData) { }
|
||||
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
||||
const uint64_t& aInputBlockId,
|
||||
|
@ -232,7 +232,7 @@ public:
|
|||
override;
|
||||
virtual CompositorAnimationStorage* GetAnimationStorage(const uint64_t& aId) 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;
|
||||
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
||||
const uint64_t& aInputBlockId,
|
||||
|
|
|
@ -437,13 +437,12 @@ CrossProcessCompositorBridgeParent::FlushApzRepaints(const uint64_t& aLayersId)
|
|||
|
||||
void
|
||||
CrossProcessCompositorBridgeParent::GetAPZTestData(
|
||||
const LayerTransactionParent* aLayerTree,
|
||||
const uint64_t& aLayersId,
|
||||
APZTestData* aOutData)
|
||||
{
|
||||
uint64_t id = aLayerTree->GetId();
|
||||
MOZ_ASSERT(id != 0);
|
||||
MOZ_ASSERT(aLayersId != 0);
|
||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||
*aOutData = sIndirectLayerTrees[id].mApzTestData;
|
||||
*aOutData = sIndirectLayerTrees[aLayersId].mApzTestData;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -105,7 +105,7 @@ public:
|
|||
virtual CompositorAnimationStorage*
|
||||
GetAnimationStorage(const uint64_t& aId) 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;
|
||||
virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
|
||||
const uint64_t& aInputBlockId,
|
||||
|
|
|
@ -849,7 +849,7 @@ LayerTransactionParent::RecvFlushApzRepaints()
|
|||
mozilla::ipc::IPCResult
|
||||
LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData)
|
||||
{
|
||||
mCompositorBridge->GetAPZTestData(this, aOutData);
|
||||
mCompositorBridge->GetAPZTestData(GetId(), aOutData);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ include WebRenderMessages;
|
|||
include protocol PCompositorBridge;
|
||||
include protocol PTexture;
|
||||
|
||||
using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
|
||||
using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
|
||||
using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
|
||||
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
|
||||
|
@ -76,6 +77,7 @@ parent:
|
|||
sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
|
||||
sync SetAsyncZoom(ViewID scrollId, float zoom);
|
||||
async FlushApzRepaints();
|
||||
sync GetAPZTestData() returns (APZTestData data);
|
||||
|
||||
async Shutdown();
|
||||
child:
|
||||
|
|
|
@ -371,7 +371,7 @@ WebRenderBridgeParent::UpdateAPZ()
|
|||
if (RefPtr<APZCTreeManager> apzc = cbp->GetAPZCTreeManager()) {
|
||||
apzc->UpdateHitTestingTree(rootLayersId, rootWrbp->GetScrollData(),
|
||||
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 WebRenderScrollData& aScrollData)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
||||
aContentSize, dl, dlDesc, aScrollData);
|
||||
return IPC_OK();
|
||||
|
@ -429,6 +432,9 @@ WebRenderBridgeParent::RecvDPSyncEnd(const gfx::IntSize &aSize,
|
|||
const WrBuiltDisplayListDescriptor& dlDesc,
|
||||
const WebRenderScrollData& aScrollData)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
|
||||
aContentSize, dl, dlDesc, aScrollData);
|
||||
return IPC_OK();
|
||||
|
@ -695,6 +701,9 @@ WebRenderBridgeParent::RecvRemoveExternalImageId(const ExternalImageId& aImageId
|
|||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
mChildLayerObserverEpoch = aLayerObserverEpoch;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -702,6 +711,9 @@ WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverE
|
|||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvClearCachedResources()
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), false);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -732,6 +744,9 @@ mozilla::ipc::IPCResult
|
|||
WebRenderBridgeParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
|
||||
nsTArray<ScrollableLayerGuid>&& aTargets)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
mCompositorBridge->SetConfirmedTargetAPZC(GetLayersId(), aBlockId, aTargets);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -741,6 +756,9 @@ WebRenderBridgeParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScr
|
|||
const float& aX,
|
||||
const float& aY)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aScrollId);
|
||||
if (!apzc) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
|
@ -753,6 +771,9 @@ mozilla::ipc::IPCResult
|
|||
WebRenderBridgeParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
||||
const float& aZoom)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aScrollId);
|
||||
if (!apzc) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
|
@ -764,10 +785,20 @@ WebRenderBridgeParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
|||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvFlushApzRepaints()
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
mCompositorBridge->FlushApzRepaints(GetLayersId());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvGetAPZTestData(APZTestData* aOutData)
|
||||
{
|
||||
mCompositorBridge->GetAPZTestData(GetLayersId(), aOutData);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
|
@ -1067,6 +1098,9 @@ mozilla::ipc::IPCResult
|
|||
WebRenderBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle,
|
||||
const TextureInfo& aInfo)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
if (!AddCompositable(aHandle, aInfo)) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
@ -1076,6 +1110,9 @@ WebRenderBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle,
|
|||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
ReleaseCompositable(aHandle);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -1083,6 +1120,9 @@ WebRenderBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle
|
|||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvInitReadLocks(ReadLockArray&& aReadLocks)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
if (!AddReadLocks(Move(aReadLocks))) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
|
|
@ -126,6 +126,7 @@ public:
|
|||
mozilla::ipc::IPCResult RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollId,
|
||||
const float& aZoom) override;
|
||||
mozilla::ipc::IPCResult RecvFlushApzRepaints() override;
|
||||
mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* data) override;
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
void SetWebRenderProfilerEnabled(bool aEnabled);
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace layers {
|
|||
WebRenderCompositableHolder::AsyncImagePipelineHolder::AsyncImagePipelineHolder()
|
||||
: mInitialised(false)
|
||||
, mIsChanged(false)
|
||||
, mUseExternalImage(false)
|
||||
, mFilter(WrImageRendering::Auto)
|
||||
, mMixBlendMode(WrMixBlendMode::Normal)
|
||||
{}
|
||||
|
@ -293,6 +294,7 @@ WebRenderCompositableHolder::UpdateImageKeys(wr::WebRenderAPI* aApi,
|
|||
if (texture == aHolder->mCurrentTexture) {
|
||||
// Reuse previous ImageKeys.
|
||||
aKeys.AppendElements(aHolder->mKeys);
|
||||
aUseExternalImage = aHolder->mUseExternalImage;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -306,7 +308,7 @@ WebRenderCompositableHolder::UpdateImageKeys(wr::WebRenderAPI* aApi,
|
|||
return true;
|
||||
}
|
||||
|
||||
aUseExternalImage = GetImageKeyForTextureHost(aApi, texture, aKeys);
|
||||
aUseExternalImage = aHolder->mUseExternalImage = GetImageKeyForTextureHost(aApi, texture, aKeys);
|
||||
MOZ_ASSERT(!aKeys.IsEmpty());
|
||||
aHolder->mKeys.AppendElements(aKeys);
|
||||
aHolder->mCurrentTexture = texture;
|
||||
|
|
|
@ -122,6 +122,7 @@ private:
|
|||
|
||||
bool mInitialised;
|
||||
bool mIsChanged;
|
||||
bool mUseExternalImage;
|
||||
LayerRect mScBounds;
|
||||
gfx::Matrix4x4 mScTransform;
|
||||
gfx::MaybeIntSize mScaleToSize;
|
||||
|
|
|
@ -32,6 +32,7 @@ WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
|
|||
, mNeedsComposite(false)
|
||||
, mIsFirstPaint(false)
|
||||
, mTarget(nullptr)
|
||||
, mPaintSequenceNumber(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(WebRenderLayerManager);
|
||||
}
|
||||
|
@ -124,6 +125,13 @@ WebRenderLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
|
|||
bool
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -209,6 +217,7 @@ WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback
|
|||
scrollData.SetIsFirstPaint();
|
||||
mIsFirstPaint = false;
|
||||
}
|
||||
scrollData.SetPaintSequenceNumber(mPaintSequenceNumber);
|
||||
if (mRoot) {
|
||||
PopulateScrollData(scrollData, mRoot.get());
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "Layers.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/layers/APZTestData.h"
|
||||
#include "mozilla/layers/TransactionIdAllocator.h"
|
||||
#include "mozilla/webrender/WebRenderTypes.h"
|
||||
|
||||
|
@ -126,6 +127,16 @@ public:
|
|||
void SetTransactionIncomplete() { mTransactionIncomplete = true; }
|
||||
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:
|
||||
/**
|
||||
* 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
|
||||
// back to mTarget.
|
||||
RefPtr<gfxContext> mTarget;
|
||||
|
||||
// See equivalent field in ClientLayerManager
|
||||
uint32_t mPaintSequenceNumber;
|
||||
// See equivalent field in ClientLayerManager
|
||||
APZTestData mApzTestData;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
|
|
@ -80,6 +80,7 @@ WebRenderLayerScrollData::GetTransformTyped() const
|
|||
|
||||
WebRenderScrollData::WebRenderScrollData()
|
||||
: mIsFirstPaint(false)
|
||||
, mPaintSequenceNumber(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -151,5 +152,17 @@ WebRenderScrollData::IsFirstPaint() const
|
|||
return mIsFirstPaint;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderScrollData::SetPaintSequenceNumber(uint32_t aPaintSequenceNumber)
|
||||
{
|
||||
mPaintSequenceNumber = aPaintSequenceNumber;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
WebRenderScrollData::GetPaintSequenceNumber() const
|
||||
{
|
||||
return mPaintSequenceNumber;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -121,6 +121,8 @@ public:
|
|||
|
||||
void SetIsFirstPaint();
|
||||
bool IsFirstPaint() const;
|
||||
void SetPaintSequenceNumber(uint32_t aPaintSequenceNumber);
|
||||
uint32_t GetPaintSequenceNumber() const;
|
||||
|
||||
friend struct IPC::ParamTraits<WebRenderScrollData>;
|
||||
|
||||
|
@ -146,6 +148,7 @@ private:
|
|||
nsTArray<WebRenderLayerScrollData> mLayerScrollData;
|
||||
|
||||
bool mIsFirstPaint;
|
||||
uint32_t mPaintSequenceNumber;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
@ -219,6 +222,7 @@ struct ParamTraits<mozilla::layers::WebRenderScrollData>
|
|||
WriteParam(aMsg, aParam.mScrollMetadatas);
|
||||
WriteParam(aMsg, aParam.mLayerScrollData);
|
||||
WriteParam(aMsg, aParam.mIsFirstPaint);
|
||||
WriteParam(aMsg, aParam.mPaintSequenceNumber);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -226,7 +230,8 @@ struct ParamTraits<mozilla::layers::WebRenderScrollData>
|
|||
{
|
||||
return ReadParam(aMsg, aIter, &aResult->mScrollMetadatas)
|
||||
&& 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
|
||||
[PWebRenderBridge::SetAsyncZoom]
|
||||
description = test only
|
||||
[PWebRenderBridge::GetAPZTestData]
|
||||
description = test only
|
||||
[PHal::GetCurrentBatteryInformation]
|
||||
description =
|
||||
[PHal::GetCurrentNetworkInformation]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// |jit-test| allow-oom
|
||||
if (getBuildConfiguration().debug === true)
|
||||
quit(0);
|
||||
function f(){};
|
||||
|
@ -9,4 +8,4 @@ try {
|
|||
} catch (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) {
|
||||
if (getElementsHeader()->numShiftedElements() > 0)
|
||||
unshiftElements();
|
||||
moveShiftedElements();
|
||||
|
||||
// When an array's length becomes non-writable, writes to indexes
|
||||
// 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);
|
||||
|
||||
if (MOZ_UNLIKELY(header->numShiftedElements() + count > ObjectElements::MaxShiftedElements)) {
|
||||
unshiftElements();
|
||||
moveShiftedElements();
|
||||
header = getElementsHeader();
|
||||
}
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ ObjectElements::FreezeElements(JSContext* cx, HandleNativeObject obj)
|
|||
return true;
|
||||
|
||||
if (obj->getElementsHeader()->numShiftedElements() > 0)
|
||||
obj->unshiftElements();
|
||||
obj->moveShiftedElements();
|
||||
|
||||
ObjectElements* header = obj->getElementsHeader();
|
||||
|
||||
|
@ -707,7 +707,7 @@ NativeObject::maybeDensifySparseElements(JSContext* cx, HandleNativeObject obj)
|
|||
}
|
||||
|
||||
void
|
||||
NativeObject::unshiftElements()
|
||||
NativeObject::moveShiftedElements()
|
||||
{
|
||||
ObjectElements* header = getElementsHeader();
|
||||
uint32_t numShifted = header->numShiftedElements();
|
||||
|
@ -723,7 +723,7 @@ NativeObject::unshiftElements()
|
|||
elements_ = newHeader->elements();
|
||||
|
||||
// To move the elements, temporarily update initializedLength to include
|
||||
// both shifted and unshifted elements.
|
||||
// the shifted elements.
|
||||
newHeader->initializedLength += numShifted;
|
||||
|
||||
// Move the elements. Initialize to |undefined| to ensure pre-barriers
|
||||
|
@ -739,14 +739,14 @@ NativeObject::unshiftElements()
|
|||
}
|
||||
|
||||
void
|
||||
NativeObject::maybeUnshiftElements()
|
||||
NativeObject::maybeMoveShiftedElements()
|
||||
{
|
||||
ObjectElements* header = getElementsHeader();
|
||||
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)
|
||||
unshiftElements();
|
||||
moveShiftedElements();
|
||||
}
|
||||
|
||||
// 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())
|
||||
MOZ_CRASH();
|
||||
|
||||
// If there are shifted elements, consider unshifting them first. If we
|
||||
// don't unshift here, the code below will include the shifted elements in
|
||||
// the resize.
|
||||
// If there are shifted elements, consider moving them first. If we don't
|
||||
// move them here, the code below will include the shifted elements in the
|
||||
// resize.
|
||||
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||
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)
|
||||
return true;
|
||||
numShifted = getElementsHeader()->numShiftedElements();
|
||||
|
||||
// Ensure |reqCapacity + numShifted| below won't overflow by forcing an
|
||||
// unshift in that case.
|
||||
// If |reqCapacity + numShifted| overflows, we just move all shifted
|
||||
// elements to avoid the problem.
|
||||
CheckedInt<uint32_t> checkedReqCapacity(reqCapacity);
|
||||
checkedReqCapacity += numShifted;
|
||||
if (MOZ_UNLIKELY(!checkedReqCapacity.isValid())) {
|
||||
unshiftElements();
|
||||
moveShiftedElements();
|
||||
numShifted = 0;
|
||||
}
|
||||
}
|
||||
|
@ -932,10 +941,10 @@ NativeObject::shrinkElements(JSContext* cx, uint32_t reqCapacity)
|
|||
if (!hasDynamicElements())
|
||||
return;
|
||||
|
||||
// If we have shifted elements, consider unshifting them.
|
||||
// If we have shifted elements, consider moving them.
|
||||
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||
if (numShifted > 0) {
|
||||
maybeUnshiftElements();
|
||||
maybeMoveShiftedElements();
|
||||
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
|
||||
* 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
|
||||
* are frozen, have copy-on-write elements, or on arrays with non-writable
|
||||
* length).
|
||||
|
@ -205,7 +205,7 @@ class ObjectElements
|
|||
};
|
||||
|
||||
// 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 MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
|
||||
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.
|
||||
inline bool tryShiftDenseElements(uint32_t count);
|
||||
|
||||
// Unshift all shifted elements so that numShiftedElements is 0.
|
||||
void unshiftElements();
|
||||
// Move the elements header and all shifted elements to the start of the
|
||||
// allocated elements space, so that numShiftedElements is 0 afterwards.
|
||||
void moveShiftedElements();
|
||||
|
||||
// If this object has many shifted elements, unshift them.
|
||||
void maybeUnshiftElements();
|
||||
// If this object has many shifted elements call moveShiftedElements.
|
||||
void maybeMoveShiftedElements();
|
||||
|
||||
static bool goodElementsAllocationAmount(JSContext* cx, uint32_t reqAllocated,
|
||||
uint32_t length, uint32_t* goodAmount);
|
||||
|
|
|
@ -20,6 +20,22 @@
|
|||
#include <prio.h>
|
||||
|
||||
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 {
|
||||
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#include "nsXULAppAPI.h"
|
||||
#include "WrapperFactory.h"
|
||||
|
||||
#include "AutoMemMap.h"
|
||||
#include "ScriptPreloader-inl.h"
|
||||
|
||||
#include "mozilla/AddonPathService.h"
|
||||
#include "mozilla/scache/StartupCache.h"
|
||||
#include "mozilla/scache/StartupCacheUtils.h"
|
||||
|
@ -51,6 +54,7 @@
|
|||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::scache;
|
||||
using namespace mozilla::loader;
|
||||
using namespace xpc;
|
||||
using namespace JS;
|
||||
|
||||
|
@ -506,25 +510,6 @@ mozJSComponentLoader::CreateLoaderGlobal(JSContext* aCx,
|
|||
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*
|
||||
mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
|
||||
nsIFile* aComponentFile,
|
||||
|
@ -671,54 +656,13 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
|||
.setSourceIsLazy(!!cache);
|
||||
|
||||
if (realFile) {
|
||||
int64_t fileSize;
|
||||
rv = aComponentFile->GetFileSize(&fileSize);
|
||||
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;
|
||||
}
|
||||
AutoMemMap map;
|
||||
MOZ_TRY(map.init(aComponentFile));
|
||||
|
||||
// Note: exceptions will get handled further down;
|
||||
// don't early return for them here.
|
||||
Compile(cx, options, buf, fileSize32, &script);
|
||||
|
||||
PR_MemUnmap(buf, fileSize32);
|
||||
auto buf = map.get<char>();
|
||||
Compile(cx, options, buf.get(), map.size(), &script);
|
||||
} else {
|
||||
rv = aInfo.EnsureScriptChannel();
|
||||
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";
|
||||
let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
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 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";
|
||||
yield promise;
|
||||
return Components.utils.isDeadWrapper(doc);
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
#include "RegionBuilder.h"
|
||||
#include "SVGSVGElement.h"
|
||||
#include "DisplayItemClip.h"
|
||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsXULPopupManager.h"
|
||||
|
@ -8508,6 +8509,8 @@ nsLayoutUtils::DoLogTestDataForPaint(LayerManager* aManager,
|
|||
{
|
||||
if (ClientLayerManager* mgr = aManager->AsClientLayerManager()) {
|
||||
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",
|
||||
"resource://gre/modules/PageActions.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
var {
|
||||
IconDetails,
|
||||
} = ExtensionUtils;
|
||||
} = ExtensionParent;
|
||||
|
||||
// WeakMap[Extension -> PageAction]
|
||||
var pageActionMap = new WeakMap();
|
||||
|
|
|
@ -5,13 +5,11 @@
|
|||
|
||||
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/XPCOMUtils.jsm");
|
||||
|
||||
let { PlatformInfo } = ExtensionUtils;
|
||||
|
||||
if (PlatformInfo.os == "android" && !Services.prefs.getBoolPref("network.mdns.use_js_fallback")) {
|
||||
if (AppConstants.platform == "android" && !Services.prefs.getBoolPref("network.mdns.use_js_fallback")) {
|
||||
Cu.import("resource://gre/modules/MulticastDNSAndroid.jsm");
|
||||
} else {
|
||||
Cu.import("resource://gre/modules/MulticastDNS.jsm");
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[display-none.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
[iframe-no-root.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[multiple-thresholds.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[root-margin.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[same-document-no-root.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
[same-document-zero-size-target.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
[shadow-content.html]
|
||||
type: testharness
|
||||
prefs: [dom.webcomponents.enabled:true]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
|
|
|
@ -7,6 +7,7 @@ module.exports = {
|
|||
"Ci": true,
|
||||
"Cr": true,
|
||||
"Cu": true,
|
||||
"StructuredCloneHolder": false,
|
||||
"TextDecoder": false,
|
||||
"TextEncoder": false,
|
||||
|
||||
|
@ -20,6 +21,7 @@ module.exports = {
|
|||
"AppConstants": true,
|
||||
"Extension": true,
|
||||
"ExtensionAPI": true,
|
||||
"ExtensionCommon": true,
|
||||
"ExtensionManagement": true,
|
||||
"ExtensionUtils": true,
|
||||
"extensions": true,
|
||||
|
|
|
@ -97,13 +97,12 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
|
|||
var {
|
||||
GlobalManager,
|
||||
ParentAPIManager,
|
||||
StartupCache,
|
||||
apiManager: Management,
|
||||
} = ExtensionParent;
|
||||
|
||||
const {
|
||||
classifyPermission,
|
||||
EventEmitter,
|
||||
StartupCache,
|
||||
getUniqueId,
|
||||
} = ExtensionUtils;
|
||||
|
||||
|
@ -145,6 +144,29 @@ function validateThemeManifest(manifestProperties) {
|
|||
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 UUID_MAP_PREF = "extensions.webextensions.uuids";
|
||||
const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall";
|
||||
|
|
|
@ -40,22 +40,41 @@ const {
|
|||
DefaultMap,
|
||||
EventEmitter,
|
||||
LimitedSet,
|
||||
SpreadArgs,
|
||||
defineLazyGetter,
|
||||
getMessageManager,
|
||||
getUniqueId,
|
||||
injectAPI,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
LocalAPIImplementation,
|
||||
LocaleData,
|
||||
NoCloneSpreadArgs,
|
||||
SchemaAPIInterface,
|
||||
SingletonEventManager,
|
||||
} = ExtensionCommon;
|
||||
|
||||
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.
|
||||
*
|
||||
|
@ -115,15 +134,16 @@ class Port {
|
|||
},
|
||||
|
||||
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);
|
||||
fire.asyncWithoutClone(portObj);
|
||||
});
|
||||
}).api(),
|
||||
|
||||
onMessage: new SingletonEventManager(this.context, "Port.onMessage", fire => {
|
||||
return this.registerOnMessage(msg => {
|
||||
msg = Cu.cloneInto(msg, this.context.cloneScope);
|
||||
return this.registerOnMessage(holder => {
|
||||
let msg = holder.deserialize(this.context.cloneScope);
|
||||
fire.asyncWithoutClone(msg, portObj);
|
||||
});
|
||||
}).api(),
|
||||
|
@ -202,7 +222,9 @@ class Port {
|
|||
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() {
|
||||
|
@ -307,7 +329,9 @@ class Messenger {
|
|||
}
|
||||
|
||||
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 => {
|
||||
if (error.result == MessageChannel.RESULT_NO_HANDLER) {
|
||||
return Promise.reject({message: "Could not establish connection. Receiving end does not exist."});
|
||||
|
@ -336,7 +360,7 @@ class Messenger {
|
|||
filter(sender, recipient));
|
||||
},
|
||||
|
||||
receiveMessage: ({target, data: message, sender, recipient}) => {
|
||||
receiveMessage: ({target, data: holder, sender, recipient}) => {
|
||||
if (!this.context.active) {
|
||||
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);
|
||||
sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope);
|
||||
|
||||
|
@ -393,7 +417,7 @@ class Messenger {
|
|||
} else if (error.result === MessageChannel.RESULT_DISCONNECTED) {
|
||||
error = null;
|
||||
}
|
||||
port.disconnectByOtherEnd(error);
|
||||
port.disconnectByOtherEnd(new StructuredCloneHolder(error));
|
||||
});
|
||||
|
||||
return port.api();
|
||||
|
@ -734,7 +758,9 @@ class ChildAPIManager {
|
|||
let listener = map.ids.get(data.listenerId);
|
||||
|
||||
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)) {
|
||||
Services.console.logStringMessage(
|
||||
|
@ -747,7 +773,9 @@ class ChildAPIManager {
|
|||
if ("error" in data) {
|
||||
deferred.reject(data.error);
|
||||
} 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);
|
||||
break;
|
||||
|
|
|
@ -29,6 +29,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||
"resource://gre/modules/Schemas.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
|
||||
"@mozilla.org/content/style-sheet-service;1",
|
||||
"nsIStyleSheetService");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
var {
|
||||
|
@ -36,7 +40,6 @@ var {
|
|||
DefaultWeakMap,
|
||||
EventEmitter,
|
||||
ExtensionError,
|
||||
SpreadArgs,
|
||||
defineLazyGetter,
|
||||
getConsole,
|
||||
getInnerWindowID,
|
||||
|
@ -48,6 +51,39 @@ var {
|
|||
|
||||
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 {
|
||||
constructor(envType, extension) {
|
||||
this.envType = envType;
|
||||
|
@ -311,6 +347,8 @@ class BaseContext {
|
|||
dump(`Promise resolved after context unloaded\n`);
|
||||
} else if (!this.active) {
|
||||
dump(`Promise resolved while context is inactive\n`);
|
||||
} else if (args instanceof NoCloneSpreadArgs) {
|
||||
this.runSafeWithoutClone(callback, ...args.unwrappedValues);
|
||||
} else if (args instanceof SpreadArgs) {
|
||||
runSafe(callback, ...args);
|
||||
} else {
|
||||
|
@ -336,6 +374,9 @@ class BaseContext {
|
|||
dump(`Promise resolved after context unloaded\n`);
|
||||
} else if (!this.active) {
|
||||
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) {
|
||||
runSafe(resolve, value.length == 1 ? value[0] : value);
|
||||
} else {
|
||||
|
@ -1067,7 +1108,7 @@ class SchemaAPIManager extends EventEmitter {
|
|||
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/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,
|
||||
CanOfAPIs,
|
||||
LocalAPIImplementation,
|
||||
LocaleData,
|
||||
NoCloneSpreadArgs,
|
||||
SchemaAPIInterface,
|
||||
SchemaAPIManager,
|
||||
SingletonEventManager,
|
||||
SpreadArgs,
|
||||
ignoreEvent,
|
||||
stylesheetMap,
|
||||
};
|
||||
|
|
|
@ -24,6 +24,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
|||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||
"resource:///modules/E10SUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "IndexedDB",
|
||||
"resource://gre/modules/IndexedDB.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||
"resource://gre/modules/MessageChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
|
||||
|
@ -46,12 +48,13 @@ var {
|
|||
BaseContext,
|
||||
CanOfAPIs,
|
||||
SchemaAPIManager,
|
||||
SpreadArgs,
|
||||
} = ExtensionCommon;
|
||||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
ExtensionError,
|
||||
MessageManagerProxy,
|
||||
SpreadArgs,
|
||||
defineLazyGetter,
|
||||
promiseDocumentLoaded,
|
||||
promiseEvent,
|
||||
|
@ -619,7 +622,9 @@ ParentAPIManager = {
|
|||
result.then(result => {
|
||||
result = result instanceof SpreadArgs ? [...result] : [result];
|
||||
|
||||
reply({result});
|
||||
let holder = new StructuredCloneHolder(result);
|
||||
|
||||
reply({result: holder});
|
||||
}, error => {
|
||||
error = context.normalizeError(error);
|
||||
reply({error: {message: error.message, fileName: error.fileName}});
|
||||
|
@ -651,7 +656,7 @@ ParentAPIManager = {
|
|||
childId,
|
||||
listenerId: data.listenerId,
|
||||
path: data.path,
|
||||
args: listenerArgs,
|
||||
args: new StructuredCloneHolder(listenerArgs),
|
||||
},
|
||||
{
|
||||
recipient: {childId},
|
||||
|
@ -1070,11 +1075,305 @@ function extensionNameFromURI(uri) {
|
|||
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,
|
||||
GlobalManager,
|
||||
HiddenExtensionPage,
|
||||
IconDetails,
|
||||
ParentAPIManager,
|
||||
StartupCache,
|
||||
WebExtensionPolicy,
|
||||
apiManager,
|
||||
get baseManifestProperties() {
|
||||
|
@ -1095,3 +1394,39 @@ const ExtensionParent = {
|
|||
watchExtensionProxyContextLoad,
|
||||
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 Cr = Components.results;
|
||||
|
||||
const INTEGER = /^[1-9]\d*$/;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.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",
|
||||
"resource://gre/modules/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "IndexedDB",
|
||||
"resource://gre/modules/IndexedDB.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||
"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() {
|
||||
return new ConsoleAPI({
|
||||
|
@ -51,134 +35,6 @@ function getUniqueId() {
|
|||
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
|
||||
|
@ -251,21 +107,6 @@ function instanceOf(value, 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
|
||||
* constructor if one is not present.
|
||||
|
@ -308,176 +149,6 @@ function getInnerWindowID(window) {
|
|||
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 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
|
||||
* newer ones are added.
|
||||
|
@ -744,28 +374,6 @@ function flushJarCache(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.
|
||||
* Accepts several formats:
|
||||
|
@ -784,25 +392,6 @@ function normalizeTime(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
|
||||
* 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
|
||||
* 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 = {
|
||||
classifyPermission,
|
||||
defineLazyGetter,
|
||||
extend,
|
||||
findPathInObject,
|
||||
flushJarCache,
|
||||
getConsole,
|
||||
getInnerWindowID,
|
||||
|
@ -1076,8 +619,6 @@ this.ExtensionUtils = {
|
|||
getUniqueId,
|
||||
filterStack,
|
||||
getWinUtils,
|
||||
ignoreEvent,
|
||||
injectAPI,
|
||||
instanceOf,
|
||||
normalizeTime,
|
||||
promiseDocumentLoaded,
|
||||
|
@ -1088,17 +629,10 @@ this.ExtensionUtils = {
|
|||
runSafeSync,
|
||||
runSafeSyncWithoutClone,
|
||||
runSafeWithoutClone,
|
||||
stylesheetMap,
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
EventEmitter,
|
||||
ExtensionError,
|
||||
IconDetails,
|
||||
LimitedSet,
|
||||
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 global = this;
|
||||
|
||||
this.HostManifestManager = {
|
||||
_initializePromise: null,
|
||||
_lookup: null,
|
||||
|
@ -236,7 +238,7 @@ this.NativeApp = class extends EventEmitter {
|
|||
app.on("message", (what, msg) => port.postMessage(msg));
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
port.registerOnMessage(msg => app.send(msg));
|
||||
port.registerOnMessage(holder => app.send(holder));
|
||||
port.registerOnDisconnect(msg => app.close());
|
||||
}
|
||||
|
||||
|
@ -334,10 +336,11 @@ this.NativeApp = class extends EventEmitter {
|
|||
})();
|
||||
}
|
||||
|
||||
send(msg) {
|
||||
send(holder) {
|
||||
if (this._isDisconnected) {
|
||||
throw new this.context.cloneScope.Error("Attempt to postMessage on disconnected port");
|
||||
}
|
||||
let msg = holder.deserialize(global);
|
||||
if (Cu.getClassName(msg, true) != "ArrayBuffer") {
|
||||
// This error cannot be triggered by extensions; it indicates an error in
|
||||
// our implementation.
|
||||
|
@ -412,14 +415,14 @@ this.NativeApp = class extends EventEmitter {
|
|||
this._cleanup();
|
||||
}
|
||||
|
||||
sendMessage(msg) {
|
||||
sendMessage(holder) {
|
||||
let responsePromise = new Promise((resolve, reject) => {
|
||||
this.once("message", (what, msg) => { resolve(msg); });
|
||||
this.once("disconnect", (what, err) => { reject(err); });
|
||||
});
|
||||
|
||||
let result = this.startupPromise.then(() => {
|
||||
this.send(msg);
|
||||
this.send(holder);
|
||||
return responsePromise;
|
||||
});
|
||||
|
||||
|
|
|
@ -21,16 +21,19 @@ Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
|||
var {
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
StartupCache,
|
||||
instanceOf,
|
||||
} = ExtensionUtils;
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||
"resource://gre/modules/ExtensionParent.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "contentPolicyService",
|
||||
"@mozilla.org/addons/content-policy;1",
|
||||
"nsIAddonContentPolicy");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "StartupCache", () => ExtensionParent.StartupCache);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Schemas"];
|
||||
|
||||
const {DEBUG} = AppConstants;
|
||||
|
|
|
@ -9,6 +9,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "clearTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionCommon",
|
||||
"resource://gre/modules/ExtensionCommon.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "require",
|
||||
|
@ -19,7 +21,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
|
|||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
const {
|
||||
getWinUtils,
|
||||
stylesheetMap,
|
||||
} = ExtensionUtils;
|
||||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
@ -124,7 +125,7 @@ const BrowserListener = {
|
|||
let winUtils = getWinUtils(content);
|
||||
|
||||
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";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
|
||||
|
@ -14,11 +16,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
|
|||
"resource://gre/modules/EventEmitter.jsm");
|
||||
|
||||
var {
|
||||
ignoreEvent,
|
||||
normalizeTime,
|
||||
PlatformInfo,
|
||||
} = ExtensionUtils;
|
||||
|
||||
var {
|
||||
ignoreEvent,
|
||||
} = ExtensionCommon;
|
||||
|
||||
const DOWNLOAD_ITEM_FIELDS = ["id", "url", "referrer", "filename", "incognito",
|
||||
"danger", "mime", "startTime", "endTime",
|
||||
"estimatedEndTime", "state",
|
||||
|
@ -392,7 +396,7 @@ this.downloads = class extends ExtensionAPI {
|
|||
downloads: {
|
||||
download(options) {
|
||||
let {filename} = options;
|
||||
if (filename && PlatformInfo.os === "win") {
|
||||
if (filename && AppConstants.platform === "win") {
|
||||
// cross platform javascript code uses "/"
|
||||
filename = filename.replace(/\//g, "\\");
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
|
|||
|
||||
var {
|
||||
ignoreEvent,
|
||||
} = ExtensionUtils;
|
||||
} = ExtensionCommon;
|
||||
|
||||
// WeakMap[Extension -> Map[id -> Notification]]
|
||||
let notificationsMap = new WeakMap();
|
||||
|
|
|
@ -6,6 +6,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
|||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
|
||||
"resource://gre/modules/Extension.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||
"resource://gre/modules/ExtensionParent.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
|
@ -100,7 +102,7 @@ this.runtime = class extends ExtensionAPI {
|
|||
},
|
||||
|
||||
getPlatformInfo: function() {
|
||||
return Promise.resolve(ExtensionUtils.PlatformInfo);
|
||||
return Promise.resolve(ExtensionParent.PlatformInfo);
|
||||
},
|
||||
|
||||
openOptionsPage: function() {
|
||||
|
|
|
@ -18,15 +18,9 @@ add_task(async function test_sendMessage_error() {
|
|||
// because there is no onMessage listener.
|
||||
[[null, null, null], "Could not establish connection. Receiving end does not exist."],
|
||||
|
||||
// Structural cloning doesn't work with DOM but we fall back
|
||||
// JSON serialization, so we don't expect another error.
|
||||
[[null, location, null], "Could not establish connection. Receiving end does not exist."],
|
||||
|
||||
// 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).
|
||||
// Structured cloning doesn't work with DOM objects
|
||||
[[null, location, null], "The object could not be cloned."],
|
||||
[[null, [circ, location], null], "The object could not be cloned."],
|
||||
];
|
||||
|
||||
// Repeat all tests with the undefined value instead of null.
|
||||
|
|
|
@ -287,7 +287,7 @@ while True:
|
|||
});
|
||||
|
||||
let buffer = NativeApp.encodeMessage(mockContext, MSG);
|
||||
app.send(buffer);
|
||||
app.send(new StructuredCloneHolder(buffer));
|
||||
await recvPromise;
|
||||
|
||||
app._cleanup();
|
||||
|
|
|
@ -74,8 +74,8 @@ const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml
|
|||
var gViewDefault = "addons://discover/";
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "extensionStylesheets", () => {
|
||||
const {ExtensionUtils} = Cu.import("resource://gre/modules/ExtensionUtils.jsm", {});
|
||||
return ExtensionUtils.extensionStylesheets;
|
||||
const {ExtensionParent} = Cu.import("resource://gre/modules/ExtensionParent.jsm", {});
|
||||
return ExtensionParent.extensionStylesheets;
|
||||
});
|
||||
|
||||
var gStrings = {};
|
||||
|
|
|
@ -41,7 +41,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Locale",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
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",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
|
Загрузка…
Ссылка в новой задаче