mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to b2g-inbound
This commit is contained in:
commit
e5c5b4e184
@ -85,7 +85,7 @@ public:
|
||||
virtual ~MediaDocumentStreamListener();
|
||||
void SetStreamListener(nsIStreamListener *aListener);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
||||
|
@ -60,9 +60,7 @@ void
|
||||
AudioNodeStream::SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
|
||||
double aStreamTime)
|
||||
{
|
||||
TrackTicks ticks =
|
||||
WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime(
|
||||
aStreamTime, this, aRelativeToStream);
|
||||
TrackTicks ticks = TicksFromDestinationTime(aRelativeToStream, aStreamTime);
|
||||
mEngine->SetStreamTimeParameter(aIndex, ticks);
|
||||
}
|
||||
|
||||
@ -517,4 +515,36 @@ AudioNodeStream::FinishOutput()
|
||||
}
|
||||
}
|
||||
|
||||
TrackTicks
|
||||
AudioNodeStream::TicksFromDestinationTime(MediaStream* aDestination,
|
||||
double aSeconds)
|
||||
{
|
||||
MOZ_ASSERT(aDestination->AsAudioNodeStream() &&
|
||||
aDestination->AsAudioNodeStream()->SampleRate() == SampleRate());
|
||||
|
||||
double destinationSeconds = std::max(0.0, aSeconds);
|
||||
StreamTime streamTime = SecondsToMediaTime(destinationSeconds);
|
||||
// MediaTime does not have the resolution of double
|
||||
double offset = destinationSeconds - MediaTimeToSeconds(streamTime);
|
||||
|
||||
GraphTime graphTime = aDestination->StreamTimeToGraphTime(streamTime);
|
||||
StreamTime thisStreamTime = GraphTimeToStreamTimeOptimistic(graphTime);
|
||||
double thisSeconds = MediaTimeToSeconds(thisStreamTime) + offset;
|
||||
MOZ_ASSERT(thisSeconds >= 0.0);
|
||||
// Round to nearest
|
||||
TrackTicks ticks = thisSeconds * SampleRate() + 0.5;
|
||||
return ticks;
|
||||
}
|
||||
|
||||
double
|
||||
AudioNodeStream::DestinationTimeFromTicks(AudioNodeStream* aDestination,
|
||||
TrackTicks aPosition)
|
||||
{
|
||||
MOZ_ASSERT(SampleRate() == aDestination->SampleRate());
|
||||
StreamTime sourceTime = TicksToTimeRoundDown(SampleRate(), aPosition);
|
||||
GraphTime graphTime = StreamTimeToGraphTime(sourceTime);
|
||||
StreamTime destinationTime = aDestination->GraphTimeToStreamTimeOptimistic(graphTime);
|
||||
return MediaTimeToSeconds(destinationTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -128,6 +128,19 @@ public:
|
||||
AudioNodeEngine* Engine() { return mEngine; }
|
||||
TrackRate SampleRate() const { return mSampleRate; }
|
||||
|
||||
/**
|
||||
* Convert a time in seconds on the destination stream to TrackTicks
|
||||
* on this stream.
|
||||
*/
|
||||
TrackTicks TicksFromDestinationTime(MediaStream* aDestination,
|
||||
double aSeconds);
|
||||
/**
|
||||
* Get the destination stream time in seconds corresponding to a position on
|
||||
* this stream.
|
||||
*/
|
||||
double DestinationTimeFromTicks(AudioNodeStream* aDestination,
|
||||
TrackTicks aPosition);
|
||||
|
||||
protected:
|
||||
void AdvanceOutputSegment();
|
||||
void FinishOutput();
|
||||
|
@ -314,10 +314,9 @@ private:
|
||||
// Add the delay caused by the main thread
|
||||
playbackTick += mSharedBuffers->DelaySoFar();
|
||||
// Compute the playback time in the coordinate system of the destination
|
||||
// FIXME: bug 970773
|
||||
double playbackTime =
|
||||
WebAudioUtils::StreamPositionToDestinationTime(playbackTick,
|
||||
mSource,
|
||||
mDestination);
|
||||
mSource->DestinationTimeFromTicks(mDestination, playbackTick);
|
||||
|
||||
class Command : public nsRunnable
|
||||
{
|
||||
|
@ -23,35 +23,11 @@ struct ConvertTimeToTickHelper
|
||||
{
|
||||
ConvertTimeToTickHelper* This = static_cast<ConvertTimeToTickHelper*> (aClosure);
|
||||
MOZ_ASSERT(This->mSourceStream->SampleRate() == This->mDestinationStream->SampleRate());
|
||||
return WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime(
|
||||
aTime, This->mSourceStream, This->mDestinationStream);
|
||||
return This->mSourceStream->
|
||||
TicksFromDestinationTime(This->mDestinationStream, aTime);
|
||||
}
|
||||
};
|
||||
|
||||
TrackTicks
|
||||
WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime(double aTime,
|
||||
AudioNodeStream* aSource,
|
||||
MediaStream* aDestination)
|
||||
{
|
||||
StreamTime streamTime = std::max<MediaTime>(0, SecondsToMediaTime(aTime));
|
||||
GraphTime graphTime = aDestination->StreamTimeToGraphTime(streamTime);
|
||||
StreamTime thisStreamTime = aSource->GraphTimeToStreamTimeOptimistic(graphTime);
|
||||
TrackTicks ticks = TimeToTicksRoundUp(aSource->SampleRate(), thisStreamTime);
|
||||
return ticks;
|
||||
}
|
||||
|
||||
double
|
||||
WebAudioUtils::StreamPositionToDestinationTime(TrackTicks aSourcePosition,
|
||||
AudioNodeStream* aSource,
|
||||
AudioNodeStream* aDestination)
|
||||
{
|
||||
MOZ_ASSERT(aSource->SampleRate() == aDestination->SampleRate());
|
||||
StreamTime sourceTime = TicksToTimeRoundDown(aSource->SampleRate(), aSourcePosition);
|
||||
GraphTime graphTime = aSource->StreamTimeToGraphTime(sourceTime);
|
||||
StreamTime destinationTime = aDestination->GraphTimeToStreamTimeOptimistic(graphTime);
|
||||
return MediaTimeToSeconds(destinationTime);
|
||||
}
|
||||
|
||||
void
|
||||
WebAudioUtils::ConvertAudioParamToTicks(AudioParamTimeline& aParam,
|
||||
AudioNodeStream* aSource,
|
||||
|
@ -50,15 +50,6 @@ struct WebAudioUtils {
|
||||
return 1.0 - std::exp(-1.0 / (aDuration * aSampleRate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a time in second relative to the destination stream to
|
||||
* TrackTicks relative to the source stream.
|
||||
*/
|
||||
static TrackTicks
|
||||
ConvertDestinationStreamTimeToSourceStreamTime(double aTime,
|
||||
AudioNodeStream* aSource,
|
||||
MediaStream* aDestination);
|
||||
|
||||
/**
|
||||
* Converts AudioParamTimeline floating point time values to tick values
|
||||
* with respect to a source and a destination AudioNodeStream.
|
||||
@ -114,14 +105,6 @@ struct WebAudioUtils {
|
||||
return aTime >= 0 && aTime <= (MEDIA_TIME_MAX >> MEDIA_TIME_FRAC_BITS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a stream position into the time coordinate of the destination
|
||||
* stream.
|
||||
*/
|
||||
static double StreamPositionToDestinationTime(TrackTicks aSourcePosition,
|
||||
AudioNodeStream* aSource,
|
||||
AudioNodeStream* aDestination);
|
||||
|
||||
/**
|
||||
* Converts a floating point value to an integral type in a safe and
|
||||
* platform agnostic way. The following program demonstrates the kinds
|
||||
|
@ -63,6 +63,8 @@ support-files =
|
||||
[test_bug875221.html]
|
||||
[test_bug875402.html]
|
||||
[test_bug894150.html]
|
||||
[test_bug956489.html]
|
||||
[test_bug964376.html]
|
||||
[test_channelMergerNode.html]
|
||||
[test_channelMergerNodeWithVolume.html]
|
||||
[test_channelSplitterNode.html]
|
||||
|
55
content/media/webaudio/test/test_bug956489.html
Normal file
55
content/media/webaudio/test/test_bug956489.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test when and currentTime are in the same coordinate system</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var freq = 330;
|
||||
|
||||
var context = new AudioContext();
|
||||
|
||||
var buffer = context.createBuffer(1, context.sampleRate / freq, context.sampleRate);
|
||||
for (var i = 0; i < buffer.length; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(2 * Math.PI * i / buffer.length);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.loop = true;
|
||||
source.buffer = buffer;
|
||||
|
||||
setTimeout(function () {
|
||||
var finished = false;
|
||||
|
||||
source.start(context.currentTime);
|
||||
var processor = context.createScriptProcessor(256, 1, 1);
|
||||
processor.onaudioprocess = function (e) {
|
||||
if (finished) return;
|
||||
var c = e.inputBuffer.getChannelData(0);
|
||||
var result = true;
|
||||
|
||||
for (var i = 0; i < buffer.length; ++i) {
|
||||
if (Math.abs(c[i] - buffer.getChannelData(0)[i]) > 1e-9) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
finished = true;
|
||||
ok(result, "when and currentTime are in same time coordinate system");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
processor.connect(context.destination);
|
||||
source.connect(processor);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
64
content/media/webaudio/test/test_bug964376.html
Normal file
64
content/media/webaudio/test/test_bug964376.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test repeating audio is not distorted</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function gcd(a, b) {
|
||||
if (b === 0) {
|
||||
return a;
|
||||
}
|
||||
return gcd(b, a % b);
|
||||
}
|
||||
|
||||
var SAMPLE_PLACEMENT = 128;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
|
||||
createGraph: function(context) {
|
||||
var freq = Math.round(context.sampleRate / SAMPLE_PLACEMENT);
|
||||
var dur = context.sampleRate / gcd(freq, context.sampleRate);
|
||||
var buffer = context.createBuffer(1, dur, context.sampleRate);
|
||||
|
||||
for (var i = 0; i < context.sampleRate; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(freq * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.loop = true;
|
||||
source.playbackRate.setValueAtTime(0.5, SAMPLE_PLACEMENT / context.sampleRate);
|
||||
source.start(0);
|
||||
|
||||
return source;
|
||||
},
|
||||
|
||||
createExpectedBuffers: function(context) {
|
||||
var freq = Math.round(context.sampleRate / SAMPLE_PLACEMENT);
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
var c = expectedBuffer.getChannelData(0);
|
||||
for (var i = 0; i < c.length; ++i) {
|
||||
if (i < SAMPLE_PLACEMENT) {
|
||||
c[i] = Math.sin(freq * 2 * Math.PI * i / context.sampleRate);
|
||||
} else {
|
||||
c[i] = Math.sin(freq / 2 * 2 * Math.PI * (i + SAMPLE_PLACEMENT) / context.sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -57,11 +57,11 @@ private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
class WorkerPromiseTask MOZ_FINAL : public WorkerRunnable
|
||||
class WorkerPromiseTask MOZ_FINAL : public WorkerSameThreadRunnable
|
||||
{
|
||||
public:
|
||||
WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
: WorkerSameThreadRunnable(aWorkerPrivate)
|
||||
, mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
@ -161,7 +161,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerPromiseResolverTask MOZ_FINAL : public WorkerRunnable,
|
||||
class WorkerPromiseResolverTask MOZ_FINAL : public WorkerSameThreadRunnable,
|
||||
public PromiseResolverMixin
|
||||
{
|
||||
public:
|
||||
@ -169,7 +169,7 @@ public:
|
||||
Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
: WorkerSameThreadRunnable(aWorkerPrivate),
|
||||
PromiseResolverMixin(aPromise, aValue, aState)
|
||||
{}
|
||||
|
||||
@ -832,7 +832,7 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WorkerPromiseTask> task = new WorkerPromiseTask(worker, this);
|
||||
worker->Dispatch(task);
|
||||
task->Dispatch(worker->GetJSContext());
|
||||
}
|
||||
mTaskPending = true;
|
||||
}
|
||||
@ -1045,7 +1045,7 @@ Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WorkerPromiseResolverTask> task =
|
||||
new WorkerPromiseResolverTask(worker, this, aValue, aState);
|
||||
worker->Dispatch(task);
|
||||
task->Dispatch(worker->GetJSContext());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -7,10 +7,12 @@
|
||||
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Value.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
@ -478,3 +480,43 @@ MainThreadWorkerControlRunnable::PostDispatch(JSContext* aCx,
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
|
||||
|
||||
bool
|
||||
WorkerSameThreadRunnable::PreDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerSameThreadRunnable::PostDispatch(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
if (aDispatchResult) {
|
||||
DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
|
||||
// Should never fail since if this thread is still running, so should the
|
||||
// parent and it should be able to process a control runnable.
|
||||
MOZ_ASSERT(willIncrement);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerSameThreadRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aRunResult)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
DebugOnly<bool> willDecrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
|
||||
MOZ_ASSERT(willDecrement);
|
||||
|
||||
if (!aRunResult) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,6 +314,34 @@ protected:
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
// A WorkerRunnable that should be dispatched from the worker to itself for
|
||||
// async tasks. This will increment the busy count PostDispatch() (only if
|
||||
// dispatch was successful) and decrement it in PostRun().
|
||||
//
|
||||
// Async tasks will almost always want to use this since
|
||||
// a WorkerSameThreadRunnable keeps the Worker from being GCed.
|
||||
class WorkerSameThreadRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
WorkerSameThreadRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
|
||||
{ }
|
||||
|
||||
virtual ~WorkerSameThreadRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aRunResult) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workerrunnable_h__
|
||||
|
@ -1038,6 +1038,12 @@ nsEditorEventListener::ShouldHandleNativeKeyBindings(nsIDOMEvent* aKeyEvent)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mEditor->GetDocument();
|
||||
if (doc->HasFlag(NODE_IS_EDITABLE)) {
|
||||
// Don't need to perform any checks in designMode documents.
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIContent* editingHost = htmlEditor->GetActiveEditingHost();
|
||||
if (!editingHost) {
|
||||
return false;
|
||||
|
@ -145,6 +145,8 @@ skip-if = toolkit == 'android'
|
||||
[test_bug796839.html]
|
||||
[test_bug832025.html]
|
||||
[test_bug857487.html]
|
||||
[test_bug966155.html]
|
||||
skip-if = os != "win"
|
||||
[test_bug966552.html]
|
||||
skip-if = os != "win"
|
||||
[test_contenteditable_focus.html]
|
||||
|
57
editor/libeditor/html/tests/test_bug966155.html
Normal file
57
editor/libeditor/html/tests/test_bug966155.html
Normal file
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=966155
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 966155</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=966155">Mozilla Bug 966155</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
var win = window.open("data:text/html,<input><iframe onload=\"contentDocument.designMode = 'on';\">", "", "test-966155");
|
||||
win.addEventListener("load", function onLoad() {
|
||||
win.removeEventListener("load", onLoad);
|
||||
runTest(win);
|
||||
}, false);
|
||||
});
|
||||
|
||||
function runTest(win) {
|
||||
SimpleTest.waitForFocus(function() {
|
||||
var doc = win.document;
|
||||
var iframe = doc.querySelector("iframe");
|
||||
var iframeDoc = iframe.contentDocument;
|
||||
var input = doc.querySelector("input");
|
||||
iframe.focus();
|
||||
iframeDoc.body.focus();
|
||||
// Type some text
|
||||
"test".split("").forEach(function(letter) {
|
||||
synthesizeKey(letter, {}, win);
|
||||
});
|
||||
is(iframeDoc.body.textContent, "test", "entered the text");
|
||||
// focus the input box
|
||||
input.focus();
|
||||
// press tab
|
||||
synthesizeKey("VK_TAB", {}, win);
|
||||
// Now press Ctrl+Backspace
|
||||
synthesizeKey("VK_BACK_SPACE", {ctrlKey: true}, win);
|
||||
is(iframeDoc.body.textContent, "", "deleted the text");
|
||||
win.close();
|
||||
SimpleTest.finish();
|
||||
}, win);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -10,7 +10,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=966552
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=966552">Mozilla Bug 289384</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=966552">Mozilla Bug 966552</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
@ -380,7 +380,7 @@ GrallocImage::GetAsSourceSurface()
|
||||
|
||||
|
||||
TextureClient*
|
||||
GrallocImage::GetTextureClient()
|
||||
GrallocImage::GetTextureClient(CompositableClient* aClient)
|
||||
{
|
||||
if (!mTextureClient) {
|
||||
const SurfaceDescriptor& sd = GetSurfaceDescriptor();
|
||||
|
@ -149,7 +149,7 @@ public:
|
||||
|
||||
virtual ISharedImage* AsSharedImage() MOZ_OVERRIDE { return this; }
|
||||
|
||||
virtual TextureClient* GetTextureClient() MOZ_OVERRIDE;
|
||||
virtual TextureClient* GetTextureClient(CompositableClient* aClient) MOZ_OVERRIDE;
|
||||
|
||||
virtual uint8_t* GetBuffer()
|
||||
{
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "GrallocImages.h"
|
||||
#endif
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "mozilla/gfx/QuartzSupport.h"
|
||||
@ -38,15 +39,12 @@
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
using namespace android;
|
||||
using mozilla::gfx::DataSourceSurface;
|
||||
using mozilla::gfx::SourceSurface;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class DataSourceSurface;
|
||||
class SourceSurface;
|
||||
|
||||
Atomic<int32_t> Image::sSerialCounter(0);
|
||||
|
||||
@ -711,5 +709,45 @@ RemoteBitmapImage::GetAsSourceSurface()
|
||||
return newSurf;
|
||||
}
|
||||
|
||||
CairoImage::CairoImage()
|
||||
: Image(nullptr, ImageFormat::CAIRO_SURFACE)
|
||||
{}
|
||||
|
||||
CairoImage::~CairoImage()
|
||||
{
|
||||
}
|
||||
|
||||
TextureClient*
|
||||
CairoImage::GetTextureClient(CompositableClient *aClient)
|
||||
{
|
||||
CompositableForwarder* forwarder = aClient->GetForwarder();
|
||||
RefPtr<TextureClient> textureClient = mTextureClients.Get(forwarder->GetSerial());
|
||||
if (textureClient) {
|
||||
return textureClient;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> surface = GetAsSourceSurface();
|
||||
MOZ_ASSERT(surface);
|
||||
|
||||
textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(),
|
||||
TEXTURE_FLAGS_DEFAULT);
|
||||
MOZ_ASSERT(textureClient->AsTextureClientDrawTarget());
|
||||
if (!textureClient->AsTextureClientDrawTarget()->AllocateForSurface(surface->GetSize()) ||
|
||||
!textureClient->Lock(OPEN_WRITE_ONLY)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
// We must not keep a reference to the DrawTarget after it has been unlocked.
|
||||
RefPtr<DrawTarget> dt = textureClient->AsTextureClientDrawTarget()->GetAsDrawTarget();
|
||||
dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
|
||||
}
|
||||
|
||||
textureClient->Unlock();
|
||||
|
||||
mTextureClients.Put(forwarder->GetSerial(), textureClient);
|
||||
return textureClient;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
#ifndef XPCOM_GLUE_AVOID_NSPR
|
||||
/**
|
||||
@ -146,6 +147,8 @@ class ImageClient;
|
||||
class SharedPlanarYCbCrImage;
|
||||
class DeprecatedSharedPlanarYCbCrImage;
|
||||
class TextureClient;
|
||||
class CompositableClient;
|
||||
class CompositableForwarder;
|
||||
class SurfaceDescriptor;
|
||||
|
||||
struct ImageBackendData
|
||||
@ -165,7 +168,7 @@ public:
|
||||
* For use with the CompositableClient only (so that the later can
|
||||
* synchronize the TextureClient with the TextureHost).
|
||||
*/
|
||||
virtual TextureClient* GetTextureClient() = 0;
|
||||
virtual TextureClient* GetTextureClient(CompositableClient* aClient) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -905,7 +908,8 @@ protected:
|
||||
* device output color space. This class is very simple as all backends
|
||||
* have to know about how to deal with drawing a cairo image.
|
||||
*/
|
||||
class CairoImage : public Image {
|
||||
class CairoImage : public Image,
|
||||
public ISharedImage {
|
||||
public:
|
||||
struct Data {
|
||||
gfxASurface* mDeprecatedSurface;
|
||||
@ -939,9 +943,14 @@ public:
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
virtual ISharedImage* AsSharedImage() { return this; }
|
||||
virtual uint8_t* GetBuffer() { return nullptr; }
|
||||
virtual TextureClient* GetTextureClient(CompositableClient* aClient);
|
||||
|
||||
gfx::IntSize GetSize() { return mSize; }
|
||||
|
||||
CairoImage() : Image(nullptr, ImageFormat::CAIRO_SURFACE) {}
|
||||
CairoImage();
|
||||
~CairoImage();
|
||||
|
||||
nsCountedRef<nsMainThreadSurfaceRef> mDeprecatedSurface;
|
||||
gfx::IntSize mSize;
|
||||
@ -949,6 +958,7 @@ public:
|
||||
// mSourceSurface wraps mDeprrecatedSurface's data, therefore it should not
|
||||
// outlive mDeprecatedSurface
|
||||
nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
|
||||
nsDataHashtable<nsUint32HashKey, RefPtr<TextureClient> > mTextureClients;
|
||||
};
|
||||
|
||||
class RemoteBitmapImage : public Image {
|
||||
|
@ -10,7 +10,7 @@ using namespace mozilla;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
TextureClient*
|
||||
MacIOSurfaceImage::GetTextureClient()
|
||||
MacIOSurfaceImage::GetTextureClient(CompositableClient* aClient)
|
||||
{
|
||||
if (!mTextureClient) {
|
||||
RefPtr<MacIOSurfaceTextureClientOGL> buffer =
|
||||
|
@ -51,7 +51,7 @@ public:
|
||||
|
||||
virtual TemporaryRef<gfx::SourceSurface> GetAsSourceSurface();
|
||||
|
||||
virtual TextureClient* GetTextureClient() MOZ_OVERRIDE;
|
||||
virtual TextureClient* GetTextureClient(CompositableClient* aClient) MOZ_OVERRIDE;
|
||||
virtual uint8_t* GetBuffer() MOZ_OVERRIDE { return nullptr; }
|
||||
|
||||
MacIOSurfaceImage() : Image(nullptr, ImageFormat::MAC_IOSURFACE) {}
|
||||
|
@ -145,9 +145,9 @@ ImageClientSingle::UpdateImageInternal(ImageContainer* aContainer,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (image->AsSharedImage() && image->AsSharedImage()->GetTextureClient()) {
|
||||
if (image->AsSharedImage() && image->AsSharedImage()->GetTextureClient(this)) {
|
||||
// fast path: no need to allocate and/or copy image data
|
||||
RefPtr<TextureClient> texture = image->AsSharedImage()->GetTextureClient();
|
||||
RefPtr<TextureClient> texture = image->AsSharedImage()->GetTextureClient(this);
|
||||
|
||||
|
||||
if (mFrontBuffer) {
|
||||
|
@ -265,7 +265,6 @@ public:
|
||||
aLayer == mLayer ||
|
||||
aFlags & FORCE_DETACH) {
|
||||
SetLayer(nullptr);
|
||||
SetCompositor(nullptr);
|
||||
mAttached = false;
|
||||
mKeepAttached = false;
|
||||
if (mBackendData) {
|
||||
|
@ -48,7 +48,8 @@ class CompositableForwarder : public ISurfaceAllocator
|
||||
public:
|
||||
|
||||
CompositableForwarder()
|
||||
: mMultiProcess(false)
|
||||
: mSerial(++sSerialCounter)
|
||||
, mMultiProcess(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
@ -246,10 +247,14 @@ public:
|
||||
return mTextureFactoryIdentifier;
|
||||
}
|
||||
|
||||
int32_t GetSerial() { return mSerial; }
|
||||
|
||||
protected:
|
||||
TextureFactoryIdentifier mTextureFactoryIdentifier;
|
||||
bool mMultiProcess;
|
||||
nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
|
||||
const int32_t mSerial;
|
||||
static mozilla::Atomic<int32_t> sSerialCounter;
|
||||
bool mMultiProcess;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -28,6 +28,8 @@ namespace layers {
|
||||
|
||||
/*static*/ CompositorChild* CompositorChild::sCompositor;
|
||||
|
||||
Atomic<int32_t> CompositableForwarder::sSerialCounter(0);
|
||||
|
||||
CompositorChild::CompositorChild(ClientLayerManager *aLayerManager)
|
||||
: mLayerManager(aLayerManager)
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ DeprecatedSharedPlanarYCbCrImage::~DeprecatedSharedPlanarYCbCrImage() {
|
||||
}
|
||||
|
||||
TextureClient*
|
||||
SharedPlanarYCbCrImage::GetTextureClient()
|
||||
SharedPlanarYCbCrImage::GetTextureClient(CompositableClient* aClient)
|
||||
{
|
||||
return mTextureClient.get();
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ public:
|
||||
~SharedPlanarYCbCrImage();
|
||||
|
||||
virtual ISharedImage* AsSharedImage() MOZ_OVERRIDE { return this; }
|
||||
virtual TextureClient* GetTextureClient() MOZ_OVERRIDE;
|
||||
virtual TextureClient* GetTextureClient(CompositableClient* aClient) MOZ_OVERRIDE;
|
||||
virtual uint8_t* GetBuffer() MOZ_OVERRIDE;
|
||||
|
||||
virtual already_AddRefed<gfxASurface> DeprecatedGetAsSurface() MOZ_OVERRIDE;
|
||||
|
@ -234,7 +234,7 @@ SharedRGBImage::GetBufferSize()
|
||||
}
|
||||
|
||||
TextureClient*
|
||||
SharedRGBImage::GetTextureClient()
|
||||
SharedRGBImage::GetTextureClient(CompositableClient* aClient)
|
||||
{
|
||||
return mTextureClient.get();
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ public:
|
||||
|
||||
bool AllocateBuffer(nsIntSize aSize, gfxImageFormat aImageFormat);
|
||||
|
||||
TextureClient* GetTextureClient() MOZ_OVERRIDE { return nullptr; }
|
||||
TextureClient* GetTextureClient(CompositableClient* aClient) MOZ_OVERRIDE { return nullptr; }
|
||||
|
||||
protected:
|
||||
gfx::IntSize mSize;
|
||||
@ -110,7 +110,7 @@ public:
|
||||
|
||||
virtual ISharedImage* AsSharedImage() MOZ_OVERRIDE { return this; }
|
||||
|
||||
virtual TextureClient* GetTextureClient() MOZ_OVERRIDE;
|
||||
virtual TextureClient* GetTextureClient(CompositableClient* aClient) MOZ_OVERRIDE;
|
||||
|
||||
virtual uint8_t* GetBuffer() MOZ_OVERRIDE;
|
||||
|
||||
|
@ -81,7 +81,8 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
|
||||
gfxFloat emHeight;
|
||||
// Scale for vertical design metric conversion: pixels per design unit.
|
||||
gfxFloat yScale;
|
||||
// If this remains at 0.0, we can't use metrics from OS/2 etc.
|
||||
gfxFloat yScale = 0.0;
|
||||
if (FT_IS_SCALABLE(mFace)) {
|
||||
// Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
|
||||
// have subpixel accuracy.
|
||||
@ -93,11 +94,17 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
|
||||
emHeight = mFace->units_per_EM * yScale;
|
||||
} else { // Not scalable.
|
||||
// FT_Size_Metrics doc says x_scale is "only relevant for scalable
|
||||
// font formats".
|
||||
gfxFloat emUnit = mFace->units_per_EM;
|
||||
emHeight = ftMetrics.y_ppem;
|
||||
yScale = emHeight / emUnit;
|
||||
// FT_Face doc says units_per_EM and a bunch of following fields
|
||||
// are "only relevant to scalable outlines". If it's an sfnt,
|
||||
// we can get units_per_EM from the 'head' table instead; otherwise,
|
||||
// we don't have a unitsPerEm value so we can't compute/use yScale.
|
||||
const TT_Header* head =
|
||||
static_cast<TT_Header*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_head));
|
||||
if (head) {
|
||||
gfxFloat emUnit = head->Units_Per_EM;
|
||||
emHeight = ftMetrics.y_ppem;
|
||||
yScale = emHeight / emUnit;
|
||||
}
|
||||
}
|
||||
|
||||
TT_OS2 *os2 =
|
||||
@ -108,7 +115,7 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
|
||||
|
||||
gfxFloat lineHeight;
|
||||
if (os2 && os2->sTypoAscender) {
|
||||
if (os2 && os2->sTypoAscender && yScale > 0.0) {
|
||||
aMetrics->emAscent = os2->sTypoAscender * yScale;
|
||||
aMetrics->emDescent = -os2->sTypoDescender * yScale;
|
||||
FT_Short typoHeight =
|
||||
@ -150,7 +157,7 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
aMetrics->xHeight = -extents.y_bearing;
|
||||
aMetrics->aveCharWidth = extents.x_advance;
|
||||
} else {
|
||||
if (os2 && os2->sxHeight) {
|
||||
if (os2 && os2->sxHeight && yScale > 0.0) {
|
||||
aMetrics->xHeight = os2->sxHeight * yScale;
|
||||
} else {
|
||||
// CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
|
||||
@ -195,7 +202,7 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
// Therefore get the underline position directly from the table
|
||||
// ourselves when this table exists. Use FreeType's metrics for
|
||||
// other (including older PostScript) fonts.
|
||||
if (mFace->underline_position && mFace->underline_thickness) {
|
||||
if (mFace->underline_position && mFace->underline_thickness && yScale > 0.0) {
|
||||
aMetrics->underlineSize = mFace->underline_thickness * yScale;
|
||||
TT_Postscript *post = static_cast<TT_Postscript*>
|
||||
(FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
|
||||
@ -211,7 +218,7 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
aMetrics->underlineOffset = -aMetrics->underlineSize;
|
||||
}
|
||||
|
||||
if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition) {
|
||||
if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition && yScale > 0.0) {
|
||||
aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
|
||||
aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
|
||||
} else { // No strikeout info.
|
||||
|
@ -575,10 +575,8 @@ GetOutlineInnerRect(nsIFrame* aFrame)
|
||||
(aFrame->Properties().Get(nsIFrame::OutlineInnerRectProperty()));
|
||||
if (savedOutlineInnerRect)
|
||||
return *savedOutlineInnerRect;
|
||||
// FIXME (bug 599652): We probably want something narrower than either
|
||||
// overflow rect here, but for now use the visual overflow in order to
|
||||
// be consistent with ComputeEffectsRect in nsFrame.cpp.
|
||||
return aFrame->GetVisualOverflowRect();
|
||||
NS_NOTREACHED("we should have saved a frame property");
|
||||
return nsRect(nsPoint(0, 0), aFrame->GetSize());
|
||||
}
|
||||
|
||||
void
|
||||
@ -608,37 +606,22 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
|
||||
nscolor bgColor =
|
||||
bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
|
||||
|
||||
// When the outline property is set on :-moz-anonymous-block or
|
||||
// :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
|
||||
// outline from the inline that was broken because it contained a
|
||||
// block. In that case, we don't want a really wide outline if the
|
||||
// block inside the inline is narrow, so union the actual contents of
|
||||
// the anonymous blocks.
|
||||
nsIFrame *frameForArea = aForFrame;
|
||||
do {
|
||||
nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo();
|
||||
if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
|
||||
pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
|
||||
break;
|
||||
// If we're done, we really want it and all its later siblings.
|
||||
frameForArea = frameForArea->GetFirstPrincipalChild();
|
||||
NS_ASSERTION(frameForArea, "anonymous block with no children?");
|
||||
} while (frameForArea);
|
||||
nsRect innerRect; // relative to aBorderArea.TopLeft()
|
||||
if (frameForArea == aForFrame) {
|
||||
innerRect = GetOutlineInnerRect(aForFrame);
|
||||
nsRect innerRect;
|
||||
if (
|
||||
#ifdef MOZ_XUL
|
||||
aStyleContext->GetPseudoType() == nsCSSPseudoElements::ePseudo_XULTree
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
) {
|
||||
// FIXME: This behavior doesn't make sense; we should switch back to
|
||||
// using aBorderArea. But since this has been broken since bug
|
||||
// 133165 in August of 2004, that switch should be made in its own
|
||||
// patch changing only that behavior.
|
||||
innerRect = aForFrame->GetVisualOverflowRect();
|
||||
} else {
|
||||
for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
|
||||
// The outline has already been included in aForFrame's overflow
|
||||
// area, but not in those of its descendants, so we have to
|
||||
// include it. Otherwise we'll end up drawing the outline inside
|
||||
// the border.
|
||||
nsRect r(GetOutlineInnerRect(frameForArea) +
|
||||
frameForArea->GetOffsetTo(aForFrame));
|
||||
innerRect.UnionRect(innerRect, r);
|
||||
}
|
||||
innerRect = GetOutlineInnerRect(aForFrame);
|
||||
}
|
||||
|
||||
innerRect += aBorderArea.TopLeft();
|
||||
nscoord offset = ourOutline->mOutlineOffset;
|
||||
innerRect.Inflate(offset, offset);
|
||||
|
@ -1639,7 +1639,7 @@ nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin,
|
||||
*
|
||||
* @param aVal The value to constrain (in/out)
|
||||
*/
|
||||
static void ConstrainToCoordValues(gfxFloat &aVal)
|
||||
static void ConstrainToCoordValues(gfxFloat& aVal)
|
||||
{
|
||||
if (aVal <= nscoord_MIN)
|
||||
aVal = nscoord_MIN;
|
||||
@ -1647,6 +1647,32 @@ static void ConstrainToCoordValues(gfxFloat &aVal)
|
||||
aVal = nscoord_MAX;
|
||||
}
|
||||
|
||||
static void ConstrainToCoordValues(gfxFloat& aStart, gfxFloat& aSize)
|
||||
{
|
||||
gfxFloat max = aStart + aSize;
|
||||
|
||||
// Clamp the end points to within nscoord range
|
||||
ConstrainToCoordValues(aStart);
|
||||
ConstrainToCoordValues(max);
|
||||
|
||||
aSize = max - aStart;
|
||||
// If the width if still greater than the max nscoord, then bring both
|
||||
// endpoints in by the same amount until it fits.
|
||||
if (aSize > nscoord_MAX) {
|
||||
gfxFloat excess = aSize - nscoord_MAX;
|
||||
excess /= 2;
|
||||
|
||||
aStart += excess;
|
||||
aSize = nscoord_MAX;
|
||||
} else if (aSize < nscoord_MIN) {
|
||||
gfxFloat excess = aSize - nscoord_MIN;
|
||||
excess /= 2;
|
||||
|
||||
aStart -= excess;
|
||||
aSize = nscoord_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor)
|
||||
{
|
||||
@ -1655,10 +1681,8 @@ nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor)
|
||||
scaledRect.ScaleRoundOut(aFactor);
|
||||
|
||||
/* We now need to constrain our results to the max and min values for coords. */
|
||||
ConstrainToCoordValues(scaledRect.x);
|
||||
ConstrainToCoordValues(scaledRect.y);
|
||||
ConstrainToCoordValues(scaledRect.width);
|
||||
ConstrainToCoordValues(scaledRect.height);
|
||||
ConstrainToCoordValues(scaledRect.x, scaledRect.width);
|
||||
ConstrainToCoordValues(scaledRect.y, scaledRect.height);
|
||||
|
||||
/* Now typecast everything back. This is guaranteed to be safe. */
|
||||
return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()),
|
||||
|
@ -4976,27 +4976,6 @@ ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
|
||||
// box-shadow
|
||||
r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
|
||||
|
||||
const nsStyleOutline* outline = aFrame->StyleOutline();
|
||||
uint8_t outlineStyle = outline->GetOutlineStyle();
|
||||
if (outlineStyle != NS_STYLE_BORDER_STYLE_NONE) {
|
||||
nscoord width;
|
||||
DebugOnly<bool> result = outline->GetOutlineWidth(width);
|
||||
NS_ASSERTION(result, "GetOutlineWidth had no cached outline width");
|
||||
if (width > 0) {
|
||||
aFrame->Properties().
|
||||
Set(nsIFrame::OutlineInnerRectProperty(), new nsRect(r));
|
||||
|
||||
nscoord offset = outline->mOutlineOffset;
|
||||
nscoord inflateBy = std::max(width + offset, 0);
|
||||
// FIXME (bug 599652): We probably want outline to be drawn around
|
||||
// something smaller than the visual overflow rect (perhaps the
|
||||
// scrollable overflow rect is correct). When we change that, we
|
||||
// need to keep this code (and the storing of properties just
|
||||
// above) in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
|
||||
r.Inflate(inflateBy, inflateBy);
|
||||
}
|
||||
}
|
||||
|
||||
// border-image-outset.
|
||||
// We need to include border-image-outset because it can cause the
|
||||
// border image to be drawn beyond the border box.
|
||||
@ -6854,6 +6833,185 @@ IsInlineFrame(nsIFrame *aFrame)
|
||||
return type == nsGkAtoms::inlineFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the union of the border boxes of aFrame and its descendants,
|
||||
* in aFrame's coordinate space (if aApplyTransform is false) or its
|
||||
* post-transform coordinate space (if aApplyTransform is true).
|
||||
*/
|
||||
static nsRect
|
||||
UnionBorderBoxes(nsIFrame* aFrame, bool aApplyTransform,
|
||||
const nsSize* aSizeOverride = nullptr,
|
||||
const nsOverflowAreas* aOverflowOverride = nullptr)
|
||||
{
|
||||
const nsRect bounds(nsPoint(0, 0),
|
||||
aSizeOverride ? *aSizeOverride : aFrame->GetSize());
|
||||
|
||||
// Start from our border-box, transformed. See comment below about
|
||||
// transform of children.
|
||||
nsRect u;
|
||||
bool doTransform = aApplyTransform && aFrame->IsTransformed();
|
||||
if (doTransform) {
|
||||
u = nsDisplayTransform::TransformRect(bounds, aFrame,
|
||||
nsPoint(0, 0), &bounds);
|
||||
} else {
|
||||
u = bounds;
|
||||
}
|
||||
|
||||
// Only iterate through the children if the overflow areas suggest
|
||||
// that we might need to, and if the frame doesn't clip its overflow
|
||||
// anyway.
|
||||
if (aOverflowOverride) {
|
||||
if (!doTransform &&
|
||||
bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) &&
|
||||
bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
|
||||
return u;
|
||||
}
|
||||
} else {
|
||||
if (!doTransform &&
|
||||
bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) &&
|
||||
bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) {
|
||||
return u;
|
||||
}
|
||||
}
|
||||
const nsStyleDisplay* disp = aFrame->StyleDisplay();
|
||||
nsIAtom* fType = aFrame->GetType();
|
||||
if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) ||
|
||||
fType == nsGkAtoms::scrollFrame ||
|
||||
fType == nsGkAtoms::svgOuterSVGFrame) {
|
||||
return u;
|
||||
}
|
||||
|
||||
nsRect clipPropClipRect;
|
||||
bool hasClipPropClip =
|
||||
aFrame->GetClipPropClipRect(disp, &clipPropClipRect, bounds.Size());
|
||||
|
||||
// Iterate over all children except pop-ups.
|
||||
const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
|
||||
nsIFrame::kSelectPopupList);
|
||||
for (nsIFrame::ChildListIterator childLists(aFrame);
|
||||
!childLists.IsDone(); childLists.Next()) {
|
||||
if (skip.Contains(childLists.CurrentID())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsFrameList children = childLists.CurrentList();
|
||||
for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
|
||||
nsIFrame* child = e.get();
|
||||
// Note that passing |true| for aApplyTransform when
|
||||
// child->Preserves3D() is incorrect if our aApplyTransform is
|
||||
// false... but the opposite would be as well. This is because
|
||||
// elements within a preserve-3d scene are always transformed up
|
||||
// to the top of the scene. This means we don't have a
|
||||
// mechanism for getting a transform up to an intermediate point
|
||||
// within the scene. We choose to over-transform rather than
|
||||
// under-transform because this is consistent with other
|
||||
// overflow areas.
|
||||
nsRect childRect = UnionBorderBoxes(child, true) +
|
||||
child->GetPosition();
|
||||
|
||||
if (hasClipPropClip) {
|
||||
// Intersect with the clip before transforming.
|
||||
childRect.IntersectRect(childRect, clipPropClipRect);
|
||||
}
|
||||
|
||||
// Note that we transform each child separately according to
|
||||
// aFrame's transform, and then union, which gives a different
|
||||
// (smaller) result from unioning and then transforming the
|
||||
// union. This doesn't match the way we handle overflow areas
|
||||
// with 2-D transforms, though it does match the way we handle
|
||||
// overflow areas in preserve-3d 3-D scenes.
|
||||
if (doTransform && !child->Preserves3D()) {
|
||||
childRect = nsDisplayTransform::TransformRect(childRect, aFrame,
|
||||
nsPoint(0, 0), &bounds);
|
||||
}
|
||||
u.UnionRectEdges(u, childRect);
|
||||
}
|
||||
}
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
static void
|
||||
ComputeAndIncludeOutlineArea(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas,
|
||||
const nsSize& aNewSize)
|
||||
{
|
||||
const nsStyleOutline* outline = aFrame->StyleOutline();
|
||||
if (outline->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
nscoord width;
|
||||
DebugOnly<bool> result = outline->GetOutlineWidth(width);
|
||||
NS_ASSERTION(result, "GetOutlineWidth had no cached outline width");
|
||||
if (width <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the outline property is set on :-moz-anonymous-block or
|
||||
// :-moz-anonymous-positioned-block pseudo-elements, it inherited
|
||||
// that outline from the inline that was broken because it
|
||||
// contained a block. In that case, we don't want a really wide
|
||||
// outline if the block inside the inline is narrow, so union the
|
||||
// actual contents of the anonymous blocks.
|
||||
nsIFrame *frameForArea = aFrame;
|
||||
do {
|
||||
nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo();
|
||||
if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
|
||||
pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
|
||||
break;
|
||||
// If we're done, we really want it and all its later siblings.
|
||||
frameForArea = frameForArea->GetFirstPrincipalChild();
|
||||
NS_ASSERTION(frameForArea, "anonymous block with no children?");
|
||||
} while (frameForArea);
|
||||
|
||||
// Find the union of the border boxes of all descendants, or in
|
||||
// the block-in-inline case, all descendants we care about.
|
||||
//
|
||||
// Note that the interesting perspective-related cases are taken
|
||||
// care of by the code that handles those issues for overflow
|
||||
// calling FinishAndStoreOverflow again, which in turn calls this
|
||||
// function again. We still need to deal with preserve-3d a bit.
|
||||
nsRect innerRect;
|
||||
if (frameForArea == aFrame) {
|
||||
innerRect = UnionBorderBoxes(aFrame, false, &aNewSize, &aOverflowAreas);
|
||||
} else {
|
||||
for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
|
||||
nsRect r(UnionBorderBoxes(frameForArea, true));
|
||||
|
||||
// Adjust for offsets transforms up to aFrame's pre-transform
|
||||
// (i.e., normal) coordinate space; see comments in
|
||||
// UnionBorderBoxes for some of the subtlety here.
|
||||
for (nsIFrame *f = frameForArea, *parent = f->GetParent();
|
||||
/* see middle of loop */;
|
||||
f = parent, parent = f->GetParent()) {
|
||||
r += f->GetPosition();
|
||||
if (parent == aFrame) {
|
||||
break;
|
||||
}
|
||||
if (parent->IsTransformed() && !f->Preserves3D()) {
|
||||
r = nsDisplayTransform::TransformRect(r, parent, nsPoint(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
innerRect.UnionRect(innerRect, r);
|
||||
}
|
||||
}
|
||||
|
||||
aFrame->Properties().Set(nsIFrame::OutlineInnerRectProperty(),
|
||||
new nsRect(innerRect));
|
||||
|
||||
nscoord offset = outline->mOutlineOffset;
|
||||
nscoord inflateBy = std::max(width + offset, 0);
|
||||
|
||||
// Keep this code (and the storing of properties just above) in
|
||||
// sync with GetOutlineInnerRect in nsCSSRendering.cpp.
|
||||
nsRect outerRect(innerRect);
|
||||
outerRect.Inflate(inflateBy, inflateBy);
|
||||
|
||||
nsRect& vo = aOverflowAreas.VisualOverflow();
|
||||
vo.UnionRectEdges(vo, outerRect);
|
||||
}
|
||||
|
||||
bool
|
||||
nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
|
||||
nsSize aNewSize, nsSize* aOldSize)
|
||||
@ -6933,6 +7091,8 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
|
||||
}
|
||||
}
|
||||
|
||||
ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
|
||||
|
||||
// Nothing in here should affect scrollable overflow.
|
||||
aOverflowAreas.VisualOverflow() =
|
||||
ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize);
|
||||
|
28
layout/reftests/outline/outline-and-3d-transform-1-ref.html
Normal file
28
layout/reftests/outline/outline-and-3d-transform-1-ref.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Testcase for outline around 3-D transform</title>
|
||||
<style>
|
||||
|
||||
html, body { margin: 0; padding: 0; border: none }
|
||||
|
||||
div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
body > div {
|
||||
margin-top: 200px;
|
||||
margin-left: 200px;
|
||||
transform-style: flat;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body > div > div {
|
||||
position: absolute; top: 0; left: 0;
|
||||
height: 150px; width: 150px; top: -25px; left: -25px;
|
||||
background: rgba(255, 255, 0, 0.4);
|
||||
outline: 2px dashed blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div><div></div></div>
|
40
layout/reftests/outline/outline-and-3d-transform-1a.html
Normal file
40
layout/reftests/outline/outline-and-3d-transform-1a.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Testcase for outline around 3-D transform</title>
|
||||
<style>
|
||||
|
||||
html, body { margin: 0; padding: 0; border: none }
|
||||
|
||||
div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
body > div {
|
||||
margin-top: 200px;
|
||||
margin-left: 200px;
|
||||
transform-style: flat;
|
||||
outline: 2px dashed blue;
|
||||
}
|
||||
|
||||
body > div > div {
|
||||
transform: rotateX(30deg);
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
body > div > div > div {
|
||||
transform: rotateX(30deg);
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
body > div > div > div > div {
|
||||
transform: scale(1.5, 3);
|
||||
transform-origin: 50% 50%;
|
||||
background: rgba(255, 255, 0, 0.4);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<div><div><div><div></div></div></div></div>
|
40
layout/reftests/outline/outline-and-3d-transform-1b.html
Normal file
40
layout/reftests/outline/outline-and-3d-transform-1b.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Testcase for outline around 3-D transform</title>
|
||||
<style>
|
||||
|
||||
html, body { margin: 0; padding: 0; border: none }
|
||||
|
||||
div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
body > div {
|
||||
margin-top: 200px;
|
||||
margin-left: 200px;
|
||||
transform-style: flat;
|
||||
outline: 2px dashed blue;
|
||||
}
|
||||
|
||||
body > div > div {
|
||||
transform: rotateX(90deg);
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
body > div > div > div {
|
||||
transform: rotateX(-30deg);
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
body > div > div > div > div {
|
||||
transform: scale(1.5, 3);
|
||||
transform-origin: 50% 50%;
|
||||
background: rgba(255, 255, 0, 0.4);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<div><div><div><div></div></div></div></div>
|
36
layout/reftests/outline/outline-and-3d-transform-2-ref.html
Normal file
36
layout/reftests/outline/outline-and-3d-transform-2-ref.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Testcase for outline around 3-D transform</title>
|
||||
<style>
|
||||
|
||||
html, body { margin: 0; padding: 0; border: none }
|
||||
|
||||
div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
body > div {
|
||||
margin-top: 200px;
|
||||
margin-left: 200px;
|
||||
transform-style: flat;
|
||||
}
|
||||
|
||||
body > div > div {
|
||||
transform: rotateX(30deg);
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
border: 5px solid green;
|
||||
margin: -5px;
|
||||
}
|
||||
|
||||
body > div > div > div {
|
||||
transform: rotateX(30deg);
|
||||
width: 50px; margin-left: 20px; margin-top: -5px;
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
border: 5px solid blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div><div><div></div></div></div>
|
35
layout/reftests/outline/outline-and-3d-transform-2.html
Normal file
35
layout/reftests/outline/outline-and-3d-transform-2.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Testcase for outline around 3-D transform</title>
|
||||
<style>
|
||||
|
||||
html, body { margin: 0; padding: 0; border: none }
|
||||
|
||||
div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
body > div {
|
||||
margin-top: 200px;
|
||||
margin-left: 200px;
|
||||
transform-style: flat;
|
||||
}
|
||||
|
||||
body > div > div {
|
||||
transform: rotateX(30deg);
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
outline: 5px solid green;
|
||||
}
|
||||
|
||||
body > div > div > div {
|
||||
transform: rotateX(30deg);
|
||||
width: 50px; margin-left: 25px;
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
outline: 5px solid blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div><div><div></div></div></div>
|
13
layout/reftests/outline/outline-and-box-shadow-ref.html
Normal file
13
layout/reftests/outline/outline-and-box-shadow-ref.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>outline and box-shadow</title>
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0 }
|
||||
p {
|
||||
margin: 48px;
|
||||
border: 2px solid blue;
|
||||
padding: 5px; /* ensure no font overhang */
|
||||
background: yellow; color: black;
|
||||
box-shadow: 10px 10px 10px 0px black;
|
||||
}
|
||||
</style>
|
||||
<p>The outline should be adjacent to the background.</p>
|
13
layout/reftests/outline/outline-and-box-shadow.html
Normal file
13
layout/reftests/outline/outline-and-box-shadow.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>outline and box-shadow</title>
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0 }
|
||||
p {
|
||||
margin: 50px;
|
||||
outline: 2px solid blue;
|
||||
padding: 5px; /* ensure no font overhang */
|
||||
background: yellow; color: black;
|
||||
box-shadow: 10px 10px 10px 2px black;
|
||||
}
|
||||
</style>
|
||||
<p>The outline should be adjacent to the background.</p>
|
4
layout/reftests/outline/reftest.list
Normal file
4
layout/reftests/outline/reftest.list
Normal file
@ -0,0 +1,4 @@
|
||||
== outline-and-box-shadow.html outline-and-box-shadow-ref.html
|
||||
== outline-and-3d-transform-1a.html outline-and-3d-transform-1-ref.html
|
||||
== outline-and-3d-transform-1b.html outline-and-3d-transform-1-ref.html
|
||||
== outline-and-3d-transform-2.html outline-and-3d-transform-2-ref.html
|
@ -219,6 +219,8 @@ skip-if(Android||B2G) include native-theme/reftest.list
|
||||
# netwerk/
|
||||
skip-if(B2G) include ../../netwerk/test/reftest/reftest.list
|
||||
|
||||
include outline/reftest.list
|
||||
|
||||
# object/
|
||||
skip-if(B2G) include object/reftest.list
|
||||
|
||||
|
@ -1543,7 +1543,7 @@ nsStyleGradient::HasCalc()
|
||||
return true;
|
||||
}
|
||||
return mBgPosX.IsCalcUnit() || mBgPosY.IsCalcUnit() || mAngle.IsCalcUnit() ||
|
||||
mRadiusX.IsCalcUnit() || mRadiusX.IsCalcUnit();
|
||||
mRadiusX.IsCalcUnit() || mRadiusY.IsCalcUnit();
|
||||
}
|
||||
|
||||
// --------------------
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- -*- Mode: HTML; tab-width: 4; indent-tabs-mode: nil; -*- -->
|
||||
<!-- vim: set shiftwidth=4 tabstop=4 autoindent noexpandtab: -->
|
||||
<!-- -*- Mode: HTML; tab-width: 2; indent-tabs-mode: nil; -*- -->
|
||||
<!-- vim: set shiftwidth=2 tabstop=2 autoindent expandtab: -->
|
||||
<!-- 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/. -->
|
||||
@ -179,7 +179,7 @@ function fileentry_changed() {
|
||||
var log = null;
|
||||
|
||||
log = e.target.result;
|
||||
|
||||
|
||||
if (log)
|
||||
process_log(log);
|
||||
else
|
||||
@ -217,7 +217,7 @@ function process_log(contents) {
|
||||
var state = match[1];
|
||||
var random = match[2];
|
||||
var url = match[3];
|
||||
var extra = match[4];
|
||||
var extra = match[4];
|
||||
gTestItems.push(
|
||||
{
|
||||
pass: !state.match(/DEBUG-INFO$|FAIL$/),
|
||||
|
@ -1655,7 +1655,7 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
|
||||
result += "REFTEST IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n";
|
||||
} else {
|
||||
result += "\n";
|
||||
gDumpLog("REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n");
|
||||
result += "REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n";
|
||||
}
|
||||
} else {
|
||||
result += "\n";
|
||||
|
@ -19,7 +19,7 @@ def format_char(c):
|
||||
return "\\"
|
||||
elif c == 0x22:
|
||||
return "\\\""
|
||||
elif c == 0x39:
|
||||
elif c == 0x27:
|
||||
return "\\'"
|
||||
elif c < 0x20 or c >= 0x80 and c <= 0xff:
|
||||
return "\\x%02x" % c
|
||||
|
Loading…
Reference in New Issue
Block a user