Merge inbound to mozilla-central a=merge

This commit is contained in:
arthur.iakab 2018-11-15 11:54:15 +02:00
commit c0b26c4076
223 changed files with 4488 additions and 2657 deletions

View File

@ -4,7 +4,6 @@ support-files =
head.js
[browser_ext_autocompletepopup.js]
[browser_ext_legacy_extension_context_contentscript.js]
[browser_ext_windows_allowScriptsToClose.js]
[include:browser-common.ini]

View File

@ -1,173 +0,0 @@
"use strict";
const {
LegacyExtensionContext,
} = ChromeUtils.import("resource://gre/modules/LegacyExtensionsUtils.jsm", {});
function promiseAddonStartup(extension) {
const {Management} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
return new Promise((resolve) => {
let listener = (evt, extensionInstance) => {
Management.off("startup", listener);
resolve(extensionInstance);
};
Management.on("startup", listener);
});
}
/**
* This test case ensures that the LegacyExtensionContext can receive a connection
* from a content script and that the received port contains the expected sender
* tab info.
*/
add_task(async function test_legacy_extension_context_contentscript_connection() {
function backgroundScript() {
// Extract the assigned uuid from the background page url and send it
// in a test message.
let uuid = window.location.hostname;
browser.test.onMessage.addListener(async msg => {
if (msg == "open-test-tab") {
let tab = await browser.tabs.create({url: "http://example.com/"});
browser.test.sendMessage("get-expected-sender-info",
{uuid, tab});
} else if (msg == "close-current-tab") {
try {
let [tab] = await browser.tabs.query({active: true});
await browser.tabs.remove(tab.id);
browser.test.sendMessage("current-tab-closed", true);
} catch (e) {
browser.test.sendMessage("current-tab-closed", false);
}
}
});
browser.test.sendMessage("ready");
}
function contentScript() {
browser.runtime.sendMessage("webextension -> legacy_extension message", (reply) => {
browser.test.assertEq("legacy_extension -> webextension reply", reply,
"Got the expected reply from the LegacyExtensionContext");
browser.test.sendMessage("got-reply-message");
});
let port = browser.runtime.connect();
port.onMessage.addListener(msg => {
browser.test.assertEq("legacy_extension -> webextension port message", msg,
"Got the expected message from the LegacyExtensionContext");
port.postMessage("webextension -> legacy_extension port message");
});
}
let extensionData = {
background: `new ${backgroundScript}`,
manifest: {
content_scripts: [
{
matches: ["http://example.com/*"],
js: ["content-script.js"],
},
],
},
files: {
"content-script.js": `new ${contentScript}`,
},
};
let extension = ExtensionTestUtils.loadExtension(extensionData);
let waitForExtensionReady = extension.awaitMessage("ready");
let waitForExtensionInstance = promiseAddonStartup(extension);
extension.startup();
let extensionInstance = await waitForExtensionInstance;
// Connect to the target extension.id as an external context
// using the given custom sender info.
let legacyContext = new LegacyExtensionContext(extensionInstance);
let waitConnectPort = new Promise(resolve => {
let {browser} = legacyContext.api;
browser.runtime.onConnect.addListener(port => {
resolve(port);
});
});
let waitMessage = new Promise(resolve => {
let {browser} = legacyContext.api;
browser.runtime.onMessage.addListener((singleMsg, msgSender, sendReply) => {
sendReply("legacy_extension -> webextension reply");
resolve({singleMsg, msgSender});
});
});
is(legacyContext.envType, "legacy_extension",
"LegacyExtensionContext instance has the expected type");
ok(legacyContext.api, "Got the API object");
await waitForExtensionReady;
extension.sendMessage("open-test-tab");
let {tab} = await extension.awaitMessage("get-expected-sender-info");
let {singleMsg, msgSender} = await waitMessage;
is(singleMsg, "webextension -> legacy_extension message",
"Got the expected message");
ok(msgSender, "Got a message sender object");
is(msgSender.id, extension.id, "The sender has the expected id property");
is(msgSender.url, "http://example.com/", "The sender has the expected url property");
ok(msgSender.tab, "The sender has a tab property");
is(msgSender.tab.id, tab.id, "The port sender has the expected tab.id");
// Wait confirmation that the reply has been received.
await extension.awaitMessage("got-reply-message");
let port = await waitConnectPort;
ok(port, "Got the Port API object");
ok(port.sender, "The port has a sender property");
is(port.sender.id, extension.id, "The port sender has an id property");
is(port.sender.url, "http://example.com/", "The port sender has the expected url property");
ok(port.sender.tab, "The port sender has a tab property");
is(port.sender.tab.id, tab.id, "The port sender has the expected tab.id");
let waitPortMessage = new Promise(resolve => {
port.onMessage.addListener((msg) => {
resolve(msg);
});
});
port.postMessage("legacy_extension -> webextension port message");
let msg = await waitPortMessage;
is(msg, "webextension -> legacy_extension port message",
"LegacyExtensionContext received the expected message from the webextension");
let waitForDisconnect = new Promise(resolve => {
port.onDisconnect.addListener(resolve);
});
let waitForTestDone = extension.awaitMessage("current-tab-closed");
extension.sendMessage("close-current-tab");
await waitForDisconnect;
info("Got the disconnect event on tab closed");
let success = await waitForTestDone;
ok(success, "Test completed successfully");
await extension.unload();
});

View File

@ -2,20 +2,10 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
requestLongerTimeout(2);
function add_tasks(task) {
add_task(task.bind(null, {embedded: false}));
add_task(task.bind(null, {embedded: true}));
}
async function loadExtension(options) {
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "temporary",
embedded: options.embedded,
manifest: Object.assign({
"permissions": ["tabs"],
}, options.manifest),
@ -69,12 +59,12 @@ async function loadExtension(options) {
return extension;
}
add_tasks(async function test_inline_options(extraOptions) {
info(`Test options opened inline (${JSON.stringify(extraOptions)})`);
add_task(async function test_inline_options() {
info(`Test options opened inline`);
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
let extension = await loadExtension(Object.assign({}, extraOptions, {
let extension = await loadExtension({
manifest: {
applications: {gecko: {id: "inline_options@tests.mozilla.org"}},
"options_ui": {
@ -194,7 +184,7 @@ add_tasks(async function test_inline_options(extraOptions) {
browser.test.notifyFail("options-ui");
}
},
}));
});
await Promise.all([
extension.awaitMessage("options-html-inbound-pong"),
@ -212,12 +202,12 @@ add_tasks(async function test_inline_options(extraOptions) {
BrowserTestUtils.removeTab(tab);
});
add_tasks(async function test_tab_options(extraOptions) {
info(`Test options opened in a tab (${JSON.stringify(extraOptions)})`);
add_task(async function test_tab_options() {
info(`Test options opened in a tab`);
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
let extension = await loadExtension(Object.assign({}, extraOptions, {
let extension = await loadExtension({
manifest: {
applications: {gecko: {id: "tab_options@tests.mozilla.org"}},
"options_ui": {
@ -307,7 +297,7 @@ add_tasks(async function test_tab_options(extraOptions) {
browser.test.notifyFail("options-ui-tab");
}
},
}));
});
await extension.awaitFinish("options-ui-tab");
await extension.unload();
@ -315,10 +305,10 @@ add_tasks(async function test_tab_options(extraOptions) {
BrowserTestUtils.removeTab(tab);
});
add_tasks(async function test_options_no_manifest(extraOptions) {
info(`Test with no manifest key (${JSON.stringify(extraOptions)})`);
add_task(async function test_options_no_manifest() {
info(`Test with no manifest key`);
let extension = await loadExtension(Object.assign({}, extraOptions, {
let extension = await loadExtension({
manifest: {
applications: {gecko: {id: "no_options@tests.mozilla.org"}},
},
@ -333,7 +323,7 @@ add_tasks(async function test_options_no_manifest(extraOptions) {
browser.test.notifyPass("options-no-manifest");
},
}));
});
await extension.awaitFinish("options-no-manifest");
await extension.unload();

View File

@ -28,4 +28,7 @@ DOM_WEBIDL_PREF(dom_netinfo_enabled)
DOM_WEBIDL_PREF(dom_fetchObserver_enabled)
DOM_WEBIDL_PREF(dom_enable_performance_observer)
DOM_WEBIDL_PREF(dom_performance_enable_scheduler_timing)
DOM_WEBIDL_PREF(dom_reporting_enabled)
DOM_WEBIDL_PREF(dom_reporting_testing_enabled)
DOM_WEBIDL_PREF(dom_reporting_featurePolicy_enabled)
DOM_WEBIDL_PREF(javascript_options_streams)

View File

@ -52,6 +52,8 @@ nsDOMNavigationTiming::Clear()
mDOMContentLoadedEventStart = TimeStamp();
mDOMContentLoadedEventEnd = TimeStamp();
mDOMComplete = TimeStamp();
mContentfulPaint = TimeStamp();
mNonBlankPaint = TimeStamp();
mDocShellHasBeenActiveSinceNavigationStart = false;
}
@ -308,7 +310,7 @@ nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer)
{
// Check TTI: see if it's been 5 seconds since the last Long Task
TimeStamp now = TimeStamp::Now();
MOZ_RELEASE_ASSERT(!mNonBlankPaint.IsNull(), "TTI timeout with no non-blank-paint?");
MOZ_RELEASE_ASSERT(!mContentfulPaint.IsNull(), "TTI timeout with no contentful-paint?");
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
TimeStamp lastLongTaskEnded;
@ -323,11 +325,10 @@ nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer)
return;
}
}
// To correctly implement TTI/TTFI as proposed, we'd need to use
// FirstContentfulPaint (FCP, which we have not yet implemented) instead
// of FirstNonBlankPaing (FNBP) to start at, and not fire it until there
// are no more than 2 network loads. By the proposed definition, without
// that we're closer to TimeToFirstInteractive.
// To correctly implement TTI/TTFI as proposed, we'd need to not
// fire it until there are no more than 2 network loads. By the
// proposed definition, without that we're closer to
// TimeToFirstInteractive.
// XXX check number of network loads, and if > 2 mark to check if loads
// decreases to 2 (or record that point and let the normal timer here
@ -341,7 +342,7 @@ nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer)
mTTFI = MaxWithinWindowBeginningAtMin(lastLongTaskEnded, mDOMContentLoadedEventEnd,
TimeDuration::FromMilliseconds(TTI_WINDOW_SIZE_MS));
if (mTTFI.IsNull()) {
mTTFI = mNonBlankPaint;
mTTFI = mContentfulPaint;
}
}
// XXX Implement TTI via check number of network loads, and if > 2 mark
@ -399,15 +400,6 @@ nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument()
}
#endif
if (!mTTITimer) {
mTTITimer = NS_NewTimer();
}
// TTI is first checked 5 seconds after the FCP (non-blank-paint is very close to FCP).
mTTITimer->InitWithNamedFuncCallback(TTITimeoutCallback, this, TTI_WINDOW_SIZE_MS,
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
"nsDOMNavigationTiming::TTITimeout");
if (mDocShellHasBeenActiveSinceNavigationStart) {
if (net::nsHttp::IsBeforeLastActiveTabLoadOptimization(mNavigationStart)) {
Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_NETOPT_MS,
@ -425,6 +417,42 @@ nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument()
}
}
void
nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());
if (!mContentfulPaint.IsNull()) {
return;
}
mContentfulPaint = TimeStamp::Now();
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
TimeDuration elapsed = mContentfulPaint - mNavigationStart;
nsAutoCString spec;
if (mLoadedURI) {
mLoadedURI->GetSpec(spec);
}
nsPrintfCString marker("Contentful paint after %dms for URL %s, %s",
int(elapsed.ToMilliseconds()), spec.get(),
mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start and first non-blank paint");
profiler_add_marker(marker.get());
}
#endif
if (!mTTITimer) {
mTTITimer = NS_NewTimer();
}
// TTI is first checked 5 seconds after the FCP (non-blank-paint is very close to FCP).
mTTITimer->InitWithNamedFuncCallback(TTITimeoutCallback, this, TTI_WINDOW_SIZE_MS,
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
"nsDOMNavigationTiming::TTITimeout");
}
void
nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument()
{

View File

@ -97,6 +97,10 @@ public:
{
return TimeStampToDOM(mNonBlankPaint);
}
DOMTimeMilliSec GetTimeToContentfulPaint() const
{
return TimeStampToDOM(mContentfulPaint);
}
DOMTimeMilliSec GetTimeToTTFI() const
{
return TimeStampToDOM(mTTFI);
@ -174,6 +178,7 @@ public:
void NotifyLongTask(mozilla::TimeStamp aWhen);
void NotifyNonBlankPaintForRootContentDocument();
void NotifyContentfulPaintForRootContentDocument();
void NotifyDOMContentFlushedForRootContentDocument();
void NotifyDocShellStateChanged(DocShellState aDocShellState);
@ -209,6 +214,7 @@ private:
DOMHighResTimeStamp mNavigationStartHighRes;
mozilla::TimeStamp mNavigationStart;
mozilla::TimeStamp mNonBlankPaint;
mozilla::TimeStamp mContentfulPaint;
mozilla::TimeStamp mDOMContentFlushed;
mozilla::TimeStamp mBeforeUnloadStart;

View File

@ -44,6 +44,9 @@ DEPRECATED_OPERATION(IDBOpenDBOptions_StorageType)
DEPRECATED_OPERATION(DOMAttrModifiedEvent)
DEPRECATED_OPERATION(MozBoxOrInlineBoxDisplay)
DEPRECATED_OPERATION(DOMQuadBoundsAttr)
DEPRECATED_OPERATION(DeprecatedTestingInterface)
DEPRECATED_OPERATION(DeprecatedTestingMethod)
DEPRECATED_OPERATION(DeprecatedTestingAttribute)
DEPRECATED_OPERATION(CreateImageBitmapCanvasRenderingContext2D)
DEPRECATED_OPERATION(MozRequestFullScreenDeprecatedPrefix)
DEPRECATED_OPERATION(MozfullscreenchangeDeprecatedPrefix)

View File

@ -3044,6 +3044,11 @@ nsIDocument::InitFeaturePolicy(nsIChannel* aChannel)
mFeaturePolicy->InheritPolicy(parentPolicy);
}
// We don't want to parse the http Feature-Policy header if this pref is off.
if (!StaticPrefs::dom_security_featurePolicy_header_enabled()) {
return NS_OK;
}
nsCOMPtr<nsIHttpChannel> httpChannel;
nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -31,7 +31,7 @@
#include "xpcpublic.h"
#include "js/CompilationAndEvaluation.h"
#include "js/JSON.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/Preferences.h"
@ -1391,13 +1391,17 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
dataStringBuf, dataStringLength);
}
JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength,
JS::SourceBufferHolder::GiveOwnership);
if (!dataStringBuf || dataStringLength == 0) {
return;
}
JS::UniqueTwoByteChars srcChars(dataStringBuf);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, std::move(srcChars), dataStringLength)) {
return;
}
JS::CompileOptions options(cx);
options.setFileAndLine(url.get(), 1);
options.setNoScriptRval(true);

View File

@ -237,6 +237,8 @@
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ImageBitmapBinding.h"
#include "mozilla/dom/InstallTriggerBinding.h"
#include "mozilla/dom/Report.h"
#include "mozilla/dom/ReportingObserver.h"
#include "mozilla/dom/ServiceWorker.h"
#include "mozilla/dom/ServiceWorkerRegistration.h"
#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
@ -337,6 +339,9 @@ using mozilla::dom::cache::CacheStorage;
// Min idle notification time in seconds.
#define MIN_IDLE_NOTIFICATION_TIME_S 1
// Max number of Report objects
#define MAX_REPORT_RECORDS 100
static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
static bool gIdleObserversAPIFuzzTimeDisabled = false;
@ -1471,6 +1476,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReportRecords)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReportingObservers)
tmp->TraverseHostObjectURIs(cb);
@ -1560,6 +1567,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReportRecords)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReportingObservers)
tmp->UnlinkHostObjectURIs();
@ -5669,6 +5678,7 @@ nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
if (mPerformance) {
mPerformance->MemoryPressure();
mReportRecords.Clear();
}
return NS_OK;
}
@ -8086,6 +8096,62 @@ nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter *aOuterWindow)
MOZ_ASSERT(aOuterWindow);
}
void
nsPIDOMWindowInner::RegisterReportingObserver(ReportingObserver* aObserver,
bool aBuffered)
{
MOZ_ASSERT(aObserver);
if (mReportingObservers.Contains(aObserver)) {
return;
}
if (NS_WARN_IF(!mReportingObservers.AppendElement(aObserver, fallible))) {
return;
}
if (!aBuffered) {
return;
}
for (Report* report : mReportRecords) {
aObserver->MaybeReport(report);
}
}
void
nsPIDOMWindowInner::UnregisterReportingObserver(ReportingObserver* aObserver)
{
MOZ_ASSERT(aObserver);
mReportingObservers.RemoveElement(aObserver);
}
void
nsPIDOMWindowInner::BroadcastReport(Report* aReport)
{
MOZ_ASSERT(aReport);
for (ReportingObserver* observer : mReportingObservers) {
observer->MaybeReport(aReport);
}
if (NS_WARN_IF(!mReportRecords.AppendElement(aReport, fallible))) {
return;
}
while (mReportRecords.Length() > MAX_REPORT_RECORDS) {
mReportRecords.RemoveElementAt(0);
}
}
void
nsPIDOMWindowInner::NotifyReportingObservers()
{
for (ReportingObserver* observer : mReportingObservers) {
observer->MaybeNotify();
}
}
nsPIDOMWindowInner::~nsPIDOMWindowInner() {}
#undef FORWARD_TO_OUTER

View File

@ -16,7 +16,7 @@
#include "jsfriendapi.h"
#include "js/CompilationAndEvaluation.h"
#include "js/OffThreadScriptCompilation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "nsIScriptContext.h"
#include "nsIScriptElement.h"
#include "nsIScriptGlobalObject.h"
@ -96,9 +96,16 @@ nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
}
// Compile.
const nsPromiseFlatString& flatBody = PromiseFlatString(aBody);
JS::SourceText<char16_t> source;
if (!source.init(cx, flatBody.get(), flatBody.Length(),
JS::SourceOwnership::Borrowed))
{
return NS_ERROR_FAILURE;
}
JS::Rooted<JSFunction*> fun(cx);
JS::SourceBufferHolder source(PromiseFlatString(aBody).get(), aBody.Length(),
JS::SourceBufferHolder::NoOwnership);
if (!JS::CompileFunction(cx, aScopeChain, aOptions,
PromiseFlatCString(aName).get(),
aArgCount, aArgArray,
@ -217,7 +224,7 @@ nsJSUtils::ExecutionContext::JoinAndExec(JS::OffThreadToken** aOffThreadToken,
nsresult
nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
JS::SourceBufferHolder& aSrcBuf,
JS::SourceText<char16_t>& aSrcBuf,
JS::MutableHandle<JSScript*> aScript)
{
if (mSkip) {
@ -270,8 +277,15 @@ nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
}
const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(),
JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(mCx, flatScript.get(), flatScript.Length(),
JS::SourceOwnership::Borrowed))
{
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
JS::Rooted<JSScript*> script(mCx);
return CompileAndExec(aCompileOptions, srcBuf, &script);
}
@ -473,7 +487,7 @@ nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRe
nsresult
nsJSUtils::CompileModule(JSContext* aCx,
JS::SourceBufferHolder& aSrcBuf,
JS::SourceText<char16_t>& aSrcBuf,
JS::Handle<JSObject*> aEvaluationGlobal,
JS::CompileOptions &aCompileOptions,
JS::MutableHandle<JSObject*> aModule)

View File

@ -20,6 +20,7 @@
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/Conversions.h"
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "nsString.h"
@ -157,9 +158,9 @@ public:
MOZ_MUST_USE nsresult JoinAndExec(JS::OffThreadToken** aOffThreadToken,
JS::MutableHandle<JSScript*> aScript);
// Compile a script contained in a SourceBuffer, and execute it.
// Compile a script contained in a SourceText, and execute it.
nsresult CompileAndExec(JS::CompileOptions& aCompileOptions,
JS::SourceBufferHolder& aSrcBuf,
JS::SourceText<char16_t>& aSrcBuf,
JS::MutableHandle<JSScript*> aScript);
// Compile a script contained in a string, and execute it.
@ -186,7 +187,7 @@ public:
};
static nsresult CompileModule(JSContext* aCx,
JS::SourceBufferHolder& aSrcBuf,
JS::SourceText<char16_t>& aSrcBuf,
JS::Handle<JSObject*> aEvaluationGlobal,
JS::CompileOptions &aCompileOptions,
JS::MutableHandle<JSObject*> aModule);

View File

@ -58,6 +58,9 @@ class Element;
class MozIdleObserver;
class Navigator;
class Performance;
class Report;
class ReportBody;
class ReportingObserver;
class Selection;
class ServiceWorker;
class ServiceWorkerDescriptor;
@ -628,6 +631,19 @@ public:
already_AddRefed<mozilla::AutoplayPermissionManager>
GetAutoplayPermissionManager();
void
RegisterReportingObserver(mozilla::dom::ReportingObserver* aObserver,
bool aBuffered);
void
UnregisterReportingObserver(mozilla::dom::ReportingObserver* aObserver);
void
BroadcastReport(mozilla::dom::Report* aReport);
void
NotifyReportingObservers();
protected:
void CreatePerformanceObjectIfNeeded();
@ -722,6 +738,10 @@ protected:
// The event dispatch code sets and unsets this while keeping
// the event object alive.
mozilla::dom::Event* mEvent;
// List of Report objects for ReportingObservers.
nsTArray<RefPtr<mozilla::dom::ReportingObserver>> mReportingObservers;
nsTArray<RefPtr<mozilla::dom::Report>> mReportRecords;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)

View File

@ -13,6 +13,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs.h"
#include "mozilla/Unused.h"
#include "mozilla/UseCounter.h"
@ -29,6 +30,7 @@
#include "nsINode.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsIURIFixup.h"
#include "nsIXPConnect.h"
#include "nsUTF8Utils.h"
#include "WorkerPrivate.h"
@ -43,6 +45,7 @@
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DeprecationReportBody.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/HTMLObjectElement.h"
@ -50,6 +53,7 @@
#include "mozilla/dom/HTMLEmbedElement.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "mozilla/dom/HTMLEmbedElementBinding.h"
#include "mozilla/dom/ReportingUtils.h"
#include "mozilla/dom/XULElementBinding.h"
#include "mozilla/dom/XULFrameElementBinding.h"
#include "mozilla/dom/XULMenuElementBinding.h"
@ -2200,7 +2204,7 @@ GetCachedSlotStorageObjectSlow(JSContext* cx, JS::Handle<JSObject*> obj,
}
*isXray = true;
return xpc::EnsureXrayExpandoObject(cx, obj);;
return xpc::EnsureXrayExpandoObject(cx, obj);
}
DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayExpandoObjectClass, 0);
@ -4103,11 +4107,95 @@ SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter)
namespace {
#define DEPRECATED_OPERATION(_op) #_op,
static const char* kDeprecatedOperations[] = {
#include "nsDeprecatedOperationList.h"
nullptr
};
#undef DEPRECATED_OPERATION
void
ReportDeprecation(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
nsIDocument::DeprecatedOperations aOperation,
const nsAString& aFileName,
const Nullable<uint32_t>& aLineNumber,
const Nullable<uint32_t>& aColumnNumber)
{
MOZ_ASSERT(aURI);
// Anonymize the URL.
// Strip the URL of any possible username/password and make it ready to be
// presented in the UI.
nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
if (NS_WARN_IF(!urifixup)) {
return;
}
nsCOMPtr<nsIURI> exposableURI;
nsresult rv = urifixup->CreateExposableURI(aURI, getter_AddRefs(exposableURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoCString spec;
rv = exposableURI->GetSpec(spec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoString type;
type.AssignASCII(kDeprecatedOperations[aOperation]);
nsAutoCString key;
key.AssignASCII(kDeprecatedOperations[aOperation]);
key.AppendASCII("Warning");
nsAutoString msg;
rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
key.get(), msg);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
RefPtr<DeprecationReportBody> body =
new DeprecationReportBody(aWindow, type, Nullable<Date>(),
msg, aFileName, aLineNumber, aColumnNumber);
ReportingUtils::Report(aWindow, nsGkAtoms::deprecation,
NS_ConvertUTF8toUTF16(spec), body);
}
void
MaybeReportDeprecation(nsPIDOMWindowInner* aWindow,
nsIDocument::DeprecatedOperations aOperation,
const nsAString& aFileName,
const Nullable<uint32_t>& aLineNumber,
const Nullable<uint32_t>& aColumnNumber)
{
MOZ_ASSERT(aWindow);
if (!StaticPrefs::dom_reporting_enabled()) {
return;
}
if (NS_WARN_IF(!aWindow->GetExtantDoc())) {
return;
}
nsCOMPtr<nsIURI> uri = aWindow->GetExtantDoc()->GetDocumentURI();
if (NS_WARN_IF(!uri)) {
return;
}
ReportDeprecation(aWindow, uri, aOperation, aFileName, aLineNumber,
aColumnNumber);
}
// This runnable is used to write a deprecation message from a worker to the
// console running on the main-thread.
class DeprecationWarningRunnable final : public WorkerProxyToMainThreadRunnable
{
nsIDocument::DeprecatedOperations mOperation;
const nsIDocument::DeprecatedOperations mOperation;
public:
explicit DeprecationWarningRunnable(nsIDocument::DeprecatedOperations aOperation)
@ -4161,6 +4249,21 @@ DeprecationWarning(const GlobalObject& aGlobal,
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
if (window && window->GetExtantDoc()) {
window->GetExtantDoc()->WarnOnceAbout(aOperation);
nsAutoCString fileName;
Nullable<uint32_t> lineNumber;
Nullable<uint32_t> columnNumber;
uint32_t line = 0;
uint32_t column = 0;
if (nsJSUtils::GetCallingLocation(aGlobal.Context(), fileName,
&line, &column)) {
lineNumber.SetValue(line);
columnNumber.SetValue(column);
}
MaybeReportDeprecation(window, aOperation,
NS_ConvertUTF8toUTF16(fileName), lineNumber,
columnNumber);
}
return;

View File

@ -396,6 +396,7 @@ NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
: nsGenericHTMLElement(std::move(aNodeInfo)),
mResetLayer(true) ,
mMaybeModified(false) ,
mWriteOnly(false)
{}
@ -1010,6 +1011,7 @@ HTMLCanvasElement::GetContext(const nsAString& aContextId,
nsISupports** aContext)
{
ErrorResult rv;
mMaybeModified = true; // For FirstContentfulPaint
*aContext = GetContext(nullptr, aContextId, JS::NullHandleValue, rv).take();
return rv.StealNSResult();
}
@ -1024,6 +1026,7 @@ HTMLCanvasElement::GetContext(JSContext* aCx,
return nullptr;
}
mMaybeModified = true; // For FirstContentfulPaint
return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
aContextOptions.isObject() ? aContextOptions : JS::NullHandleValue,
aRv);

View File

@ -349,6 +349,8 @@ public:
already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
bool MaybeModified() const { return mMaybeModified; };
protected:
virtual ~HTMLCanvasElement();
@ -387,6 +389,7 @@ protected:
AsyncCanvasRenderer* GetAsyncCanvasRenderer();
bool mResetLayer;
bool mMaybeModified; // we fetched the context, so we may have written to the canvas
RefPtr<HTMLCanvasElement> mOriginalCanvas;
RefPtr<PrintCallback> mPrintCallback;
RefPtr<HTMLCanvasPrintState> mPrintState;

View File

@ -48,7 +48,10 @@ var gTestWindows = [
{ test: "file_fullscreen-lenient-setters.html" },
{ test: "file_fullscreen-table.html" },
{ test: "file_fullscreen-event-order.html" },
{ test: "file_fullscreen-featurePolicy.html", prefs: [["dom.security.featurePolicy.enabled", true]] },
{ test: "file_fullscreen-featurePolicy.html",
prefs: [["dom.security.featurePolicy.enabled", true],
["dom.security.featurePolicy.header.enabled", true],
["dom.security.featurePolicy.webidl.enabled", true]] },
];
var testWindow = null;

View File

@ -353,6 +353,13 @@ AmbientLightEventWarning=Use of the ambient light sensor is deprecated.
IDBOpenDBOptions_StorageTypeWarning=The storage attribute in options passed to indexedDB.open is deprecated and will soon be removed. To get persistent storage, please use navigator.storage.persist() instead.
DOMQuadBoundsAttrWarning=DOMQuad.bounds is deprecated in favor of DOMQuad.getBounds()
UnsupportedEntryTypesIgnored=Ignoring unsupported entryTypes: %S.
#LOCALIZATION NOTE(DeprecatedTestingInterfaceWarning): Do not translate this message. It's just testing only.
DeprecatedTestingInterfaceWarning=TestingDeprecatedInterface is a testing-only interface and this is its testing deprecation message.
#LOCALIZATION NOTE(DeprecatedTestingMethodWarning): Do not translate this message. It's just testing only.
DeprecatedTestingMethodWarning=TestingDeprecatedInterface.deprecatedMethod() is a testing-only method and this is its testing deprecation message.
#LOCALIZATION NOTE(DeprecatedTestingAttributeWarning): Do not translate this message. It's just testing only.
DeprecatedTestingAttributeWarning=TestingDeprecatedInterface.deprecatedAttribute is a testing-only attribute and this is its testing deprecation message.
# LOCALIZATION NOTE (CreateImageBitmapCanvasRenderingContext2DWarning): Do not translate CanvasRenderingContext2D and createImageBitmap.
CreateImageBitmapCanvasRenderingContext2DWarning=Use of CanvasRenderingContext2D in createImageBitmap is deprecated.
# LOCALIZATION NOTE (MozRequestFullScreenDeprecatedPrefixWarning): Do not translate mozRequestFullScreen.

View File

@ -102,6 +102,7 @@ DIRS += [
'websocket',
'serviceworkers',
'simpledb',
'reporting',
]
if CONFIG['MOZ_LIBPRIO']:

View File

@ -440,6 +440,20 @@ public:
mPerformance->GetRandomTimelineSeed());
}
DOMTimeMilliSec TimeToContentfulPaint() const
{
if (!nsContentUtils::IsPerformanceTimingEnabled() ||
nsContentUtils::ShouldResistFingerprinting()) {
return 0;
}
if (mPerformance->IsSystemPrincipal()) {
return GetDOMTiming()->GetTimeToContentfulPaint();
}
return nsRFPService::ReduceTimePrecisionAsMSecs(
GetDOMTiming()->GetTimeToContentfulPaint(),
mPerformance->GetRandomTimelineSeed());
}
DOMTimeMilliSec TimeToDOMContentFlushed() const
{
if (!nsContentUtils::IsPerformanceTimingEnabled() ||

View File

@ -0,0 +1,76 @@
/* -*- 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/DeprecationReportBody.h"
#include "mozilla/dom/ReportingBinding.h"
namespace mozilla {
namespace dom {
DeprecationReportBody::DeprecationReportBody(nsPIDOMWindowInner* aWindow,
const nsAString& aId,
const Nullable<Date>& aDate,
const nsAString& aMessage,
const nsAString& aSourceFile,
const Nullable<uint32_t>& aLineNumber,
const Nullable<uint32_t>& aColumnNumber)
: ReportBody(aWindow)
, mId(aId)
, mDate(aDate)
, mMessage(aMessage)
, mSourceFile(aSourceFile)
, mLineNumber(aLineNumber)
, mColumnNumber(aColumnNumber)
{
MOZ_ASSERT(aWindow);
}
DeprecationReportBody::~DeprecationReportBody() = default;
JSObject*
DeprecationReportBody::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return DeprecationReportBody_Binding::Wrap(aCx, this, aGivenProto);
}
void
DeprecationReportBody::GetId(nsAString& aId) const
{
aId = mId;
}
Nullable<Date>
DeprecationReportBody::GetAnticipatedRemoval() const
{
return mDate;
}
void
DeprecationReportBody::GetMessage(nsAString& aMessage) const
{
aMessage = mMessage;
}
void
DeprecationReportBody::GetSourceFile(nsAString& aSourceFile) const
{
aSourceFile = mSourceFile;
}
Nullable<uint32_t>
DeprecationReportBody::GetLineNumber() const
{
return mLineNumber;
}
Nullable<uint32_t>
DeprecationReportBody::GetColumnNumber() const
{
return mColumnNumber;
}
} // dom namespace
} // mozilla namespace

View File

@ -0,0 +1,62 @@
/* -*- 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/. */
#ifndef mozilla_dom_DeprecationReportBody_h
#define mozilla_dom_DeprecationReportBody_h
#include "mozilla/dom/ReportBody.h"
#include "mozilla/dom/Date.h"
namespace mozilla {
namespace dom {
class DeprecationReportBody final : public ReportBody
{
public:
DeprecationReportBody(nsPIDOMWindowInner* aWindow,
const nsAString& aId,
const Nullable<Date>& aDate,
const nsAString& aMessage,
const nsAString& aSourceFile,
const Nullable<uint32_t>& aLineNumber,
const Nullable<uint32_t>& aColumnNumber);
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void
GetId(nsAString& aId) const;
Nullable<Date>
GetAnticipatedRemoval() const;
void
GetMessage(nsAString& aMessage) const;
void
GetSourceFile(nsAString& aSourceFile) const;
Nullable<uint32_t>
GetLineNumber() const;
Nullable<uint32_t>
GetColumnNumber() const;
private:
~DeprecationReportBody();
const nsString mId;
const Nullable<Date> mDate;
const nsString mMessage;
const nsString mSourceFile;
const Nullable<uint32_t> mLineNumber;
const Nullable<uint32_t> mColumnNumber;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_DeprecationReportBody_h

View File

@ -0,0 +1,66 @@
/* -*- 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/FeaturePolicyViolationReportBody.h"
#include "mozilla/dom/FeaturePolicyBinding.h"
namespace mozilla {
namespace dom {
FeaturePolicyViolationReportBody::FeaturePolicyViolationReportBody(nsPIDOMWindowInner* aWindow,
const nsAString& aFeatureId,
const nsAString& aSourceFile,
const Nullable<int32_t>& aLineNumber,
const Nullable<int32_t>& aColumnNumber,
const nsAString& aDisposition)
: ReportBody(aWindow)
, mFeatureId(aFeatureId)
, mSourceFile(aSourceFile)
, mLineNumber(aLineNumber)
, mColumnNumber(aColumnNumber)
, mDisposition(aDisposition)
{}
FeaturePolicyViolationReportBody::~FeaturePolicyViolationReportBody() = default;
JSObject*
FeaturePolicyViolationReportBody::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return FeaturePolicyViolationReportBody_Binding::Wrap(aCx, this, aGivenProto);
}
void
FeaturePolicyViolationReportBody::GetFeatureId(nsAString& aFeatureId) const
{
aFeatureId = mFeatureId;
}
void
FeaturePolicyViolationReportBody::GetSourceFile(nsAString& aSourceFile) const
{
aSourceFile = mSourceFile;
}
Nullable<int32_t>
FeaturePolicyViolationReportBody::GetLineNumber() const
{
return mLineNumber;
}
Nullable<int32_t>
FeaturePolicyViolationReportBody::GetColumnNumber() const
{
return mColumnNumber;
}
void
FeaturePolicyViolationReportBody::GetDisposition(nsAString& aDisposition) const
{
aDisposition = mDisposition;
}
} // dom namespace
} // mozilla namespace

View File

@ -0,0 +1,57 @@
/* -*- 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/. */
#ifndef mozilla_dom_FeaturePolicyViolationReportBody_h
#define mozilla_dom_FeaturePolicyViolationReportBody_h
#include "mozilla/dom/ReportBody.h"
#include "mozilla/dom/Date.h"
namespace mozilla {
namespace dom {
class FeaturePolicyViolationReportBody final : public ReportBody
{
public:
FeaturePolicyViolationReportBody(nsPIDOMWindowInner* aWindow,
const nsAString& aFeatureId,
const nsAString& aSourceFile,
const Nullable<int32_t>& aLineNumber,
const Nullable<int32_t>& aColumnNumber,
const nsAString& aDisposition);
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void
GetFeatureId(nsAString& aFeatureId) const;
void
GetSourceFile(nsAString& aSourceFile) const;
Nullable<int32_t>
GetLineNumber() const;
Nullable<int32_t>
GetColumnNumber() const;
void
GetDisposition(nsAString& aDisposition) const;
private:
~FeaturePolicyViolationReportBody();
const nsString mFeatureId;
const nsString mSourceFile;
const Nullable<int32_t> mLineNumber;
const Nullable<int32_t> mColumnNumber;
const nsString mDisposition;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_FeaturePolicyViolationReportBody_h

70
dom/reporting/Report.cpp Normal file
View File

@ -0,0 +1,70 @@
/* -*- 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/Report.h"
#include "mozilla/dom/ReportBody.h"
#include "mozilla/dom/ReportingBinding.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Report, mWindow, mBody)
NS_IMPL_CYCLE_COLLECTING_ADDREF(Report)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Report)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Report)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
Report::Report(nsPIDOMWindowInner* aWindow,
const nsAString& aType,
const nsAString& aURL,
ReportBody* aBody)
: mWindow(aWindow)
, mType(aType)
, mURL(aURL)
, mBody(aBody)
{
MOZ_ASSERT(aWindow);
}
Report::~Report() = default;
already_AddRefed<Report>
Report::Clone()
{
RefPtr<Report> report =
new Report(mWindow, mType, mURL, mBody);
return report.forget();
}
JSObject*
Report::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return Report_Binding::Wrap(aCx, this, aGivenProto);
}
void
Report::GetType(nsAString& aType) const
{
aType = mType;
}
void
Report::GetUrl(nsAString& aURL) const
{
aURL = mURL;
}
ReportBody*
Report::GetBody() const
{
return mBody;
}
} // dom namespace
} // mozilla namespace

66
dom/reporting/Report.h Normal file
View File

@ -0,0 +1,66 @@
/* -*- 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/. */
#ifndef mozilla_dom_Report_h
#define mozilla_dom_Report_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class ReportBody;
class Report final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Report)
Report(nsPIDOMWindowInner* aWindow,
const nsAString& aType,
const nsAString& aURL,
ReportBody* aBody);
already_AddRefed<Report>
Clone();
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
nsPIDOMWindowInner*
GetParentObject() const
{
return mWindow;
}
void
GetType(nsAString& aType) const;
void
GetUrl(nsAString& aURL) const;
ReportBody*
GetBody() const;
private:
~Report();
nsCOMPtr<nsPIDOMWindowInner> mWindow;
const nsString mType;
const nsString mURL;
RefPtr<ReportBody> mBody;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_Report_h

View File

@ -0,0 +1,38 @@
/* -*- 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/ReportBody.h"
#include "mozilla/dom/ReportingBinding.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ReportBody, mWindow)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ReportBody)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ReportBody)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReportBody)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
ReportBody::ReportBody(nsPIDOMWindowInner* aWindow)
: mWindow(aWindow)
{
MOZ_ASSERT(aWindow);
}
ReportBody::~ReportBody() = default;
JSObject*
ReportBody::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return ReportBody_Binding::Wrap(aCx, this, aGivenProto);
}
} // dom namespace
} // mozilla namespace

View File

@ -0,0 +1,47 @@
/* -*- 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/. */
#ifndef mozilla_dom_ReportBody_h
#define mozilla_dom_ReportBody_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
class ReportBody : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ReportBody)
explicit ReportBody(nsPIDOMWindowInner* aWindow);
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
nsPIDOMWindowInner*
GetParentObject() const
{
return mWindow;
}
protected:
virtual ~ReportBody();
nsCOMPtr<nsPIDOMWindowInner> mWindow;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_ReportBody_h

View File

@ -0,0 +1,197 @@
/* -*- 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/ReportingObserver.h"
#include "mozilla/dom/ReportingBinding.h"
#include "nsContentUtils.h"
#include "nsGlobalWindowInner.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(ReportingObserver)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ReportingObserver)
tmp->Shutdown();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReports)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ReportingObserver)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReports)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(ReportingObserver)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ReportingObserver)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ReportingObserver)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReportingObserver)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END
/* static */ already_AddRefed<ReportingObserver>
ReportingObserver::Constructor(const GlobalObject& aGlobal,
ReportingObserverCallback& aCallback,
const ReportingObserverOptions& aOptions,
ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(window);
nsTArray<nsString> types;
if (aOptions.mTypes.WasPassed()) {
types = aOptions.mTypes.Value();
}
RefPtr<ReportingObserver> ro =
new ReportingObserver(window, aCallback, types, aOptions.mBuffered);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (NS_WARN_IF(!obs)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
aRv = obs->AddObserver(ro, "memory-pressure", true);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return ro.forget();
}
ReportingObserver::ReportingObserver(nsPIDOMWindowInner* aWindow,
ReportingObserverCallback& aCallback,
const nsTArray<nsString>& aTypes,
bool aBuffered)
: mWindow(aWindow)
, mCallback(&aCallback)
, mTypes(aTypes)
, mBuffered(aBuffered)
{
MOZ_ASSERT(aWindow);
}
ReportingObserver::~ReportingObserver()
{
Shutdown();
}
void
ReportingObserver::Shutdown()
{
Disconnect();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, "memory-pressure");
}
}
JSObject*
ReportingObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return ReportingObserver_Binding::Wrap(aCx, this, aGivenProto);
}
void
ReportingObserver::Observe()
{
mWindow->RegisterReportingObserver(this, mBuffered);
}
void
ReportingObserver::Disconnect()
{
if (mWindow) {
mWindow->UnregisterReportingObserver(this);
}
}
void
ReportingObserver::TakeRecords(nsTArray<RefPtr<Report>>& aRecords)
{
mReports.SwapElements(aRecords);
}
void
ReportingObserver::MaybeReport(Report* aReport)
{
MOZ_ASSERT(aReport);
if (!mTypes.IsEmpty()) {
nsAutoString type;
aReport->GetType(type);
if (!mTypes.Contains(type)) {
return;
}
}
bool wasEmpty = mReports.IsEmpty();
RefPtr<Report> report = aReport->Clone();
MOZ_ASSERT(report);
if (NS_WARN_IF(!mReports.AppendElement(report, fallible))) {
return;
}
if (!wasEmpty) {
return;
}
nsCOMPtr<nsPIDOMWindowInner> window = mWindow;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction(
"ReportingObserver::MaybeReport",
[window]() {
window->NotifyReportingObservers();
});
NS_DispatchToCurrentThread(r);
}
void
ReportingObserver::MaybeNotify()
{
if (mReports.IsEmpty()) {
return;
}
// Let's take the ownership of the reports.
nsTArray<RefPtr<Report>> list;
list.SwapElements(mReports);
Sequence<OwningNonNull<Report>> reports;
for (Report* report : list) {
if (NS_WARN_IF(!reports.AppendElement(*report, fallible))) {
return;
}
}
// We should report if this throws exception. But where?
mCallback->Call(reports, *this);
}
NS_IMETHODIMP
ReportingObserver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(!strcmp(aTopic, "memory-pressure"));
mReports.Clear();
return NS_OK;
}
} // dom namespace
} // mozilla namespace

View File

@ -0,0 +1,89 @@
/* -*- 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/. */
#ifndef mozilla_dom_ReportingObserver_h
#define mozilla_dom_ReportingObserver_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsWrapperCache.h"
#include "nsTArray.h"
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
class Report;
class ReportingObserverCallback;
struct ReportingObserverOptions;
class ReportingObserver final : public nsIObserver
, public nsWrapperCache
, public nsSupportsWeakReference
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ReportingObserver,
nsIObserver)
NS_DECL_NSIOBSERVER
static already_AddRefed<ReportingObserver>
Constructor(const GlobalObject& aGlobal,
ReportingObserverCallback& aCallback,
const ReportingObserverOptions& aOptions,
ErrorResult& aRv);
ReportingObserver(nsPIDOMWindowInner* aWindow,
ReportingObserverCallback& aCallback,
const nsTArray<nsString>& aTypes,
bool aBuffered);
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
nsPIDOMWindowInner*
GetParentObject() const
{
return mWindow;
}
void
Observe();
void
Disconnect();
void
TakeRecords(nsTArray<RefPtr<Report>>& aRecords);
void
MaybeReport(Report* aReport);
void
MaybeNotify();
private:
~ReportingObserver();
void
Shutdown();
nsTArray<RefPtr<Report>> mReports;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<ReportingObserverCallback> mCallback;
nsTArray<nsString> mTypes;
bool mBuffered;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_ReportingObserver_h

View File

@ -0,0 +1,33 @@
/* -*- 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/ReportingUtils.h"
#include "mozilla/dom/ReportBody.h"
#include "mozilla/dom/Report.h"
#include "nsAtom.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
/* static */ void
ReportingUtils::Report(nsPIDOMWindowInner* aWindow,
nsAtom* aType,
const nsAString& aURL,
ReportBody* aBody)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aBody);
RefPtr<mozilla::dom::Report> report =
new mozilla::dom::Report(aWindow, nsDependentAtomString(aType), aURL,
aBody);
aWindow->BroadcastReport(report);
}
} // dom namespace
} // mozilla namespace

View File

@ -0,0 +1,32 @@
/* -*- 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/. */
#ifndef mozilla_dom_ReportingUtils_h
#define mozilla_dom_ReportingUtils_h
#include "nsString.h"
class nsIPDOMWindowInner;
namespace mozilla {
namespace dom {
class ReportBody;
class ReportingUtils final
{
public:
static void
Report(nsPIDOMWindowInner* aWindow,
nsAtom* aType,
const nsAString& aURL,
ReportBody* aBody);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ReportingUtils_h

View File

@ -0,0 +1,59 @@
/* -*- 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/TestingDeprecatedInterface.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestingDeprecatedInterface, mGlobal)
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestingDeprecatedInterface)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestingDeprecatedInterface)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestingDeprecatedInterface)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/* static */ already_AddRefed<TestingDeprecatedInterface>
TestingDeprecatedInterface::Constructor(const GlobalObject& aGlobal,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(global);
RefPtr<TestingDeprecatedInterface> obj =
new TestingDeprecatedInterface(global);
return obj.forget();
}
TestingDeprecatedInterface::TestingDeprecatedInterface(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal)
{}
TestingDeprecatedInterface::~TestingDeprecatedInterface() = default;
JSObject*
TestingDeprecatedInterface::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return TestingDeprecatedInterface_Binding::Wrap(aCx, this, aGivenProto);
}
void
TestingDeprecatedInterface::DeprecatedMethod() const
{}
bool
TestingDeprecatedInterface::DeprecatedAttribute() const
{
return true;
}
} // dom namespace
} // mozilla namespace

View File

@ -0,0 +1,55 @@
/* -*- 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/. */
#ifndef mozilla_dom_TestingDeprecatedInterface_h
#define mozilla_dom_TestingDeprecatedInterface_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class TestingDeprecatedInterface final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestingDeprecatedInterface)
static already_AddRefed<TestingDeprecatedInterface>
Constructor(const GlobalObject& aGlobal,
ErrorResult& aRv);
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
nsIGlobalObject*
GetParentObject() const
{
return mGlobal;
}
void
DeprecatedMethod() const;
bool
DeprecatedAttribute() const;
private:
explicit TestingDeprecatedInterface(nsIGlobalObject* aGlobal);
~TestingDeprecatedInterface();
nsCOMPtr<nsIGlobalObject> mGlobal;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_TestingDeprecatedInterface_h

34
dom/reporting/moz.build Normal file
View File

@ -0,0 +1,34 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
EXPORTS.mozilla.dom = [
'DeprecationReportBody.h',
'FeaturePolicyViolationReportBody.h',
'Report.h',
'ReportBody.h',
'ReportingObserver.h',
'ReportingUtils.h',
'TestingDeprecatedInterface.h',
]
UNIFIED_SOURCES += [
'DeprecationReportBody.cpp',
'FeaturePolicyViolationReportBody.cpp',
'Report.cpp',
'ReportBody.cpp',
'ReportingObserver.cpp',
'ReportingUtils.cpp',
'TestingDeprecatedInterface.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
with Files('**'):
BUG_COMPONENT = ('DOM', 'Security')
FINAL_LIBRARY = 'xul'
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']

View File

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/mochitest-test",
],
};

View File

@ -0,0 +1,7 @@
[DEFAULT]
prefs =
dom.reporting.enabled=true
dom.reporting.testing.enabled=true
[test_deprecated.html]
[test_memoryPressure.html]

View File

@ -0,0 +1,143 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Deprecated reports</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
let testingInterface;
(new Promise(resolve => {
info("Testing DeprecatedTestingInterface report");
let obs = new ReportingObserver((reports, o) => {
is(obs, o, "Same observer!");
ok(reports.length == 1, "We have 1 report");
let report = reports[0];
is(report.type, "deprecation", "Deprecation report received");
is(report.url, location.href, "URL is window.location");
ok(!!report.body, "The report has a body");
ok(report.body instanceof DeprecationReportBody, "Correct type for the body");
is(report.body.id, "DeprecatedTestingInterface", "report.body.id matches DeprecatedTestingMethod");
ok(!report.body.anticipatedRemoval, "We don't have a anticipatedRemoval");
ok(report.body.message.includes("TestingDeprecatedInterface"), "We have a message");
is(report.body.sourceFile, location.href, "We have a sourceFile");
is(report.body.lineNumber, 40, "We have a lineNumber");
is(report.body.columnNumber, 21, "We have a columnNumber");
obs.disconnect();
resolve();
});
ok(!!obs, "ReportingObserver is a thing");
obs.observe();
ok(true, "ReportingObserver.observe() is callable");
testingInterface = new TestingDeprecatedInterface();
ok(true, "Created a deprecated interface");
}))
.then(() => {
info("Testing DeprecatedTestingMethod report");
return new Promise(resolve => {
let obs = new ReportingObserver((reports, o) => {
is(obs, o, "Same observer!");
ok(reports.length == 1, "We have 1 report");
let report = reports[0];
is(report.type, "deprecation", "Deprecation report received");
is(report.url, location.href, "URL is window.location");
ok(!!report.body, "The report has a body");
ok(report.body instanceof DeprecationReportBody, "Correct type for the body");
is(report.body.id, "DeprecatedTestingMethod", "report.body.id matches DeprecatedTestingMethod");
ok(!report.body.anticipatedRemoval, "We don't have a anticipatedRemoval");
ok(report.body.message.includes("TestingDeprecatedInterface.deprecatedMethod"), "We have a message");
is(report.body.sourceFile, location.href, "We have a sourceFile");
is(report.body.lineNumber, 71, "We have a lineNumber");
is(report.body.columnNumber, 4, "We have a columnNumber");
obs.disconnect();
resolve();
});
ok(!!obs, "ReportingObserver is a thing");
obs.observe();
ok(true, "ReportingObserver.observe() is callable");
testingInterface.deprecatedMethod();
ok(true, "Run a deprecated method.");
});
})
.then(() => {
info("Testing DeprecatedTestingAttribute report");
return new Promise(resolve => {
let obs = new ReportingObserver((reports, o) => {
is(obs, o, "Same observer!");
ok(reports.length == 1, "We have 1 report");
let report = reports[0];
is(report.type, "deprecation", "Deprecation report received");
is(report.url, location.href, "URL is window.location");
ok(!!report.body, "The report has a body");
ok(report.body instanceof DeprecationReportBody, "Correct type for the body");
is(report.body.id, "DeprecatedTestingAttribute", "report.body.id matches DeprecatedTestingAttribute");
ok(!report.body.anticipatedRemoval, "We don't have a anticipatedRemoval");
ok(report.body.message.includes("TestingDeprecatedInterface.deprecatedAttribute"), "We have a message");
is(report.body.sourceFile, location.href, "We have a sourceFile");
is(report.body.lineNumber, 103, "We have a lineNumber");
is(report.body.columnNumber, 4, "We have a columnNumber");
obs.disconnect();
resolve();
});
ok(!!obs, "ReportingObserver is a thing");
obs.observe();
ok(true, "ReportingObserver.observe() is callable");
ok(testingInterface.deprecatedAttribute, "Attributed called");
});
})
.then(() => {
info("Testing ReportingObserver.takeRecords()");
let p = new Promise(resolve => {
let obs = new ReportingObserver((reports, o) => {
is(obs, o, "Same observer!");
resolve(obs);
});
ok(!!obs, "ReportingObserver is a thing");
obs.observe();
ok(true, "ReportingObserver.observe() is callable");
testingInterface.deprecatedMethod();
ok(true, "Run a deprecated method.");
});
return p.then(obs => {
let reports = obs.takeRecords();
is(reports.length, 0, "No reports after an callback");
testingInterface.deprecatedAttribute + 1;
reports = obs.takeRecords();
ok(reports.length >= 1, "We have at least 1 report");
reports = obs.takeRecords();
is(reports.length, 0, "No more reports");
});
})
.then(() => { SimpleTest.finish(); });
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for ReportingObserver + memory-pressure</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
info("Testing TakeRecords() without memory-pressure");
let o = new ReportingObserver(() => {});
o.observe();
new TestingDeprecatedInterface();
let r = o.takeRecords();
is(r.length, 1, "We have 1 report");
r = o.takeRecords();
is(r.length, 0, "We have 0 reports after a takeRecords()");
info("Testing DeprecatedTestingMethod report");
new TestingDeprecatedInterface();
SpecialPowers.Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize");
r = o.takeRecords();
is(r.length, 0, "We have 0 reports after a memory-pressure");
</script>
</body>
</html>

View File

@ -16,7 +16,7 @@
#include "jsfriendapi.h"
#include "js/CompilationAndEvaluation.h"
#include "js/OffThreadScriptCompilation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "js/Utility.h"
#include "xpcpublic.h"
#include "nsCycleCollectionParticipant.h"
@ -68,7 +68,7 @@
#include "nsIScriptError.h"
#include "nsIOutputStream.h"
using JS::SourceBufferHolder;
using JS::SourceText;
using mozilla::Telemetry::LABELS_DOM_SCRIPT_PRELOAD_RESULT;
@ -1933,11 +1933,11 @@ ScriptLoader::CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest)
return ProcessRequest(aRequest);
}
mozilla::Maybe<SourceBufferHolder>
mozilla::Maybe<SourceText<char16_t>>
ScriptLoader::GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest)
{
// Return a SourceBufferHolder object holding the script's source text.
// Ownership of the buffer is transferred to the resulting SourceBufferHolder.
// Return a SourceText<char16_t> object holding the script's source text.
// Ownership of the buffer is transferred to the resulting holder.
// If there's no script text, we try to get it from the element
if (aRequest->mIsInline) {
@ -1951,12 +1951,28 @@ ScriptLoader::GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest)
}
memcpy(chars.get(), inlineData.get(), nbytes);
return Some(SourceBufferHolder(std::move(chars), inlineData.Length()));
SourceText<char16_t> srcBuf;
if (!srcBuf.init(aCx, std::move(chars), inlineData.Length())) {
return Nothing();
}
return Some(SourceText<char16_t>(std::move(srcBuf)));
}
size_t length = aRequest->ScriptText().length();
JS::UniqueTwoByteChars chars(aRequest->ScriptText().extractOrCopyRawBuffer());
return Some(SourceBufferHolder(std::move(chars), length));
if (!chars) {
JS_ReportOutOfMemory(aCx);
return Nothing();
}
SourceText<char16_t> srcBuf;
if (!srcBuf.init(aCx, std::move(chars), length)) {
return Nothing();
}
return Some(SourceText<char16_t>(std::move(srcBuf)));
}
nsresult

View File

@ -32,7 +32,9 @@
class nsIURI;
namespace JS {
class SourceBufferHolder;
template<typename UnitT> class SourceText;
} // namespace JS
namespace mozilla {
@ -507,8 +509,8 @@ private:
void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest);
mozilla::Maybe<JS::SourceBufferHolder> GetScriptSource(JSContext* aCx,
ScriptLoadRequest* aRequest);
mozilla::Maybe<JS::SourceText<char16_t>>
GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest);
void SetModuleFetchStarted(ModuleLoadRequest *aRequest);
void SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest* aRequest,

View File

@ -50,6 +50,9 @@
*
* From a C++ point of view, use FeaturePolicyUtils to obtain the list of
* features and to check if they are allowed in the current context.
*
* dom.security.featurePolicy.header.enabled pref can be used to disable the
* HTTP header support.
**/
class nsIDocument;

View File

@ -6,8 +6,11 @@
#include "FeaturePolicyUtils.h"
#include "mozilla/dom/FeaturePolicy.h"
#include "mozilla/dom/FeaturePolicyViolationReportBody.h"
#include "mozilla/dom/ReportingUtils.h"
#include "mozilla/StaticPrefs.h"
#include "nsIDocument.h"
#include "nsIURIFixup.h"
namespace mozilla {
namespace dom {
@ -87,7 +90,72 @@ FeaturePolicyUtils::IsFeatureAllowed(nsIDocument* aDocument,
FeaturePolicy* policy = aDocument->Policy();
MOZ_ASSERT(policy);
return policy->AllowsFeatureInternal(aFeatureName, policy->DefaultOrigin());
if (policy->AllowsFeatureInternal(aFeatureName, policy->DefaultOrigin())) {
return true;
}
ReportViolation(aDocument, aFeatureName);
return false;
}
/* static */ void
FeaturePolicyUtils::ReportViolation(nsIDocument* aDocument,
const nsAString& aFeatureName)
{
MOZ_ASSERT(aDocument);
nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
if (NS_WARN_IF(!uri)) {
return;
}
// Strip the URL of any possible username/password and make it ready to be
// presented in the UI.
nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
if (NS_WARN_IF(!urifixup)) {
return;
}
nsCOMPtr<nsIURI> exposableURI;
nsresult rv = urifixup->CreateExposableURI(uri, getter_AddRefs(exposableURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoCString spec;
rv = exposableURI->GetSpec(spec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
JSContext* cx = nsContentUtils::GetCurrentJSContext();
if (NS_WARN_IF(!cx)) {
return;
}
nsAutoCString fileName;
Nullable<int32_t> lineNumber;
Nullable<int32_t> columnNumber;
uint32_t line = 0;
uint32_t column = 0;
if (nsJSUtils::GetCallingLocation(cx, fileName, &line, &column)) {
lineNumber.SetValue(static_cast<int32_t>(line));
columnNumber.SetValue(static_cast<int32_t>(column));
}
nsPIDOMWindowInner* window = aDocument->GetInnerWindow();
if (NS_WARN_IF(!window)) {
return;
}
RefPtr<FeaturePolicyViolationReportBody> body =
new FeaturePolicyViolationReportBody(window, aFeatureName,
NS_ConvertUTF8toUTF16(fileName),
lineNumber, columnNumber,
NS_LITERAL_STRING("enforce"));
ReportingUtils::Report(window,
nsGkAtoms::featurePolicyViolation,
NS_ConvertUTF8toUTF16(spec), body);
}
} // dom namespace

View File

@ -48,6 +48,11 @@ public:
// Returns the default policy value for aFeatureName.
static FeaturePolicyValue
DefaultAllowListFeature(const nsAString& aFeatureName);
private:
static void
ReportViolation(nsIDocument* aDocument,
const nsAString& aFeatureName);
};
} // dom namespace

View File

@ -1,6 +1,8 @@
[DEFAULT]
prefs =
dom.security.featurePolicy.enabled=true
dom.security.featurePolicy.header.enabled=true
dom.security.featurePolicy.webidl.enabled=true
support-files =
empty.html
test_parser.html^headers^

View File

@ -809,12 +809,14 @@ StripURIForReporting(nsIURI* aURI,
// 1) If the origin of uri is a globally unique identifier (for example,
// aURI has a scheme of data, blob, or filesystem), then return the
// ASCII serialization of uris scheme.
bool isHttpOrFtp =
(NS_SUCCEEDED(aURI->SchemeIs("http", &isHttpOrFtp)) && isHttpOrFtp) ||
(NS_SUCCEEDED(aURI->SchemeIs("https", &isHttpOrFtp)) && isHttpOrFtp) ||
(NS_SUCCEEDED(aURI->SchemeIs("ftp", &isHttpOrFtp)) && isHttpOrFtp);
bool isHttpFtpOrWs =
(NS_SUCCEEDED(aURI->SchemeIs("http", &isHttpFtpOrWs)) && isHttpFtpOrWs) ||
(NS_SUCCEEDED(aURI->SchemeIs("https", &isHttpFtpOrWs)) && isHttpFtpOrWs) ||
(NS_SUCCEEDED(aURI->SchemeIs("ftp", &isHttpFtpOrWs)) && isHttpFtpOrWs) ||
(NS_SUCCEEDED(aURI->SchemeIs("ws", &isHttpFtpOrWs)) && isHttpFtpOrWs) ||
(NS_SUCCEEDED(aURI->SchemeIs("wss", &isHttpFtpOrWs)) && isHttpFtpOrWs);
if (!isHttpOrFtp) {
if (!isHttpFtpOrWs) {
// not strictly spec compliant, but what we really care about is
// http/https and also ftp. If it's not http/https or ftp, then treat aURI
// as if it's a globally unique identifier and just return the scheme.

View File

@ -45,7 +45,11 @@ function nextTest() {
document.body.appendChild(iframe);
}
SpecialPowers.pushPrefEnv({"set": [["dom.security.featurePolicy.enabled", true]]}).then(nextTest);
SpecialPowers.pushPrefEnv({"set": [
["dom.security.featurePolicy.enabled", true],
["dom.security.featurePolicy.header.enabled", true],
["dom.security.featurePolicy.webidl.enabled", true],
]}).then(nextTest);
</script>
</body>
</html>

View File

@ -550,6 +550,6 @@ Document implements DocumentOrShadowRoot;
// https://wicg.github.io/feature-policy/#policy
partial interface Document {
[SameObject, Pref="dom.security.featurePolicy.enabled"]
[SameObject, Pref="dom.security.featurePolicy.webidl.enabled"]
readonly attribute Policy policy;
};

View File

@ -13,3 +13,12 @@ interface Policy {
sequence<DOMString> allowedFeatures();
sequence<DOMString> getAllowlistForFeature(DOMString feature);
};
[Func="mozilla::dom::DOMPrefs::dom_reporting_featurePolicy_enabled"]
interface FeaturePolicyViolationReportBody : ReportBody {
readonly attribute DOMString featureId;
readonly attribute DOMString? sourceFile;
readonly attribute long? lineNumber;
readonly attribute long? columnNumber;
readonly attribute DOMString disposition;
};

View File

@ -72,7 +72,7 @@ HTMLIFrameElement implements BrowserElement;
// https://wicg.github.io/feature-policy/#policy
partial interface HTMLIFrameElement {
[SameObject, Pref="dom.security.featurePolicy.enabled"]
[SameObject, Pref="dom.security.featurePolicy.webidl.enabled"]
readonly attribute Policy policy;
[CEReactions, SetterThrows, Pure, Pref="dom.security.featurePolicy.enabled"]

View File

@ -39,6 +39,10 @@ interface PerformanceTiming {
[Pref="dom.performance.time_to_non_blank_paint.enabled"]
readonly attribute unsigned long long timeToNonBlankPaint;
// Returns 0 if a contentful paint has not happened.
[Pref="dom.performance.time_to_contentful_paint.enabled"]
readonly attribute unsigned long long timeToContentfulPaint;
// This is a Mozilla proprietary extension and not part of the
// performance/navigation timing specification. It marks the
// completion of the first presentation flush after DOMContentLoaded.

View File

@ -0,0 +1,57 @@
/* -*- 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/.
*
* The origin of this IDL file is
* https://w3c.github.io/reporting/#interface-reporting-observer
*/
[Func="mozilla::dom::DOMPrefs::dom_reporting_enabled"]
interface ReportBody {
};
[Func="mozilla::dom::DOMPrefs::dom_reporting_enabled"]
interface Report {
readonly attribute DOMString type;
readonly attribute DOMString url;
readonly attribute ReportBody? body;
};
[Constructor(ReportingObserverCallback callback, optional ReportingObserverOptions options),
Func="mozilla::dom::DOMPrefs::dom_reporting_enabled"]
interface ReportingObserver {
void observe();
void disconnect();
ReportList takeRecords();
};
callback ReportingObserverCallback = void (sequence<Report> reports, ReportingObserver observer);
dictionary ReportingObserverOptions {
sequence<DOMString> types;
boolean buffered = false;
};
typedef sequence<Report> ReportList;
[Func="mozilla::dom::DOMPrefs::dom_reporting_enabled"]
interface DeprecationReportBody : ReportBody {
readonly attribute DOMString id;
readonly attribute Date? anticipatedRemoval;
readonly attribute DOMString message;
readonly attribute DOMString? sourceFile;
readonly attribute unsigned long? lineNumber;
readonly attribute unsigned long? columnNumber;
};
[Constructor(), Deprecated="DeprecatedTestingInterface",
Func="mozilla::dom::DOMPrefs::dom_reporting_testing_enabled",
Exposed=(Window,DedicatedWorker)]
interface TestingDeprecatedInterface {
[Deprecated="DeprecatedTestingMethod"]
void deprecatedMethod();
[Deprecated="DeprecatedTestingAttribute"]
readonly attribute boolean deprecatedAttribute;
};

View File

@ -740,6 +740,7 @@ WEBIDL_FILES = [
'PushSubscriptionOptions.webidl',
'RadioNodeList.webidl',
'Range.webidl',
'Reporting.webidl',
'Request.webidl',
'Response.webidl',
'RTCStatsReport.webidl',

View File

@ -6,6 +6,8 @@
#include "ScriptLoader.h"
#include <algorithm>
#include "nsIChannel.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
@ -26,7 +28,7 @@
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "nsError.h"
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
@ -2124,11 +2126,19 @@ ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
loadInfo.mScriptTextLength,
JS::SourceBufferHolder::GiveOwnership);
loadInfo.mScriptTextBuf = nullptr;
loadInfo.mScriptTextLength = 0;
// Pass ownership of the data, first to local variables, then to the
// UniqueTwoByteChars moved into the |init| function.
size_t dataLength = 0;
char16_t* data = nullptr;
std::swap(dataLength, loadInfo.mScriptTextLength);
std::swap(data, loadInfo.mScriptTextBuf);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(aCx, JS::UniqueTwoByteChars(data), dataLength)) {
mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
return true;
}
// Our ErrorResult still shouldn't be a failure.
MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");

View File

@ -9,7 +9,7 @@
#include "js/CompilationAndEvaluation.h"
#include "js/LocaleSensitive.h"
#include "js/MemoryMetrics.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "MessageEventRunnable.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs.h"
@ -4902,12 +4902,15 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
JS::Rooted<JS::Value> unused(aes.cx());
JS::SourceBufferHolder srcBuf(script.BeginReading(), script.Length(),
JS::SourceBufferHolder::NoOwnership);
if (!JS::Evaluate(aes.cx(), options, srcBuf, &unused) &&
!JS_IsExceptionPending(aCx)) {
retval = false;
break;
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(aes.cx(), script.BeginReading(), script.Length(),
JS::SourceOwnership::Borrowed) ||
!JS::Evaluate(aes.cx(), options, srcBuf, &unused))
{
if (!JS_IsExceptionPending(aCx)) {
retval = false;
break;
}
}
} else {
ErrorResult rv;

View File

@ -17,7 +17,7 @@
#include "mozilla/dom/ScriptLoader.h"
#include "mozilla/dom/WorkletImpl.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "nsIInputStreamPump.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsNetUtil.h"
@ -401,10 +401,10 @@ ExecutionRunnable::RunOnWorkletThread()
JSAutoRealm ar(cx, globalObj);
JS::SourceBufferHolder buffer(mScriptBuffer.release(), mScriptLength,
JS::SourceBufferHolder::GiveOwnership);
JS::Rooted<JS::Value> unused(cx);
if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
JS::SourceText<char16_t> buffer;
if (!buffer.init(cx, std::move(mScriptBuffer), mScriptLength) ||
!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
ErrorResult error;
error.MightThrowJSException();
error.StealExceptionFromJSContext(cx);

View File

@ -23,6 +23,8 @@
#include "mozilla/ArrayUtils.h"
#include <algorithm>
#include "XULDocument.h"
#include "nsError.h"
@ -89,7 +91,7 @@
#include "nsTextNode.h"
#include "nsJSUtils.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "mozilla/dom/URL.h"
#include "nsIContentPolicy.h"
#include "mozAutoDocUpdate.h"
@ -1283,22 +1285,19 @@ XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
mOffThreadCompileStringBuf,
mOffThreadCompileStringLength);
if (NS_SUCCEEDED(rv)) {
// Attempt to give ownership of the buffer to the JS engine. If
// we hit offthread compilation, however, we will have to take it
// back below in order to keep the memory alive until compilation
// completes.
JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
mOffThreadCompileStringLength,
JS::SourceBufferHolder::GiveOwnership);
mOffThreadCompileStringBuf = nullptr;
mOffThreadCompileStringLength = 0;
// Pass ownership of the buffer, carefully emptying the existing
// fields in the process. Note that the |Compile| function called
// below always takes ownership of the buffer.
char16_t* units = nullptr;
size_t unitsLength = 0;
rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this);
std::swap(units, mOffThreadCompileStringBuf);
std::swap(unitsLength, mOffThreadCompileStringLength);
rv = mCurrentScriptProto->Compile(units, unitsLength,
JS::SourceOwnership::TakeOwnership,
uri, 1, this, this);
if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
// We will be notified via OnOffThreadCompileComplete when the
// compile finishes. The JS engine has taken ownership of the
// source buffer.
MOZ_RELEASE_ASSERT(!srcBuf.ownsChars());
mOffThreadCompiling = true;
BlockOnload();
return NS_OK;

View File

@ -505,9 +505,11 @@ XULContentSinkImpl::HandleEndElement(const char16_t *aName)
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
script->mOutOfLine = false;
if (doc)
script->Compile(mText, mTextLength, mDocumentURL,
if (doc) {
script->Compile(mText, mTextLength,
JS::SourceOwnership::Borrowed, mDocumentURL,
script->mLineNo, doc);
}
}
FlushText(false);

View File

@ -19,7 +19,7 @@
#include "mozilla/EventStates.h"
#include "mozilla/DeclarationBlock.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "nsFocusManager.h"
#include "nsHTMLStyleSheet.h"
#include "nsNameSpaceManager.h"
@ -2278,7 +2278,9 @@ OffThreadScriptReceiverCallback(JS::OffThreadToken* aToken, void* aCallbackData)
}
nsresult
nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
nsXULPrototypeScript::Compile(const char16_t* aText,
size_t aTextLength,
JS::SourceOwnership aOwnership,
nsIURI* aURI, uint32_t aLineNo,
nsIDocument* aDocument,
nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
@ -2286,10 +2288,21 @@ nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
// We'll compile the script in the compilation scope.
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
if (aOwnership == JS::SourceOwnership::TakeOwnership) {
// In this early-exit case -- before the |srcBuf.init| call will
// own |aText| -- we must relinquish ownership manually.
js_free(const_cast<char16_t*>(aText));
}
return NS_ERROR_UNEXPECTED;
}
JSContext* cx = jsapi.cx();
JS::SourceText<char16_t> srcBuf;
if (NS_WARN_IF(!srcBuf.init(cx, aText, aTextLength, aOwnership))) {
return NS_ERROR_FAILURE;
}
nsAutoCString urlspec;
nsresult rv = aURI->GetSpec(urlspec);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -2309,9 +2322,8 @@ nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
JS::ExposeObjectToActiveJS(scope);
}
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) {
if (!JS::CompileOffThread(cx, options,
aSrcBuf,
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) {
if (!JS::CompileOffThread(cx, options, srcBuf,
OffThreadScriptReceiverCallback,
static_cast<void*>(aOffThreadReceiver))) {
return NS_ERROR_OUT_OF_MEMORY;
@ -2319,26 +2331,13 @@ nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
} else {
JS::Rooted<JSScript*> script(cx);
if (!JS::Compile(cx, options, aSrcBuf, &script))
if (!JS::Compile(cx, options, srcBuf, &script))
return NS_ERROR_OUT_OF_MEMORY;
Set(script);
}
return NS_OK;
}
nsresult
nsXULPrototypeScript::Compile(const char16_t* aText,
int32_t aTextLength,
nsIURI* aURI,
uint32_t aLineNo,
nsIDocument* aDocument,
nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
{
JS::SourceBufferHolder srcBuf(aText, aTextLength,
JS::SourceBufferHolder::NoOwnership);
return Compile(srcBuf, aURI, aLineNo, aDocument, aOffThreadReceiver);
}
void
nsXULPrototypeScript::UnlinkJSObjects()
{

View File

@ -12,6 +12,7 @@
#ifndef nsXULElement_h__
#define nsXULElement_h__
#include "js/SourceText.h"
#include "js/TracingAPI.h"
#include "mozilla/Attributes.h"
#include "nsIServiceManager.h"
@ -52,10 +53,6 @@ enum class CallerType : uint32_t;
} // namespace dom
} // namespace mozilla
namespace JS {
class SourceBufferHolder;
} // namespace JS
////////////////////////////////////////////////////////////////////////
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
@ -218,12 +215,8 @@ public:
nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput,
nsXULPrototypeDocument* aProtoDoc);
nsresult Compile(JS::SourceBufferHolder& aSrcBuf,
nsIURI* aURI, uint32_t aLineNo,
nsIDocument* aDocument,
nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr);
nsresult Compile(const char16_t* aText, int32_t aTextLength,
nsresult Compile(const char16_t* aText, size_t aTextLength,
JS::SourceOwnership aOwnership,
nsIURI* aURI, uint32_t aLineNo,
nsIDocument* aDocument,
nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr);

View File

@ -18,7 +18,7 @@
#include "jsapi.h"
#include "js/CharacterEncoding.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "xpcpublic.h"
@ -492,9 +492,14 @@ XPCShellEnvironment::EvaluateString(const nsString& aString,
JS::CompileOptions options(cx);
options.setFileAndLine("typein", 0);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, aString.get(), aString.Length(),
JS::SourceOwnership::Borrowed))
{
return false;
}
JS::Rooted<JSScript*> script(cx);
JS::SourceBufferHolder srcBuf(aString.get(), aString.Length(),
JS::SourceBufferHolder::NoOwnership);
if (!JS::Compile(cx, options, srcBuf, &script))
{
return false;

View File

@ -82,6 +82,12 @@ class UTF8Chars : public mozilla::Range<unsigned char>
UTF8Chars(const char* aBytes, size_t aLength)
: Base(reinterpret_cast<unsigned char*>(const_cast<char*>(aBytes)), aLength)
{}
UTF8Chars(mozilla::Utf8Unit* aUnits, size_t aLength)
: UTF8Chars(reinterpret_cast<char*>(aUnits), aLength)
{}
UTF8Chars(const mozilla::Utf8Unit* aUnits, size_t aLength)
: UTF8Chars(reinterpret_cast<const char*>(aUnits), aLength)
{}
};
/*
@ -108,6 +114,10 @@ class UTF8CharsZ : public mozilla::RangedPtr<unsigned char>
MOZ_ASSERT(aBytes[aLength] == '\0');
}
UTF8CharsZ(mozilla::Utf8Unit* aUnits, size_t aLength)
: UTF8CharsZ(reinterpret_cast<char*>(aUnits), aLength)
{}
using Base::operator=;
char* c_str() { return reinterpret_cast<char*>(get()); }

View File

@ -26,7 +26,7 @@ namespace JS {
template<typename T> class AutoVector;
class SourceBufferHolder;
template<typename UnitT> class SourceText;
} // namespace JS
@ -107,7 +107,7 @@ CloneAndExecuteScript(JSContext* cx, AutoVector<JSObject*>& envChain, Handle<JSS
*/
extern JS_PUBLIC_API(bool)
Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, MutableHandle<Value> rval);
SourceText<char16_t>& srcBuf, MutableHandle<Value> rval);
/**
* As above, but providing an explicit scope chain. envChain must not include
@ -116,7 +116,7 @@ Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
*/
extern JS_PUBLIC_API(bool)
Evaluate(JSContext* cx, AutoVector<JSObject*>& envChain, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, MutableHandle<Value> rval);
SourceText<char16_t>& srcBuf, MutableHandle<Value> rval);
/**
* Evaluate the provided UTF-8 data in the scope of the current global of |cx|,
@ -154,7 +154,7 @@ EvaluateUtf8Path(JSContext* cx, const ReadOnlyCompileOptions& options,
*/
extern JS_PUBLIC_API(bool)
Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, MutableHandle<JSScript*> script);
SourceText<char16_t>& srcBuf, MutableHandle<JSScript*> script);
/**
* Compile the provided UTF-8 data into a script. If the data contains invalid
@ -166,6 +166,20 @@ extern JS_PUBLIC_API(bool)
CompileUtf8(JSContext* cx, const ReadOnlyCompileOptions& options,
const char* bytes, size_t length, MutableHandle<JSScript*> script);
/**
* Compile the provided UTF-8 data into a script. If the data contains invalid
* UTF-8, an error is reported.
*
* |script| is always set to the compiled script or to null in case of error.
*
* NOTE: This function DOES NOT INFLATE the UTF-8 bytes to UTF-16 before
* compiling them. UTF-8 compilation is currently experimental and has
* known bugs. Use only if you're willing to tolerate unspecified bugs!
*/
extern JS_PUBLIC_API(bool)
CompileUtf8DontInflate(JSContext* cx, const ReadOnlyCompileOptions& options,
const char* bytes, size_t length, MutableHandle<JSScript*> script);
/**
* Compile the provided Latin-1 data (i.e. each byte directly corresponds to
* the same Unicode code point) into a script.
@ -189,6 +203,20 @@ extern JS_PUBLIC_API(bool)
CompileUtf8File(JSContext* cx, const ReadOnlyCompileOptions& options,
FILE* file, MutableHandle<JSScript*> script);
/**
* Compile the UTF-8 contents of the given file into a script. If the contents
* contain any malformed UTF-8, an error is reported.
*
* |script| is always set to the compiled script or to null in case of error.
*
* NOTE: This function DOES NOT INFLATE the UTF-8 bytes to UTF-16 before
* compiling them. UTF-8 compilation is currently experimental and has
* known bugs. Use only if you're willing to tolerate unspecified bugs!
*/
extern JS_PUBLIC_API(bool)
CompileUtf8FileDontInflate(JSContext* cx, const ReadOnlyCompileOptions& options,
FILE* file, MutableHandle<JSScript*> script);
/**
* Compile the UTF-8 contents of the file at the given path into a script.
* (The path itself is in the system encoding, not [necessarily] UTF-8.) If
@ -202,7 +230,7 @@ CompileUtf8Path(JSContext* cx, const ReadOnlyCompileOptions& options,
extern JS_PUBLIC_API(bool)
CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, MutableHandle<JSScript*> script);
SourceText<char16_t>& srcBuf, MutableHandle<JSScript*> script);
/**
* Compile the given Latin-1 data for non-syntactic scope.
@ -227,7 +255,7 @@ extern JS_PUBLIC_API(bool)
CompileFunction(JSContext* cx, AutoVector<JSObject*>& envChain,
const ReadOnlyCompileOptions& options,
const char* name, unsigned nargs, const char* const* argnames,
SourceBufferHolder& srcBuf, MutableHandle<JSFunction*> fun);
SourceText<char16_t>& srcBuf, MutableHandle<JSFunction*> fun);
/**
* Same as above, but taking UTF-8 encoded const char* for the function body.

View File

@ -27,7 +27,7 @@ class JSScript;
namespace JS {
class SourceBufferHolder;
template<typename UnitT> class SourceText;
} // namespace JS
@ -61,8 +61,9 @@ CanDecodeOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t
*/
extern JS_PUBLIC_API(bool)
CompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf,
OffThreadCompileCallback callback, void* callbackData);
CompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
void* callbackData);
extern JS_PUBLIC_API(JSScript*)
FinishOffThreadScript(JSContext* cx, OffThreadToken* token);
@ -72,7 +73,7 @@ CancelOffThreadScript(JSContext* cx, OffThreadToken* token);
extern JS_PUBLIC_API(bool)
CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, OffThreadCompileCallback callback,
SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
void* callbackData);
extern JS_PUBLIC_API(JSObject*)

View File

@ -1,141 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
/*
* SourceBufferHolder groups buffer and length values and provides a way to
* optionally pass ownership of the buffer to the JS engine without copying.
*
* Rules for use:
*
* 1) The data array must be allocated with js_malloc() or js_realloc() if
* ownership is being granted to the SourceBufferHolder.
* 2) If ownership is not given to the SourceBufferHolder, then the memory
* must be kept alive until the JS compilation is complete.
* 3) Any code calling SourceBufferHolder::take() must guarantee to keep the
* memory alive until JS compilation completes. Normally only the JS
* engine should be calling take().
*
* Example use:
*
* size_t length = 512;
* char16_t* chars = js_pod_malloc<char16_t>(length);
* JS::SourceBufferHolder srcBuf(chars, length, JS::SourceBufferHolder::GiveOwnership);
* JS::Compile(cx, options, srcBuf);
*/
#ifndef js_SourceBufferHolder_h
#define js_SourceBufferHolder_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include <stddef.h> // size_t
#include "js/Utility.h" // JS::UniqueTwoByteChars
namespace JS {
class SourceBufferHolder final
{
private:
const char16_t* data_;
size_t length_;
bool ownsChars_;
private:
void fixEmptyBuffer() {
// Ensure that null buffers properly return an unowned, empty,
// null-terminated string.
static const char16_t NullChar_ = 0;
if (!data_) {
data_ = &NullChar_;
length_ = 0;
ownsChars_ = false;
}
}
public:
enum Ownership {
NoOwnership,
GiveOwnership
};
SourceBufferHolder(const char16_t* data, size_t dataLength, Ownership ownership)
: data_(data),
length_(dataLength),
ownsChars_(ownership == GiveOwnership)
{
fixEmptyBuffer();
}
SourceBufferHolder(UniqueTwoByteChars&& data, size_t dataLength)
: data_(data.release()),
length_(dataLength),
ownsChars_(true)
{
fixEmptyBuffer();
}
SourceBufferHolder(SourceBufferHolder&& other)
: data_(other.data_),
length_(other.length_),
ownsChars_(other.ownsChars_)
{
other.data_ = nullptr;
other.length_ = 0;
other.ownsChars_ = false;
}
~SourceBufferHolder() {
if (ownsChars_) {
js_free(const_cast<char16_t*>(data_));
}
}
/** Access the underlying source buffer without affecting ownership. */
const char16_t* get() const {
return data_;
}
/** Length of the source buffer in char16_t code units (not bytes). */
size_t length() const {
return length_;
}
/**
* Returns true if the SourceBufferHolder owns the buffer and will free it
* upon destruction. If true, it is legal to call take().
*/
bool ownsChars() const {
return ownsChars_;
}
/**
* Retrieve and take ownership of the underlying data buffer. The caller
* is now responsible for calling js_free() on the returned value, *but
* only after JS script compilation has completed*.
*
* After the buffer has been taken the SourceBufferHolder functions as if
* it had been constructed on an unowned buffer; get() and length() still
* work. In order for this to be safe the taken buffer must be kept alive
* until after JS script compilation completes as noted above.
*
* It's the caller's responsibility to check ownsChars() before taking the
* buffer. Taking and then free'ing an unowned buffer will have dire
* consequences.
*/
char16_t* take() {
MOZ_ASSERT(ownsChars_);
ownsChars_ = false;
return const_cast<char16_t*>(data_);
}
private:
SourceBufferHolder(SourceBufferHolder&) = delete;
SourceBufferHolder& operator=(SourceBufferHolder&) = delete;
};
} // namespace JS
#endif /* js_SourceBufferHolder_h */

282
js/public/SourceText.h Normal file
View File

@ -0,0 +1,282 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
/*
* SourceText encapsulates a count of char16_t (UTF-16) or Utf8Unit (UTF-8)
* code units (note: code *units*, not bytes or code points) and those code
* units ("source units"). (Latin-1 is not supported: all places where Latin-1
* must be compiled first convert to a supported encoding.)
*
* A SourceText either observes without owning, or takes ownership of, source
* units passed to |SourceText::init|. Thus SourceText can be used to
* efficiently avoid copying.
*
* Rules for use:
*
* 1) The passed-in source units must be allocated with js_malloc(),
* js_calloc(), or js_realloc() if |SourceText::init| is instructed to take
* ownership of the source units.
* 2) If |SourceText::init| merely borrows the source units, the user must
* keep them alive until associated JS compilation is complete.
* 3) Code that calls |SourceText::take{Chars,Units}()| must keep the source
* units alive until JS compilation completes. Normally only the JS engine
* should call |SourceText::take{Chars,Units}()|.
* 4) Use the appropriate SourceText parameterization depending on the source
* units encoding.
*
* Example use:
*
* size_t length = 512;
* char16_t* chars = js_pod_malloc<char16_t>(length);
* if (!chars) {
* JS_ReportOutOfMemory(cx);
* return false;
* }
* JS::SourceText<char16_t> srcBuf;
* if (!srcBuf.init(cx, chars, length, JS::SourceOwnership::TakeOwnership)) {
* return false;
* }
* JS::Rooted<JSScript*> script(cx);
* if (!JS::Compile(cx, options, srcBuf, &script)) {
* return false;
* }
*/
#ifndef js_SourceText_h
#define js_SourceText_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/Attributes.h" // MOZ_COLD, MOZ_IS_CLASS_INIT, MOZ_MUST_USE
#include "mozilla/Likely.h" // MOZ_UNLIKELY
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include <stddef.h> // size_t
#include <stdint.h> // UINT32_MAX
#include <type_traits> // std::conditional, std::is_same
#include "js/UniquePtr.h" // js::UniquePtr
#include "js/Utility.h" // JS::FreePolicy
namespace JS {
namespace detail {
MOZ_COLD extern JS_PUBLIC_API(void)
ReportSourceTooLong(JSContext* cx);
} // namespace detail
enum class SourceOwnership
{
Borrowed,
TakeOwnership,
};
template<typename Unit>
class SourceText final
{
private:
static_assert(std::is_same<Unit, mozilla::Utf8Unit>::value ||
std::is_same<Unit, char16_t>::value,
"Unit must be either char16_t or Utf8Unit for "
"SourceText<Unit>");
/** |char16_t| or |Utf8Unit| source units of uncertain validity. */
const Unit* units_ = nullptr;
/** The length in code units of |units_|. */
uint32_t length_ = 0;
/**
* Whether this owns |units_| or merely observes source units owned by some
* other object.
*/
bool ownsUnits_ = false;
public:
// A C++ character type that can represent the source units -- suitable for
// passing to C++ string functions.
using CharT =
typename std::conditional<std::is_same<Unit, char16_t>::value,
char16_t,
char>::type;
public:
/**
* Construct a SourceText. It must be initialized using |init()| before it
* can be used as compilation source text.
*/
SourceText() = default;
/**
* Construct a SourceText from contents extracted from |other|. This
* SourceText will then act exactly as |other| would have acted, had it
* not been passed to this function. |other| will return to its default-
* constructed state and must have |init()| called on it to use it.
*/
SourceText(SourceText&& other)
: units_(other.units_),
length_(other.length_),
ownsUnits_(other.ownsUnits_)
{
other.units_ = nullptr;
other.length_ = 0;
other.ownsUnits_ = false;
}
~SourceText() {
if (ownsUnits_) {
js_free(const_cast<Unit*>(units_));
}
}
/**
* Initialize this with source unit data: |char16_t| for UTF-16 source
* units, or |Utf8Unit| for UTF-8 source units.
*
* If |ownership == TakeOwnership|, *this function* takes ownership of
* |units|, *even if* this function fails, and you MUST NOT free |units|
* yourself. This single-owner-friendly approach reduces risk of leaks on
* failure.
*
* |units| may be null if |unitsLength == 0|; if so, this will silently be
* initialized using non-null, unowned units.
*/
MOZ_IS_CLASS_INIT MOZ_MUST_USE bool
init(JSContext* cx, const Unit* units, size_t unitsLength, SourceOwnership ownership) {
MOZ_ASSERT_IF(units == nullptr, unitsLength == 0);
// Ideally we'd use |Unit| and not cast below, but the risk of a static
// initializer is too great.
static const CharT emptyString[] = { '\0' };
// Initialize all fields *before* checking length. This ensures that
// if |ownership == SourceOwnership::TakeOwnership|, |units| will be
// freed when |this|'s destructor is called.
if (units) {
units_ = units;
length_ = static_cast<uint32_t>(unitsLength);
ownsUnits_ = ownership == SourceOwnership::TakeOwnership;
} else {
units_ = reinterpret_cast<const Unit*>(emptyString);
length_ = 0;
ownsUnits_ = false;
}
// IMPLEMENTATION DETAIL, DO NOT RELY ON: This limit is used so we can
// store offsets in |JSScript|s as |uint32_t|. It could be lifted
// fairly easily if desired, as the compiler uses |size_t| internally.
if (MOZ_UNLIKELY(unitsLength > UINT32_MAX)) {
detail::ReportSourceTooLong(cx);
return false;
}
return true;
}
/**
* Exactly identical to the |init()| overload above that accepts
* |const Unit*|, but instead takes character data: |const CharT*|.
*
* (We can't just write this to accept |const CharT*|, because then in the
* UTF-16 case this overload and the one above would be identical. So we
* use SFINAE to expose the |CharT| overload only if it's different.)
*/
template<typename Char,
typename =
typename std::enable_if<std::is_same<Char, CharT>::value &&
!std::is_same<Char, Unit>::value>::type>
MOZ_IS_CLASS_INIT MOZ_MUST_USE bool
init(JSContext* cx, const Char* chars, size_t charsLength, SourceOwnership ownership) {
return init(cx, reinterpret_cast<const Unit*>(chars), charsLength, ownership);
}
/**
* Initialize this using source units transferred out of |data|.
*/
MOZ_MUST_USE bool init(JSContext* cx,
js::UniquePtr<CharT[], JS::FreePolicy> data,
size_t dataLength)
{
return init(cx, data.release(), dataLength, SourceOwnership::TakeOwnership);
}
/**
* Access the encapsulated data using a code unit type.
*
* This function is useful for code that wants to interact with source text
* as *code units*, not as string data. This doesn't matter for UTF-16,
* but it's a crucial distinction for UTF-8. When UTF-8 source text is
* encapsulated, |Unit| being |mozilla::Utf8Unit| unambiguously indicates
* that the code units are UTF-8. In contrast |const char*| returned by
* |get()| below could hold UTF-8 (or its ASCII subset) or Latin-1 or (in
* particularly cursed embeddings) EBCDIC or some other legacy character
* set. Prefer this function to |get()| wherever possible.
*/
const Unit* units() const { return units_; }
/**
* Access the encapsulated data using a character type.
*
* This function is useful for interactions with character-centric actions
* like interacting with UniqueChars/UniqueTwoByteChars or printing out
* text in a debugger, that only work with |CharT|. But as |CharT| loses
* encoding specificity when UTF-8 source text is encapsulated, prefer
* |units()| to this function.
*/
const CharT* get() const { return reinterpret_cast<const CharT*>(units_); }
/**
* Returns true if this owns the source units and will free them on
* destruction. If true, it is legal to call |take{Chars,Units}()|.
*/
bool ownsUnits() const {
return ownsUnits_;
}
/**
* Count of the underlying source units -- code units, not bytes or code
* points -- in this.
*/
uint32_t length() const {
return length_;
}
/**
* Retrieve and take ownership of the underlying source units. The caller
* is now responsible for calling js_free() on the returned value, *but
* only after JS script compilation has completed*.
*
* After underlying source units have been taken, this will continue to
* refer to the same data -- it just won't own the data. get() and
* length() will return the same values, but ownsUnits() will be false.
* The taken source units must be kept alive until after JS script
* compilation completes, as noted above, for this to be safe.
*
* The caller must check ownsUnits() before calling takeUnits(). Taking
* and then free'ing an unowned buffer will have dire consequences.
*/
Unit* takeUnits() {
MOZ_ASSERT(ownsUnits_);
ownsUnits_ = false;
return const_cast<Unit*>(units_);
}
/**
* Akin to |takeUnits()| in all respects, but returns characters rather
* than units.
*/
CharT* takeChars() {
return reinterpret_cast<CharT*>(takeUnits());
}
private:
SourceText(const SourceText&) = delete;
void operator=(const SourceText&) = delete;
};
} // namespace JS
#endif /* js_SourceText_h */

View File

@ -227,7 +227,7 @@ const WHITELIST_TYPES: &'static [&'static str] = &[
"JS::ServoSizes",
"js::shadow::Object",
"js::shadow::ObjectGroup",
"JS::SourceBufferHolder",
"JS::SourceText",
"js::StackFormat",
"JSStructuredCloneCallbacks",
"JS::Symbol",

View File

@ -18,7 +18,7 @@ typedef uint32_t HashNumber;
#include "js/Conversions.h"
#include "js/Initialization.h"
#include "js/MemoryMetrics.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "js/StructuredClone.h"
// Replacements for types that are too difficult for rust-bindgen.

View File

@ -232,10 +232,11 @@ impl Runtime {
let _ar = AutoRealm::with_obj(self.cx(), glob.get());
let options = CompileOptionsWrapper::new(self.cx(), filename_cstr.as_ptr(), line_num);
let mut srcBuf = JS::SourceBufferHolder {
data_: ptr,
let mut srcBuf = JS::SourceText {
units_: ptr,
length_: len as _,
ownsChars_: false
ownsUnits_: false,
_phantom_0: marker::PhantomData
};
if !JS::Evaluate(self.cx(), options.ptr, &mut srcBuf, rval) {
debug!("...err!");

View File

@ -38,7 +38,7 @@ using ValueVector = JS::GCVector<JS::Value>;
using IdVector = JS::GCVector<jsid>;
using ScriptVector = JS::GCVector<JSScript*>;
class SourceBufferHolder;
template<typename UnitT> class SourceText;
class HandleValueArray;

View File

@ -9,9 +9,9 @@
#include "mozilla/HashFunctions.h"
#include "mozilla/Range.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/BytecodeCompilation.h"
#include "gc/HashUtil.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "vm/Debugger.h"
#include "vm/GlobalObject.h"
@ -29,7 +29,8 @@ using mozilla::RangedPtr;
using JS::AutoCheckCannotGC;
using JS::AutoStableStringChars;
using JS::CompileOptions;
using JS::SourceBufferHolder;
using JS::SourceOwnership;
using JS::SourceText;
// We should be able to assert this for *any* fp->environmentChain().
static void
@ -317,12 +318,18 @@ EvalKernel(JSContext* cx, HandleValue v, EvalType evalType, AbstractFramePtr cal
return false;
}
SourceText<char16_t> srcBuf;
const char16_t* chars = linearChars.twoByteRange().begin().get();
SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript* compiled = frontend::CompileEvalScript(cx, env, enclosing, options, srcBuf);
SourceOwnership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceOwnership::TakeOwnership
: SourceOwnership::Borrowed;
if (!srcBuf.init(cx, chars, linearStr->length(), ownership)) {
return false;
}
frontend::EvalScriptInfo info(cx, options, env, enclosing);
JSScript* compiled = frontend::CompileEvalScript(info, srcBuf);
if (!compiled) {
return false;
}
@ -401,12 +408,18 @@ js::DirectEvalStringFromIon(JSContext* cx,
return false;
}
SourceText<char16_t> srcBuf;
const char16_t* chars = linearChars.twoByteRange().begin().get();
SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript* compiled = frontend::CompileEvalScript(cx, env, enclosing, options, srcBuf);
SourceOwnership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceOwnership::TakeOwnership
: SourceOwnership::Borrowed;
if (!srcBuf.init(cx, chars, linearStr->length(), ownership)) {
return false;
}
frontend::EvalScriptInfo info(cx, options, env, enclosing);
JSScript* compiled = frontend::CompileEvalScript(info, srcBuf);
if (!compiled) {
return false;
}

View File

@ -1287,10 +1287,10 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
// ModuleBuilder
ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module,
const frontend::TokenStreamAnyChars& tokenStream)
const frontend::EitherParser& eitherParser)
: cx_(cx),
module_(cx, module),
tokenStream_(tokenStream),
eitherParser_(eitherParser),
requestedModuleSpecifiers_(cx, AtomSet(cx)),
requestedModules_(cx, RequestedModuleVector(cx)),
importEntries_(cx, ImportEntryMap(cx)),
@ -1418,7 +1418,7 @@ ModuleBuilder::processImport(frontend::BinaryNode* importNode)
uint32_t line;
uint32_t column;
tokenStream_.lineAndColumnAt(importNameNode->pn_pos.begin, &line, &column);
eitherParser_.computeLineAndColumn(importNameNode->pn_pos.begin, &line, &column);
RootedImportEntryObject importEntry(cx_);
importEntry = ImportEntryObject::create(cx_, module, importName, localName, line, column);
@ -1686,7 +1686,7 @@ ModuleBuilder::appendExportEntry(HandleAtom exportName, HandleAtom localName,
uint32_t line = 0;
uint32_t column = 0;
if (node) {
tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column);
eitherParser_.computeLineAndColumn(node->pn_pos.begin, &line, &column);
}
Rooted<ExportEntryObject*> exportEntry(cx_);
@ -1701,7 +1701,7 @@ ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleReq
{
uint32_t line;
uint32_t column;
tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column);
eitherParser_.computeLineAndColumn(node->pn_pos.begin, &line, &column);
Rooted<ExportEntryObject*> exportEntry(cx_);
exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest, importName, nullptr,
@ -1729,7 +1729,7 @@ ModuleBuilder::maybeAppendRequestedModule(HandleAtom specifier, frontend::ParseN
uint32_t line;
uint32_t column;
tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column);
eitherParser_.computeLineAndColumn(node->pn_pos.begin, &line, &column);
JSContext* cx = cx_;
RootedRequestedModuleObject req(cx, RequestedModuleObject::create(cx, specifier, line, column));

View File

@ -12,6 +12,7 @@
#include "jsapi.h"
#include "builtin/SelfHostingDefines.h"
#include "frontend/EitherParser.h"
#include "gc/Zone.h"
#include "js/GCVector.h"
#include "js/Id.h"
@ -29,7 +30,6 @@ namespace frontend {
class BinaryNode;
class ListNode;
class ParseNode;
class TokenStreamAnyChars;
} /* namespace frontend */
typedef Rooted<ModuleObject*> RootedModuleObject;
@ -355,9 +355,14 @@ class ModuleObject : public NativeObject
// creating a ModuleObject.
class MOZ_STACK_CLASS ModuleBuilder
{
public:
explicit ModuleBuilder(JSContext* cx, HandleModuleObject module,
const frontend::TokenStreamAnyChars& tokenStream);
const frontend::EitherParser& eitherParser);
public:
template<class Parser>
explicit ModuleBuilder(JSContext* cx, HandleModuleObject module, Parser* parser)
: ModuleBuilder(cx, module, frontend::EitherParser(parser))
{}
bool processImport(frontend::BinaryNode* importNode);
bool processExport(frontend::ParseNode* exportNode);
@ -384,7 +389,7 @@ class MOZ_STACK_CLASS ModuleBuilder
JSContext* cx_;
RootedModuleObject module_;
const frontend::TokenStreamAnyChars& tokenStream_;
frontend::EitherParser eitherParser_;
RootedAtomSet requestedModuleSpecifiers_;
RootedRequestedModuleVector requestedModules_;
RootedImportEntryMap importEntries_;

View File

@ -16,9 +16,9 @@
#include "builtin/Array.h"
#include "builtin/Reflect.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/TokenStream.h"
#include "js/CharacterEncoding.h"
#include "js/StableStringChars.h"
#include "vm/JSAtom.h"
@ -238,7 +238,7 @@ class NodeBuilder
typedef AutoValueArray<AST_LIMIT> CallbackArray;
JSContext* cx;
TokenStreamAnyChars* tokenStream;
frontend::Parser<frontend::FullParseHandler, char16_t>* parser;
bool saveLoc; /* save source location information? */
char const* src; /* source filename or null */
RootedValue srcval; /* source filename JS value or null */
@ -247,8 +247,13 @@ class NodeBuilder
public:
NodeBuilder(JSContext* c, bool l, char const* s)
: cx(c), tokenStream(nullptr), saveLoc(l), src(s), srcval(c), callbacks(cx),
userv(c)
: cx(c),
parser(nullptr),
saveLoc(l),
src(s),
srcval(c),
callbacks(cx),
userv(c)
{}
MOZ_MUST_USE bool init(HandleObject userobj = nullptr) {
@ -299,8 +304,8 @@ class NodeBuilder
return true;
}
void setTokenStream(TokenStreamAnyChars* ts) {
tokenStream = ts;
void setParser(frontend::Parser<frontend::FullParseHandler, char16_t>* p) {
parser = p;
}
private:
@ -706,8 +711,8 @@ NodeBuilder::newNodeLoc(TokenPos* pos, MutableHandleValue dst)
uint32_t startLineNum, startColumnIndex;
uint32_t endLineNum, endColumnIndex;
tokenStream->srcCoords.lineNumAndColumnIndex(pos->begin, &startLineNum, &startColumnIndex);
tokenStream->srcCoords.lineNumAndColumnIndex(pos->end, &endLineNum, &endColumnIndex);
parser->anyChars.srcCoords.lineNumAndColumnIndex(pos->begin, &startLineNum, &startColumnIndex);
parser->anyChars.srcCoords.lineNumAndColumnIndex(pos->end, &endLineNum, &endColumnIndex);
if (!newObject(&to)) {
return false;
@ -1833,9 +1838,9 @@ class ASTSerializer
return builder.init(userobj);
}
void setParser(Parser<FullParseHandler, char16_t>* p) {
void setParser(frontend::Parser<frontend::FullParseHandler, char16_t>* p) {
parser = p;
builder.setTokenStream(&p->anyChars);
builder.setParser(p);
}
bool program(ListNode* node, MutableHandleValue dst);
@ -2017,7 +2022,12 @@ ASTSerializer::blockStatement(ListNode* node, MutableHandleValue dst)
bool
ASTSerializer::program(ListNode* node, MutableHandleValue dst)
{
MOZ_ASSERT(parser->anyChars.srcCoords.lineNum(node->pn_pos.begin) == lineno);
#ifdef DEBUG
{
const auto& srcCoords = parser->anyChars.srcCoords;
MOZ_ASSERT(srcCoords.lineNum(node->pn_pos.begin) == lineno);
}
#endif
NodeVector stmts(cx);
return statements(node, stmts) &&
@ -3818,7 +3828,7 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
return false;
}
ModuleBuilder builder(cx, module, parser.anyChars);
ModuleBuilder builder(cx, module, &parser);
ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
pn = parser.moduleBody(&modulesc);

View File

@ -47,7 +47,7 @@
#include "js/Debug.h"
#include "js/HashTable.h"
#include "js/LocaleSensitive.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "js/StructuredClone.h"
#include "js/UbiNode.h"
@ -93,7 +93,8 @@ using mozilla::Maybe;
using JS::AutoStableStringChars;
using JS::CompileOptions;
using JS::SourceBufferHolder;
using JS::SourceOwnership;
using JS::SourceText;
// If fuzzingSafe is set, remove functionality that could cause problems with
// fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
@ -4033,7 +4034,11 @@ EvalReturningScope(JSContext* cx, unsigned argc, Value* vp)
options.setFileAndLine(filename.get(), lineno);
options.setNoScriptRval(true);
JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, src, srclen, SourceOwnership::Borrowed)) {
return false;
}
RootedScript script(cx);
if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
return false;
@ -4134,7 +4139,11 @@ ShellCloneAndExecuteScript(JSContext* cx, unsigned argc, Value* vp)
options.setFileAndLine(filename.get(), lineno);
options.setNoScriptRval(true);
JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, src, srclen, SourceOwnership::Borrowed)) {
return false;
}
RootedScript script(cx);
if (!JS::Compile(cx, options, srcBuf, &script)) {
return false;
@ -5558,9 +5567,13 @@ js::TestingFunctionArgumentToScript(JSContext* cx,
}
const char16_t* chars = linearChars.twoByteRange().begin().get();
SourceText<char16_t> source;
if (!source.init(cx, chars, len, SourceOwnership::Borrowed)) {
return nullptr;
}
RootedScript script(cx);
CompileOptions options(cx);
SourceBufferHolder source(chars, len, SourceBufferHolder::NoOwnership);
if (!JS::Compile(cx, options, source, &script)) {
return nullptr;
}

View File

@ -0,0 +1,189 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 frontend_BytecodeCompilation_h
#define frontend_BytecodeCompilation_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS
#include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Nothing
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include <stddef.h> // size_t
#include <stdint.h> // uint32_t
#include "frontend/EitherParser.h" // js::frontend::EitherParser
#include "frontend/ParseContext.h" // js::frontend::UsedNameTracker
#include "frontend/SharedContext.h" // js::frontend::Directives, js::frontend::{,Eval,Global}SharedContext
#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions
#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted
#include "js/SourceText.h" // JS::SourceText
#include "vm/JSContext.h" // js::AutoKeepAtoms
#include "vm/JSScript.h" // js::{FunctionAsync,Generator}Kind, js::LazyScript, JSScript, js::ScriptSource, js::ScriptSourceObject
#include "vm/Scope.h" // js::ScopeKind
class JSFunction;
class JSObject;
namespace js {
namespace frontend {
template<typename Unit> class SourceAwareCompiler;
template<typename Unit> class ScriptCompiler;
template<typename Unit> class ModuleCompiler;
template<typename Unit> class StandaloneFunctionCompiler;
// The BytecodeCompiler class contains resources common to compiling scripts and
// function bodies.
class MOZ_STACK_CLASS BytecodeCompiler
{
protected:
AutoKeepAtoms keepAtoms;
JSContext* cx;
const JS::ReadOnlyCompileOptions& options;
JS::Rooted<ScriptSourceObject*> sourceObject;
ScriptSource* scriptSource = nullptr;
mozilla::Maybe<UsedNameTracker> usedNames;
Directives directives;
JS::Rooted<JSScript*> script;
protected:
BytecodeCompiler(JSContext* cx, const JS::ReadOnlyCompileOptions& options);
template<typename Unit> friend class SourceAwareCompiler;
template<typename Unit> friend class ScriptCompiler;
template<typename Unit> friend class ModuleCompiler;
template<typename Unit> friend class StandaloneFunctionCompiler;
public:
JSContext* context() const {
return cx;
}
ScriptSourceObject* sourceObjectPtr() const {
return sourceObject.get();
}
protected:
void assertSourceCreated() const {
MOZ_ASSERT(sourceObject != nullptr);
MOZ_ASSERT(scriptSource != nullptr);
}
MOZ_MUST_USE bool createScriptSource(const mozilla::Maybe<uint32_t>& parameterListEnd);
void createUsedNames() {
usedNames.emplace(cx);
}
// Create a script for source of the given length, using the explicitly-
// provided toString offsets as the created script's offsets in the source.
MOZ_MUST_USE bool internalCreateScript(uint32_t toStringStart, uint32_t toStringEnd,
uint32_t sourceBufferLength);
MOZ_MUST_USE bool emplaceEmitter(mozilla::Maybe<BytecodeEmitter>& emitter,
const EitherParser& parser, SharedContext* sharedContext);
// This function lives here, not in SourceAwareCompiler, because it mostly
// uses fields in *this* class.
template<typename Unit>
MOZ_MUST_USE bool assignSource(JS::SourceText<Unit>& sourceBuffer);
bool canLazilyParse() const;
MOZ_MUST_USE bool
deoptimizeArgumentsInEnclosingScripts(JSContext* cx, JS::Handle<JSObject*> environment);
};
class MOZ_STACK_CLASS GlobalScriptInfo final
: public BytecodeCompiler
{
GlobalSharedContext globalsc_;
public:
GlobalScriptInfo(JSContext* cx, const JS::ReadOnlyCompileOptions& options, ScopeKind scopeKind)
: BytecodeCompiler(cx, options),
globalsc_(cx, scopeKind, directives, options.extraWarningsOption)
{
MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
}
GlobalSharedContext* sharedContext() {
return &globalsc_;
}
};
extern JSScript*
CompileGlobalScript(GlobalScriptInfo& info, JS::SourceText<char16_t>& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr);
extern JSScript*
CompileGlobalScript(GlobalScriptInfo& info, JS::SourceText<mozilla::Utf8Unit>& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr);
class MOZ_STACK_CLASS EvalScriptInfo final
: public BytecodeCompiler
{
JS::Handle<JSObject*> environment_;
EvalSharedContext evalsc_;
public:
EvalScriptInfo(JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JS::Handle<JSObject*> environment, JS::Handle<Scope*> enclosingScope)
: BytecodeCompiler(cx, options),
environment_(environment),
evalsc_(cx, environment_, enclosingScope, directives, options.extraWarningsOption)
{}
HandleObject environment() {
return environment_;
}
EvalSharedContext* sharedContext() {
return &evalsc_;
}
};
extern JSScript*
CompileEvalScript(EvalScriptInfo& info, JS::SourceText<char16_t>& srcBuf);
class MOZ_STACK_CLASS ModuleInfo final
: public BytecodeCompiler
{
public:
ModuleInfo(JSContext* cx, const JS::ReadOnlyCompileOptions& options)
: BytecodeCompiler(cx, options)
{}
};
class MOZ_STACK_CLASS StandaloneFunctionInfo final
: public BytecodeCompiler
{
public:
StandaloneFunctionInfo(JSContext* cx, const JS::ReadOnlyCompileOptions& options)
: BytecodeCompiler(cx, options)
{}
};
extern MOZ_MUST_USE bool
CompileLazyFunction(JSContext* cx, JS::Handle<LazyScript*> lazy,
const char16_t* units, size_t length);
extern MOZ_MUST_USE bool
CompileLazyFunction(JSContext* cx, JS::Handle<LazyScript*> lazy,
const mozilla::Utf8Unit* units, size_t length);
} // namespace frontend
} // namespace js
#endif // frontend_BytecodeCompilation_h

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@
#include "NamespaceImports.h"
#include "js/CompileOptions.h"
#include "js/SourceText.h"
#include "vm/Scope.h"
#include "vm/TraceLogging.h"
@ -29,12 +30,6 @@ class ErrorReporter;
class FunctionBox;
class ParseNode;
JSScript*
CompileGlobalScript(JSContext* cx, ScopeKind scopeKind,
const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr);
#if defined(JS_BUILD_BINAST)
JSScript*
@ -48,25 +43,15 @@ CompileLazyBinASTFunction(JSContext* cx, Handle<LazyScript*> lazy, const uint8_t
#endif // JS_BUILD_BINAST
JSScript*
CompileEvalScript(JSContext* cx, HandleObject environment,
HandleScope enclosingScope,
const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr);
ModuleObject*
CompileModule(JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JS::SourceText<char16_t>& srcBuf);
ModuleObject*
CompileModule(JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf);
ModuleObject*
CompileModule(JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
JS::SourceText<char16_t>& srcBuf,
ScriptSourceObject** sourceObjectOut);
MOZ_MUST_USE bool
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
//
// Compile a single function. The source in srcBuf must match the ECMA-262
// FunctionExpression production.
@ -82,26 +67,26 @@ CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* cha
MOZ_MUST_USE bool
CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
JS::SourceText<char16_t>& srcBuf,
const mozilla::Maybe<uint32_t>& parameterListEnd,
HandleScope enclosingScope = nullptr);
MOZ_MUST_USE bool
CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun,
const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
JS::SourceText<char16_t>& srcBuf,
const mozilla::Maybe<uint32_t>& parameterListEnd);
MOZ_MUST_USE bool
CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
JS::SourceText<char16_t>& srcBuf,
const mozilla::Maybe<uint32_t>& parameterListEnd);
MOZ_MUST_USE bool
CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
const JS::ReadOnlyCompileOptions& options,
JS::SourceBufferHolder& srcBuf,
JS::SourceText<char16_t>& srcBuf,
const mozilla::Maybe<uint32_t>& parameterListEnd);
ScriptSourceObject*

View File

@ -35,6 +35,7 @@
#include "frontend/ForOfEmitter.h"
#include "frontend/ForOfLoopControl.h"
#include "frontend/IfEmitter.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/NameOpEmitter.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"

View File

@ -16,6 +16,7 @@
#include "mozilla/Move.h"
#include "mozilla/Tuple.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/Utf8.h"
#include "mozilla/Variant.h"
#include <utility>
@ -99,11 +100,19 @@ struct ParserNewObjectBox
struct ParseHandlerMatcher
{
template<class Parser>
frontend::FullParseHandler& match(Parser *parser) {
frontend::FullParseHandler& match(Parser* parser) {
return parser->handler;
}
};
struct AnyCharsMatcher
{
template<class Parser>
frontend::TokenStreamAnyChars& match(Parser* parser) {
return parser->anyChars;
}
};
struct ParserBaseMatcher
{
template<class Parser>
@ -127,7 +136,8 @@ namespace frontend {
class EitherParser : public BCEParserHandle
{
// Leave this as a variant, to promote good form until 8-bit parser integration.
mozilla::Variant<Parser<FullParseHandler, char16_t>* const> parser;
mozilla::Variant<Parser<FullParseHandler, char16_t>* const,
Parser<FullParseHandler, mozilla::Utf8Unit>* const> parser;
using Node = typename FullParseHandler::Node;
@ -164,6 +174,13 @@ class EitherParser : public BCEParserHandle
return parser.match(std::move(matcher));
}
const TokenStreamAnyChars& anyChars() const {
return parser.match(detail::AnyCharsMatcher());
}
void computeLineAndColumn(uint32_t offset, uint32_t* line, uint32_t* column) const {
return anyChars().lineAndColumnAt(offset, line, column);
}
};
} /* namespace frontend */

View File

@ -7,6 +7,7 @@
#include "frontend/EmitterScope.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/TDZCheckCache.h"
#include "vm/GlobalObject.h"

View File

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 frontend_ModuleSharedContext_h
#define frontend_ModuleSharedContext_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/Attributes.h" // MOZ_STACK_CLASS
#include "builtin/ModuleObject.h" // js::Module{Builder,Object}
#include "frontend/SharedContext.h" // js::frontend::SharedContext
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
#include "vm/Scope.h" // js::{Module,}Scope
namespace js {
namespace frontend {
class MOZ_STACK_CLASS ModuleSharedContext
: public SharedContext
{
JS::Rooted<ModuleObject*> module_;
JS::Rooted<Scope*> enclosingScope_;
public:
JS::Rooted<ModuleScope::Data*> bindings;
ModuleBuilder& builder;
ModuleSharedContext(JSContext* cx, ModuleObject* module, Scope* enclosingScope,
ModuleBuilder& builder);
JS::Handle<ModuleObject*> module() const { return module_; }
Scope* compilationEnclosingScope() const override { return enclosingScope_; }
};
inline ModuleSharedContext*
SharedContext::asModuleContext()
{
MOZ_ASSERT(isModuleContext());
return static_cast<ModuleSharedContext*>(this);
}
} // namespace frontend
} // namespace js
#endif /* frontend_ModuleSharedContext_h */

View File

@ -11,6 +11,7 @@
#include "frontend/BytecodeCompiler.h"
#include "frontend/ErrorReporter.h"
#include "frontend/NameCollections.h"
#include "frontend/SharedContext.h"
namespace js {

View File

@ -37,6 +37,7 @@
#include "builtin/SelfHostingDefines.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/FoldConstants.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
#include "irregexp/RegExpParser.h"

View File

@ -5,6 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "frontend/SharedContext.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/ParseContext-inl.h"
#include "vm/EnvironmentObject-inl.h"

View File

@ -10,7 +10,6 @@
#include "jspubtd.h"
#include "jstypes.h"
#include "builtin/ModuleObject.h"
#include "ds/InlineTable.h"
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
@ -584,29 +583,6 @@ SharedContext::asFunctionBox()
return static_cast<FunctionBox*>(this);
}
class MOZ_STACK_CLASS ModuleSharedContext : public SharedContext
{
RootedModuleObject module_;
RootedScope enclosingScope_;
public:
Rooted<ModuleScope::Data*> bindings;
ModuleBuilder& builder;
ModuleSharedContext(JSContext* cx, ModuleObject* module, Scope* enclosingScope,
ModuleBuilder& builder);
HandleModuleObject module() const { return module_; }
Scope* compilationEnclosingScope() const override { return enclosingScope_; }
};
inline ModuleSharedContext*
SharedContext::asModuleContext()
{
MOZ_ASSERT(isModuleContext());
return static_cast<ModuleSharedContext*>(this);
}
// In generators, we treat all bindings as closed so that they get stored on
// the heap. This way there is less information to copy off the stack when
// suspending, and back on when resuming. It also avoids the need to create

View File

@ -8,7 +8,6 @@
if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_BINARYDATA'] = True
DEFINES['ENABLE_WASM_BULKMEM_OPS'] = True
DEFINES['ENABLE_WASM_THREAD_OPS'] = True
DEFINES['ENABLE_WASM_GC'] = True
DEFINES['ENABLE_WASM_GENERALIZED_TABLES'] = True
DEFINES['WASM_PRIVATE_REFTYPES'] = True
@ -22,9 +21,10 @@ if CONFIG['JS_CODEGEN_X64'] or CONFIG['JS_CODEGEN_ARM64']:
if CONFIG['MOZ_DEBUG'] or CONFIG['NIGHTLY_BUILD']:
DEFINES['JS_CACHEIR_SPEW'] = True
# Build with SharedArrayBuffer code.
# Build with SharedArrayBuffer/wasm-thread-ops code.
# NOTE: This Realm creation options decide if this is exposed to script.
DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
DEFINES['ENABLE_WASM_THREAD_OPS'] = True
# CTypes
if CONFIG['JS_HAS_CTYPES']:

View File

@ -22,6 +22,7 @@ UNIFIED_SOURCES += [
'testChromeBuffer.cpp',
'testCloneScript.cpp',
'testCompileNonSyntactic.cpp',
'testCompileUtf8.cpp',
'testDateToLocaleString.cpp',
'testDebugger.cpp',
'testDeepFreeze.cpp',

View File

@ -4,7 +4,7 @@
#include "gc/GCInternals.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "jsapi-tests/tests.h"
#include "vm/Monitor.h"
#include "vm/MutexIDs.h"
@ -74,9 +74,10 @@ testCompile(bool nonSyntactic)
JS::CompileOptions options(cx);
options.setNonSyntacticScope(nonSyntactic);
JS::RootedScript script(cx);
JS::SourceText<char16_t> buf;
CHECK(buf.init(cx, src_16, length, JS::SourceOwnership::Borrowed));
JS::SourceBufferHolder buf(src_16, length, JS::SourceBufferHolder::NoOwnership);
JS::RootedScript script(cx);
// Check explicit non-syntactic compilation first to make sure it doesn't
// modify our options object.
@ -87,7 +88,9 @@ testCompile(bool nonSyntactic)
CHECK_EQUAL(script->hasNonSyntacticScope(), true);
{
JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, src_16, length, JS::SourceOwnership::Borrowed));
CHECK(CompileForNonSyntacticScope(cx, options, srcBuf, &script));
CHECK_EQUAL(script->hasNonSyntacticScope(), true);
}
@ -100,7 +103,9 @@ testCompile(bool nonSyntactic)
CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
{
JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, src_16, length, JS::SourceOwnership::Borrowed));
CHECK(Compile(cx, options, srcBuf, &script));
CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
}
@ -110,7 +115,9 @@ testCompile(bool nonSyntactic)
OffThreadTask task;
OffThreadToken* token;
JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, src_16, length, JS::SourceOwnership::Borrowed));
CHECK(CompileOffThread(cx, options, srcBuf, task.OffThreadCallback, &task));
CHECK(token = task.waitUntilDone(cx));
CHECK(script = FinishOffThreadScript(cx, token));

View File

@ -0,0 +1,279 @@
/* 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/ArrayUtils.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Utf8.h"
#include <cstring>
#include "jsfriendapi.h"
#include "js/CharacterEncoding.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceText.h"
#include "jsapi-tests/tests.h"
#include "vm/ErrorReporting.h"
using mozilla::ArrayLength;
using mozilla::IsAsciiHexDigit;
using mozilla::Utf8Unit;
BEGIN_TEST(testUtf8BadBytes)
{
static const char badLeadingUnit[] = "var x = \x80";
CHECK(testBadUtf8(badLeadingUnit,
JSMSG_BAD_LEADING_UTF8_UNIT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(startsWith(chars, "0x80"));
CHECK(isBadLeadUnitMessage(chars));
return true;
},
"0x80"));
static const char badSecondInTwoByte[] = "var x = \xDF\x20";
CHECK(testBadUtf8(badSecondInTwoByte,
JSMSG_BAD_TRAILING_UTF8_UNIT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isBadTrailingBytesMessage(chars));
CHECK(contains(chars, "0x20"));
return true;
},
"0xDF 0x20"));
static const char badSecondInThreeByte[] = "var x = \xEF\x17\xA7";
CHECK(testBadUtf8(badSecondInThreeByte,
JSMSG_BAD_TRAILING_UTF8_UNIT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isBadTrailingBytesMessage(chars));
CHECK(contains(chars, "0x17"));
return true;
},
// Validating stops with the first invalid code unit and
// shouldn't go beyond that.
"0xEF 0x17"));
static const char lengthTwoTooShort[] = "var x = \xDF";
CHECK(testBadUtf8(lengthTwoTooShort,
JSMSG_NOT_ENOUGH_CODE_UNITS,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isNotEnoughUnitsMessage(chars));
CHECK(contains(chars, "0xDF"));
CHECK(contains(chars, " 1 byte, but 0 bytes were present"));
return true;
},
"0xDF"));
static const char forbiddenHighSurrogate[] = "var x = \xED\xA2\x87";
CHECK(testBadUtf8(forbiddenHighSurrogate,
JSMSG_FORBIDDEN_UTF8_CODE_POINT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isSurrogateMessage(chars));
CHECK(contains(chars, "0xD887"));
return true;
},
"0xED 0xA2 0x87"));
static const char forbiddenLowSurrogate[] = "var x = \xED\xB7\xAF";
CHECK(testBadUtf8(forbiddenLowSurrogate,
JSMSG_FORBIDDEN_UTF8_CODE_POINT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isSurrogateMessage(chars));
CHECK(contains(chars, "0xDDEF"));
return true;
},
"0xED 0xB7 0xAF"));
static const char oneTooBig[] = "var x = \xF4\x90\x80\x80";
CHECK(testBadUtf8(oneTooBig,
JSMSG_FORBIDDEN_UTF8_CODE_POINT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isTooBigMessage(chars));
CHECK(contains(chars, "0x110000"));
return true;
},
"0xF4 0x90 0x80 0x80"));
static const char notShortestFormZero[] = "var x = \xC0\x80";
CHECK(testBadUtf8(notShortestFormZero,
JSMSG_FORBIDDEN_UTF8_CODE_POINT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isNotShortestFormMessage(chars));
CHECK(startsWith(chars, "0x0 isn't "));
return true;
},
"0xC0 0x80"));
static const char notShortestFormNonzero[] = "var x = \xE0\x87\x80";
CHECK(testBadUtf8(notShortestFormNonzero,
JSMSG_FORBIDDEN_UTF8_CODE_POINT,
[this](JS::ConstUTF8CharsZ message) {
const char* chars = message.c_str();
CHECK(isNotShortestFormMessage(chars));
CHECK(startsWith(chars, "0x1C0 isn't "));
return true;
},
"0xE0 0x87 0x80"));
return true;
}
static constexpr size_t LengthOfByte = ArrayLength("0xFF") - 1;
static bool
startsWithByte(const char* str)
{
return str[0] == '0' &&
str[1] == 'x' &&
IsAsciiHexDigit(str[2]) &&
IsAsciiHexDigit(str[3]);
}
static bool
startsWith(const char* str, const char* prefix)
{
return std::strncmp(prefix, str, strlen(prefix)) == 0;
}
static bool
contains(const char* str, const char* substr)
{
return std::strstr(str, substr) != nullptr;
}
static bool
equals(const char* str, const char* expected)
{
return std::strcmp(str, expected) == 0;
}
static bool
isBadLeadUnitMessage(const char* str)
{
return startsWithByte(str) &&
equals(str + LengthOfByte,
" byte doesn't begin a valid UTF-8 code point");
}
static bool
isBadTrailingBytesMessage(const char* str)
{
return startsWith(str, "bad trailing UTF-8 byte ");
}
static bool
isNotEnoughUnitsMessage(const char* str)
{
return startsWithByte(str) &&
startsWith(str + LengthOfByte,
" byte in UTF-8 must be followed by ");
}
static bool
isForbiddenCodePointMessage(const char* str)
{
return contains(str, "isn't a valid code point because");
}
static bool
isSurrogateMessage(const char* str)
{
return isForbiddenCodePointMessage(str) &&
contains(str, " it's a UTF-16 surrogate");
}
static bool
isTooBigMessage(const char* str)
{
return isForbiddenCodePointMessage(str) &&
contains(str, "the maximum code point is U+10FFFF");
}
static bool
isNotShortestFormMessage(const char* str)
{
return isForbiddenCodePointMessage(str) &&
contains(str, "it wasn't encoded in shortest possible form");
}
bool
compileUtf8(const char* chars, size_t len, JS::MutableHandleScript script)
{
JS::RealmOptions globalOptions;
JS::RootedObject global(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
JS::FireOnNewGlobalHook, globalOptions));
CHECK(global);
JSAutoRealm ar(cx, global);
JS::CompileOptions options(cx);
return JS::CompileUtf8DontInflate(cx, options, chars, len, script);
}
template<size_t N, typename TestMessage>
bool
testBadUtf8(const char (&chars)[N], unsigned errorNumber,
TestMessage testMessage, const char* badBytes)
{
JS::Rooted<JSScript*> script(cx);
CHECK(!compileUtf8(chars, N - 1, &script));
JS::RootedValue exn(cx);
CHECK(JS_GetPendingException(cx, &exn));
JS_ClearPendingException(cx);
js::ErrorReport report(cx);
CHECK(report.init(cx, exn, js::ErrorReport::WithSideEffects));
const auto* errorReport = report.report();
CHECK(errorReport->errorNumber == errorNumber);
CHECK(testMessage(errorReport->message()));
{
const auto& notes = errorReport->notes;
CHECK(notes != nullptr);
auto iter = notes->begin();
CHECK(iter != notes->end());
const char* noteMessage = (*iter)->message().c_str();
// The prefix ought always be the same.
static const char expectedPrefix[] =
"the code units comprising this invalid code point were: ";
constexpr size_t expectedPrefixLen = ArrayLength(expectedPrefix) - 1;
CHECK(startsWith(noteMessage, expectedPrefix));
// The end of the prefix is the bad bytes.
CHECK(equals(noteMessage + expectedPrefixLen, badBytes));
++iter;
CHECK(iter == notes->end());
}
static const char16_t expectedContext[] = u"var x = ";
constexpr size_t expectedContextLen = ArrayLength(expectedContext) - 1;
const char16_t* lineOfContext = errorReport->linebuf();
size_t lineOfContextLength = errorReport->linebufLength();
CHECK(lineOfContext[lineOfContextLength] == '\0');
CHECK(lineOfContextLength == expectedContextLen);
CHECK(std::memcmp(lineOfContext, expectedContext, expectedContextLen * sizeof(char16_t)) == 0);
return true;
}
END_TEST(testUtf8BadBytes)

View File

@ -5,7 +5,7 @@
#include "jsfriendapi.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "jsapi-tests/tests.h"
#include "vm/ErrorReporting.h"
@ -42,8 +42,11 @@ eval(const char16_t* chars, size_t len, JS::MutableHandleValue rval)
CHECK(global);
JSAutoRealm ar(cx, global);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, chars, len, JS::SourceOwnership::Borrowed));
JS::CompileOptions options(cx);
JS::SourceBufferHolder srcBuf(chars, len, JS::SourceBufferHolder::NoOwnership);
return JS::Evaluate(cx, options, srcBuf, rval);
}

View File

@ -3,7 +3,7 @@
*/
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "jsapi-tests/tests.h"
using mozilla::ArrayLength;
@ -19,7 +19,10 @@ BEGIN_TEST(testJSEvaluateScript)
JS::CompileOptions opts(cx);
JS::AutoObjectVector scopeChain(cx);
CHECK(scopeChain.append(obj));
JS::SourceBufferHolder srcBuf(src, ArrayLength(src) - 1, JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, src, ArrayLength(src) - 1, JS::SourceOwnership::Borrowed));
CHECK(JS::Evaluate(cx, scopeChain, opts.setFileAndLine(__FILE__, __LINE__),
srcBuf, &retval));

View File

@ -4,7 +4,7 @@
#include "jsfriendapi.h"
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "jsapi-tests/tests.h"
BEGIN_TEST(testMutedErrors)
@ -57,7 +57,9 @@ eval(const char* asciiChars, bool mutedErrors, JS::MutableHandleValue rval)
options.setMutedErrors(mutedErrors)
.setFileAndLine("", 0);
JS::SourceBufferHolder srcBuf(chars.get(), len, JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, chars.get(), len, JS::SourceOwnership::Borrowed));
return JS::Evaluate(cx, options, srcBuf, rval);
}

View File

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "js/CompilationAndEvaluation.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "jsapi-tests/tests.h"
struct ScriptObjectFixture : public JSAPITest {
@ -81,8 +81,10 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript)
JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, uc_code, code_size, JS::SourceOwnership::Borrowed));
JS::RootedScript script(cx);
JS::SourceBufferHolder srcBuf(uc_code, code_size, JS::SourceBufferHolder::NoOwnership);
CHECK(JS::Compile(cx, options, srcBuf, &script));
return tryScript(script);
@ -94,8 +96,10 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript_empty)
JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, uc_code, 0, JS::SourceOwnership::Borrowed));
JS::RootedScript script(cx);
JS::SourceBufferHolder srcBuf(uc_code, 0, JS::SourceBufferHolder::NoOwnership);
CHECK(JS::Compile(cx, options, srcBuf, &script));
return tryScript(script);
@ -107,8 +111,10 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScriptForPrincipal
JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__);
JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, uc_code, code_size, JS::SourceOwnership::Borrowed));
JS::RootedScript script(cx);
JS::SourceBufferHolder srcBuf(uc_code, code_size, JS::SourceBufferHolder::NoOwnership);
CHECK(JS::Compile(cx, options, srcBuf, &script));
return tryScript(script);

View File

@ -62,7 +62,7 @@
#include "js/LocaleSensitive.h"
#include "js/Proxy.h"
#include "js/SliceBudget.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "js/StructuredClone.h"
#include "js/Utility.h"
@ -114,7 +114,7 @@ using mozilla::Some;
using JS::AutoStableStringChars;
using JS::CompileOptions;
using JS::ReadOnlyCompileOptions;
using JS::SourceBufferHolder;
using JS::SourceText;
#ifdef HAVE_VA_LIST_AS_ARRAY
#define JS_ADDRESSOF_VA_LIST(ap) ((va_list*)(ap))
@ -4162,7 +4162,7 @@ JS::FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, Han
JS_PUBLIC_API(bool)
JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, JS::MutableHandleObject module)
SourceText<char16_t>& srcBuf, JS::MutableHandleObject module)
{
MOZ_ASSERT(!cx->zone()->isAtomsZone());
AssertHeapIsIdle();

View File

@ -15,6 +15,7 @@
#include "mozilla/Range.h"
#include "mozilla/RangedPtr.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Utf8.h"
#include "mozilla/Variant.h"
#include <stdarg.h>
@ -51,7 +52,8 @@
namespace JS {
class SourceBufferHolder;
template<typename UnitT> class SourceText;
class TwoByteChars;
/** AutoValueArray roots an internal fixed-size array of Values. */
@ -3125,7 +3127,7 @@ FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleS
*/
extern JS_PUBLIC_API(bool)
CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, JS::MutableHandleObject moduleRecord);
SourceText<char16_t>& srcBuf, JS::MutableHandleObject moduleRecord);
/**
* Set a private value associated with a source text module record.

View File

@ -151,7 +151,7 @@ EXPORTS.js += [
'../public/RootingAPI.h',
'../public/SavedFrameAPI.h',
'../public/SliceBudget.h',
'../public/SourceBufferHolder.h',
'../public/SourceText.h',
'../public/StableStringChars.h',
'../public/Stream.h',
'../public/StructuredClone.h',

View File

@ -70,6 +70,7 @@
#if defined(JS_BUILD_BINAST)
# include "frontend/BinSource.h"
#endif // defined(JS_BUILD_BINAST)
#include "frontend/ModuleSharedContext.h"
#include "frontend/Parser.h"
#include "gc/PublicIterators.h"
#include "jit/arm/Simulator-arm.h"
@ -86,7 +87,7 @@
#include "js/Initialization.h"
#include "js/JSON.h"
#include "js/Printf.h"
#include "js/SourceBufferHolder.h"
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "js/StructuredClone.h"
#include "js/SweepingAPI.h"
@ -879,8 +880,15 @@ RegisterScriptPathWithModuleLoader(JSContext* cx, HandleScript script, const cha
return true;
}
enum class CompileUtf8
{
InflateToUtf16,
DontInflate,
};
static MOZ_MUST_USE bool
RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
RunFile(JSContext* cx, const char* filename, FILE* file, CompileUtf8 compileMethod,
bool compileOnly)
{
SkipUTF8BOM(file);
@ -906,9 +914,18 @@ RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
.setIsRunOnce(true)
.setNoScriptRval(true);
if (!JS::CompileUtf8File(cx, options, file, &script)) {
return false;
if (compileMethod == CompileUtf8::DontInflate) {
fprintf(stderr, "(compiling '%s' as UTF-8 without inflating)\n", filename);
if (!JS::CompileUtf8FileDontInflate(cx, options, file, &script)) {
return false;
}
} else {
if (!JS::CompileUtf8File(cx, options, file, &script)) {
return false;
}
}
MOZ_ASSERT(script);
}
@ -1363,6 +1380,7 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, bool compileOnly)
enum FileKind
{
FileScript,
FileScriptUtf8, // FileScript, but don't inflate to UTF-16 before parsing
FileModule,
FileBinAST
};
@ -1385,7 +1403,7 @@ ReportCantOpenErrorUnknownEncoding(JSContext* cx, const char* filename)
}
static MOZ_MUST_USE bool
Process(JSContext* cx, const char* filename, bool forceTTY, FileKind kind = FileScript)
Process(JSContext* cx, const char* filename, bool forceTTY, FileKind kind)
{
FILE* file;
if (forceTTY || !filename || strcmp(filename, "-") == 0) {
@ -1403,7 +1421,12 @@ Process(JSContext* cx, const char* filename, bool forceTTY, FileKind kind = File
// It's not interactive - just execute it.
switch (kind) {
case FileScript:
if (!RunFile(cx, filename, file, compileOnly)) {
if (!RunFile(cx, filename, file, CompileUtf8::InflateToUtf16, compileOnly)) {
return false;
}
break;
case FileScriptUtf8:
if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly)) {
return false;
}
break;
@ -2141,8 +2164,13 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
}
} else {
mozilla::Range<const char16_t> chars = codeChars.twoByteRange();
JS::SourceBufferHolder srcBuf(chars.begin().get(), chars.length(),
JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, chars.begin().get(), chars.length(),
JS::SourceOwnership::Borrowed))
{
return false;
}
if (envChain.length() == 0) {
(void) JS::Compile(cx, options, srcBuf, &script);
} else {
@ -2383,8 +2411,12 @@ Run(JSContext* cx, unsigned argc, Value* vp)
return false;
}
JS::SourceBufferHolder srcBuf(chars.twoByteRange().begin().get(), str->length(),
JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, chars.twoByteRange().begin().get(), str->length(),
JS::SourceOwnership::Borrowed))
{
return false;
}
RootedScript script(cx);
int64_t startClock = PRMJ_Now();
@ -3863,10 +3895,14 @@ EvalInContext(JSContext* cx, unsigned argc, Value* vp)
JS_ReportErrorASCII(cx, "Invalid scope argument to evalcx");
return false;
}
JS::CompileOptions opts(cx);
opts.setFileAndLine(filename.get(), lineno);
JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
if (!JS::Evaluate(cx, opts, srcBuf, args.rval())) {
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, src, srclen, JS::SourceOwnership::Borrowed) ||
!JS::Evaluate(cx, opts, srcBuf, args.rval()))
{
return false;
}
}
@ -3979,9 +4015,11 @@ WorkerMain(WorkerInput* input)
AutoReportException are(cx);
RootedScript script(cx);
JS::SourceBufferHolder srcBuf(input->chars.get(), input->length,
JS::SourceBufferHolder::NoOwnership);
if (!JS::Compile(cx, options, srcBuf, &script)) {
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, input->chars.get(), input->length,
JS::SourceOwnership::Borrowed) ||
!JS::Compile(cx, options, srcBuf, &script))
{
break;
}
RootedValue result(cx);
@ -4656,13 +4694,21 @@ Compile(JSContext* cx, unsigned argc, Value* vp)
.setFileAndLine("<string>", 1)
.setIsRunOnce(true)
.setNoScriptRval(true);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, stableChars.twoByteRange().begin().get(), scriptContents->length(),
JS::SourceOwnership::Borrowed))
{
return false;
}
RootedScript script(cx);
JS::SourceBufferHolder srcBuf(stableChars.twoByteRange().begin().get(),
scriptContents->length(),
JS::SourceBufferHolder::NoOwnership);
bool ok = JS::Compile(cx, options, srcBuf, &script);
if (!JS::Compile(cx, options, srcBuf, &script)) {
return false;
}
args.rval().setUndefined();
return ok;
return true;
}
static ShellCompartmentPrivate*
@ -4724,8 +4770,10 @@ ParseModule(JSContext* cx, unsigned argc, Value* vp)
}
const char16_t* chars = stableChars.twoByteRange().begin().get();
JS::SourceBufferHolder srcBuf(chars, scriptContents->length(),
JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, chars, scriptContents->length(), JS::SourceOwnership::Borrowed)) {
return false;
}
RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
if (!module) {
@ -5266,7 +5314,7 @@ Parse(JSContext* cx, unsigned argc, Value* vp)
return false;
}
ModuleBuilder builder(cx, module, parser.anyChars);
ModuleBuilder builder(cx, module, &parser);
ModuleSharedContext modulesc(cx, module, nullptr, builder);
pn = parser.moduleBody(&modulesc);
@ -5436,10 +5484,9 @@ OffThreadCompileScript(JSContext* cx, unsigned argc, Value* vp)
return false;
}
JS::SourceBufferHolder srcBuf(job->sourceChars(), length,
JS::SourceBufferHolder::NoOwnership);
if (!JS::CompileOffThread(cx, options, srcBuf,
OffThreadCompileScriptCallback, job))
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, job->sourceChars(), length, JS::SourceOwnership::Borrowed) ||
!JS::CompileOffThread(cx, options, srcBuf, OffThreadCompileScriptCallback, job))
{
job->cancel();
DeleteOffThreadJob(cx, job);
@ -5530,10 +5577,9 @@ OffThreadCompileModule(JSContext* cx, unsigned argc, Value* vp)
return false;
}
JS::SourceBufferHolder srcBuf(job->sourceChars(), length,
JS::SourceBufferHolder::NoOwnership);
if (!JS::CompileOffThreadModule(cx, options, srcBuf,
OffThreadCompileScriptCallback, job))
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, job->sourceChars(), length, JS::SourceOwnership::Borrowed) ||
!JS::CompileOffThreadModule(cx, options, srcBuf, OffThreadCompileScriptCallback, job))
{
job->cancel();
DeleteOffThreadJob(cx, job);
@ -8211,9 +8257,12 @@ EntryPoints(JSContext* cx, unsigned argc, Value* vp)
if (!stableChars.initTwoByte(cx, codeString)) {
return false;
}
JS::SourceBufferHolder srcBuf(stableChars.twoByteRange().begin().get(),
codeString->length(),
JS::SourceBufferHolder::NoOwnership);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, stableChars.twoByteRange().begin().get(), codeString->length(),
JS::SourceOwnership::Borrowed))
{
return false;
}
CompileOptions options(cx);
options.setIntroductionType("entryPoint eval")
@ -10252,6 +10301,7 @@ ProcessArgs(JSContext* cx, OptionParser* op)
}
MultiStringRange filePaths = op->getMultiStringOption('f');
MultiStringRange utf8FilePaths = op->getMultiStringOption('u');
MultiStringRange codeChunks = op->getMultiStringOption('e');
MultiStringRange modulePaths = op->getMultiStringOption('m');
MultiStringRange binASTPaths(nullptr, nullptr);
@ -10260,12 +10310,13 @@ ProcessArgs(JSContext* cx, OptionParser* op)
#endif // JS_BUILD_BINAST
if (filePaths.empty() &&
utf8FilePaths.empty() &&
codeChunks.empty() &&
modulePaths.empty() &&
binASTPaths.empty() &&
!op->getStringArg("script"))
{
return Process(cx, nullptr, true); /* Interactive. */
return Process(cx, nullptr, true, FileScript); /* Interactive. */
}
if (const char* path = op->getStringOption("module-load-path")) {
@ -10292,19 +10343,39 @@ ProcessArgs(JSContext* cx, OptionParser* op)
return false;
}
while (!filePaths.empty() || !codeChunks.empty() || !modulePaths.empty() || !binASTPaths.empty()) {
while (!filePaths.empty() ||
!utf8FilePaths.empty() ||
!codeChunks.empty() ||
!modulePaths.empty() ||
!binASTPaths.empty())
{
size_t fpArgno = filePaths.empty() ? SIZE_MAX : filePaths.argno();
size_t ufpArgno = utf8FilePaths.empty() ? SIZE_MAX : utf8FilePaths.argno();
size_t ccArgno = codeChunks.empty() ? SIZE_MAX : codeChunks.argno();
size_t mpArgno = modulePaths.empty() ? SIZE_MAX : modulePaths.argno();
size_t baArgno = binASTPaths.empty() ? SIZE_MAX : binASTPaths.argno();
if (fpArgno < ccArgno && fpArgno < mpArgno && fpArgno < baArgno) {
if (fpArgno < ufpArgno && fpArgno < ccArgno && fpArgno < mpArgno && fpArgno < baArgno) {
char* path = filePaths.front();
if (!Process(cx, path, false, FileScript)) {
return false;
}
filePaths.popFront();
} else if (ccArgno < fpArgno && ccArgno < mpArgno && ccArgno < baArgno) {
continue;
}
if (ufpArgno < fpArgno && ufpArgno < ccArgno && ufpArgno < mpArgno && ufpArgno < baArgno) {
char* path = utf8FilePaths.front();
if (!Process(cx, path, false, FileScriptUtf8)) {
return false;
}
utf8FilePaths.popFront();
continue;
}
if (ccArgno < fpArgno && ccArgno < ufpArgno && ccArgno < mpArgno && ccArgno < baArgno) {
const char* code = codeChunks.front();
JS::CompileOptions opts(cx);
@ -10321,20 +10392,31 @@ ProcessArgs(JSContext* cx, OptionParser* op)
if (sc->quitting) {
break;
}
} else if (baArgno < fpArgno && baArgno < ccArgno && baArgno < mpArgno) {
continue;
}
if (baArgno < fpArgno && baArgno < ufpArgno && baArgno < ccArgno && baArgno < mpArgno) {
char* path = binASTPaths.front();
if (!Process(cx, path, false, FileBinAST)) {
return false;
}
binASTPaths.popFront();
} else {
MOZ_ASSERT(mpArgno < fpArgno && mpArgno < ccArgno && mpArgno < baArgno);
char* path = modulePaths.front();
if (!Process(cx, path, false, FileModule)) {
return false;
}
modulePaths.popFront();
continue;
}
MOZ_ASSERT(mpArgno < fpArgno &&
mpArgno < ufpArgno &&
mpArgno < ccArgno &&
mpArgno < baArgno);
char* path = modulePaths.front();
if (!Process(cx, path, false, FileModule)) {
return false;
}
modulePaths.popFront();
}
if (sc->quitting) {
@ -10343,13 +10425,13 @@ ProcessArgs(JSContext* cx, OptionParser* op)
/* The |script| argument is processed after all options. */
if (const char* path = op->getStringArg("script")) {
if (!Process(cx, path, false)) {
if (!Process(cx, path, false, FileScript)) {
return false;
}
}
if (op->getBoolOption('i')) {
if (!Process(cx, nullptr, true)) {
if (!Process(cx, nullptr, true, FileScript)) {
return false;
}
}
@ -10957,6 +11039,9 @@ main(int argc, char** argv, char** envp)
op.setVersion(JS_GetImplementationVersion());
if (!op.addMultiStringOption('f', "file", "PATH", "File path to run")
|| !op.addMultiStringOption('u', "utf8-file", "PATH",
"File path to run, directly parsing file contents as UTF-8 "
"without first inflating to UTF-16")
|| !op.addMultiStringOption('m', "module", "PATH", "Module path to run")
#if defined(JS_BUILD_BINAST)
|| !op.addMultiStringOption('B', "binast", "PATH", "BinAST path to run")

View File

@ -9,6 +9,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/MaybeOneOf.h"
#include "mozilla/Utf8.h"
#include "js/Vector.h"
#include "vm/JSContext.h"
@ -159,6 +160,14 @@ class StringBuffer
return append(chars, chars + len);
}
/**
* Interpret the provided count of UTF-8 code units as UTF-8, and append
* the represented code points to this. If the code units contain invalid
* UTF-8, leave the internal buffer in a consistent but unspecified state,
* report an error, and return false.
*/
MOZ_MUST_USE bool append(const mozilla::Utf8Unit* units, size_t len);
MOZ_MUST_USE bool append(const JS::ConstCharPtr chars, size_t len) {
return append(chars.get(), chars.get() + len);
}

View File

@ -8,13 +8,19 @@
#include "mozilla/Range.h"
#include "mozilla/Sprintf.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Utf8.h"
#include <algorithm>
#include <type_traits>
#include "util/StringBuffer.h"
#include "util/Unicode.h" // unicode::REPLACEMENT_CHARACTER
#include "vm/JSContext.h"
using mozilla::IsAscii;
using mozilla::Utf8Unit;
using namespace js;
Latin1CharsZ
@ -607,3 +613,68 @@ JS::StringIsASCII(const char* s)
}
return true;
}
bool
StringBuffer::append(const Utf8Unit* units, size_t len)
{
if (isLatin1()) {
Latin1CharBuffer& latin1 = latin1Chars();
while (len > 0) {
if (!IsAscii(*units)) {
break;
}
if (!latin1.append(units->toUnsignedChar())) {
return false;
}
++units;
--len;
}
if (len == 0) {
return true;
}
// Non-ASCII doesn't *necessarily* mean we couldn't keep appending to
// |latin1|, but it's only possible for [U+0080, U+0100) code points,
// and handling the full complexity of UTF-8 only for that very small
// additional range isn't worth it. Inflate to two-byte storage before
// appending the remaining code points.
if (!inflateChars()) {
return false;
}
}
UTF8Chars remainingUtf8(units, len);
// Determine how many UTF-16 code units are required to represent the
// remaining units.
size_t utf16Len = 0;
auto countInflated = [&utf16Len](char16_t c) -> LoopDisposition {
utf16Len++;
return LoopDisposition::Continue;
};
if (!InflateUTF8ToUTF16<OnUTF8Error::Throw>(cx, remainingUtf8, countInflated)) {
return false;
}
TwoByteCharBuffer& buf = twoByteChars();
size_t i = buf.length();
if (!buf.growByUninitialized(utf16Len)) {
return false;
}
MOZ_ASSERT(i + utf16Len == buf.length(),
"growByUninitialized assumed to increase length immediately");
char16_t* toFill = &buf[i];
auto appendUtf16 = [&toFill](char16_t unit) {
*toFill++ = unit;
return LoopDisposition::Continue;
};
MOZ_ALWAYS_TRUE(InflateUTF8ToUTF16<OnUTF8Error::Throw>(cx, remainingUtf8, appendUtf16));
MOZ_ASSERT(toFill == buf.end());
return true;
}

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