зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
ca03f2d467
|
@ -3,5 +3,7 @@ skip-if = e10s # Bug 942707 - PDF viewer doesn't work with e10s.
|
|||
support-files = file_pdfjs_test.pdf
|
||||
|
||||
[browser_pdfjs_main.js]
|
||||
skip-if = debug # bug 1058695
|
||||
[browser_pdfjs_savedialog.js]
|
||||
[browser_pdfjs_views.js]
|
||||
skip-if = debug # bug 1058695
|
||||
|
|
|
@ -1188,7 +1188,7 @@ public:
|
|||
* Should only be called during MediaStreamListener callbacks or during
|
||||
* ProcessedMediaStream::ProcessInput().
|
||||
*/
|
||||
void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
|
||||
virtual void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
|
||||
{
|
||||
*mPendingUpdateRunnables.AppendElement() = aRunnable;
|
||||
}
|
||||
|
|
|
@ -52,10 +52,13 @@
|
|||
if (validStructuredMessage(message)) {
|
||||
switch (message.action) {
|
||||
case "test_status":
|
||||
ok(message.expected === undefined, message.subtest, message.message);
|
||||
break;
|
||||
case "test_end":
|
||||
ok(message.expected === undefined, message.test, message.message);
|
||||
let test_tokens = message.test.split("/");
|
||||
let test_name = test_tokens[test_tokens.length - 1];
|
||||
if (message.subtest) {
|
||||
test_name += " | " + message.subtest;
|
||||
}
|
||||
ok(message.expected === undefined, test_name, message.message);
|
||||
break;
|
||||
case "log":
|
||||
info(message.message);
|
||||
|
|
|
@ -134,6 +134,7 @@ SVGSwitchElement::FindActiveChild() const
|
|||
if (allowReorder && !acceptLangs.IsEmpty()) {
|
||||
int32_t bestLanguagePreferenceRank = -1;
|
||||
nsIContent *bestChild = nullptr;
|
||||
nsIContent *defaultChild = nullptr;
|
||||
for (nsIContent* child = nsINode::GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
|
@ -152,7 +153,12 @@ SVGSwitchElement::FindActiveChild() const
|
|||
// best possible match
|
||||
return child;
|
||||
case -1:
|
||||
// not found
|
||||
// no match
|
||||
break;
|
||||
case -2:
|
||||
// no systemLanguage attribute. If there's nothing better
|
||||
// we'll use the last such child.
|
||||
defaultChild = child;
|
||||
break;
|
||||
default:
|
||||
if (bestLanguagePreferenceRank == -1 ||
|
||||
|
@ -167,7 +173,7 @@ SVGSwitchElement::FindActiveChild() const
|
|||
bestChild = child;
|
||||
}
|
||||
}
|
||||
return bestChild;
|
||||
return bestChild ? bestChild : defaultChild;
|
||||
}
|
||||
|
||||
for (nsIContent* child = nsINode::GetFirstChild();
|
||||
|
|
|
@ -77,6 +77,10 @@ SVGTests::GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const
|
|||
{
|
||||
const nsDefaultStringComparator defaultComparator;
|
||||
|
||||
if (!mStringListAttributes[LANGUAGE].IsExplicitlySet()) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
int32_t lowestRank = -1;
|
||||
|
||||
for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) {
|
||||
|
|
|
@ -42,7 +42,8 @@ public:
|
|||
* exactly equals one of the language names or exactly equals a prefix of
|
||||
* one of the language names in the systemLanguage attribute.
|
||||
* @returns 2 * the lowest index in the aAcceptLangs that matches + 1
|
||||
* if only the prefix matches, or -1 if no indices match.
|
||||
* if only the prefix matches, -2 if there's no systemLanguage attribute,
|
||||
* or -1 if no indices match.
|
||||
* XXX This algorithm is O(M*N).
|
||||
*/
|
||||
int32_t GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const;
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<switch id="s">
|
||||
<rect systemLanguage="fr" id="first" x="75" y="100" width="70" height="70" fill="yellow"/>
|
||||
<rect id="second" x="75" y="100" width="50" height="50" fill="lime"/>
|
||||
<rect x="75" y="100" width="80" height="80" fill="red"/>
|
||||
<rect id="third" x="75" y="100" width="80" height="80" fill="red"/>
|
||||
</switch>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 478 B После Ширина: | Высота: | Размер: 489 B |
|
@ -34,7 +34,7 @@ function checkBounds(element, x, y, w, h)
|
|||
++test;
|
||||
}
|
||||
|
||||
function checkWidth(element, x, y, w, h)
|
||||
function checkWidth(element, w)
|
||||
{
|
||||
var bbox = element.getBBox();
|
||||
var name = element.nodeName;
|
||||
|
@ -56,6 +56,7 @@ function run1()
|
|||
var s = doc.getElementById("s");
|
||||
var first = doc.getElementById("first");
|
||||
var second = doc.getElementById("second");
|
||||
var third = doc.getElementById("third");
|
||||
|
||||
/* test for an exact match */
|
||||
second.setAttribute("systemLanguage", "en-gb");
|
||||
|
@ -63,11 +64,11 @@ function run1()
|
|||
|
||||
/* test for a close match i.e. the same language prefix */
|
||||
second.setAttribute("systemLanguage", "en-us");
|
||||
checkWidth(s, 75, 100, 50, 50);
|
||||
checkWidth(s, 50);
|
||||
|
||||
/* test that we pick the first match */
|
||||
first.setAttribute("systemLanguage", "it");
|
||||
checkWidth(s, 75, 100, 70, 70);
|
||||
checkWidth(s, 70);
|
||||
|
||||
/* this time with reordering */
|
||||
first.setAttribute("systemLanguage", "fr");
|
||||
|
@ -75,20 +76,33 @@ function run1()
|
|||
|
||||
/* test for an exact match */
|
||||
second.setAttribute("systemLanguage", "en-gb");
|
||||
checkWidth(s, 75, 100, 50, 50);
|
||||
checkWidth(s, 50);
|
||||
|
||||
/* test for a close match i.e. the same language prefix */
|
||||
second.setAttribute("systemLanguage", "en-us");
|
||||
checkWidth(s, 75, 100, 50, 50);
|
||||
checkWidth(s, 50);
|
||||
|
||||
/* test that we pick the best match */
|
||||
second.setAttribute("systemLanguage", "it");
|
||||
checkWidth(s, 75, 100, 50, 50);
|
||||
checkWidth(s, 50);
|
||||
|
||||
/* test that we use the default if nothing matches */
|
||||
second.setAttribute("systemLanguage", "fr");
|
||||
checkWidth(s, 80);
|
||||
|
||||
/* test we still ignore non-matches */
|
||||
second.removeAttribute("systemLanguage");
|
||||
third.setAttribute("systemLanguage", "fr");
|
||||
checkWidth(s, 50);
|
||||
|
||||
/* check what happens if nothing matches */
|
||||
second.setAttribute("systemLanguage", "fr");
|
||||
checkWidth(s, 0);
|
||||
|
||||
/* test that we pick the best match */
|
||||
first.setAttribute("systemLanguage", "en");
|
||||
second.setAttribute("systemLanguage", "en-gb");
|
||||
checkWidth(s, 75, 100, 50, 50);
|
||||
checkWidth(s, 50);
|
||||
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -349,7 +349,7 @@ public:
|
|||
(*p >= 0x61 && *p <= 0x7A)) {
|
||||
mValue.Append(*p);
|
||||
} else {
|
||||
mValue.AppendPrintf("%%%X", *p);
|
||||
mValue.AppendPrintf("%%%.2X", *p);
|
||||
}
|
||||
|
||||
++p;
|
||||
|
|
|
@ -25,6 +25,7 @@ skip-if = buildapp == 'mulet'
|
|||
[test_bug999456.html]
|
||||
[test_bug1022229.html]
|
||||
[test_bug1043106.html]
|
||||
[test_bug1064481.html]
|
||||
[test_clearTimeoutIntervalNoArg.html]
|
||||
[test_consoleEmptyStack.html]
|
||||
[test_constructor-assignment.html]
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1064481
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1064481</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1064481">Mozilla Bug 1064481</a>
|
||||
<iframe id="iframe"></iframe>
|
||||
<script type="application/javascript">
|
||||
|
||||
var a = new URLSearchParams();
|
||||
|
||||
a.set('foobar', 'a\nb');
|
||||
is(a.toString(), 'foobar=a%0Ab', "Bug fixed");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -38,7 +38,7 @@ protected:
|
|||
|
||||
bool StringifyToJSON(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
nsAString& aJSON);
|
||||
nsAString& aJSON) const;
|
||||
private:
|
||||
// aString is expected to actually be an nsAString*. Should only be
|
||||
// called from StringifyToJSON.
|
||||
|
|
|
@ -1635,7 +1635,7 @@ DictionaryBase::ParseJSON(JSContext* aCx,
|
|||
bool
|
||||
DictionaryBase::StringifyToJSON(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
nsAString& aJSON)
|
||||
nsAString& aJSON) const
|
||||
{
|
||||
return JS_Stringify(aCx, aValue, JS::NullPtr(), JS::NullHandleValue,
|
||||
AppendJSONToString, &aJSON);
|
||||
|
|
|
@ -11240,7 +11240,7 @@ class CGDictionary(CGThing):
|
|||
JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); // Usage approved by bholley
|
||||
JS::Rooted<JS::Value> obj(cx);
|
||||
return ToObjectInternal(cx, &obj) && StringifyToJSON(cx, &obj, aJSON);
|
||||
"""))
|
||||
"""), const=True)
|
||||
|
||||
def toObjectInternalMethod(self):
|
||||
body = ""
|
||||
|
|
|
@ -19,6 +19,13 @@ using namespace mozilla::dom;
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
FakeMediaStreamGraph::DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
|
||||
{
|
||||
nsRefPtr<nsIRunnable> task = aRunnable;
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
CameraPreviewMediaStream::CameraPreviewMediaStream(DOMMediaStream* aWrapper)
|
||||
: MediaStream(aWrapper)
|
||||
, mMutex("mozilla::camera::CameraPreviewMediaStream")
|
||||
|
@ -27,6 +34,7 @@ CameraPreviewMediaStream::CameraPreviewMediaStream(DOMMediaStream* aWrapper)
|
|||
, mRateLimit(false)
|
||||
{
|
||||
SetGraphImpl(MediaStreamGraph::GetInstance());
|
||||
mFakeMediaStreamGraph = new FakeMediaStreamGraph();
|
||||
mIsConsumed = false;
|
||||
}
|
||||
|
||||
|
@ -55,11 +63,10 @@ CameraPreviewMediaStream::AddVideoOutput(VideoFrameContainer* aContainer)
|
|||
if (mVideoOutputs.Length() > 1) {
|
||||
return;
|
||||
}
|
||||
MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
|
||||
mIsConsumed = true;
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyConsumptionChanged(gm, MediaStreamListener::CONSUMED);
|
||||
l->NotifyConsumptionChanged(mFakeMediaStreamGraph, MediaStreamListener::CONSUMED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,11 +79,10 @@ CameraPreviewMediaStream::RemoveVideoOutput(VideoFrameContainer* aContainer)
|
|||
if (!mVideoOutputs.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
|
||||
mIsConsumed = false;
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyConsumptionChanged(gm, MediaStreamListener::NOT_CONSUMED);
|
||||
l->NotifyConsumptionChanged(mFakeMediaStreamGraph, MediaStreamListener::NOT_CONSUMED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,9 +96,9 @@ CameraPreviewMediaStream::AddListener(MediaStreamListener* aListener)
|
|||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
|
||||
MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
|
||||
listener->NotifyBlockingChanged(gm, MediaStreamListener::UNBLOCKED);
|
||||
listener->NotifyBlockingChanged(mFakeMediaStreamGraph, MediaStreamListener::UNBLOCKED);
|
||||
listener->NotifyHasCurrentData(mFakeMediaStreamGraph);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -100,10 +106,9 @@ CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
|
|||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
|
||||
nsRefPtr<MediaStreamListener> listener(aListener);
|
||||
mListeners.RemoveElement(aListener);
|
||||
listener->NotifyEvent(gm, MediaStreamListener::EVENT_REMOVED);
|
||||
listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamListener::EVENT_REMOVED);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -11,6 +11,23 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
class FakeMediaStreamGraph : public MediaStreamGraph
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FakeMediaStreamGraph)
|
||||
public:
|
||||
FakeMediaStreamGraph()
|
||||
: MediaStreamGraph()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
~FakeMediaStreamGraph()
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a stream for camera preview.
|
||||
*
|
||||
|
@ -50,6 +67,7 @@ protected:
|
|||
int32_t mInvalidatePending;
|
||||
uint32_t mDiscardedFrames;
|
||||
bool mRateLimit;
|
||||
nsRefPtr<FakeMediaStreamGraph> mFakeMediaStreamGraph;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -51,10 +51,13 @@
|
|||
if (validStructuredMessage(message)) {
|
||||
switch (message.action) {
|
||||
case "test_status":
|
||||
ok(message.expected === undefined, message.subtest, message.message);
|
||||
break;
|
||||
case "test_end":
|
||||
ok(message.expected === undefined, message.test, message.message);
|
||||
let test_tokens = message.test.split("/");
|
||||
let test_name = test_tokens[test_tokens.length - 1];
|
||||
if (message.subtest) {
|
||||
test_name += " | " + message.subtest;
|
||||
}
|
||||
ok(message.expected === undefined, test_name, message.message);
|
||||
break;
|
||||
case "log":
|
||||
info(message.message);
|
||||
|
|
|
@ -67,10 +67,13 @@
|
|||
if (validStructuredMessage(message)) {
|
||||
switch (message.action) {
|
||||
case "test_status":
|
||||
ok(message.expected === undefined, message.subtest, message.message);
|
||||
break;
|
||||
case "test_end":
|
||||
ok(message.expected === undefined, message.test, message.message);
|
||||
let test_tokens = message.test.split("/");
|
||||
let test_name = test_tokens[test_tokens.length - 1];
|
||||
if (message.subtest) {
|
||||
test_name += " | " + message.subtest;
|
||||
}
|
||||
ok(message.expected === undefined, test_name, message.message);
|
||||
break;
|
||||
case "log":
|
||||
info(message.message);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
interface nsIDocument;
|
||||
interface nsIURI;
|
||||
|
||||
[uuid(8d2f64db-5585-4876-a1c9-391e16549068)]
|
||||
[builtinclass, uuid(79ad5b57-d65d-46d3-b5e9-32d27e16052d)]
|
||||
interface nsIServiceWorkerManager : nsISupports
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -2641,8 +2641,9 @@ ContentParent::Observe(nsISupports* aSubject,
|
|||
MOZ_ASSERT(cmsg[identOffset - 1] == '=');
|
||||
FileDescriptor dmdFileDesc;
|
||||
#ifdef MOZ_DMD
|
||||
FILE *dmdFile;
|
||||
nsAutoString dmdIdent(Substring(msg, identOffset));
|
||||
if (!dmdIdent.IsEmpty()) {
|
||||
FILE *dmdFile = nullptr;
|
||||
nsresult rv = nsMemoryInfoDumper::OpenDMDFile(dmdIdent, Pid(), &dmdFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// Proceed with the memory report as if DMD were disabled.
|
||||
|
@ -2652,6 +2653,7 @@ ContentParent::Observe(nsISupports* aSubject,
|
|||
dmdFileDesc = FILEToFileDescriptor(dmdFile);
|
||||
fclose(dmdFile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
unused << SendPMemoryReportRequestConstructor(
|
||||
generation, anonymize, minimize, dmdFileDesc);
|
||||
|
|
|
@ -555,7 +555,46 @@ RTCPeerConnection.prototype = {
|
|||
},
|
||||
|
||||
createOffer: function(onSuccess, onError, options) {
|
||||
options = options || {};
|
||||
|
||||
// TODO: Remove old constraint-like RTCOptions support soon (Bug 1064223).
|
||||
function convertLegacyOptions(o) {
|
||||
if (!(o.mandatory || o.optional) ||
|
||||
Object.keys(o).length != ((o.mandatory && o.optional)? 2 : 1)) {
|
||||
return false;
|
||||
}
|
||||
let old = o.mandatory || {};
|
||||
if (o.mandatory) {
|
||||
delete o.mandatory;
|
||||
}
|
||||
if (o.optional) {
|
||||
o.optional.forEach(one => {
|
||||
// The old spec had optional as an array of objects w/1 attribute each.
|
||||
// Assumes our JS-webidl bindings only populate passed-in properties.
|
||||
let key = Object.keys(one)[0];
|
||||
if (key && old[key] === undefined) {
|
||||
old[key] = one[key];
|
||||
}
|
||||
});
|
||||
delete o.optional;
|
||||
}
|
||||
o.offerToReceiveAudio = old.OfferToReceiveAudio;
|
||||
o.offerToReceiveVideo = old.OfferToReceiveVideo;
|
||||
o.mozDontOfferDataChannel = old.MozDontOfferDataChannel;
|
||||
o.mozBundleOnly = old.MozBundleOnly;
|
||||
Object.keys(o).forEach(k => {
|
||||
if (o[k] === undefined) {
|
||||
delete o[k];
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options && convertLegacyOptions(options)) {
|
||||
this.logWarning(
|
||||
"Mandatory/optional in createOffer options is deprecated! Use " +
|
||||
JSON.stringify(options) + " instead (note the case difference)!",
|
||||
null, 0);
|
||||
}
|
||||
this._queueOrRun({
|
||||
func: this._createOffer,
|
||||
args: [onSuccess, onError, options],
|
||||
|
@ -598,6 +637,12 @@ RTCPeerConnection.prototype = {
|
|||
},
|
||||
|
||||
setLocalDescription: function(desc, onSuccess, onError) {
|
||||
if (!onSuccess || !onError) {
|
||||
this.logWarning(
|
||||
"setLocalDescription called without success/failure callbacks. This is deprecated, and will be an error in the future.",
|
||||
null, 0);
|
||||
}
|
||||
|
||||
this._localType = desc.type;
|
||||
|
||||
let type;
|
||||
|
@ -629,6 +674,11 @@ RTCPeerConnection.prototype = {
|
|||
},
|
||||
|
||||
setRemoteDescription: function(desc, onSuccess, onError) {
|
||||
if (!onSuccess || !onError) {
|
||||
this.logWarning(
|
||||
"setRemoteDescription called without success/failure callbacks. This is deprecated, and will be an error in the future.",
|
||||
null, 0);
|
||||
}
|
||||
this._remoteType = desc.type;
|
||||
|
||||
let type;
|
||||
|
@ -765,6 +815,11 @@ RTCPeerConnection.prototype = {
|
|||
},
|
||||
|
||||
addIceCandidate: function(cand, onSuccess, onError) {
|
||||
if (!onSuccess || !onError) {
|
||||
this.logWarning(
|
||||
"addIceCandidate called without success/failure callbacks. This is deprecated, and will be an error in the future.",
|
||||
null, 0);
|
||||
}
|
||||
if (!cand.candidate && !cand.sdpMLineIndex) {
|
||||
throw new this._win.DOMError("",
|
||||
"Invalid candidate passed to addIceCandidate!");
|
||||
|
|
|
@ -60,10 +60,13 @@
|
|||
if (validStructuredMessage(message)) {
|
||||
switch (message.action) {
|
||||
case "test_status":
|
||||
ok(message.expected === undefined, message.subtest, message.message);
|
||||
break;
|
||||
case "test_end":
|
||||
ok(message.expected === undefined, message.test, message.message);
|
||||
let test_tokens = message.test.split("/");
|
||||
let test_name = test_tokens[test_tokens.length - 1];
|
||||
if (message.subtest) {
|
||||
test_name += " | " + message.subtest;
|
||||
}
|
||||
ok(message.expected === undefined, test_name, message.message);
|
||||
break;
|
||||
case "log":
|
||||
info(message.message);
|
||||
|
|
|
@ -547,6 +547,8 @@ function PeerConnectionTest(options) {
|
|||
else
|
||||
this.pcRemote = null;
|
||||
|
||||
this.steeplechase = this.pcLocal === null || this.pcRemote === null;
|
||||
|
||||
// Create command chain instance and assign default commands
|
||||
this.chain = new CommandChain(this, options.commands);
|
||||
if (!options.is_local) {
|
||||
|
@ -863,10 +865,109 @@ PCT_iceCandidateHandler(caller, candidate) {
|
|||
target.storeOrAddIceCandidate(candidate);
|
||||
} else {
|
||||
info("sending ice candidate to signaling server");
|
||||
send_message({"ice_candidate": candidate});
|
||||
send_message({"type": "ice_candidate", "ice_candidate": candidate});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Installs a polling function for the socket.io client to read
|
||||
* all messages from the chat room into a message queue.
|
||||
*/
|
||||
PeerConnectionTest.prototype.setupSignalingClient = function
|
||||
PCT_setupSignalingClient() {
|
||||
var self = this;
|
||||
|
||||
self.signalingMessageQueue = [];
|
||||
self.signalingCallbacks = {};
|
||||
self.signalingLoopRun = true;
|
||||
|
||||
function queueMessage(message) {
|
||||
info("Received signaling message: " + JSON.stringify(message));
|
||||
var fired = false;
|
||||
Object.keys(self.signalingCallbacks).forEach(function(name) {
|
||||
if (name === message.type) {
|
||||
info("Invoking callback for message type: " + name);
|
||||
self.signalingCallbacks[name](message);
|
||||
fired = true;
|
||||
}
|
||||
});
|
||||
if (!fired) {
|
||||
self.signalingMessageQueue.push(message);
|
||||
info("signalingMessageQueue.length: " + self.signalingMessageQueue.length);
|
||||
}
|
||||
if (self.signalingLoopRun) {
|
||||
wait_for_message().then(queueMessage);
|
||||
} else {
|
||||
info("Exiting signaling message event loop");
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_message().then(queueMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a flag to stop reading further messages from the chat room.
|
||||
*/
|
||||
PeerConnectionTest.prototype.signalingMessagesFinished = function
|
||||
PCT_signalingMessagesFinished() {
|
||||
this.signalingLoopRun = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to stop reading message from chat room once trickle ICE
|
||||
* on the far end is over.
|
||||
*
|
||||
* @param {string} caller
|
||||
* The lable of the caller of the function
|
||||
*/
|
||||
PeerConnectionTest.prototype.signalEndOfTrickleIce = function
|
||||
PCT_signalEndOfTrickleIce(caller) {
|
||||
if (this.steeplechase) {
|
||||
send_message({"type": "end_of_trickle_ice"});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a callback function to deliver messages from the chat room
|
||||
* directly instead of storing them in the message queue.
|
||||
*
|
||||
* @param {string} messageType
|
||||
* For which message types should the callback get invoked.
|
||||
*
|
||||
* @param {function} onMessage
|
||||
* The function which gets invoked if a message of the messageType
|
||||
* has been received from the chat room.
|
||||
*/
|
||||
PeerConnectionTest.prototype.registerSignalingCallback = function
|
||||
PCT_registerSignalingCallback(messageType, onMessage) {
|
||||
this.signalingCallbacks[messageType] = onMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the message queue for the first message of a given type
|
||||
* and invokes the given callback function, or registers the callback
|
||||
* function for future messages if the queue contains no such message.
|
||||
*
|
||||
* @param {string} messageType
|
||||
* The type of message to search and register for.
|
||||
*
|
||||
* @param {function} onMessage
|
||||
* The callback function which gets invoked with the messages
|
||||
* of the given mesage type.
|
||||
*/
|
||||
PeerConnectionTest.prototype.getSignalingMessage = function
|
||||
PCT_getSignalingMessage(messageType, onMessage) {
|
||||
for(var i=0; i < this.signalingMessageQueue.length; i++) {
|
||||
if (messageType === this.signalingMessageQueue[i].type) {
|
||||
//FIXME
|
||||
info("invoking callback on message " + i + " from message queue, for message type:" + messageType);
|
||||
onMessage(this.signalingMessageQueue.splice(i, 1)[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.registerSignalingCallback(messageType, onMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class handles tests for data channels.
|
||||
*
|
||||
|
@ -1991,6 +2092,7 @@ PeerConnectionWrapper.prototype = {
|
|||
if (!anEvent.candidate) {
|
||||
info(self.label + ": received end of trickle ICE event");
|
||||
self.endOfTrickleIce = true;
|
||||
test.signalEndOfTrickleIce(self.label);
|
||||
} else {
|
||||
if (self.endOfTrickleIce) {
|
||||
ok(false, "received ICE candidate after end of trickle");
|
||||
|
@ -2005,8 +2107,6 @@ PeerConnectionWrapper.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
//FIXME: in the steeplecase scenario we need to setup a permanent listener
|
||||
// for ice candidates from the signaling server here
|
||||
self._pc.onicecandidate = iceCandidateCallback;
|
||||
},
|
||||
|
||||
|
@ -2041,7 +2141,18 @@ PeerConnectionWrapper.prototype = {
|
|||
if (!options) {
|
||||
return 0;
|
||||
}
|
||||
if (options.offerToReceiveAudio) {
|
||||
|
||||
var offerToReceiveAudio = options.offerToReceiveAudio;
|
||||
|
||||
// TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223).
|
||||
if (options.mandatory && options.mandatory.OfferToReceiveAudio !== undefined) {
|
||||
offerToReceiveAudio = options.mandatory.OfferToReceiveAudio;
|
||||
} else if (options.optional && options.optional[0] &&
|
||||
options.optional[0].OfferToReceiveAudio !== undefined) {
|
||||
offerToReceiveAudio = options.optional[0].OfferToReceiveAudio;
|
||||
}
|
||||
|
||||
if (offerToReceiveAudio) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -2079,7 +2190,18 @@ PeerConnectionWrapper.prototype = {
|
|||
if (!options) {
|
||||
return 0;
|
||||
}
|
||||
if (options.offerToReceiveVideo) {
|
||||
|
||||
var offerToReceiveVideo = options.offerToReceiveVideo;
|
||||
|
||||
// TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223).
|
||||
if (options.mandatory && options.mandatory.OfferToReceiveVideo !== undefined) {
|
||||
offerToReceiveVideo = options.mandatory.OfferToReceiveVideo;
|
||||
} else if (options.optional && options.optional[0] &&
|
||||
options.optional[0].OfferToReceiveVideo !== undefined) {
|
||||
offerToReceiveVideo = options.optional[0].OfferToReceiveVideo;
|
||||
}
|
||||
|
||||
if (offerToReceiveVideo) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
|
|
|
@ -24,24 +24,27 @@ function dumpSdp(test) {
|
|||
dump("ERROR: SDP answer: " + test._remote_answer.sdp.replace(/[\r]/g, ''));
|
||||
}
|
||||
|
||||
if (typeof test.pcLocal.iceConnectionLog !== 'undefined') {
|
||||
if ((test.pcLocal) && (typeof test.pcLocal.iceConnectionLog !== 'undefined')) {
|
||||
dump("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog + "\n");
|
||||
}
|
||||
if (typeof test.pcRemote.iceConnectionLog !== 'undefined') {
|
||||
if ((test.pcRemote) && (typeof test.pcRemote.iceConnectionLog !== 'undefined')) {
|
||||
dump("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog + "\n");
|
||||
}
|
||||
|
||||
if ((typeof test.pcLocal.setRemoteDescDate !== 'undefined') &&
|
||||
if ((test.pcLocal) && (test.pcRemote) &&
|
||||
(typeof test.pcLocal.setRemoteDescDate !== 'undefined') &&
|
||||
(typeof test.pcRemote.setLocalDescDate !== 'undefined')) {
|
||||
var delta = deltaSeconds(test.pcLocal.setRemoteDescDate, test.pcRemote.setLocalDescDate);
|
||||
dump("Delay between pcLocal.setRemote <-> pcRemote.setLocal: " + delta + "\n");
|
||||
}
|
||||
if ((typeof test.pcLocal.setRemoteDescDate !== 'undefined') &&
|
||||
if ((test.pcLocal) && (test.pcRemote) &&
|
||||
(typeof test.pcLocal.setRemoteDescDate !== 'undefined') &&
|
||||
(typeof test.pcLocal.setRemoteDescStableEventDate !== 'undefined')) {
|
||||
var delta = deltaSeconds(test.pcLocal.setRemoteDescDate, test.pcLocal.setRemoteDescStableEventDate);
|
||||
dump("Delay between pcLocal.setRemote <-> pcLocal.signalingStateStable: " + delta + "\n");
|
||||
}
|
||||
if ((typeof test.pcRemote.setLocalDescDate !== 'undefined') &&
|
||||
if ((test.pcLocal) && (test.pcRemote) &&
|
||||
(typeof test.pcRemote.setLocalDescDate !== 'undefined') &&
|
||||
(typeof test.pcRemote.setLocalDescStableEventDate !== 'undefined')) {
|
||||
var delta = deltaSeconds(test.pcRemote.setLocalDescDate, test.pcRemote.setLocalDescStableEventDate);
|
||||
dump("Delay between pcRemote.setLocal <-> pcRemote.signalingStateStable: " + delta + "\n");
|
||||
|
@ -49,6 +52,22 @@ function dumpSdp(test) {
|
|||
}
|
||||
|
||||
var commandsPeerConnection = [
|
||||
[
|
||||
'PC_SETUP_SIGNALING_CLIENT',
|
||||
function (test) {
|
||||
if (test.steeplechase) {
|
||||
test.setupSignalingClient();
|
||||
test.registerSignalingCallback("ice_candidate", function (message) {
|
||||
var pc = test.pcRemote ? test.pcRemote : test.pcLocal;
|
||||
pc.storeOrAddIceCandidate(new mozRTCIceCandidate(message.ice_candidate));
|
||||
});
|
||||
test.registerSignalingCallback("end_of_trickle_ice", function (message) {
|
||||
test.signalingMessagesFinished();
|
||||
});
|
||||
}
|
||||
test.next();
|
||||
}
|
||||
],
|
||||
[
|
||||
'PC_LOCAL_SETUP_ICE_LOGGER',
|
||||
function (test) {
|
||||
|
@ -145,11 +164,12 @@ var commandsPeerConnection = [
|
|||
test.createOffer(test.pcLocal, function (offer) {
|
||||
is(test.pcLocal.signalingState, STABLE,
|
||||
"Local create offer does not change signaling state");
|
||||
if (!test.pcRemote) {
|
||||
send_message({"offer": test.originalOffer,
|
||||
if (test.steeplechase) {
|
||||
send_message({"type": "offer",
|
||||
"offer": test.originalOffer,
|
||||
"offer_constraints": test.pcLocal.constraints,
|
||||
"offer_options": test.pcLocal.offerOptions});
|
||||
test._local_offer = test.pcLocal._last_offer;
|
||||
test._local_offer = test.originalOffer;
|
||||
test._offer_constraints = test.pcLocal.constraints;
|
||||
test._offer_options = test.pcLocal.offerOptions;
|
||||
}
|
||||
|
@ -170,13 +190,13 @@ var commandsPeerConnection = [
|
|||
[
|
||||
'PC_REMOTE_GET_OFFER',
|
||||
function (test) {
|
||||
if (test.pcLocal) {
|
||||
if (!test.steeplechase) {
|
||||
test._local_offer = test.originalOffer;
|
||||
test._offer_constraints = test.pcLocal.constraints;
|
||||
test._offer_options = test.pcLocal.offerOptions;
|
||||
test.next();
|
||||
} else {
|
||||
wait_for_message().then(function(message) {
|
||||
test.getSignalingMessage("offer", function (message) {
|
||||
ok("offer" in message, "Got an offer message");
|
||||
test._local_offer = new mozRTCSessionDescription(message.offer);
|
||||
test._offer_constraints = message.offer_constraints;
|
||||
|
@ -224,8 +244,9 @@ var commandsPeerConnection = [
|
|||
test.createAnswer(test.pcRemote, function (answer) {
|
||||
is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER,
|
||||
"Remote createAnswer does not change signaling state");
|
||||
if (!test.pcLocal) {
|
||||
send_message({"answer": test.originalAnswer,
|
||||
if (test.steeplechase) {
|
||||
send_message({"type": "answer",
|
||||
"answer": test.originalAnswer,
|
||||
"answer_constraints": test.pcRemote.constraints});
|
||||
test._remote_answer = test.pcRemote._last_answer;
|
||||
test._answer_constraints = test.pcRemote.constraints;
|
||||
|
@ -261,7 +282,7 @@ var commandsPeerConnection = [
|
|||
return resultArray;
|
||||
}
|
||||
|
||||
const offerTriples = _sdpCandidatesIntoArray(test.originalOffer.sdp);
|
||||
const offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp);
|
||||
info("Offer ICE host candidates: " + JSON.stringify(offerTriples));
|
||||
|
||||
const answerTriples = _sdpCandidatesIntoArray(test.originalAnswer.sdp);
|
||||
|
@ -269,7 +290,7 @@ var commandsPeerConnection = [
|
|||
|
||||
for (var i=0; i< offerTriples.length; i++) {
|
||||
if (answerTriples.indexOf(offerTriples[i]) !== -1) {
|
||||
dump("SDP offer: " + test.originalOffer.sdp.replace(/[\r]/g, '') + "\n");
|
||||
dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n");
|
||||
dump("SDP answer: " + test.originalAnswer.sdp.replace(/[\r]/g, '') + "\n");
|
||||
ok(false, "This IP:Port " + offerTriples[i] + " appears in SDP offer and answer!");
|
||||
}
|
||||
|
@ -293,12 +314,12 @@ var commandsPeerConnection = [
|
|||
[
|
||||
'PC_LOCAL_GET_ANSWER',
|
||||
function (test) {
|
||||
if (test.pcRemote) {
|
||||
if (!test.steeplechase) {
|
||||
test._remote_answer = test.originalAnswer;
|
||||
test._answer_constraints = test.pcRemote.constraints;
|
||||
test.next();
|
||||
} else {
|
||||
wait_for_message().then(function(message) {
|
||||
test.getSignalingMessage("answer", function (message) {
|
||||
ok("answer" in message, "Got an answer message");
|
||||
test._remote_answer = new mozRTCSessionDescription(message.answer);
|
||||
test._answer_constraints = message.answer_constraints;
|
||||
|
@ -762,8 +783,9 @@ var commandsDataChannel = [
|
|||
"Local create offer does not change signaling state");
|
||||
ok(offer.sdp.contains("m=application"),
|
||||
"m=application is contained in the SDP");
|
||||
if (!test.pcRemote) {
|
||||
send_message({"offer": test.originalOffer,
|
||||
if (test.steeplechase) {
|
||||
send_message({"type": "offer",
|
||||
"offer": test.originalOffer,
|
||||
"offer_constraints": test.pcLocal.constraints,
|
||||
"offer_options": test.pcLocal.offerOptions});
|
||||
test._local_offer = test.pcLocal._last_offer;
|
||||
|
@ -788,13 +810,13 @@ var commandsDataChannel = [
|
|||
[
|
||||
'PC_REMOTE_GET_OFFER',
|
||||
function (test) {
|
||||
if (test.pcLocal) {
|
||||
if (!test.steeplechase) {
|
||||
test._local_offer = test.originalOffer;
|
||||
test._offer_constraints = test.pcLocal.constraints;
|
||||
test._offer_options = test.pcLocal.offerOptions;
|
||||
test.next();
|
||||
} else {
|
||||
wait_for_message().then(function(message) {
|
||||
test.getSignalingMessage("offer", function (message) {
|
||||
ok("offer" in message, "Got an offer message");
|
||||
test._local_offer = new mozRTCSessionDescription(message.offer);
|
||||
test._offer_constraints = message.offer_constraints;
|
||||
|
@ -845,8 +867,9 @@ var commandsDataChannel = [
|
|||
"Remote createAnswer does not change signaling state");
|
||||
ok(answer.sdp.contains("m=application"),
|
||||
"m=application is contained in the SDP");
|
||||
if (!test.pcLocal) {
|
||||
send_message({"answer": test.originalAnswer,
|
||||
if (test.steeplechase) {
|
||||
send_message({"type":"answer",
|
||||
"answer": test.originalAnswer,
|
||||
"answer_constraints": test.pcRemote.constraints});
|
||||
test._remote_answer = test.pcRemote._last_answer;
|
||||
test._answer_constraints = test.pcRemote.constraints;
|
||||
|
@ -892,12 +915,12 @@ var commandsDataChannel = [
|
|||
[
|
||||
'PC_LOCAL_GET_ANSWER',
|
||||
function (test) {
|
||||
if (test.pcRemote) {
|
||||
if (!test.steeplechase) {
|
||||
test._remote_answer = test.originalAnswer;
|
||||
test._answer_constraints = test.pcRemote.constraints;
|
||||
test.next();
|
||||
} else {
|
||||
wait_for_message().then(function(message) {
|
||||
test.getSignalingMessage("answer", function (message) {
|
||||
ok("answer" in message, "Got an answer message");
|
||||
test._remote_answer = new mozRTCSessionDescription(message.answer);
|
||||
test._answer_constraints = message.answer_constraints;
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
|
||||
runNetworkTest(function() {
|
||||
var test = new PeerConnectionTest();
|
||||
test.setOfferOptions({ offerToReceiveAudio: true });
|
||||
|
||||
// TODO: Stop using old constraint-like RTCOptions soon (Bug 1064223).
|
||||
// Watch out for case-difference when fixing: { offerToReceiveAudio: true }
|
||||
test.setOfferOptions({ mandatory: { OfferToReceiveAudio: true } });
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
runNetworkTest(function() {
|
||||
var test = new PeerConnectionTest();
|
||||
test.setOfferOptions({ offerToReceiveVideo: true });
|
||||
// TODO: Stop using old constraint-like RTCOptions soon (Bug 1064223).
|
||||
// Watch out for case-difference when fixing: { offerToReceiveVideo: true }
|
||||
test.setOfferOptions({ optional: [{ OfferToReceiveAudio: true }] });
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -55,8 +55,19 @@ dictionary RTCDataChannelInit {
|
|||
dictionary RTCOfferOptions {
|
||||
long offerToReceiveVideo;
|
||||
long offerToReceiveAudio;
|
||||
boolean MozDontOfferDataChannel;
|
||||
boolean MozBundleOnly;
|
||||
boolean mozDontOfferDataChannel;
|
||||
boolean mozBundleOnly;
|
||||
|
||||
// TODO: Remove old constraint-like RTCOptions support soon (Bug 1064223).
|
||||
DeprecatedRTCOfferOptionsSet mandatory;
|
||||
sequence<DeprecatedRTCOfferOptionsSet> _optional;
|
||||
};
|
||||
|
||||
dictionary DeprecatedRTCOfferOptionsSet {
|
||||
boolean OfferToReceiveAudio; // Note the uppercase 'O'
|
||||
boolean OfferToReceiveVideo; // Note the uppercase 'O'
|
||||
boolean MozDontOfferDataChannel; // Note the uppercase 'M'
|
||||
boolean MozBundleOnly; // Note the uppercase 'M'
|
||||
};
|
||||
|
||||
interface RTCDataChannel;
|
||||
|
|
|
@ -343,7 +343,9 @@ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& a
|
|||
|
||||
ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
|
||||
{
|
||||
MOZ_ASSERT(!IsControllingDocuments());
|
||||
if (IsControllingDocuments()) {
|
||||
NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
|
|
@ -853,7 +853,8 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
|
|||
mTouchBlockBalance(0),
|
||||
mTreeManager(aTreeManager),
|
||||
mAPZCId(sAsyncPanZoomControllerCount++),
|
||||
mSharedLock(nullptr)
|
||||
mSharedLock(nullptr),
|
||||
mAsyncTransformAppliedToContent(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AsyncPanZoomController);
|
||||
|
||||
|
@ -2434,6 +2435,7 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
|
|||
// fling is happening, it has to keep compositing so that the animation is
|
||||
// smooth. If an animation frame is requested, it is the compositor's
|
||||
// responsibility to schedule a composite.
|
||||
mAsyncTransformAppliedToContent = false;
|
||||
bool requestAnimationFrame = false;
|
||||
Vector<Task*> deferredTasks;
|
||||
|
||||
|
|
|
@ -1110,9 +1110,22 @@ public:
|
|||
mTestAsyncScrollOffset = aPoint;
|
||||
}
|
||||
|
||||
void MarkAsyncTransformAppliedToContent()
|
||||
{
|
||||
mAsyncTransformAppliedToContent = true;
|
||||
}
|
||||
|
||||
bool GetAsyncTransformAppliedToContent() const
|
||||
{
|
||||
return mAsyncTransformAppliedToContent;
|
||||
}
|
||||
|
||||
private:
|
||||
// Extra offset to add in SampleContentTransformForFrame for testing
|
||||
CSSPoint mTestAsyncScrollOffset;
|
||||
// Flag to track whether or not the APZ transform is not used. This
|
||||
// flag is recomputed for every composition frame.
|
||||
bool mAsyncTransformAppliedToContent;
|
||||
};
|
||||
|
||||
class AsyncPanZoomAnimation {
|
||||
|
|
|
@ -138,7 +138,8 @@ GetBaseTransform2D(Layer* aLayer, Matrix* aTransform)
|
|||
|
||||
static void
|
||||
TranslateShadowLayer2D(Layer* aLayer,
|
||||
const gfxPoint& aTranslation)
|
||||
const gfxPoint& aTranslation,
|
||||
bool aAdjustClipRect)
|
||||
{
|
||||
// This layer might also be a scrollable layer and have an async transform.
|
||||
// To make sure we don't clobber that, we start with the shadow transform.
|
||||
|
@ -173,7 +174,7 @@ TranslateShadowLayer2D(Layer* aLayer,
|
|||
layerComposite->SetShadowTransformSetByAnimation(false);
|
||||
|
||||
const nsIntRect* clipRect = aLayer->GetClipRect();
|
||||
if (clipRect) {
|
||||
if (aAdjustClipRect && clipRect) {
|
||||
nsIntRect transformedClipRect(*clipRect);
|
||||
transformedClipRect.MoveBy(aTranslation.x, aTranslation.y);
|
||||
layerComposite->SetShadowClipRect(&transformedClipRect);
|
||||
|
@ -257,7 +258,7 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
|
|||
// aTransformedSubtreeRoot. Also, once we do encounter such a child, we don't
|
||||
// need to recurse any deeper because the fixed layers are relative to their
|
||||
// nearest scrollable layer.
|
||||
if (aLayer == aTransformedSubtreeRoot || !isFixedOrSticky) {
|
||||
if (!isFixedOrSticky) {
|
||||
// ApplyAsyncContentTransformToTree will call this function again for
|
||||
// nested scrollable layers, so we don't need to recurse if the layer is
|
||||
// scrollable.
|
||||
|
@ -349,8 +350,15 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
|
|||
IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
|
||||
}
|
||||
|
||||
// Finally, apply the 2D translation to the layer transform.
|
||||
TranslateShadowLayer2D(aLayer, ThebesPoint(translation));
|
||||
// Finally, apply the 2D translation to the layer transform. Note that in
|
||||
// general we need to apply the same translation to the layer's clip rect, so
|
||||
// that the effective transform on the clip rect takes it back to where it was
|
||||
// originally, had there been no async scroll. In the case where the
|
||||
// fixed/sticky layer is the same as aTransformedSubtreeRoot, then the clip
|
||||
// rect is not affected by the scroll-induced async scroll transform anyway
|
||||
// (since the clip is applied post-transform) so we don't need to make the
|
||||
// adjustment.
|
||||
TranslateShadowLayer2D(aLayer, ThebesPoint(translation), aLayer != aTransformedSubtreeRoot);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -504,16 +512,16 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint)
|
|||
}
|
||||
|
||||
static bool
|
||||
SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aPoint)
|
||||
SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aSampleTime)
|
||||
{
|
||||
bool activeAnimations = false;
|
||||
for (LayerMetricsWrapper child = aLayer.GetFirstChild(); child;
|
||||
child = child.GetNextSibling()) {
|
||||
activeAnimations |= SampleAPZAnimations(child, aPoint);
|
||||
activeAnimations |= SampleAPZAnimations(child, aSampleTime);
|
||||
}
|
||||
|
||||
if (AsyncPanZoomController* apzc = aLayer.GetApzc()) {
|
||||
activeAnimations |= apzc->AdvanceAnimations(aPoint);
|
||||
activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
|
||||
}
|
||||
|
||||
return activeAnimations;
|
||||
|
@ -574,6 +582,10 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer)
|
|||
scrollOffset,
|
||||
&overscrollTransform);
|
||||
|
||||
if (!aLayer->IsScrollInfoLayer()) {
|
||||
controller->MarkAsyncTransformAppliedToContent();
|
||||
}
|
||||
|
||||
const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
|
||||
CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel();
|
||||
CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ?
|
||||
|
|
|
@ -439,7 +439,8 @@ ContainerRender(ContainerT* aContainer,
|
|||
// underlying layer.
|
||||
for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
|
||||
if (AsyncPanZoomController* apzc = i.GetApzc()) {
|
||||
if (!Matrix4x4(apzc->GetCurrentAsyncTransform()).IsIdentity()) {
|
||||
if (!apzc->GetAsyncTransformAppliedToContent()
|
||||
&& !Matrix4x4(apzc->GetCurrentAsyncTransform()).IsIdentity()) {
|
||||
aManager->UnusedApzTransformWarning();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -390,7 +390,6 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
|
|||
ImageResource(aURI), // invoke superclass's constructor
|
||||
mSize(0,0),
|
||||
mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
|
||||
mAnim(nullptr),
|
||||
mLockCount(0),
|
||||
mDecodeCount(0),
|
||||
mRequestedSampleSize(0),
|
||||
|
@ -463,7 +462,6 @@ RasterImage::~RasterImage()
|
|||
}
|
||||
}
|
||||
|
||||
delete mAnim;
|
||||
mAnim = nullptr;
|
||||
|
||||
// Total statistics
|
||||
|
@ -1123,7 +1121,7 @@ RasterImage::EnsureAnimExists()
|
|||
if (!mAnim) {
|
||||
|
||||
// Create the animation context
|
||||
mAnim = new FrameAnimator(mFrameBlender, mAnimationMode);
|
||||
mAnim = MakeUnique<FrameAnimator>(mFrameBlender, mAnimationMode);
|
||||
|
||||
// We don't support discarding animated images (See bug 414259).
|
||||
// Lock the image and throw away the key.
|
||||
|
@ -1646,7 +1644,6 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
|||
StopAnimation();
|
||||
mAnimationFinished = false;
|
||||
if (mAnim) {
|
||||
delete mAnim;
|
||||
mAnim = nullptr;
|
||||
}
|
||||
// If there's only one frame, this could cause flickering
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#ifdef DEBUG
|
||||
#include "imgIContainerDebug.h"
|
||||
#endif
|
||||
|
@ -644,7 +645,7 @@ private: // data
|
|||
// IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
|
||||
// that the frames actually exist (they may have been discarded to save memory, or
|
||||
// we maybe decoding on draw).
|
||||
FrameAnimator* mAnim;
|
||||
UniquePtr<FrameAnimator> mAnim;
|
||||
|
||||
// Discard members
|
||||
uint32_t mLockCount;
|
||||
|
|
|
@ -1127,18 +1127,21 @@ class AsmJSModule
|
|||
// the exported functions have been added.
|
||||
|
||||
bool addExportedFunction(PropertyName *name,
|
||||
uint32_t srcStart,
|
||||
uint32_t srcEnd,
|
||||
uint32_t funcSrcBegin,
|
||||
uint32_t funcSrcEnd,
|
||||
PropertyName *maybeFieldName,
|
||||
ArgCoercionVector &&argCoercions,
|
||||
ReturnType returnType)
|
||||
{
|
||||
// NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
|
||||
// (the entire file) and ExportedFunctions store offsets relative to
|
||||
// the beginning of the module (so that they are caching-invariant).
|
||||
JS_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
|
||||
ExportedFunction func(name, srcStart, srcEnd, maybeFieldName,
|
||||
mozilla::Move(argCoercions), returnType);
|
||||
if (exports_.length() >= UINT32_MAX)
|
||||
return false;
|
||||
return exports_.append(mozilla::Move(func));
|
||||
JS_ASSERT(srcStart_ < funcSrcBegin);
|
||||
JS_ASSERT(funcSrcBegin < funcSrcEnd);
|
||||
ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
|
||||
maybeFieldName, mozilla::Move(argCoercions), returnType);
|
||||
return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func));
|
||||
}
|
||||
unsigned numExportedFunctions() const {
|
||||
JS_ASSERT(isFinishedWithFunctionBodies());
|
||||
|
|
|
@ -1015,13 +1015,8 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
void define(ModuleCompiler &m, ParseNode *fn) {
|
||||
JS_ASSERT(!defined_);
|
||||
defined_ = true;
|
||||
|
||||
// The begin/end char range is relative to the beginning of the module.
|
||||
// hence the assertions.
|
||||
JS_ASSERT(fn->pn_pos.begin > m.srcStart());
|
||||
JS_ASSERT(fn->pn_pos.begin <= fn->pn_pos.end);
|
||||
srcBegin_ = fn->pn_pos.begin - m.srcStart();
|
||||
srcEnd_ = fn->pn_pos.end - m.srcStart();
|
||||
srcBegin_ = fn->pn_pos.begin;
|
||||
srcEnd_ = fn->pn_pos.end;
|
||||
}
|
||||
|
||||
uint32_t srcBegin() const { JS_ASSERT(defined_); return srcBegin_; }
|
||||
|
@ -1462,7 +1457,6 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
Label &syncInterruptLabel() { return syncInterruptLabel_; }
|
||||
bool hasError() const { return errorString_ != nullptr; }
|
||||
const AsmJSModule &module() const { return *module_.get(); }
|
||||
uint32_t srcStart() const { return module_->srcStart(); }
|
||||
bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); }
|
||||
bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); }
|
||||
bool supportsSimd() const { return supportsSimd_; }
|
||||
|
|
|
@ -799,7 +799,7 @@ FuncConvert(JSContext *cx, unsigned argc, Value *vp)
|
|||
Elem *val = TypedObjectMemory<Elem *>(args[0]);
|
||||
RetElem result[Vret::lanes];
|
||||
for (unsigned i = 0; i < Vret::lanes; i++)
|
||||
result[i] = RetElem(val[i]);
|
||||
result[i] = ConvertScalar<RetElem>(val[i]);
|
||||
return StoreResult<Vret>(cx, args, result);
|
||||
}
|
||||
|
||||
|
|
|
@ -1296,6 +1296,12 @@ InitTypeClasses(JSContext* cx, HandleObject parent)
|
|||
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
|
||||
return false;
|
||||
|
||||
// Alias 'ctypes.jschar' as 'ctypes.char16_t' to prevent breaking addons
|
||||
// that are still using jschar (bug 1064935).
|
||||
if (!JS_DefineProperty(cx, parent, "jschar", typeObj_char16_t,
|
||||
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
|
||||
return false;
|
||||
|
||||
// Create objects representing the special types void_t and voidptr_t.
|
||||
RootedObject typeObj(cx,
|
||||
CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
evalcx(" s = 3; s; for(let x = 0; x < 1; ++x) { null }", newGlobal())
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
(function() {
|
||||
for (let x = 0; x < 1; ++x) {
|
||||
if (x % 6 == 4) {} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})()
|
|
@ -0,0 +1,8 @@
|
|||
// |jit-test| error: ReferenceError
|
||||
|
||||
eval("(function() { " + "\
|
||||
var o = {};\
|
||||
o.watch('p', function() { });\
|
||||
for (var i = 0; i < 10; \u5ede ++)\
|
||||
o.p = 123;\
|
||||
" + " })();");
|
|
@ -134,6 +134,9 @@ class BaselineFrame
|
|||
uint8_t *pointer = (uint8_t *)this + Size() + offsetOfCalleeToken();
|
||||
*(CalleeToken *)pointer = token;
|
||||
}
|
||||
bool isConstructing() const {
|
||||
return CalleeTokenIsConstructing(calleeToken());
|
||||
}
|
||||
JSScript *script() const {
|
||||
if (isEvalFrame())
|
||||
return evalScript();
|
||||
|
|
|
@ -778,22 +778,6 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, Val
|
|||
//
|
||||
// UseCount_Fallback
|
||||
//
|
||||
static bool
|
||||
IsTopFrameConstructing(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(cx->currentlyRunningInJit());
|
||||
JitActivationIterator activations(cx->runtime());
|
||||
JitFrameIterator iter(activations);
|
||||
JS_ASSERT(iter.type() == JitFrame_Exit);
|
||||
|
||||
++iter;
|
||||
JS_ASSERT(iter.type() == JitFrame_BaselineStub);
|
||||
|
||||
++iter;
|
||||
JS_ASSERT(iter.isBaselineJS());
|
||||
|
||||
return iter.isConstructing();
|
||||
}
|
||||
|
||||
static bool
|
||||
EnsureCanEnterIon(JSContext *cx, ICUseCount_Fallback *stub, BaselineFrame *frame,
|
||||
|
@ -804,15 +788,14 @@ EnsureCanEnterIon(JSContext *cx, ICUseCount_Fallback *stub, BaselineFrame *frame
|
|||
|
||||
bool isLoopEntry = (JSOp(*pc) == JSOP_LOOPENTRY);
|
||||
|
||||
bool isConstructing = IsTopFrameConstructing(cx);
|
||||
MethodStatus stat;
|
||||
if (isLoopEntry) {
|
||||
JS_ASSERT(LoopEntryCanIonOsr(pc));
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile at loop entry!");
|
||||
stat = CanEnterAtBranch(cx, script, frame, pc, isConstructing);
|
||||
stat = CanEnterAtBranch(cx, script, frame, pc);
|
||||
} else if (frame->isFunctionFrame()) {
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile function from top for later entry!");
|
||||
stat = CompileFunctionForBaseline(cx, script, frame, isConstructing);
|
||||
stat = CompileFunctionForBaseline(cx, script, frame);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2195,8 +2195,7 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
|
|||
// Decide if a transition from interpreter execution to Ion code should occur.
|
||||
// May compile or recompile the target JSScript.
|
||||
MethodStatus
|
||||
jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
|
||||
jsbytecode *pc, bool isConstructing)
|
||||
jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(jit::IsIonEnabled(cx));
|
||||
JS_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
|
||||
|
@ -2230,7 +2229,8 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
|
|||
// - Returns Method_Skipped if pc doesn't match
|
||||
// (This means a background thread compilation with that pc could have started or not.)
|
||||
RootedScript rscript(cx, script);
|
||||
MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution);
|
||||
MethodStatus status = Compile(cx, rscript, osrFrame, pc, osrFrame->isConstructing(),
|
||||
SequentialExecution);
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(cx, script);
|
||||
|
@ -2309,8 +2309,7 @@ jit::CanEnter(JSContext *cx, RunState &state)
|
|||
}
|
||||
|
||||
MethodStatus
|
||||
jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame,
|
||||
bool isConstructing)
|
||||
jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame)
|
||||
{
|
||||
JS_ASSERT(jit::IsIonEnabled(cx));
|
||||
JS_ASSERT(frame->fun()->nonLazyScript()->canIonCompile());
|
||||
|
@ -2326,7 +2325,7 @@ jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFram
|
|||
|
||||
// Attempt compilation. Returns Method_Compiled if already compiled.
|
||||
MethodStatus status =
|
||||
Compile(cx, script, frame, nullptr, isConstructing, SequentialExecution);
|
||||
Compile(cx, script, frame, nullptr, frame->isConstructing(), SequentialExecution);
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(cx, script);
|
||||
|
|
|
@ -84,10 +84,9 @@ void SetIonContext(IonContext *ctx);
|
|||
bool CanIonCompileScript(JSContext *cx, JSScript *script, bool osr);
|
||||
|
||||
MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script,
|
||||
BaselineFrame *frame, jsbytecode *pc, bool isConstructing);
|
||||
BaselineFrame *frame, jsbytecode *pc);
|
||||
MethodStatus CanEnter(JSContext *cx, RunState &state);
|
||||
MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame,
|
||||
bool isConstructing);
|
||||
MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame);
|
||||
MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs);
|
||||
|
||||
MethodStatus CanEnterInParallel(JSContext *cx, HandleScript script);
|
||||
|
|
|
@ -2815,6 +2815,10 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
|||
RootedPropertyName name(cx, cache.name());
|
||||
RootedId id(cx, AtomToId(name));
|
||||
|
||||
RootedTypeObject oldType(cx, obj->getType(cx));
|
||||
if (!oldType)
|
||||
return false;
|
||||
|
||||
// Stop generating new stubs once we hit the stub count limit, see
|
||||
// GetPropertyCache.
|
||||
NativeSetPropCacheability canCache = CanAttachNone;
|
||||
|
@ -2846,12 +2850,6 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure the object de-lazifies its type. We do this here so that
|
||||
// the parallel IC can share code that assumes that native objects all
|
||||
// have a type object.
|
||||
if (obj->isNative() && !obj->getType(cx))
|
||||
return false;
|
||||
|
||||
RootedShape shape(cx);
|
||||
RootedObject holder(cx);
|
||||
bool checkTypeset;
|
||||
|
@ -2873,7 +2871,6 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
|||
|
||||
uint32_t oldSlots = obj->numDynamicSlots();
|
||||
RootedShape oldShape(cx, obj->lastProperty());
|
||||
RootedTypeObject oldType(cx, obj->type());
|
||||
|
||||
// Set/Add the property on the object, the inlined cache are setup for the next execution.
|
||||
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
|
||||
|
|
|
@ -2015,7 +2015,7 @@ InlineFrameIterator::isConstructing() const
|
|||
bool
|
||||
JitFrameIterator::isConstructing() const
|
||||
{
|
||||
return GetCalleeTokenTag(calleeToken()) == CalleeToken_FunctionConstructing;
|
||||
return CalleeTokenIsConstructing(calleeToken());
|
||||
}
|
||||
|
||||
unsigned
|
||||
|
|
|
@ -52,6 +52,11 @@ CalleeTokenIsFunction(CalleeToken token)
|
|||
CalleeTokenTag tag = GetCalleeTokenTag(token);
|
||||
return tag == CalleeToken_Function || tag == CalleeToken_FunctionConstructing;
|
||||
}
|
||||
static inline bool
|
||||
CalleeTokenIsConstructing(CalleeToken token)
|
||||
{
|
||||
return GetCalleeTokenTag(token) == CalleeToken_FunctionConstructing;
|
||||
}
|
||||
static inline JSFunction *
|
||||
CalleeTokenToFunction(CalleeToken token)
|
||||
{
|
||||
|
|
|
@ -59,7 +59,16 @@ LoopUnroller::getReplacementDefinition(MDefinition *def)
|
|||
}
|
||||
|
||||
DefinitionMap::Ptr p = unrolledDefinitions.lookup(def);
|
||||
JS_ASSERT(p);
|
||||
if (!p) {
|
||||
// After phi analysis (TypeAnalyzer::replaceRedundantPhi) the resume
|
||||
// point at the start of a block can contain definitions from within
|
||||
// the block itself.
|
||||
JS_ASSERT(def->isConstant());
|
||||
|
||||
MConstant *constant = MConstant::New(alloc, def->toConstant()->value());
|
||||
oldPreheader->insertBefore(*oldPreheader->begin(), constant);
|
||||
return constant;
|
||||
}
|
||||
|
||||
return p->value();
|
||||
}
|
||||
|
@ -115,7 +124,10 @@ LoopUnroller::go(LoopIterationBound *bound)
|
|||
JitSpew(JitSpew_Unrolling, "Attempting to unroll loop");
|
||||
|
||||
header = bound->header;
|
||||
JS_ASSERT(header->isLoopHeader());
|
||||
|
||||
// UCE might have determined this isn't actually a loop.
|
||||
if (!header->isLoopHeader())
|
||||
return;
|
||||
|
||||
backedge = header->backedge();
|
||||
oldPreheader = header->loopPredecessor();
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "gc/Marking.h"
|
||||
#include "jit/JitCompartment.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/StopIterationObject.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "prmjtime.h"
|
||||
|
||||
#include "builtin/TestingFunctions.h"
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
@ -597,11 +598,7 @@ JS_GetCustomIteratorCount(JSContext *cx)
|
|||
JS_FRIEND_API(bool)
|
||||
JS_IsDeadWrapper(JSObject *obj)
|
||||
{
|
||||
if (!obj->is<ProxyObject>()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj->as<ProxyObject>().handler()->family() == &DeadObjectProxy::family;
|
||||
return IsDeadProxyObject(obj);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -218,6 +218,7 @@
|
|||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/IonCode.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
#include "vm/ProxyObject.h"
|
||||
|
|
|
@ -709,10 +709,9 @@ TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoK
|
|||
{
|
||||
JS_ASSERT(!UseNewTypeForInitializer(script, pc, kind));
|
||||
|
||||
/* :XXX: Limit script->length so we don't need to check the offset up front? */
|
||||
uint32_t offset = script->pcToOffset(pc);
|
||||
|
||||
if (!script->compileAndGo() || offset >= AllocationSiteKey::OFFSET_LIMIT)
|
||||
if (offset >= AllocationSiteKey::OFFSET_LIMIT)
|
||||
return GetTypeNewObject(cx, kind);
|
||||
|
||||
AllocationSiteKey key;
|
||||
|
|
3170
js/src/jsproxy.cpp
3170
js/src/jsproxy.cpp
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -308,68 +308,6 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler
|
|||
virtual JSObject *weakmapKeyDelegate(JSObject *proxy) const MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
/*
|
||||
* Dispatch point for handlers that executes the appropriate C++ or scripted traps.
|
||||
*
|
||||
* Important: All proxy traps need either (a) an AutoEnterPolicy in their
|
||||
* Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug
|
||||
* 945826 comment 0.
|
||||
*/
|
||||
class Proxy
|
||||
{
|
||||
public:
|
||||
/* ES5 Harmony fundamental proxy traps. */
|
||||
static bool preventExtensions(JSContext *cx, HandleObject proxy);
|
||||
static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
static bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
|
||||
static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
|
||||
static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props);
|
||||
|
||||
/* ES5 Harmony derived proxy traps. */
|
||||
static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
|
||||
static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
|
||||
static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
bool strict, MutableHandleValue vp);
|
||||
static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
|
||||
static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
|
||||
static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
|
||||
static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
|
||||
static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
|
||||
static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
|
||||
static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
|
||||
static const char *className(JSContext *cx, HandleObject proxy);
|
||||
static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
|
||||
static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
|
||||
static bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp);
|
||||
static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
|
||||
static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
|
||||
static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
|
||||
|
||||
static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable);
|
||||
static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id);
|
||||
|
||||
static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end,
|
||||
HandleObject result);
|
||||
|
||||
/* IC entry path for handling __noSuchMethod__ on access. */
|
||||
static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
};
|
||||
|
||||
// Use these in places where you don't want to #include vm/ProxyObject.h.
|
||||
extern JS_FRIEND_DATA(const js::Class* const) CallableProxyClassPtr;
|
||||
extern JS_FRIEND_DATA(const js::Class* const) UncallableProxyClassPtr;
|
||||
|
|
|
@ -209,48 +209,6 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
|
|||
typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;
|
||||
typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
|
||||
|
||||
class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
|
||||
{
|
||||
public:
|
||||
explicit MOZ_CONSTEXPR DeadObjectProxy()
|
||||
: BaseProxyHandler(&family)
|
||||
{ }
|
||||
|
||||
/* ES5 Harmony fundamental wrapper traps. */
|
||||
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
||||
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
|
||||
AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
|
||||
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const MOZ_OVERRIDE;
|
||||
virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
|
||||
bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
|
||||
JSContext *cx) const MOZ_OVERRIDE;
|
||||
virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
||||
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
|
||||
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
|
||||
virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
|
||||
MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
|
||||
MutableHandleObject protop) const MOZ_OVERRIDE;
|
||||
|
||||
static const char family;
|
||||
static const DeadObjectProxy singleton;
|
||||
};
|
||||
|
||||
extern JSObject *
|
||||
TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
|
||||
HandleObject parent);
|
||||
|
@ -283,13 +241,6 @@ UnwrapOneChecked(JSObject *obj, bool stopAtOuter = true);
|
|||
JS_FRIEND_API(bool)
|
||||
IsCrossCompartmentWrapper(JSObject *obj);
|
||||
|
||||
bool
|
||||
IsDeadProxyObject(JSObject *obj);
|
||||
|
||||
JSObject *
|
||||
NewDeadProxyObject(JSContext *cx, JSObject *parent,
|
||||
const ProxyOptions &options = ProxyOptions());
|
||||
|
||||
void
|
||||
NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper);
|
||||
|
||||
|
|
|
@ -223,15 +223,22 @@ UNIFIED_SOURCES += [
|
|||
'jsopcode.cpp',
|
||||
'jsprf.cpp',
|
||||
'jspropertytree.cpp',
|
||||
'jsproxy.cpp',
|
||||
'jsreflect.cpp',
|
||||
'jsscript.cpp',
|
||||
'jsstr.cpp',
|
||||
'jswatchpoint.cpp',
|
||||
'jsweakmap.cpp',
|
||||
'jswrapper.cpp',
|
||||
'perf/jsperf.cpp',
|
||||
'prmjtime.cpp',
|
||||
'proxy/BaseProxyHandler.cpp',
|
||||
'proxy/CrossCompartmentWrapper.cpp',
|
||||
'proxy/DeadObjectProxy.cpp',
|
||||
'proxy/DirectProxyHandler.cpp',
|
||||
'proxy/Proxy.cpp',
|
||||
'proxy/ScriptedDirectProxyHandler.cpp',
|
||||
'proxy/ScriptedIndirectProxyHandler.cpp',
|
||||
'proxy/SecurityWrapper.cpp',
|
||||
'proxy/Wrapper.cpp',
|
||||
'vm/ArgumentsObject.cpp',
|
||||
'vm/ArrayBufferObject.cpp',
|
||||
'vm/CallNonGenericMethod.cpp',
|
||||
|
|
|
@ -0,0 +1,343 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "jsproxy.h"
|
||||
|
||||
#include "vm/ProxyObject.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
bool
|
||||
BaseProxyHandler::enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act,
|
||||
bool *bp) const
|
||||
{
|
||||
*bp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, GET);
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!getPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
*bp = !!desc.object();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
// Note: Proxy::set needs to invoke hasOwn to determine where the setter
|
||||
// lives, so we allow SET operations to invoke us.
|
||||
assertEnteredPolicy(cx, proxy, id, GET | SET);
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
*bp = !!desc.object();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, GET);
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!getPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
if (!desc.object()) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
if (!desc.getter() ||
|
||||
(!desc.hasGetterObject() && desc.getter() == JS_PropertyStub))
|
||||
{
|
||||
vp.set(desc.value());
|
||||
return true;
|
||||
}
|
||||
if (desc.hasGetterObject())
|
||||
return InvokeGetterOrSetter(cx, receiver, ObjectValue(*desc.getterObject()),
|
||||
0, nullptr, vp);
|
||||
if (!desc.isShared())
|
||||
vp.set(desc.value());
|
||||
else
|
||||
vp.setUndefined();
|
||||
|
||||
return CallJSPropertyOp(cx, desc.getter(), receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, bool strict, MutableHandleValue vp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, SET);
|
||||
|
||||
// Find an own or inherited property. The code here is strange for maximum
|
||||
// backward compatibility with earlier code written before ES6 and before
|
||||
// SetPropertyIgnoringNamedGetter.
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
bool descIsOwn = desc.object() != nullptr;
|
||||
if (!descIsOwn) {
|
||||
if (!getPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
}
|
||||
|
||||
return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, strict,
|
||||
vp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handler,
|
||||
HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, MutableHandle<PropertyDescriptor> desc,
|
||||
bool descIsOwn, bool strict, MutableHandleValue vp)
|
||||
{
|
||||
/* The control-flow here differs from ::get() because of the fall-through case below. */
|
||||
if (descIsOwn) {
|
||||
JS_ASSERT(desc.object());
|
||||
|
||||
// Check for read-only properties.
|
||||
if (desc.isReadonly())
|
||||
return strict ? Throw(cx, id, JSMSG_READ_ONLY) : true;
|
||||
if (!desc.setter()) {
|
||||
// Be wary of the odd explicit undefined setter case possible through
|
||||
// Object.defineProperty.
|
||||
if (!desc.hasSetterObject())
|
||||
desc.setSetter(JS_StrictPropertyStub);
|
||||
} else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
|
||||
if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
|
||||
return false;
|
||||
if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
|
||||
return true;
|
||||
if (desc.isShared())
|
||||
return true;
|
||||
}
|
||||
if (!desc.getter()) {
|
||||
// Same as above for the null setter case.
|
||||
if (!desc.hasGetterObject())
|
||||
desc.setGetter(JS_PropertyStub);
|
||||
}
|
||||
desc.value().set(vp.get());
|
||||
return handler->defineProperty(cx, receiver, id, desc);
|
||||
}
|
||||
if (desc.object()) {
|
||||
// Check for read-only properties.
|
||||
if (desc.isReadonly())
|
||||
return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
|
||||
if (!desc.setter()) {
|
||||
// Be wary of the odd explicit undefined setter case possible through
|
||||
// Object.defineProperty.
|
||||
if (!desc.hasSetterObject())
|
||||
desc.setSetter(JS_StrictPropertyStub);
|
||||
} else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
|
||||
if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
|
||||
return false;
|
||||
if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
|
||||
return true;
|
||||
if (desc.isShared())
|
||||
return true;
|
||||
}
|
||||
if (!desc.getter()) {
|
||||
// Same as above for the null setter case.
|
||||
if (!desc.hasGetterObject())
|
||||
desc.setGetter(JS_PropertyStub);
|
||||
}
|
||||
desc.value().set(vp.get());
|
||||
return handler->defineProperty(cx, receiver, id, desc);
|
||||
}
|
||||
|
||||
desc.object().set(receiver);
|
||||
desc.value().set(vp.get());
|
||||
desc.setAttributes(JSPROP_ENUMERATE);
|
||||
desc.setGetter(nullptr);
|
||||
desc.setSetter(nullptr); // Pick up the class getter/setter.
|
||||
return handler->defineProperty(cx, receiver, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
|
||||
JS_ASSERT(props.length() == 0);
|
||||
|
||||
if (!getOwnPropertyNames(cx, proxy, props))
|
||||
return false;
|
||||
|
||||
/* Select only the enumerable properties through in-place iteration. */
|
||||
RootedId id(cx);
|
||||
size_t i = 0;
|
||||
for (size_t j = 0, len = props.length(); j < len; j++) {
|
||||
JS_ASSERT(i <= j);
|
||||
id = props[j];
|
||||
if (JSID_IS_SYMBOL(id))
|
||||
continue;
|
||||
|
||||
AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
if (desc.object() && desc.isEnumerable())
|
||||
props[i++].set(id);
|
||||
}
|
||||
|
||||
JS_ASSERT(i <= props.length());
|
||||
props.resize(i);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
|
||||
|
||||
AutoIdVector props(cx);
|
||||
if ((flags & JSITER_OWNONLY)
|
||||
? !keys(cx, proxy, props)
|
||||
: !enumerate(cx, proxy, props)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const
|
||||
{
|
||||
MOZ_CRASH("callable proxies should implement call trap");
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const
|
||||
{
|
||||
MOZ_CRASH("callable proxies should implement construct trap");
|
||||
}
|
||||
|
||||
const char *
|
||||
BaseProxyHandler::className(JSContext *cx, HandleObject proxy) const
|
||||
{
|
||||
return proxy->isCallable() ? "Function" : "Object";
|
||||
}
|
||||
|
||||
JSString *
|
||||
BaseProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const
|
||||
{
|
||||
if (proxy->isCallable())
|
||||
return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}");
|
||||
RootedValue v(cx, ObjectValue(*proxy));
|
||||
ReportIsNotFunction(cx, v);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy,
|
||||
RegExpGuard *g) const
|
||||
{
|
||||
MOZ_CRASH("This should have been a wrapped regexp");
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const
|
||||
{
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
return DefaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const
|
||||
{
|
||||
ReportIncompatible(cx, args);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
|
||||
bool *bp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
||||
RootedValue val(cx, ObjectValue(*proxy.get()));
|
||||
js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
|
||||
JSDVG_SEARCH_STACK, val, js::NullPtr());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy) const
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
BaseProxyHandler::objectMoved(JSObject *proxy, const JSObject *old) const
|
||||
{
|
||||
}
|
||||
|
||||
JSObject *
|
||||
BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const
|
||||
{
|
||||
MOZ_CRASH("Must override getPrototypeOf with lazy prototype.");
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::setPrototypeOf(JSContext *cx, HandleObject, HandleObject, bool *) const
|
||||
{
|
||||
// Disallow sets of protos on proxies with lazy protos, but no hook.
|
||||
// This keeps us away from the footgun of having the first proto set opt
|
||||
// you out of having dynamic protos altogether.
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
|
||||
"incompatible Proxy");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
|
||||
proxy->getClass()->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::unwatch(JSContext *cx, HandleObject proxy, HandleId id) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
|
||||
HandleObject result) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
||||
|
||||
return js::SliceSlowly(cx, proxy, proxy, begin, end, result);
|
||||
}
|
|
@ -4,180 +4,16 @@
|
|||
* 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 "jsiter.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsexn.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsiter.h"
|
||||
|
||||
#include "vm/ErrorObject.h"
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "jscompartmentinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
/*
|
||||
* Wrapper forwards this call directly to the wrapped object for efficiency
|
||||
* and transparency. In particular, the hint is needed to properly stringify
|
||||
* Date objects in certain cases - see bug 646129. Note also the
|
||||
* SecurityWrapper overrides this trap to avoid information leaks. See bug
|
||||
* 720619.
|
||||
*/
|
||||
bool
|
||||
Wrapper::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp) const
|
||||
{
|
||||
vp.set(ObjectValue(*proxy->as<ProxyObject>().target()));
|
||||
if (hint == JSTYPE_VOID)
|
||||
return ToPrimitive(cx, vp);
|
||||
return ToPrimitive(cx, hint, vp);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, const Wrapper *handler,
|
||||
const WrapperOptions *options)
|
||||
{
|
||||
JS_ASSERT(parent);
|
||||
|
||||
RootedValue priv(cx, ObjectValue(*obj));
|
||||
mozilla::Maybe<WrapperOptions> opts;
|
||||
if (!options) {
|
||||
opts.emplace();
|
||||
opts->selectDefaultClass(obj->isCallable());
|
||||
options = opts.ptr();
|
||||
}
|
||||
return NewProxyObject(cx, handler, priv, options->proto(), parent, *options);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, const Wrapper *handler)
|
||||
{
|
||||
JS_ASSERT(!obj->isCallable());
|
||||
existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
|
||||
return existing;
|
||||
}
|
||||
|
||||
const Wrapper *
|
||||
Wrapper::wrapperHandler(JSObject *wrapper)
|
||||
{
|
||||
JS_ASSERT(wrapper->is<WrapperObject>());
|
||||
return static_cast<const Wrapper*>(wrapper->as<ProxyObject>().handler());
|
||||
}
|
||||
|
||||
JSObject *
|
||||
Wrapper::wrappedObject(JSObject *wrapper)
|
||||
{
|
||||
JS_ASSERT(wrapper->is<WrapperObject>());
|
||||
return wrapper->as<ProxyObject>().target();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::UncheckedUnwrap(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp)
|
||||
{
|
||||
unsigned flags = 0;
|
||||
while (true) {
|
||||
if (!wrapped->is<WrapperObject>() ||
|
||||
MOZ_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject))
|
||||
{
|
||||
break;
|
||||
}
|
||||
flags |= Wrapper::wrapperHandler(wrapped)->flags();
|
||||
wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
|
||||
|
||||
// This can be called from DirectProxyHandler::weakmapKeyDelegate() on a
|
||||
// wrapper whose referent has been moved while it is still unmarked.
|
||||
if (wrapped)
|
||||
wrapped = MaybeForwarded(wrapped);
|
||||
}
|
||||
if (flagsp)
|
||||
*flagsp = flags;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::CheckedUnwrap(JSObject *obj, bool stopAtOuter)
|
||||
{
|
||||
while (true) {
|
||||
JSObject *wrapper = obj;
|
||||
obj = UnwrapOneChecked(obj, stopAtOuter);
|
||||
if (!obj || obj == wrapper)
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::UnwrapOneChecked(JSObject *obj, bool stopAtOuter)
|
||||
{
|
||||
if (!obj->is<WrapperObject>() ||
|
||||
MOZ_UNLIKELY(!!obj->getClass()->ext.innerObject && stopAtOuter))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
const Wrapper *handler = Wrapper::wrapperHandler(obj);
|
||||
return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
|
||||
}
|
||||
|
||||
bool
|
||||
js::IsCrossCompartmentWrapper(JSObject *obj)
|
||||
{
|
||||
return IsWrapper(obj) &&
|
||||
!!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
|
||||
}
|
||||
|
||||
const char Wrapper::family = 0;
|
||||
const Wrapper Wrapper::singleton((unsigned)0);
|
||||
const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
|
||||
JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
|
||||
|
||||
/* Compartments. */
|
||||
|
||||
extern JSObject *
|
||||
js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
|
||||
HandleObject parent)
|
||||
{
|
||||
// Allow wrapping outer window proxies.
|
||||
JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
|
||||
return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton);
|
||||
}
|
||||
|
||||
ErrorCopier::~ErrorCopier()
|
||||
{
|
||||
JSContext *cx = ac->context()->asJSContext();
|
||||
if (ac->origin() != cx->compartment() && cx->isExceptionPending()) {
|
||||
RootedValue exc(cx);
|
||||
if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
|
||||
cx->clearPendingException();
|
||||
ac.reset();
|
||||
Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
|
||||
JSObject *copyobj = js_CopyErrorObject(cx, errObj);
|
||||
if (copyobj)
|
||||
cx->setPendingException(ObjectValue(*copyobj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cross compartment wrappers. */
|
||||
|
||||
bool Wrapper::finalizeInBackground(Value priv) const
|
||||
{
|
||||
if (!priv.isObject())
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Make the 'background-finalized-ness' of the wrapper the same as the
|
||||
* wrapped object, to allow transplanting between them.
|
||||
*
|
||||
* If the wrapped object is in the nursery then we know it doesn't have a
|
||||
* finalizer, and so background finalization is ok.
|
||||
*/
|
||||
if (IsInsideNursery(&priv.toObject()))
|
||||
return true;
|
||||
return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind());
|
||||
}
|
||||
|
||||
#define PIERCE(cx, wrapper, pre, op, post) \
|
||||
JS_BEGIN_MACRO \
|
||||
|
@ -596,271 +432,11 @@ CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper,
|
|||
|
||||
const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
|
||||
|
||||
/* Security wrappers. */
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const
|
||||
js::IsCrossCompartmentWrapper(JSObject *obj)
|
||||
{
|
||||
// Just like BaseProxyHandler, SecurityWrappers claim by default to always
|
||||
// be extensible, so as not to leak information about the state of the
|
||||
// underlying wrapped thing.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::preventExtensions(JSContext *cx, HandleObject wrapper) const
|
||||
{
|
||||
// See above.
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::enter(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
Wrapper::Action act, bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
*bp = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::setPrototypeOf(JSContext *cx, HandleObject wrapper,
|
||||
HandleObject proto, bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For security wrappers, we run the DefaultValue algorithm on the wrapper
|
||||
// itself, which means that the existing security policy on operations like
|
||||
// toString() will take effect and do the right thing here.
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::defaultValue(JSContext *cx, HandleObject wrapper,
|
||||
JSType hint, MutableHandleValue vp) const
|
||||
{
|
||||
return DefaultValue(cx, wrapper, hint, vp);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::regexp_toShared(JSContext *cx, HandleObject obj, RegExpGuard *g) const
|
||||
{
|
||||
return Base::regexp_toShared(cx, obj, g);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::boxedValue_unbox(JSContext *cx, HandleObject obj, MutableHandleValue vp) const
|
||||
{
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::defineProperty(JSContext *cx, HandleObject wrapper,
|
||||
HandleId id, MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
if (desc.getter() || desc.setter()) {
|
||||
JSString *str = IdToString(cx, id);
|
||||
AutoStableStringChars chars(cx);
|
||||
const char16_t *prop = nullptr;
|
||||
if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
|
||||
prop = chars.twoByteChars();
|
||||
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_ACCESSOR_DEF_DENIED, prop);
|
||||
return false;
|
||||
}
|
||||
|
||||
return Base::defineProperty(cx, wrapper, id, desc);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::watch(JSContext *cx, HandleObject proxy,
|
||||
HandleId id, HandleObject callable) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::unwatch(JSContext *cx, HandleObject proxy,
|
||||
HandleId id) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template class js::SecurityWrapper<Wrapper>;
|
||||
template class js::SecurityWrapper<CrossCompartmentWrapper>;
|
||||
|
||||
bool
|
||||
DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
|
||||
{
|
||||
// This is kind of meaningless, but dead-object semantics aside,
|
||||
// [[Extensible]] always being true is consistent with other proxy types.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::preventExtensions(JSContext *cx, HandleObject proxy) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
|
||||
AutoIdVector &props) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
|
||||
bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *
|
||||
DeadObjectProxy::className(JSContext *cx, HandleObject wrapper) const
|
||||
{
|
||||
return "DeadObject";
|
||||
}
|
||||
|
||||
JSString *
|
||||
DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::defaultValue(JSContext *cx, HandleObject obj, JSType hint,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const
|
||||
{
|
||||
protop.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char DeadObjectProxy::family = 0;
|
||||
const DeadObjectProxy DeadObjectProxy::singleton;
|
||||
|
||||
bool
|
||||
js::IsDeadProxyObject(JSObject *obj)
|
||||
{
|
||||
return obj->is<ProxyObject>() &&
|
||||
obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
|
||||
return IsWrapper(obj) &&
|
||||
!!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
|
||||
}
|
||||
|
||||
void
|
|
@ -0,0 +1,159 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfun.h" // XXXefaust Bug 1064662
|
||||
|
||||
#include "vm/ProxyObject.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
bool
|
||||
DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
|
||||
{
|
||||
// This is kind of meaningless, but dead-object semantics aside,
|
||||
// [[Extensible]] always being true is consistent with other proxy types.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::preventExtensions(JSContext *cx, HandleObject proxy) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
|
||||
AutoIdVector &props) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
|
||||
bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *
|
||||
DeadObjectProxy::className(JSContext *cx, HandleObject wrapper) const
|
||||
{
|
||||
return "DeadObject";
|
||||
}
|
||||
|
||||
JSString *
|
||||
DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::defaultValue(JSContext *cx, HandleObject obj, JSType hint,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const
|
||||
{
|
||||
protop.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char DeadObjectProxy::family = 0;
|
||||
const DeadObjectProxy DeadObjectProxy::singleton;
|
||||
|
||||
|
||||
bool
|
||||
js::IsDeadProxyObject(JSObject *obj)
|
||||
{
|
||||
return obj->is<ProxyObject>() &&
|
||||
obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- 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 proxy_DeadObjectProxy_h
|
||||
#define proxy_DeadObjectProxy_h
|
||||
|
||||
#include "jsproxy.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class DeadObjectProxy : public BaseProxyHandler
|
||||
{
|
||||
public:
|
||||
explicit MOZ_CONSTEXPR DeadObjectProxy()
|
||||
: BaseProxyHandler(&family)
|
||||
{ }
|
||||
|
||||
/* ES5 Harmony fundamental wrapper traps. */
|
||||
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
||||
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
|
||||
AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
|
||||
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const MOZ_OVERRIDE;
|
||||
virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
|
||||
bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
|
||||
JSContext *cx) const MOZ_OVERRIDE;
|
||||
virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
||||
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
|
||||
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
|
||||
virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
|
||||
MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
|
||||
MutableHandleObject protop) const MOZ_OVERRIDE;
|
||||
|
||||
static const char family;
|
||||
static const DeadObjectProxy singleton;
|
||||
};
|
||||
|
||||
bool
|
||||
IsDeadProxyObject(JSObject *obj);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* proxy_DeadObjectProxy_h */
|
|
@ -0,0 +1,250 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "jsproxy.h"
|
||||
#include "jswrapper.h" // UncheckedUnwrap
|
||||
|
||||
#include "vm/ProxyObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
bool
|
||||
DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
|
||||
JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JS_GetPropertyDescriptorById(cx, target, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return js::GetOwnPropertyDescriptor(cx, target, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, SET);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
RootedValue v(cx, desc.value());
|
||||
return CheckDefineProperty(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter()) &&
|
||||
JS_DefinePropertyById(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter());
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
|
||||
AutoIdVector &props) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, SET);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::deleteGeneric(cx, target, id, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy,
|
||||
AutoIdVector &props) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
|
||||
JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return GetPropertyNames(cx, target, 0, &props);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
|
||||
RootedValue target(cx, proxy->as<ProxyObject>().private_());
|
||||
return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
|
||||
RootedValue target(cx, proxy->as<ProxyObject>().private_());
|
||||
return InvokeConstructor(cx, target, args.length(), args.array(), args.rval().address());
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const
|
||||
{
|
||||
args.setThis(ObjectValue(*args.thisv().toObject().as<ProxyObject>().target()));
|
||||
if (!test(args.thisv())) {
|
||||
ReportIncompatible(cx, args);
|
||||
return false;
|
||||
}
|
||||
|
||||
return CallNativeImpl(cx, impl, args);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
|
||||
bool *bp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
||||
bool b;
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
if (!HasInstance(cx, target, v, &b))
|
||||
return false;
|
||||
*bp = !!b;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::getProto(cx, target, protop);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::setProto(cx, target, proto, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
|
||||
JSContext *cx) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return ObjectClassIs(target, classValue, cx);
|
||||
}
|
||||
|
||||
const char *
|
||||
DirectProxyHandler::className(JSContext *cx, HandleObject proxy) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::className(cx, target);
|
||||
}
|
||||
|
||||
JSString *
|
||||
DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy,
|
||||
unsigned indent) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return fun_toStringHelper(cx, target, indent);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy,
|
||||
RegExpGuard *g) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return RegExpToShared(cx, target, g);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return Unbox(cx, target, vp);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
DirectProxyHandler::weakmapKeyDelegate(JSObject *proxy) const
|
||||
{
|
||||
return UncheckedUnwrap(proxy);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, GET);
|
||||
JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
|
||||
bool found;
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
if (!JS_HasPropertyById(cx, target, id, &found))
|
||||
return false;
|
||||
*bp = !!found;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
// Note: Proxy::set needs to invoke hasOwn to determine where the setter
|
||||
// lives, so we allow SET operations to invoke us.
|
||||
assertEnteredPolicy(cx, proxy, id, GET | SET);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!JS_GetPropertyDescriptorById(cx, target, id, &desc))
|
||||
return false;
|
||||
*bp = (desc.object() == target);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, GET);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::getGeneric(cx, target, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, bool strict, MutableHandleValue vp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, id, SET);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::setGeneric(cx, target, receiver, id, vp, strict);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return GetPropertyNames(cx, target, JSITER_OWNONLY, &props);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
|
||||
JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return GetIterator(cx, target, flags, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::isExtensible(cx, target, extensible);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return JSObject::preventExtensions(cx, target);
|
||||
}
|
|
@ -0,0 +1,903 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
#include "proxy/ScriptedDirectProxyHandler.h"
|
||||
#include "proxy/ScriptedIndirectProxyHandler.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsinferinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/ObjectImpl-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
void
|
||||
js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext *cx, jsid id)
|
||||
{
|
||||
if (JS_IsExceptionPending(cx))
|
||||
return;
|
||||
|
||||
if (JSID_IS_VOID(id)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_OBJECT_ACCESS_DENIED);
|
||||
} else {
|
||||
JSString *str = IdToString(cx, id);
|
||||
AutoStableStringChars chars(cx);
|
||||
const char16_t *prop = nullptr;
|
||||
if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
|
||||
prop = chars.twoByteChars();
|
||||
|
||||
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
|
||||
prop);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
js::AutoEnterPolicy::recordEnter(JSContext *cx, HandleObject proxy, HandleId id, Action act)
|
||||
{
|
||||
if (allowed()) {
|
||||
context = cx;
|
||||
enteredProxy.emplace(proxy);
|
||||
enteredId.emplace(id);
|
||||
enteredAction = act;
|
||||
prev = cx->runtime()->enteredPolicy;
|
||||
cx->runtime()->enteredPolicy = this;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
js::AutoEnterPolicy::recordLeave()
|
||||
{
|
||||
if (enteredProxy) {
|
||||
JS_ASSERT(context->runtime()->enteredPolicy == this);
|
||||
context->runtime()->enteredPolicy = prev;
|
||||
}
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id,
|
||||
BaseProxyHandler::Action act)
|
||||
{
|
||||
MOZ_ASSERT(proxy->is<ProxyObject>());
|
||||
MOZ_ASSERT(cx->runtime()->enteredPolicy);
|
||||
MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredProxy->get() == proxy);
|
||||
MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredId->get() == id);
|
||||
MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredAction & act);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \
|
||||
JS_BEGIN_MACRO \
|
||||
RootedObject proto(cx); \
|
||||
if (!JSObject::getProto(cx, proxy, &proto)) \
|
||||
return false; \
|
||||
if (!proto) \
|
||||
return true; \
|
||||
assertSameCompartment(cx, proxy, proto); \
|
||||
return protoCall; \
|
||||
JS_END_MACRO \
|
||||
|
||||
bool
|
||||
Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
desc.object().set(nullptr); // default result if we refuse to perform this action
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
if (!handler->hasPrototype())
|
||||
return handler->getPropertyDescriptor(cx, proxy, id, desc);
|
||||
if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc))
|
||||
return false;
|
||||
if (desc.object())
|
||||
return true;
|
||||
INVOKE_ON_PROTOTYPE(cx, handler, proxy, JS_GetPropertyDescriptorById(cx, proto, id, desc));
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
return NewPropertyDescriptorObject(cx, desc, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
desc.object().set(nullptr); // default result if we refuse to perform this action
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!Proxy::getOwnPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
return NewPropertyDescriptorObject(cx, desc, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
return proxy->as<ProxyObject>().handler()->getOwnPropertyNames(cx, proxy, props);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
*bp = true; // default result if we refuse to perform this action
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, bp);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::AppendUnique(JSContext *cx, AutoIdVector &base, AutoIdVector &others)
|
||||
{
|
||||
AutoIdVector uniqueOthers(cx);
|
||||
if (!uniqueOthers.reserve(others.length()))
|
||||
return false;
|
||||
for (size_t i = 0; i < others.length(); ++i) {
|
||||
bool unique = true;
|
||||
for (size_t j = 0; j < base.length(); ++j) {
|
||||
if (others[i].get() == base[j]) {
|
||||
unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unique)
|
||||
uniqueOthers.append(others[i]);
|
||||
}
|
||||
return base.appendAll(uniqueOthers);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
if (!handler->hasPrototype())
|
||||
return proxy->as<ProxyObject>().handler()->enumerate(cx, proxy, props);
|
||||
if (!handler->keys(cx, proxy, props))
|
||||
return false;
|
||||
AutoIdVector protoProps(cx);
|
||||
INVOKE_ON_PROTOTYPE(cx, handler, proxy,
|
||||
GetPropertyNames(cx, proto, 0, &protoProps) &&
|
||||
AppendUnique(cx, props, protoProps));
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
*bp = false; // default result if we refuse to perform this action
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
if (!handler->hasPrototype())
|
||||
return handler->has(cx, proxy, id, bp);
|
||||
if (!handler->hasOwn(cx, proxy, id, bp))
|
||||
return false;
|
||||
if (*bp)
|
||||
return true;
|
||||
bool Bp;
|
||||
INVOKE_ON_PROTOTYPE(cx, handler, proxy,
|
||||
JS_HasPropertyById(cx, proto, id, &Bp) &&
|
||||
((*bp = Bp) || true));
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
*bp = false; // default result if we refuse to perform this action
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
return handler->hasOwn(cx, proxy, id, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
vp.setUndefined(); // default result if we refuse to perform this action
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
bool own;
|
||||
if (!handler->hasPrototype()) {
|
||||
own = true;
|
||||
} else {
|
||||
if (!handler->hasOwn(cx, proxy, id, &own))
|
||||
return false;
|
||||
}
|
||||
if (own)
|
||||
return handler->get(cx, proxy, receiver, id, vp);
|
||||
INVOKE_ON_PROTOTYPE(cx, handler, proxy, JSObject::getGeneric(cx, proto, receiver, id, vp));
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::callProp(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
// The inline caches need an access point for JSOP_CALLPROP sites that accounts
|
||||
// for the possibility of __noSuchMethod__
|
||||
if (!Proxy::get(cx, proxy, receiver, id, vp))
|
||||
return false;
|
||||
|
||||
#if JS_HAS_NO_SUCH_METHOD
|
||||
if (MOZ_UNLIKELY(vp.isPrimitive())) {
|
||||
if (!OnUnknownMethod(cx, proxy, IdToValue(id), vp))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
|
||||
// If the proxy doesn't require that we consult its prototype for the
|
||||
// non-own cases, we can sink to the |set| trap.
|
||||
if (!handler->hasPrototype())
|
||||
return handler->set(cx, proxy, receiver, id, strict, vp);
|
||||
|
||||
// If we have an existing (own or non-own) property with a setter, we want
|
||||
// to invoke that.
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
|
||||
return false;
|
||||
if (desc.object() && desc.setter() && desc.setter() != JS_StrictPropertyStub)
|
||||
return CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp);
|
||||
|
||||
// Ok. Either there was no pre-existing property, or it was a value prop
|
||||
// that we're going to shadow. Make a property descriptor and define it.
|
||||
//
|
||||
// Note that for pre-existing own value properties, we inherit the existing
|
||||
// attributes, since we're really just changing the value and not trying to
|
||||
// reconfigure the property.
|
||||
Rooted<PropertyDescriptor> newDesc(cx);
|
||||
if (desc.object() == proxy)
|
||||
newDesc.setAttributes(desc.attributes());
|
||||
else
|
||||
newDesc.setAttributes(JSPROP_ENUMERATE);
|
||||
newDesc.value().set(vp);
|
||||
return handler->defineProperty(cx, receiver, id, &newDesc);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
return handler->keys(cx, proxy, props);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
vp.setUndefined(); // default result if we refuse to perform this action
|
||||
if (!handler->hasPrototype()) {
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
|
||||
BaseProxyHandler::ENUMERATE, true);
|
||||
// If the policy denies access but wants us to return true, we need
|
||||
// to hand a valid (empty) iterator object to the caller.
|
||||
if (!policy.allowed()) {
|
||||
AutoIdVector props(cx);
|
||||
return policy.returnValue() &&
|
||||
EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
|
||||
}
|
||||
return handler->iterate(cx, proxy, flags, vp);
|
||||
}
|
||||
AutoIdVector props(cx);
|
||||
// The other Proxy::foo methods do the prototype-aware work for us here.
|
||||
if ((flags & JSITER_OWNONLY)
|
||||
? !Proxy::keys(cx, proxy, props)
|
||||
: !Proxy::enumerate(cx, proxy, props)) {
|
||||
return false;
|
||||
}
|
||||
return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy, extensible);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::preventExtensions(JSContext *cx, HandleObject proxy)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
return handler->preventExtensions(cx, proxy);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
|
||||
// Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
|
||||
// can only set our default value once we're sure that we're not calling the
|
||||
// trap.
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
|
||||
BaseProxyHandler::CALL, true);
|
||||
if (!policy.allowed()) {
|
||||
args.rval().setUndefined();
|
||||
return policy.returnValue();
|
||||
}
|
||||
|
||||
return handler->call(cx, proxy, args);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
|
||||
// Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
|
||||
// can only set our default value once we're sure that we're not calling the
|
||||
// trap.
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
|
||||
BaseProxyHandler::CALL, true);
|
||||
if (!policy.allowed()) {
|
||||
args.rval().setUndefined();
|
||||
return policy.returnValue();
|
||||
}
|
||||
|
||||
return handler->construct(cx, proxy, args);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
RootedObject proxy(cx, &args.thisv().toObject());
|
||||
// Note - we don't enter a policy here because our security architecture
|
||||
// guards against nativeCall by overriding the trap itself in the right
|
||||
// circumstances.
|
||||
return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
*bp = false; // default result if we refuse to perform this action
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
|
||||
if (!policy.allowed())
|
||||
return policy.returnValue();
|
||||
return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->objectClassIs(proxy, classValue, cx);
|
||||
}
|
||||
|
||||
const char *
|
||||
Proxy::className(JSContext *cx, HandleObject proxy)
|
||||
{
|
||||
// Check for unbounded recursion, but don't signal an error; className
|
||||
// needs to be infallible.
|
||||
int stackDummy;
|
||||
if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
|
||||
return "too much recursion";
|
||||
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
|
||||
BaseProxyHandler::GET, /* mayThrow = */ false);
|
||||
// Do the safe thing if the policy rejects.
|
||||
if (!policy.allowed()) {
|
||||
return handler->BaseProxyHandler::className(cx, proxy);
|
||||
}
|
||||
return handler->className(cx, proxy);
|
||||
}
|
||||
|
||||
JSString *
|
||||
Proxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return nullptr);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
|
||||
BaseProxyHandler::GET, /* mayThrow = */ false);
|
||||
// Do the safe thing if the policy rejects.
|
||||
if (!policy.allowed())
|
||||
return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
|
||||
return handler->fun_toString(cx, proxy, indent);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->defaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject *>(0x1);
|
||||
|
||||
bool
|
||||
Proxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject proto)
|
||||
{
|
||||
JS_ASSERT(proxy->getTaggedProto().isLazy());
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->getPrototypeOf(cx, proxy, proto);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
|
||||
{
|
||||
JS_ASSERT(proxy->getTaggedProto().isLazy());
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->setPrototypeOf(cx, proxy, proto, bp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Proxy::watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Proxy::unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Proxy::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
|
||||
HandleObject result)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
|
||||
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
|
||||
/* mayThrow = */ true);
|
||||
if (!policy.allowed()) {
|
||||
if (policy.returnValue()) {
|
||||
JS_ASSERT(!cx->isExceptionPending());
|
||||
return js::SliceSlowly(cx, proxy, proxy, begin, end, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return handler->slice(cx, proxy, begin, end, result);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js::proxy_innerObject(JSObject *obj)
|
||||
{
|
||||
return obj->as<ProxyObject>().private_().toObjectOrNull();
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleObject objp, MutableHandleShape propp)
|
||||
{
|
||||
bool found;
|
||||
if (!Proxy::has(cx, obj, id, &found))
|
||||
return false;
|
||||
|
||||
if (found) {
|
||||
MarkNonNativePropertyFound(propp);
|
||||
objp.set(obj);
|
||||
} else {
|
||||
objp.set(nullptr);
|
||||
propp.set(nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleObject objp, MutableHandleShape propp)
|
||||
{
|
||||
RootedId id(cx, NameToId(name));
|
||||
return proxy_LookupGeneric(cx, obj, id, objp, propp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
MutableHandleObject objp, MutableHandleShape propp)
|
||||
{
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
return proxy_LookupGeneric(cx, obj, id, objp, propp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
|
||||
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
desc.object().set(obj);
|
||||
desc.value().set(value);
|
||||
desc.setAttributes(attrs);
|
||||
desc.setGetter(getter);
|
||||
desc.setSetter(setter);
|
||||
return Proxy::defineProperty(cx, obj, id, &desc);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_DefineProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
|
||||
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
Rooted<jsid> id(cx, NameToId(name));
|
||||
return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
|
||||
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
return Proxy::get(cx, obj, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
Rooted<jsid> id(cx, NameToId(name));
|
||||
return proxy_GetGeneric(cx, obj, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
return proxy_GetGeneric(cx, obj, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_SetGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleValue vp, bool strict)
|
||||
{
|
||||
return Proxy::set(cx, obj, obj, id, strict, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleValue vp, bool strict)
|
||||
{
|
||||
Rooted<jsid> id(cx, NameToId(name));
|
||||
return proxy_SetGeneric(cx, obj, id, vp, strict);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_SetElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
MutableHandleValue vp, bool strict)
|
||||
{
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
return proxy_SetGeneric(cx, obj, id, vp, strict);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
|
||||
{
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
|
||||
return false;
|
||||
*attrsp = desc.attributes();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
|
||||
{
|
||||
/* Lookup the current property descriptor so we have setter/getter/value. */
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
|
||||
return false;
|
||||
desc.setAttributes(*attrsp);
|
||||
return Proxy::defineProperty(cx, obj, id, &desc);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
|
||||
{
|
||||
bool deleted;
|
||||
if (!Proxy::delete_(cx, obj, id, &deleted))
|
||||
return false;
|
||||
*succeeded = deleted;
|
||||
return js_SuppressDeletedProperty(cx, obj, id);
|
||||
}
|
||||
|
||||
void
|
||||
js::proxy_Trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->is<ProxyObject>());
|
||||
ProxyObject::trace(trc, obj);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ProxyObject::trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
ProxyObject *proxy = &obj->as<ProxyObject>();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (trc->runtime()->gc.isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
|
||||
JSObject *referent = MaybeForwarded(&proxy->private_().toObject());
|
||||
if (referent->compartment() != proxy->compartment()) {
|
||||
/*
|
||||
* Assert that this proxy is tracked in the wrapper map. We maintain
|
||||
* the invariant that the wrapped object is the key in the wrapper map.
|
||||
*/
|
||||
Value key = ObjectValue(*referent);
|
||||
WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
|
||||
JS_ASSERT(p);
|
||||
JS_ASSERT(*p->value().unsafeGet() == ObjectValue(*proxy));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Note: If you add new slots here, make sure to change
|
||||
// nuke() to cope.
|
||||
MarkCrossCompartmentSlot(trc, obj, proxy->slotOfPrivate(), "private");
|
||||
MarkSlot(trc, proxy->slotOfExtra(0), "extra0");
|
||||
|
||||
/*
|
||||
* The GC can use the second reserved slot to link the cross compartment
|
||||
* wrappers into a linked list, in which case we don't want to trace it.
|
||||
*/
|
||||
if (!proxy->is<CrossCompartmentWrapperObject>())
|
||||
MarkSlot(trc, proxy->slotOfExtra(1), "extra1");
|
||||
|
||||
/*
|
||||
* Allow for people to add extra slots to "proxy" classes, without allowing
|
||||
* them to set their own trace hook. Trace the extras.
|
||||
*/
|
||||
unsigned numSlots = JSCLASS_RESERVED_SLOTS(proxy->getClass());
|
||||
for (unsigned i = PROXY_MINIMUM_SLOTS; i < numSlots; i++)
|
||||
MarkSlot(trc, proxy->slotOfClassSpecific(i), "class-specific");
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js::proxy_WeakmapKeyDelegate(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->is<ProxyObject>());
|
||||
return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_Convert(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
|
||||
{
|
||||
JS_ASSERT(proxy->is<ProxyObject>());
|
||||
return Proxy::defaultValue(cx, proxy, hint, vp);
|
||||
}
|
||||
|
||||
void
|
||||
js::proxy_Finalize(FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->is<ProxyObject>());
|
||||
obj->as<ProxyObject>().handler()->finalize(fop, obj);
|
||||
}
|
||||
|
||||
void
|
||||
js::proxy_ObjectMoved(JSObject *obj, const JSObject *old)
|
||||
{
|
||||
JS_ASSERT(obj->is<ProxyObject>());
|
||||
obj->as<ProxyObject>().handler()->objectMoved(obj, old);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_HasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
|
||||
{
|
||||
bool b;
|
||||
if (!Proxy::hasInstance(cx, proxy, v, &b))
|
||||
return false;
|
||||
*bp = !!b;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_Call(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject proxy(cx, &args.callee());
|
||||
JS_ASSERT(proxy->is<ProxyObject>());
|
||||
return Proxy::call(cx, proxy, args);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_Construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject proxy(cx, &args.callee());
|
||||
JS_ASSERT(proxy->is<ProxyObject>());
|
||||
return Proxy::construct(cx, proxy, args);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_Watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable)
|
||||
{
|
||||
return Proxy::watch(cx, obj, id, callable);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_Unwatch(JSContext *cx, HandleObject obj, HandleId id)
|
||||
{
|
||||
return Proxy::unwatch(cx, obj, id);
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_Slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
|
||||
HandleObject result)
|
||||
{
|
||||
return Proxy::slice(cx, proxy, begin, end, result);
|
||||
}
|
||||
|
||||
#define PROXY_CLASS(callOp, constructOp) \
|
||||
PROXY_CLASS_DEF("Proxy", \
|
||||
0, /* additional slots */ \
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy), \
|
||||
callOp, \
|
||||
constructOp)
|
||||
|
||||
const Class js::ProxyObject::uncallableClass_ = PROXY_CLASS(nullptr, nullptr);
|
||||
const Class js::ProxyObject::callableClass_ = PROXY_CLASS(proxy_Call, proxy_Construct);
|
||||
|
||||
const Class* const js::CallableProxyClassPtr = &ProxyObject::callableClass_;
|
||||
const Class* const js::UncallableProxyClassPtr = &ProxyObject::uncallableClass_;
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::NewProxyObject(JSContext *cx, const BaseProxyHandler *handler, HandleValue priv, JSObject *proto_,
|
||||
JSObject *parent_, const ProxyOptions &options)
|
||||
{
|
||||
return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), parent_,
|
||||
options);
|
||||
}
|
||||
|
||||
void
|
||||
ProxyObject::renew(JSContext *cx, const BaseProxyHandler *handler, Value priv)
|
||||
{
|
||||
JS_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
|
||||
JS_ASSERT(getParent() == cx->global());
|
||||
JS_ASSERT(getClass() == &uncallableClass_);
|
||||
JS_ASSERT(!getClass()->ext.innerObject);
|
||||
JS_ASSERT(getTaggedProto().isLazy());
|
||||
|
||||
setHandler(handler);
|
||||
setCrossCompartmentSlot(PRIVATE_SLOT, priv);
|
||||
setSlot(EXTRA_SLOT + 0, UndefinedValue());
|
||||
setSlot(EXTRA_SLOT + 1, UndefinedValue());
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js_InitProxyClass(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
static const JSFunctionSpec static_methods[] = {
|
||||
JS_FN("create", proxy_create, 2, 0),
|
||||
JS_FN("createFunction", proxy_createFunction, 3, 0),
|
||||
JS_FN("revocable", proxy_revocable, 2, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
||||
RootedFunction ctor(cx);
|
||||
ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
if (!JS_DefineFunctions(cx, ctor, static_methods))
|
||||
return nullptr;
|
||||
if (!JS_DefineProperty(cx, obj, "Proxy", ctor, 0,
|
||||
JS_PropertyStub, JS_StrictPropertyStub)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
|
||||
return ctor;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* -*- 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 proxy_Proxy_h
|
||||
#define proxy_Proxy_h
|
||||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
#include "js/Class.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class RegExpGuard;
|
||||
|
||||
/*
|
||||
* Dispatch point for handlers that executes the appropriate C++ or scripted traps.
|
||||
*
|
||||
* Important: All proxy traps need either (a) an AutoEnterPolicy in their
|
||||
* Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug
|
||||
* 945826 comment 0.
|
||||
*/
|
||||
class Proxy
|
||||
{
|
||||
public:
|
||||
/* ES5 Harmony fundamental proxy traps. */
|
||||
static bool preventExtensions(JSContext *cx, HandleObject proxy);
|
||||
static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
static bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
|
||||
static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
|
||||
static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props);
|
||||
|
||||
/* ES5 Harmony derived proxy traps. */
|
||||
static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
|
||||
static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
|
||||
static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
bool strict, MutableHandleValue vp);
|
||||
static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
|
||||
static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
|
||||
static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
|
||||
static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
|
||||
static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
|
||||
static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
|
||||
static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
|
||||
static const char *className(JSContext *cx, HandleObject proxy);
|
||||
static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
|
||||
static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
|
||||
static bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp);
|
||||
static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
|
||||
static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
|
||||
static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
|
||||
|
||||
static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable);
|
||||
static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id);
|
||||
|
||||
static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end,
|
||||
HandleObject result);
|
||||
|
||||
/* IC entry path for handling __noSuchMethod__ on access. */
|
||||
static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id,
|
||||
MutableHandleValue vp);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* proxy_Proxy_h */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,78 @@
|
|||
/* -*- 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 proxy_ScriptedDirectProxyHandler_h
|
||||
#define proxy_ScriptedDirectProxyHandler_h
|
||||
|
||||
#include "jsproxy.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Derived class for all scripted direct proxy handlers. */
|
||||
class ScriptedDirectProxyHandler : public DirectProxyHandler {
|
||||
public:
|
||||
MOZ_CONSTEXPR ScriptedDirectProxyHandler()
|
||||
: DirectProxyHandler(&family)
|
||||
{ }
|
||||
|
||||
/* ES5 Harmony fundamental proxy traps. */
|
||||
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
||||
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy,
|
||||
AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
|
||||
/* ES5 Harmony derived proxy traps. */
|
||||
virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE {
|
||||
return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
|
||||
}
|
||||
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
// Kick keys out to getOwnPropertyName and then filter. [[GetOwnProperty]] could potentially
|
||||
// change the enumerability of the target's properties.
|
||||
virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE {
|
||||
return BaseProxyHandler::keys(cx, proxy, props);
|
||||
}
|
||||
virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
|
||||
MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
|
||||
/* ES6 Harmony traps */
|
||||
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool isScripted() const MOZ_OVERRIDE { return true; }
|
||||
|
||||
static const char family;
|
||||
static const ScriptedDirectProxyHandler singleton;
|
||||
|
||||
// The "proxy extra" slot index in which the handler is stored. Revocable proxies need to set
|
||||
// this at revocation time.
|
||||
static const int HANDLER_EXTRA = 0;
|
||||
// The "function extended" slot index in which the revocation object is stored. Per spec, this
|
||||
// is to be cleared during the first revocation.
|
||||
static const int REVOKE_SLOT = 0;
|
||||
};
|
||||
|
||||
bool
|
||||
proxy(JSContext *cx, unsigned argc, jsval *vp);
|
||||
|
||||
bool
|
||||
proxy_revocable(JSContext *cx, unsigned argc, jsval *vp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* proxy_ScriptedDirectProxyHandler_h */
|
|
@ -0,0 +1,462 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
|
||||
#include "proxy/ScriptedIndirectProxyHandler.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
static bool
|
||||
GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name,
|
||||
MutableHandleValue fvalp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
return JSObject::getProperty(cx, handler, handler, name, fvalp);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetDerivedTrap(JSContext *cx, HandleObject handler, HandlePropertyName name,
|
||||
MutableHandleValue fvalp)
|
||||
{
|
||||
JS_ASSERT(name == cx->names().has ||
|
||||
name == cx->names().hasOwn ||
|
||||
name == cx->names().get ||
|
||||
name == cx->names().set ||
|
||||
name == cx->names().keys ||
|
||||
name == cx->names().iterate);
|
||||
|
||||
return JSObject::getProperty(cx, handler, handler, name, fvalp);
|
||||
}
|
||||
|
||||
static bool
|
||||
Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
|
||||
}
|
||||
|
||||
static bool
|
||||
Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval)
|
||||
{
|
||||
if (!IdToStringOrSymbol(cx, id, rval))
|
||||
return false;
|
||||
return Trap(cx, handler, fval, 1, rval.address(), rval);
|
||||
}
|
||||
|
||||
static bool
|
||||
Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value v_,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
RootedValue v(cx, v_);
|
||||
if (!IdToStringOrSymbol(cx, id, rval))
|
||||
return false;
|
||||
JS::AutoValueArray<2> argv(cx);
|
||||
argv[0].set(rval);
|
||||
argv[1].set(v);
|
||||
return Trap(cx, handler, fval, 2, argv.begin(), rval);
|
||||
}
|
||||
|
||||
static bool
|
||||
IndicatePropertyNotFound(MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
desc.object().set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ValueToBool(HandleValue v, bool *bp)
|
||||
{
|
||||
*bp = ToBoolean(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
|
||||
{
|
||||
JS_ASSERT(props.length() == 0);
|
||||
|
||||
if (array.isPrimitive())
|
||||
return true;
|
||||
|
||||
RootedObject obj(cx, &array.toObject());
|
||||
uint32_t length;
|
||||
if (!GetLengthProperty(cx, obj, &length))
|
||||
return false;
|
||||
|
||||
RootedValue v(cx);
|
||||
for (uint32_t n = 0; n < length; ++n) {
|
||||
if (!CheckForInterrupt(cx))
|
||||
return false;
|
||||
if (!JSObject::getElement(cx, obj, obj, n, &v))
|
||||
return false;
|
||||
RootedId id(cx);
|
||||
if (!ValueToId<CanGC>(cx, v, &id))
|
||||
return false;
|
||||
if (!props.append(id))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
* Old-style indirect proxies allow callers to specify distinct scripted
|
||||
* [[Call]] and [[Construct]] traps. We use an intermediate object so that we
|
||||
* can stash this information in a single reserved slot on the proxy object.
|
||||
*
|
||||
* Note - Currently this is slightly unnecesary, because we actually have 2
|
||||
* extra slots, neither of which are used for ScriptedIndirectProxy. But we're
|
||||
* eventually moving towards eliminating one of those slots, and so we don't
|
||||
* want to add a dependency here.
|
||||
*/
|
||||
static const Class CallConstructHolder = {
|
||||
"CallConstructHolder",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
// This variable exists solely to provide a unique address for use as an identifier.
|
||||
const char ScriptedIndirectProxyHandler::family = 0;
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy,
|
||||
bool *extensible) const
|
||||
{
|
||||
// Scripted indirect proxies don't support extensibility changes.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const
|
||||
{
|
||||
// See above.
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReturnedValueMustNotBePrimitive(JSContext *cx, HandleObject proxy, JSAtom *atom, const Value &v)
|
||||
{
|
||||
if (v.isPrimitive()) {
|
||||
JSAutoByteString bytes;
|
||||
if (AtomToPrintableString(cx, atom, &bytes)) {
|
||||
RootedValue val(cx, ObjectOrNullValue(proxy));
|
||||
js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
|
||||
JSDVG_SEARCH_STACK, val, js::NullPtr(), bytes.ptr());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
GetIndirectProxyHandlerObject(JSObject *proxy)
|
||||
{
|
||||
return proxy->as<ProxyObject>().private_().toObjectOrNull();
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
return GetFundamentalTrap(cx, handler, cx->names().getPropertyDescriptor, &fval) &&
|
||||
Trap1(cx, handler, fval, id, &value) &&
|
||||
((value.get().isUndefined() && IndicatePropertyNotFound(desc)) ||
|
||||
(ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
|
||||
ParsePropertyDescriptorObject(cx, proxy, value, desc)));
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &fval) &&
|
||||
Trap1(cx, handler, fval, id, &value) &&
|
||||
((value.get().isUndefined() && IndicatePropertyNotFound(desc)) ||
|
||||
(ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
|
||||
ParsePropertyDescriptorObject(cx, proxy, value, desc)));
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) &&
|
||||
NewPropertyDescriptorObject(cx, desc, &value) &&
|
||||
Trap2(cx, handler, fval, id, value, &value);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
|
||||
AutoIdVector &props) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyNames, &fval) &&
|
||||
Trap(cx, handler, fval, 0, nullptr, &value) &&
|
||||
ArrayToIdVector(cx, value, props);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
return GetFundamentalTrap(cx, handler, cx->names().delete_, &fval) &&
|
||||
Trap1(cx, handler, fval, id, &value) &&
|
||||
ValueToBool(value, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
return GetFundamentalTrap(cx, handler, cx->names().enumerate, &fval) &&
|
||||
Trap(cx, handler, fval, 0, nullptr, &value) &&
|
||||
ArrayToIdVector(cx, value, props);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
if (!GetDerivedTrap(cx, handler, cx->names().has, &fval))
|
||||
return false;
|
||||
if (!IsCallable(fval))
|
||||
return BaseProxyHandler::has(cx, proxy, id, bp);
|
||||
return Trap1(cx, handler, fval, id, &value) &&
|
||||
ValueToBool(value, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue fval(cx), value(cx);
|
||||
if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval))
|
||||
return false;
|
||||
if (!IsCallable(fval))
|
||||
return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
|
||||
return Trap1(cx, handler, fval, id, &value) &&
|
||||
ValueToBool(value, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue idv(cx);
|
||||
if (!IdToStringOrSymbol(cx, id, &idv))
|
||||
return false;
|
||||
JS::AutoValueArray<2> argv(cx);
|
||||
argv[0].setObjectOrNull(receiver);
|
||||
argv[1].set(idv);
|
||||
RootedValue fval(cx);
|
||||
if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
|
||||
return false;
|
||||
if (!IsCallable(fval))
|
||||
return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
|
||||
return Trap(cx, handler, fval, 2, argv.begin(), vp);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, bool strict, MutableHandleValue vp) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue idv(cx);
|
||||
if (!IdToStringOrSymbol(cx, id, &idv))
|
||||
return false;
|
||||
JS::AutoValueArray<3> argv(cx);
|
||||
argv[0].setObjectOrNull(receiver);
|
||||
argv[1].set(idv);
|
||||
argv[2].set(vp);
|
||||
RootedValue fval(cx);
|
||||
if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
|
||||
return false;
|
||||
if (!IsCallable(fval))
|
||||
return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
|
||||
return Trap(cx, handler, fval, 3, argv.begin(), &idv);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue value(cx);
|
||||
if (!GetDerivedTrap(cx, handler, cx->names().keys, &value))
|
||||
return false;
|
||||
if (!IsCallable(value))
|
||||
return BaseProxyHandler::keys(cx, proxy, props);
|
||||
return Trap(cx, handler, value, 0, nullptr, &value) &&
|
||||
ArrayToIdVector(cx, value, props);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue value(cx);
|
||||
if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
|
||||
return false;
|
||||
if (!IsCallable(value))
|
||||
return BaseProxyHandler::iterate(cx, proxy, flags, vp);
|
||||
return Trap(cx, handler, value, 0, nullptr, vp) &&
|
||||
ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
|
||||
RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
|
||||
JS_ASSERT(ccHolder->getClass() == &CallConstructHolder);
|
||||
RootedValue call(cx, ccHolder->getReservedSlot(0));
|
||||
JS_ASSERT(call.isObject() && call.toObject().isCallable());
|
||||
return Invoke(cx, args.thisv(), call, args.length(), args.array(), args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
|
||||
RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
|
||||
JS_ASSERT(ccHolder->getClass() == &CallConstructHolder);
|
||||
RootedValue construct(cx, ccHolder->getReservedSlot(1));
|
||||
JS_ASSERT(construct.isObject() && construct.toObject().isCallable());
|
||||
return InvokeConstructor(cx, construct, args.length(), args.array(),
|
||||
args.rval().address());
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptedIndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const
|
||||
{
|
||||
return BaseProxyHandler::nativeCall(cx, test, impl, args);
|
||||
}
|
||||
|
||||
JSString *
|
||||
ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const
|
||||
{
|
||||
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
||||
if (!proxy->isCallable()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_INCOMPATIBLE_PROTO,
|
||||
js_Function_str, js_toString_str,
|
||||
"object");
|
||||
return nullptr;
|
||||
}
|
||||
RootedObject obj(cx, &proxy->as<ProxyObject>().extra(0).toObject().getReservedSlot(0).toObject());
|
||||
return fun_toStringHelper(cx, obj, indent);
|
||||
}
|
||||
|
||||
const ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton;
|
||||
|
||||
bool
|
||||
js::proxy_create(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() < 1) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"create", "0", "s");
|
||||
return false;
|
||||
}
|
||||
JSObject *handler = NonNullObject(cx, args[0]);
|
||||
if (!handler)
|
||||
return false;
|
||||
JSObject *proto, *parent = nullptr;
|
||||
if (args.get(1).isObject()) {
|
||||
proto = &args[1].toObject();
|
||||
parent = proto->getParent();
|
||||
} else {
|
||||
JS_ASSERT(IsFunctionObject(&args.callee()));
|
||||
proto = nullptr;
|
||||
}
|
||||
if (!parent)
|
||||
parent = args.callee().getParent();
|
||||
RootedValue priv(cx, ObjectValue(*handler));
|
||||
JSObject *proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton,
|
||||
priv, proto, parent);
|
||||
if (!proxy)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*proxy);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_createFunction(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() < 2) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"createFunction", "1", "");
|
||||
return false;
|
||||
}
|
||||
RootedObject handler(cx, NonNullObject(cx, args[0]));
|
||||
if (!handler)
|
||||
return false;
|
||||
RootedObject proto(cx), parent(cx);
|
||||
parent = args.callee().getParent();
|
||||
proto = parent->global().getOrCreateFunctionPrototype(cx);
|
||||
if (!proto)
|
||||
return false;
|
||||
parent = proto->getParent();
|
||||
|
||||
RootedObject call(cx, ValueToCallable(cx, args[1], args.length() - 2));
|
||||
if (!call)
|
||||
return false;
|
||||
RootedObject construct(cx, nullptr);
|
||||
if (args.length() > 2) {
|
||||
construct = ValueToCallable(cx, args[2], args.length() - 3);
|
||||
if (!construct)
|
||||
return false;
|
||||
} else {
|
||||
construct = call;
|
||||
}
|
||||
|
||||
// Stash the call and construct traps on a holder object that we can stick
|
||||
// in a slot on the proxy.
|
||||
RootedObject ccHolder(cx, JS_NewObjectWithGivenProto(cx, Jsvalify(&CallConstructHolder),
|
||||
js::NullPtr(), cx->global()));
|
||||
if (!ccHolder)
|
||||
return false;
|
||||
ccHolder->setReservedSlot(0, ObjectValue(*call));
|
||||
ccHolder->setReservedSlot(1, ObjectValue(*construct));
|
||||
|
||||
RootedValue priv(cx, ObjectValue(*handler));
|
||||
ProxyOptions options;
|
||||
options.selectDefaultClass(true);
|
||||
JSObject *proxy =
|
||||
ProxyObject::New(cx, &ScriptedIndirectProxyHandler::singleton,
|
||||
priv, TaggedProto(proto), parent, options);
|
||||
if (!proxy)
|
||||
return false;
|
||||
proxy->as<ProxyObject>().setExtra(0, ObjectValue(*ccHolder));
|
||||
|
||||
args.rval().setObject(*proxy);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- 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 proxy_ScriptedIndirectProxyHandler_h
|
||||
#define proxy_ScriptedIndirectProxyHandler_h
|
||||
|
||||
#include "jsproxy.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Derived class for all scripted indirect proxy handlers. */
|
||||
class ScriptedIndirectProxyHandler : public BaseProxyHandler
|
||||
{
|
||||
public:
|
||||
MOZ_CONSTEXPR ScriptedIndirectProxyHandler()
|
||||
: BaseProxyHandler(&family)
|
||||
{ }
|
||||
|
||||
/* ES5 Harmony fundamental proxy traps. */
|
||||
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
||||
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
||||
virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy,
|
||||
AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
|
||||
/* ES5 Harmony derived proxy traps. */
|
||||
virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
|
||||
bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
|
||||
MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
|
||||
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
||||
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const MOZ_OVERRIDE;
|
||||
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
|
||||
virtual bool isScripted() const MOZ_OVERRIDE { return true; }
|
||||
|
||||
static const char family;
|
||||
static const ScriptedIndirectProxyHandler singleton;
|
||||
};
|
||||
|
||||
bool
|
||||
proxy_create(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
bool
|
||||
proxy_createFunction(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* proxy_ScriptedIndirectProxyHandler_h */
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const
|
||||
{
|
||||
// Just like BaseProxyHandler, SecurityWrappers claim by default to always
|
||||
// be extensible, so as not to leak information about the state of the
|
||||
// underlying wrapped thing.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::preventExtensions(JSContext *cx, HandleObject wrapper) const
|
||||
{
|
||||
// See above.
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::enter(JSContext *cx, HandleObject wrapper, HandleId id,
|
||||
Wrapper::Action act, bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
*bp = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
|
||||
CallArgs args) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::setPrototypeOf(JSContext *cx, HandleObject wrapper,
|
||||
HandleObject proto, bool *bp) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For security wrappers, we run the DefaultValue algorithm on the wrapper
|
||||
// itself, which means that the existing security policy on operations like
|
||||
// toString() will take effect and do the right thing here.
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::defaultValue(JSContext *cx, HandleObject wrapper,
|
||||
JSType hint, MutableHandleValue vp) const
|
||||
{
|
||||
return DefaultValue(cx, wrapper, hint, vp);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::regexp_toShared(JSContext *cx, HandleObject obj, RegExpGuard *g) const
|
||||
{
|
||||
return Base::regexp_toShared(cx, obj, g);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::boxedValue_unbox(JSContext *cx, HandleObject obj, MutableHandleValue vp) const
|
||||
{
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::defineProperty(JSContext *cx, HandleObject wrapper,
|
||||
HandleId id, MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
if (desc.getter() || desc.setter()) {
|
||||
JSString *str = IdToString(cx, id);
|
||||
AutoStableStringChars chars(cx);
|
||||
const char16_t *prop = nullptr;
|
||||
if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
|
||||
prop = chars.twoByteChars();
|
||||
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_ACCESSOR_DEF_DENIED, prop);
|
||||
return false;
|
||||
}
|
||||
|
||||
return Base::defineProperty(cx, wrapper, id, desc);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::watch(JSContext *cx, HandleObject proxy,
|
||||
HandleId id, HandleObject callable) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::unwatch(JSContext *cx, HandleObject proxy,
|
||||
HandleId id) const
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template class js::SecurityWrapper<Wrapper>;
|
||||
template class js::SecurityWrapper<CrossCompartmentWrapper>;
|
|
@ -0,0 +1,167 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsexn.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "vm/ErrorObject.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
/*
|
||||
* Wrapper forwards this call directly to the wrapped object for efficiency
|
||||
* and transparency. In particular, the hint is needed to properly stringify
|
||||
* Date objects in certain cases - see bug 646129. Note also the
|
||||
* SecurityWrapper overrides this trap to avoid information leaks. See bug
|
||||
* 720619.
|
||||
*/
|
||||
bool
|
||||
Wrapper::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp) const
|
||||
{
|
||||
vp.set(ObjectValue(*proxy->as<ProxyObject>().target()));
|
||||
if (hint == JSTYPE_VOID)
|
||||
return ToPrimitive(cx, vp);
|
||||
return ToPrimitive(cx, hint, vp);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, const Wrapper *handler,
|
||||
const WrapperOptions *options)
|
||||
{
|
||||
JS_ASSERT(parent);
|
||||
|
||||
RootedValue priv(cx, ObjectValue(*obj));
|
||||
mozilla::Maybe<WrapperOptions> opts;
|
||||
if (!options) {
|
||||
opts.emplace();
|
||||
opts->selectDefaultClass(obj->isCallable());
|
||||
options = opts.ptr();
|
||||
}
|
||||
return NewProxyObject(cx, handler, priv, options->proto(), parent, *options);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, const Wrapper *handler)
|
||||
{
|
||||
JS_ASSERT(!obj->isCallable());
|
||||
existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
|
||||
return existing;
|
||||
}
|
||||
|
||||
const Wrapper *
|
||||
Wrapper::wrapperHandler(JSObject *wrapper)
|
||||
{
|
||||
JS_ASSERT(wrapper->is<WrapperObject>());
|
||||
return static_cast<const Wrapper*>(wrapper->as<ProxyObject>().handler());
|
||||
}
|
||||
|
||||
JSObject *
|
||||
Wrapper::wrappedObject(JSObject *wrapper)
|
||||
{
|
||||
JS_ASSERT(wrapper->is<WrapperObject>());
|
||||
return wrapper->as<ProxyObject>().target();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::UncheckedUnwrap(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp)
|
||||
{
|
||||
unsigned flags = 0;
|
||||
while (true) {
|
||||
if (!wrapped->is<WrapperObject>() ||
|
||||
MOZ_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject))
|
||||
{
|
||||
break;
|
||||
}
|
||||
flags |= Wrapper::wrapperHandler(wrapped)->flags();
|
||||
wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
|
||||
|
||||
// This can be called from DirectProxyHandler::weakmapKeyDelegate() on a
|
||||
// wrapper whose referent has been moved while it is still unmarked.
|
||||
if (wrapped)
|
||||
wrapped = MaybeForwarded(wrapped);
|
||||
}
|
||||
if (flagsp)
|
||||
*flagsp = flags;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::CheckedUnwrap(JSObject *obj, bool stopAtOuter)
|
||||
{
|
||||
while (true) {
|
||||
JSObject *wrapper = obj;
|
||||
obj = UnwrapOneChecked(obj, stopAtOuter);
|
||||
if (!obj || obj == wrapper)
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
js::UnwrapOneChecked(JSObject *obj, bool stopAtOuter)
|
||||
{
|
||||
if (!obj->is<WrapperObject>() ||
|
||||
MOZ_UNLIKELY(!!obj->getClass()->ext.innerObject && stopAtOuter))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
const Wrapper *handler = Wrapper::wrapperHandler(obj);
|
||||
return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
|
||||
}
|
||||
|
||||
const char Wrapper::family = 0;
|
||||
const Wrapper Wrapper::singleton((unsigned)0);
|
||||
const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
|
||||
JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
|
||||
|
||||
/* Compartments. */
|
||||
|
||||
extern JSObject *
|
||||
js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
|
||||
HandleObject parent)
|
||||
{
|
||||
// Allow wrapping outer window proxies.
|
||||
JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
|
||||
return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton);
|
||||
}
|
||||
|
||||
ErrorCopier::~ErrorCopier()
|
||||
{
|
||||
JSContext *cx = ac->context()->asJSContext();
|
||||
if (ac->origin() != cx->compartment() && cx->isExceptionPending()) {
|
||||
RootedValue exc(cx);
|
||||
if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
|
||||
cx->clearPendingException();
|
||||
ac.reset();
|
||||
Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
|
||||
JSObject *copyobj = js_CopyErrorObject(cx, errObj);
|
||||
if (copyobj)
|
||||
cx->setPendingException(ObjectValue(*copyobj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Wrapper::finalizeInBackground(Value priv) const
|
||||
{
|
||||
if (!priv.isObject())
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Make the 'background-finalized-ness' of the wrapper the same as the
|
||||
* wrapped object, to allow transplanting between them.
|
||||
*
|
||||
* If the wrapped object is in the nursery then we know it doesn't have a
|
||||
* finalizer, and so background finalization is ok.
|
||||
*/
|
||||
if (IsInsideNursery(&priv.toObject()))
|
||||
return true;
|
||||
return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind());
|
||||
}
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 add';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var b = int32x4(10, 20, 30, 40);
|
||||
var c = SIMD.int32x4.add(a, b);
|
||||
|
@ -18,6 +16,17 @@ function test() {
|
|||
assertEq(c.z, 33);
|
||||
assertEq(c.w, 44);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var e = int32x4(1, -1, 0, 0);
|
||||
var f = SIMD.int32x4.add(d, e);
|
||||
assertEq(f.x, INT32_MIN);
|
||||
assertEq(f.y, INT32_MAX);
|
||||
assertEq(f.z, INT32_MAX);
|
||||
assertEq(f.w, INT32_MIN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 and';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var b = int32x4(10, 20, 30, 40);
|
||||
var c = SIMD.int32x4.and(a, b);
|
||||
|
@ -18,6 +16,17 @@ function test() {
|
|||
assertEq(c.z, 2);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var e = int32x4(INT32_MIN, INT32_MAX, INT32_MAX, INT32_MIN);
|
||||
var f = SIMD.int32x4.and(d, e);
|
||||
assertEq(f.x, (INT32_MAX & INT32_MIN) | 0);
|
||||
assertEq(f.y, (INT32_MIN & INT32_MAX) | 0);
|
||||
assertEq(f.z, (INT32_MAX & INT32_MAX) | 0);
|
||||
assertEq(f.w, (INT32_MIN & INT32_MIN) | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 equal';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 20, 30, 40);
|
||||
var b = int32x4(10, 20, 30, 4);
|
||||
var c = SIMD.int32x4.equal(a, b);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 fromFloat32x4';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1.1, 2.2, 3.3, 4.6);
|
||||
var c = SIMD.int32x4.fromFloat32x4(a);
|
||||
assertEq(c.x, 1);
|
||||
|
@ -17,6 +15,13 @@ function test() {
|
|||
assertEq(c.z, 3);
|
||||
assertEq(c.w, 4);
|
||||
|
||||
var d = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var f = SIMD.int32x4.fromFloat32x4(d);
|
||||
assertEq(f.x, 0);
|
||||
assertEq(f.y, 0);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,14 +8,19 @@ var summary = 'int32x4 fromFloat32x4Bits';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var c = SIMD.int32x4.fromFloat32x4Bits(a);
|
||||
assertEq(c.x, 1065353216);
|
||||
assertEq(c.y, 1073741824);
|
||||
assertEq(c.z, 1077936128);
|
||||
assertEq(c.w, 1082130432);
|
||||
assertEq(c.x, 0x3f800000 | 0);
|
||||
assertEq(c.y, 0x40000000 | 0);
|
||||
assertEq(c.z, 0x40400000 | 0);
|
||||
assertEq(c.w, 0x40800000 | 0);
|
||||
|
||||
var d = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var f = SIMD.int32x4.fromFloat32x4Bits(d);
|
||||
assertEq(f.x, 0x7fc00000 | 0);
|
||||
assertEq(f.y, 0x80000000 | 0);
|
||||
assertEq(f.z, 0x7f800000 | 0);
|
||||
assertEq(f.w, 0xff800000 | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 greaterThan';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 20, 3, 40);
|
||||
var b = int32x4(10, 2, 30, 4);
|
||||
var c = SIMD.int32x4.greaterThan(b,a);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 lessThan';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 20, 3, 40);
|
||||
var b = int32x4(10, 2, 30, 4);
|
||||
var c = SIMD.int32x4.lessThan(a, b);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 lsh';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
for (var bits = 0; bits < 32; bits++) {
|
||||
var a = int32x4(-1, 2, -3, 4);
|
||||
var c = SIMD.int32x4.shiftLeft(a, bits);
|
||||
|
@ -19,6 +17,16 @@ function test() {
|
|||
assertEq(c.w, 4 << bits);
|
||||
}
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var f = SIMD.int32x4.shiftLeft(d, 1);
|
||||
assertEq(f.x, (INT32_MAX << 1) | 0);
|
||||
assertEq(f.y, (INT32_MIN << 1) | 0);
|
||||
assertEq(f.z, (INT32_MAX << 1) | 0);
|
||||
assertEq(f.w, (INT32_MIN << 1) | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 mul';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var b = int32x4(10, 20, 30, 40);
|
||||
var c = SIMD.int32x4.mul(a, b);
|
||||
|
@ -18,6 +16,17 @@ function test() {
|
|||
assertEq(c.z, 90);
|
||||
assertEq(c.w, 160);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var e = int32x4(-1, -1, INT32_MIN, INT32_MIN);
|
||||
var f = SIMD.int32x4.mul(d, e);
|
||||
assertEq(f.x, (INT32_MAX * -1) | 0);
|
||||
assertEq(f.y, (INT32_MIN * -1) | 0);
|
||||
assertEq(f.z, (INT32_MAX * INT32_MIN) | 0);
|
||||
assertEq(f.w, (INT32_MIN * INT32_MIN) | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 neg';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var c = SIMD.int32x4.neg(a);
|
||||
assertEq(c.x, -1);
|
||||
|
@ -17,6 +15,16 @@ function test() {
|
|||
assertEq(c.z, -3);
|
||||
assertEq(c.w, -4);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, -0, 0);
|
||||
var f = SIMD.int32x4.neg(d);
|
||||
assertEq(f.x, -INT32_MAX | 0);
|
||||
assertEq(f.y, -INT32_MIN | 0);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 not';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var c = SIMD.int32x4.not(a);
|
||||
assertEq(c.x, -2);
|
||||
|
@ -17,6 +15,16 @@ function test() {
|
|||
assertEq(c.z, -4);
|
||||
assertEq(c.w, -5);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, 0, 0);
|
||||
var f = SIMD.int32x4.not(d);
|
||||
assertEq(f.x, ~INT32_MAX | 0);
|
||||
assertEq(f.y, ~INT32_MIN | 0);
|
||||
assertEq(f.z, ~0 | 0);
|
||||
assertEq(f.w, ~0 | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 or';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var b = int32x4(10, 20, 30, 40);
|
||||
var c = SIMD.int32x4.or(a, b);
|
||||
|
@ -18,6 +16,17 @@ function test() {
|
|||
assertEq(c.z, 31);
|
||||
assertEq(c.w, 44);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var e = int32x4(INT32_MIN, INT32_MAX, INT32_MAX, INT32_MIN);
|
||||
var f = SIMD.int32x4.or(d, e);
|
||||
assertEq(f.x, (INT32_MAX | INT32_MIN) | 0);
|
||||
assertEq(f.y, (INT32_MIN | INT32_MAX) | 0);
|
||||
assertEq(f.z, (INT32_MAX | INT32_MAX) | 0);
|
||||
assertEq(f.w, (INT32_MIN | INT32_MIN) | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 rsh';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
for (var bits = 0; bits < 32; bits++) {
|
||||
var a = int32x4(-1, 2, -3, 4);
|
||||
var c = SIMD.int32x4.shiftRight(a, bits);
|
||||
|
@ -19,6 +17,15 @@ function test() {
|
|||
assertEq(c.w, 4 >> bits);
|
||||
}
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var f = SIMD.int32x4.shiftRight(d, 1);
|
||||
assertEq(f.x, INT32_MAX >> 1);
|
||||
assertEq(f.y, INT32_MIN >> 1);
|
||||
assertEq(f.z, INT32_MAX >> 1);
|
||||
assertEq(f.w, INT32_MIN >> 1);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 shuffle';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var c = SIMD.int32x4.shuffle(a, 0x1B);
|
||||
assertEq(c.x, 4);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 shuffleMix';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var b = int32x4(10, 20, 30, 40);
|
||||
var c = SIMD.int32x4.shuffleMix(a,b, 0x1B);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 sub';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var b = int32x4(10, 20, 30, 40);
|
||||
var c = SIMD.int32x4.sub(b,a);
|
||||
|
@ -18,6 +16,17 @@ function test() {
|
|||
assertEq(c.z, 27);
|
||||
assertEq(c.w, 36);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(-1, 1, INT32_MAX, INT32_MIN);
|
||||
var e = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var f = SIMD.int32x4.sub(e, d);
|
||||
assertEq(f.x, (INT32_MAX - -1) | 0);
|
||||
assertEq(f.y, (INT32_MIN - 1) | 0);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 ursh';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
for (var bits = 0; bits < 32; bits++) {
|
||||
var a = int32x4(-1, 2, -3, 4);
|
||||
var c = SIMD.int32x4.shiftRightLogical(a, bits);
|
||||
|
@ -19,6 +17,16 @@ function test() {
|
|||
assertEq(c.w >>> 0, 4 >>> bits);
|
||||
}
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var f = SIMD.int32x4.shiftRightLogical(d, 0);
|
||||
assertEq(f.x, (INT32_MAX >>> 0) | 0);
|
||||
assertEq(f.y, (INT32_MIN >>> 0) | 0);
|
||||
assertEq(f.z, (INT32_MAX >>> 0) | 0);
|
||||
assertEq(f.w, (INT32_MIN >>> 0) | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,17 +8,17 @@ var summary = 'int32x4 with';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var x = SIMD.int32x4.withX(a, 5);
|
||||
var y = SIMD.int32x4.withY(a, 5);
|
||||
var z = SIMD.int32x4.withZ(a, 5);
|
||||
var w = SIMD.int32x4.withW(a, 5);
|
||||
var w = SIMD.int32x4.withW(a, INT32_MAX + 1);
|
||||
assertEq(x.x, 5);
|
||||
assertEq(y.y, 5);
|
||||
assertEq(z.z, 5);
|
||||
assertEq(w.w, 5);
|
||||
assertEq(w.w, (INT32_MAX + 1) | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 with';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var x = SIMD.int32x4.withFlagX(a, true);
|
||||
var y = SIMD.int32x4.withFlagY(a, false);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'int32x4 xor';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var b = int32x4(10, 20, 30, 40);
|
||||
var c = SIMD.int32x4.xor(a, b);
|
||||
|
@ -18,6 +16,17 @@ function test() {
|
|||
assertEq(c.z, 29);
|
||||
assertEq(c.w, 44);
|
||||
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN);
|
||||
var e = int32x4(INT32_MIN, INT32_MAX, INT32_MAX, INT32_MIN);
|
||||
var f = SIMD.int32x4.xor(d, e);
|
||||
assertEq(f.x, (INT32_MAX ^ INT32_MIN) | 0);
|
||||
assertEq(f.y, (INT32_MIN ^ INT32_MAX) | 0);
|
||||
assertEq(f.z, (INT32_MAX ^ INT32_MAX) | 0);
|
||||
assertEq(f.w, (INT32_MIN ^ INT32_MIN) | 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "jsfun.h"
|
||||
#include "jsobj.h"
|
||||
|
||||
#include "proxy/Proxy.h"
|
||||
#include "vm/ProxyObject.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include "vm/ObjectImpl.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsproxy.h"
|
||||
|
||||
#include "proxy/Proxy.h"
|
||||
#include "vm/ProxyObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsproxy.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
#include "gc/Zone.h"
|
||||
#include "proxy/Proxy.h"
|
||||
#include "vm/Shape.h"
|
||||
|
||||
/*
|
||||
|
|
|
@ -744,20 +744,14 @@ Activation::Activation(ThreadSafeContext *cx, Kind kind)
|
|||
kind_(kind)
|
||||
{
|
||||
cx->perThreadData->activation_ = this;
|
||||
|
||||
// Link the activation into the list of profiling activations if needed.
|
||||
if (isProfiling())
|
||||
registerProfiling();
|
||||
}
|
||||
|
||||
Activation::~Activation()
|
||||
{
|
||||
JS_ASSERT_IF(isProfiling(), this != cx_->perThreadData->profilingActivation_);
|
||||
JS_ASSERT(cx_->perThreadData->activation_ == this);
|
||||
JS_ASSERT(hideScriptedCallerCount_ == 0);
|
||||
cx_->perThreadData->activation_ = prev_;
|
||||
|
||||
if (isProfiling())
|
||||
unregisterProfiling();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1554,11 +1554,11 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
|
|||
fp_(nullptr),
|
||||
exitReason_(AsmJSExit::None)
|
||||
{
|
||||
(void) entrySP_; // squelch GCC warning
|
||||
|
||||
// NB: this is a hack and can be removed once Ion switches over to
|
||||
// JS::ProfilingFrameIterator.
|
||||
if (cx->runtime()->spsProfiler.enabled()) {
|
||||
// Use a profiler string that matches jsMatch regex in
|
||||
// browser/devtools/profiler/cleopatra/js/parserWorker.js.
|
||||
// (For now use a single static string to avoid further slowing down
|
||||
// calls into asm.js.)
|
||||
profiler_ = &cx->runtime()->spsProfiler;
|
||||
profiler_->enterAsmJS("asm.js code :0", this);
|
||||
}
|
||||
|
@ -1568,14 +1568,21 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
|
|||
|
||||
prevAsmJS_ = cx->mainThread().asmJSActivationStack_;
|
||||
|
||||
{
|
||||
JSRuntime::AutoLockForInterrupt lock(cx->runtime());
|
||||
cx->mainThread().asmJSActivationStack_ = this;
|
||||
}
|
||||
|
||||
(void) entrySP_; // squelch GCC warning
|
||||
// Now that the AsmJSActivation is fully initialized, make it visible to
|
||||
// asynchronous profiling.
|
||||
registerProfiling();
|
||||
}
|
||||
|
||||
AsmJSActivation::~AsmJSActivation()
|
||||
{
|
||||
// Hide this activation from the profiler before is is destroyed.
|
||||
unregisterProfiling();
|
||||
|
||||
if (profiler_)
|
||||
profiler_->exitAsmJS();
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "mozilla/StartupTimeline.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/XPTInterfaceInfoManager.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
|
@ -3557,7 +3556,8 @@ nsXPCComponents_Utils::SetAddonInterposition(const nsACString &addonIdStr,
|
|||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::Now(double *aRetval)
|
||||
{
|
||||
TimeStamp start = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
|
||||
bool isInconsistent = false;
|
||||
TimeStamp start = TimeStamp::ProcessCreation(isInconsistent);
|
||||
*aRetval = (TimeStamp::Now() - start).ToMilliseconds();
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -120,10 +120,6 @@ ForceCOWBehavior(JSObject *obj)
|
|||
"instances modulo this hack");
|
||||
return true;
|
||||
}
|
||||
// Proxies get OpaqueXrayTraits, but we still need COWs to them for now to
|
||||
// let the SpecialPowers wrapper work.
|
||||
if (key == JSProto_Proxy)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1936,6 +1936,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder);
|
||||
aBuilder->SetContainsBlendModes(BlendModeSet());
|
||||
|
||||
nsRect dirtyRectOutsideTransform = dirtyRect;
|
||||
if (isTransformed) {
|
||||
const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
|
||||
if (aBuilder->IsForPainting() &&
|
||||
|
@ -1960,6 +1961,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
|
||||
bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
|
||||
nsRect dirtyRectOutsideSVGEffects = dirtyRect;
|
||||
if (usingSVGEffects) {
|
||||
dirtyRect =
|
||||
nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
|
||||
|
@ -2082,6 +2084,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
* output even if the element being filtered wouldn't otherwise do so.
|
||||
*/
|
||||
if (usingSVGEffects) {
|
||||
// Revert to the post-filter dirty rect.
|
||||
buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects);
|
||||
/* List now emptied, so add the new list to the top. */
|
||||
resultList.AppendNewToTop(
|
||||
new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList));
|
||||
|
@ -2116,7 +2120,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
clipState.Restore();
|
||||
// Revert to the dirtyrect coming in from the parent, without our transform
|
||||
// taken into account.
|
||||
buildingDisplayList.SetDirtyRect(aDirtyRect);
|
||||
buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform);
|
||||
// Revert to the outer reference frame and offset because all display
|
||||
// items we create from now on are outside the transform.
|
||||
const nsIFrame* outerReferenceFrame =
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<body style="height: 5000px; overflow:hidden">
|
||||
<div style="display: block; margin-top: 100px; position:fixed; overflow:scroll; top: 0px; width: 50px; height: 100px"><div style="height:200px; background-color: red"></div></div>
|
||||
<div style="display: block; margin-top: 20px; position:relative; left: 50px; width: 50px; height: 100px; background-color: red"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html reftest-async-scroll
|
||||
reftest-displayport-x="0" reftest-displayport-y="0"
|
||||
reftest-displayport-w="800" reftest-displayport-h="2000"
|
||||
reftest-async-scroll-x="0" reftest-async-scroll-y="80">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<body style="height: 5000px; overflow:hidden">
|
||||
<!-- In this test the fixed-position element is a child of the scrollable layer, but
|
||||
gets a clip rect because it is itself a container for scrolling sublayers. This
|
||||
tests that such a layer is transformed correctly while an async scroll transform
|
||||
is in effect. -->
|
||||
<div style="display: block; margin-top: 100px; position:fixed; overflow:scroll; top: 0px; width: 50px; height: 100px"><div style="height:200px; background-color: red"></div></div>
|
||||
<div style="display: block; margin-top: 100px; position:relative; left: 50px; width: 50px; height: 100px; background-color: red"></div>
|
||||
</body>
|
||||
</html>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче