зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to birch.
This commit is contained in:
Коммит
84b40fcc6a
|
@ -4219,7 +4219,6 @@ MOZ_OMX_PLUGIN=
|
|||
MOZ_VP8=
|
||||
MOZ_VP8_ERROR_CONCEALMENT=
|
||||
MOZ_VP8_ENCODER=
|
||||
MOZ_WEBVTT=1
|
||||
MOZ_WEBSPEECH=1
|
||||
VPX_AS=
|
||||
VPX_ASFLAGS=
|
||||
|
@ -5701,10 +5700,6 @@ if test -n "$MOZ_OPUS"; then
|
|||
AC_DEFINE(MOZ_OPUS)
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_WEBVTT"; then
|
||||
AC_DEFINE(MOZ_WEBVTT)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Check alsa availability on Linux if using sydneyaudio
|
||||
dnl ========================================================
|
||||
|
@ -8909,7 +8904,6 @@ AC_SUBST(MOZ_VORBIS)
|
|||
AC_SUBST(MOZ_TREMOR)
|
||||
AC_SUBST(MOZ_OPUS)
|
||||
AC_SUBST(MOZ_WEBM)
|
||||
AC_SUBST(MOZ_WEBVTT)
|
||||
AC_SUBST(MOZ_DASH)
|
||||
AC_SUBST(MOZ_WMF)
|
||||
AC_SUBST(MOZ_MEDIA_PLUGINS)
|
||||
|
|
|
@ -177,6 +177,18 @@ AudioNodeStream::EnsureTrack()
|
|||
return track;
|
||||
}
|
||||
|
||||
bool
|
||||
AudioNodeStream::AllInputsFinished() const
|
||||
{
|
||||
uint32_t inputCount = mInputs.Length();
|
||||
for (uint32_t i = 0; i < inputCount; ++i) {
|
||||
if (!mInputs[i]->GetSource()->IsFinishedOnGraphThread()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !!inputCount;
|
||||
}
|
||||
|
||||
AudioChunk*
|
||||
AudioNodeStream::ObtainInputBlock(AudioChunk* aTmpChunk)
|
||||
{
|
||||
|
|
|
@ -74,6 +74,7 @@ public:
|
|||
double aStreamTime);
|
||||
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo);
|
||||
TrackTicks GetCurrentPosition();
|
||||
bool AllInputsFinished() const;
|
||||
|
||||
// Any thread
|
||||
AudioNodeEngine* Engine() { return mEngine; }
|
||||
|
|
|
@ -413,12 +413,11 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
|
|||
: AudioNode(aContext)
|
||||
, mLoopStart(0.0)
|
||||
, mLoopEnd(0.0)
|
||||
, mLoop(false)
|
||||
, mStartCalled(false)
|
||||
, mPlaybackRate(new AudioParam(this, SendPlaybackRateToStream, 1.0f))
|
||||
, mPannerNode(nullptr)
|
||||
, mLoop(false)
|
||||
, mStartCalled(false)
|
||||
{
|
||||
SetProduceOwnOutput(true);
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(
|
||||
new AudioBufferSourceNodeEngine(aContext->Destination()),
|
||||
MediaStreamGraph::INTERNAL_STREAM);
|
||||
|
@ -503,6 +502,9 @@ AudioBufferSourceNode::Start(JSContext* aCx, double aWhen, double aOffset,
|
|||
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::DURATION,
|
||||
NS_lround(endOffset*rate) - offsetTicks);
|
||||
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::SAMPLE_RATE, rate);
|
||||
|
||||
MOZ_ASSERT(!mPlayingRef, "We can only accept a successful start() call once");
|
||||
mPlayingRef.Take(this);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -528,7 +530,9 @@ void
|
|||
AudioBufferSourceNode::NotifyMainThreadStateChanged()
|
||||
{
|
||||
if (mStream->IsFinished()) {
|
||||
SetProduceOwnOutput(false);
|
||||
// Drop the playing reference
|
||||
// Warning: The below line might delete this.
|
||||
mPlayingRef.Drop(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,16 +50,6 @@ public:
|
|||
mPannerNode = aPannerNode;
|
||||
}
|
||||
|
||||
void JSBindingFinalized()
|
||||
{
|
||||
// If the JS binding goes away on a node which never received a start()
|
||||
// call, then it can no longer produce output.
|
||||
if (!mStartCalled) {
|
||||
SetProduceOwnOutput(false);
|
||||
}
|
||||
AudioNode::JSBindingFinalized();
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioBufferSourceNode, AudioNode)
|
||||
|
||||
|
@ -126,13 +116,14 @@ public:
|
|||
|
||||
private:
|
||||
static void SendPlaybackRateToStream(AudioNode* aNode);
|
||||
nsRefPtr<AudioBuffer> mBuffer;
|
||||
double mLoopStart;
|
||||
double mLoopEnd;
|
||||
bool mLoop;
|
||||
bool mStartCalled;
|
||||
nsRefPtr<AudioBuffer> mBuffer;
|
||||
nsRefPtr<AudioParam> mPlaybackRate;
|
||||
PannerNode* mPannerNode;
|
||||
SelfReference<AudioBufferSourceNode> mPlayingRef; // a reference to self while playing
|
||||
bool mLoop;
|
||||
bool mStartCalled;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include "AudioContext.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
|
@ -33,7 +32,7 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioContext, Release)
|
|||
|
||||
static uint8_t gWebAudioOutputKey;
|
||||
|
||||
AudioContext::AudioContext(nsIDOMWindow* aWindow)
|
||||
AudioContext::AudioContext(nsPIDOMWindow* aWindow)
|
||||
: mWindow(aWindow)
|
||||
, mDestination(new AudioDestinationNode(this, MediaStreamGraph::GetInstance()))
|
||||
{
|
||||
|
@ -233,13 +232,19 @@ AudioContext::CurrentTime() const
|
|||
void
|
||||
AudioContext::Suspend()
|
||||
{
|
||||
DestinationStream()->ChangeExplicitBlockerCount(1);
|
||||
MediaStream* ds = DestinationStream();
|
||||
if (ds) {
|
||||
ds->ChangeExplicitBlockerCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioContext::Resume()
|
||||
{
|
||||
DestinationStream()->ChangeExplicitBlockerCount(-1);
|
||||
MediaStream* ds = DestinationStream();
|
||||
if (ds) {
|
||||
ds->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "MediaBufferDecoder.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
|
||||
// X11 has a #define for CurrentTime. Unbelievable :-(.
|
||||
// See content/media/DOMMediaStream.h for more fun!
|
||||
|
@ -29,7 +28,7 @@
|
|||
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
class nsIDOMWindow;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -53,14 +52,14 @@ class PannerNode;
|
|||
class AudioContext MOZ_FINAL : public nsWrapperCache,
|
||||
public EnableWebAudioCheck
|
||||
{
|
||||
explicit AudioContext(nsIDOMWindow* aParentWindow);
|
||||
explicit AudioContext(nsPIDOMWindow* aParentWindow);
|
||||
~AudioContext();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioContext)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioContext)
|
||||
|
||||
nsIDOMWindow* GetParentObject() const
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return mWindow;
|
||||
}
|
||||
|
@ -148,7 +147,7 @@ private:
|
|||
friend struct ::mozilla::WebAudioDecodeJob;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMWindow> mWindow;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsRefPtr<AudioDestinationNode> mDestination;
|
||||
nsRefPtr<AudioListener> mListener;
|
||||
MediaBufferDecoder mDecoder;
|
||||
|
|
|
@ -9,31 +9,17 @@
|
|||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeStream.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioDestinationNode, AudioNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioDestinationNode, AudioNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AudioDestinationNode)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
|
||||
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioDestinationNode)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioDestinationNode)
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(AudioDestinationNode, AudioNode)
|
||||
|
||||
AudioDestinationNode::AudioDestinationNode(AudioContext* aContext, MediaStreamGraph* aGraph)
|
||||
: AudioNode(aContext)
|
||||
{
|
||||
mStream = aGraph->CreateAudioNodeStream(new AudioNodeEngine(),
|
||||
MediaStreamGraph::EXTERNAL_STREAM);
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
|
|
@ -14,19 +14,12 @@ namespace dom {
|
|||
|
||||
class AudioContext;
|
||||
|
||||
/**
|
||||
* Need to have an nsWrapperCache on AudioDestinationNodes since
|
||||
* AudioContext.destination returns them.
|
||||
*/
|
||||
class AudioDestinationNode : public AudioNode,
|
||||
public nsWrapperCache
|
||||
class AudioDestinationNode : public AudioNode
|
||||
{
|
||||
public:
|
||||
AudioDestinationNode(AudioContext* aContext, MediaStreamGraph* aGraph);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AudioDestinationNode,
|
||||
AudioNode)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -35,12 +28,6 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void JSBindingFinalized()
|
||||
{
|
||||
// Don't do anything special for destination nodes, as they will always
|
||||
// remain accessible through the AudioContext.
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,44 +13,32 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
inline void
|
||||
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
||||
AudioNode::InputNode& aField,
|
||||
const char* aName,
|
||||
unsigned aFlags)
|
||||
{
|
||||
CycleCollectionNoteChild(aCallback, aField.mInputNode.get(), aName, aFlags);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
tmp->DisconnectFromGraph();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
inline void
|
||||
ImplCycleCollectionUnlink(nsCycleCollectionTraversalCallback& aCallback,
|
||||
AudioNode::InputNode& aField,
|
||||
const char* aName,
|
||||
unsigned aFlags)
|
||||
{
|
||||
aField.mInputNode = nullptr;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_3(AudioNode, mContext, mInputNodes, mOutputNodes)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioNode)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioNode)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioNode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
NS_IMPL_ADDREF_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioNode)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
||||
|
||||
AudioNode::AudioNode(AudioContext* aContext)
|
||||
: mContext(aContext)
|
||||
, mJSBindingFinalized(false)
|
||||
, mCanProduceOwnOutput(false)
|
||||
, mOutputEnded(false)
|
||||
{
|
||||
MOZ_ASSERT(aContext);
|
||||
nsDOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
AudioNode::~AudioNode()
|
||||
{
|
||||
DestroyMediaStream();
|
||||
DisconnectFromGraph();
|
||||
MOZ_ASSERT(mInputNodes.IsEmpty());
|
||||
MOZ_ASSERT(mOutputNodes.IsEmpty());
|
||||
}
|
||||
|
@ -81,21 +69,8 @@ FindIndexOfNodeWithPorts(const nsTArray<AudioNode::InputNode>& aInputNodes, cons
|
|||
}
|
||||
|
||||
void
|
||||
AudioNode::UpdateOutputEnded()
|
||||
AudioNode::DisconnectFromGraph()
|
||||
{
|
||||
if (mOutputEnded) {
|
||||
// Already ended, so nothing to do.
|
||||
return;
|
||||
}
|
||||
if (mCanProduceOwnOutput ||
|
||||
!mInputNodes.IsEmpty() ||
|
||||
(!mJSBindingFinalized && NumberOfInputs() > 0)) {
|
||||
// This node could still produce output in the future.
|
||||
return;
|
||||
}
|
||||
|
||||
mOutputEnded = true;
|
||||
|
||||
// Addref this temporarily so the refcount bumping below doesn't destroy us
|
||||
// prematurely
|
||||
nsRefPtr<AudioNode> kungFuDeathGrip = this;
|
||||
|
@ -106,9 +81,8 @@ AudioNode::UpdateOutputEnded()
|
|||
// Disconnect inputs. We don't need them anymore.
|
||||
while (!mInputNodes.IsEmpty()) {
|
||||
uint32_t i = mInputNodes.Length() - 1;
|
||||
nsRefPtr<AudioNode> input = mInputNodes[i].mInputNode.forget();
|
||||
nsRefPtr<AudioNode> input = mInputNodes[i].mInputNode;
|
||||
mInputNodes.RemoveElementAt(i);
|
||||
NS_ASSERTION(mOutputNodes.Contains(this), "input/output inconsistency");
|
||||
input->mOutputNodes.RemoveElement(this);
|
||||
}
|
||||
|
||||
|
@ -117,12 +91,9 @@ AudioNode::UpdateOutputEnded()
|
|||
nsRefPtr<AudioNode> output = mOutputNodes[i].forget();
|
||||
mOutputNodes.RemoveElementAt(i);
|
||||
uint32_t inputIndex = FindIndexOfNode(output->mInputNodes, this);
|
||||
NS_ASSERTION(inputIndex != nsTArray<AudioNode::InputNode>::NoIndex, "input/output inconsistency");
|
||||
// It doesn't matter which one we remove, since we're going to remove all
|
||||
// entries for this node anyway.
|
||||
output->mInputNodes.RemoveElementAt(inputIndex);
|
||||
|
||||
output->UpdateOutputEnded();
|
||||
}
|
||||
|
||||
DestroyMediaStream();
|
||||
|
@ -143,11 +114,6 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
|
|||
return;
|
||||
}
|
||||
|
||||
if (IsOutputEnded() || aDestination.IsOutputEnded()) {
|
||||
// No need to connect since we're not going to produce anything other
|
||||
// than silence.
|
||||
return;
|
||||
}
|
||||
if (FindIndexOfNodeWithPorts(aDestination.mInputNodes, this, aInput, aOutput) !=
|
||||
nsTArray<AudioNode::InputNode>::NoIndex) {
|
||||
// connection already exists.
|
||||
|
@ -157,9 +123,6 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
|
|||
// The MediaStreamGraph will handle cycle detection. We don't need to do it
|
||||
// here.
|
||||
|
||||
// Addref this temporarily so the refcount bumping below doesn't destroy us
|
||||
nsRefPtr<AudioNode> kungFuDeathGrip = this;
|
||||
|
||||
mOutputNodes.AppendElement(&aDestination);
|
||||
InputNode* input = aDestination.mInputNodes.AppendElement();
|
||||
input->mInputNode = this;
|
||||
|
@ -219,10 +182,6 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
|
|||
return;
|
||||
}
|
||||
|
||||
// Disconnect everything connected to this output. First find the
|
||||
// corresponding inputs and remove them.
|
||||
nsAutoTArray<nsRefPtr<AudioNode>,4> outputsToUpdate;
|
||||
|
||||
for (int32_t i = mOutputNodes.Length() - 1; i >= 0; --i) {
|
||||
AudioNode* dest = mOutputNodes[i];
|
||||
for (int32_t j = dest->mInputNodes.Length() - 1; j >= 0; --j) {
|
||||
|
@ -232,17 +191,12 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
|
|||
// Remove one instance of 'dest' from mOutputNodes. There could be
|
||||
// others, and it's not correct to remove them all since some of them
|
||||
// could be for different output ports.
|
||||
*outputsToUpdate.AppendElement() = mOutputNodes[i].forget();
|
||||
mOutputNodes.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < outputsToUpdate.Length(); ++i) {
|
||||
outputsToUpdate[i]->UpdateOutputEnded();
|
||||
}
|
||||
|
||||
// This disconnection may have disconnected a panner and a source.
|
||||
Context()->UpdatePannerSource();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef AudioNode_h_
|
||||
#define AudioNode_h_
|
||||
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "EnableWebAudioCheck.h"
|
||||
|
@ -26,6 +27,36 @@ namespace dom {
|
|||
|
||||
struct ThreeDPoint;
|
||||
|
||||
template<class T>
|
||||
class SelfReference {
|
||||
public:
|
||||
SelfReference() : mHeld(false) {}
|
||||
~SelfReference()
|
||||
{
|
||||
NS_ASSERTION(!mHeld, "Forgot to drop the self reference?");
|
||||
}
|
||||
|
||||
void Take(T* t)
|
||||
{
|
||||
if (!mHeld) {
|
||||
mHeld = true;
|
||||
t->AddRef();
|
||||
}
|
||||
}
|
||||
void Drop(T* t)
|
||||
{
|
||||
if (mHeld) {
|
||||
mHeld = false;
|
||||
t->Release();
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const { return mHeld; }
|
||||
|
||||
private:
|
||||
bool mHeld;
|
||||
};
|
||||
|
||||
/**
|
||||
* The DOM object representing a Web Audio AudioNode.
|
||||
*
|
||||
|
@ -38,14 +69,8 @@ struct ThreeDPoint;
|
|||
* in the future. If it isn't, then we break its connections to its inputs
|
||||
* and outputs, allowing nodes to be immediately disconnected. This
|
||||
* disconnection is done internally, invisible to DOM users.
|
||||
*
|
||||
* We say that a node cannot produce output in the future if it has no inputs
|
||||
* that can, and it is not producing output itself without any inputs, and
|
||||
* either it can never have any inputs or it has no JS wrapper. (If it has a
|
||||
* JS wrapper and can accept inputs, then a new input could be added in
|
||||
* the future.)
|
||||
*/
|
||||
class AudioNode : public nsISupports,
|
||||
class AudioNode : public nsDOMEventTargetHelper,
|
||||
public EnableWebAudioCheck
|
||||
{
|
||||
public:
|
||||
|
@ -71,14 +96,8 @@ public:
|
|||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(AudioNode)
|
||||
|
||||
void JSBindingFinalized()
|
||||
{
|
||||
NS_ASSERTION(!mJSBindingFinalized, "JS binding already finalized");
|
||||
mJSBindingFinalized = true;
|
||||
UpdateOutputEnded();
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioNode,
|
||||
nsDOMEventTargetHelper)
|
||||
|
||||
virtual AudioBufferSourceNode* AsAudioBufferSourceNode() {
|
||||
return nullptr;
|
||||
|
@ -105,10 +124,6 @@ public:
|
|||
virtual uint32_t NumberOfInputs() const { return 1; }
|
||||
virtual uint32_t NumberOfOutputs() const { return 1; }
|
||||
|
||||
// This could possibly delete 'this'.
|
||||
void UpdateOutputEnded();
|
||||
bool IsOutputEnded() const { return mOutputEnded; }
|
||||
|
||||
struct InputNode {
|
||||
~InputNode()
|
||||
{
|
||||
|
@ -117,9 +132,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// Strong reference.
|
||||
// May be null if the source node has gone away.
|
||||
nsRefPtr<AudioNode> mInputNode;
|
||||
// Weak reference.
|
||||
AudioNode* mInputNode;
|
||||
nsRefPtr<MediaInputPort> mStreamPort;
|
||||
// The index of the input port this node feeds into.
|
||||
uint32_t mInputPort;
|
||||
|
@ -129,21 +143,15 @@ public:
|
|||
|
||||
MediaStream* Stream() { return mStream; }
|
||||
|
||||
// Set this to true when the node can produce its own output even if there
|
||||
// are no inputs.
|
||||
void SetProduceOwnOutput(bool aCanProduceOwnOutput)
|
||||
{
|
||||
mCanProduceOwnOutput = aCanProduceOwnOutput;
|
||||
if (!aCanProduceOwnOutput) {
|
||||
UpdateOutputEnded();
|
||||
}
|
||||
}
|
||||
|
||||
const nsTArray<InputNode>& InputNodes() const
|
||||
{
|
||||
return mInputNodes;
|
||||
}
|
||||
|
||||
private:
|
||||
// This could possibly delete 'this'.
|
||||
void DisconnectFromGraph();
|
||||
|
||||
protected:
|
||||
static void Callback(AudioNode* aNode) { /* not implemented */ }
|
||||
|
||||
|
@ -171,15 +179,6 @@ private:
|
|||
// exact matching entry, since mOutputNodes doesn't include the port
|
||||
// identifiers and the same node could be connected on multiple ports.
|
||||
nsTArray<nsRefPtr<AudioNode> > mOutputNodes;
|
||||
// True if the JS binding has been finalized (so script no longer has
|
||||
// a reference to this node).
|
||||
bool mJSBindingFinalized;
|
||||
// True if this node can produce its own output even when all inputs
|
||||
// have ended their output.
|
||||
bool mCanProduceOwnOutput;
|
||||
// True if this node can never produce anything except silence in the future.
|
||||
// Updated by UpdateOutputEnded().
|
||||
bool mOutputEnded;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -25,14 +25,41 @@ NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
|
|||
|
||||
class DelayNodeEngine : public AudioNodeEngine
|
||||
{
|
||||
class PlayingRefChanged : public nsRunnable
|
||||
{
|
||||
public:
|
||||
enum ChangeType { ADDREF, RELEASE };
|
||||
PlayingRefChanged(DelayNode& aNode, ChangeType aChange)
|
||||
: mNode(aNode)
|
||||
, mChange(aChange)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mChange == ADDREF) {
|
||||
mNode.mPlayingRef.Take(&mNode);
|
||||
} else if (mChange == RELEASE) {
|
||||
mNode.mPlayingRef.Drop(&mNode);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
DelayNode& mNode;
|
||||
ChangeType mChange;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit DelayNodeEngine(AudioDestinationNode* aDestination)
|
||||
DelayNodeEngine(AudioDestinationNode* aDestination, DelayNode& aDelay)
|
||||
: mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
, mDelayNode(aDelay)
|
||||
// Keep the default value in sync with the default value in DelayNode::DelayNode.
|
||||
, mDelay(0.f)
|
||||
, mMaxDelay(0.)
|
||||
, mWriteIndex(0)
|
||||
, mLeftOverData(INT32_MIN)
|
||||
, mCurrentDelayTime(0.)
|
||||
{
|
||||
}
|
||||
|
@ -98,7 +125,30 @@ public:
|
|||
MOZ_ASSERT(mSource == aStream, "Invalid source stream");
|
||||
|
||||
const bool firstTime = !!!mBuffer.Length();
|
||||
const uint32_t numChannels = aInput.mChannelData.Length();
|
||||
const uint32_t numChannels = aInput.IsNull() ?
|
||||
mBuffer.Length() :
|
||||
aInput.mChannelData.Length();
|
||||
|
||||
bool playedBackAllLeftOvers = false;
|
||||
if (!mBuffer.IsEmpty() &&
|
||||
mLeftOverData == INT32_MIN &&
|
||||
aStream->AllInputsFinished()) {
|
||||
mLeftOverData = static_cast<int32_t>(mCurrentDelayTime * IdealAudioRate());
|
||||
|
||||
nsRefPtr<PlayingRefChanged> refchanged =
|
||||
new PlayingRefChanged(mDelayNode, PlayingRefChanged::ADDREF);
|
||||
NS_DispatchToMainThread(refchanged);
|
||||
} else if (mLeftOverData != INT32_MIN) {
|
||||
mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
|
||||
if (mLeftOverData <= 0) {
|
||||
mLeftOverData = INT32_MIN;
|
||||
playedBackAllLeftOvers = true;
|
||||
|
||||
nsRefPtr<PlayingRefChanged> refchanged =
|
||||
new PlayingRefChanged(mDelayNode, PlayingRefChanged::RELEASE);
|
||||
NS_DispatchToMainThread(refchanged);
|
||||
}
|
||||
}
|
||||
|
||||
if (!EnsureBuffer(numChannels)) {
|
||||
aOutput->SetNull(0);
|
||||
|
@ -134,7 +184,7 @@ public:
|
|||
|
||||
float* buffer = mBuffer[channel].Elements();
|
||||
const uint32_t bufferLength = mBuffer[channel].Length();
|
||||
const float* input = static_cast<const float*>(aInput.mChannelData[channel]);
|
||||
const float* input = static_cast<const float*>(aInput.mChannelData.SafeElementAt(channel));
|
||||
float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[channel]));
|
||||
|
||||
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
|
||||
|
@ -146,7 +196,9 @@ public:
|
|||
}
|
||||
|
||||
// Write the input sample to the correct location in our buffer
|
||||
buffer[writeIndex] = input[i];
|
||||
if (input) {
|
||||
buffer[writeIndex] = input[i];
|
||||
}
|
||||
|
||||
// Now, determine the correct read position. We adjust the read position to be
|
||||
// from currentDelayTime seconds in the past. We also interpolate the two input
|
||||
|
@ -183,10 +235,16 @@ public:
|
|||
mWriteIndex = writeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (playedBackAllLeftOvers) {
|
||||
// Delete our buffered data once we no longer need it
|
||||
mBuffer.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
AudioNodeStream* mSource;
|
||||
AudioNodeStream* mDestination;
|
||||
DelayNode& mDelayNode;
|
||||
AudioParamTimeline mDelay;
|
||||
// Maximum delay time in seconds
|
||||
double mMaxDelay;
|
||||
|
@ -195,6 +253,9 @@ public:
|
|||
// Write index for the buffer, to write the frames to the correct index of the buffer
|
||||
// given the current delay.
|
||||
uint32_t mWriteIndex;
|
||||
// How much data we have in our buffer which needs to be flushed out when our inputs
|
||||
// finish.
|
||||
int32_t mLeftOverData;
|
||||
// Current delay time, in seconds
|
||||
double mCurrentDelayTime;
|
||||
};
|
||||
|
@ -203,7 +264,7 @@ DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
|
|||
: AudioNode(aContext)
|
||||
, mDelay(new AudioParam(this, SendDelayToStream, 0.0f))
|
||||
{
|
||||
DelayNodeEngine* engine = new DelayNodeEngine(aContext->Destination());
|
||||
DelayNodeEngine* engine = new DelayNodeEngine(aContext->Destination(), *this);
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
|
|
|
@ -37,9 +37,11 @@ public:
|
|||
|
||||
private:
|
||||
static void SendDelayToStream(AudioNode* aNode);
|
||||
friend class DelayNodeEngine;
|
||||
|
||||
private:
|
||||
nsRefPtr<AudioParam> mDelay;
|
||||
SelfReference<DelayNode> mPlayingRef;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -180,7 +180,9 @@ PannerNode::PannerNode(AudioContext* aContext)
|
|||
|
||||
PannerNode::~PannerNode()
|
||||
{
|
||||
Context()->UnregisterPannerNode(this);
|
||||
if (Context()) {
|
||||
Context()->UnregisterPannerNode(this);
|
||||
}
|
||||
DestroyMediaStream();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ addLoadEvent(function() {
|
|||
is(destination.context, context, "Destination node has proper context");
|
||||
is(destination.numberOfInputs, 1, "Destination node has 1 inputs");
|
||||
is(destination.numberOfOutputs, 0, "Destination node has 0 outputs");
|
||||
ok(destination instanceof EventTarget, "AudioNodes must be EventTargets");
|
||||
|
||||
testWith(context, buffer, destination, function(source) {
|
||||
source.start(0);
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMSVGAnimatedTransformList.h"
|
||||
#include "DOMSVGTransformList.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGAttrTearoffTable.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformListBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static
|
||||
nsSVGAttrTearoffTable<SVGAnimatedTransformList,DOMSVGAnimatedTransformList>
|
||||
sSVGAnimatedTransformListTearoffTable;
|
||||
|
||||
NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGAnimatedTransformList, mElement)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGAnimatedTransformList)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGAnimatedTransformList)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGAnimatedTransformList)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
DOMSVGAnimatedTransformList::WrapObject(JSContext* aCx, JSObject* aScope)
|
||||
{
|
||||
return mozilla::dom::SVGAnimatedTransformListBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
already_AddRefed<DOMSVGTransformList>
|
||||
DOMSVGAnimatedTransformList::BaseVal()
|
||||
{
|
||||
if (!mBaseVal) {
|
||||
mBaseVal = new DOMSVGTransformList(this, InternalAList().GetBaseValue());
|
||||
}
|
||||
nsRefPtr<DOMSVGTransformList> baseVal = mBaseVal;
|
||||
return baseVal.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMSVGTransformList>
|
||||
DOMSVGAnimatedTransformList::AnimVal()
|
||||
{
|
||||
if (!mAnimVal) {
|
||||
mAnimVal = new DOMSVGTransformList(this, InternalAList().GetAnimValue());
|
||||
}
|
||||
nsRefPtr<DOMSVGTransformList> animVal = mAnimVal;
|
||||
return animVal.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<DOMSVGAnimatedTransformList>
|
||||
DOMSVGAnimatedTransformList::GetDOMWrapper(SVGAnimatedTransformList *aList,
|
||||
nsSVGElement *aElement)
|
||||
{
|
||||
nsRefPtr<DOMSVGAnimatedTransformList> wrapper =
|
||||
sSVGAnimatedTransformListTearoffTable.GetTearoff(aList);
|
||||
if (!wrapper) {
|
||||
wrapper = new DOMSVGAnimatedTransformList(aElement);
|
||||
sSVGAnimatedTransformListTearoffTable.AddTearoff(aList, wrapper);
|
||||
}
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
/* static */ DOMSVGAnimatedTransformList*
|
||||
DOMSVGAnimatedTransformList::GetDOMWrapperIfExists(
|
||||
SVGAnimatedTransformList *aList)
|
||||
{
|
||||
return sSVGAnimatedTransformListTearoffTable.GetTearoff(aList);
|
||||
}
|
||||
|
||||
DOMSVGAnimatedTransformList::~DOMSVGAnimatedTransformList()
|
||||
{
|
||||
// Script no longer has any references to us, to our base/animVal objects, or
|
||||
// to any of their list items.
|
||||
sSVGAnimatedTransformListTearoffTable.RemoveTearoff(&InternalAList());
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGAnimatedTransformList::InternalBaseValListWillChangeLengthTo(
|
||||
uint32_t aNewLength)
|
||||
{
|
||||
// When the number of items in our internal counterpart's baseVal changes,
|
||||
// we MUST keep our baseVal in sync. If we don't, script will either see a
|
||||
// list that is too short and be unable to access indexes that should be
|
||||
// valid, or else, MUCH WORSE, script will see a list that is too long and be
|
||||
// able to access "items" at indexes that are out of bounds (read/write to
|
||||
// bad memory)!!
|
||||
|
||||
nsRefPtr<DOMSVGAnimatedTransformList> kungFuDeathGrip;
|
||||
if (mBaseVal) {
|
||||
if (aNewLength < mBaseVal->LengthNoFlush()) {
|
||||
// InternalListLengthWillChange might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
}
|
||||
mBaseVal->InternalListLengthWillChange(aNewLength);
|
||||
}
|
||||
|
||||
// If our attribute is not animating, then our animVal mirrors our baseVal
|
||||
// and we must sync its length too. (If our attribute is animating, then the
|
||||
// SMIL engine takes care of calling InternalAnimValListWillChangeLengthTo()
|
||||
// if necessary.)
|
||||
|
||||
if (!IsAnimating()) {
|
||||
InternalAnimValListWillChangeLengthTo(aNewLength);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGAnimatedTransformList::InternalAnimValListWillChangeLengthTo(
|
||||
uint32_t aNewLength)
|
||||
{
|
||||
if (mAnimVal) {
|
||||
mAnimVal->InternalListLengthWillChange(aNewLength);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DOMSVGAnimatedTransformList::IsAnimating() const
|
||||
{
|
||||
return InternalAList().IsAnimating();
|
||||
}
|
||||
|
||||
SVGAnimatedTransformList&
|
||||
DOMSVGAnimatedTransformList::InternalAList()
|
||||
{
|
||||
return *mElement->GetAnimatedTransformList();
|
||||
}
|
||||
|
||||
const SVGAnimatedTransformList&
|
||||
DOMSVGAnimatedTransformList::InternalAList() const
|
||||
{
|
||||
return *mElement->GetAnimatedTransformList();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,130 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_DOMSVGANIMATEDTRANSFORMLIST_H__
|
||||
#define MOZILLA_DOMSVGANIMATEDTRANSFORMLIST_H__
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DOMSVGTransformList;
|
||||
class SVGAnimatedTransformList;
|
||||
|
||||
/**
|
||||
* Class DOMSVGAnimatedTransformList
|
||||
*
|
||||
* This class is used to create the DOM tearoff objects that wrap internal
|
||||
* SVGAnimatedTransformList objects.
|
||||
*
|
||||
* See the architecture comment in DOMSVGAnimatedLengthList.h (that's
|
||||
* LENGTH list). The comment for that class largly applies to this one too
|
||||
* and will go a long way to helping you understand the architecture here.
|
||||
*
|
||||
* This class is strongly intertwined with DOMSVGTransformList and
|
||||
* DOMSVGTransform.
|
||||
* Our DOMSVGTransformList base and anim vals are friends and take care of
|
||||
* nulling out our pointers to them when they die (making our pointers to them
|
||||
* true weak refs).
|
||||
*/
|
||||
class DOMSVGAnimatedTransformList MOZ_FINAL : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
friend class DOMSVGTransformList;
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGAnimatedTransformList)
|
||||
|
||||
/**
|
||||
* Factory method to create and return a DOMSVGAnimatedTransformList wrapper
|
||||
* for a given internal SVGAnimatedTransformList object. The factory takes
|
||||
* care of caching the object that it returns so that the same object can be
|
||||
* returned for the given SVGAnimatedTransformList each time it is requested.
|
||||
* The cached object is only removed from the cache when it is destroyed due
|
||||
* to there being no more references to it or to any of its descendant
|
||||
* objects. If that happens, any subsequent call requesting the DOM wrapper
|
||||
* for the SVGAnimatedTransformList will naturally result in a new
|
||||
* DOMSVGAnimatedTransformList being returned.
|
||||
*/
|
||||
static already_AddRefed<DOMSVGAnimatedTransformList>
|
||||
GetDOMWrapper(SVGAnimatedTransformList *aList, nsSVGElement *aElement);
|
||||
|
||||
/**
|
||||
* This method returns the DOMSVGAnimatedTransformList wrapper for an internal
|
||||
* SVGAnimatedTransformList object if it currently has a wrapper. If it does
|
||||
* not, then nullptr is returned.
|
||||
*/
|
||||
static DOMSVGAnimatedTransformList*
|
||||
GetDOMWrapperIfExists(SVGAnimatedTransformList *aList);
|
||||
|
||||
/**
|
||||
* Called by internal code to notify us when we need to sync the length of
|
||||
* our baseVal DOM list with its internal list. This is called just prior to
|
||||
* the length of the internal baseVal list being changed so that any DOM list
|
||||
* items that need to be removed from the DOM list can first get their values
|
||||
* from their internal counterpart.
|
||||
*
|
||||
* The only time this method could fail is on OOM when trying to increase the
|
||||
* length of the DOM list. If that happens then this method simply clears the
|
||||
* list and returns. Callers just proceed as normal, and we simply accept
|
||||
* that the DOM list will be empty (until successfully set to a new value).
|
||||
*/
|
||||
void InternalBaseValListWillChangeLengthTo(uint32_t aNewLength);
|
||||
void InternalAnimValListWillChangeLengthTo(uint32_t aNewLength);
|
||||
|
||||
/**
|
||||
* Returns true if our attribute is animating (in which case our animVal is
|
||||
* not simply a mirror of our baseVal).
|
||||
*/
|
||||
bool IsAnimating() const;
|
||||
|
||||
// WebIDL
|
||||
nsSVGElement* GetParentObject() const { return mElement; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) MOZ_OVERRIDE;
|
||||
// These aren't weak refs because mBaseVal and mAnimVal are weak
|
||||
already_AddRefed<DOMSVGTransformList> BaseVal();
|
||||
already_AddRefed<DOMSVGTransformList> AnimVal();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Only our static GetDOMWrapper() factory method may create objects of our
|
||||
* type.
|
||||
*/
|
||||
explicit DOMSVGAnimatedTransformList(nsSVGElement *aElement)
|
||||
: mBaseVal(nullptr)
|
||||
, mAnimVal(nullptr)
|
||||
, mElement(aElement)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
~DOMSVGAnimatedTransformList();
|
||||
|
||||
/// Get a reference to this DOM wrapper object's internal counterpart.
|
||||
SVGAnimatedTransformList& InternalAList();
|
||||
const SVGAnimatedTransformList& InternalAList() const;
|
||||
|
||||
// Weak refs to our DOMSVGTransformList baseVal/animVal objects. These objects
|
||||
// are friends and take care of clearing these pointers when they die, making
|
||||
// these true weak references.
|
||||
DOMSVGTransformList *mBaseVal;
|
||||
DOMSVGTransformList *mAnimVal;
|
||||
|
||||
// Strong ref to our element to keep it alive. We hold this not only for
|
||||
// ourself, but also for our base/animVal and all of their items.
|
||||
nsRefPtr<nsSVGElement> mElement;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_DOMSVGANIMATEDTRANSFORMLIST_H__
|
|
@ -6,7 +6,7 @@
|
|||
#include "DOMSVGTransformList.h"
|
||||
#include "mozilla/dom/SVGTransform.h"
|
||||
#include "mozilla/dom/SVGMatrix.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/SVGTransformListBinding.h"
|
||||
|
@ -36,7 +36,7 @@ namespace mozilla {
|
|||
using namespace dom;
|
||||
|
||||
// We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
|
||||
// clear our DOMSVGAnimatedTransformList's weak ref to us to be safe. (The other
|
||||
// clear our SVGAnimatedTransformList's weak ref to us to be safe. (The other
|
||||
// option would be to not unlink and rely on the breaking of the other edges in
|
||||
// the cycle, as NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransformList)
|
||||
|
@ -116,7 +116,7 @@ DOMSVGTransformList::InternalListLengthWillChange(uint32_t aNewLength)
|
|||
SVGTransformList&
|
||||
DOMSVGTransformList::InternalList() const
|
||||
{
|
||||
SVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
|
||||
nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
|
||||
return IsAnimValList() && alist->mAnimVal ?
|
||||
*alist->mAnimVal :
|
||||
alist->mBaseVal;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef MOZILLA_DOMSVGTRANSFORMLIST_H__
|
||||
#define MOZILLA_DOMSVGTRANSFORMLIST_H__
|
||||
|
||||
#include "DOMSVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformList.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDebug.h"
|
||||
|
@ -31,7 +31,7 @@ class SVGTransform;
|
|||
* This class is used to create the DOM tearoff objects that wrap internal
|
||||
* SVGTransformList objects.
|
||||
*
|
||||
* See the architecture comment in DOMSVGAnimatedTransformList.h.
|
||||
* See the architecture comment in SVGAnimatedTransformList.h.
|
||||
*/
|
||||
class DOMSVGTransformList MOZ_FINAL : public nsISupports,
|
||||
public nsWrapperCache
|
||||
|
@ -42,7 +42,7 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGTransformList)
|
||||
|
||||
DOMSVGTransformList(DOMSVGAnimatedTransformList *aAList,
|
||||
DOMSVGTransformList(dom::SVGAnimatedTransformList *aAList,
|
||||
const SVGTransformList &aInternalList)
|
||||
: mAList(aAList)
|
||||
{
|
||||
|
@ -160,7 +160,7 @@ private:
|
|||
// of clearing our pointer to them when they die.
|
||||
FallibleTArray<dom::SVGTransform*> mItems;
|
||||
|
||||
nsRefPtr<DOMSVGAnimatedTransformList> mAList;
|
||||
nsRefPtr<dom::SVGAnimatedTransformList> mAList;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -17,7 +17,6 @@ FAIL_ON_WARNINGS = 1
|
|||
CPPSRCS = \
|
||||
DOMSVGAnimatedLengthList.cpp \
|
||||
DOMSVGAnimatedNumberList.cpp \
|
||||
DOMSVGAnimatedTransformList.cpp \
|
||||
DOMSVGLength.cpp \
|
||||
DOMSVGLengthList.cpp \
|
||||
DOMSVGNumber.cpp \
|
||||
|
@ -31,6 +30,7 @@ CPPSRCS = \
|
|||
nsDOMSVGZoomEvent.cpp \
|
||||
nsISVGPoint.cpp \
|
||||
nsSVGAngle.cpp \
|
||||
nsSVGAnimatedTransformList.cpp \
|
||||
nsSVGBoolean.cpp \
|
||||
nsSVGClass.cpp \
|
||||
nsSVGDataParser.cpp \
|
||||
|
@ -172,12 +172,14 @@ EXPORTS = \
|
|||
EXPORTS_NAMESPACES = mozilla/dom
|
||||
|
||||
EXPORTS_mozilla/dom = \
|
||||
nsSVGAnimatedTransformList.h \
|
||||
SVGAElement.h \
|
||||
SVGAltGlyphElement.h \
|
||||
SVGAngle.h \
|
||||
SVGAnimatedAngle.h \
|
||||
SVGAnimatedBoolean.h \
|
||||
SVGAnimatedLength.h \
|
||||
SVGAnimatedTransformList.h \
|
||||
SVGAnimateElement.h \
|
||||
SVGAnimateTransformElement.h \
|
||||
SVGAnimateMotionElement.h \
|
||||
|
|
|
@ -3,318 +3,136 @@
|
|||
* 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 "SVGAnimatedTransformList.h"
|
||||
#include "DOMSVGAnimatedTransformList.h"
|
||||
|
||||
#include "mozilla/dom/SVGAnimationElement.h"
|
||||
#include "nsSMILValue.h"
|
||||
#include "prdtoa.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "nsSVGTransform.h"
|
||||
#include "SVGTransformListSMILType.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformList.h"
|
||||
#include "DOMSVGTransformList.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "nsSVGAttrTearoffTable.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformListBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
nsresult
|
||||
SVGAnimatedTransformList::SetBaseValueString(const nsAString& aValue)
|
||||
static
|
||||
nsSVGAttrTearoffTable<nsSVGAnimatedTransformList, SVGAnimatedTransformList>
|
||||
sSVGAnimatedTransformListTearoffTable;
|
||||
|
||||
NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedTransformList, mElement)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAnimatedTransformList, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAnimatedTransformList, Release)
|
||||
|
||||
JSObject*
|
||||
SVGAnimatedTransformList::WrapObject(JSContext* aCx, JSObject* aScope)
|
||||
{
|
||||
SVGTransformList newBaseValue;
|
||||
nsresult rv = newBaseValue.SetValueFromString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
DOMSVGAnimatedTransformList *domWrapper =
|
||||
DOMSVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// We must send this notification *before* changing mBaseVal! If the length
|
||||
// of our baseVal is being reduced, our baseVal's DOM wrapper list may have
|
||||
// to remove DOM items from itself, and any removed DOM items need to copy
|
||||
// their internal counterpart values *before* we change them.
|
||||
//
|
||||
domWrapper->InternalBaseValListWillChangeLengthTo(newBaseValue.Length());
|
||||
}
|
||||
|
||||
// We don't need to call DidChange* here - we're only called by
|
||||
// nsSVGElement::ParseAttribute under Element::SetAttr,
|
||||
// which takes care of notifying.
|
||||
|
||||
rv = mBaseVal.CopyFrom(newBaseValue);
|
||||
if (NS_FAILED(rv) && domWrapper) {
|
||||
// Attempting to increase mBaseVal's length failed - reduce domWrapper
|
||||
// back to the same length:
|
||||
domWrapper->InternalBaseValListWillChangeLengthTo(mBaseVal.Length());
|
||||
} else {
|
||||
mIsAttrSet = true;
|
||||
}
|
||||
return rv;
|
||||
return SVGAnimatedTransformListBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedTransformList::ClearBaseValue()
|
||||
//----------------------------------------------------------------------
|
||||
already_AddRefed<DOMSVGTransformList>
|
||||
SVGAnimatedTransformList::BaseVal()
|
||||
{
|
||||
DOMSVGAnimatedTransformList *domWrapper =
|
||||
DOMSVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// We must send this notification *before* changing mBaseVal! (See above.)
|
||||
domWrapper->InternalBaseValListWillChangeLengthTo(0);
|
||||
if (!mBaseVal) {
|
||||
mBaseVal = new DOMSVGTransformList(this, InternalAList().GetBaseValue());
|
||||
}
|
||||
mBaseVal.Clear();
|
||||
mIsAttrSet = false;
|
||||
// Caller notifies
|
||||
nsRefPtr<DOMSVGTransformList> baseVal = mBaseVal;
|
||||
return baseVal.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGAnimatedTransformList::SetAnimValue(const SVGTransformList& aValue,
|
||||
nsSVGElement *aElement)
|
||||
already_AddRefed<DOMSVGTransformList>
|
||||
SVGAnimatedTransformList::AnimVal()
|
||||
{
|
||||
DOMSVGAnimatedTransformList *domWrapper =
|
||||
DOMSVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// A new animation may totally change the number of items in the animVal
|
||||
// list, replacing what was essentially a mirror of the baseVal list, or
|
||||
// else replacing and overriding an existing animation. When this happens
|
||||
// we must try and keep our animVal's DOM wrapper in sync (see the comment
|
||||
// in DOMSVGAnimatedTransformList::InternalBaseValListWillChangeLengthTo).
|
||||
//
|
||||
// It's not possible for us to reliably distinguish between calls to this
|
||||
// method that are setting a new sample for an existing animation, and
|
||||
// calls that are setting the first sample of an animation that will
|
||||
// override an existing animation. Happily it's cheap to just blindly
|
||||
// notify our animVal's DOM wrapper of its internal counterpart's new value
|
||||
// each time this method is called, so that's what we do.
|
||||
//
|
||||
// Note that we must send this notification *before* setting or changing
|
||||
// mAnimVal! (See the comment in SetBaseValueString above.)
|
||||
//
|
||||
domWrapper->InternalAnimValListWillChangeLengthTo(aValue.Length());
|
||||
}
|
||||
if (!mAnimVal) {
|
||||
mAnimVal = new SVGTransformList();
|
||||
mAnimVal = new DOMSVGTransformList(this, InternalAList().GetAnimValue());
|
||||
}
|
||||
nsresult rv = mAnimVal->CopyFrom(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
// OOM. We clear the animation, and, importantly, ClearAnimValue() ensures
|
||||
// that mAnimVal and its DOM wrapper (if any) will have the same length!
|
||||
ClearAnimValue(aElement);
|
||||
return rv;
|
||||
nsRefPtr<DOMSVGTransformList> animVal = mAnimVal;
|
||||
return animVal.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<SVGAnimatedTransformList>
|
||||
SVGAnimatedTransformList::GetDOMWrapper(nsSVGAnimatedTransformList *aList,
|
||||
nsSVGElement *aElement)
|
||||
{
|
||||
nsRefPtr<SVGAnimatedTransformList> wrapper =
|
||||
sSVGAnimatedTransformListTearoffTable.GetTearoff(aList);
|
||||
if (!wrapper) {
|
||||
wrapper = new SVGAnimatedTransformList(aElement);
|
||||
sSVGAnimatedTransformListTearoffTable.AddTearoff(aList, wrapper);
|
||||
}
|
||||
aElement->DidAnimateTransformList();
|
||||
return NS_OK;
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
/* static */ SVGAnimatedTransformList*
|
||||
SVGAnimatedTransformList::GetDOMWrapperIfExists(
|
||||
nsSVGAnimatedTransformList *aList)
|
||||
{
|
||||
return sSVGAnimatedTransformListTearoffTable.GetTearoff(aList);
|
||||
}
|
||||
|
||||
SVGAnimatedTransformList::~SVGAnimatedTransformList()
|
||||
{
|
||||
// Script no longer has any references to us, to our base/animVal objects, or
|
||||
// to any of their list items.
|
||||
sSVGAnimatedTransformListTearoffTable.RemoveTearoff(&InternalAList());
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedTransformList::ClearAnimValue(nsSVGElement *aElement)
|
||||
SVGAnimatedTransformList::InternalBaseValListWillChangeLengthTo(
|
||||
uint32_t aNewLength)
|
||||
{
|
||||
DOMSVGAnimatedTransformList *domWrapper =
|
||||
DOMSVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// When all animation ends, animVal simply mirrors baseVal, which may have
|
||||
// a different number of items to the last active animated value. We must
|
||||
// keep the length of our animVal's DOM wrapper list in sync, and again we
|
||||
// must do that before touching mAnimVal. See comments above.
|
||||
//
|
||||
domWrapper->InternalAnimValListWillChangeLengthTo(mBaseVal.Length());
|
||||
// When the number of items in our internal counterpart's baseVal changes,
|
||||
// we MUST keep our baseVal in sync. If we don't, script will either see a
|
||||
// list that is too short and be unable to access indexes that should be
|
||||
// valid, or else, MUCH WORSE, script will see a list that is too long and be
|
||||
// able to access "items" at indexes that are out of bounds (read/write to
|
||||
// bad memory)!!
|
||||
|
||||
nsRefPtr<SVGAnimatedTransformList> kungFuDeathGrip;
|
||||
if (mBaseVal) {
|
||||
if (aNewLength < mBaseVal->LengthNoFlush()) {
|
||||
// InternalListLengthWillChange might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
}
|
||||
mBaseVal->InternalListLengthWillChange(aNewLength);
|
||||
}
|
||||
|
||||
// If our attribute is not animating, then our animVal mirrors our baseVal
|
||||
// and we must sync its length too. (If our attribute is animating, then the
|
||||
// SMIL engine takes care of calling InternalAnimValListWillChangeLengthTo()
|
||||
// if necessary.)
|
||||
|
||||
if (!IsAnimating()) {
|
||||
InternalAnimValListWillChangeLengthTo(aNewLength);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedTransformList::InternalAnimValListWillChangeLengthTo(
|
||||
uint32_t aNewLength)
|
||||
{
|
||||
if (mAnimVal) {
|
||||
mAnimVal->InternalListLengthWillChange(aNewLength);
|
||||
}
|
||||
mAnimVal = nullptr;
|
||||
aElement->DidAnimateTransformList();
|
||||
}
|
||||
|
||||
bool
|
||||
SVGAnimatedTransformList::IsExplicitlySet() const
|
||||
SVGAnimatedTransformList::IsAnimating() const
|
||||
{
|
||||
// Like other methods of this name, we need to know when a transform value has
|
||||
// been explicitly set.
|
||||
//
|
||||
// There are three ways an animated list can become set:
|
||||
// 1) Markup -- we set mIsAttrSet to true on any successful call to
|
||||
// SetBaseValueString and clear it on ClearBaseValue (as called by
|
||||
// nsSVGElement::UnsetAttr or a failed nsSVGElement::ParseAttribute)
|
||||
// 2) DOM call -- simply fetching the baseVal doesn't mean the transform value
|
||||
// has been set. It is set if that baseVal has one or more transforms in
|
||||
// the list.
|
||||
// 3) Animation -- which will cause the mAnimVal member to be allocated
|
||||
return mIsAttrSet || !mBaseVal.IsEmpty() || mAnimVal;
|
||||
return InternalAList().IsAnimating();
|
||||
}
|
||||
|
||||
nsISMILAttr*
|
||||
SVGAnimatedTransformList::ToSMILAttr(nsSVGElement* aSVGElement)
|
||||
nsSVGAnimatedTransformList&
|
||||
SVGAnimatedTransformList::InternalAList()
|
||||
{
|
||||
return new SMILAnimatedTransformList(this, aSVGElement);
|
||||
return *mElement->GetAnimatedTransformList();
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGAnimatedTransformList::SMILAnimatedTransformList::ValueFromString(
|
||||
const nsAString& aStr,
|
||||
const dom::SVGAnimationElement* aSrcElement,
|
||||
nsSMILValue& aValue,
|
||||
bool& aPreventCachingOfSandwich) const
|
||||
const nsSVGAnimatedTransformList&
|
||||
SVGAnimatedTransformList::InternalAList() const
|
||||
{
|
||||
NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE);
|
||||
NS_ABORT_IF_FALSE(aValue.IsNull(),
|
||||
"aValue should have been cleared before calling ValueFromString");
|
||||
|
||||
const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type);
|
||||
const nsIAtom* transformType = nsGkAtoms::translate; // default val
|
||||
if (typeAttr) {
|
||||
if (typeAttr->Type() != nsAttrValue::eAtom) {
|
||||
// Recognized values of |type| are parsed as an atom -- so if we have
|
||||
// something other than an atom, then we know already our |type| is
|
||||
// invalid.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
transformType = typeAttr->GetAtomValue();
|
||||
}
|
||||
|
||||
ParseValue(aStr, transformType, aValue);
|
||||
aPreventCachingOfSandwich = false;
|
||||
return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedTransformList::SMILAnimatedTransformList::ParseValue(
|
||||
const nsAString& aSpec,
|
||||
const nsIAtom* aTransformType,
|
||||
nsSMILValue& aResult)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aResult.IsNull(), "Unexpected type for SMIL value");
|
||||
|
||||
// nsSVGSMILTransform constructor should be expecting array with 3 params
|
||||
PR_STATIC_ASSERT(SVGTransformSMILData::NUM_SIMPLE_PARAMS == 3);
|
||||
|
||||
float params[3] = { 0.f };
|
||||
int32_t numParsed = ParseParameterList(aSpec, params, 3);
|
||||
uint16_t transformType;
|
||||
|
||||
if (aTransformType == nsGkAtoms::translate) {
|
||||
// tx [ty=0]
|
||||
if (numParsed != 1 && numParsed != 2)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_TRANSLATE;
|
||||
} else if (aTransformType == nsGkAtoms::scale) {
|
||||
// sx [sy=sx]
|
||||
if (numParsed != 1 && numParsed != 2)
|
||||
return;
|
||||
if (numParsed == 1) {
|
||||
params[1] = params[0];
|
||||
}
|
||||
transformType = SVG_TRANSFORM_SCALE;
|
||||
} else if (aTransformType == nsGkAtoms::rotate) {
|
||||
// r [cx=0 cy=0]
|
||||
if (numParsed != 1 && numParsed != 3)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_ROTATE;
|
||||
} else if (aTransformType == nsGkAtoms::skewX) {
|
||||
// x-angle
|
||||
if (numParsed != 1)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_SKEWX;
|
||||
} else if (aTransformType == nsGkAtoms::skewY) {
|
||||
// y-angle
|
||||
if (numParsed != 1)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_SKEWY;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
nsSMILValue val(&SVGTransformListSMILType::sSingleton);
|
||||
SVGTransformSMILData transform(transformType, params);
|
||||
if (NS_FAILED(SVGTransformListSMILType::AppendTransform(transform, val))) {
|
||||
return; // OOM
|
||||
}
|
||||
|
||||
// Success! Populate our outparam with parsed value.
|
||||
aResult.Swap(val);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
inline void
|
||||
SkipWsp(nsACString::const_iterator& aIter,
|
||||
const nsACString::const_iterator& aIterEnd)
|
||||
{
|
||||
while (aIter != aIterEnd && IsSVGWhitespace(*aIter))
|
||||
++aIter;
|
||||
}
|
||||
} // end anonymous namespace block
|
||||
|
||||
int32_t
|
||||
SVGAnimatedTransformList::SMILAnimatedTransformList::ParseParameterList(
|
||||
const nsAString& aSpec,
|
||||
float* aVars,
|
||||
int32_t aNVars)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 spec(aSpec);
|
||||
|
||||
nsACString::const_iterator start, end;
|
||||
spec.BeginReading(start);
|
||||
spec.EndReading(end);
|
||||
|
||||
SkipWsp(start, end);
|
||||
|
||||
int numArgsFound = 0;
|
||||
|
||||
while (start != end) {
|
||||
char const *arg = start.get();
|
||||
char *argend;
|
||||
float f = float(PR_strtod(arg, &argend));
|
||||
if (arg == argend || argend > end.get() || !NS_finite(f))
|
||||
return -1;
|
||||
|
||||
if (numArgsFound < aNVars) {
|
||||
aVars[numArgsFound] = f;
|
||||
}
|
||||
|
||||
start.advance(argend - arg);
|
||||
numArgsFound++;
|
||||
|
||||
SkipWsp(start, end);
|
||||
if (*start == ',') {
|
||||
++start;
|
||||
SkipWsp(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
return numArgsFound;
|
||||
}
|
||||
|
||||
nsSMILValue
|
||||
SVGAnimatedTransformList::SMILAnimatedTransformList::GetBaseValue() const
|
||||
{
|
||||
// To benefit from Return Value Optimization and avoid copy constructor calls
|
||||
// due to our use of return-by-value, we must return the exact same object
|
||||
// from ALL return points. This function must only return THIS variable:
|
||||
nsSMILValue val(&SVGTransformListSMILType::sSingleton);
|
||||
if (!SVGTransformListSMILType::AppendTransforms(mVal->mBaseVal, val)) {
|
||||
val = nsSMILValue();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGAnimatedTransformList::SMILAnimatedTransformList::SetAnimValue(
|
||||
const nsSMILValue& aNewAnimValue)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(
|
||||
aNewAnimValue.mType == &SVGTransformListSMILType::sSingleton,
|
||||
"Unexpected type to assign animated value");
|
||||
SVGTransformList animVal;
|
||||
if (!SVGTransformListSMILType::GetTransforms(aNewAnimValue,
|
||||
animVal.mItems)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return mVal->SetAnimValue(animVal, mElement);
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedTransformList::SMILAnimatedTransformList::ClearAnimValue()
|
||||
{
|
||||
if (mVal->mAnimVal) {
|
||||
mVal->ClearAnimValue(mElement);
|
||||
}
|
||||
return *mElement->GetAnimatedTransformList();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -4,138 +4,129 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_SVGANIMATEDTRANSFORMLIST_H__
|
||||
#define MOZILLA_SVGANIMATEDTRANSFORMLIST_H__
|
||||
#ifndef mozilla_dom_SVGAnimatedTransformList_h
|
||||
#define mozilla_dom_SVGAnimatedTransformList_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsISMILAttr.h"
|
||||
#include "SVGTransformList.h"
|
||||
|
||||
class nsIAtom;
|
||||
class nsSMILValue;
|
||||
class nsSVGElement;
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DOMSVGTransformList;
|
||||
class nsSVGAnimatedTransformList;
|
||||
|
||||
namespace dom {
|
||||
class SVGAnimationElement;
|
||||
class SVGTransform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class SVGAnimatedTransformList
|
||||
*
|
||||
* This class is very different to the SVG DOM interface of the same name found
|
||||
* in the SVG specification. This is a lightweight internal class - see
|
||||
* DOMSVGAnimatedTransformList for the heavier DOM class that wraps instances of
|
||||
* this class and implements the SVG specification's SVGAnimatedTransformList
|
||||
* DOM interface.
|
||||
* This class is used to create the DOM tearoff objects that wrap internal
|
||||
* nsSVGAnimatedTransformList objects.
|
||||
*
|
||||
* Except where noted otherwise, this class' methods take care of keeping the
|
||||
* appropriate DOM wrappers in sync (see the comment in
|
||||
* DOMSVGAnimatedTransformList::InternalBaseValListWillChangeTo) so that their
|
||||
* consumers don't need to concern themselves with that.
|
||||
* See the architecture comment in DOMSVGAnimatedLengthList.h (that's
|
||||
* LENGTH list). The comment for that class largly applies to this one too
|
||||
* and will go a long way to helping you understand the architecture here.
|
||||
*
|
||||
* This class is strongly intertwined with DOMSVGTransformList and
|
||||
* DOMSVGTransform.
|
||||
* Our DOMSVGTransformList base and anim vals are friends and take care of
|
||||
* nulling out our pointers to them when they die (making our pointers to them
|
||||
* true weak refs).
|
||||
*/
|
||||
class SVGAnimatedTransformList
|
||||
class SVGAnimatedTransformList MOZ_FINAL : public nsWrapperCache
|
||||
{
|
||||
// friends so that they can get write access to mBaseVal
|
||||
friend class dom::SVGTransform;
|
||||
friend class DOMSVGTransformList;
|
||||
friend class mozilla::DOMSVGTransformList;
|
||||
|
||||
public:
|
||||
SVGAnimatedTransformList() : mIsAttrSet(false) { }
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedTransformList)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedTransformList)
|
||||
|
||||
/**
|
||||
* Because it's so important that mBaseVal and its DOMSVGTransformList wrapper
|
||||
* (if any) be kept in sync (see the comment in
|
||||
* DOMSVGAnimatedTransformList::InternalBaseValListWillChangeTo), this method
|
||||
* returns a const reference. Only our friend classes may get mutable
|
||||
* references to mBaseVal.
|
||||
* Factory method to create and return a SVGAnimatedTransformList wrapper
|
||||
* for a given internal nsSVGAnimatedTransformList object. The factory takes
|
||||
* care of caching the object that it returns so that the same object can be
|
||||
* returned for the given nsSVGAnimatedTransformList each time it is requested.
|
||||
* The cached object is only removed from the cache when it is destroyed due
|
||||
* to there being no more references to it or to any of its descendant
|
||||
* objects. If that happens, any subsequent call requesting the DOM wrapper
|
||||
* for the nsSVGAnimatedTransformList will naturally result in a new
|
||||
* SVGAnimatedTransformList being returned.
|
||||
*/
|
||||
const SVGTransformList& GetBaseValue() const {
|
||||
return mBaseVal;
|
||||
}
|
||||
|
||||
nsresult SetBaseValueString(const nsAString& aValue);
|
||||
|
||||
void ClearBaseValue();
|
||||
|
||||
const SVGTransformList& GetAnimValue() const {
|
||||
return mAnimVal ? *mAnimVal : mBaseVal;
|
||||
}
|
||||
|
||||
nsresult SetAnimValue(const SVGTransformList& aNewAnimValue,
|
||||
nsSVGElement *aElement);
|
||||
|
||||
void ClearAnimValue(nsSVGElement *aElement);
|
||||
static already_AddRefed<SVGAnimatedTransformList>
|
||||
GetDOMWrapper(nsSVGAnimatedTransformList *aList, nsSVGElement *aElement);
|
||||
|
||||
/**
|
||||
* Returns true if the corresponding transform attribute is set (or animated)
|
||||
* to a valid value. Unlike HasTransform it will return true for an empty
|
||||
* transform.
|
||||
* This method returns the SVGAnimatedTransformList wrapper for an internal
|
||||
* nsSVGAnimatedTransformList object if it currently has a wrapper. If it does
|
||||
* not, then nullptr is returned.
|
||||
*/
|
||||
bool IsExplicitlySet() const;
|
||||
static SVGAnimatedTransformList*
|
||||
GetDOMWrapperIfExists(nsSVGAnimatedTransformList *aList);
|
||||
|
||||
/**
|
||||
* Returns true if the corresponding transform attribute is set (or animated)
|
||||
* to a valid value, such that we have at least one transform in our list.
|
||||
* Returns false otherwise (e.g. if the transform attribute is missing or empty
|
||||
* or invalid).
|
||||
* Called by internal code to notify us when we need to sync the length of
|
||||
* our baseVal DOM list with its internal list. This is called just prior to
|
||||
* the length of the internal baseVal list being changed so that any DOM list
|
||||
* items that need to be removed from the DOM list can first get their values
|
||||
* from their internal counterpart.
|
||||
*
|
||||
* The only time this method could fail is on OOM when trying to increase the
|
||||
* length of the DOM list. If that happens then this method simply clears the
|
||||
* list and returns. Callers just proceed as normal, and we simply accept
|
||||
* that the DOM list will be empty (until successfully set to a new value).
|
||||
*/
|
||||
bool HasTransform() const
|
||||
{ return (mAnimVal && !mAnimVal->IsEmpty()) || !mBaseVal.IsEmpty(); }
|
||||
void InternalBaseValListWillChangeLengthTo(uint32_t aNewLength);
|
||||
void InternalAnimValListWillChangeLengthTo(uint32_t aNewLength);
|
||||
|
||||
bool IsAnimating() const {
|
||||
return !!mAnimVal;
|
||||
}
|
||||
/**
|
||||
* Returns true if our attribute is animating (in which case our animVal is
|
||||
* not simply a mirror of our baseVal).
|
||||
*/
|
||||
bool IsAnimating() const;
|
||||
|
||||
/// Callers own the returned nsISMILAttr
|
||||
nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
|
||||
// WebIDL
|
||||
nsSVGElement* GetParentObject() const { return mElement; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) MOZ_OVERRIDE;
|
||||
// These aren't weak refs because mBaseVal and mAnimVal are weak
|
||||
already_AddRefed<DOMSVGTransformList> BaseVal();
|
||||
already_AddRefed<DOMSVGTransformList> AnimVal();
|
||||
|
||||
private:
|
||||
|
||||
// mAnimVal is a pointer to allow us to determine if we're being animated or
|
||||
// not. Making it a non-pointer member and using mAnimVal.IsEmpty() to check
|
||||
// if we're animating is not an option, since that would break animation *to*
|
||||
// the empty string (<set to="">).
|
||||
|
||||
SVGTransformList mBaseVal;
|
||||
nsAutoPtr<SVGTransformList> mAnimVal;
|
||||
bool mIsAttrSet;
|
||||
|
||||
struct SMILAnimatedTransformList : public nsISMILAttr
|
||||
/**
|
||||
* Only our static GetDOMWrapper() factory method may create objects of our
|
||||
* type.
|
||||
*/
|
||||
explicit SVGAnimatedTransformList(nsSVGElement *aElement)
|
||||
: mBaseVal(nullptr)
|
||||
, mAnimVal(nullptr)
|
||||
, mElement(aElement)
|
||||
{
|
||||
public:
|
||||
SMILAnimatedTransformList(SVGAnimatedTransformList* aVal,
|
||||
nsSVGElement* aSVGElement)
|
||||
: mVal(aVal)
|
||||
, mElement(aSVGElement)
|
||||
{}
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
// nsISMILAttr methods
|
||||
virtual nsresult ValueFromString(const nsAString& aStr,
|
||||
const dom::SVGAnimationElement* aSrcElement,
|
||||
nsSMILValue& aValue,
|
||||
bool& aPreventCachingOfSandwich) const;
|
||||
virtual nsSMILValue GetBaseValue() const;
|
||||
virtual void ClearAnimValue();
|
||||
virtual nsresult SetAnimValue(const nsSMILValue& aValue);
|
||||
~SVGAnimatedTransformList();
|
||||
|
||||
protected:
|
||||
static void ParseValue(const nsAString& aSpec,
|
||||
const nsIAtom* aTransformType,
|
||||
nsSMILValue& aResult);
|
||||
static int32_t ParseParameterList(const nsAString& aSpec, float* aVars,
|
||||
int32_t aNVars);
|
||||
/// Get a reference to this DOM wrapper object's internal counterpart.
|
||||
nsSVGAnimatedTransformList& InternalAList();
|
||||
const nsSVGAnimatedTransformList& InternalAList() const;
|
||||
|
||||
// These will stay alive because a nsISMILAttr only lives as long
|
||||
// as the Compositing step, and DOM elements don't get a chance to
|
||||
// die during that.
|
||||
SVGAnimatedTransformList* mVal;
|
||||
nsSVGElement* mElement;
|
||||
};
|
||||
// Weak refs to our DOMSVGTransformList baseVal/animVal objects. These objects
|
||||
// are friends and take care of clearing these pointers when they die, making
|
||||
// these true weak references.
|
||||
DOMSVGTransformList *mBaseVal;
|
||||
DOMSVGTransformList *mAnimVal;
|
||||
|
||||
// Strong ref to our element to keep it alive. We hold this not only for
|
||||
// ourself, but also for our base/animVal and all of their items.
|
||||
nsRefPtr<nsSVGElement> mElement;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_SVGANIMATEDTRANSFORMLIST_H__
|
||||
#endif // mozilla_dom_SVGAnimatedTransformList_h
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "SVGFragmentIdentifier.h"
|
||||
#include "mozilla/dom/SVGSVGElement.h"
|
||||
#include "mozilla/dom/SVGViewElement.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -156,7 +156,7 @@ SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString &aViewSpec,
|
|||
}
|
||||
preserveAspectRatioFound = true;
|
||||
} else if (IsMatchingParameter(token, NS_LITERAL_STRING("transform"))) {
|
||||
SVGAnimatedTransformList transforms;
|
||||
nsSVGAnimatedTransformList transforms;
|
||||
if (transformFound ||
|
||||
NS_FAILED(transforms.SetBaseValueString(params))) {
|
||||
return false;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "mozilla/dom/SVGGradientElement.h"
|
||||
|
||||
#include "DOMSVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGRadialGradientElementBinding.h"
|
||||
#include "mozilla/dom/SVGLinearGradientElementBinding.h"
|
||||
#include "mozilla/Util.h"
|
||||
|
@ -78,12 +78,12 @@ SVGGradientElement::GradientUnits()
|
|||
}
|
||||
|
||||
/* readonly attribute SVGAnimatedTransformList gradientTransform; */
|
||||
already_AddRefed<DOMSVGAnimatedTransformList>
|
||||
already_AddRefed<SVGAnimatedTransformList>
|
||||
SVGGradientElement::GradientTransform()
|
||||
{
|
||||
// We're creating a DOM wrapper, so we must tell GetAnimatedTransformList
|
||||
// to allocate the SVGAnimatedTransformList if it hasn't already done so:
|
||||
return DOMSVGAnimatedTransformList::GetDOMWrapper(
|
||||
return SVGAnimatedTransformList::GetDOMWrapper(
|
||||
GetAnimatedTransformList(DO_ALLOCATE), this);
|
||||
}
|
||||
|
||||
|
@ -172,11 +172,11 @@ SVGLinearGradientElement::Y2()
|
|||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
SVGAnimatedTransformList*
|
||||
nsSVGAnimatedTransformList*
|
||||
SVGGradientElement::GetAnimatedTransformList(uint32_t aFlags)
|
||||
{
|
||||
if (!mGradientTransform && (aFlags & DO_ALLOCATE)) {
|
||||
mGradientTransform = new SVGAnimatedTransformList();
|
||||
mGradientTransform = new nsSVGAnimatedTransformList();
|
||||
}
|
||||
return mGradientTransform;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
#ifndef __NS_SVGGRADIENTELEMENT_H__
|
||||
#define __NS_SVGGRADIENTELEMENT_H__
|
||||
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsSVGLength2.h"
|
||||
#include "nsSVGEnum.h"
|
||||
#include "nsSVGString.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
|
||||
static const unsigned short SVG_SPREADMETHOD_UNKNOWN = 0;
|
||||
static const unsigned short SVG_SPREADMETHOD_PAD = 1;
|
||||
|
@ -29,11 +29,10 @@ NS_NewSVGRadialGradientElement(nsIContent** aResult,
|
|||
already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DOMSVGAnimatedTransformList;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class SVGAnimatedTransformList;
|
||||
|
||||
//--------------------- Gradients------------------------
|
||||
|
||||
typedef nsSVGElement SVGGradientElementBase;
|
||||
|
@ -53,7 +52,7 @@ public:
|
|||
// nsIContent
|
||||
NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
|
||||
|
||||
virtual SVGAnimatedTransformList*
|
||||
virtual nsSVGAnimatedTransformList*
|
||||
GetAnimatedTransformList(uint32_t aFlags = 0);
|
||||
virtual nsIAtom* GetTransformListAttrName() const {
|
||||
return nsGkAtoms::gradientTransform;
|
||||
|
@ -61,7 +60,7 @@ public:
|
|||
|
||||
// WebIDL
|
||||
already_AddRefed<nsIDOMSVGAnimatedEnumeration> GradientUnits();
|
||||
already_AddRefed<DOMSVGAnimatedTransformList> GradientTransform();
|
||||
already_AddRefed<SVGAnimatedTransformList> GradientTransform();
|
||||
already_AddRefed<nsIDOMSVGAnimatedEnumeration> SpreadMethod();
|
||||
already_AddRefed<nsIDOMSVGAnimatedString> Href();
|
||||
|
||||
|
@ -79,7 +78,7 @@ protected:
|
|||
static StringInfo sStringInfo[1];
|
||||
|
||||
// SVGGradientElement values
|
||||
nsAutoPtr<SVGAnimatedTransformList> mGradientTransform;
|
||||
nsAutoPtr<nsSVGAnimatedTransformList> mGradientTransform;
|
||||
};
|
||||
|
||||
//---------------------Linear Gradients------------------------
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
#include "mozilla/Util.h"
|
||||
|
||||
#include "DOMSVGAnimatedTransformList.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGPatternElement.h"
|
||||
#include "mozilla/dom/SVGPatternElementBinding.h"
|
||||
|
||||
|
@ -94,12 +94,12 @@ SVGPatternElement::PatternContentUnits()
|
|||
return mEnumAttributes[PATTERNCONTENTUNITS].ToDOMAnimatedEnum(this);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMSVGAnimatedTransformList>
|
||||
already_AddRefed<SVGAnimatedTransformList>
|
||||
SVGPatternElement::PatternTransform()
|
||||
{
|
||||
// We're creating a DOM wrapper, so we must tell GetAnimatedTransformList
|
||||
// to allocate the SVGAnimatedTransformList if it hasn't already done so:
|
||||
return DOMSVGAnimatedTransformList::GetDOMWrapper(
|
||||
return SVGAnimatedTransformList::GetDOMWrapper(
|
||||
GetAnimatedTransformList(DO_ALLOCATE), this);
|
||||
}
|
||||
|
||||
|
@ -157,11 +157,11 @@ SVGPatternElement::IsAttributeMapped(const nsIAtom* name) const
|
|||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
SVGAnimatedTransformList*
|
||||
nsSVGAnimatedTransformList*
|
||||
SVGPatternElement::GetAnimatedTransformList(uint32_t aFlags)
|
||||
{
|
||||
if (!mPatternTransform && (aFlags & DO_ALLOCATE)) {
|
||||
mPatternTransform = new SVGAnimatedTransformList();
|
||||
mPatternTransform = new nsSVGAnimatedTransformList();
|
||||
}
|
||||
return mPatternTransform;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "nsSVGElement.h"
|
||||
#include "nsSVGViewBox.h"
|
||||
#include "SVGAnimatedPreserveAspectRatio.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
|
||||
class nsSVGPatternFrame;
|
||||
|
||||
|
@ -20,9 +20,8 @@ nsresult NS_NewSVGPatternElement(nsIContent **aResult,
|
|||
already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
|
||||
namespace mozilla {
|
||||
class DOMSVGAnimatedTransformList;
|
||||
|
||||
namespace dom {
|
||||
class SVGAnimatedTransformList;
|
||||
|
||||
typedef nsSVGElement SVGPatternElementBase;
|
||||
|
||||
|
@ -47,7 +46,7 @@ public:
|
|||
// nsSVGSVGElement methods:
|
||||
virtual bool HasValidDimensions() const;
|
||||
|
||||
virtual mozilla::SVGAnimatedTransformList*
|
||||
virtual mozilla::nsSVGAnimatedTransformList*
|
||||
GetAnimatedTransformList(uint32_t aFlags = 0);
|
||||
virtual nsIAtom* GetTransformListAttrName() const {
|
||||
return nsGkAtoms::patternTransform;
|
||||
|
@ -58,7 +57,7 @@ public:
|
|||
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
|
||||
already_AddRefed<nsIDOMSVGAnimatedEnumeration> PatternUnits();
|
||||
already_AddRefed<nsIDOMSVGAnimatedEnumeration> PatternContentUnits();
|
||||
already_AddRefed<DOMSVGAnimatedTransformList> PatternTransform();
|
||||
already_AddRefed<SVGAnimatedTransformList> PatternTransform();
|
||||
already_AddRefed<SVGAnimatedLength> X();
|
||||
already_AddRefed<SVGAnimatedLength> Y();
|
||||
already_AddRefed<SVGAnimatedLength> Width();
|
||||
|
@ -81,7 +80,7 @@ protected:
|
|||
nsSVGEnum mEnumAttributes[2];
|
||||
static EnumInfo sEnumInfo[2];
|
||||
|
||||
nsAutoPtr<mozilla::SVGAnimatedTransformList> mPatternTransform;
|
||||
nsAutoPtr<mozilla::nsSVGAnimatedTransformList> mPatternTransform;
|
||||
|
||||
enum { HREF };
|
||||
nsSVGString mStringAttributes[1];
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
#include "mozilla/dom/SVGTransform.h"
|
||||
#include "mozilla/dom/SVGMatrix.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsError.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "nsSVGAttrTearoffTable.h"
|
||||
#include "mozilla/dom/SVGTransformBinding.h"
|
||||
|
||||
|
@ -283,7 +283,7 @@ SVGTransform::RemovingFromList()
|
|||
nsSVGTransform&
|
||||
SVGTransform::InternalItem()
|
||||
{
|
||||
SVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
|
||||
nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
|
||||
return mIsAnimValItem && alist->mAnimVal ?
|
||||
(*alist->mAnimVal)[mListIndex] :
|
||||
alist->mBaseVal[mListIndex];
|
||||
|
@ -299,7 +299,7 @@ SVGTransform::InternalItem() const
|
|||
bool
|
||||
SVGTransform::IndexIsValid()
|
||||
{
|
||||
SVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
|
||||
nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList();
|
||||
return (mIsAnimValItem &&
|
||||
mListIndex < alist->GetAnimValue().Length()) ||
|
||||
(!mIsAnimValItem &&
|
||||
|
|
|
@ -29,7 +29,7 @@ class SVGTransform;
|
|||
*/
|
||||
class SVGTransformList
|
||||
{
|
||||
friend class SVGAnimatedTransformList;
|
||||
friend class nsSVGAnimatedTransformList;
|
||||
friend class DOMSVGTransformList;
|
||||
friend class dom::SVGTransform;
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/SVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGTransformableElement.h"
|
||||
#include "mozilla/dom/SVGMatrix.h"
|
||||
#include "mozilla/dom/SVGSVGElement.h"
|
||||
#include "DOMSVGAnimatedTransformList.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDOMMutationEvent.h"
|
||||
#include "nsIFrame.h"
|
||||
|
@ -18,12 +18,12 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
already_AddRefed<DOMSVGAnimatedTransformList>
|
||||
already_AddRefed<SVGAnimatedTransformList>
|
||||
SVGTransformableElement::Transform()
|
||||
{
|
||||
// We're creating a DOM wrapper, so we must tell GetAnimatedTransformList
|
||||
// to allocate the SVGAnimatedTransformList if it hasn't already done so:
|
||||
return DOMSVGAnimatedTransformList::GetDOMWrapper(
|
||||
return SVGAnimatedTransformList::GetDOMWrapper(
|
||||
GetAnimatedTransformList(DO_ALLOCATE), this).get();
|
||||
|
||||
}
|
||||
|
@ -136,11 +136,11 @@ SVGTransformableElement::SetAnimateMotionTransform(const gfxMatrix* aMatrix)
|
|||
DidAnimateTransformList();
|
||||
}
|
||||
|
||||
SVGAnimatedTransformList*
|
||||
nsSVGAnimatedTransformList*
|
||||
SVGTransformableElement::GetAnimatedTransformList(uint32_t aFlags)
|
||||
{
|
||||
if (!mTransforms && (aFlags & DO_ALLOCATE)) {
|
||||
mTransforms = new SVGAnimatedTransformList();
|
||||
mTransforms = new nsSVGAnimatedTransformList();
|
||||
}
|
||||
return mTransforms;
|
||||
}
|
||||
|
|
|
@ -6,15 +6,14 @@
|
|||
#ifndef SVGTransformableElement_h
|
||||
#define SVGTransformableElement_h
|
||||
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "gfxMatrix.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
|
||||
namespace mozilla {
|
||||
class DOMSVGAnimatedTransformList;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class SVGAnimatedTransformList;
|
||||
class SVGGraphicsElement;
|
||||
class SVGMatrix;
|
||||
class SVGIRect;
|
||||
|
@ -29,7 +28,7 @@ public:
|
|||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
|
||||
|
||||
// WebIDL
|
||||
already_AddRefed<DOMSVGAnimatedTransformList> Transform();
|
||||
already_AddRefed<SVGAnimatedTransformList> Transform();
|
||||
nsSVGElement* GetNearestViewportElement();
|
||||
nsSVGElement* GetFarthestViewportElement();
|
||||
already_AddRefed<SVGIRect> GetBBox(ErrorResult& rv);
|
||||
|
@ -53,7 +52,7 @@ public:
|
|||
virtual const gfxMatrix* GetAnimateMotionTransform() const;
|
||||
virtual void SetAnimateMotionTransform(const gfxMatrix* aMatrix);
|
||||
|
||||
virtual SVGAnimatedTransformList*
|
||||
virtual nsSVGAnimatedTransformList*
|
||||
GetAnimatedTransformList(uint32_t aFlags = 0);
|
||||
virtual nsIAtom* GetTransformListAttrName() const {
|
||||
return nsGkAtoms::transform;
|
||||
|
@ -64,7 +63,7 @@ public:
|
|||
protected:
|
||||
// nsSVGElement overrides
|
||||
|
||||
nsAutoPtr<SVGAnimatedTransformList> mTransforms;
|
||||
nsAutoPtr<nsSVGAnimatedTransformList> mTransforms;
|
||||
|
||||
// XXX maybe move this to property table, to save space on un-animated elems?
|
||||
nsAutoPtr<gfxMatrix> mAnimateMotionTransform;
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGAnimationElement.h"
|
||||
#include "nsSMILValue.h"
|
||||
#include "prdtoa.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "nsSVGTransform.h"
|
||||
#include "SVGTransformListSMILType.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace dom;
|
||||
|
||||
nsresult
|
||||
nsSVGAnimatedTransformList::SetBaseValueString(const nsAString& aValue)
|
||||
{
|
||||
SVGTransformList newBaseValue;
|
||||
nsresult rv = newBaseValue.SetValueFromString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
SVGAnimatedTransformList *domWrapper =
|
||||
SVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// We must send this notification *before* changing mBaseVal! If the length
|
||||
// of our baseVal is being reduced, our baseVal's DOM wrapper list may have
|
||||
// to remove DOM items from itself, and any removed DOM items need to copy
|
||||
// their internal counterpart values *before* we change them.
|
||||
//
|
||||
domWrapper->InternalBaseValListWillChangeLengthTo(newBaseValue.Length());
|
||||
}
|
||||
|
||||
// We don't need to call DidChange* here - we're only called by
|
||||
// nsSVGElement::ParseAttribute under Element::SetAttr,
|
||||
// which takes care of notifying.
|
||||
|
||||
rv = mBaseVal.CopyFrom(newBaseValue);
|
||||
if (NS_FAILED(rv) && domWrapper) {
|
||||
// Attempting to increase mBaseVal's length failed - reduce domWrapper
|
||||
// back to the same length:
|
||||
domWrapper->InternalBaseValListWillChangeLengthTo(mBaseVal.Length());
|
||||
} else {
|
||||
mIsAttrSet = true;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGAnimatedTransformList::ClearBaseValue()
|
||||
{
|
||||
SVGAnimatedTransformList *domWrapper =
|
||||
SVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// We must send this notification *before* changing mBaseVal! (See above.)
|
||||
domWrapper->InternalBaseValListWillChangeLengthTo(0);
|
||||
}
|
||||
mBaseVal.Clear();
|
||||
mIsAttrSet = false;
|
||||
// Caller notifies
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGAnimatedTransformList::SetAnimValue(const SVGTransformList& aValue,
|
||||
nsSVGElement *aElement)
|
||||
{
|
||||
SVGAnimatedTransformList *domWrapper =
|
||||
SVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// A new animation may totally change the number of items in the animVal
|
||||
// list, replacing what was essentially a mirror of the baseVal list, or
|
||||
// else replacing and overriding an existing animation. When this happens
|
||||
// we must try and keep our animVal's DOM wrapper in sync (see the comment
|
||||
// in SVGAnimatedTransformList::InternalBaseValListWillChangeLengthTo).
|
||||
//
|
||||
// It's not possible for us to reliably distinguish between calls to this
|
||||
// method that are setting a new sample for an existing animation, and
|
||||
// calls that are setting the first sample of an animation that will
|
||||
// override an existing animation. Happily it's cheap to just blindly
|
||||
// notify our animVal's DOM wrapper of its internal counterpart's new value
|
||||
// each time this method is called, so that's what we do.
|
||||
//
|
||||
// Note that we must send this notification *before* setting or changing
|
||||
// mAnimVal! (See the comment in SetBaseValueString above.)
|
||||
//
|
||||
domWrapper->InternalAnimValListWillChangeLengthTo(aValue.Length());
|
||||
}
|
||||
if (!mAnimVal) {
|
||||
mAnimVal = new SVGTransformList();
|
||||
}
|
||||
nsresult rv = mAnimVal->CopyFrom(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
// OOM. We clear the animation, and, importantly, ClearAnimValue() ensures
|
||||
// that mAnimVal and its DOM wrapper (if any) will have the same length!
|
||||
ClearAnimValue(aElement);
|
||||
return rv;
|
||||
}
|
||||
aElement->DidAnimateTransformList();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGAnimatedTransformList::ClearAnimValue(nsSVGElement *aElement)
|
||||
{
|
||||
SVGAnimatedTransformList *domWrapper =
|
||||
SVGAnimatedTransformList::GetDOMWrapperIfExists(this);
|
||||
if (domWrapper) {
|
||||
// When all animation ends, animVal simply mirrors baseVal, which may have
|
||||
// a different number of items to the last active animated value. We must
|
||||
// keep the length of our animVal's DOM wrapper list in sync, and again we
|
||||
// must do that before touching mAnimVal. See comments above.
|
||||
//
|
||||
domWrapper->InternalAnimValListWillChangeLengthTo(mBaseVal.Length());
|
||||
}
|
||||
mAnimVal = nullptr;
|
||||
aElement->DidAnimateTransformList();
|
||||
}
|
||||
|
||||
bool
|
||||
nsSVGAnimatedTransformList::IsExplicitlySet() const
|
||||
{
|
||||
// Like other methods of this name, we need to know when a transform value has
|
||||
// been explicitly set.
|
||||
//
|
||||
// There are three ways an animated list can become set:
|
||||
// 1) Markup -- we set mIsAttrSet to true on any successful call to
|
||||
// SetBaseValueString and clear it on ClearBaseValue (as called by
|
||||
// nsSVGElement::UnsetAttr or a failed nsSVGElement::ParseAttribute)
|
||||
// 2) DOM call -- simply fetching the baseVal doesn't mean the transform value
|
||||
// has been set. It is set if that baseVal has one or more transforms in
|
||||
// the list.
|
||||
// 3) Animation -- which will cause the mAnimVal member to be allocated
|
||||
return mIsAttrSet || !mBaseVal.IsEmpty() || mAnimVal;
|
||||
}
|
||||
|
||||
nsISMILAttr*
|
||||
nsSVGAnimatedTransformList::ToSMILAttr(nsSVGElement* aSVGElement)
|
||||
{
|
||||
return new SMILAnimatedTransformList(this, aSVGElement);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGAnimatedTransformList::SMILAnimatedTransformList::ValueFromString(
|
||||
const nsAString& aStr,
|
||||
const dom::SVGAnimationElement* aSrcElement,
|
||||
nsSMILValue& aValue,
|
||||
bool& aPreventCachingOfSandwich) const
|
||||
{
|
||||
NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE);
|
||||
NS_ABORT_IF_FALSE(aValue.IsNull(),
|
||||
"aValue should have been cleared before calling ValueFromString");
|
||||
|
||||
const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type);
|
||||
const nsIAtom* transformType = nsGkAtoms::translate; // default val
|
||||
if (typeAttr) {
|
||||
if (typeAttr->Type() != nsAttrValue::eAtom) {
|
||||
// Recognized values of |type| are parsed as an atom -- so if we have
|
||||
// something other than an atom, then we know already our |type| is
|
||||
// invalid.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
transformType = typeAttr->GetAtomValue();
|
||||
}
|
||||
|
||||
ParseValue(aStr, transformType, aValue);
|
||||
aPreventCachingOfSandwich = false;
|
||||
return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGAnimatedTransformList::SMILAnimatedTransformList::ParseValue(
|
||||
const nsAString& aSpec,
|
||||
const nsIAtom* aTransformType,
|
||||
nsSMILValue& aResult)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aResult.IsNull(), "Unexpected type for SMIL value");
|
||||
|
||||
// nsSVGSMILTransform constructor should be expecting array with 3 params
|
||||
PR_STATIC_ASSERT(SVGTransformSMILData::NUM_SIMPLE_PARAMS == 3);
|
||||
|
||||
float params[3] = { 0.f };
|
||||
int32_t numParsed = ParseParameterList(aSpec, params, 3);
|
||||
uint16_t transformType;
|
||||
|
||||
if (aTransformType == nsGkAtoms::translate) {
|
||||
// tx [ty=0]
|
||||
if (numParsed != 1 && numParsed != 2)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_TRANSLATE;
|
||||
} else if (aTransformType == nsGkAtoms::scale) {
|
||||
// sx [sy=sx]
|
||||
if (numParsed != 1 && numParsed != 2)
|
||||
return;
|
||||
if (numParsed == 1) {
|
||||
params[1] = params[0];
|
||||
}
|
||||
transformType = SVG_TRANSFORM_SCALE;
|
||||
} else if (aTransformType == nsGkAtoms::rotate) {
|
||||
// r [cx=0 cy=0]
|
||||
if (numParsed != 1 && numParsed != 3)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_ROTATE;
|
||||
} else if (aTransformType == nsGkAtoms::skewX) {
|
||||
// x-angle
|
||||
if (numParsed != 1)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_SKEWX;
|
||||
} else if (aTransformType == nsGkAtoms::skewY) {
|
||||
// y-angle
|
||||
if (numParsed != 1)
|
||||
return;
|
||||
transformType = SVG_TRANSFORM_SKEWY;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
nsSMILValue val(&SVGTransformListSMILType::sSingleton);
|
||||
SVGTransformSMILData transform(transformType, params);
|
||||
if (NS_FAILED(SVGTransformListSMILType::AppendTransform(transform, val))) {
|
||||
return; // OOM
|
||||
}
|
||||
|
||||
// Success! Populate our outparam with parsed value.
|
||||
aResult.Swap(val);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
inline void
|
||||
SkipWsp(nsACString::const_iterator& aIter,
|
||||
const nsACString::const_iterator& aIterEnd)
|
||||
{
|
||||
while (aIter != aIterEnd && IsSVGWhitespace(*aIter))
|
||||
++aIter;
|
||||
}
|
||||
} // end anonymous namespace block
|
||||
|
||||
int32_t
|
||||
nsSVGAnimatedTransformList::SMILAnimatedTransformList::ParseParameterList(
|
||||
const nsAString& aSpec,
|
||||
float* aVars,
|
||||
int32_t aNVars)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 spec(aSpec);
|
||||
|
||||
nsACString::const_iterator start, end;
|
||||
spec.BeginReading(start);
|
||||
spec.EndReading(end);
|
||||
|
||||
SkipWsp(start, end);
|
||||
|
||||
int numArgsFound = 0;
|
||||
|
||||
while (start != end) {
|
||||
char const *arg = start.get();
|
||||
char *argend;
|
||||
float f = float(PR_strtod(arg, &argend));
|
||||
if (arg == argend || argend > end.get() || !NS_finite(f))
|
||||
return -1;
|
||||
|
||||
if (numArgsFound < aNVars) {
|
||||
aVars[numArgsFound] = f;
|
||||
}
|
||||
|
||||
start.advance(argend - arg);
|
||||
numArgsFound++;
|
||||
|
||||
SkipWsp(start, end);
|
||||
if (*start == ',') {
|
||||
++start;
|
||||
SkipWsp(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
return numArgsFound;
|
||||
}
|
||||
|
||||
nsSMILValue
|
||||
nsSVGAnimatedTransformList::SMILAnimatedTransformList::GetBaseValue() const
|
||||
{
|
||||
// To benefit from Return Value Optimization and avoid copy constructor calls
|
||||
// due to our use of return-by-value, we must return the exact same object
|
||||
// from ALL return points. This function must only return THIS variable:
|
||||
nsSMILValue val(&SVGTransformListSMILType::sSingleton);
|
||||
if (!SVGTransformListSMILType::AppendTransforms(mVal->mBaseVal, val)) {
|
||||
val = nsSMILValue();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGAnimatedTransformList::SMILAnimatedTransformList::SetAnimValue(
|
||||
const nsSMILValue& aNewAnimValue)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(
|
||||
aNewAnimValue.mType == &SVGTransformListSMILType::sSingleton,
|
||||
"Unexpected type to assign animated value");
|
||||
SVGTransformList animVal;
|
||||
if (!SVGTransformListSMILType::GetTransforms(aNewAnimValue,
|
||||
animVal.mItems)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return mVal->SetAnimValue(animVal, mElement);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGAnimatedTransformList::SMILAnimatedTransformList::ClearAnimValue()
|
||||
{
|
||||
if (mVal->mAnimVal) {
|
||||
mVal->ClearAnimValue(mElement);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,141 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_SVGANIMATEDTRANSFORMLIST_H__
|
||||
#define MOZILLA_SVGANIMATEDTRANSFORMLIST_H__
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsISMILAttr.h"
|
||||
#include "SVGTransformList.h"
|
||||
|
||||
class nsIAtom;
|
||||
class nsSMILValue;
|
||||
class nsSVGElement;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class SVGAnimationElement;
|
||||
class SVGTransform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class nsSVGAnimatedTransformList
|
||||
*
|
||||
* This class is very different to the SVG DOM interface of the same name found
|
||||
* in the SVG specification. This is a lightweight internal class - see
|
||||
* SVGAnimatedTransformList for the heavier DOM class that wraps instances of
|
||||
* this class and implements the SVG specification's SVGAnimatedTransformList
|
||||
* DOM interface.
|
||||
*
|
||||
* Except where noted otherwise, this class' methods take care of keeping the
|
||||
* appropriate DOM wrappers in sync (see the comment in
|
||||
* SVGAnimatedTransformList::InternalBaseValListWillChangeTo) so that their
|
||||
* consumers don't need to concern themselves with that.
|
||||
*/
|
||||
class nsSVGAnimatedTransformList
|
||||
{
|
||||
// friends so that they can get write access to mBaseVal
|
||||
friend class dom::SVGTransform;
|
||||
friend class DOMSVGTransformList;
|
||||
|
||||
public:
|
||||
nsSVGAnimatedTransformList() : mIsAttrSet(false) { }
|
||||
|
||||
/**
|
||||
* Because it's so important that mBaseVal and its DOMSVGTransformList wrapper
|
||||
* (if any) be kept in sync (see the comment in
|
||||
* SVGAnimatedTransformList::InternalBaseValListWillChangeTo), this method
|
||||
* returns a const reference. Only our friend classes may get mutable
|
||||
* references to mBaseVal.
|
||||
*/
|
||||
const SVGTransformList& GetBaseValue() const {
|
||||
return mBaseVal;
|
||||
}
|
||||
|
||||
nsresult SetBaseValueString(const nsAString& aValue);
|
||||
|
||||
void ClearBaseValue();
|
||||
|
||||
const SVGTransformList& GetAnimValue() const {
|
||||
return mAnimVal ? *mAnimVal : mBaseVal;
|
||||
}
|
||||
|
||||
nsresult SetAnimValue(const SVGTransformList& aNewAnimValue,
|
||||
nsSVGElement *aElement);
|
||||
|
||||
void ClearAnimValue(nsSVGElement *aElement);
|
||||
|
||||
/**
|
||||
* Returns true if the corresponding transform attribute is set (or animated)
|
||||
* to a valid value. Unlike HasTransform it will return true for an empty
|
||||
* transform.
|
||||
*/
|
||||
bool IsExplicitlySet() const;
|
||||
|
||||
/**
|
||||
* Returns true if the corresponding transform attribute is set (or animated)
|
||||
* to a valid value, such that we have at least one transform in our list.
|
||||
* Returns false otherwise (e.g. if the transform attribute is missing or empty
|
||||
* or invalid).
|
||||
*/
|
||||
bool HasTransform() const
|
||||
{ return (mAnimVal && !mAnimVal->IsEmpty()) || !mBaseVal.IsEmpty(); }
|
||||
|
||||
bool IsAnimating() const {
|
||||
return !!mAnimVal;
|
||||
}
|
||||
|
||||
/// Callers own the returned nsISMILAttr
|
||||
nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
|
||||
|
||||
private:
|
||||
|
||||
// mAnimVal is a pointer to allow us to determine if we're being animated or
|
||||
// not. Making it a non-pointer member and using mAnimVal.IsEmpty() to check
|
||||
// if we're animating is not an option, since that would break animation *to*
|
||||
// the empty string (<set to="">).
|
||||
|
||||
SVGTransformList mBaseVal;
|
||||
nsAutoPtr<SVGTransformList> mAnimVal;
|
||||
bool mIsAttrSet;
|
||||
|
||||
struct SMILAnimatedTransformList : public nsISMILAttr
|
||||
{
|
||||
public:
|
||||
SMILAnimatedTransformList(nsSVGAnimatedTransformList* aVal,
|
||||
nsSVGElement* aSVGElement)
|
||||
: mVal(aVal)
|
||||
, mElement(aSVGElement)
|
||||
{}
|
||||
|
||||
// nsISMILAttr methods
|
||||
virtual nsresult ValueFromString(const nsAString& aStr,
|
||||
const dom::SVGAnimationElement* aSrcElement,
|
||||
nsSMILValue& aValue,
|
||||
bool& aPreventCachingOfSandwich) const;
|
||||
virtual nsSMILValue GetBaseValue() const;
|
||||
virtual void ClearAnimValue();
|
||||
virtual nsresult SetAnimValue(const nsSMILValue& aValue);
|
||||
|
||||
protected:
|
||||
static void ParseValue(const nsAString& aSpec,
|
||||
const nsIAtom* aTransformType,
|
||||
nsSMILValue& aResult);
|
||||
static int32_t ParseParameterList(const nsAString& aSpec, float* aVars,
|
||||
int32_t aNVars);
|
||||
|
||||
// These will stay alive because a nsISMILAttr only lives as long
|
||||
// as the Compositing step, and DOM elements don't get a chance to
|
||||
// die during that.
|
||||
nsSVGAnimatedTransformList* mVal;
|
||||
nsSVGElement* mElement;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_SVGANIMATEDTRANSFORMLIST_H__
|
|
@ -24,6 +24,7 @@
|
|||
#include "nsCSSProps.h"
|
||||
#include "nsCSSParser.h"
|
||||
#include "nsEventListenerManager.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "nsSVGLength2.h"
|
||||
#include "nsSVGNumber2.h"
|
||||
#include "nsSVGNumberPair.h"
|
||||
|
@ -38,7 +39,6 @@
|
|||
#include "SVGAnimatedLengthList.h"
|
||||
#include "SVGAnimatedPointList.h"
|
||||
#include "SVGAnimatedPathSegList.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "nsIFrame.h"
|
||||
#include <stdarg.h>
|
||||
|
@ -601,8 +601,8 @@ nsSVGElement::ParseAttribute(int32_t aNamespaceID,
|
|||
// Check for SVGAnimatedTransformList attribute
|
||||
} else if (GetTransformListAttrName() == aAttribute) {
|
||||
// The transform attribute is being set, so we must ensure that the
|
||||
// SVGAnimatedTransformList is/has been allocated:
|
||||
SVGAnimatedTransformList *transformList =
|
||||
// nsSVGAnimatedTransformList is/has been allocated:
|
||||
nsSVGAnimatedTransformList *transformList =
|
||||
GetAnimatedTransformList(DO_ALLOCATE);
|
||||
rv = transformList->SetBaseValueString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -820,7 +820,7 @@ nsSVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsIAtom* aName,
|
|||
|
||||
// Check if this is a transform list attribute going away
|
||||
if (GetTransformListAttrName() == aName) {
|
||||
SVGAnimatedTransformList *transformList = GetAnimatedTransformList();
|
||||
nsSVGAnimatedTransformList *transformList = GetAnimatedTransformList();
|
||||
if (transformList) {
|
||||
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
||||
transformList->ClearBaseValue();
|
||||
|
|
|
@ -52,7 +52,7 @@ class SVGUserUnitList;
|
|||
class SVGAnimatedPointList;
|
||||
class SVGAnimatedPathSegList;
|
||||
class SVGAnimatedPreserveAspectRatio;
|
||||
class SVGAnimatedTransformList;
|
||||
class nsSVGAnimatedTransformList;
|
||||
class SVGStringList;
|
||||
class DOMSVGStringList;
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ public:
|
|||
typedef mozilla::SVGAnimatedPointList SVGAnimatedPointList;
|
||||
typedef mozilla::SVGAnimatedPathSegList SVGAnimatedPathSegList;
|
||||
typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio;
|
||||
typedef mozilla::SVGAnimatedTransformList SVGAnimatedTransformList;
|
||||
typedef mozilla::nsSVGAnimatedTransformList nsSVGAnimatedTransformList;
|
||||
typedef mozilla::SVGStringList SVGStringList;
|
||||
|
||||
// nsISupports
|
||||
|
@ -264,18 +264,18 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
/**
|
||||
* Get the SVGAnimatedTransformList for this element.
|
||||
* Get the nsSVGAnimatedTransformList for this element.
|
||||
*
|
||||
* Despite the fact that animated transform lists are used for a variety of
|
||||
* attributes, no SVG element uses more than one.
|
||||
*
|
||||
* It's relatively uncommon for elements to have their transform attribute
|
||||
* set, so to save memory the SVGAnimatedTransformList is not allocated until
|
||||
* set, so to save memory the nsSVGAnimatedTransformList is not allocated until
|
||||
* the attribute is set/animated or its DOM wrapper is created. Callers that
|
||||
* require the SVGAnimatedTransformList to be allocated and for this method
|
||||
* require the nsSVGAnimatedTransformList to be allocated and for this method
|
||||
* to return non-null must pass the DO_ALLOCATE flag.
|
||||
*/
|
||||
virtual SVGAnimatedTransformList* GetAnimatedTransformList(
|
||||
virtual nsSVGAnimatedTransformList* GetAnimatedTransformList(
|
||||
uint32_t aFlags = 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -82,10 +82,6 @@ DOMInterfaces = {
|
|||
'nativeType': 'mozilla::dom::Activity',
|
||||
},
|
||||
|
||||
'AnalyserNode': {
|
||||
'wrapperCache': False
|
||||
},
|
||||
|
||||
'AnimationEvent': {
|
||||
'nativeType': 'nsDOMAnimationEvent',
|
||||
},
|
||||
|
@ -112,7 +108,6 @@ DOMInterfaces = {
|
|||
'AudioBufferSourceNode': {
|
||||
'implicitJSContext': [ 'start', 'noteOn', 'noteGrainOn' ],
|
||||
'resultNotAddRefed': [ 'playbackRate' ],
|
||||
'wrapperCache': False
|
||||
},
|
||||
|
||||
'AudioListener' : {
|
||||
|
@ -133,7 +128,6 @@ DOMInterfaces = {
|
|||
|
||||
'BiquadFilterNode': {
|
||||
'resultNotAddRefed': [ 'frequency', 'q', 'gain' ],
|
||||
'wrapperCache': False
|
||||
},
|
||||
|
||||
'Blob': [
|
||||
|
@ -229,7 +223,6 @@ DOMInterfaces = {
|
|||
'DelayNode': [
|
||||
{
|
||||
'resultNotAddRefed': [ 'delayTime' ],
|
||||
'wrapperCache': False
|
||||
}],
|
||||
|
||||
'DesktopNotificationCenter': {
|
||||
|
@ -292,7 +285,6 @@ DOMInterfaces = {
|
|||
'binaryNames': {
|
||||
'release': 'getRelease'
|
||||
},
|
||||
'wrapperCache': False
|
||||
},
|
||||
|
||||
'Element': {
|
||||
|
@ -378,7 +370,6 @@ DOMInterfaces = {
|
|||
|
||||
'GainNode': {
|
||||
'resultNotAddRefed': [ 'gain' ],
|
||||
'wrapperCache': False
|
||||
},
|
||||
|
||||
'Gamepad': {
|
||||
|
@ -656,7 +647,6 @@ DOMInterfaces = {
|
|||
'PannerNode': [
|
||||
{
|
||||
'resultNotAddRefed': [ 'coneGain', 'distanceGain' ],
|
||||
'wrapperCache': False
|
||||
}],
|
||||
|
||||
'Performance': {
|
||||
|
@ -740,8 +730,7 @@ DOMInterfaces = {
|
|||
},
|
||||
|
||||
'SVGAnimatedTransformList': {
|
||||
'nativeType': 'mozilla::DOMSVGAnimatedTransformList',
|
||||
'headerFile': 'DOMSVGAnimatedTransformList.h'
|
||||
'nativeOwnership': 'refcounted',
|
||||
},
|
||||
|
||||
'SVGAnimationElement': {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*/
|
||||
|
||||
[PrefControlled]
|
||||
interface AudioNode {
|
||||
interface AudioNode : EventTarget {
|
||||
|
||||
[Throws]
|
||||
void connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
|
||||
|
|
|
@ -15,6 +15,10 @@ interface DummyInterface {
|
|||
USSDReceivedEventDict ussdReceivedEvent();
|
||||
InspectorRGBTriple rgbTriple();
|
||||
Function getFunction();
|
||||
void funcSocketsDict(optional SocketsDict arg);
|
||||
void funcHttpConnDict(optional HttpConnDict arg);
|
||||
void funcWebSocketDict(optional WebSocketDict arg);
|
||||
void funcDNSCacheDict(optional DNSCacheDict arg);
|
||||
};
|
||||
|
||||
interface DummyInterfaceWorkers {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
dictionary SocketsDict {
|
||||
sequence<DOMString> host;
|
||||
sequence<unsigned long> port;
|
||||
sequence<boolean> active;
|
||||
sequence<unsigned long> tcp;
|
||||
sequence<double> socksent;
|
||||
sequence<double> sockreceived;
|
||||
double sent = 0;
|
||||
double received = 0;
|
||||
};
|
||||
|
||||
dictionary HttpConnInfoDict {
|
||||
sequence<unsigned long> rtt;
|
||||
sequence<unsigned long> ttl;
|
||||
};
|
||||
|
||||
dictionary HttpConnDict {
|
||||
sequence<DOMString> host;
|
||||
sequence<unsigned long> port;
|
||||
sequence<HttpConnInfoDict> active;
|
||||
sequence<HttpConnInfoDict> idle;
|
||||
sequence<boolean> spdy;
|
||||
sequence<boolean> ssl;
|
||||
};
|
||||
|
||||
dictionary WebSocketDict {
|
||||
sequence<DOMString> hostport;
|
||||
sequence<unsigned long> msgsent;
|
||||
sequence<unsigned long> msgreceived;
|
||||
sequence<double> sentsize;
|
||||
sequence<double> receivedsize;
|
||||
sequence<boolean> encrypted;
|
||||
};
|
||||
|
||||
dictionary DNSCacheDict {
|
||||
sequence<DOMString> hostname;
|
||||
sequence<sequence<DOMString>> hostaddr;
|
||||
sequence<DOMString> family;
|
||||
sequence<double> expiration;
|
||||
};
|
|
@ -158,6 +158,7 @@ webidl_files = \
|
|||
MozActivity.webidl \
|
||||
MutationEvent.webidl \
|
||||
MutationObserver.webidl \
|
||||
NetDashboard.webidl \
|
||||
Node.webidl \
|
||||
NodeFilter.webidl \
|
||||
NodeIterator.webidl \
|
||||
|
|
|
@ -2731,31 +2731,37 @@ WifiWorker.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let ssid = network.ssid;
|
||||
let networkKey = getNetworkKey(network);
|
||||
|
||||
if (!(networkKey in this.configuredNetworks)) {
|
||||
this._sendMessage(message, false, "Trying to forget an unknown network", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let configured = this.configuredNetworks[networkKey];
|
||||
this._reconnectOnDisconnect = (this.currentNetwork &&
|
||||
(this.currentNetwork.ssid === ssid));
|
||||
WifiManager.removeNetwork(configured.netId, function(ok) {
|
||||
this._reloadConfiguredNetworks((function(ok) {
|
||||
// Give it a chance to remove the network even if reload is failed.
|
||||
if (!ok) {
|
||||
self._sendMessage(message, false, "Unable to remove the network", msg);
|
||||
self._reconnectOnDisconnect = false;
|
||||
debug("Warning !!! Failed to reload the configured networks");
|
||||
}
|
||||
|
||||
let ssid = network.ssid;
|
||||
let networkKey = getNetworkKey(network);
|
||||
if (!(networkKey in this.configuredNetworks)) {
|
||||
this._sendMessage(message, false, "Trying to forget an unknown network", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
WifiManager.saveConfig(function() {
|
||||
self._reloadConfiguredNetworks(function() {
|
||||
self._sendMessage(message, true, true, msg);
|
||||
let self = this;
|
||||
let configured = this.configuredNetworks[networkKey];
|
||||
this._reconnectOnDisconnect = (this.currentNetwork &&
|
||||
(this.currentNetwork.ssid === ssid));
|
||||
WifiManager.removeNetwork(configured.netId, function(ok) {
|
||||
if (!ok) {
|
||||
self._sendMessage(message, false, "Unable to remove the network", msg);
|
||||
self._reconnectOnDisconnect = false;
|
||||
return;
|
||||
}
|
||||
|
||||
WifiManager.saveConfig(function() {
|
||||
self._reloadConfiguredNetworks(function() {
|
||||
self._sendMessage(message, true, true, msg);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
wps: function(msg) {
|
||||
|
|
|
@ -859,6 +859,9 @@ public:
|
|||
* will be mirrored here. This allows for asynchronous animation of the
|
||||
* margins by reconciling the difference between this value and a value that
|
||||
* is updated more frequently.
|
||||
* If the left or top margins are negative, it means that the elements this
|
||||
* layer represents are auto-positioned, and so fixed position margins should
|
||||
* not have an effect on the corresponding axis.
|
||||
*/
|
||||
void SetFixedPositionMargins(const gfx::Margin& aMargins)
|
||||
{
|
||||
|
|
|
@ -104,10 +104,10 @@ ContentClientRemote::EndPaint()
|
|||
}
|
||||
}
|
||||
|
||||
TemporaryRef<DrawTarget>
|
||||
ContentClientRemote::CreateDTBuffer(ContentType aType,
|
||||
const nsIntRect& aRect,
|
||||
uint32_t aFlags)
|
||||
void
|
||||
ContentClientRemote::BuildTextureClient(ContentType aType,
|
||||
const nsIntRect& aRect,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mIsNewBuffer,
|
||||
"Bad! Did we create a buffer twice without painting?");
|
||||
|
@ -127,6 +127,14 @@ ContentClientRemote::CreateDTBuffer(ContentType aType,
|
|||
MOZ_ASSERT(IsSurfaceDescriptorValid(*mTextureClient->GetDescriptor()));
|
||||
|
||||
CreateFrontBufferAndNotify(aRect);
|
||||
}
|
||||
|
||||
TemporaryRef<DrawTarget>
|
||||
ContentClientRemote::CreateDTBuffer(ContentType aType,
|
||||
const nsIntRect& aRect,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
BuildTextureClient(aType, aRect, aFlags);
|
||||
|
||||
RefPtr<DrawTarget> ret = mTextureClient->LockDrawTarget();
|
||||
return ret.forget();
|
||||
|
@ -137,24 +145,7 @@ ContentClientRemote::CreateBuffer(ContentType aType,
|
|||
const nsIntRect& aRect,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mIsNewBuffer,
|
||||
"Bad! Did we create a buffer twice without painting?");
|
||||
|
||||
mIsNewBuffer = true;
|
||||
|
||||
if (mTextureClient) {
|
||||
mOldTextures.AppendElement(mTextureClient);
|
||||
DestroyBuffers();
|
||||
}
|
||||
mTextureInfo.mTextureFlags = aFlags | HostRelease;
|
||||
mTextureClient = CreateTextureClient(TEXTURE_CONTENT);
|
||||
|
||||
mContentType = aType;
|
||||
mSize = gfx::IntSize(aRect.width, aRect.height);
|
||||
mTextureClient->EnsureAllocated(mSize, mContentType);
|
||||
MOZ_ASSERT(IsSurfaceDescriptorValid(*mTextureClient->GetDescriptor()));
|
||||
|
||||
CreateFrontBufferAndNotify(aRect);
|
||||
BuildTextureClient(aType, aRect, aFlags);
|
||||
|
||||
nsRefPtr<gfxASurface> ret = mTextureClient->LockSurface();
|
||||
return ret.forget();
|
||||
|
|
|
@ -215,6 +215,11 @@ protected:
|
|||
const nsIntRegion& aVisibleRegion,
|
||||
bool aDidSelfCopy);
|
||||
|
||||
// create and configure mTextureClient
|
||||
void BuildTextureClient(ContentType aType,
|
||||
const nsIntRect& aRect,
|
||||
uint32_t aFlags);
|
||||
|
||||
// Create the front buffer for the ContentClient/Host pair if necessary
|
||||
// and notify the compositor that we have created the buffer(s).
|
||||
virtual void CreateFrontBufferAndNotify(const nsIntRect& aBufferRect) = 0;
|
||||
|
|
|
@ -642,17 +642,24 @@ CompositorParent::TransformFixedLayers(Layer* aLayer,
|
|||
// aFixedLayerMargins are the margins we expect to be at at the current
|
||||
// time, obtained via SyncViewportInfo, and fixedMargins are the margins
|
||||
// that were used during layout.
|
||||
// If top/left of fixedMargins are negative, that indicates that this layer
|
||||
// represents auto-positioned elements, and should not be affected by
|
||||
// fixed margins at all.
|
||||
const gfx::Margin& fixedMargins = aLayer->GetFixedPositionMargins();
|
||||
if (anchor.x > 0) {
|
||||
translation.x -= aFixedLayerMargins.right - fixedMargins.right;
|
||||
} else {
|
||||
translation.x += aFixedLayerMargins.left - fixedMargins.left;
|
||||
if (fixedMargins.left >= 0) {
|
||||
if (anchor.x > 0) {
|
||||
translation.x -= aFixedLayerMargins.right - fixedMargins.right;
|
||||
} else {
|
||||
translation.x += aFixedLayerMargins.left - fixedMargins.left;
|
||||
}
|
||||
}
|
||||
|
||||
if (anchor.y > 0) {
|
||||
translation.y -= aFixedLayerMargins.bottom - fixedMargins.bottom;
|
||||
} else {
|
||||
translation.y += aFixedLayerMargins.top - fixedMargins.top;
|
||||
if (fixedMargins.top >= 0) {
|
||||
if (anchor.y > 0) {
|
||||
translation.y -= aFixedLayerMargins.bottom - fixedMargins.bottom;
|
||||
} else {
|
||||
translation.y += aFixedLayerMargins.top - fixedMargins.top;
|
||||
}
|
||||
}
|
||||
|
||||
// The transform already takes the resolution scale into account. Since we
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html style="transform-style: preserve-3d;"><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=windows-1252"></head><body>
|
||||
<iframe></iframe>
|
||||
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="UTF-8">
|
||||
</head><body>
|
||||
<div style="display: table-caption"><iframe></iframe></div>
|
||||
|
||||
</body></html>
|
|
@ -391,3 +391,5 @@ load 770381-1.html
|
|||
load 795646.html
|
||||
load 813372-1.html
|
||||
load 836990-1.html
|
||||
pref(layers.force-active,true) load 859526-1.html
|
||||
pref(layers.force-active,true) load 859630-1.html
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
#include "nsStyleStructInlines.h"
|
||||
#include "nsAnimationManager.h"
|
||||
#include "nsTransitionManager.h"
|
||||
#include "nsSVGIntegrationUtils.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
|
@ -7752,6 +7753,11 @@ DoApplyRenderingChangeToTree(nsIFrame* aFrame,
|
|||
// opacity updates in many cases.
|
||||
needInvalidatingPaint = true;
|
||||
aFrame->MarkLayersActive(nsChangeHint_UpdateOpacityLayer);
|
||||
if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
|
||||
// SVG effects paints the opacity without using
|
||||
// nsDisplayOpacity. We need to invalidate manually.
|
||||
aFrame->InvalidateFrameSubtree();
|
||||
}
|
||||
}
|
||||
if ((aChange & nsChangeHint_UpdateTransformLayer) &&
|
||||
aFrame->IsTransformed()) {
|
||||
|
|
|
@ -1395,10 +1395,16 @@ static void Sort(nsDisplayList* aList, int32_t aCount, nsDisplayList::SortLEQ aC
|
|||
}
|
||||
}
|
||||
|
||||
static nsIContent* FindContentInDocument(nsIContent* aContent, nsIDocument* aDoc) {
|
||||
nsIContent* c = aContent;
|
||||
static nsIContent* FindContentInDocument(nsDisplayItem* aItem, nsIDocument* aDoc) {
|
||||
nsIFrame* frame = aItem->GetUnderlyingFrame();
|
||||
nsIContent* c = frame->GetContent();
|
||||
for (;;) {
|
||||
nsIDocument* d = c->OwnerDoc();
|
||||
nsIDocument* d;
|
||||
if (c) {
|
||||
d = c->OwnerDoc();
|
||||
} else {
|
||||
d = frame->PresContext()->Document();
|
||||
}
|
||||
if (d == aDoc) {
|
||||
return c;
|
||||
}
|
||||
|
@ -1422,10 +1428,8 @@ static bool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
|
|||
// mixed into the same list. Ensure that we're looking at content
|
||||
// in commonAncestor's document.
|
||||
nsIDocument* commonAncestorDoc = commonAncestor->OwnerDoc();
|
||||
nsIContent* content1 = FindContentInDocument(aItem1->GetUnderlyingFrame()->GetContent(),
|
||||
commonAncestorDoc);
|
||||
nsIContent* content2 = FindContentInDocument(aItem2->GetUnderlyingFrame()->GetContent(),
|
||||
commonAncestorDoc);
|
||||
nsIContent* content1 = FindContentInDocument(aItem1, commonAncestorDoc);
|
||||
nsIContent* content2 = FindContentInDocument(aItem2, commonAncestorDoc);
|
||||
if (!content1 || !content2) {
|
||||
NS_ERROR("Document trees are mixed up!");
|
||||
// Something weird going on
|
||||
|
@ -2957,6 +2961,19 @@ nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
|
|||
aContainerParameters.mYScale,
|
||||
NSAppUnitsToFloatPixels(fixedMargins.left, factor) *
|
||||
aContainerParameters.mXScale);
|
||||
|
||||
// If the frame is auto-positioned on either axis, set the top/left layer
|
||||
// margins to -1, to indicate to the compositor that this layer is
|
||||
// unaffected by fixed margins.
|
||||
if (position->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
|
||||
position->mOffset.GetRightUnit() == eStyleUnit_Auto) {
|
||||
fixedLayerMargins.left = -1;
|
||||
}
|
||||
if (position->mOffset.GetTopUnit() == eStyleUnit_Auto &&
|
||||
position->mOffset.GetBottomUnit() == eStyleUnit_Auto) {
|
||||
fixedLayerMargins.top = -1;
|
||||
}
|
||||
|
||||
layer->SetFixedPositionMargins(fixedLayerMargins);
|
||||
|
||||
return layer.forget();
|
||||
|
|
|
@ -409,6 +409,11 @@ nsRangeFrame::GetValueAtEventPoint(nsGUIEvent* aEvent)
|
|||
nsPoint point =
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, absPoint, this);
|
||||
|
||||
if (point == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
|
||||
// We don't want to change the current value for this error state.
|
||||
return GetValue();
|
||||
}
|
||||
|
||||
nsRect rangeContentRect = GetContentRectRelativeToSelf();
|
||||
nsSize thumbSize;
|
||||
|
||||
|
@ -729,7 +734,8 @@ nsRangeFrame::ShouldUseNativeStyle() const
|
|||
{
|
||||
return (StyleDisplay()->mAppearance == NS_THEME_RANGE) &&
|
||||
!PresContext()->HasAuthorSpecifiedRules(const_cast<nsRangeFrame*>(this),
|
||||
STYLES_DISABLING_NATIVE_THEMING) &&
|
||||
(NS_AUTHOR_SPECIFIED_BORDER |
|
||||
NS_AUTHOR_SPECIFIED_BACKGROUND)) &&
|
||||
!PresContext()->HasAuthorSpecifiedRules(mTrackDiv->GetPrimaryFrame(),
|
||||
STYLES_DISABLING_NATIVE_THEMING) &&
|
||||
!PresContext()->HasAuthorSpecifiedRules(mProgressDiv->GetPrimaryFrame(),
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "nsPresContext.h"
|
||||
#include "nsFrameManager.h"
|
||||
#include "nsCSSFrameConstructor.h"
|
||||
#include "nsIDocumentInlines.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "nsBlockFrame.h"
|
||||
|
@ -115,8 +116,7 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
|
|||
nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aReflowStatus,
|
||||
nscoord aContainingBlockWidth,
|
||||
nscoord aContainingBlockHeight,
|
||||
const nsRect& aContainingBlock,
|
||||
bool aConstrainHeight,
|
||||
bool aCBWidthChanged,
|
||||
bool aCBHeightChanged,
|
||||
|
@ -135,7 +135,7 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
|
|||
// Reflow the frame
|
||||
nsReflowStatus kidStatus = NS_FRAME_COMPLETE;
|
||||
ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState,
|
||||
aContainingBlockWidth, aContainingBlockHeight,
|
||||
aContainingBlock,
|
||||
aConstrainHeight, kidFrame, kidStatus,
|
||||
aOverflowAreas);
|
||||
nsIFrame* nextFrame = kidFrame->GetNextInFlow();
|
||||
|
@ -348,10 +348,9 @@ nsAbsoluteContainingBlock::DoMarkFramesDirty(bool aMarkAllDirty)
|
|||
|
||||
nsresult
|
||||
nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
|
||||
nsPresContext* aPresContext,
|
||||
nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nscoord aContainingBlockWidth,
|
||||
nscoord aContainingBlockHeight,
|
||||
const nsRect& aContainingBlock,
|
||||
bool aConstrainHeight,
|
||||
nsIFrame* aKidFrame,
|
||||
nsReflowStatus& aStatus,
|
||||
|
@ -379,21 +378,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
|
|||
AutoNoisyIndenter indent(nsBlockFrame::gNoisy);
|
||||
#endif // DEBUG
|
||||
|
||||
nsresult rv;
|
||||
// Get the border values
|
||||
nsMargin border = aReflowState.mStyleBorder->GetComputedBorder();
|
||||
|
||||
// Respect fixed position margins.
|
||||
if (aDelegatingFrame->GetAbsoluteListID() == nsIFrame::kFixedList) {
|
||||
const nsMargin& fixedMargins = aPresContext->PresShell()->
|
||||
GetContentDocumentFixedPositionMargins();
|
||||
|
||||
border += fixedMargins;
|
||||
aContainingBlockWidth -= fixedMargins.left + fixedMargins.right;
|
||||
aContainingBlockHeight -= fixedMargins.top + fixedMargins.bottom;
|
||||
}
|
||||
|
||||
nscoord availWidth = aContainingBlockWidth;
|
||||
nscoord availWidth = aContainingBlock.width;
|
||||
if (availWidth == -1) {
|
||||
NS_ASSERTION(aReflowState.ComputedWidth() != NS_UNCONSTRAINEDSIZE,
|
||||
"Must have a useful width _somewhere_");
|
||||
|
@ -404,12 +389,15 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
|
|||
nsHTMLReflowMetrics kidDesiredSize;
|
||||
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame,
|
||||
nsSize(availWidth, NS_UNCONSTRAINEDSIZE),
|
||||
aContainingBlockWidth,
|
||||
aContainingBlockHeight);
|
||||
aContainingBlock.width,
|
||||
aContainingBlock.height);
|
||||
|
||||
// Send the WillReflow() notification and position the frame
|
||||
aKidFrame->WillReflow(aPresContext);
|
||||
|
||||
// Get the border values
|
||||
const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder();
|
||||
|
||||
bool constrainHeight = (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE)
|
||||
&& aConstrainHeight
|
||||
// Don't split if told not to (e.g. for fixed frames)
|
||||
|
@ -427,12 +415,15 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
|
|||
}
|
||||
|
||||
// Do the reflow
|
||||
rv = aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowState, aStatus);
|
||||
nsresult rv = aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowState, aStatus);
|
||||
|
||||
// If we're solving for 'left' or 'top', then compute it now that we know the
|
||||
// width/height
|
||||
if ((NS_AUTOOFFSET == kidReflowState.mComputedOffsets.left) ||
|
||||
(NS_AUTOOFFSET == kidReflowState.mComputedOffsets.top)) {
|
||||
nscoord aContainingBlockWidth = aContainingBlock.width;
|
||||
nscoord aContainingBlockHeight = aContainingBlock.height;
|
||||
|
||||
if (-1 == aContainingBlockWidth) {
|
||||
// Get the containing block width/height
|
||||
kidReflowState.ComputeContainingBlockRectangle(aPresContext,
|
||||
|
@ -463,6 +454,21 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
|
|||
nsRect rect(border.left + kidReflowState.mComputedOffsets.left + kidReflowState.mComputedMargin.left,
|
||||
border.top + kidReflowState.mComputedOffsets.top + kidReflowState.mComputedMargin.top,
|
||||
kidDesiredSize.width, kidDesiredSize.height);
|
||||
|
||||
// Offset the frame rect by the given origin of the absolute containing block.
|
||||
// If the frame is auto-positioned on both sides of an axis, it will be
|
||||
// positioned based on its containing block and we don't need to offset.
|
||||
if (aContainingBlock.TopLeft() != nsPoint(0, 0)) {
|
||||
if (!(kidReflowState.mStylePosition->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
|
||||
kidReflowState.mStylePosition->mOffset.GetRightUnit() == eStyleUnit_Auto)) {
|
||||
rect.x += aContainingBlock.x;
|
||||
}
|
||||
if (!(kidReflowState.mStylePosition->mOffset.GetTopUnit() == eStyleUnit_Auto &&
|
||||
kidReflowState.mStylePosition->mOffset.GetBottomUnit() == eStyleUnit_Auto)) {
|
||||
rect.y += aContainingBlock.y;
|
||||
}
|
||||
}
|
||||
|
||||
aKidFrame->SetRect(rect);
|
||||
|
||||
nsView* view = aKidFrame->GetView();
|
||||
|
|
|
@ -86,14 +86,12 @@ public:
|
|||
nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aReflowStatus,
|
||||
nscoord aContainingBlockWidth,
|
||||
nscoord aContainingBlockHeight,
|
||||
const nsRect& aContainingBlock,
|
||||
bool aConstrainHeight,
|
||||
bool aCBWidthChanged,
|
||||
bool aCBHeightChanged,
|
||||
nsOverflowAreas* aOverflowAreas);
|
||||
|
||||
|
||||
void DestroyFrames(nsIFrame* aDelegatingFrame,
|
||||
nsIFrame* aDestructRoot);
|
||||
|
||||
|
@ -122,8 +120,7 @@ protected:
|
|||
nsresult ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
|
||||
nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nscoord aContainingBlockWidth,
|
||||
nscoord aContainingBlockHeight,
|
||||
const nsRect& aContainingBlockRect,
|
||||
bool aConstrainHeight,
|
||||
nsIFrame* aKidFrame,
|
||||
nsReflowStatus& aStatus,
|
||||
|
|
|
@ -1229,10 +1229,10 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
|
|||
!(isRoot && NS_UNCONSTRAINEDSIZE == reflowState->ComputedHeight()) &&
|
||||
aMetrics.height != oldSize.height;
|
||||
|
||||
nsRect containingBlock(nsPoint(0, 0), containingBlockSize);
|
||||
absoluteContainer->Reflow(this, aPresContext, *reflowState,
|
||||
state.mReflowStatus,
|
||||
containingBlockSize.width,
|
||||
containingBlockSize.height, true,
|
||||
containingBlock, true,
|
||||
cbWidthChanged, cbHeightChanged,
|
||||
&aMetrics.mOverflowAreas);
|
||||
|
||||
|
|
|
@ -1673,7 +1673,9 @@ WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsD
|
|||
// and then flush this list into aOutput by wrapping the whole lot with a single
|
||||
// nsDisplayTransform.
|
||||
|
||||
if (childFrame && (childFrame->GetParent()->Preserves3DChildren() || childFrame == aFrame)) {
|
||||
if (childFrame &&
|
||||
childFrame->GetParent() &&
|
||||
(childFrame->GetParent()->Preserves3DChildren() || childFrame == aFrame)) {
|
||||
switch (item->GetType()) {
|
||||
case nsDisplayItem::TYPE_TRANSFORM: {
|
||||
if (!aTemp->IsEmpty()) {
|
||||
|
@ -4114,8 +4116,9 @@ nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
|
|||
nsContainerFrame* container = do_QueryFrame(this);
|
||||
NS_ASSERTION(container, "Abs-pos children only supported on container frames for now");
|
||||
|
||||
nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight);
|
||||
absoluteContainer->Reflow(container, aPresContext, aReflowState, aStatus,
|
||||
containingBlockWidth, containingBlockHeight,
|
||||
containingBlock,
|
||||
aConstrainHeight, true, true, // XXX could be optimized
|
||||
&aDesiredSize.mOverflowAreas);
|
||||
}
|
||||
|
@ -4583,7 +4586,14 @@ nsIFrame::AreLayersMarkedActive(nsChangeHint aChangeHint)
|
|||
{
|
||||
LayerActivity* layerActivity =
|
||||
static_cast<LayerActivity*>(Properties().Get(LayerActivityProperty()));
|
||||
return layerActivity && (layerActivity->mChangeHint & aChangeHint);
|
||||
if (layerActivity && (layerActivity->mChangeHint & aChangeHint)) {
|
||||
return true;
|
||||
}
|
||||
if (aChangeHint & nsChangeHint_UpdateTransformLayer &&
|
||||
Preserves3D()) {
|
||||
return GetParent()->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
|
|
@ -250,18 +250,20 @@ ViewportFrame::Reflow(nsPresContext* aPresContext,
|
|||
|
||||
// If a scroll position clamping scroll-port size has been set, layout
|
||||
// fixed position elements to this size instead of the computed size.
|
||||
nscoord width = reflowState.ComputedWidth();
|
||||
nscoord height = reflowState.ComputedHeight();
|
||||
nsRect rect(0, 0, reflowState.ComputedWidth(), reflowState.ComputedHeight());
|
||||
if (aPresContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
|
||||
nsSize size = aPresContext->PresShell()->
|
||||
GetScrollPositionClampingScrollPortSize();
|
||||
width = size.width;
|
||||
height = size.height;
|
||||
rect.width = size.width;
|
||||
rect.height = size.height;
|
||||
}
|
||||
|
||||
// Make sure content document fixed-position margins are respected.
|
||||
rect.Deflate(aPresContext->PresShell()->GetContentDocumentFixedPositionMargins());
|
||||
|
||||
// Just reflow all the fixed-pos frames.
|
||||
rv = GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus,
|
||||
width, height,
|
||||
rect,
|
||||
false, true, true, // XXX could be optimized
|
||||
&aDesiredSize.mOverflowAreas);
|
||||
}
|
||||
|
|
|
@ -132,14 +132,7 @@ inDOMUtils::GetChildrenForNode(nsIDOMNode* aNode,
|
|||
if (aShowingAnonymousContent) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
||||
if (content) {
|
||||
nsRefPtr<nsBindingManager> bindingManager =
|
||||
inLayoutUtils::GetBindingManagerFor(aNode);
|
||||
if (bindingManager) {
|
||||
bindingManager->GetAnonymousNodesFor(content, getter_AddRefs(kids));
|
||||
if (!kids) {
|
||||
bindingManager->GetContentListFor(content, getter_AddRefs(kids));
|
||||
}
|
||||
}
|
||||
kids = content->GetChildren(nsIContent::eAllChildren);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -337,19 +337,13 @@ inDeepTreeWalker::PushNode(nsIDOMNode* aNode)
|
|||
}
|
||||
|
||||
if (!kids) {
|
||||
if (mShowAnonymousContent) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
||||
nsRefPtr<nsBindingManager> bindingManager;
|
||||
if (content &&
|
||||
(bindingManager = inLayoutUtils::GetBindingManagerFor(aNode))) {
|
||||
bindingManager->GetAnonymousNodesFor(content, getter_AddRefs(kids));
|
||||
if (!kids)
|
||||
bindingManager->GetContentListFor(content, getter_AddRefs(kids));
|
||||
} else {
|
||||
aNode->GetChildNodes(getter_AddRefs(kids));
|
||||
}
|
||||
} else
|
||||
aNode->GetChildNodes(getter_AddRefs(kids));
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
||||
if (content && mShowAnonymousContent) {
|
||||
kids = content->GetChildren(nsIContent::eAllChildren);
|
||||
}
|
||||
}
|
||||
if (!kids) {
|
||||
aNode->GetChildNodes(getter_AddRefs(kids));
|
||||
}
|
||||
|
||||
item.kids = kids;
|
||||
|
|
|
@ -114,11 +114,9 @@ ifdef MOZ_ENABLE_SKIA
|
|||
SHARED_LIBRARY_LIBS += $(MOZ_SKIA_LIBS)
|
||||
endif
|
||||
|
||||
ifdef MOZ_WEBVTT
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/media/webvtt/$(LIB_PREFIX)webvtt.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_WEBRTC
|
||||
ifndef MOZ_WEBRTC_IN_LIBXUL
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<filter id="f"/>
|
||||
|
||||
<g filter="url(#f)">
|
||||
<text>a𞠯</text>
|
||||
</g>
|
||||
|
||||
<script>
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
var text = document.getElementsByTagName("text")[0];
|
||||
text.firstChild.data = "d";
|
||||
text.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "g"));
|
||||
}, false);
|
||||
|
||||
</script>
|
||||
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 386 B |
|
@ -157,6 +157,7 @@ load 842009-1.svg
|
|||
load 842630-1.svg
|
||||
load 842909-1.svg
|
||||
load 843072-1.svg
|
||||
load 843917-1.svg
|
||||
load 847139-1.svg
|
||||
load 849688-1.svg
|
||||
load 849688-2.svg
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "nsSVGEffects.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -179,7 +179,7 @@ nsSVGDisplayContainerFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
|
|||
|
||||
if (mContent->IsSVG()) {
|
||||
nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
|
||||
SVGAnimatedTransformList* transformList =
|
||||
nsSVGAnimatedTransformList* transformList =
|
||||
content->GetAnimatedTransformList();
|
||||
if ((transformList && transformList->HasTransform()) ||
|
||||
content->GetAnimateMotionTransform()) {
|
||||
|
|
|
@ -192,7 +192,7 @@ nsSVGForeignObjectFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
|
|||
}
|
||||
|
||||
nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
|
||||
SVGAnimatedTransformList* transformList =
|
||||
nsSVGAnimatedTransformList* transformList =
|
||||
content->GetAnimatedTransformList();
|
||||
if (transformList && transformList->HasTransform()) {
|
||||
if (aOwnTransform) {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsIDOMSVGAnimatedNumber.h"
|
||||
#include "nsSVGEffects.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
|
||||
// XXX Tight coupling with content classes ahead!
|
||||
|
||||
|
@ -157,10 +157,10 @@ nsSVGGradientFrame::GetSpreadMethod()
|
|||
return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
|
||||
}
|
||||
|
||||
const SVGAnimatedTransformList*
|
||||
const nsSVGAnimatedTransformList*
|
||||
nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault)
|
||||
{
|
||||
SVGAnimatedTransformList *thisTransformList =
|
||||
nsSVGAnimatedTransformList *thisTransformList =
|
||||
static_cast<dom::SVGGradientElement*>(mContent)->GetAnimatedTransformList();
|
||||
|
||||
if (thisTransformList && thisTransformList->IsExplicitlySet())
|
||||
|
@ -201,7 +201,7 @@ nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource,
|
|||
gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
|
||||
}
|
||||
|
||||
const SVGAnimatedTransformList* animTransformList =
|
||||
const nsSVGAnimatedTransformList* animTransformList =
|
||||
GetGradientTransformList(mContent);
|
||||
if (!animTransformList)
|
||||
return bboxMatrix;
|
||||
|
|
|
@ -22,7 +22,7 @@ class nsStyleContext;
|
|||
struct gfxRect;
|
||||
|
||||
namespace mozilla {
|
||||
class SVGAnimatedTransformList;
|
||||
class nsSVGAnimatedTransformList;
|
||||
|
||||
namespace dom {
|
||||
class SVGLinearGradientElement;
|
||||
|
@ -79,7 +79,7 @@ private:
|
|||
void GetStopInformation(int32_t aIndex,
|
||||
float *aOffset, nscolor *aColor, float *aStopOpacity);
|
||||
|
||||
const mozilla::SVGAnimatedTransformList* GetGradientTransformList(
|
||||
const mozilla::nsSVGAnimatedTransformList* GetGradientTransformList(
|
||||
nsIContent* aDefault);
|
||||
// Will be singular for gradientUnits="objectBoundingBox" with an empty bbox.
|
||||
gfxMatrix GetGradientTransform(nsIFrame *aSource,
|
||||
|
|
|
@ -152,7 +152,7 @@ nsSVGPathGeometryFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
|
|||
}
|
||||
|
||||
nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
|
||||
SVGAnimatedTransformList* transformList =
|
||||
nsSVGAnimatedTransformList* transformList =
|
||||
content->GetAnimatedTransformList();
|
||||
if ((transformList && transformList->HasTransform()) ||
|
||||
content->GetAnimateMotionTransform()) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "nsSVGGeometryFrame.h"
|
||||
#include "mozilla/dom/SVGPatternElement.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGAnimatedTransformList.h"
|
||||
#include "SVGContentUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -443,10 +443,10 @@ nsSVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
|
|||
mEnumAttributes[aIndex].GetAnimValue();
|
||||
}
|
||||
|
||||
SVGAnimatedTransformList*
|
||||
nsSVGAnimatedTransformList*
|
||||
nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault)
|
||||
{
|
||||
SVGAnimatedTransformList *thisTransformList =
|
||||
nsSVGAnimatedTransformList *thisTransformList =
|
||||
static_cast<SVGPatternElement *>(mContent)->GetAnimatedTransformList();
|
||||
|
||||
if (thisTransformList && thisTransformList->IsExplicitlySet())
|
||||
|
@ -462,7 +462,7 @@ nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault)
|
|||
gfxMatrix
|
||||
nsSVGPatternFrame::GetPatternTransform()
|
||||
{
|
||||
SVGAnimatedTransformList* animTransformList =
|
||||
nsSVGAnimatedTransformList* animTransformList =
|
||||
GetPatternTransformList(mContent);
|
||||
if (!animTransformList)
|
||||
return gfxMatrix();
|
||||
|
|
|
@ -18,7 +18,7 @@ class nsSVGViewBox;
|
|||
|
||||
namespace mozilla {
|
||||
class SVGAnimatedPreserveAspectRatio;
|
||||
class SVGAnimatedTransformList;
|
||||
class nsSVGAnimatedTransformList;
|
||||
} // namespace mozilla
|
||||
|
||||
typedef nsSVGPaintServerFrame nsSVGPatternFrameBase;
|
||||
|
@ -90,7 +90,7 @@ protected:
|
|||
{
|
||||
return GetEnumValue(aIndex, mContent);
|
||||
}
|
||||
mozilla::SVGAnimatedTransformList* GetPatternTransformList(
|
||||
mozilla::nsSVGAnimatedTransformList* GetPatternTransformList(
|
||||
nsIContent* aDefault);
|
||||
gfxMatrix GetPatternTransform();
|
||||
const nsSVGViewBox &GetViewBox(nsIContent *aDefault);
|
||||
|
|
|
@ -2880,7 +2880,7 @@ void
|
|||
GlyphMetricsUpdater::Run(nsSVGTextFrame2* aFrame)
|
||||
{
|
||||
aFrame->mPositioningDirty = true;
|
||||
nsSVGUtils::InvalidateBounds(aFrame, false);
|
||||
nsSVGEffects::InvalidateRenderingObservers(aFrame);
|
||||
nsSVGUtils::ScheduleReflowSVG(aFrame);
|
||||
aFrame->mGlyphMetricsUpdater = nullptr;
|
||||
}
|
||||
|
|
|
@ -628,6 +628,9 @@ pref("ui.scrolling.overscroll_snap_limit", -1);
|
|||
// The minimum amount of space that must be present for an axis to be considered scrollable,
|
||||
// in 1/1000ths of pixels.
|
||||
pref("ui.scrolling.min_scrollable_distance", -1);
|
||||
// The axis lock mode for panning behaviour - set between standard, free and sticky
|
||||
pref("ui.scrolling.axis_lock_mode", "standard");
|
||||
|
||||
|
||||
// Enable accessibility mode if platform accessibility is enabled.
|
||||
pref("accessibility.accessfu.activate", 2);
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.mozilla.gecko.gfx;
|
|||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.PrefsHelper;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.ZoomConstraints;
|
||||
|
@ -61,6 +62,12 @@ class JavaPanZoomController
|
|||
// Angle from axis within which we stay axis-locked
|
||||
private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
|
||||
|
||||
// Axis-lock breakout angle
|
||||
private static final double AXIS_BREAKOUT_ANGLE = Math.PI / 8.0;
|
||||
|
||||
// The distance the user has to pan before we consider breaking out of a locked axis
|
||||
public static final float AXIS_BREAKOUT_THRESHOLD = 1/32f * GeckoAppShell.getDpi();
|
||||
|
||||
// The maximum amount we allow you to zoom into a page
|
||||
private static final float MAX_ZOOM = 4.0f;
|
||||
|
||||
|
@ -74,24 +81,31 @@ class JavaPanZoomController
|
|||
private static final int BOUNCE_ANIMATION_DURATION = 250;
|
||||
|
||||
private enum PanZoomState {
|
||||
NOTHING, /* no touch-start events received */
|
||||
FLING, /* all touches removed, but we're still scrolling page */
|
||||
TOUCHING, /* one touch-start event received */
|
||||
PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
|
||||
PANNING, /* panning without axis lock */
|
||||
PANNING_HOLD, /* in panning, but not moving.
|
||||
* similar to TOUCHING but after starting a pan */
|
||||
PANNING_HOLD_LOCKED, /* like PANNING_HOLD, but axis lock still in effect */
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
ANIMATED_ZOOM, /* animated zoom to a new rect */
|
||||
BOUNCE, /* in a bounce animation */
|
||||
NOTHING, /* no touch-start events received */
|
||||
FLING, /* all touches removed, but we're still scrolling page */
|
||||
TOUCHING, /* one touch-start event received */
|
||||
PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */
|
||||
PANNING_LOCKED_Y, /* as above for Y axis */
|
||||
PANNING, /* panning without axis lock */
|
||||
PANNING_HOLD, /* in panning, but not moving.
|
||||
* similar to TOUCHING but after starting a pan */
|
||||
PANNING_HOLD_LOCKED_X, /* like PANNING_HOLD, but axis lock still in effect for X axis */
|
||||
PANNING_HOLD_LOCKED_Y, /* as above but for Y axis */
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
ANIMATED_ZOOM, /* animated zoom to a new rect */
|
||||
BOUNCE, /* in a bounce animation */
|
||||
WAITING_LISTENERS, /* a state halfway between NOTHING and TOUCHING - the user has
|
||||
put a finger down, but we don't yet know if a touch listener has
|
||||
prevented the default actions yet. we still need to abort animations. */
|
||||
AUTONAV, /* We are scrolling using an AutonavRunnable animation. This is similar
|
||||
to the FLING state except that it must be stopped manually by the code that
|
||||
started it, and it's velocity can be updated while it's running. */
|
||||
}
|
||||
|
||||
WAITING_LISTENERS, /* a state halfway between NOTHING and TOUCHING - the user has
|
||||
put a finger down, but we don't yet know if a touch listener has
|
||||
prevented the default actions yet. we still need to abort animations. */
|
||||
AUTONAV, /* We are scrolling using an AutonavRunnable animation. This is similar
|
||||
to the FLING state except that it must be stopped manually by the code that
|
||||
started it, and it's velocity can be updated while it's running. */
|
||||
private enum AxisLockMode {
|
||||
STANDARD, /* Default axis locking mode that doesn't break out until finger release */
|
||||
FREE, /* No locking at all */
|
||||
STICKY /* Break out with hysteresis so that it feels as free as possible whilst locking */
|
||||
}
|
||||
|
||||
private final PanZoomTarget mTarget;
|
||||
|
@ -113,6 +127,8 @@ class JavaPanZoomController
|
|||
private PanZoomState mState;
|
||||
/* The per-frame zoom delta for the currently-running AUTONAV animation. */
|
||||
private float mAutonavZoomDelta;
|
||||
/* The user selected panning mode */
|
||||
private AxisLockMode mMode;
|
||||
|
||||
public JavaPanZoomController(PanZoomTarget target, View view, EventDispatcher eventDispatcher) {
|
||||
mTarget = target;
|
||||
|
@ -130,6 +146,26 @@ class JavaPanZoomController
|
|||
registerEventListener(MESSAGE_ZOOM_PAGE);
|
||||
registerEventListener(MESSAGE_TOUCH_LISTENER);
|
||||
|
||||
mMode = AxisLockMode.STANDARD;
|
||||
|
||||
PrefsHelper.getPref("ui.scrolling.axis_lock_mode", new PrefsHelper.PrefHandlerBase() {
|
||||
@Override public void prefValue(String pref, String value) {
|
||||
if (value.equals("standard")) {
|
||||
mMode = AxisLockMode.STANDARD;
|
||||
} else if (value.equals("free")) {
|
||||
mMode = AxisLockMode.FREE;
|
||||
} else {
|
||||
mMode = AxisLockMode.STICKY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isObserver() {
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Axis.initPrefs();
|
||||
}
|
||||
|
||||
|
@ -383,9 +419,11 @@ class JavaPanZoomController
|
|||
return false;
|
||||
case TOUCHING:
|
||||
case PANNING:
|
||||
case PANNING_LOCKED:
|
||||
case PANNING_LOCKED_X:
|
||||
case PANNING_LOCKED_Y:
|
||||
case PANNING_HOLD:
|
||||
case PANNING_HOLD_LOCKED:
|
||||
case PANNING_HOLD_LOCKED_X:
|
||||
case PANNING_HOLD_LOCKED_Y:
|
||||
case PINCHING:
|
||||
Log.e(LOGTAG, "Received impossible touch down while in " + mState);
|
||||
return false;
|
||||
|
@ -420,10 +458,15 @@ class JavaPanZoomController
|
|||
track(event);
|
||||
return true;
|
||||
|
||||
case PANNING_HOLD_LOCKED:
|
||||
setState(PanZoomState.PANNING_LOCKED);
|
||||
case PANNING_HOLD_LOCKED_X:
|
||||
setState(PanZoomState.PANNING_LOCKED_X);
|
||||
track(event);
|
||||
return true;
|
||||
case PANNING_HOLD_LOCKED_Y:
|
||||
setState(PanZoomState.PANNING_LOCKED_Y);
|
||||
// fall through
|
||||
case PANNING_LOCKED:
|
||||
case PANNING_LOCKED_X:
|
||||
case PANNING_LOCKED_Y:
|
||||
track(event);
|
||||
return true;
|
||||
|
||||
|
@ -466,9 +509,11 @@ class JavaPanZoomController
|
|||
return false;
|
||||
|
||||
case PANNING:
|
||||
case PANNING_LOCKED:
|
||||
case PANNING_LOCKED_X:
|
||||
case PANNING_LOCKED_Y:
|
||||
case PANNING_HOLD:
|
||||
case PANNING_HOLD_LOCKED:
|
||||
case PANNING_HOLD_LOCKED_X:
|
||||
case PANNING_HOLD_LOCKED_Y:
|
||||
setState(PanZoomState.FLING);
|
||||
fling();
|
||||
return true;
|
||||
|
@ -571,15 +616,19 @@ class JavaPanZoomController
|
|||
mY.startTouch(y);
|
||||
mLastEventTime = time;
|
||||
|
||||
if (!mX.scrollable() || !mY.scrollable()) {
|
||||
setState(PanZoomState.PANNING);
|
||||
} else if (angle < AXIS_LOCK_ANGLE || angle > (Math.PI - AXIS_LOCK_ANGLE)) {
|
||||
mY.setScrollingDisabled(true);
|
||||
setState(PanZoomState.PANNING_LOCKED);
|
||||
} else if (Math.abs(angle - (Math.PI / 2)) < AXIS_LOCK_ANGLE) {
|
||||
mX.setScrollingDisabled(true);
|
||||
setState(PanZoomState.PANNING_LOCKED);
|
||||
} else {
|
||||
if (mMode == AxisLockMode.STANDARD || mMode == AxisLockMode.STICKY) {
|
||||
if (!mX.scrollable() || !mY.scrollable()) {
|
||||
setState(PanZoomState.PANNING);
|
||||
} else if (angle < AXIS_LOCK_ANGLE || angle > (Math.PI - AXIS_LOCK_ANGLE)) {
|
||||
mY.setScrollingDisabled(true);
|
||||
setState(PanZoomState.PANNING_LOCKED_X);
|
||||
} else if (Math.abs(angle - (Math.PI / 2)) < AXIS_LOCK_ANGLE) {
|
||||
mX.setScrollingDisabled(true);
|
||||
setState(PanZoomState.PANNING_LOCKED_Y);
|
||||
} else {
|
||||
setState(PanZoomState.PANNING);
|
||||
}
|
||||
} else if (mMode == AxisLockMode.FREE) {
|
||||
setState(PanZoomState.PANNING);
|
||||
}
|
||||
}
|
||||
|
@ -599,6 +648,29 @@ class JavaPanZoomController
|
|||
}
|
||||
mLastEventTime = time;
|
||||
|
||||
|
||||
// if we're axis-locked check if the user is trying to scroll away from the lock
|
||||
if (mMode == AxisLockMode.STICKY) {
|
||||
float dx = mX.panDistance(x);
|
||||
float dy = mY.panDistance(y);
|
||||
double angle = Math.atan2(dy, dx); // range [-pi, pi]
|
||||
angle = Math.abs(angle); // range [0, pi]
|
||||
|
||||
if (Math.abs(dx) > AXIS_BREAKOUT_THRESHOLD || Math.abs(dy) > AXIS_BREAKOUT_THRESHOLD) {
|
||||
if (mState == PanZoomState.PANNING_LOCKED_X) {
|
||||
if (angle > AXIS_BREAKOUT_ANGLE && angle < (Math.PI - AXIS_BREAKOUT_ANGLE)) {
|
||||
mY.setScrollingDisabled(false);
|
||||
setState(PanZoomState.PANNING);
|
||||
}
|
||||
} else if (mState == PanZoomState.PANNING_LOCKED_Y) {
|
||||
if (Math.abs(angle - (Math.PI / 2)) > AXIS_BREAKOUT_ANGLE) {
|
||||
mX.setScrollingDisabled(false);
|
||||
setState(PanZoomState.PANNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mX.updateWithTouchAt(x, timeDelta);
|
||||
mY.updateWithTouchAt(y, timeDelta);
|
||||
}
|
||||
|
@ -617,12 +689,14 @@ class JavaPanZoomController
|
|||
if (stopped()) {
|
||||
if (mState == PanZoomState.PANNING) {
|
||||
setState(PanZoomState.PANNING_HOLD);
|
||||
} else if (mState == PanZoomState.PANNING_LOCKED) {
|
||||
setState(PanZoomState.PANNING_HOLD_LOCKED);
|
||||
} else if (mState == PanZoomState.PANNING_LOCKED_X) {
|
||||
setState(PanZoomState.PANNING_HOLD_LOCKED_X);
|
||||
} else if (mState == PanZoomState.PANNING_LOCKED_Y) {
|
||||
setState(PanZoomState.PANNING_HOLD_LOCKED_Y);
|
||||
} else {
|
||||
// should never happen, but handle anyway for robustness
|
||||
Log.e(LOGTAG, "Impossible case " + mState + " when stopped in track");
|
||||
setState(PanZoomState.PANNING_HOLD_LOCKED);
|
||||
setState(PanZoomState.PANNING_HOLD);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,27 +5,14 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "mozilla/net/Dashboard.h"
|
||||
#include "mozilla/net/HttpInfo.h"
|
||||
#include "mozilla/dom/NetDashboardBinding.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(Dashboard, nsIDashboard, nsIDashboardEventNotifier)
|
||||
|
||||
#define CREATE_ARRAY_OBJECT(object) \
|
||||
JSObject* object = JS_NewArrayObject(cx, 0, nullptr); \
|
||||
if (!object) \
|
||||
return NS_ERROR_OUT_OF_MEMORY ; \
|
||||
|
||||
#define SET_ELEMENT(object, func, param, index) \
|
||||
if (!JS_DefineElement(cx, object, index, func(param), \
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) \
|
||||
return NS_ERROR_OUT_OF_MEMORY; \
|
||||
|
||||
#define SET_PROPERTY(finalObject, object, property) \
|
||||
val = OBJECT_TO_JSVAL(object); \
|
||||
if (!JS_DefineProperty(cx, finalObject, #property, \
|
||||
val, nullptr, nullptr, JSPROP_ENUMERATE)) \
|
||||
return NS_ERROR_OUT_OF_MEMORY; \
|
||||
using mozilla::dom::Sequence;
|
||||
|
||||
Dashboard::Dashboard()
|
||||
{
|
||||
|
@ -62,50 +49,53 @@ Dashboard::GetSocketsDispatch()
|
|||
nsresult
|
||||
Dashboard::GetSockets()
|
||||
{
|
||||
JS::Value val;
|
||||
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
||||
JSAutoRequest request(cx);
|
||||
|
||||
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
|
||||
if (!finalObject)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mozilla::dom::SocketsDict dict;
|
||||
dict.mHost.Construct();
|
||||
dict.mPort.Construct();
|
||||
dict.mActive.Construct();
|
||||
dict.mTcp.Construct();
|
||||
dict.mSocksent.Construct();
|
||||
dict.mSockreceived.Construct();
|
||||
dict.mSent = 0;
|
||||
dict.mReceived = 0;
|
||||
|
||||
CREATE_ARRAY_OBJECT(hostJs);
|
||||
CREATE_ARRAY_OBJECT(portJs);
|
||||
CREATE_ARRAY_OBJECT(activeJs);
|
||||
CREATE_ARRAY_OBJECT(sentJs);
|
||||
CREATE_ARRAY_OBJECT(receivedJs);
|
||||
CREATE_ARRAY_OBJECT(tcpJs);
|
||||
CREATE_ARRAY_OBJECT(sockSentJs);
|
||||
CREATE_ARRAY_OBJECT(sockRecJs);
|
||||
mSock.totalSent = 0;
|
||||
mSock.totalRecv = 0;
|
||||
Sequence<uint32_t> &ports = dict.mPort.Value();
|
||||
Sequence<nsString> &hosts = dict.mHost.Value();
|
||||
Sequence<bool> &active = dict.mActive.Value();
|
||||
Sequence<uint32_t> &tcp = dict.mTcp.Value();
|
||||
Sequence<double> &sent = dict.mSocksent.Value();
|
||||
Sequence<double> &received = dict.mSockreceived.Value();
|
||||
|
||||
for (uint32_t i = 0; i < mSock.data.Length(); i++) {
|
||||
JSString* hostString = JS_NewStringCopyZ(cx, mSock.data[i].host.get());
|
||||
SET_ELEMENT(hostJs, STRING_TO_JSVAL, hostString, i);
|
||||
SET_ELEMENT(portJs, INT_TO_JSVAL, mSock.data[i].port, i);
|
||||
SET_ELEMENT(activeJs, BOOLEAN_TO_JSVAL, mSock.data[i].active, i);
|
||||
SET_ELEMENT(tcpJs, INT_TO_JSVAL, mSock.data[i].tcp, i);
|
||||
SET_ELEMENT(sockSentJs, DOUBLE_TO_JSVAL, (double) mSock.data[i].sent, i);
|
||||
SET_ELEMENT(sockRecJs, DOUBLE_TO_JSVAL, (double) mSock.data[i].received, i);
|
||||
mSock.totalSent += mSock.data[i].sent;
|
||||
mSock.totalRecv += mSock.data[i].received;
|
||||
uint32_t length = mSock.data.Length();
|
||||
if (!ports.SetCapacity(length) || !hosts.SetCapacity(length) ||
|
||||
!active.SetCapacity(length) || !tcp.SetCapacity(length) ||
|
||||
!sent.SetCapacity(length) || !received.SetCapacity(length)) {
|
||||
mSock.cb = nullptr;
|
||||
mSock.data.Clear();
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SET_ELEMENT(sentJs, DOUBLE_TO_JSVAL, (double) mSock.totalSent, 0);
|
||||
SET_ELEMENT(receivedJs, DOUBLE_TO_JSVAL, (double) mSock.totalRecv, 0);
|
||||
for (uint32_t i = 0; i < mSock.data.Length(); i++) {
|
||||
CopyASCIItoUTF16(mSock.data[i].host, *hosts.AppendElement());
|
||||
*ports.AppendElement() = mSock.data[i].port;
|
||||
*active.AppendElement() = mSock.data[i].active;
|
||||
*tcp.AppendElement() = mSock.data[i].tcp;
|
||||
*sent.AppendElement() = (double) mSock.data[i].sent;
|
||||
*received.AppendElement() = (double) mSock.data[i].received;
|
||||
dict.mSent += mSock.data[i].sent;
|
||||
dict.mReceived += mSock.data[i].received;
|
||||
}
|
||||
|
||||
SET_PROPERTY(finalObject, hostJs, host);
|
||||
SET_PROPERTY(finalObject, portJs, port);
|
||||
SET_PROPERTY(finalObject, activeJs, active);
|
||||
SET_PROPERTY(finalObject, tcpJs, tcp);
|
||||
SET_PROPERTY(finalObject, sockSentJs, socksent);
|
||||
SET_PROPERTY(finalObject, sockRecJs, sockreceived);
|
||||
SET_PROPERTY(finalObject, sentJs, sent);
|
||||
SET_PROPERTY(finalObject, receivedJs, received);
|
||||
|
||||
val = OBJECT_TO_JSVAL(finalObject);
|
||||
JS::Value val;
|
||||
if (!dict.ToObject(cx, nullptr, &val)) {
|
||||
mSock.cb = nullptr;
|
||||
mSock.data.Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mSock.cb->OnDashboardDataAvailable(val);
|
||||
mSock.cb = nullptr;
|
||||
|
||||
|
@ -138,60 +128,81 @@ Dashboard::GetHttpDispatch()
|
|||
nsresult
|
||||
Dashboard::GetHttpConnections()
|
||||
{
|
||||
JS::Value val;
|
||||
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
||||
JSAutoRequest request(cx);
|
||||
|
||||
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
|
||||
if (!finalObject)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mozilla::dom::HttpConnDict dict;
|
||||
dict.mActive.Construct();
|
||||
dict.mHost.Construct();
|
||||
dict.mIdle.Construct();
|
||||
dict.mPort.Construct();
|
||||
dict.mSpdy.Construct();
|
||||
dict.mSsl.Construct();
|
||||
|
||||
CREATE_ARRAY_OBJECT(hostJs);
|
||||
CREATE_ARRAY_OBJECT(portJs);
|
||||
CREATE_ARRAY_OBJECT(activeJs);
|
||||
CREATE_ARRAY_OBJECT(idleJs);
|
||||
CREATE_ARRAY_OBJECT(spdyJs);
|
||||
CREATE_ARRAY_OBJECT(sslJs);
|
||||
using mozilla::dom::HttpConnInfoDict;
|
||||
Sequence<HttpConnInfoDict> &active = dict.mActive.Value();
|
||||
Sequence<nsString> &hosts = dict.mHost.Value();
|
||||
Sequence<HttpConnInfoDict> &idle = dict.mIdle.Value();
|
||||
Sequence<uint32_t> &ports = dict.mPort.Value();
|
||||
Sequence<bool> &spdy = dict.mSpdy.Value();
|
||||
Sequence<bool> &ssl = dict.mSsl.Value();
|
||||
|
||||
for (uint32_t i = 0; i < mHttp.data.Length(); i++) {
|
||||
JSString* hostString = JS_NewStringCopyZ(cx, mHttp.data[i].host.get());
|
||||
SET_ELEMENT(hostJs, STRING_TO_JSVAL, hostString, i);
|
||||
SET_ELEMENT(portJs, INT_TO_JSVAL, mHttp.data[i].port, i);
|
||||
|
||||
JSObject* rtt_Active = JS_NewArrayObject(cx, 0, nullptr);
|
||||
JSObject* timeToLive_Active = JS_NewArrayObject(cx, 0, nullptr);
|
||||
for (uint32_t j = 0; j < mHttp.data[i].active.Length(); j++) {
|
||||
SET_ELEMENT(rtt_Active, INT_TO_JSVAL, mHttp.data[i].active[j].rtt, j);
|
||||
SET_ELEMENT(timeToLive_Active, INT_TO_JSVAL, mHttp.data[i].active[j].ttl, j);
|
||||
}
|
||||
JSObject* active = JS_NewObject(cx, nullptr, nullptr, nullptr);
|
||||
SET_PROPERTY(active, rtt_Active, rtt);
|
||||
SET_PROPERTY(active, timeToLive_Active, ttl);
|
||||
SET_ELEMENT(activeJs, OBJECT_TO_JSVAL, active, i);
|
||||
|
||||
JSObject* rtt_Idle = JS_NewArrayObject(cx, 0, nullptr);
|
||||
JSObject* timeToLive_Idle = JS_NewArrayObject(cx, 0, nullptr);
|
||||
for (uint32_t j = 0; j < mHttp.data[i].idle.Length(); j++) {
|
||||
SET_ELEMENT(rtt_Idle, INT_TO_JSVAL, mHttp.data[i].idle[j].rtt, j);
|
||||
SET_ELEMENT(timeToLive_Idle, INT_TO_JSVAL, mHttp.data[i].idle[j].ttl, j);
|
||||
}
|
||||
JSObject* idle = JS_NewObject(cx, nullptr, nullptr, nullptr);
|
||||
SET_PROPERTY(idle, rtt_Idle, rtt);
|
||||
SET_PROPERTY(idle, timeToLive_Idle, ttl);
|
||||
SET_ELEMENT(idleJs, OBJECT_TO_JSVAL, idle, i);
|
||||
|
||||
SET_ELEMENT(spdyJs, BOOLEAN_TO_JSVAL, mHttp.data[i].spdy, i);
|
||||
SET_ELEMENT(sslJs, BOOLEAN_TO_JSVAL, mHttp.data[i].ssl, i);
|
||||
uint32_t length = mHttp.data.Length();
|
||||
if (!active.SetCapacity(length) || !hosts.SetCapacity(length) ||
|
||||
!idle.SetCapacity(length) || !ports.SetCapacity(length) ||
|
||||
!spdy.SetCapacity(length) || !ssl.SetCapacity(length)) {
|
||||
mHttp.cb = nullptr;
|
||||
mHttp.data.Clear();
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SET_PROPERTY(finalObject, hostJs, host);
|
||||
SET_PROPERTY(finalObject, portJs, port);
|
||||
SET_PROPERTY(finalObject, activeJs, active);
|
||||
SET_PROPERTY(finalObject, idleJs, idle);
|
||||
SET_PROPERTY(finalObject, spdyJs, spdy);
|
||||
SET_PROPERTY(finalObject, sslJs, ssl);
|
||||
for (uint32_t i = 0; i < mHttp.data.Length(); i++) {
|
||||
CopyASCIItoUTF16(mHttp.data[i].host,*hosts.AppendElement());
|
||||
*ports.AppendElement() = mHttp.data[i].port;
|
||||
*spdy.AppendElement() = mHttp.data[i].spdy;
|
||||
*ssl.AppendElement() = mHttp.data[i].ssl;
|
||||
HttpConnInfoDict &activeInfo = *active.AppendElement();
|
||||
activeInfo.mRtt.Construct();
|
||||
activeInfo.mTtl.Construct();
|
||||
Sequence<uint32_t> &active_rtt = activeInfo.mRtt.Value();
|
||||
Sequence<uint32_t> &active_ttl = activeInfo.mTtl.Value();
|
||||
if (!active_rtt.SetCapacity(mHttp.data[i].active.Length()) ||
|
||||
!active_ttl.SetCapacity(mHttp.data[i].active.Length())) {
|
||||
mHttp.cb = nullptr;
|
||||
mHttp.data.Clear();
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
for (uint32_t j = 0; j < mHttp.data[i].active.Length(); j++) {
|
||||
*active_rtt.AppendElement() = mHttp.data[i].active[j].rtt;
|
||||
*active_ttl.AppendElement() = mHttp.data[i].active[j].ttl;
|
||||
}
|
||||
|
||||
val = OBJECT_TO_JSVAL(finalObject);
|
||||
HttpConnInfoDict &idleInfo = *idle.AppendElement();
|
||||
idleInfo.mRtt.Construct();
|
||||
idleInfo.mTtl.Construct();
|
||||
Sequence<uint32_t> &idle_rtt = idleInfo.mRtt.Value();
|
||||
Sequence<uint32_t> &idle_ttl = idleInfo.mTtl.Value();
|
||||
if (!idle_rtt.SetCapacity(mHttp.data[i].idle.Length()) ||
|
||||
!idle_ttl.SetCapacity(mHttp.data[i].idle.Length())) {
|
||||
mHttp.cb = nullptr;
|
||||
mHttp.data.Clear();
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
for (uint32_t j = 0; j < mHttp.data[i].idle.Length(); j++) {
|
||||
*idle_rtt.AppendElement() = mHttp.data[i].idle[j].rtt;
|
||||
*idle_ttl.AppendElement() = mHttp.data[i].idle[j].ttl;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Value val;
|
||||
if (!dict.ToObject(cx, nullptr, &val)) {
|
||||
mHttp.cb = nullptr;
|
||||
mHttp.data.Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mHttp.cb->OnDashboardDataAvailable(val);
|
||||
mHttp.cb = nullptr;
|
||||
|
||||
|
@ -288,41 +299,50 @@ Dashboard::RequestWebsocketConnections(NetDashboardCallback* cb)
|
|||
nsresult
|
||||
Dashboard::GetWebSocketConnections()
|
||||
{
|
||||
JS::Value val;
|
||||
JSString* jsstring;
|
||||
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
||||
JSAutoRequest request(cx);
|
||||
|
||||
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
|
||||
if (!finalObject)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mozilla::dom::WebSocketDict dict;
|
||||
dict.mEncrypted.Construct();
|
||||
dict.mHostport.Construct();
|
||||
dict.mMsgreceived.Construct();
|
||||
dict.mMsgsent.Construct();
|
||||
dict.mReceivedsize.Construct();
|
||||
dict.mSentsize.Construct();
|
||||
|
||||
CREATE_ARRAY_OBJECT(hostJs);
|
||||
CREATE_ARRAY_OBJECT(msgSentJs);
|
||||
CREATE_ARRAY_OBJECT(msgRecvJs);
|
||||
CREATE_ARRAY_OBJECT(sizeSentJs);
|
||||
CREATE_ARRAY_OBJECT(sizeRecvJs);
|
||||
CREATE_ARRAY_OBJECT(encryptJs);
|
||||
Sequence<bool> &encrypted = dict.mEncrypted.Value();
|
||||
Sequence<nsString> &hostport = dict.mHostport.Value();
|
||||
Sequence<uint32_t> &received = dict.mMsgreceived.Value();
|
||||
Sequence<uint32_t> &sent = dict.mMsgsent.Value();
|
||||
Sequence<double> &receivedSize = dict.mReceivedsize.Value();
|
||||
Sequence<double> &sentSize = dict.mSentsize.Value();
|
||||
|
||||
uint32_t length = mWs.data.Length();
|
||||
if (!encrypted.SetCapacity(length) || !hostport.SetCapacity(length) ||
|
||||
!received.SetCapacity(length) || !sent.SetCapacity(length) ||
|
||||
!receivedSize.SetCapacity(length) || !sentSize.SetCapacity(length)) {
|
||||
mWs.cb = nullptr;
|
||||
mWs.data.Clear();
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mozilla::MutexAutoLock lock(mWs.lock);
|
||||
for (uint32_t i = 0; i < mWs.data.Length(); i++) {
|
||||
jsstring = JS_NewStringCopyN(cx, mWs.data[i].mHost.get(), mWs.data[i].mHost.Length());
|
||||
SET_ELEMENT(hostJs, STRING_TO_JSVAL, jsstring, i);
|
||||
SET_ELEMENT(msgSentJs, INT_TO_JSVAL, mWs.data[i].mMsgSent, i);
|
||||
SET_ELEMENT(msgRecvJs, INT_TO_JSVAL, mWs.data[i].mMsgReceived, i);
|
||||
SET_ELEMENT(sizeSentJs, DOUBLE_TO_JSVAL, (double) mWs.data[i].mSizeSent, i);
|
||||
SET_ELEMENT(sizeRecvJs, DOUBLE_TO_JSVAL, (double) mWs.data[i].mSizeReceived, i);
|
||||
SET_ELEMENT(encryptJs, BOOLEAN_TO_JSVAL, mWs.data[i].mEncrypted, i);
|
||||
CopyASCIItoUTF16(mWs.data[i].mHost, *hostport.AppendElement());
|
||||
*sent.AppendElement() = mWs.data[i].mMsgSent;
|
||||
*received.AppendElement() = mWs.data[i].mMsgReceived;
|
||||
*receivedSize.AppendElement() = mWs.data[i].mSizeSent;
|
||||
*sentSize.AppendElement() = mWs.data[i].mSizeReceived;
|
||||
*encrypted.AppendElement() = mWs.data[i].mEncrypted;
|
||||
}
|
||||
|
||||
SET_PROPERTY(finalObject, hostJs, hostport);
|
||||
SET_PROPERTY(finalObject, msgSentJs, msgsent);
|
||||
SET_PROPERTY(finalObject, msgRecvJs, msgreceived);
|
||||
SET_PROPERTY(finalObject, sizeSentJs, sentsize);
|
||||
SET_PROPERTY(finalObject, sizeRecvJs, receivedsize);
|
||||
SET_PROPERTY(finalObject, encryptJs, encrypted);
|
||||
|
||||
val = OBJECT_TO_JSVAL(finalObject);
|
||||
JS::Value val;
|
||||
if (!dict.ToObject(cx, nullptr, &val)) {
|
||||
mWs.cb = nullptr;
|
||||
mWs.data.Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mWs.cb->OnDashboardDataAvailable(val);
|
||||
mWs.cb = nullptr;
|
||||
|
||||
|
@ -362,50 +382,56 @@ Dashboard::GetDnsInfoDispatch()
|
|||
nsresult
|
||||
Dashboard::GetDNSCacheEntries()
|
||||
{
|
||||
JS::Value val;
|
||||
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
||||
JSAutoRequest request(cx);
|
||||
|
||||
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
|
||||
if (!finalObject)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mozilla::dom::DNSCacheDict dict;
|
||||
dict.mExpiration.Construct();
|
||||
dict.mFamily.Construct();
|
||||
dict.mHostaddr.Construct();
|
||||
dict.mHostname.Construct();
|
||||
|
||||
CREATE_ARRAY_OBJECT(nameJs);
|
||||
CREATE_ARRAY_OBJECT(addrJs);
|
||||
CREATE_ARRAY_OBJECT(familyJs);
|
||||
CREATE_ARRAY_OBJECT(expiresJs);
|
||||
Sequence<double> &expiration = dict.mExpiration.Value();
|
||||
Sequence<nsString> &family = dict.mFamily.Value();
|
||||
Sequence<Sequence<nsString>> &hostaddr = dict.mHostaddr.Value();
|
||||
Sequence<nsString> &hostname = dict.mHostname.Value();
|
||||
|
||||
for (uint32_t i = 0; i < mDns.data.Length(); i++) {
|
||||
JSString* hostnameString = JS_NewStringCopyZ(cx, mDns.data[i].hostname.get());
|
||||
SET_ELEMENT(nameJs, STRING_TO_JSVAL, hostnameString, i);
|
||||
|
||||
JSObject* addrObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
|
||||
if (!addrObject)
|
||||
uint32_t length = mDns.data.Length();
|
||||
if (!expiration.SetCapacity(length) || !family.SetCapacity(length) ||
|
||||
!hostaddr.SetCapacity(length) || !hostname.SetCapacity(length)) {
|
||||
mDns.cb = nullptr;
|
||||
mDns.data.Clear();
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
for (uint32_t j = 0; j < mDns.data[i].hostaddr.Length(); j++) {
|
||||
JSString* addrString = JS_NewStringCopyZ(cx, mDns.data[i].hostaddr[j].get());
|
||||
SET_ELEMENT(addrObject, STRING_TO_JSVAL, addrString, j);
|
||||
}
|
||||
|
||||
SET_ELEMENT(addrJs, OBJECT_TO_JSVAL, addrObject, i);
|
||||
|
||||
JSString* familyString;
|
||||
if (mDns.data[i].family == PR_AF_INET6)
|
||||
familyString = JS_NewStringCopyZ(cx, "ipv6");
|
||||
else
|
||||
familyString = JS_NewStringCopyZ(cx, "ipv4");
|
||||
|
||||
SET_ELEMENT(familyJs, STRING_TO_JSVAL, familyString, i);
|
||||
SET_ELEMENT(expiresJs, DOUBLE_TO_JSVAL, (double) mDns.data[i].expiration, i);
|
||||
}
|
||||
|
||||
SET_PROPERTY(finalObject, nameJs, hostname);
|
||||
SET_PROPERTY(finalObject, addrJs, hostaddr);
|
||||
SET_PROPERTY(finalObject, familyJs, family);
|
||||
SET_PROPERTY(finalObject, expiresJs, expiration);
|
||||
for (uint32_t i = 0; i < mDns.data.Length(); i++) {
|
||||
CopyASCIItoUTF16(mDns.data[i].hostname, *hostname.AppendElement());
|
||||
*expiration.AppendElement() = mDns.data[i].expiration;
|
||||
|
||||
val = OBJECT_TO_JSVAL(finalObject);
|
||||
Sequence<nsString> &addrs = *hostaddr.AppendElement();
|
||||
if (!addrs.SetCapacity(mDns.data[i].hostaddr.Length())) {
|
||||
mDns.cb = nullptr;
|
||||
mDns.data.Clear();
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
for (uint32_t j = 0; j < mDns.data[i].hostaddr.Length(); j++) {
|
||||
CopyASCIItoUTF16(mDns.data[i].hostaddr[j], *addrs.AppendElement());
|
||||
}
|
||||
|
||||
if (mDns.data[i].family == PR_AF_INET6)
|
||||
CopyASCIItoUTF16("ipv6", *family.AppendElement());
|
||||
else
|
||||
CopyASCIItoUTF16("ipv4", *family.AppendElement());
|
||||
}
|
||||
|
||||
JS::Value val;
|
||||
if (!dict.ToObject(cx, nullptr, &val)) {
|
||||
mDns.cb = nullptr;
|
||||
mDns.data.Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mDns.cb->OnDashboardDataAvailable(val);
|
||||
mDns.cb = nullptr;
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "nsIDashboardEventNotifier.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIThread.h"
|
||||
|
|
|
@ -28,6 +28,8 @@ class ErrorCodes(object):
|
|||
INVALID_XPATH_SELECTOR = 51
|
||||
INVALID_XPATH_SELECTOR_RETURN_TYPER = 52
|
||||
INVALID_RESPONSE = 53
|
||||
FRAME_SEND_NOT_INITIALIZED_ERROR = 54
|
||||
FRAME_SEND_FAILURE_ERROR = 55
|
||||
MARIONETTE_ERROR = 500
|
||||
|
||||
class MarionetteException(Exception):
|
||||
|
@ -97,3 +99,8 @@ class InvalidSelectorException(MarionetteException):
|
|||
class MoveTargetOutOfBoundsException(MarionetteException):
|
||||
pass
|
||||
|
||||
class FrameSendNotInitializedError(MarionetteException):
|
||||
pass
|
||||
|
||||
class FrameSendFailureError(MarionetteException):
|
||||
pass
|
||||
|
|
|
@ -372,7 +372,11 @@ class Marionette(object):
|
|||
or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER:
|
||||
raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace)
|
||||
elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS:
|
||||
MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace)
|
||||
raise MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace)
|
||||
elif status == ErrorCodes.FRAME_SEND_NOT_INITIALIZED_ERROR:
|
||||
raise FrameSendNotInitializedError(message=message, status=status, stacktrace=stacktrace)
|
||||
elif status == ErrorCodes.FRAME_SEND_FAILURE_ERROR:
|
||||
raise FrameSendFailureError(message=message, status=status, stacktrace=stacktrace)
|
||||
else:
|
||||
raise MarionetteException(message=message, status=status, stacktrace=stacktrace)
|
||||
raise MarionetteException(message=response, status=500)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
import time
|
||||
from marionette_test import MarionetteTestCase
|
||||
|
||||
"""
|
||||
### Disabled due to bug 838607
|
||||
class TestClick(MarionetteTestCase):
|
||||
def test_click(self):
|
||||
test_html = self.marionette.absolute_url("test.html")
|
||||
|
@ -13,19 +15,19 @@ class TestClick(MarionetteTestCase):
|
|||
link.click()
|
||||
self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
|
||||
|
||||
# disabled due to bug 860104
|
||||
#def testClickingALinkMadeUpOfNumbersIsHandledCorrectly(self):
|
||||
# test_html = self.marionette.absolute_url("clicks.html")
|
||||
# self.marionette.navigate(test_html)
|
||||
# self.marionette.find_element("link text", "333333").click()
|
||||
# count = 0
|
||||
# while len(self.marionette.find_elements("id", "username")) == 0:
|
||||
# count += 1
|
||||
# time.sleep(1)
|
||||
# if count == 30:
|
||||
# self.fail("Element id=username not found after 30 seconds")
|
||||
def testClickingALinkMadeUpOfNumbersIsHandledCorrectly(self):
|
||||
test_html = self.marionette.absolute_url("clicks.html")
|
||||
self.marionette.navigate(test_html)
|
||||
self.marionette.find_element("link text", "333333").click()
|
||||
count = 0
|
||||
while len(self.marionette.find_elements("id", "username")) == 0:
|
||||
count += 1
|
||||
time.sleep(1)
|
||||
if count == 30:
|
||||
self.fail("Element id=username not found after 30 seconds")
|
||||
|
||||
# self.assertEqual(self.marionette.title, "XHTML Test Page")
|
||||
self.assertEqual(self.marionette.title, "XHTML Test Page")
|
||||
"""
|
||||
|
||||
class TestClickChrome(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -150,6 +150,27 @@ function MarionetteRemoteFrame(windowId, frameId) {
|
|||
// persistent list of remote frames that Marionette has loaded a frame script in
|
||||
let remoteFrames = [];
|
||||
|
||||
/*
|
||||
* Custom exceptions
|
||||
*/
|
||||
function FrameSendNotInitializedError(frame) {
|
||||
this.code = 54;
|
||||
this.frame = frame;
|
||||
this.message = "Error sending message to frame (NS_ERROR_NOT_INITIALIZED)";
|
||||
this.toString = function() {
|
||||
return this.message + " " + this.frame + "; frame has closed.";
|
||||
}
|
||||
}
|
||||
|
||||
function FrameSendFailureError(frame) {
|
||||
this.code = 55;
|
||||
this.frame = frame;
|
||||
this.message = "Error sending message to frame (NS_ERROR_FAILURE)";
|
||||
this.toString = function() {
|
||||
return this.message + " " + this.frame + "; frame not responding.";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This actor is responsible for all marionette API calls. It gets created
|
||||
* for each connection and manages all chrome and browser based calls. It
|
||||
|
@ -203,11 +224,7 @@ MarionetteDriverActor.prototype = {
|
|||
switchToGlobalMessageManager: function MDA_switchToGlobalMM() {
|
||||
if (this.currentRemoteFrame !== null) {
|
||||
this.removeMessageManagerListeners(this.messageManager);
|
||||
try {
|
||||
// this can fail if the frame is already gone
|
||||
this.sendAsync("sleepSession");
|
||||
}
|
||||
catch(e) {}
|
||||
this.sendAsync("sleepSession", null, null, true);
|
||||
}
|
||||
this.messageManager = this.globalMessageManager;
|
||||
this.currentRemoteFrame = null;
|
||||
|
@ -221,15 +238,40 @@ MarionetteDriverActor.prototype = {
|
|||
* @param object values
|
||||
* Object to send to the listener
|
||||
*/
|
||||
sendAsync: function MDA_sendAsync(name, values) {
|
||||
sendAsync: function MDA_sendAsync(name, values, commandId, ignoreFailure) {
|
||||
let success = true;
|
||||
if (values instanceof Object && commandId) {
|
||||
values.command_id = commandId;
|
||||
}
|
||||
if (this.currentRemoteFrame !== null) {
|
||||
this.messageManager.sendAsyncMessage(
|
||||
"Marionette:" + name + this.currentRemoteFrame.targetFrameId, values);
|
||||
try {
|
||||
this.messageManager.sendAsyncMessage(
|
||||
"Marionette:" + name + this.currentRemoteFrame.targetFrameId, values);
|
||||
}
|
||||
catch(e) {
|
||||
if (!ignoreFailure) {
|
||||
success = false;
|
||||
let error = e;
|
||||
switch(e.result) {
|
||||
case Components.results.NS_ERROR_FAILURE:
|
||||
error = new FrameSendFailureError(this.currentRemoteFrame);
|
||||
break;
|
||||
case Components.results.NS_ERROR_NOT_INITIALIZED:
|
||||
error = new FrameSendNotInitializedError(this.currentRemoteFrame);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
code = error.hasOwnProperty('code') ? e.code : 500;
|
||||
this.sendError(error.toString(), code, error.stack, commandId);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.messageManager.broadcastAsyncMessage(
|
||||
"Marionette:" + name + this.curBrowser.curFrameId, values);
|
||||
}
|
||||
return success;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -764,12 +806,15 @@ MarionetteDriverActor.prototype = {
|
|||
aRequest.newSandbox = true;
|
||||
}
|
||||
if (this.context == "content") {
|
||||
this.sendAsync("executeScript", {value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
newSandbox: aRequest.newSandbox,
|
||||
timeout: timeout,
|
||||
command_id: command_id,
|
||||
specialPowers: aRequest.specialPowers});
|
||||
this.sendAsync("executeScript",
|
||||
{
|
||||
value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
newSandbox: aRequest.newSandbox,
|
||||
timeout: timeout,
|
||||
specialPowers: aRequest.specialPowers
|
||||
},
|
||||
command_id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -852,13 +897,16 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("executeJSScript", { value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
newSandbox: aRequest.newSandbox,
|
||||
async: aRequest.async,
|
||||
timeout: timeout,
|
||||
command_id: command_id,
|
||||
specialPowers: aRequest.specialPowers });
|
||||
this.sendAsync("executeJSScript",
|
||||
{
|
||||
value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
newSandbox: aRequest.newSandbox,
|
||||
async: aRequest.async,
|
||||
timeout: timeout,
|
||||
specialPowers: aRequest.specialPowers
|
||||
},
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -888,13 +936,16 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
|
||||
if (this.context == "content") {
|
||||
this.sendAsync("executeAsyncScript", {value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
id: this.command_id,
|
||||
newSandbox: aRequest.newSandbox,
|
||||
timeout: timeout,
|
||||
command_id: command_id,
|
||||
specialPowers: aRequest.specialPowers});
|
||||
this.sendAsync("executeAsyncScript",
|
||||
{
|
||||
value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
id: this.command_id,
|
||||
newSandbox: aRequest.newSandbox,
|
||||
timeout: timeout,
|
||||
specialPowers: aRequest.specialPowers
|
||||
},
|
||||
command_id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -994,7 +1045,7 @@ MarionetteDriverActor.prototype = {
|
|||
if (this.context != "chrome") {
|
||||
aRequest.command_id = command_id;
|
||||
aRequest.pageTimeout = this.pageTimeout;
|
||||
this.sendAsync("goUrl", aRequest);
|
||||
this.sendAsync("goUrl", aRequest, command_id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1031,7 +1082,7 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendResponse(this.getCurrentWindow().location.href, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("getUrl", {command_id: this.command_id});
|
||||
this.sendAsync("getUrl", {}, this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1040,7 +1091,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
getTitle: function MDA_getTitle() {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("getTitle", {command_id: this.command_id});
|
||||
this.sendAsync("getTitle", {}, this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1055,7 +1106,7 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendResponse(pageSource, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("getPageSource", {command_id: this.command_id});
|
||||
this.sendAsync("getPageSource", {}, this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1064,7 +1115,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
goBack: function MDA_goBack() {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("goBack", {command_id: this.command_id});
|
||||
this.sendAsync("goBack", {}, this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1072,7 +1123,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
goForward: function MDA_goForward() {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("goForward", {command_id: this.command_id});
|
||||
this.sendAsync("goForward", {}, this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1080,7 +1131,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
refresh: function MDA_refresh() {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("refresh", {command_id: this.command_id});
|
||||
this.sendAsync("refresh", {}, this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1245,7 +1296,7 @@ MarionetteDriverActor.prototype = {
|
|||
this.switchToGlobalMessageManager();
|
||||
}
|
||||
aRequest.command_id = command_id;
|
||||
this.sendAsync("switchToFrame", aRequest);
|
||||
this.sendAsync("switchToFrame", aRequest, command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1267,8 +1318,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("setSearchTimeout", {value: aRequest.value,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("setSearchTimeout",
|
||||
{ value: aRequest.value },
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1318,10 +1370,13 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("singleTap", {value: serId,
|
||||
corx: x,
|
||||
cory: y,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("singleTap",
|
||||
{
|
||||
value: serId,
|
||||
corx: x,
|
||||
cory: y
|
||||
},
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1340,10 +1395,13 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("doubleTap", {value: serId,
|
||||
corx: x,
|
||||
cory: y,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("doubleTap",
|
||||
{
|
||||
value: serId,
|
||||
corx: x,
|
||||
cory: y
|
||||
},
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1362,10 +1420,13 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("press", {value: element,
|
||||
corx: x,
|
||||
cory: y,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("press",
|
||||
{
|
||||
value: element,
|
||||
corx: x,
|
||||
cory: y
|
||||
},
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1383,9 +1444,12 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("cancelTouch", {value: element,
|
||||
touchId: touchId,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("cancelTouch",
|
||||
{
|
||||
value: element,
|
||||
touchId: touchId
|
||||
},
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1405,11 +1469,14 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("release", {value: element,
|
||||
touchId: touchId,
|
||||
corx: x,
|
||||
cory: y,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("release",
|
||||
{
|
||||
value: element,
|
||||
touchId: touchId,
|
||||
corx: x,
|
||||
cory: y
|
||||
},
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1425,9 +1492,12 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("actionChain", {chain: aRequest.chain,
|
||||
nextId: aRequest.nextId,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("actionChain",
|
||||
{
|
||||
chain: aRequest.chain,
|
||||
nextId: aRequest.nextId
|
||||
},
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1446,9 +1516,12 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("multiAction", {value: aRequest.value,
|
||||
maxlen: aRequest.max_length,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("multiAction",
|
||||
{
|
||||
value: aRequest.value,
|
||||
maxlen: aRequest.max_length
|
||||
},
|
||||
this.command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1480,10 +1553,13 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("findElementContent", {value: aRequest.value,
|
||||
using: aRequest.using,
|
||||
element: aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("findElementContent",
|
||||
{
|
||||
value: aRequest.value,
|
||||
using: aRequest.using,
|
||||
element: aRequest.element
|
||||
},
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1514,10 +1590,13 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("findElementsContent", {value: aRequest.value,
|
||||
using: aRequest.using,
|
||||
element: aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("findElementsContent",
|
||||
{
|
||||
value: aRequest.value,
|
||||
using: aRequest.using,
|
||||
element: aRequest.element
|
||||
},
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1526,7 +1605,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
getActiveElement: function MDA_getActiveElement(){
|
||||
let command_id = this.command_id = this.getCommandId();
|
||||
this.sendAsync("getActiveElement", {command_id: command_id});
|
||||
this.sendAsync("getActiveElement", {}, command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1563,8 +1642,9 @@ MarionetteDriverActor.prototype = {
|
|||
self.sendError("The frame closed during the click, recovering to allow further communications", 500, null, command_id);
|
||||
};
|
||||
curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true);
|
||||
this.sendAsync("clickElement", {element: aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("clickElement",
|
||||
{ element: aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1590,9 +1670,12 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("getElementAttribute", {element: aRequest.element,
|
||||
name: aRequest.name,
|
||||
command_id: command_id});
|
||||
this.sendAsync("getElementAttribute",
|
||||
{
|
||||
element: aRequest.element,
|
||||
name: aRequest.name
|
||||
},
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1620,8 +1703,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("getElementText", {element: aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("getElementText",
|
||||
{ element: aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1645,8 +1729,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("getElementTagName", {element: aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("getElementTagName",
|
||||
{ element: aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1670,8 +1755,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("isElementDisplayed", {element:aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("isElementDisplayed",
|
||||
{ element:aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1701,8 +1787,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("isElementEnabled", {element:aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("isElementEnabled",
|
||||
{ element:aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1735,8 +1822,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("isElementSelected", {element:aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("isElementSelected",
|
||||
{ element:aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1755,8 +1843,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("getElementSize", {element:aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("getElementSize",
|
||||
{ element:aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1783,9 +1872,12 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("sendKeysToElement", {element:aRequest.element,
|
||||
value: aRequest.value,
|
||||
command_id: command_id});
|
||||
this.sendAsync("sendKeysToElement",
|
||||
{
|
||||
element:aRequest.element,
|
||||
value: aRequest.value
|
||||
},
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1798,8 +1890,9 @@ MarionetteDriverActor.prototype = {
|
|||
this.command_id = this.getCommandId();
|
||||
this.logRequest("setTestName", aRequest);
|
||||
this.testName = aRequest.value;
|
||||
this.sendAsync("setTestName", {value: aRequest.value,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("setTestName",
|
||||
{ value: aRequest.value },
|
||||
this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1829,15 +1922,17 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("clearElement", {element:aRequest.element,
|
||||
command_id: command_id});
|
||||
this.sendAsync("clearElement",
|
||||
{ element:aRequest.element },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
getElementPosition: function MDA_getElementPosition(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("getElementPosition", {element:aRequest.element,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("getElementPosition",
|
||||
{ element:aRequest.element },
|
||||
this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1845,8 +1940,9 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
addCookie: function MDA_addCookie(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("addCookie", {cookie:aRequest.cookie,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("addCookie",
|
||||
{ cookie:aRequest.cookie },
|
||||
this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1854,7 +1950,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
getAllCookies: function MDA_getAllCookies() {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("getAllCookies", {command_id: this.command_id});
|
||||
this.sendAsync("getAllCookies", {}, this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1862,7 +1958,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
deleteAllCookies: function MDA_deleteAllCookies() {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("deleteAllCookies", {command_id: this.command_id});
|
||||
this.sendAsync("deleteAllCookies", {}, this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1870,8 +1966,9 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
deleteCookie: function MDA_deleteCookie(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("deleteCookie", {name:aRequest.name,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("deleteCookie",
|
||||
{ name:aRequest.name },
|
||||
this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1970,7 +2067,7 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
getAppCacheStatus: function MDA_getAppCacheStatus(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("getAppCacheStatus", {command_id: this.command_id});
|
||||
this.sendAsync("getAppCacheStatus", {}, this.command_id);
|
||||
},
|
||||
|
||||
_emu_cb_id: 0,
|
||||
|
@ -1988,7 +2085,7 @@ MarionetteDriverActor.prototype = {
|
|||
|
||||
emulatorCmdResult: function emulatorCmdResult(message) {
|
||||
if (this.context != "chrome") {
|
||||
this.sendAsync("emulatorCmdResult", message);
|
||||
this.sendAsync("emulatorCmdResult", message, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2031,8 +2128,9 @@ MarionetteDriverActor.prototype = {
|
|||
this.sendOk(command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("importScript", {script: aRequest.script,
|
||||
command_id: command_id});
|
||||
this.sendAsync("importScript",
|
||||
{ script: aRequest.script },
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2042,9 +2140,12 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
screenShot: function MDA_saveScreenshot(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
this.sendAsync("screenShot", {element: aRequest.element,
|
||||
highlights: aRequest.highlights,
|
||||
command_id: this.command_id});
|
||||
this.sendAsync("screenShot",
|
||||
{
|
||||
element: aRequest.element,
|
||||
highlights: aRequest.highlights
|
||||
},
|
||||
this.command_id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2148,11 +2249,12 @@ MarionetteDriverActor.prototype = {
|
|||
// XXX: Should have a better way of determining that this message
|
||||
// is from a remote frame.
|
||||
this.currentRemoteFrame.targetFrameId = this.generateFrameId(message.json.value);
|
||||
this.sendAsync(
|
||||
"setState",
|
||||
{scriptTimeout: this.scriptTimeout,
|
||||
searchTimeout: this.curBrowser.elementManager.searchTimeout,
|
||||
command_id: this.currentRemoteFrame.command_id});
|
||||
this.sendAsync("setState",
|
||||
{
|
||||
scriptTimeout: this.scriptTimeout,
|
||||
searchTimeout: this.curBrowser.elementManager.searchTimeout
|
||||
},
|
||||
this.currentRemoteFrame.command_id);
|
||||
}
|
||||
|
||||
let browserType;
|
||||
|
@ -2169,7 +2271,11 @@ MarionetteDriverActor.prototype = {
|
|||
this.curBrowser.elementManager.seenItems[reg.id] = Cu.getWeakReference(listenerWindow); //add to seenItems
|
||||
reg.importedScripts = this.importedScripts.path;
|
||||
if (nullPrevious && (this.curBrowser.curFrameId != null)) {
|
||||
this.sendAsync("newSession", {B2G: (appName == "B2G")});
|
||||
if (!this.sendAsync("newSession",
|
||||
{ B2G: (appName == "B2G") },
|
||||
this.newSessionCommandId)) {
|
||||
return;
|
||||
}
|
||||
if (this.curBrowser.newSession) {
|
||||
this.sendResponse(reg.id, this.newSessionCommandId);
|
||||
this.newSessionCommandId = null;
|
||||
|
|
|
@ -977,28 +977,21 @@ function do_load_child_test_harness()
|
|||
if (typeof do_load_child_test_harness.alreadyRun != "undefined")
|
||||
return;
|
||||
do_load_child_test_harness.alreadyRun = 1;
|
||||
|
||||
function addQuotes (str) {
|
||||
return '"' + str + '"';
|
||||
}
|
||||
var quoted_head_files = _HEAD_FILES.map(addQuotes);
|
||||
var quoted_tail_files = _TAIL_FILES.map(addQuotes);
|
||||
|
||||
_XPCSHELL_PROCESS = "parent";
|
||||
|
||||
let command =
|
||||
"const _HEAD_JS_PATH='" + _HEAD_JS_PATH + "'; "
|
||||
+ "const _HTTPD_JS_PATH='" + _HTTPD_JS_PATH + "'; "
|
||||
+ "const _HEAD_FILES=[" + quoted_head_files.join() + "];"
|
||||
+ "const _TAIL_FILES=[" + quoted_tail_files.join() + "];"
|
||||
"const _HEAD_JS_PATH=" + uneval(_HEAD_JS_PATH) + "; "
|
||||
+ "const _HTTPD_JS_PATH=" + uneval(_HTTPD_JS_PATH) + "; "
|
||||
+ "const _HEAD_FILES=" + uneval(_HEAD_FILES) + "; "
|
||||
+ "const _TAIL_FILES=" + uneval(_TAIL_FILES) + "; "
|
||||
+ "const _XPCSHELL_PROCESS='child';";
|
||||
|
||||
if (this._TESTING_MODULES_DIR) {
|
||||
normalized = this._TESTING_MODULES_DIR.replace('\\', '\\\\', 'g');
|
||||
command += "const _TESTING_MODULES_DIR='" + normalized + "'; ";
|
||||
command += " const _TESTING_MODULES_DIR=" + uneval(_TESTING_MODULES_DIR) + ";";
|
||||
}
|
||||
|
||||
command += "load(_HEAD_JS_PATH);";
|
||||
command += " load(_HEAD_JS_PATH);";
|
||||
|
||||
sendCommand(command);
|
||||
}
|
||||
|
|
|
@ -549,13 +549,14 @@ File.makeDir = function makeDir(path, options) {
|
|||
* @param {string} path The path to the file.
|
||||
* @param {number=} bytes Optionally, an upper bound to the number of bytes
|
||||
* to read.
|
||||
* @param {JSON} options Additional options.
|
||||
*
|
||||
* @resolves {Uint8Array} A buffer holding the bytes
|
||||
* read from the file.
|
||||
*/
|
||||
File.read = function read(path, bytes) {
|
||||
File.read = function read(path, bytes, options) {
|
||||
let promise = Scheduler.post("read",
|
||||
[Type.path.toMsg(path), bytes], path);
|
||||
[Type.path.toMsg(path), bytes, options], path);
|
||||
return promise.then(
|
||||
function onSuccess(data) {
|
||||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
||||
|
|
|
@ -889,7 +889,11 @@ let test_duration = maketest("duration", function duration(test) {
|
|||
let pathDest = OS.Path.join(OS.Constants.Path.tmpDir,
|
||||
"osfile async test read writeAtomic.tmp");
|
||||
let tmpPath = pathDest + ".tmp";
|
||||
let contents = yield OS.File.read(pathSource);
|
||||
let readOptions = {
|
||||
outExecutionDuration: null
|
||||
};
|
||||
let contents = yield OS.File.read(pathSource, undefined, readOptions);
|
||||
testOptions(readOptions);
|
||||
// Options structure passed to a OS.File writeAtomic method.
|
||||
let writeAtomicOptions = {
|
||||
// This field should be first initialized with the actual
|
||||
|
|
|
@ -63,6 +63,7 @@ EXTRA_JS_MODULES = \
|
|||
ColorConversion.js \
|
||||
ClusterLib.js \
|
||||
BookmarkJSONUtils.jsm \
|
||||
PlacesBackups.jsm \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_JS_MODULES = \
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab filetype=javascript
|
||||
* 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/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PlacesBackups"];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
this.PlacesBackups = {
|
||||
get _filenamesRegex() {
|
||||
// Get the localized backup filename, will be used to clear out
|
||||
// old backups with a localized name (bug 445704).
|
||||
let localizedFilename =
|
||||
PlacesUtils.getFormattedString("bookmarksArchiveFilename", [new Date()]);
|
||||
let localizedFilenamePrefix =
|
||||
localizedFilename.substr(0, localizedFilename.indexOf("-"));
|
||||
delete this._filenamesRegex;
|
||||
return this._filenamesRegex =
|
||||
new RegExp("^(bookmarks|" + localizedFilenamePrefix + ")-([0-9-]+)\.(json|html)");
|
||||
},
|
||||
|
||||
get folder() {
|
||||
let bookmarksBackupDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
bookmarksBackupDir.append("bookmarkbackups");
|
||||
if (!bookmarksBackupDir.exists()) {
|
||||
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0700", 8));
|
||||
if (!bookmarksBackupDir.exists())
|
||||
throw("Unable to create bookmarks backup folder");
|
||||
}
|
||||
delete this.folder;
|
||||
return this.folder = bookmarksBackupDir;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cache current backups in a sorted (by date DESC) array.
|
||||
*/
|
||||
get entries() {
|
||||
delete this.entries;
|
||||
this.entries = [];
|
||||
let files = this.folder.directoryEntries;
|
||||
while (files.hasMoreElements()) {
|
||||
let entry = files.getNext().QueryInterface(Ci.nsIFile);
|
||||
// A valid backup is any file that matches either the localized or
|
||||
// not-localized filename (bug 445704).
|
||||
let matches = entry.leafName.match(this._filenamesRegex);
|
||||
if (!entry.isHidden() && matches) {
|
||||
// Remove bogus backups in future dates.
|
||||
if (this.getDateForFile(entry) > new Date()) {
|
||||
entry.remove(false);
|
||||
continue;
|
||||
}
|
||||
this.entries.push(entry);
|
||||
}
|
||||
}
|
||||
this.entries.sort((a, b) => {
|
||||
let aDate = this.getDateForFile(a);
|
||||
let bDate = this.getDateForFile(b);
|
||||
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
|
||||
});
|
||||
return this.entries;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a filename for bookmarks backup files.
|
||||
*
|
||||
* @param [optional] aDateObj
|
||||
* Date object used to build the filename.
|
||||
* Will use current date if empty.
|
||||
* @return A bookmarks backup filename.
|
||||
*/
|
||||
getFilenameForDate: function PB_getFilenameForDate(aDateObj) {
|
||||
let dateObj = aDateObj || new Date();
|
||||
// Use YYYY-MM-DD (ISO 8601) as it doesn't contain illegal characters
|
||||
// and makes the alphabetical order of multiple backup files more useful.
|
||||
return "bookmarks-" + dateObj.toLocaleFormat("%Y-%m-%d") + ".json";
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a Date object from a backup file. The date is the backup
|
||||
* creation date.
|
||||
*
|
||||
* @param aBackupFile
|
||||
* nsIFile of the backup.
|
||||
* @return A Date object for the backup's creation time.
|
||||
*/
|
||||
getDateForFile: function PB_getDateForFile(aBackupFile) {
|
||||
let filename = aBackupFile.leafName;
|
||||
let matches = filename.match(this._filenamesRegex);
|
||||
if (!matches)
|
||||
throw("Invalid backup file name: " + filename);
|
||||
return new Date(matches[2].replace(/-/g, "/"));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the most recent backup file.
|
||||
*
|
||||
* @param [optional] aFileExt
|
||||
* Force file extension. Either "html" or "json".
|
||||
* Will check for both if not defined.
|
||||
* @returns nsIFile backup file
|
||||
*/
|
||||
getMostRecent: function PB_getMostRecent(aFileExt) {
|
||||
let fileExt = aFileExt || "(json|html)";
|
||||
for (let i = 0; i < this.entries.length; i++) {
|
||||
let rx = new RegExp("\." + fileExt + "$");
|
||||
if (this.entries[i].leafName.match(rx))
|
||||
return this.entries[i];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes bookmarks using JSON, and writes to the supplied file.
|
||||
* Note: any item that should not be backed up must be annotated with
|
||||
* "places/excludeFromBackup".
|
||||
*
|
||||
* @param aFile
|
||||
* nsIFile where to save JSON backup.
|
||||
* @return {Promise}
|
||||
*/
|
||||
saveBookmarksToJSONFile: function PB_saveBookmarksToJSONFile(aFile) {
|
||||
return Task.spawn(function() {
|
||||
if (!aFile.exists())
|
||||
aFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0600", 8));
|
||||
if (!aFile.exists() || !aFile.isWritable()) {
|
||||
throw new Error("Unable to create bookmarks backup file: " + aFile.leafName);
|
||||
}
|
||||
|
||||
yield BookmarkJSONUtils.exportToFile(aFile);
|
||||
|
||||
if (aFile.parent.equals(this.folder)) {
|
||||
// Update internal cache.
|
||||
this.entries.push(aFile);
|
||||
} else {
|
||||
// If we are saving to a folder different than our backups folder, then
|
||||
// we also want to copy this new backup to it.
|
||||
// This way we ensure the latest valid backup is the same saved by the
|
||||
// user. See bug 424389.
|
||||
let latestBackup = this.getMostRecent("json");
|
||||
if (!latestBackup || latestBackup != aFile) {
|
||||
let name = this.getFilenameForDate();
|
||||
let file = this.folder.clone();
|
||||
file.append(name);
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
} else {
|
||||
// Update internal cache if we are not replacing an existing
|
||||
// backup file.
|
||||
this.entries.push(file);
|
||||
}
|
||||
aFile.copyTo(this.folder, name);
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a dated backup in <profile>/bookmarkbackups.
|
||||
* Stores the bookmarks using JSON.
|
||||
* Note: any item that should not be backed up must be annotated with
|
||||
* "places/excludeFromBackup".
|
||||
*
|
||||
* @param [optional] int aMaxBackups
|
||||
* The maximum number of backups to keep.
|
||||
* @param [optional] bool aForceBackup
|
||||
* Forces creating a backup even if one was already
|
||||
* created that day (overwrites).
|
||||
* @return {Promise}
|
||||
*/
|
||||
create: function PB_create(aMaxBackups, aForceBackup) {
|
||||
return Task.spawn(function() {
|
||||
// Construct the new leafname.
|
||||
let newBackupFilename = this.getFilenameForDate();
|
||||
let mostRecentBackupFile = this.getMostRecent();
|
||||
|
||||
if (!aForceBackup) {
|
||||
let numberOfBackupsToDelete = 0;
|
||||
if (aMaxBackups !== undefined && aMaxBackups > -1)
|
||||
numberOfBackupsToDelete = this.entries.length - aMaxBackups;
|
||||
|
||||
if (numberOfBackupsToDelete > 0) {
|
||||
// If we don't have today's backup, remove one more so that
|
||||
// the total backups after this operation does not exceed the
|
||||
// number specified in the pref.
|
||||
if (!mostRecentBackupFile ||
|
||||
mostRecentBackupFile.leafName != newBackupFilename)
|
||||
numberOfBackupsToDelete++;
|
||||
|
||||
while (numberOfBackupsToDelete--) {
|
||||
let oldestBackup = this.entries.pop();
|
||||
oldestBackup.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Do nothing if we already have this backup or we don't want backups.
|
||||
if (aMaxBackups === 0 ||
|
||||
(mostRecentBackupFile &&
|
||||
mostRecentBackupFile.leafName == newBackupFilename))
|
||||
return;
|
||||
}
|
||||
|
||||
let newBackupFile = this.folder.clone();
|
||||
newBackupFile.append(newBackupFilename);
|
||||
|
||||
if (aForceBackup && newBackupFile.exists())
|
||||
newBackupFile.remove(false);
|
||||
|
||||
if (newBackupFile.exists())
|
||||
return;
|
||||
|
||||
yield this.saveBookmarksToJSONFile(newBackupFile);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
|
@ -76,8 +76,7 @@ if CONFIG['MOZ_OPUS']:
|
|||
if CONFIG['MOZ_WEBM']:
|
||||
add_tier_dir('platform', 'media/libnestegg')
|
||||
|
||||
if CONFIG['MOZ_WEBVTT']:
|
||||
add_tier_dir('platform', 'media/webvtt')
|
||||
add_tier_dir('platform', 'media/webvtt')
|
||||
|
||||
if CONFIG['MOZ_VP8'] and not CONFIG['MOZ_NATIVE_LIBVPX']:
|
||||
add_tier_dir('platform', 'media/libvpx')
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
/**
|
||||
* Provides information about a specific implementation class. If you want
|
||||
* your class to implement nsIClassInfo, see nsIClassInfo.h for instructions --
|
||||
* you most likely do not want to inherit from nsIClassInfo.
|
||||
* your class to implement nsIClassInfo, see nsIClassInfoImpl.h for
|
||||
* instructions--you most likely do not want to inherit from nsIClassInfo.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(986c11d0-f340-11d4-9075-0010a4e73d9a)]
|
||||
|
|
Загрузка…
Ссылка в новой задаче