зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
65ea740eda
|
@ -48,6 +48,7 @@ toolkit/library
|
|||
profile
|
||||
services
|
||||
startupcache
|
||||
devtools/platform
|
||||
devtools/server
|
||||
devtools/shared
|
||||
browser/app
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# 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/.
|
||||
|
||||
if CONFIG['MOZ_DEVTOOLS'] and CONFIG['MOZ_DEVTOOLS'] not in ('all', 'server'):
|
||||
if CONFIG['MOZ_DEVTOOLS'] and CONFIG['MOZ_DEVTOOLS'] not in ('all', 'server', 'addon'):
|
||||
error('Unsupported MOZ_DEVTOOLS value: %s' % (CONFIG['MOZ_DEVTOOLS']))
|
||||
|
||||
if CONFIG['MOZ_DEVTOOLS'] == 'all':
|
||||
|
@ -12,11 +12,20 @@ if CONFIG['MOZ_DEVTOOLS'] == 'all':
|
|||
'client',
|
||||
]
|
||||
|
||||
# `addon` is a special build mode to strip everything except binary components
|
||||
# and shim modules that are going to stay in Firefox once DevTools ship as an
|
||||
# add-on.
|
||||
# `platform` contains all native components
|
||||
DIRS += [
|
||||
'shim',
|
||||
'platform',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DEVTOOLS'] != 'addon':
|
||||
DIRS += [
|
||||
'server',
|
||||
'shared',
|
||||
'shim',
|
||||
]
|
||||
]
|
||||
|
||||
# /browser uses DIST_SUBDIR. We opt-in to this treatment when building
|
||||
# DevTools for the browser to keep the root omni.ja slim for use by external XUL
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIJSInspector.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'jsinspector'
|
||||
|
||||
SOURCES += [
|
||||
'nsJSInspector.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
|
@ -0,0 +1,19 @@
|
|||
// Parent config file for all devtools xpcshell files.
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"plugin:mozilla/xpcshell-test"
|
||||
],
|
||||
"rules": {
|
||||
// Allow non-camelcase so that run_test doesn't produce a warning.
|
||||
"camelcase": "off",
|
||||
// Allow using undefined variables so that tests can refer to functions
|
||||
// and variables defined in head.js files, without having to maintain a
|
||||
// list of globals in each .eslintrc file.
|
||||
// Note that bug 1168340 will eventually help auto-registering globals
|
||||
// from head.js files.
|
||||
"no-undef": "off",
|
||||
"block-scoped-var": "off",
|
||||
// Tests can always import anything.
|
||||
"mozilla/reject-some-requires": "off",
|
||||
}
|
||||
}
|
|
@ -3,9 +3,13 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
// Test the basic functionality of the nsIJSInspector component.
|
||||
var gCount = 0;
|
||||
const MAX = 10;
|
||||
|
||||
var inspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
|
||||
var tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_nsjsinspector.js]
|
|
@ -16,18 +16,6 @@ BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
|
|||
MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIJSInspector.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'jsinspector'
|
||||
|
||||
SOURCES += [
|
||||
'nsJSInspector.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
DevToolsModules(
|
||||
'child.js',
|
||||
'content-server.jsm',
|
||||
|
|
|
@ -43,7 +43,6 @@ support-files =
|
|||
[test_nesting-03.js]
|
||||
[test_forwardingprefix.js]
|
||||
[test_getyoungestframe.js]
|
||||
[test_nsjsinspector.js]
|
||||
[test_dbgactor.js]
|
||||
[test_dbgglobal.js]
|
||||
[test_dbgclient_debuggerstatement.js]
|
||||
|
|
|
@ -329,15 +329,15 @@ public:
|
|||
* Get the SMIL override style declaration for this element. If the
|
||||
* rule hasn't been created, this method simply returns null.
|
||||
*/
|
||||
virtual DeclarationBlock* GetSMILOverrideStyleDeclaration();
|
||||
DeclarationBlock* GetSMILOverrideStyleDeclaration();
|
||||
|
||||
/**
|
||||
* Set the SMIL override style declaration for this element. If
|
||||
* aNotify is true, this method will notify the document's pres
|
||||
* context, so that the style changes will be noticed.
|
||||
*/
|
||||
virtual nsresult SetSMILOverrideStyleDeclaration(
|
||||
DeclarationBlock* aDeclaration, bool aNotify);
|
||||
nsresult SetSMILOverrideStyleDeclaration(DeclarationBlock* aDeclaration,
|
||||
bool aNotify);
|
||||
|
||||
/**
|
||||
* Returns a new nsISMILAttr that allows the caller to animate the given
|
||||
|
@ -357,7 +357,7 @@ public:
|
|||
* Note: This method is analogous to the 'GetStyle' method in
|
||||
* nsGenericHTMLElement and nsStyledElement.
|
||||
*/
|
||||
virtual nsICSSDeclaration* GetSMILOverrideStyle();
|
||||
nsICSSDeclaration* GetSMILOverrideStyle();
|
||||
|
||||
/**
|
||||
* Returns if the element is labelable as per HTML specification.
|
||||
|
|
|
@ -275,7 +275,7 @@ public:
|
|||
|
||||
/**
|
||||
* SMIL Overridde style rules (for SMIL animation of CSS properties)
|
||||
* @see nsIContent::GetSMILOverrideStyle
|
||||
* @see Element::GetSMILOverrideStyle
|
||||
*/
|
||||
nsCOMPtr<nsICSSDeclaration> mSMILOverrideStyle;
|
||||
|
||||
|
|
|
@ -113,7 +113,6 @@ const char* mozilla::dom::ContentPrefs::gInitPrefs[] = {
|
|||
"javascript.options.wasm_baselinejit",
|
||||
"javascript.options.werror",
|
||||
"javascript.use_us_english_locale",
|
||||
"jsloader.reuseGlobal",
|
||||
"layout.idle_period.required_quiescent_frames",
|
||||
"layout.idle_period.time_limit",
|
||||
"layout.interruptible-reflow.enabled",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "MediaDecoder.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
@ -33,7 +34,36 @@ void
|
|||
MediaStreamVideoRecorderSink::SetCurrentFrames(const VideoSegment& aSegment)
|
||||
{
|
||||
MOZ_ASSERT(mVideoEncoder);
|
||||
// If we're suspended (paused) we don't forward frames
|
||||
if (!mSuspended) {
|
||||
mVideoEncoder->SetCurrentFrames(aSegment);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaEncoder::Suspend()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mLastPauseStartTime = TimeStamp::Now();
|
||||
mSuspended = true;
|
||||
mVideoSink->Suspend();
|
||||
}
|
||||
|
||||
void
|
||||
MediaEncoder::Resume()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mSuspended) {
|
||||
return;
|
||||
}
|
||||
media::TimeUnit timeSpentPaused =
|
||||
media::TimeUnit::FromTimeDuration(
|
||||
TimeStamp::Now() - mLastPauseStartTime);
|
||||
MOZ_ASSERT(timeSpentPaused.ToMicroseconds() >= 0);
|
||||
MOZ_RELEASE_ASSERT(timeSpentPaused.IsValid());
|
||||
mMicrosecondsSpentPaused += timeSpentPaused.ToMicroseconds();;
|
||||
mSuspended = false;
|
||||
mVideoSink->Resume();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -49,7 +79,9 @@ MediaEncoder::NotifyRealtimeData(MediaStreamGraph* aGraph,
|
|||
uint32_t aTrackEvents,
|
||||
const MediaSegment& aRealtimeMedia)
|
||||
{
|
||||
if (mSuspended == RECORD_NOT_SUSPENDED) {
|
||||
if (mSuspended) {
|
||||
return;
|
||||
}
|
||||
// Process the incoming raw track data from MediaStreamGraph, called on the
|
||||
// thread of MediaStreamGraph.
|
||||
if (mAudioEncoder && aRealtimeMedia.GetType() == MediaSegment::AUDIO) {
|
||||
|
@ -63,7 +95,6 @@ MediaEncoder::NotifyRealtimeData(MediaStreamGraph* aGraph,
|
|||
aTrackOffset, aTrackEvents,
|
||||
aRealtimeMedia);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -88,24 +119,6 @@ MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
|||
NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
|
||||
}
|
||||
}
|
||||
if (mSuspended == RECORD_RESUMED) {
|
||||
if (mVideoEncoder) {
|
||||
if (aQueuedMedia.GetType() == MediaSegment::VIDEO) {
|
||||
// insert a null frame of duration equal to the first segment passed
|
||||
// after Resume(), so it'll get added to one of the DirectListener frames
|
||||
VideoSegment segment;
|
||||
gfx::IntSize size(0,0);
|
||||
segment.AppendFrame(nullptr, aQueuedMedia.GetDuration(), size,
|
||||
PRINCIPAL_HANDLE_NONE);
|
||||
mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID,
|
||||
aTrackOffset, aTrackEvents,
|
||||
segment);
|
||||
mSuspended = RECORD_NOT_SUSPENDED;
|
||||
}
|
||||
} else {
|
||||
mSuspended = RECORD_NOT_SUSPENDED; // no video
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,12 +131,6 @@ MediaEncoder::NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
|
|||
{
|
||||
if (!mDirectConnected) {
|
||||
NotifyRealtimeData(aGraph, aID, aTrackOffset, 0, aQueuedMedia);
|
||||
} else {
|
||||
if (mSuspended == RECORD_RESUMED) {
|
||||
if (!mVideoEncoder) {
|
||||
mSuspended = RECORD_NOT_SUSPENDED; // no video
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,6 +348,29 @@ MediaEncoder::WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder)
|
|||
mState = ENCODE_ERROR;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Update timestamps to accommodate pauses
|
||||
const nsTArray<RefPtr<EncodedFrame> >& encodedFrames =
|
||||
encodedVideoData.GetEncodedFrames();
|
||||
// Take a copy of the atomic so we don't continually access it
|
||||
uint64_t microsecondsSpentPaused = mMicrosecondsSpentPaused;
|
||||
for (size_t i = 0; i < encodedFrames.Length(); ++i) {
|
||||
RefPtr<EncodedFrame> frame = encodedFrames[i];
|
||||
if (frame->GetTimeStamp() > microsecondsSpentPaused &&
|
||||
frame->GetTimeStamp() - microsecondsSpentPaused > mLastMuxedTimestamp) {
|
||||
// Use the adjusted timestamp if it's after the last timestamp
|
||||
frame->SetTimeStamp(frame->GetTimeStamp() - microsecondsSpentPaused);
|
||||
} else {
|
||||
// If not, we force the last time stamp. We do this so the frames are
|
||||
// still around and in order in case the codec needs to reference them.
|
||||
// Dropping them here may result in artifacts in playback.
|
||||
frame->SetTimeStamp(mLastMuxedTimestamp);
|
||||
}
|
||||
MOZ_ASSERT(mLastMuxedTimestamp <= frame->GetTimeStamp(),
|
||||
"Our frames should be ordered by this point!");
|
||||
mLastMuxedTimestamp = frame->GetTimeStamp();
|
||||
}
|
||||
|
||||
rv = mWriter->WriteEncodedTrack(encodedVideoData,
|
||||
aTrackEncoder->IsEncodingComplete() ?
|
||||
ContainerWriter::END_OF_STREAM : 0);
|
||||
|
|
|
@ -24,15 +24,20 @@ class MediaStreamVideoRecorderSink : public MediaStreamVideoSink
|
|||
{
|
||||
public:
|
||||
explicit MediaStreamVideoRecorderSink(VideoTrackEncoder* aEncoder)
|
||||
: mVideoEncoder(aEncoder) {}
|
||||
: mVideoEncoder(aEncoder)
|
||||
, mSuspended(false) {}
|
||||
|
||||
// MediaStreamVideoSink methods
|
||||
virtual void SetCurrentFrames(const VideoSegment& aSegment) override;
|
||||
virtual void ClearFrames() override {}
|
||||
|
||||
void Resume() { mSuspended = false; }
|
||||
void Suspend() { mSuspended = true; }
|
||||
|
||||
private:
|
||||
virtual ~MediaStreamVideoRecorderSink() {}
|
||||
VideoTrackEncoder* mVideoEncoder;
|
||||
Atomic<bool> mSuspended;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -98,32 +103,19 @@ public :
|
|||
, mShutdown(false)
|
||||
, mDirectConnected(false)
|
||||
, mSuspended(false)
|
||||
, mMicrosecondsSpentPaused(0)
|
||||
, mLastMuxedTimestamp(0)
|
||||
{}
|
||||
|
||||
~MediaEncoder() {};
|
||||
|
||||
enum SuspendState {
|
||||
RECORD_NOT_SUSPENDED,
|
||||
RECORD_SUSPENDED,
|
||||
RECORD_RESUMED
|
||||
};
|
||||
|
||||
/* Note - called from control code, not on MSG threads. */
|
||||
void Suspend()
|
||||
{
|
||||
mSuspended = RECORD_SUSPENDED;
|
||||
}
|
||||
void Suspend();
|
||||
|
||||
/**
|
||||
* Note - called from control code, not on MSG threads.
|
||||
* Arm to collect the Duration of the next video frame and give it
|
||||
* to the next frame, in order to avoid any possible loss of sync. */
|
||||
void Resume()
|
||||
{
|
||||
if (mSuspended == RECORD_SUSPENDED) {
|
||||
mSuspended = RECORD_RESUMED;
|
||||
}
|
||||
}
|
||||
* Calculates time spent paused in order to offset frames. */
|
||||
void Resume();
|
||||
|
||||
/**
|
||||
* Tells us which Notify to pay attention to for media
|
||||
|
@ -243,7 +235,18 @@ private:
|
|||
int mState;
|
||||
bool mShutdown;
|
||||
bool mDirectConnected;
|
||||
Atomic<int> mSuspended;
|
||||
// Tracks if the encoder is suspended (paused). Used on the main thread and
|
||||
// MediaRecorder's read thread.
|
||||
Atomic<bool> mSuspended;
|
||||
// Timestamp of when the last pause happened. Should only be accessed on the
|
||||
// main thread.
|
||||
TimeStamp mLastPauseStartTime;
|
||||
// Exposes the time spend paused in microseconds. Read by the main thread
|
||||
// and MediaRecorder's read thread. Should only be written by main thread.
|
||||
Atomic<uint64_t> mMicrosecondsSpentPaused;
|
||||
// The timestamp of the last muxed sample. Should only be used on
|
||||
// MediaRecorder's read thread.
|
||||
uint64_t mLastMuxedTimestamp;
|
||||
// Get duration from create encoder, for logging purpose
|
||||
double GetEncodeTimeStamp()
|
||||
{
|
||||
|
|
|
@ -802,6 +802,9 @@ tags=msg
|
|||
[test_mediarecorder_getencodeddata.html]
|
||||
skip-if = android_version == '17' # android(bug 1232305)
|
||||
tags=msg
|
||||
[test_mediarecorder_pause_resume_video.html]
|
||||
skip-if = toolkit == 'android' # android(bug 1232305)
|
||||
tags=msg
|
||||
[test_mediarecorder_principals.html]
|
||||
skip-if = (os == 'linux' && bits == 64) || toolkit == 'android' # See bug 1266345, android(bug 1232305)
|
||||
tags=msg
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test MediaRecorder Recording doesn't record during pause</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/dom/canvas/test/captureStream_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<div id="content">
|
||||
<canvas id="video-src-canvas"></canvas>
|
||||
<video id="recorded-video"></video>>
|
||||
</div>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function startTest() {
|
||||
// Setup canvas and take a stream from it
|
||||
let canvas = document.getElementById("video-src-canvas");
|
||||
|
||||
let canvas_size = 100;
|
||||
let new_canvas_size = 50;
|
||||
|
||||
canvas.width = canvas.height = canvas_size;
|
||||
|
||||
let helper = new CaptureStreamTestHelper2D(100, 100);
|
||||
helper.drawColor(canvas, helper.red);
|
||||
|
||||
let canvasStream = canvas.captureStream();
|
||||
// Canvas set up
|
||||
|
||||
// Check values for events
|
||||
let numDataAvailabledRaised = 0;
|
||||
// Recorded data that will be playback.
|
||||
let blob;
|
||||
|
||||
mediaRecorder = new MediaRecorder(canvasStream);
|
||||
is(mediaRecorder.stream, canvasStream,
|
||||
"Media recorder stream = canvas stream at the start of recording");
|
||||
|
||||
mediaRecorder.onwarning = () => ok(false, "warning unexpectedly fired");
|
||||
|
||||
mediaRecorder.onerror = () => ok(false, "Recording failed");
|
||||
|
||||
mediaRecorder.ondataavailable = ev => {
|
||||
info("Got 'dataavailable' event");
|
||||
++numDataAvailabledRaised;
|
||||
// Save recorded data for playback
|
||||
blob = ev.data;
|
||||
};
|
||||
|
||||
mediaRecorder.onstart = () => {
|
||||
info("Got 'start' event");
|
||||
// We just want one frame encoded before we pause
|
||||
mediaRecorder.pause();
|
||||
// We may rewrite this once we settle Bug 1363915, could listen for pause event instead
|
||||
is(mediaRecorder.state, 'paused', 'Media recorder should be paused');
|
||||
// Change our canvas color and size, these changes should not be recorded due to pause
|
||||
canvas.width = canvas.height = new_canvas_size;
|
||||
helper.drawColor(canvas, helper.blue);
|
||||
|
||||
// Wait awhile with the canvas as blue. Then change color to green, resume, and record green
|
||||
let numberOfPaintsSincePause = 0;
|
||||
let draw = () => {
|
||||
numberOfPaintsSincePause++;
|
||||
if(numberOfPaintsSincePause == 60) {
|
||||
canvas.width = canvas.height = canvas_size;
|
||||
helper.drawColor(canvas, helper.green);
|
||||
} else if (numberOfPaintsSincePause == 62) {
|
||||
// Waited 2 draws since changing canvas to green, should be safe to resume
|
||||
mediaRecorder.resume();
|
||||
} else if (numberOfPaintsSincePause > 120) {
|
||||
mediaRecorder.stop();
|
||||
return; // Early return, we don't want to request any more animation frames
|
||||
}
|
||||
window.requestAnimationFrame(draw);
|
||||
};
|
||||
window.requestAnimationFrame(draw);
|
||||
};
|
||||
|
||||
mediaRecorder.onstop = () => {
|
||||
info("Got 'stop' event");
|
||||
is(mediaRecorder.state, 'inactive', 'Media recorder should be incative after stop');
|
||||
is(numDataAvailabledRaised, 1, "Expected 1 dataavailable event");
|
||||
|
||||
ok(blob, "Should have gotten a data blob");
|
||||
let video = document.getElementById("recorded-video");
|
||||
video.id = "recorded-video";
|
||||
video.src = URL.createObjectURL(blob);
|
||||
// Setup a check to make sure we don't play back any blue
|
||||
let checkVideoHasNoBlue = () => {
|
||||
if(helper.isPixel(helper.getPixel(video), helper.blue, 128)) {
|
||||
ok(false, "Video should have no blue frames");
|
||||
// Remove handler so we don't spam the log
|
||||
video.ontimeupdate = null;
|
||||
}
|
||||
};
|
||||
video.ontimeupdate = checkVideoHasNoBlue;
|
||||
video.onerror = () => {
|
||||
ok(false, "Should be able to play the recording. Got error. code=" + video.error.code);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
video.onended = () => {
|
||||
ok(helper.isPixel(helper.getPixel(video), helper.green, 128), "Last frame should be green");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
// The video will resize once it loads its metadata, only listen for resizes after that
|
||||
video.onloadedmetadata = () => {
|
||||
ok(video.videoWidth === canvas_size && video.videoHeight === canvas_size,
|
||||
"video element should be same size as canvas once metadata is loaded");
|
||||
// We shouldn't have any resize events once the video is loaded
|
||||
video.onresize = () => {
|
||||
ok(false, "Should not have any resize events!");
|
||||
};
|
||||
};
|
||||
|
||||
video.play();
|
||||
};
|
||||
|
||||
mediaRecorder.start();
|
||||
is(mediaRecorder.state, "recording", "Media recorder should be recording");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
startTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -27,7 +27,6 @@ UNIFIED_SOURCES += [
|
|||
'testDefinePropertyIgnoredAttributes.cpp',
|
||||
'testDeflateStringToUTF8Buffer.cpp',
|
||||
'testDifferentNewTargetInvokeConstructor.cpp',
|
||||
'testEnclosingFunction.cpp',
|
||||
'testErrorCopying.cpp',
|
||||
'testException.cpp',
|
||||
'testExternalArrayBuffer.cpp',
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Test script cloning.
|
||||
*/
|
||||
/* 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 "jsfriendapi.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
static JSFunction* foundFun = nullptr;
|
||||
|
||||
static bool
|
||||
CheckEnclosing(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
foundFun = js::GetOutermostEnclosingFunctionOfScriptedCaller(cx);
|
||||
|
||||
args.rval().set(UndefinedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
BEGIN_TEST(test_enclosingFunction)
|
||||
{
|
||||
CHECK(JS_DefineFunction(cx, global, "checkEnclosing", CheckEnclosing, 0, 0));
|
||||
|
||||
EXEC("checkEnclosing()");
|
||||
CHECK(foundFun == nullptr);
|
||||
|
||||
RootedFunction fun(cx);
|
||||
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(__FILE__, __LINE__);
|
||||
|
||||
const char s1chars[] = "checkEnclosing()";
|
||||
JS::AutoObjectVector emptyScopeChain(cx);
|
||||
CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s1", 0, nullptr, s1chars,
|
||||
strlen(s1chars), &fun));
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, global, "s1", fun, JSPROP_ENUMERATE));
|
||||
EXEC("s1()");
|
||||
CHECK(foundFun == fun);
|
||||
|
||||
const char s2chars[] = "return function() { checkEnclosing() }";
|
||||
CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s2", 0, nullptr, s2chars,
|
||||
strlen(s2chars), &fun));
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, global, "s2", fun, JSPROP_ENUMERATE));
|
||||
EXEC("s2()()");
|
||||
CHECK(foundFun == fun);
|
||||
|
||||
const char s3chars[] = "return function() { { let x; function g() { checkEnclosing() } return g() } }";
|
||||
CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s3", 0, nullptr, s3chars,
|
||||
strlen(s3chars), &fun));
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, global, "s3", fun, JSPROP_ENUMERATE));
|
||||
EXEC("s3()()");
|
||||
CHECK(foundFun == fun);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(test_enclosingFunction)
|
|
@ -418,34 +418,6 @@ js::RunningWithTrustedPrincipals(JSContext* cx)
|
|||
return cx->runningWithTrustedPrincipals();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSFunction*)
|
||||
js::GetOutermostEnclosingFunctionOfScriptedCaller(JSContext* cx)
|
||||
{
|
||||
ScriptFrameIter iter(cx);
|
||||
|
||||
// Skip eval frames.
|
||||
while (!iter.done() && iter.isEvalFrame())
|
||||
++iter;
|
||||
|
||||
if (iter.done())
|
||||
return nullptr;
|
||||
|
||||
if (!iter.isFunctionFrame())
|
||||
return nullptr;
|
||||
|
||||
if (iter.compartment() != cx->compartment())
|
||||
return nullptr;
|
||||
|
||||
RootedFunction curr(cx, iter.callee(cx));
|
||||
for (ScopeIter si(curr->nonLazyScript()); si; si++) {
|
||||
if (si.kind() == ScopeKind::Function)
|
||||
curr = si.scope()->as<FunctionScope>().canonicalFunction();
|
||||
}
|
||||
|
||||
assertSameCompartment(cx, curr);
|
||||
return curr;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSFunction*)
|
||||
js::DefineFunctionWithReserved(JSContext* cx, JSObject* objArg, const char* name, JSNative call,
|
||||
unsigned nargs, unsigned attrs)
|
||||
|
|
|
@ -677,18 +677,6 @@ inline void AssertSameCompartment(JSObject* objA, JSObject* objB) {}
|
|||
JS_FRIEND_API(void)
|
||||
NotifyAnimationActivity(JSObject* obj);
|
||||
|
||||
/**
|
||||
* Return the outermost enclosing function (script) of the scripted caller.
|
||||
* This function returns nullptr in several cases:
|
||||
* - no script is running on the context
|
||||
* - the caller is in global or eval code
|
||||
* In particular, this function will "stop" its outermost search at eval() and
|
||||
* thus it will really return the outermost enclosing function *since the
|
||||
* innermost eval*.
|
||||
*/
|
||||
JS_FRIEND_API(JSFunction*)
|
||||
GetOutermostEnclosingFunctionOfScriptedCaller(JSContext* cx);
|
||||
|
||||
JS_FRIEND_API(JSFunction*)
|
||||
DefineFunctionWithReserved(JSContext* cx, JSObject* obj, const char* name, JSNative call,
|
||||
unsigned nargs, unsigned attrs);
|
||||
|
@ -2850,14 +2838,6 @@ extern JS_FRIEND_API(void)
|
|||
SetJitExceptionHandler(JitExceptionHandler handler);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the nearest enclosing with environment object for a given function. If
|
||||
* the function is not scripted or is not enclosed by a with scope, returns
|
||||
* the global.
|
||||
*/
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
GetNearestEnclosingWithEnvironmentObjectForFunction(JSFunction* fun);
|
||||
|
||||
/**
|
||||
* Get the first SavedFrame object in this SavedFrame stack whose principals are
|
||||
* subsumed by the cx's principals. If there is no such frame, return nullptr.
|
||||
|
|
|
@ -3095,23 +3095,6 @@ js::GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx)
|
|||
return GetDebugEnvironment(cx, ei);
|
||||
}
|
||||
|
||||
// See declaration and documentation in jsfriendapi.h
|
||||
JS_FRIEND_API(JSObject*)
|
||||
js::GetNearestEnclosingWithEnvironmentObjectForFunction(JSFunction* fun)
|
||||
{
|
||||
if (!fun->isInterpreted())
|
||||
return &fun->global();
|
||||
|
||||
JSObject* env = fun->environment();
|
||||
while (env && !env->is<WithEnvironmentObject>())
|
||||
env = env->enclosingEnvironment();
|
||||
|
||||
if (!env)
|
||||
return &fun->global();
|
||||
|
||||
return &env->as<WithEnvironmentObject>().object();
|
||||
}
|
||||
|
||||
bool
|
||||
js::CreateObjectsForEnvironmentChain(JSContext* cx, AutoObjectVector& chain,
|
||||
HandleObject terminatingEnv, MutableHandleObject envObj)
|
||||
|
|
|
@ -54,11 +54,6 @@ using namespace mozilla::scache;
|
|||
using namespace xpc;
|
||||
using namespace JS;
|
||||
|
||||
// This JSClass exists to trick silly code that expects toString()ing the
|
||||
// global in a component scope to return something with "BackstagePass" in it
|
||||
// to continue working.
|
||||
static const JSClass kFakeBackstagePassJSClass = { "FakeBackstagePass" };
|
||||
|
||||
static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
|
||||
static const char kJSCachePrefix[] = "jsloader";
|
||||
|
||||
|
@ -196,8 +191,7 @@ mozJSComponentLoader::mozJSComponentLoader()
|
|||
: mModules(16),
|
||||
mImports(16),
|
||||
mInProgressImports(16),
|
||||
mInitialized(false),
|
||||
mReuseLoaderGlobal(false)
|
||||
mInitialized(false)
|
||||
{
|
||||
MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton");
|
||||
|
||||
|
@ -300,8 +294,6 @@ mozJSComponentLoader::ReallyInit()
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
mReuseLoaderGlobal = Preferences::GetBool("jsloader.reuseGlobal");
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secman =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
if (!secman)
|
||||
|
@ -430,9 +422,7 @@ mozJSComponentLoader::LoadModule(FileLocation& aFile)
|
|||
|
||||
// Set the location information for the new global, so that tools like
|
||||
// about:memory may use that information
|
||||
if (!mReuseLoaderGlobal) {
|
||||
xpc::SetLocationForGlobal(entryObj, spec);
|
||||
}
|
||||
|
||||
// The hash owns the ModuleEntry now, forget about it
|
||||
return entry.forget();
|
||||
|
@ -442,27 +432,7 @@ void
|
|||
mozJSComponentLoader::FindTargetObject(JSContext* aCx,
|
||||
MutableHandleObject aTargetObject)
|
||||
{
|
||||
aTargetObject.set(nullptr);
|
||||
|
||||
RootedObject targetObject(aCx);
|
||||
if (mReuseLoaderGlobal) {
|
||||
JSFunction* fun = js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
|
||||
if (fun) {
|
||||
JSObject* funParent = js::GetNearestEnclosingWithEnvironmentObjectForFunction(fun);
|
||||
if (JS_GetClass(funParent) == &kFakeBackstagePassJSClass)
|
||||
targetObject = funParent;
|
||||
}
|
||||
}
|
||||
|
||||
// The above could fail, even if mReuseLoaderGlobal, if the scripted
|
||||
// caller is not a component/JSM (it could be a DOM scope, for
|
||||
// instance).
|
||||
if (!targetObject) {
|
||||
// Our targetObject is the caller's global object. Let's get it.
|
||||
targetObject = CurrentGlobalOrNull(aCx);
|
||||
}
|
||||
|
||||
aTargetObject.set(targetObject);
|
||||
aTargetObject.set(CurrentGlobalOrNull(aCx));
|
||||
}
|
||||
|
||||
// This requires that the keys be strings and the values be pointers.
|
||||
|
@ -489,6 +459,53 @@ mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
|
|||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
mozJSComponentLoader::CreateLoaderGlobal(JSContext* aCx,
|
||||
JSAddonId* aAddonID,
|
||||
MutableHandleObject aGlobal)
|
||||
{
|
||||
RefPtr<BackstagePass> backstagePass;
|
||||
nsresult rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
CompartmentOptions options;
|
||||
|
||||
options.creationOptions()
|
||||
.setSystemZone()
|
||||
.setAddonId(aAddonID);
|
||||
|
||||
options.behaviors().setVersion(JSVERSION_LATEST);
|
||||
|
||||
if (xpc::SharedMemoryEnabled())
|
||||
options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
|
||||
|
||||
// Defer firing OnNewGlobalObject until after the __URI__ property has
|
||||
// been defined so the JS debugger can tell what module the global is
|
||||
// for
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = nsXPConnect::XPConnect()->
|
||||
InitClassesWithNewWrappedGlobal(aCx,
|
||||
static_cast<nsIGlobalObject*>(backstagePass),
|
||||
mSystemPrincipal,
|
||||
nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
|
||||
options,
|
||||
getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
RootedObject global(aCx, holder->GetJSObject());
|
||||
NS_ENSURE_TRUE_VOID(global);
|
||||
|
||||
backstagePass->SetGlobalObject(global);
|
||||
|
||||
JSAutoCompartment ac(aCx, global);
|
||||
if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
|
||||
!JS_DefineProfilingFunctions(aCx, global)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aGlobal.set(global);
|
||||
}
|
||||
|
||||
// Some stack based classes for cleaning up on early return
|
||||
class FileAutoCloser
|
||||
{
|
||||
|
@ -512,79 +529,27 @@ JSObject*
|
|||
mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
|
||||
nsIFile* aComponentFile,
|
||||
nsIURI* aURI,
|
||||
bool aReuseLoaderGlobal,
|
||||
bool* aRealFile)
|
||||
{
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
if (aReuseLoaderGlobal) {
|
||||
holder = mLoaderGlobal;
|
||||
}
|
||||
RootedObject globalObj(aCx);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
bool createdNewGlobal = false;
|
||||
CreateLoaderGlobal(aCx, MapURIToAddonID(aURI), &globalObj);
|
||||
|
||||
if (!mLoaderGlobal) {
|
||||
RefPtr<BackstagePass> backstagePass;
|
||||
rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
// |thisObj| is the object we set properties on for a particular .jsm.
|
||||
// XXX Right now, thisObj is always globalObj, but if we start
|
||||
// sharing globals between jsms, they won't be the same.
|
||||
// See bug 1186409.
|
||||
RootedObject thisObj(aCx, globalObj);
|
||||
NS_ENSURE_TRUE(thisObj, nullptr);
|
||||
|
||||
CompartmentOptions options;
|
||||
|
||||
options.creationOptions()
|
||||
.setSystemZone()
|
||||
.setAddonId(aReuseLoaderGlobal ? nullptr : MapURIToAddonID(aURI));
|
||||
|
||||
options.behaviors().setVersion(JSVERSION_LATEST);
|
||||
|
||||
if (xpc::SharedMemoryEnabled())
|
||||
options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
|
||||
|
||||
// Defer firing OnNewGlobalObject until after the __URI__ property has
|
||||
// been defined so the JS debugger can tell what module the global is
|
||||
// for
|
||||
rv = nsXPConnect::XPConnect()->
|
||||
InitClassesWithNewWrappedGlobal(aCx,
|
||||
static_cast<nsIGlobalObject*>(backstagePass),
|
||||
mSystemPrincipal,
|
||||
nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
|
||||
options,
|
||||
getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
createdNewGlobal = true;
|
||||
|
||||
RootedObject global(aCx, holder->GetJSObject());
|
||||
NS_ENSURE_TRUE(global, nullptr);
|
||||
|
||||
backstagePass->SetGlobalObject(global);
|
||||
|
||||
JSAutoCompartment ac(aCx, global);
|
||||
if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
|
||||
!JS_DefineProfilingFunctions(aCx, global)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aReuseLoaderGlobal) {
|
||||
mLoaderGlobal = holder;
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject obj(aCx, holder->GetJSObject());
|
||||
NS_ENSURE_TRUE(obj, nullptr);
|
||||
|
||||
JSAutoCompartment ac(aCx, obj);
|
||||
|
||||
if (aReuseLoaderGlobal) {
|
||||
// If we're reusing the loader global, we don't actually use the
|
||||
// global, but rather we use a different object as the 'this' object.
|
||||
obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass);
|
||||
NS_ENSURE_TRUE(obj, nullptr);
|
||||
}
|
||||
JSAutoCompartment ac(aCx, thisObj);
|
||||
|
||||
*aRealFile = false;
|
||||
|
||||
// need to be extra careful checking for URIs pointing to files
|
||||
// EnsureFile may not always get called, especially on resource URIs
|
||||
// so we need to call GetFile to make sure this is a valid file
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
|
||||
nsCOMPtr<nsIFile> testFile;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
|
@ -597,13 +562,13 @@ mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
|
|||
if (XRE_IsParentProcess()) {
|
||||
RootedObject locationObj(aCx);
|
||||
|
||||
rv = nsXPConnect::XPConnect()->WrapNative(aCx, obj, aComponentFile,
|
||||
rv = nsXPConnect::XPConnect()->WrapNative(aCx, thisObj, aComponentFile,
|
||||
NS_GET_IID(nsIFile),
|
||||
locationObj.address());
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
NS_ENSURE_TRUE(locationObj, nullptr);
|
||||
|
||||
if (!JS_DefineProperty(aCx, obj, "__LOCATION__", locationObj, 0))
|
||||
if (!JS_DefineProperty(aCx, thisObj, "__LOCATION__", locationObj, 0))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -617,19 +582,18 @@ mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
|
|||
RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length()));
|
||||
NS_ENSURE_TRUE(exposedUri, nullptr);
|
||||
|
||||
if (!JS_DefineProperty(aCx, obj, "__URI__", exposedUri, 0))
|
||||
if (!JS_DefineProperty(aCx, thisObj, "__URI__", exposedUri, 0))
|
||||
return nullptr;
|
||||
|
||||
if (createdNewGlobal) {
|
||||
{
|
||||
// AutoEntryScript required to invoke debugger hook, which is a
|
||||
// Gecko-specific concept at present.
|
||||
dom::AutoEntryScript aes(holder->GetJSObject(),
|
||||
dom::AutoEntryScript aes(globalObj,
|
||||
"component loader report global");
|
||||
RootedObject global(aes.cx(), holder->GetJSObject());
|
||||
JS_FireOnNewGlobalObject(aes.cx(), global);
|
||||
JS_FireOnNewGlobalObject(aes.cx(), globalObj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
return thisObj;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -651,14 +615,13 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
|||
nsresult rv = aInfo.EnsureURI();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aInfo.URI(),
|
||||
mReuseLoaderGlobal, &realFile));
|
||||
&realFile));
|
||||
NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
|
||||
MOZ_ASSERT(JS_IsGlobalObject(obj) == !mReuseLoaderGlobal);
|
||||
MOZ_ASSERT(JS_IsGlobalObject(obj));
|
||||
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
|
||||
RootedScript script(cx);
|
||||
RootedFunction function(cx);
|
||||
|
||||
nsAutoCString nativePath;
|
||||
rv = aInfo.URI()->GetSpec(nativePath);
|
||||
|
@ -675,52 +638,37 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
|||
rv = PathifyURI(aInfo.URI(), cachePath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!mReuseLoaderGlobal) {
|
||||
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
|
||||
if (!script && cache) {
|
||||
ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
|
||||
}
|
||||
} else if (cache) {
|
||||
ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
|
||||
function.address());
|
||||
}
|
||||
|
||||
if (script || function) {
|
||||
if (script) {
|
||||
LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
|
||||
} else if (cache) {
|
||||
// This is ok, it just means the script is not yet in the
|
||||
// cache. Could mean that the cache was corrupted and got removed,
|
||||
// but either way we're going to write this out.
|
||||
writeToCache = true;
|
||||
// ReadCachedScript and ReadCachedFunction may have set a pending
|
||||
// exception.
|
||||
// ReadCachedScript may have set a pending exception.
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
|
||||
if (!script && !function) {
|
||||
if (!script) {
|
||||
// The script wasn't in the cache , so compile it now.
|
||||
LOG(("Slow loading %s\n", nativePath.get()));
|
||||
|
||||
// Use lazy source if both of these conditions hold:
|
||||
//
|
||||
// (1) mReuseLoaderGlobal is false. If mReuseLoaderGlobal is true, we
|
||||
// can't do lazy source because we compile things as functions
|
||||
// (rather than script), and lazy source isn't supported in that
|
||||
// configuration. That's ok though, because we only do
|
||||
// mReuseLoaderGlobal on b2g, where we invoke setDiscardSource(true)
|
||||
// on the entire global.
|
||||
//
|
||||
// (2) We're using the startup cache. Non-lazy source + startup cache
|
||||
// regresses installer size (due to source code stored in XDR
|
||||
// encoded modules in omni.ja). Also, XDR decoding is relatively
|
||||
// fast. Content processes don't use the startup cache, so we want
|
||||
// them to use non-lazy source code to enable lazy parsing.
|
||||
// Use lazy source if we're using the startup cache. Non-lazy source +
|
||||
// startup cache regresses installer size (due to source code stored in
|
||||
// XDR encoded modules in omni.ja). Also, XDR decoding is relatively
|
||||
// fast. When we're not using the startup cache, we want to use non-lazy
|
||||
// source code so that we can use lazy parsing.
|
||||
// See bug 1303754.
|
||||
CompileOptions options(cx);
|
||||
options.setNoScriptRval(mReuseLoaderGlobal ? false : true)
|
||||
options.setNoScriptRval(true)
|
||||
.setVersion(JSVERSION_LATEST)
|
||||
.setFileAndLine(nativePath.get(), 1)
|
||||
.setSourceIsLazy(!mReuseLoaderGlobal && !!cache);
|
||||
.setSourceIsLazy(!!cache);
|
||||
|
||||
if (realFile) {
|
||||
int64_t fileSize;
|
||||
|
@ -766,18 +714,9 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mReuseLoaderGlobal) {
|
||||
Compile(cx, options, buf, fileSize32, &script);
|
||||
} else {
|
||||
// Note: exceptions will get handled further down;
|
||||
// don't early return for them here.
|
||||
AutoObjectVector envChain(cx);
|
||||
if (envChain.append(obj)) {
|
||||
CompileFunction(cx, envChain,
|
||||
options, nullptr, 0, nullptr,
|
||||
buf, fileSize32, &function);
|
||||
}
|
||||
}
|
||||
Compile(cx, options, buf, fileSize32, &script);
|
||||
|
||||
PR_MemUnmap(buf, fileSize32);
|
||||
} else {
|
||||
|
@ -810,51 +749,26 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
|||
|
||||
buf[len] = '\0';
|
||||
|
||||
if (!mReuseLoaderGlobal) {
|
||||
Compile(cx, options, buf.get(), bytesRead, &script);
|
||||
} else {
|
||||
// Note: exceptions will get handled further down;
|
||||
// don't early return for them here.
|
||||
AutoObjectVector envChain(cx);
|
||||
if (envChain.append(obj)) {
|
||||
CompileFunction(cx, envChain,
|
||||
options, nullptr, 0, nullptr,
|
||||
buf.get(), bytesRead, &function);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Propagate the exception, if one exists. Also, don't leave the stale
|
||||
// exception on this context.
|
||||
if (!script && !function && aPropagateExceptions &&
|
||||
jsapi.HasException()) {
|
||||
if (!script && aPropagateExceptions && jsapi.HasException()) {
|
||||
if (!jsapi.StealException(aException))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
if (!script && !function) {
|
||||
if (!script) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// We must have a script or a function (but not both!) here. We have a
|
||||
// script when we're not reusing the loader global, and a function
|
||||
// otherwise.
|
||||
MOZ_ASSERT(!!script != !!function);
|
||||
MOZ_ASSERT(!!script == JS_IsGlobalObject(obj));
|
||||
|
||||
if (script) {
|
||||
ScriptPreloader::GetSingleton().NoteScript(nativePath, cachePath, script);
|
||||
}
|
||||
|
||||
if (writeToCache) {
|
||||
// We successfully compiled the script, so cache it.
|
||||
if (script) {
|
||||
rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal,
|
||||
script);
|
||||
} else {
|
||||
rv = WriteCachedFunction(cache, cachePath, cx, mSystemPrincipal,
|
||||
function);
|
||||
}
|
||||
|
||||
// Don't treat failure to write as fatal, since we might be working
|
||||
// with a read-only cache.
|
||||
|
@ -869,34 +783,18 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
|||
// See bug 384168.
|
||||
aObject.set(obj);
|
||||
|
||||
RootedScript tableScript(cx, script);
|
||||
if (!tableScript) {
|
||||
tableScript = JS_GetFunctionScript(cx, function);
|
||||
MOZ_ASSERT(tableScript);
|
||||
}
|
||||
|
||||
aTableScript.set(tableScript);
|
||||
aTableScript.set(script);
|
||||
|
||||
|
||||
{ // Scope for AutoEntryScript
|
||||
|
||||
// We're going to run script via JS_ExecuteScript or
|
||||
// JS_CallFunction, so we need an AutoEntryScript.
|
||||
// This is Gecko-specific and not in any spec.
|
||||
// We're going to run script via JS_ExecuteScript, so we need an
|
||||
// AutoEntryScript. This is Gecko-specific and not in any spec.
|
||||
dom::AutoEntryScript aes(CurrentGlobalOrNull(cx),
|
||||
"component loader load module");
|
||||
JSContext* aescx = aes.cx();
|
||||
bool ok;
|
||||
if (script) {
|
||||
JS::RootedValue rval(cx);
|
||||
ok = JS::CloneAndExecuteScript(aescx, script, &rval);
|
||||
} else {
|
||||
RootedValue rval(cx);
|
||||
ok = JS_CallFunction(aescx, obj, function,
|
||||
JS::HandleValueArray::empty(), &rval);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (!JS::CloneAndExecuteScript(aescx, script, &rval)) {
|
||||
if (aPropagateExceptions && aes.HasException()) {
|
||||
// Ignore return value because we're returning an error code
|
||||
// anyway.
|
||||
|
@ -924,26 +822,6 @@ mozJSComponentLoader::UnloadModules()
|
|||
{
|
||||
mInitialized = false;
|
||||
|
||||
if (mLoaderGlobal) {
|
||||
MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?");
|
||||
|
||||
dom::AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
RootedObject global(cx, mLoaderGlobal->GetJSObject());
|
||||
if (global) {
|
||||
JSAutoCompartment ac(cx, global);
|
||||
if (JS_HasExtensibleLexicalEnvironment(global)) {
|
||||
JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalEnvironment(global));
|
||||
}
|
||||
JS_SetAllNonReservedSlotsToUndefined(cx, global);
|
||||
} else {
|
||||
NS_WARNING("Going to leak!");
|
||||
}
|
||||
|
||||
mLoaderGlobal = nullptr;
|
||||
}
|
||||
|
||||
mInProgressImports.Clear();
|
||||
mImports.Clear();
|
||||
|
||||
|
@ -1141,9 +1019,7 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
|
|||
|
||||
// Set the location information for the new global, so that tools like
|
||||
// about:memory may use that information
|
||||
if (!mReuseLoaderGlobal) {
|
||||
xpc::SetLocationForGlobal(newEntry->obj, aLocation);
|
||||
}
|
||||
|
||||
mod = newEntry;
|
||||
}
|
||||
|
@ -1286,9 +1162,6 @@ mozJSComponentLoader::Unload(const nsACString & aLocation)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(!mReuseLoaderGlobal, "Module unloading not supported when "
|
||||
"compartment sharing is enabled");
|
||||
|
||||
ComponentLoaderInfo info(aLocation);
|
||||
rv = info.EnsureKey();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -62,10 +62,13 @@ class mozJSComponentLoader : public mozilla::ModuleLoader,
|
|||
nsresult ReallyInit();
|
||||
void UnloadModules();
|
||||
|
||||
void CreateLoaderGlobal(JSContext* aCx,
|
||||
JSAddonId* aAddonID,
|
||||
JS::MutableHandleObject aGlobal);
|
||||
|
||||
JSObject* PrepareObjectForLocation(JSContext* aCx,
|
||||
nsIFile* aComponentFile,
|
||||
nsIURI* aComponent,
|
||||
bool aReuseLoaderGlobal,
|
||||
bool* aRealFile);
|
||||
|
||||
nsresult ObjectForLocation(ComponentLoaderInfo& aInfo,
|
||||
|
@ -83,7 +86,6 @@ class mozJSComponentLoader : public mozilla::ModuleLoader,
|
|||
|
||||
nsCOMPtr<nsIComponentManager> mCompMgr;
|
||||
nsCOMPtr<nsIPrincipal> mSystemPrincipal;
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> mLoaderGlobal;
|
||||
|
||||
class ModuleEntry : public mozilla::Module
|
||||
{
|
||||
|
@ -155,7 +157,6 @@ class mozJSComponentLoader : public mozilla::ModuleLoader,
|
|||
nsDataHashtable<nsCStringHashKey, ModuleEntry*> mInProgressImports;
|
||||
|
||||
bool mInitialized;
|
||||
bool mReuseLoaderGlobal;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,14 +43,6 @@ ReadCachedScript(StartupCache* cache, nsACString& uri, JSContext* cx,
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ReadCachedFunction(StartupCache* cache, nsACString& uri, JSContext* cx,
|
||||
nsIPrincipal* systemPrincipal, JSFunction** functionp)
|
||||
{
|
||||
// This doesn't actually work ...
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WriteCachedScript(StartupCache* cache, nsACString& uri, JSContext* cx,
|
||||
nsIPrincipal* systemPrincipal, HandleScript script)
|
||||
|
@ -75,11 +67,3 @@ WriteCachedScript(StartupCache* cache, nsACString& uri, JSContext* cx,
|
|||
size);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WriteCachedFunction(StartupCache* cache, nsACString& uri, JSContext* cx,
|
||||
nsIPrincipal* systemPrincipal, JSFunction* function)
|
||||
{
|
||||
// This doesn't actually work ...
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
|
|
@ -20,18 +20,9 @@ ReadCachedScript(mozilla::scache::StartupCache* cache, nsACString& uri,
|
|||
JSContext* cx, nsIPrincipal* systemPrincipal,
|
||||
JS::MutableHandleScript scriptp);
|
||||
|
||||
nsresult
|
||||
ReadCachedFunction(mozilla::scache::StartupCache* cache, nsACString& uri,
|
||||
JSContext* cx, nsIPrincipal* systemPrincipal,
|
||||
JSFunction** function);
|
||||
|
||||
nsresult
|
||||
WriteCachedScript(mozilla::scache::StartupCache* cache, nsACString& uri,
|
||||
JSContext* cx, nsIPrincipal* systemPrincipal,
|
||||
JS::HandleScript script);
|
||||
nsresult
|
||||
WriteCachedFunction(mozilla::scache::StartupCache* cache, nsACString& uri,
|
||||
JSContext* cx, nsIPrincipal* systemPrincipal,
|
||||
JSFunction* function);
|
||||
|
||||
#endif /* mozJSLoaderUtils_h */
|
||||
|
|
|
@ -132,15 +132,11 @@ PrepareScript(nsIURI* uri,
|
|||
const nsAString& charset,
|
||||
const char* buf,
|
||||
int64_t len,
|
||||
bool reuseGlobal,
|
||||
bool wantReturnValue,
|
||||
MutableHandleScript script,
|
||||
MutableHandleFunction function)
|
||||
MutableHandleScript script)
|
||||
{
|
||||
JS::CompileOptions options(cx);
|
||||
// Use line 0 to make the function body starts from line 1 when
|
||||
// |reuseGlobal == true|.
|
||||
options.setFileAndLine(uriStr, reuseGlobal ? 0 : 1)
|
||||
options.setFileAndLine(uriStr, 1)
|
||||
.setVersion(JSVERSION_LATEST)
|
||||
.setNoScriptRval(!wantReturnValue);
|
||||
if (!charset.IsVoid()) {
|
||||
|
@ -159,34 +155,18 @@ PrepareScript(nsIURI* uri,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!reuseGlobal) {
|
||||
if (JS_IsGlobalObject(targetObj)) {
|
||||
return JS::Compile(cx, options, srcBuf, script);
|
||||
}
|
||||
return JS::CompileForNonSyntacticScope(cx, options, srcBuf, script);
|
||||
}
|
||||
AutoObjectVector envChain(cx);
|
||||
if (!JS_IsGlobalObject(targetObj) && !envChain.append(targetObj)) {
|
||||
return false;
|
||||
}
|
||||
return JS::CompileFunction(cx, envChain, options, nullptr, 0, nullptr,
|
||||
srcBuf, function);
|
||||
}
|
||||
// We only use lazy source when no special encoding is specified because
|
||||
// the lazy source loader doesn't know the encoding.
|
||||
if (!reuseGlobal) {
|
||||
options.setSourceIsLazy(true);
|
||||
if (JS_IsGlobalObject(targetObj)) {
|
||||
return JS::Compile(cx, options, buf, len, script);
|
||||
}
|
||||
return JS::CompileForNonSyntacticScope(cx, options, buf, len, script);
|
||||
}
|
||||
AutoObjectVector envChain(cx);
|
||||
if (!JS_IsGlobalObject(targetObj) && !envChain.append(targetObj)) {
|
||||
return false;
|
||||
}
|
||||
return JS::CompileFunction(cx, envChain, options, nullptr, 0, nullptr,
|
||||
buf, len, function);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -196,18 +176,8 @@ EvalScript(JSContext* cx,
|
|||
nsIURI* uri,
|
||||
bool startupCache,
|
||||
bool preloadCache,
|
||||
MutableHandleScript script,
|
||||
HandleFunction function)
|
||||
MutableHandleScript script)
|
||||
{
|
||||
if (function) {
|
||||
script.set(JS_GetFunctionScript(cx, function));
|
||||
}
|
||||
|
||||
if (function) {
|
||||
if (!JS_CallFunction(cx, targetObj, function, JS::HandleValueArray::empty(), retval)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (JS_IsGlobalObject(targetObj)) {
|
||||
if (!JS::CloneAndExecuteScript(cx, script, retval)) {
|
||||
return false;
|
||||
|
@ -221,7 +191,6 @@ EvalScript(JSContext* cx,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSAutoCompartment rac(cx, targetObj);
|
||||
if (!JS_WrapValue(cx, retval)) {
|
||||
|
@ -270,14 +239,13 @@ public:
|
|||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader)
|
||||
|
||||
AsyncScriptLoader(nsIChannel* aChannel, bool aReuseGlobal, bool aWantReturnValue,
|
||||
AsyncScriptLoader(nsIChannel* aChannel, bool aWantReturnValue,
|
||||
JSObject* aTargetObj, const nsAString& aCharset,
|
||||
bool aCache, Promise* aPromise)
|
||||
: mChannel(aChannel)
|
||||
, mTargetObj(aTargetObj)
|
||||
, mPromise(aPromise)
|
||||
, mCharset(aCharset)
|
||||
, mReuseGlobal(aReuseGlobal)
|
||||
, mWantReturnValue(aWantReturnValue)
|
||||
, mCache(aCache)
|
||||
{
|
||||
|
@ -294,7 +262,6 @@ private:
|
|||
Heap<JSObject*> mTargetObj;
|
||||
RefPtr<Promise> mPromise;
|
||||
nsString mCharset;
|
||||
bool mReuseGlobal;
|
||||
bool mWantReturnValue;
|
||||
bool mCache;
|
||||
};
|
||||
|
@ -394,7 +361,6 @@ AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
RootedFunction function(cx);
|
||||
RootedScript script(cx);
|
||||
nsAutoCString spec;
|
||||
nsresult rv = uri->GetSpec(spec);
|
||||
|
@ -404,7 +370,7 @@ AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
|
|||
|
||||
if (!PrepareScript(uri, cx, targetObj, spec.get(), mCharset,
|
||||
reinterpret_cast<const char*>(aBuf), aLength,
|
||||
mReuseGlobal, mWantReturnValue, &script, &function))
|
||||
mWantReturnValue, &script))
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -412,7 +378,7 @@ AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
|
|||
JS::Rooted<JS::Value> retval(cx);
|
||||
if (EvalScript(cx, targetObj, &retval, uri, mCache,
|
||||
mCache && !mWantReturnValue,
|
||||
&script, function)) {
|
||||
&script)) {
|
||||
autoPromise.ResolvePromise(retval);
|
||||
}
|
||||
|
||||
|
@ -424,7 +390,6 @@ mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri,
|
|||
HandleObject targetObj,
|
||||
const nsAString& charset,
|
||||
nsIIOService* serv,
|
||||
bool reuseGlobal,
|
||||
bool wantReturnValue,
|
||||
bool cache,
|
||||
MutableHandleValue retval)
|
||||
|
@ -467,7 +432,6 @@ mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri,
|
|||
|
||||
RefPtr<AsyncScriptLoader> loadObserver =
|
||||
new AsyncScriptLoader(channel,
|
||||
reuseGlobal,
|
||||
wantReturnValue,
|
||||
targetObj,
|
||||
charset,
|
||||
|
@ -489,13 +453,10 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri,
|
|||
const nsAString& charset,
|
||||
const char* uriStr,
|
||||
nsIIOService* serv,
|
||||
bool reuseGlobal,
|
||||
bool wantReturnValue,
|
||||
MutableHandleScript script,
|
||||
MutableHandleFunction function)
|
||||
MutableHandleScript script)
|
||||
{
|
||||
script.set(nullptr);
|
||||
function.set(nullptr);
|
||||
|
||||
// We create a channel and call SetContentType, to avoid expensive MIME type
|
||||
// lookups (bug 632490).
|
||||
|
@ -540,9 +501,8 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri,
|
|||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return PrepareScript(uri, cx, targetObj, uriStr, charset,
|
||||
buf.get(), len,
|
||||
reuseGlobal, wantReturnValue,
|
||||
script, function);
|
||||
buf.get(), len, wantReturnValue,
|
||||
script);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -605,15 +565,13 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
|
|||
}
|
||||
|
||||
RootedObject targetObj(cx);
|
||||
if (options.target) {
|
||||
targetObj = options.target;
|
||||
} else {
|
||||
mozJSComponentLoader* loader = mozJSComponentLoader::Get();
|
||||
loader->FindTargetObject(cx, &targetObj);
|
||||
|
||||
// We base reusingGlobal off of what the loader told us, but we may not
|
||||
// actually be using that object.
|
||||
bool reusingGlobal = !JS_IsGlobalObject(targetObj);
|
||||
|
||||
if (options.target)
|
||||
targetObj = options.target;
|
||||
MOZ_ASSERT(JS_IsGlobalObject(targetObj));
|
||||
}
|
||||
|
||||
// Remember an object out of the calling compartment so that we
|
||||
// can properly wrap the result later.
|
||||
|
@ -694,7 +652,6 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
|
|||
cachePath.AppendPrintf("jssubloader/%d", version);
|
||||
PathifyURI(uri, cachePath);
|
||||
|
||||
RootedFunction function(cx);
|
||||
RootedScript script(cx);
|
||||
if (!options.ignoreCache) {
|
||||
if (!options.wantReturnValue)
|
||||
|
@ -710,25 +667,22 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
|
|||
// If we are doing an async load, trigger it and bail out.
|
||||
if (!script && options.async) {
|
||||
return ReadScriptAsync(uri, targetObj, options.charset, serv,
|
||||
reusingGlobal, options.wantReturnValue,
|
||||
!!cache, retval);
|
||||
options.wantReturnValue, !!cache, retval);
|
||||
}
|
||||
|
||||
if (!script) {
|
||||
if (!ReadScript(uri, cx, targetObj, options.charset,
|
||||
static_cast<const char*>(uriStr.get()), serv,
|
||||
reusingGlobal, options.wantReturnValue, &script,
|
||||
&function))
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
} else {
|
||||
if (script) {
|
||||
// |script| came from the cache, so don't bother writing it
|
||||
// |back there.
|
||||
cache = nullptr;
|
||||
} else if (!ReadScript(uri, cx, targetObj, options.charset,
|
||||
static_cast<const char*>(uriStr.get()), serv,
|
||||
options.wantReturnValue, &script)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Unused << EvalScript(cx, targetObj, retval, uri, !!cache,
|
||||
!ignoreCache && !options.wantReturnValue,
|
||||
&script, function);
|
||||
&script);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,14 +36,15 @@ private:
|
|||
bool ReadScript(nsIURI* uri, JSContext* cx, JS::HandleObject targetObj,
|
||||
const nsAString& charset, const char* uriStr,
|
||||
nsIIOService* serv,
|
||||
bool reuseGlobal, bool wantReturnValue,
|
||||
JS::MutableHandleScript script,
|
||||
JS::MutableHandleFunction function);
|
||||
bool wantReturnValue,
|
||||
JS::MutableHandleScript script);
|
||||
|
||||
nsresult ReadScriptAsync(nsIURI* uri, JS::HandleObject targetObj,
|
||||
nsresult ReadScriptAsync(nsIURI* uri,
|
||||
JS::HandleObject targetObj,
|
||||
const nsAString& charset,
|
||||
nsIIOService* serv, bool reuseGlobal,
|
||||
bool wantReturnValue, bool cache,
|
||||
nsIIOService* serv,
|
||||
bool wantReturnValue,
|
||||
bool cache,
|
||||
JS::MutableHandleValue retval);
|
||||
|
||||
nsresult DoLoadSubScriptWithOptions(const nsAString& url,
|
||||
|
|
|
@ -273,6 +273,12 @@ public:
|
|||
nsChangeHint aMinChangeHint,
|
||||
const RestyleHintData* aRestyleHintData = nullptr);
|
||||
|
||||
void PostRestyleEventForCSSRuleChanges(Element* aElement,
|
||||
nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aMinChangeHint) {
|
||||
PostRestyleEvent(aElement, aRestyleHint, aMinChangeHint);
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Asynchronously clear style data from the root frame downwards and ensure
|
||||
|
|
|
@ -4584,11 +4584,13 @@ nsIPresShell::RestyleForCSSRuleChanges()
|
|||
// If scopeRoots is empty, we know that mStylesHaveChanged was true at
|
||||
// the beginning of this function, and that we need to restyle the whole
|
||||
// document.
|
||||
restyleManager->PostRestyleEvent(root, eRestyle_Subtree,
|
||||
restyleManager->PostRestyleEventForCSSRuleChanges(root,
|
||||
eRestyle_Subtree,
|
||||
nsChangeHint(0));
|
||||
} else {
|
||||
for (Element* scopeRoot : scopeRoots) {
|
||||
restyleManager->PostRestyleEvent(scopeRoot, eRestyle_Subtree,
|
||||
restyleManager->PostRestyleEventForCSSRuleChanges(scopeRoot,
|
||||
eRestyle_Subtree,
|
||||
nsChangeHint(0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,6 +161,9 @@ public:
|
|||
inline void PostRestyleEvent(dom::Element* aElement,
|
||||
nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aMinChangeHint);
|
||||
inline void PostRestyleEventForCSSRuleChanges(dom::Element* aElement,
|
||||
nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aMinChangeHint);
|
||||
inline void RebuildAllStyleData(nsChangeHint aExtraHint,
|
||||
nsRestyleHint aRestyleHint);
|
||||
inline void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
|
||||
|
|
|
@ -24,6 +24,15 @@ RestyleManager::PostRestyleEvent(dom::Element* aElement,
|
|||
MOZ_STYLO_FORWARD(PostRestyleEvent, (aElement, aRestyleHint, aMinChangeHint));
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManager::PostRestyleEventForCSSRuleChanges(dom::Element* aElement,
|
||||
nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aMinChangeHint)
|
||||
{
|
||||
MOZ_STYLO_FORWARD(PostRestyleEventForCSSRuleChanges,
|
||||
(aElement, aRestyleHint, aMinChangeHint));
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
|
||||
nsRestyleHint aRestyleHint)
|
||||
|
|
|
@ -66,6 +66,17 @@ ServoRestyleManager::PostRestyleEvent(Element* aElement,
|
|||
Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
|
||||
}
|
||||
|
||||
void
|
||||
ServoRestyleManager::PostRestyleEventForCSSRuleChanges(
|
||||
Element* aElement,
|
||||
nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aMinChangeHint)
|
||||
{
|
||||
mRestyleForCSSRuleChanges = true;
|
||||
|
||||
PostRestyleEvent(aElement, aRestyleHint, aMinChangeHint);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ServoRestyleManager::PostRestyleEventForAnimations(Element* aElement,
|
||||
nsRestyleHint aRestyleHint)
|
||||
|
@ -553,8 +564,11 @@ ServoRestyleManager::DoProcessPendingRestyles(TraversalRestyleBehavior
|
|||
++mAnimationGeneration;
|
||||
}
|
||||
|
||||
TraversalRestyleBehavior restyleBehavior = mRestyleForCSSRuleChanges
|
||||
? TraversalRestyleBehavior::ForCSSRuleChanges
|
||||
: TraversalRestyleBehavior::Normal;
|
||||
while (animationOnly ? styleSet->StyleDocumentForAnimationOnly()
|
||||
: styleSet->StyleDocument()) {
|
||||
: styleSet->StyleDocument(restyleBehavior)) {
|
||||
if (!animationOnly) {
|
||||
ClearSnapshots();
|
||||
}
|
||||
|
@ -603,6 +617,7 @@ ServoRestyleManager::DoProcessPendingRestyles(TraversalRestyleBehavior
|
|||
styleSet->AssertTreeIsClean();
|
||||
mHaveNonAnimationRestyles = false;
|
||||
}
|
||||
mRestyleForCSSRuleChanges = false;
|
||||
mInStyleRefresh = false;
|
||||
|
||||
// Note: We are in the scope of |animationsWithDestroyedFrame|, so
|
||||
|
|
|
@ -44,6 +44,9 @@ public:
|
|||
nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aMinChangeHint);
|
||||
void PostRestyleEventForLazyConstruction();
|
||||
void PostRestyleEventForCSSRuleChanges(dom::Element* aElement,
|
||||
nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aMinChangeHint);
|
||||
void RebuildAllStyleData(nsChangeHint aExtraHint,
|
||||
nsRestyleHint aRestyleHint);
|
||||
void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
|
||||
|
@ -162,6 +165,14 @@ private:
|
|||
// creation sequence will be correct.
|
||||
bool mHaveNonAnimationRestyles = false;
|
||||
|
||||
// Set to true when posting restyle events triggered by CSS rule changes.
|
||||
// This flag is cleared once ProcessPendingRestyles has completed.
|
||||
// When we process a traversal all descendants elements of the document
|
||||
// triggered by CSS rule changes, we will need to update all elements with
|
||||
// CSS animations. We propagate TraversalRestyleBehavior::ForCSSRuleChanges
|
||||
// to traversal function if this flag is set.
|
||||
bool mRestyleForCSSRuleChanges = false;
|
||||
|
||||
// A hashtable with the elements that have changed state or attributes, in
|
||||
// order to calculate restyle hints during the traversal.
|
||||
SnapshotTable mSnapshots;
|
||||
|
|
|
@ -2537,7 +2537,7 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle
|
|||
// We delay traversing the entire document until here, since we per above we
|
||||
// may invalidate the root style when we load doc stylesheets.
|
||||
if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) {
|
||||
set->StyleDocument();
|
||||
set->StyleDocument(TraversalRestyleBehavior::Normal);
|
||||
}
|
||||
|
||||
// --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
|
||||
|
|
|
@ -157,16 +157,16 @@ fuzzy-if(d2d&&layersGPUAccelerated,3,1200) == dynamic-rect-02.svg dynamic-rect-0
|
|||
== dynamic-stroke-opacity-01.svg pass.svg
|
||||
== dynamic-stroke-width-01.svg pass.svg
|
||||
== dynamic-switch-01.svg pass.svg
|
||||
fails-if(stylo) == dynamic-text-01.svg dynamic-text-01-ref.svg
|
||||
fuzzy-if(d2d&&layersGPUAccelerated,3,12739) fails-if(stylo) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7, Win8
|
||||
fuzzy-if(d2d&&layersGPUAccelerated,2,10539) fails-if(stylo) == dynamic-text-03.svg dynamic-text-03-ref.svg # bug 776038 for Win7
|
||||
fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),47,89) fails-if(stylo) == dynamic-text-04.svg dynamic-text-04-ref.svg # bug 776038 for Win7
|
||||
== dynamic-text-01.svg dynamic-text-01-ref.svg
|
||||
fuzzy-if(d2d&&layersGPUAccelerated,3,12739) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7, Win8
|
||||
fuzzy-if(d2d&&layersGPUAccelerated,2,10539) == dynamic-text-03.svg dynamic-text-03-ref.svg # bug 776038 for Win7
|
||||
fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),47,89) == dynamic-text-04.svg dynamic-text-04-ref.svg # bug 776038 for Win7
|
||||
== dynamic-text-05.svg pass.svg
|
||||
== dynamic-text-06.svg pass.svg
|
||||
fails-if(stylo) == dynamic-text-07.svg dynamic-text-07-ref.svg
|
||||
fails-if(stylo) == dynamic-text-08.svg dynamic-text-08-ref.svg
|
||||
== dynamic-text-07.svg dynamic-text-07-ref.svg
|
||||
== dynamic-text-08.svg dynamic-text-08-ref.svg
|
||||
== dynamic-text-attr-01.svg dynamic-text-attr-01-ref.svg
|
||||
fails-if(stylo) == dynamic-textPath-01.svg dynamic-textPath-01-ref.svg
|
||||
== dynamic-textPath-01.svg dynamic-textPath-01-ref.svg
|
||||
== dynamic-textPath-02.svg dynamic-textPath-02-ref.svg
|
||||
== dynamic-textPath-03.svg dynamic-textPath-03-ref.svg
|
||||
== dynamic-use-01.svg pass.svg
|
||||
|
@ -188,7 +188,7 @@ random == dynamic-use-nested-01b.svg dynamic-use-nested-01-ref.svg
|
|||
== fallback-color-02a.svg fallback-color-02-ref.svg
|
||||
== fallback-color-02b.svg fallback-color-02-ref.svg
|
||||
== fallback-color-03.svg pass.svg
|
||||
fuzzy-if(skiaContent,1,2) fails-if(stylo) == fallback-color-04.svg pass.svg
|
||||
fuzzy-if(skiaContent,1,2) == fallback-color-04.svg pass.svg
|
||||
== fallback-color-05.svg fallback-color-05-ref.svg
|
||||
|
||||
== filter-basic-01.svg pass.svg
|
||||
|
@ -236,10 +236,10 @@ fuzzy-if(Android,18,600) == foreignObject-fixedpos-01.html foreignObject-dynamic
|
|||
|
||||
== getElementById-a-element-01.svg pass.svg
|
||||
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) fails-if(stylo) == gradient-live-01a.svg gradient-live-01-ref.svg
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) fails-if(stylo) == gradient-live-01b.svg gradient-live-01-ref.svg
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) fails-if(stylo) == gradient-live-01c.svg gradient-live-01-ref.svg
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) fails-if(stylo) == gradient-live-01d.svg gradient-live-01-ref.svg
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) == gradient-live-01a.svg gradient-live-01-ref.svg
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) == gradient-live-01b.svg gradient-live-01-ref.svg
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) == gradient-live-01c.svg gradient-live-01-ref.svg
|
||||
fuzzy-if(Android,9,980) fuzzy-if(skiaContent,3,32000) == gradient-live-01d.svg gradient-live-01-ref.svg
|
||||
== gradient-transform-01.svg pass.svg
|
||||
== href-attr-change-restyles.svg href-attr-change-restyles-ref.svg
|
||||
fuzzy-if(skiaContent,1,550) == import-svg-01.html pass.svg
|
||||
|
@ -319,7 +319,7 @@ fuzzy-if(skiaContent,1,10000) == opacity-and-transform-01.svg opacity-and-transf
|
|||
|
||||
fuzzy-if(Android,8,200) == outer-svg-border-and-padding-01.svg outer-svg-border-and-padding-01-ref.svg
|
||||
|
||||
fuzzy-if(skiaContent,7,175) fuzzy-if(skiaContent&&webrender,1,225) fails-if(stylo) == outline.html outline-ref.html
|
||||
fuzzy-if(skiaContent,7,175) fuzzy-if(skiaContent&&webrender,1,225) == outline.html outline-ref.html
|
||||
|
||||
== overflow-on-outer-svg-01.svg overflow-on-outer-svg-01-ref.svg
|
||||
== overflow-on-outer-svg-02a.xhtml overflow-on-outer-svg-02-ref.xhtml
|
||||
|
@ -333,7 +333,7 @@ fuzzy-if(skiaContent,7,175) fuzzy-if(skiaContent&&webrender,1,225) fails-if(styl
|
|||
== paint-on-maskLayer-1b.html paint-on-maskLayer-1-ref.html
|
||||
pref(layout.css.clip-path-shapes.enabled,true) == paint-on-maskLayer-1c.html paint-on-maskLayer-1-ref.html
|
||||
pref(svg.paint-order.enabled,true) == paint-order-01.svg paint-order-01-ref.svg
|
||||
pref(svg.paint-order.enabled,true) fails-if(stylo) == paint-order-02.svg paint-order-02-ref.svg
|
||||
pref(svg.paint-order.enabled,true) == paint-order-02.svg paint-order-02-ref.svg
|
||||
pref(svg.paint-order.enabled,true) == paint-order-03.svg paint-order-03-ref.svg
|
||||
|
||||
#fuzzy(23,60) fails-if(d2d) == path-01.svg path-01-ref.svg
|
||||
|
@ -393,7 +393,7 @@ fuzzy-if(skiaContent,1,3600) == rect-01.svg pass.svg
|
|||
fuzzy-if(skiaContent,1,340) fails-if(stylo) == stroke-dasharray-02.svg pass.svg
|
||||
fuzzy-if(skiaContent,1,340) == stroke-dasharray-03.svg pass.svg
|
||||
== stroke-dasharray-and-pathLength-01.svg pass.svg
|
||||
fails-if(stylo) == stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg
|
||||
== stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg
|
||||
== stroke-dashoffset-01.svg pass.svg
|
||||
== stroke-dashoffset-and-pathLength-01.svg pass.svg
|
||||
== stroke-linecap-circle-ellipse-01.svg stroke-linecap-circle-ellipse-01-ref.svg
|
||||
|
@ -434,24 +434,24 @@ fuzzy-if(skiaContent,1,2600) == svg-in-foreignObject-02.xhtml svg-in-foreignObje
|
|||
== text-font-size-01.svg pass.svg
|
||||
random-if(gtkWidget) == text-font-weight-01.svg text-font-weight-01-ref.svg # bug 386713
|
||||
== text-gradient-01.svg text-gradient-01-ref.svg
|
||||
random-if(winWidget) fails-if(stylo) == text-gradient-02.svg text-gradient-02-ref.svg # see bug 590101
|
||||
random-if(winWidget) == text-gradient-02.svg text-gradient-02-ref.svg # see bug 590101
|
||||
fuzzy-if(skiaContent,1,5500) == text-gradient-03.svg pass.svg
|
||||
HTTP(..) == text-gradient-04.svg text-gradient-04-ref.svg
|
||||
== text-in-link-01.svg text-in-link-01-ref.svg
|
||||
== text-in-link-02.svg text-in-link-02-ref.svg
|
||||
fails-if(stylo) == text-in-link-03.svg text-in-link-03-ref.svg
|
||||
== text-in-link-03.svg text-in-link-03-ref.svg
|
||||
# Tests for bug 546813: sanity-check using HTML text, then test SVG behavior.
|
||||
!= text-language-00.xhtml text-language-00-ref.xhtml
|
||||
random-if(gtkWidget) != text-language-01.xhtml text-language-01-ref.xhtml # Fails on Linux tryserver due to lack of CJK fonts.
|
||||
random-if(stylo) == text-layout-01.svg text-layout-01-ref.svg
|
||||
fails-if(stylo) == text-layout-02.svg text-layout-02-ref.svg
|
||||
fails-if(stylo) == text-layout-03.svg text-layout-03-ref.svg
|
||||
fails-if(stylo) == text-layout-04.svg text-layout-04-ref.svg
|
||||
== text-layout-01.svg text-layout-01-ref.svg
|
||||
== text-layout-02.svg text-layout-02-ref.svg
|
||||
== text-layout-03.svg text-layout-03-ref.svg
|
||||
== text-layout-04.svg text-layout-04-ref.svg
|
||||
== text-layout-05.svg text-layout-05-ref.svg
|
||||
fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,3) fails-if(stylo) == text-layout-06.svg text-layout-06-ref.svg
|
||||
fails-if(stylo) == text-layout-07.svg text-layout-07-ref.svg
|
||||
fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,3) == text-layout-06.svg text-layout-06-ref.svg
|
||||
== text-layout-07.svg text-layout-07-ref.svg
|
||||
== text-layout-08.svg text-layout-08-ref.svg
|
||||
fails-if(stylo) == text-scale-01.svg text-scale-01-ref.svg
|
||||
== text-scale-01.svg text-scale-01-ref.svg
|
||||
fuzzy-if(skiaContent,2,1000) HTTP(..) == text-scale-02.svg text-scale-02-ref.svg
|
||||
HTTP(..) == text-scale-03.svg text-scale-03-ref.svg
|
||||
|
||||
|
@ -465,7 +465,7 @@ HTTP(..) == text-scale-03.svg text-scale-03-ref.svg
|
|||
fuzzy(16,3) == text-stroke-scaling-02a.html text-stroke-scaling-02-ref.html # antialiasing
|
||||
fuzzy(16,3) == text-stroke-scaling-02b.html text-stroke-scaling-02-ref.html # antialiasing
|
||||
== text-stroke-scaling-02a.html text-stroke-scaling-02b.html
|
||||
fails-if(stylo) == textPath-01.svg textPath-01-ref.svg
|
||||
== textPath-01.svg textPath-01-ref.svg
|
||||
== textPath-02.svg pass.svg
|
||||
fuzzy-if(skiaContent,1,610) == textPath-03.svg pass.svg
|
||||
== textPath-04.svg pass.svg
|
||||
|
|
|
@ -71,14 +71,14 @@ fails-if(stylo) == anim-css-fontsize-1-from-by-px-px.svg anim-css-fontsize-1-
|
|||
== anim-css-fontsize-1-from-to-px-px.svg anim-css-fontsize-1-ref.svg
|
||||
|
||||
# 'font-size' property (accepts unitless values)
|
||||
fails-if(stylo) == anim-css-fontsize-1-from-to-no-no.svg anim-css-fontsize-1-ref.svg
|
||||
fails-if(stylo) == anim-css-fontsize-1-from-to-no-px.svg anim-css-fontsize-1-ref.svg
|
||||
fails-if(stylo) == anim-css-fontsize-1-from-to-px-no.svg anim-css-fontsize-1-ref.svg
|
||||
== anim-css-fontsize-1-from-to-no-no.svg anim-css-fontsize-1-ref.svg
|
||||
== anim-css-fontsize-1-from-to-no-px.svg anim-css-fontsize-1-ref.svg
|
||||
== anim-css-fontsize-1-from-to-px-no.svg anim-css-fontsize-1-ref.svg
|
||||
|
||||
# 'font-size' mapped attribute (accepts unitless values)
|
||||
fails-if(stylo) == anim-mapped-fontsize-1-from-to-no-no.svg anim-css-fontsize-1-ref.svg
|
||||
fails-if(stylo) == anim-mapped-fontsize-1-from-to-no-px.svg anim-css-fontsize-1-ref.svg
|
||||
fails-if(stylo) == anim-mapped-fontsize-1-from-to-px-no.svg anim-css-fontsize-1-ref.svg
|
||||
== anim-mapped-fontsize-1-from-to-no-no.svg anim-css-fontsize-1-ref.svg
|
||||
== anim-mapped-fontsize-1-from-to-no-px.svg anim-css-fontsize-1-ref.svg
|
||||
== anim-mapped-fontsize-1-from-to-px-no.svg anim-css-fontsize-1-ref.svg
|
||||
|
||||
# 'font-size' property, from/by/to with percent values
|
||||
fails-if(stylo) == anim-css-fontsize-1-from-by-pct-pct.svg anim-css-fontsize-1-ref.svg
|
||||
|
|
|
@ -825,8 +825,14 @@ ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
|
|||
}
|
||||
|
||||
bool
|
||||
ServoStyleSet::StyleDocument()
|
||||
ServoStyleSet::StyleDocument(TraversalRestyleBehavior aRestyleBehavior)
|
||||
{
|
||||
MOZ_ASSERT(
|
||||
aRestyleBehavior == TraversalRestyleBehavior::Normal ||
|
||||
aRestyleBehavior == TraversalRestyleBehavior::ForCSSRuleChanges,
|
||||
"StyleDocument() should be only called for normal traversal or CSS rule "
|
||||
"changes");
|
||||
|
||||
PreTraverse();
|
||||
|
||||
// Restyle the document from the root element and each of the document level
|
||||
|
@ -836,7 +842,7 @@ ServoStyleSet::StyleDocument()
|
|||
while (Element* root = iter.GetNextStyleRoot()) {
|
||||
if (PrepareAndTraverseSubtree(root,
|
||||
TraversalRootBehavior::Normal,
|
||||
TraversalRestyleBehavior::Normal)) {
|
||||
aRestyleBehavior)) {
|
||||
postTraversalRequired = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -230,9 +230,14 @@ public:
|
|||
* This will traverse all of the document's style roots (that is, its document
|
||||
* element, and the roots of the document-level native anonymous content).
|
||||
*
|
||||
* |aRestyleBehavior| should be `Normal` or `ForCSSRuleChanges`.
|
||||
* We need to specify |ForCSSRuleChanges| to try to update all CSS animations
|
||||
* when we call this function due to CSS rule changes since @keyframes rules
|
||||
* may have changed.
|
||||
*
|
||||
* Returns true if a post-traversal is required.
|
||||
*/
|
||||
bool StyleDocument();
|
||||
bool StyleDocument(TraversalRestyleBehavior aRestyleBehavior);
|
||||
|
||||
/**
|
||||
* Performs a Servo animation-only traversal to compute style for all nodes
|
||||
|
|
|
@ -59,10 +59,18 @@ enum class TraversalRootBehavior {
|
|||
// hints, which is what's required when handling frame reconstruction.
|
||||
// The change hints in this case are unneeded, since the old frames have
|
||||
// already been destroyed.
|
||||
// Indicates how the Servo style system should perform.
|
||||
enum class TraversalRestyleBehavior {
|
||||
// Normal processing.
|
||||
Normal,
|
||||
// Traverses in a mode that doesn't generate any change hints, which is what's
|
||||
// required when handling frame reconstruction. The change hints in this case
|
||||
// are unneeded, since the old frames have already been destroyed.
|
||||
ForReconstruct,
|
||||
// Processes animation-only restyle.
|
||||
ForAnimationOnly,
|
||||
// Traverses as normal mode but tries to update all CSS animations.
|
||||
ForCSSRuleChanges,
|
||||
};
|
||||
|
||||
// Represents which tasks are performed in a SequentialTask of UpdateAnimations.
|
||||
|
|
|
@ -176,9 +176,10 @@ nsDOMCSSAttributeDeclaration::GetCSSParsingEnvironment(CSSParsingEnvironment& aC
|
|||
nsDOMCSSDeclaration::ServoCSSParsingEnvironment
|
||||
nsDOMCSSAttributeDeclaration::GetServoCSSParsingEnvironment() const
|
||||
{
|
||||
ServoCSSParsingEnvironment parsingEnv(mElement->GetURLDataForStyleAttr(),
|
||||
mElement->OwnerDoc()->GetCompatibilityMode());
|
||||
return parsingEnv;
|
||||
return {
|
||||
mElement->GetURLDataForStyleAttr(),
|
||||
mElement->OwnerDoc()->GetCompatibilityMode(),
|
||||
};
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -285,16 +285,20 @@ nsDOMCSSDeclaration::GetServoCSSParsingEnvironmentForRule(const css::Rule* aRule
|
|||
{
|
||||
StyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
|
||||
if (!sheet) {
|
||||
return ServoCSSParsingEnvironment(nullptr, eCompatibility_FullStandards);
|
||||
return { nullptr, eCompatibility_FullStandards };
|
||||
}
|
||||
|
||||
if (nsIDocument* document = aRule->GetDocument()) {
|
||||
return ServoCSSParsingEnvironment(sheet->AsServo()->URLData(),
|
||||
document->GetCompatibilityMode());
|
||||
} else {
|
||||
return ServoCSSParsingEnvironment(sheet->AsServo()->URLData(),
|
||||
eCompatibility_FullStandards);
|
||||
return {
|
||||
sheet->AsServo()->URLData(),
|
||||
document->GetCompatibilityMode(),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
sheet->AsServo()->URLData(),
|
||||
eCompatibility_FullStandards,
|
||||
};
|
||||
}
|
||||
|
||||
template<typename GeckoFunc, typename ServoFunc>
|
||||
|
|
|
@ -149,12 +149,16 @@ protected:
|
|||
};
|
||||
|
||||
// Information neded to parse a declaration for Servo side.
|
||||
struct MOZ_STACK_CLASS ServoCSSParsingEnvironment {
|
||||
struct MOZ_STACK_CLASS ServoCSSParsingEnvironment
|
||||
{
|
||||
mozilla::URLExtraData* mUrlExtraData;
|
||||
nsCompatibility mCompatMode;
|
||||
|
||||
ServoCSSParsingEnvironment(mozilla::URLExtraData* aUrlData, nsCompatibility aCompatMode)
|
||||
: mUrlExtraData(aUrlData), mCompatMode(aCompatMode) {}
|
||||
ServoCSSParsingEnvironment(mozilla::URLExtraData* aUrlData,
|
||||
nsCompatibility aCompatMode)
|
||||
: mUrlExtraData(aUrlData)
|
||||
, mCompatMode(aCompatMode)
|
||||
{}
|
||||
};
|
||||
|
||||
// On failure, mPrincipal should be set to null in aCSSParseEnv.
|
||||
|
|
|
@ -36,13 +36,11 @@ to mochitest command.
|
|||
* test_webkit_device_pixel_ratio.html: -webkit-device-pixel-ratio [3]
|
||||
* browser_bug453896.js [8]
|
||||
* Animation support:
|
||||
* test_animations.html [3]
|
||||
* test_animations.html [1]
|
||||
* test_animations_dynamic_changes.html [1]
|
||||
* test_bug716226.html [3]
|
||||
* inserting keyframes rule doesn't trigger restyle bug 1364799:
|
||||
* test_rule_insertion.html `@keyframes` [36]
|
||||
* OMTA
|
||||
* test_animations_omta.html: bug 1361938, bug 1361663 [88]
|
||||
* test_animations_omta.html: bug 1361938, bug 1361663 [85]
|
||||
* SMIL Animation
|
||||
* test_restyles_in_smil_animation.html [2]
|
||||
* CSSOM support:
|
||||
|
|
|
@ -86,6 +86,22 @@ public class ActionBarPresenter {
|
|||
initIndicator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when ActionBar is to start interacting with user. Usually this method is called from
|
||||
* Activity.onResume.
|
||||
*/
|
||||
public void onResume() {
|
||||
mIdentityPopup.registerListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when ActionBar is going to background, but has not yet been killed. Usually this method
|
||||
* is called from Activity.onPause.
|
||||
*/
|
||||
public void onPause() {
|
||||
mIdentityPopup.unregisterListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* To display Url in CustomView only and immediately.
|
||||
*
|
||||
|
|
|
@ -231,12 +231,14 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
mLayerView.getDynamicToolbarAnimator().setPinned(true, PinReason.CUSTOM_TAB);
|
||||
actionBarPresenter.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mLayerView.getDynamicToolbarAnimator().setPinned(false, PinReason.CUSTOM_TAB);
|
||||
actionBarPresenter.onPause();
|
||||
}
|
||||
|
||||
// Usually should use onCreateOptionsMenu() to initialize menu items. But GeckoApp overwrite
|
||||
|
|
|
@ -97,7 +97,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements BundleEventListe
|
|||
mContentButtonClickListener = new ContentNotificationButtonListener();
|
||||
}
|
||||
|
||||
void registerListeners() {
|
||||
public void registerListeners() {
|
||||
EventDispatcher.getInstance().registerUiThreadListener(this,
|
||||
"Doorhanger:Logins",
|
||||
"Permissions:CheckResult");
|
||||
|
@ -540,7 +540,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements BundleEventListe
|
|||
void destroy() {
|
||||
}
|
||||
|
||||
void unregisterListeners() {
|
||||
public void unregisterListeners() {
|
||||
EventDispatcher.getInstance().unregisterUiThreadListener(this,
|
||||
"Doorhanger:Logins",
|
||||
"Permissions:CheckResult");
|
||||
|
|
|
@ -5072,12 +5072,6 @@ pref("dom.idle-observers-api.fuzz_time.disabled", true);
|
|||
// a restart is required to enable a new value.
|
||||
pref("network.activity.blipIntervalMilliseconds", 0);
|
||||
|
||||
// If true, reuse the same global for everything loaded by the component loader
|
||||
// (JS components, JSMs, etc). This saves memory, but makes it possible for
|
||||
// the scripts to interfere with each other. A restart is required for this
|
||||
// to take effect.
|
||||
pref("jsloader.reuseGlobal", false);
|
||||
|
||||
// When we're asked to take a screenshot, don't wait more than 2000ms for the
|
||||
// event loop to become idle before actually taking the screenshot.
|
||||
pref("dom.browserElement.maxScreenshotDelayMS", 2000);
|
||||
|
|
|
@ -998,7 +998,7 @@ dependencies = [
|
|||
"fontsan 0.3.2 (git+https://github.com/servo/fontsan)",
|
||||
"freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gfx_traits 0.0.1",
|
||||
"harfbuzz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"harfbuzz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipc-channel 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1124,10 +1124,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "harfbuzz-sys"
|
||||
version = "0.1.7"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -2554,12 +2555,12 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-fontconfig-sys 4.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo-fontconfig-sys"
|
||||
version = "4.0.2"
|
||||
version = "4.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"expat-sys 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2616,7 +2617,7 @@ dependencies = [
|
|||
"io-surface 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-fontconfig-sys 4.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-glutin 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"x11 2.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -3160,7 +3161,7 @@ name = "unicode-script"
|
|||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"harfbuzz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"harfbuzz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3538,7 +3539,7 @@ dependencies = [
|
|||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum glx 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b280007fa9c7442cfd1e0b1addb8d1a59240267110e8705f8f7e2c7bfb7e2f72"
|
||||
"checksum half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63d68db75012a85555434ee079e7e6337931f87a087ab2988becbadf64673a7f"
|
||||
"checksum harfbuzz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6b76113246f5c089dcf272cf89c3f61168a4d77b50ec5b2c1fab8c628c9ea762"
|
||||
"checksum harfbuzz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3072efe30deebdda55fcbe7c74c6d42f546fd8533488e43a692ea940ecface11"
|
||||
"checksum heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "556cd479866cf85c3f671209c85e8a6990211c916d1002c2fcb2e9b7cf60bc36"
|
||||
"checksum heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "46f96d52fb1564059fc97b85ef6165728cc30198ab60073bf114c66c4c89bb5d"
|
||||
"checksum heartbeats-simple 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ad003ce233955e9d95f2c69cde84e68302ba9ba4a673d351c9bff93c738aadc"
|
||||
|
@ -3646,7 +3647,7 @@ dependencies = [
|
|||
"checksum serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ad8bcf487be7d2e15d3d543f04312de991d631cfe1b43ea0ade69e6a8a5b16a1"
|
||||
"checksum servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21069a884c33fe6ee596975e1f3849ed88c4ec857fbaf11d33672d8ebe051217"
|
||||
"checksum servo-fontconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93f799b649b4a2bf362398910eca35240704c7e765e780349b2bb1070d892262"
|
||||
"checksum servo-fontconfig-sys 4.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0af4a4d7746467921486e5c5420f815cc016a6bf5574210d8e9c00f4afae224"
|
||||
"checksum servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6be80777ee6edecbbbf8774c76e19dddfe336256c57a4ded06d6ad3df7be358e"
|
||||
"checksum servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9232032c2e85118c0282c6562c84cab12316e655491ba0a5d1905b2320060d1b"
|
||||
"checksum servo-glutin 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8398095f9b3dc3c6d706d395e192624be1f1bcc6f366b009fe17a20cb5dd3d72"
|
||||
"checksum servo-skia 0.30000004.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22ba980da523e91b9d2f7da9fb35f721138a1e604b8d8191e56f403e4760a9e4"
|
||||
|
|
|
@ -10,7 +10,6 @@ use app_units::{Au, MAX_AU, MIN_AU};
|
|||
use euclid::point::Point2D;
|
||||
use euclid::rect::Rect;
|
||||
use euclid::size::Size2D;
|
||||
use std::i32;
|
||||
|
||||
// Units for use with euclid::length and euclid::scale_factor.
|
||||
|
||||
|
|
|
@ -17,12 +17,17 @@ use servo_config::opts;
|
|||
use std::mem;
|
||||
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||
use style::dom::UnsafeNode;
|
||||
use style::parallel::CHUNK_SIZE;
|
||||
use traversal::{AssignISizes, BubbleISizes};
|
||||
use traversal::AssignBSizes;
|
||||
|
||||
pub use style::parallel::traverse_dom;
|
||||
|
||||
/// Traversal chunk size.
|
||||
///
|
||||
/// FIXME(bholley): This is all likely very inefficient and should probably be
|
||||
/// reworked to mirror the style system's parallel.rs.
|
||||
pub const CHUNK_SIZE: usize = 64;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn static_assertion(node: UnsafeNode) {
|
||||
unsafe {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
use dom::TNode;
|
||||
use properties::{DeclaredValue, PropertyDeclaration};
|
||||
use values::HasViewportPercentage;
|
||||
use style_traits::HasViewportPercentage;
|
||||
|
||||
/// A structure to collect information about the cascade.
|
||||
///
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::ascii::AsciiExt;
|
|||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use stylearc::Arc;
|
||||
|
||||
/// A custom property name is just an `Atom`.
|
||||
|
@ -49,7 +49,7 @@ pub struct SpecifiedValue {
|
|||
references: HashSet<Name>,
|
||||
}
|
||||
|
||||
impl ::values::HasViewportPercentage for SpecifiedValue {
|
||||
impl HasViewportPercentage for SpecifiedValue {
|
||||
fn has_viewport_percentage(&self) -> bool {
|
||||
panic!("has_viewport_percentage called before resolving!");
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -366,6 +366,7 @@ trait PrivateMatchMethods: TElement {
|
|||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn needs_animations_update(&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
old_values: Option<&Arc<ComputedValues>>,
|
||||
new_values: &ComputedValues)
|
||||
-> bool {
|
||||
|
@ -378,7 +379,10 @@ trait PrivateMatchMethods: TElement {
|
|||
let old_box_style = old.get_box();
|
||||
let old_display_style = old_box_style.clone_display();
|
||||
let new_display_style = new_box_style.clone_display();
|
||||
// FIXME: Bug 1344581: We still need to compare keyframe rules.
|
||||
|
||||
// If the traverse is triggered by CSS rule changes,
|
||||
// we need to try to update all CSS animations.
|
||||
context.shared.traversal_flags.for_css_rule_changes() ||
|
||||
!old_box_style.animations_equals(&new_box_style) ||
|
||||
(old_display_style == display::T::none &&
|
||||
new_display_style != display::T::none &&
|
||||
|
@ -400,7 +404,7 @@ trait PrivateMatchMethods: TElement {
|
|||
use context::UpdateAnimationsTasks;
|
||||
|
||||
let mut tasks = UpdateAnimationsTasks::empty();
|
||||
if self.needs_animations_update(old_values.as_ref(), new_values) {
|
||||
if self.needs_animations_update(context, old_values.as_ref(), new_values) {
|
||||
tasks.insert(CSS_ANIMATIONS);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,16 +26,38 @@ use context::TraversalStatistics;
|
|||
use dom::{OpaqueNode, SendNode, TElement, TNode};
|
||||
use rayon;
|
||||
use scoped_tls::ScopedTLS;
|
||||
use sharing::STYLE_SHARING_CANDIDATE_CACHE_SIZE;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Borrow;
|
||||
use std::mem;
|
||||
use time;
|
||||
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
|
||||
|
||||
/// The chunk size used to split the parallel traversal nodes.
|
||||
/// The maximum number of child nodes that we will process as a single unit.
|
||||
///
|
||||
/// We send each `CHUNK_SIZE` nodes as a different work unit to the work queue.
|
||||
pub const CHUNK_SIZE: usize = 64;
|
||||
/// Larger values will increase style sharing cache hits and general DOM locality
|
||||
/// at the expense of decreased opportunities for parallelism. The style sharing
|
||||
/// cache can hold 8 entries, but not all styles are shareable, so we set this
|
||||
/// value to 16. These values have not been measured and could potentially be
|
||||
/// tuned.
|
||||
pub const WORK_UNIT_MAX: usize = 16;
|
||||
|
||||
/// A parallel top down traversal, generic over `D`.
|
||||
/// Verify that the style sharing cache size doesn't change. If it does, we should
|
||||
/// reconsider the above. We do this, rather than defining WORK_UNIT_MAX in terms
|
||||
/// of STYLE_SHARING_CANDIDATE_CACHE_SIZE, so that altering the latter doesn't
|
||||
/// have surprising effects on the parallelism characteristics of the style system.
|
||||
#[allow(dead_code)]
|
||||
fn static_assert() {
|
||||
unsafe { mem::transmute::<_, [u32; STYLE_SHARING_CANDIDATE_CACHE_SIZE]>([1; 8]); }
|
||||
}
|
||||
|
||||
/// A list of node pointers.
|
||||
///
|
||||
/// Note that the inline storage doesn't need to be sized to WORK_UNIT_MAX, but
|
||||
/// it generally seems sensible to do so.
|
||||
type NodeList<N> = SmallVec<[SendNode<N>; WORK_UNIT_MAX]>;
|
||||
|
||||
/// Entry point for the parallel traversal.
|
||||
#[allow(unsafe_code)]
|
||||
pub fn traverse_dom<E, D>(traversal: &D,
|
||||
root: E,
|
||||
|
@ -46,24 +68,29 @@ pub fn traverse_dom<E, D>(traversal: &D,
|
|||
{
|
||||
let dump_stats = traversal.shared_context().options.dump_style_statistics;
|
||||
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
|
||||
let mut nodes = NodeList::<E::ConcreteNode>::new();
|
||||
|
||||
debug_assert!(traversal.is_parallel());
|
||||
// Handle Gecko's eager initial styling. We don't currently support it
|
||||
// in conjunction with bottom-up traversal. If we did, we'd need to put
|
||||
// it on the context to make it available to the bottom-up phase.
|
||||
let (nodes, depth) = if token.traverse_unstyled_children_only() {
|
||||
let depth = if token.traverse_unstyled_children_only() {
|
||||
debug_assert!(!D::needs_postorder_traversal());
|
||||
let mut children = vec![];
|
||||
for kid in root.as_node().children() {
|
||||
if kid.as_element().map_or(false, |el| el.get_data().is_none()) {
|
||||
children.push(unsafe { SendNode::new(kid) });
|
||||
nodes.push(unsafe { SendNode::new(kid) });
|
||||
}
|
||||
}
|
||||
(children, root.depth() + 1)
|
||||
root.depth() + 1
|
||||
} else {
|
||||
(vec![unsafe { SendNode::new(root.as_node()) }], root.depth())
|
||||
nodes.push(unsafe { SendNode::new(root.as_node()) });
|
||||
root.depth()
|
||||
};
|
||||
|
||||
if nodes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let traversal_data = PerLevelTraversalData {
|
||||
current_dom_depth: depth,
|
||||
};
|
||||
|
@ -72,7 +99,13 @@ pub fn traverse_dom<E, D>(traversal: &D,
|
|||
|
||||
queue.install(|| {
|
||||
rayon::scope(|scope| {
|
||||
traverse_nodes(nodes, root, traversal_data, scope, traversal, &tls);
|
||||
traverse_nodes(nodes,
|
||||
DispatchMode::TailCall,
|
||||
root,
|
||||
traversal_data,
|
||||
scope,
|
||||
traversal,
|
||||
&tls);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -93,6 +126,17 @@ pub fn traverse_dom<E, D>(traversal: &D,
|
|||
}
|
||||
|
||||
/// A parallel top-down DOM traversal.
|
||||
///
|
||||
/// This algorithm traverses the DOM in a breadth-first, top-down manner. The
|
||||
/// goals are:
|
||||
/// * Never process a child before its parent (since child style depends on
|
||||
/// parent style). If this were to happen, the styling algorithm would panic.
|
||||
/// * Prioritize discovering nodes as quickly as possible to maximize
|
||||
/// opportunities for parallelism.
|
||||
/// * Style all the children of a given node (i.e. all sibling nodes) on
|
||||
/// a single thread (with an upper bound to handle nodes with an
|
||||
/// abnormally large number of children). This is important because we use
|
||||
/// a thread-local cache to share styles between siblings.
|
||||
#[inline(always)]
|
||||
#[allow(unsafe_code)]
|
||||
fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
||||
|
@ -104,17 +148,42 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
|||
where E: TElement + 'scope,
|
||||
D: DomTraversal<E>,
|
||||
{
|
||||
let mut discovered_child_nodes = vec![];
|
||||
debug_assert!(nodes.len() <= WORK_UNIT_MAX);
|
||||
let mut discovered_child_nodes = NodeList::<E::ConcreteNode>::new();
|
||||
{
|
||||
// Scope the borrow of the TLS so that the borrow is dropped before
|
||||
// potentially traversing a child on this thread.
|
||||
// a potential recursive call when we pass TailCall.
|
||||
let mut tlc = tls.ensure(|| traversal.create_thread_local_context());
|
||||
|
||||
for n in nodes {
|
||||
// Perform the appropriate traversal.
|
||||
// If the last node we processed produced children, spawn them off
|
||||
// into a work item. We do this at the beginning of the loop (rather
|
||||
// than at the end) so that we can traverse the children of the last
|
||||
// sibling directly on this thread without a spawn call.
|
||||
//
|
||||
// This has the important effect of removing the allocation and
|
||||
// context-switching overhead of the parallel traversal for perfectly
|
||||
// linear regions of the DOM, i.e.:
|
||||
//
|
||||
// <russian><doll><tag><nesting></nesting></tag></doll></russian>
|
||||
//
|
||||
// Which are not at all uncommon.
|
||||
if !discovered_child_nodes.is_empty() {
|
||||
let children = mem::replace(&mut discovered_child_nodes, Default::default());
|
||||
let mut traversal_data_copy = traversal_data.clone();
|
||||
traversal_data_copy.current_dom_depth += 1;
|
||||
traverse_nodes(children,
|
||||
DispatchMode::NotTailCall,
|
||||
root,
|
||||
traversal_data_copy,
|
||||
scope,
|
||||
traversal,
|
||||
tls);
|
||||
}
|
||||
|
||||
let node = **n;
|
||||
let mut children_to_process = 0isize;
|
||||
traversal.process_preorder(&mut traversal_data, &mut *tlc, node);
|
||||
traversal.process_preorder(&traversal_data, &mut *tlc, node);
|
||||
if let Some(el) = node.as_element() {
|
||||
traversal.traverse_children(&mut *tlc, el, |_tlc, kid| {
|
||||
children_to_process += 1;
|
||||
|
@ -127,11 +196,37 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
|||
}
|
||||
}
|
||||
|
||||
// Handle the children of the last element in this work unit. If any exist,
|
||||
// we can process them (or at least one work unit's worth of them) directly
|
||||
// on this thread by passing TailCall.
|
||||
if !discovered_child_nodes.is_empty() {
|
||||
traversal_data.current_dom_depth += 1;
|
||||
traverse_nodes(discovered_child_nodes, root, traversal_data, scope, traversal, tls);
|
||||
traverse_nodes(discovered_child_nodes,
|
||||
DispatchMode::TailCall,
|
||||
root,
|
||||
traversal_data,
|
||||
scope,
|
||||
traversal,
|
||||
tls);
|
||||
}
|
||||
}
|
||||
|
||||
fn traverse_nodes<'a, 'scope, E, D>(nodes: Vec<SendNode<E::ConcreteNode>>, root: OpaqueNode,
|
||||
/// Controls whether traverse_nodes may make a recursive call to continue
|
||||
/// doing work, or whether it should always dispatch work asynchronously.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum DispatchMode {
|
||||
TailCall,
|
||||
NotTailCall,
|
||||
}
|
||||
|
||||
impl DispatchMode {
|
||||
fn is_tail_call(&self) -> bool { matches!(*self, DispatchMode::TailCall) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn traverse_nodes<'a, 'scope, E, D>(nodes: NodeList<E::ConcreteNode>,
|
||||
mode: DispatchMode,
|
||||
root: OpaqueNode,
|
||||
traversal_data: PerLevelTraversalData,
|
||||
scope: &'a rayon::Scope<'scope>,
|
||||
traversal: &'scope D,
|
||||
|
@ -139,25 +234,44 @@ fn traverse_nodes<'a, 'scope, E, D>(nodes: Vec<SendNode<E::ConcreteNode>>, root:
|
|||
where E: TElement + 'scope,
|
||||
D: DomTraversal<E>,
|
||||
{
|
||||
if nodes.is_empty() {
|
||||
return;
|
||||
}
|
||||
debug_assert!(!nodes.is_empty());
|
||||
|
||||
// Optimization: traverse directly and avoid a heap-allocating spawn() call if
|
||||
// we're only pushing one work unit.
|
||||
if nodes.len() <= CHUNK_SIZE {
|
||||
let nodes = nodes.into_boxed_slice();
|
||||
// In the common case, our children fit within a single work unit, in which
|
||||
// case we can pass the SmallVec directly and avoid extra allocation.
|
||||
if nodes.len() <= WORK_UNIT_MAX {
|
||||
if mode.is_tail_call() {
|
||||
// If this is a tail call, bypass rayon and invoke top_down_dom directly.
|
||||
top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
|
||||
return;
|
||||
}
|
||||
|
||||
// General case.
|
||||
for chunk in nodes.chunks(CHUNK_SIZE) {
|
||||
let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
||||
let traversal_data = traversal_data.clone();
|
||||
} else {
|
||||
// The caller isn't done yet. Append to the queue and return synchronously.
|
||||
scope.spawn(move |scope| {
|
||||
let nodes = nodes;
|
||||
top_down_dom(&nodes, root, traversal_data, scope, traversal, tls)
|
||||
})
|
||||
top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// FIXME(bholley): This should be an ArrayVec.
|
||||
let mut first_chunk: Option<NodeList<E::ConcreteNode>> = None;
|
||||
for chunk in nodes.chunks(WORK_UNIT_MAX) {
|
||||
if mode.is_tail_call() && first_chunk.is_none() {
|
||||
first_chunk = Some(chunk.iter().cloned().collect::<NodeList<E::ConcreteNode>>());
|
||||
} else {
|
||||
let boxed = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
||||
let traversal_data_copy = traversal_data.clone();
|
||||
scope.spawn(move |scope| {
|
||||
let b = boxed;
|
||||
top_down_dom(&*b, root, traversal_data_copy, scope, traversal, tls)
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a tail call, bypass rayon for the first chunk and invoke top_down_dom
|
||||
// directly.
|
||||
debug_assert_eq!(first_chunk.is_some(), mode.is_tail_call());
|
||||
if let Some(c) = first_chunk {
|
||||
debug_assert_eq!(c.len(), WORK_UNIT_MAX);
|
||||
top_down_dom(&*c, root, traversal_data, scope, traversal, tls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2769,19 +2769,18 @@ fn static_assert() {
|
|||
pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
|
||||
use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
|
||||
|
||||
let count = other.gecko.${image_layers_field}.${field_name}Count;
|
||||
unsafe {
|
||||
Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field},
|
||||
other.gecko.${image_layers_field}.mLayers.len(),
|
||||
count as usize,
|
||||
LayerType::${shorthand.title()});
|
||||
}
|
||||
for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
|
||||
.zip(other.gecko.${image_layers_field}.mLayers.iter())
|
||||
.take(other.gecko.${image_layers_field}
|
||||
.${field_name}Count as usize) {
|
||||
.take(count as usize) {
|
||||
layer.${field_name} = other.${field_name};
|
||||
}
|
||||
self.gecko.${image_layers_field}.${field_name}Count =
|
||||
other.gecko.${image_layers_field}.${field_name}Count;
|
||||
self.gecko.${image_layers_field}.${field_name}Count = count;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2874,23 +2873,21 @@ fn static_assert() {
|
|||
pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {
|
||||
use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
|
||||
|
||||
self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count
|
||||
= cmp::min(1, other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count);
|
||||
self.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition =
|
||||
other.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition;
|
||||
let count = other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count;
|
||||
|
||||
unsafe {
|
||||
Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field},
|
||||
other.gecko.${image_layers_field}.mLayers.len(),
|
||||
count as usize,
|
||||
LayerType::${shorthand.capitalize()});
|
||||
}
|
||||
|
||||
for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
|
||||
.zip(other.gecko.${image_layers_field}.mLayers.iter()) {
|
||||
.zip(other.gecko.${image_layers_field}.mLayers.iter())
|
||||
.take(count as usize) {
|
||||
layer.mPosition.m${orientation.upper()}Position
|
||||
= other.mPosition.m${orientation.upper()}Position;
|
||||
}
|
||||
self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count
|
||||
= other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count;
|
||||
self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count = count;
|
||||
}
|
||||
|
||||
pub fn clone_${shorthand}_position_${orientation}(&self)
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
#[allow(unused_imports)]
|
||||
use values::HasViewportPercentage;
|
||||
use style_traits::HasViewportPercentage;
|
||||
use style_traits::ToCss;
|
||||
|
||||
pub mod single_value {
|
||||
|
|
|
@ -87,8 +87,8 @@ ${helpers.predefined_type("clip",
|
|||
spec="https://drafts.fxtf.org/filters/#propdef-filter">
|
||||
//pub use self::computed_value::T as SpecifiedValue;
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::{CSSFloat, HasViewportPercentage};
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::CSSFloat;
|
||||
use values::specified::{Angle, Length};
|
||||
#[cfg(feature = "gecko")]
|
||||
use values::specified::Shadow;
|
||||
|
|
|
@ -550,8 +550,8 @@ ${helpers.single_keyword_system("font-variant-caps",
|
|||
use properties::longhands::system_font::SystemFont;
|
||||
use properties::style_structs::Font;
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::{FONT_MEDIUM_PX, HasViewportPercentage};
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::FONT_MEDIUM_PX;
|
||||
use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage};
|
||||
use values::specified::{NoCalcLength, Percentage};
|
||||
use values::specified::length::FontBaseSize;
|
||||
|
|
|
@ -34,10 +34,10 @@ use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext};
|
|||
use properties::animated_properties::TransitionProperty;
|
||||
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
|
||||
use shared_lock::StylesheetGuards;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use stylesheets::{CssRuleType, Origin, UrlExtraData};
|
||||
#[cfg(feature = "servo")] use values::Either;
|
||||
use values::{HasViewportPercentage, computed};
|
||||
use values::computed;
|
||||
use cascade_info::CascadeInfo;
|
||||
use rule_tree::StrongRuleNode;
|
||||
use style_adjuster::StyleAdjuster;
|
||||
|
|
|
@ -37,6 +37,10 @@ bitflags! {
|
|||
const ANIMATION_ONLY = 0x02,
|
||||
/// Traverse without generating any change hints.
|
||||
const FOR_RECONSTRUCT = 0x04,
|
||||
/// Traverse triggered by CSS rule changes.
|
||||
/// Traverse and update all elements with CSS animations since
|
||||
/// @keyframes rules may have changed
|
||||
const FOR_CSS_RULE_CHANGES = 0x08,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +59,11 @@ impl TraversalFlags {
|
|||
pub fn for_reconstruct(&self) -> bool {
|
||||
self.contains(FOR_RECONSTRUCT)
|
||||
}
|
||||
|
||||
/// Returns true if the traversal is triggered by CSS rule changes.
|
||||
pub fn for_css_rule_changes(&self) -> bool {
|
||||
self.contains(FOR_CSS_RULE_CHANGES)
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure exists to enforce that callers invoke pre_traverse, and also
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
//! Computed values.
|
||||
|
||||
use Atom;
|
||||
use context::QuirksMode;
|
||||
use euclid::size::Size2D;
|
||||
use font_metrics::FontMetricsProvider;
|
||||
|
@ -154,6 +155,79 @@ pub trait ToComputedValue {
|
|||
fn from_computed_value(computed: &Self::ComputedValue) -> Self;
|
||||
}
|
||||
|
||||
impl<A, B> ToComputedValue for (A, B)
|
||||
where A: ToComputedValue, B: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = (
|
||||
<A as ToComputedValue>::ComputedValue,
|
||||
<B as ToComputedValue>::ComputedValue,
|
||||
);
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
(self.0.to_computed_value(context), self.1.to_computed_value(context))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
(A::from_computed_value(&computed.0), B::from_computed_value(&computed.1))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToComputedValue for Option<T>
|
||||
where T: ToComputedValue
|
||||
{
|
||||
type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
self.as_ref().map(|item| item.to_computed_value(context))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
computed.as_ref().map(T::from_computed_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToComputedValue for Size2D<T>
|
||||
where T: ToComputedValue
|
||||
{
|
||||
type ComputedValue = Size2D<<T as ToComputedValue>::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
Size2D::new(
|
||||
self.width.to_computed_value(context),
|
||||
self.height.to_computed_value(context),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
Size2D::new(
|
||||
T::from_computed_value(&computed.width),
|
||||
T::from_computed_value(&computed.height),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToComputedValue for Vec<T>
|
||||
where T: ToComputedValue
|
||||
{
|
||||
type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
self.iter().map(|item| item.to_computed_value(context)).collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
computed.iter().map(T::from_computed_value).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A marker trait to represent that the specified value is also the computed
|
||||
/// value.
|
||||
pub trait ComputedValueAsSpecified {}
|
||||
|
@ -174,6 +248,9 @@ impl<T> ToComputedValue for T
|
|||
}
|
||||
}
|
||||
|
||||
impl ComputedValueAsSpecified for Atom {}
|
||||
impl ComputedValueAsSpecified for bool {}
|
||||
|
||||
/// A computed `<angle>` value.
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
|
||||
|
|
|
@ -11,16 +11,15 @@ use parser::{Parse, ParserContext};
|
|||
use properties::shorthands::serialize_four_sides;
|
||||
use std::ascii::AsciiExt;
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::HasViewportPercentage;
|
||||
use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
use values::generics::BorderRadiusSize;
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// A generic type used for `border-radius`, `outline-radius` and `inset()` values.
|
||||
///
|
||||
/// https://drafts.csswg.org/css-backgrounds-3/#border-radius
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct BorderRadius<L> {
|
||||
/// The top left radius.
|
||||
|
@ -60,32 +59,8 @@ impl<L: ToCss + PartialEq> ToCss for BorderRadius<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: ToComputedValue> ToComputedValue for BorderRadius<L> {
|
||||
type ComputedValue = BorderRadius<L::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
BorderRadius {
|
||||
top_left: self.top_left.to_computed_value(cx),
|
||||
top_right: self.top_right.to_computed_value(cx),
|
||||
bottom_right: self.bottom_right.to_computed_value(cx),
|
||||
bottom_left: self.bottom_left.to_computed_value(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
BorderRadius {
|
||||
top_left: ToComputedValue::from_computed_value(&computed.top_left),
|
||||
top_right: ToComputedValue::from_computed_value(&computed.top_right),
|
||||
bottom_right: ToComputedValue::from_computed_value(&computed.bottom_right),
|
||||
bottom_left: ToComputedValue::from_computed_value(&computed.bottom_left),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-shapes/#typedef-shape-radius
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ShapeRadius<L> {
|
||||
|
@ -110,28 +85,6 @@ impl<L: ToCss> ToCss for ShapeRadius<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: ToComputedValue> ToComputedValue for ShapeRadius<L> {
|
||||
type ComputedValue = ShapeRadius<L::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
ShapeRadius::Length(ref lop) => ShapeRadius::Length(lop.to_computed_value(cx)),
|
||||
ShapeRadius::ClosestSide => ShapeRadius::ClosestSide,
|
||||
ShapeRadius::FarthestSide => ShapeRadius::FarthestSide,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
ShapeRadius::Length(ref lop) => ShapeRadius::Length(ToComputedValue::from_computed_value(lop)),
|
||||
ShapeRadius::ClosestSide => ShapeRadius::ClosestSide,
|
||||
ShapeRadius::FarthestSide => ShapeRadius::FarthestSide,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-shapes/#typedef-fill-rule
|
||||
// NOTE: Basic shapes spec says that these are the only two values, however
|
||||
// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
||||
|
@ -148,7 +101,7 @@ impl Default for FillRule {
|
|||
fn default() -> Self { FillRule::NonZero }
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// A generic type for representing the `polygon()` function
|
||||
///
|
||||
|
@ -212,32 +165,7 @@ impl<L: ToCss> ToCss for Polygon<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: ToComputedValue> ToComputedValue for Polygon<L> {
|
||||
type ComputedValue = Polygon<L::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
Polygon {
|
||||
fill: self.fill.to_computed_value(cx),
|
||||
coordinates: self.coordinates.iter().map(|c| {
|
||||
(c.0.to_computed_value(cx), c.1.to_computed_value(cx))
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
Polygon {
|
||||
fill: ToComputedValue::from_computed_value(&computed.fill),
|
||||
coordinates: computed.coordinates.iter().map(|c| {
|
||||
(ToComputedValue::from_computed_value(&c.0),
|
||||
ToComputedValue::from_computed_value(&c.1))
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// https://drafts.csswg.org/css-shapes/#funcdef-inset
|
||||
#[allow(missing_docs)]
|
||||
|
@ -269,37 +197,11 @@ impl<L: ToCss + PartialEq> ToCss for InsetRect<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: ToComputedValue> ToComputedValue for InsetRect<L> {
|
||||
type ComputedValue = InsetRect<L::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
InsetRect {
|
||||
top: self.top.to_computed_value(cx),
|
||||
right: self.right.to_computed_value(cx),
|
||||
bottom: self.bottom.to_computed_value(cx),
|
||||
left: self.left.to_computed_value(cx),
|
||||
round: self.round.as_ref().map(|r| r.to_computed_value(cx)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
InsetRect {
|
||||
top: ToComputedValue::from_computed_value(&computed.top),
|
||||
right: ToComputedValue::from_computed_value(&computed.right),
|
||||
bottom: ToComputedValue::from_computed_value(&computed.bottom),
|
||||
left: ToComputedValue::from_computed_value(&computed.left),
|
||||
round: computed.round.as_ref().map(|r| ToComputedValue::from_computed_value(r)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A shape source, for some reference box
|
||||
///
|
||||
/// `clip-path` uses ShapeSource<BasicShape, GeometryBox>,
|
||||
/// `shape-outside` uses ShapeSource<BasicShape, ShapeBox>
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ShapeSource<B, T> {
|
||||
|
@ -365,33 +267,3 @@ impl<B: Parse, T: Parse> Parse for ShapeSource<B, T> {
|
|||
ref_box.map(|v| ShapeSource::Box(v)).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: ToComputedValue, T: ToComputedValue> ToComputedValue for ShapeSource<B, T> {
|
||||
type ComputedValue = ShapeSource<B::ComputedValue, T::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
ShapeSource::Url(ref url) => ShapeSource::Url(url.to_computed_value(cx)),
|
||||
ShapeSource::Shape(ref shape, ref ref_box) => {
|
||||
ShapeSource::Shape(shape.to_computed_value(cx),
|
||||
ref_box.as_ref().map(|ref val| val.to_computed_value(cx)))
|
||||
},
|
||||
ShapeSource::Box(ref ref_box) => ShapeSource::Box(ref_box.to_computed_value(cx)),
|
||||
ShapeSource::None => ShapeSource::None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
ShapeSource::Url(ref url) => ShapeSource::Url(SpecifiedUrl::from_computed_value(url)),
|
||||
ShapeSource::Shape(ref shape, ref ref_box) => {
|
||||
ShapeSource::Shape(ToComputedValue::from_computed_value(shape),
|
||||
ref_box.as_ref().map(|val| ToComputedValue::from_computed_value(val)))
|
||||
},
|
||||
ShapeSource::Box(ref ref_box) => ShapeSource::Box(ToComputedValue::from_computed_value(ref_box)),
|
||||
ShapeSource::None => ShapeSource::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,14 @@
|
|||
use Atom;
|
||||
use cssparser::serialize_identifier;
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::HasViewportPercentage;
|
||||
use values::computed::{Context, ToComputedValue};
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// An [image].
|
||||
///
|
||||
/// [image]: https://drafts.csswg.org/css-images/#image-values
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Clone, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum Image<Gradient, ImageRect> {
|
||||
/// A `<url()>` image.
|
||||
|
@ -32,7 +31,7 @@ pub enum Image<Gradient, ImageRect> {
|
|||
|
||||
/// A CSS gradient.
|
||||
/// https://drafts.csswg.org/css-images/#gradients
|
||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color> {
|
||||
/// Gradients can be linear or radial.
|
||||
|
@ -45,7 +44,7 @@ pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color>
|
|||
pub compat_mode: CompatMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// Whether we used the modern notation or the compatibility `-webkit` prefix.
|
||||
pub enum CompatMode {
|
||||
|
@ -56,7 +55,7 @@ pub enum CompatMode {
|
|||
}
|
||||
|
||||
/// A gradient kind.
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position> {
|
||||
/// A linear gradient.
|
||||
|
@ -66,7 +65,7 @@ pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position> {
|
|||
}
|
||||
|
||||
/// A radial gradient's ending shape.
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum EndingShape<Length, LengthOrPercentage> {
|
||||
/// A circular gradient.
|
||||
|
@ -76,7 +75,7 @@ pub enum EndingShape<Length, LengthOrPercentage> {
|
|||
}
|
||||
|
||||
/// A circle shape.
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum Circle<Length> {
|
||||
/// A circle radius.
|
||||
|
@ -86,7 +85,7 @@ pub enum Circle<Length> {
|
|||
}
|
||||
|
||||
/// An ellipse shape.
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum Ellipse<LengthOrPercentage> {
|
||||
/// An ellipse pair of radii.
|
||||
|
@ -105,10 +104,11 @@ define_css_keyword_enum!(ShapeExtent:
|
|||
"cover" => Cover
|
||||
);
|
||||
no_viewport_percentage!(ShapeExtent);
|
||||
impl ComputedValueAsSpecified for ShapeExtent {}
|
||||
|
||||
/// A gradient item.
|
||||
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum GradientItem<Color, LengthOrPercentage> {
|
||||
/// A color stop.
|
||||
|
@ -119,7 +119,7 @@ pub enum GradientItem<Color, LengthOrPercentage> {
|
|||
|
||||
/// A color stop.
|
||||
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
|
||||
#[derive(Clone, Copy, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct ColorStop<Color, LengthOrPercentage> {
|
||||
/// The color of this stop.
|
||||
|
@ -131,7 +131,7 @@ pub struct ColorStop<Color, LengthOrPercentage> {
|
|||
/// Values for `moz-image-rect`.
|
||||
///
|
||||
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ImageRect<NumberOrPercentage> {
|
||||
|
@ -187,47 +187,6 @@ impl<G, R> HasViewportPercentage for Image<G, R>
|
|||
}
|
||||
}
|
||||
|
||||
impl<G, R> ToComputedValue for Image<G, R>
|
||||
where G: ToComputedValue, R: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = Image<<G as ToComputedValue>::ComputedValue,
|
||||
<R as ToComputedValue>::ComputedValue>;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
Image::Url(ref url) => {
|
||||
Image::Url(url.clone())
|
||||
},
|
||||
Image::Gradient(ref gradient) => {
|
||||
Image::Gradient(gradient.to_computed_value(context))
|
||||
},
|
||||
Image::Rect(ref rect) => {
|
||||
Image::Rect(rect.to_computed_value(context))
|
||||
},
|
||||
Image::Element(ref selector) => {
|
||||
Image::Element(selector.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
Image::Url(ref url) => {
|
||||
Image::Url(url.clone())
|
||||
},
|
||||
Image::Gradient(ref gradient) => {
|
||||
Image::Gradient(ToComputedValue::from_computed_value(gradient))
|
||||
},
|
||||
Image::Rect(ref rect) => {
|
||||
Image::Rect(ToComputedValue::from_computed_value(rect))
|
||||
},
|
||||
Image::Element(ref selector) => {
|
||||
Image::Element(selector.clone())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
|
||||
where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss,
|
||||
{
|
||||
|
@ -282,38 +241,6 @@ impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
|
|||
}
|
||||
}
|
||||
|
||||
impl<D, L, LoP, P, C> ToComputedValue for Gradient<D, L, LoP, P, C>
|
||||
where D: ToComputedValue,
|
||||
L: ToComputedValue,
|
||||
LoP: ToComputedValue,
|
||||
P: ToComputedValue,
|
||||
C: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = Gradient<<D as ToComputedValue>::ComputedValue,
|
||||
<L as ToComputedValue>::ComputedValue,
|
||||
<LoP as ToComputedValue>::ComputedValue,
|
||||
<P as ToComputedValue>::ComputedValue,
|
||||
<C as ToComputedValue>::ComputedValue>;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
Gradient {
|
||||
kind: self.kind.to_computed_value(context),
|
||||
items: self.items.iter().map(|s| s.to_computed_value(context)).collect(),
|
||||
repeating: self.repeating,
|
||||
compat_mode: self.compat_mode,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
Gradient {
|
||||
kind: ToComputedValue::from_computed_value(&computed.kind),
|
||||
items: computed.items.iter().map(ToComputedValue::from_computed_value).collect(),
|
||||
repeating: computed.repeating,
|
||||
compat_mode: computed.compat_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, L, LoP, P> GradientKind<D, L, LoP, P> {
|
||||
fn label(&self) -> &str {
|
||||
match *self {
|
||||
|
@ -323,43 +250,6 @@ impl<D, L, LoP, P> GradientKind<D, L, LoP, P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<D, L, LoP, P> ToComputedValue for GradientKind<D, L, LoP, P>
|
||||
where D: ToComputedValue,
|
||||
L: ToComputedValue,
|
||||
LoP: ToComputedValue,
|
||||
P: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = GradientKind<<D as ToComputedValue>::ComputedValue,
|
||||
<L as ToComputedValue>::ComputedValue,
|
||||
<LoP as ToComputedValue>::ComputedValue,
|
||||
<P as ToComputedValue>::ComputedValue>;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
GradientKind::Linear(ref direction) => {
|
||||
GradientKind::Linear(direction.to_computed_value(context))
|
||||
},
|
||||
GradientKind::Radial(ref shape, ref position) => {
|
||||
GradientKind::Radial(shape.to_computed_value(context), position.to_computed_value(context))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
GradientKind::Linear(ref direction) => {
|
||||
GradientKind::Linear(ToComputedValue::from_computed_value(direction))
|
||||
},
|
||||
GradientKind::Radial(ref shape, ref position) => {
|
||||
GradientKind::Radial(
|
||||
ToComputedValue::from_computed_value(shape),
|
||||
ToComputedValue::from_computed_value(position),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The direction of a linear gradient.
|
||||
pub trait LineDirection {
|
||||
/// Whether this direction points towards, and thus can be omitted.
|
||||
|
@ -398,53 +288,6 @@ impl<L, LoP> ToCss for EndingShape<L, LoP>
|
|||
}
|
||||
}
|
||||
|
||||
impl<L, LoP> ToComputedValue for EndingShape<L, LoP>
|
||||
where L: ToComputedValue, LoP: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = EndingShape<<L as ToComputedValue>::ComputedValue,
|
||||
<LoP as ToComputedValue>::ComputedValue>;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
EndingShape::Circle(Circle::Radius(ref length)) => {
|
||||
EndingShape::Circle(Circle::Radius(length.to_computed_value(context)))
|
||||
},
|
||||
EndingShape::Circle(Circle::Extent(extent)) => {
|
||||
EndingShape::Circle(Circle::Extent(extent))
|
||||
},
|
||||
EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
|
||||
EndingShape::Ellipse(Ellipse::Radii(
|
||||
x.to_computed_value(context),
|
||||
y.to_computed_value(context),
|
||||
))
|
||||
},
|
||||
EndingShape::Ellipse(Ellipse::Extent(extent)) => {
|
||||
EndingShape::Ellipse(Ellipse::Extent(extent))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
EndingShape::Circle(Circle::Radius(ref length)) => {
|
||||
EndingShape::Circle(Circle::Radius(ToComputedValue::from_computed_value(length)))
|
||||
},
|
||||
EndingShape::Circle(Circle::Extent(extent)) => {
|
||||
EndingShape::Circle(Circle::Extent(extent))
|
||||
},
|
||||
EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
|
||||
EndingShape::Ellipse(Ellipse::Radii(
|
||||
ToComputedValue::from_computed_value(x),
|
||||
ToComputedValue::from_computed_value(y),
|
||||
))
|
||||
},
|
||||
EndingShape::Ellipse(Ellipse::Extent(extent)) => {
|
||||
EndingShape::Ellipse(Ellipse::Extent(extent))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, L> ToCss for GradientItem<C, L>
|
||||
where C: ToCss, L: ToCss,
|
||||
{
|
||||
|
@ -456,35 +299,6 @@ impl<C, L> ToCss for GradientItem<C, L>
|
|||
}
|
||||
}
|
||||
|
||||
impl<C, L> ToComputedValue for GradientItem<C, L>
|
||||
where C: ToComputedValue, L: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = GradientItem<<C as ToComputedValue>::ComputedValue,
|
||||
<L as ToComputedValue>::ComputedValue>;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
GradientItem::ColorStop(ref stop) => {
|
||||
GradientItem::ColorStop(stop.to_computed_value(context))
|
||||
},
|
||||
GradientItem::InterpolationHint(ref hint) => {
|
||||
GradientItem::InterpolationHint(hint.to_computed_value(context))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
GradientItem::ColorStop(ref stop) => {
|
||||
GradientItem::ColorStop(ToComputedValue::from_computed_value(stop))
|
||||
},
|
||||
GradientItem::InterpolationHint(ref hint) => {
|
||||
GradientItem::InterpolationHint(ToComputedValue::from_computed_value(hint))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, L> fmt::Debug for ColorStop<C, L>
|
||||
where C: fmt::Debug, L: fmt::Debug,
|
||||
{
|
||||
|
@ -510,27 +324,6 @@ impl<C, L> ToCss for ColorStop<C, L>
|
|||
}
|
||||
}
|
||||
|
||||
impl<C, L> ToComputedValue for ColorStop<C, L>
|
||||
where C: ToComputedValue, L: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = ColorStop<<C as ToComputedValue>::ComputedValue,
|
||||
<L as ToComputedValue>::ComputedValue>;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
ColorStop {
|
||||
color: self.color.to_computed_value(context),
|
||||
position: self.position.as_ref().map(|p| p.to_computed_value(context)),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
ColorStop {
|
||||
color: ToComputedValue::from_computed_value(&computed.color),
|
||||
position: computed.position.as_ref().map(ToComputedValue::from_computed_value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> ToCss for ImageRect<C>
|
||||
where C: ToCss,
|
||||
{
|
||||
|
@ -548,29 +341,3 @@ impl<C> ToCss for ImageRect<C>
|
|||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> ToComputedValue for ImageRect<C>
|
||||
where C: ToComputedValue,
|
||||
{
|
||||
type ComputedValue = ImageRect<<C as ToComputedValue>::ComputedValue>;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
ImageRect {
|
||||
url: self.url.to_computed_value(context),
|
||||
top: self.top.to_computed_value(context),
|
||||
right: self.right.to_computed_value(context),
|
||||
bottom: self.bottom.to_computed_value(context),
|
||||
left: self.left.to_computed_value(context),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
ImageRect {
|
||||
url: ToComputedValue::from_computed_value(&computed.url),
|
||||
top: ToComputedValue::from_computed_value(&computed.top),
|
||||
right: ToComputedValue::from_computed_value(&computed.right),
|
||||
bottom: ToComputedValue::from_computed_value(&computed.bottom),
|
||||
left: ToComputedValue::from_computed_value(&computed.left),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,8 @@ use cssparser::Parser;
|
|||
use euclid::size::Size2D;
|
||||
use parser::{Parse, ParserContext};
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use super::CustomIdent;
|
||||
use super::HasViewportPercentage;
|
||||
use super::computed::{Context, ToComputedValue};
|
||||
|
||||
pub use self::basic_shape::serialize_radius_values;
|
||||
|
||||
|
@ -21,9 +19,9 @@ pub mod basic_shape;
|
|||
pub mod image;
|
||||
pub mod position;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// A type for representing CSS `widthh` and `height` values.
|
||||
/// A type for representing CSS `width` and `height` values.
|
||||
pub struct BorderRadiusSize<L>(pub Size2D<L>);
|
||||
|
||||
impl<L> HasViewportPercentage for BorderRadiusSize<L> {
|
||||
|
@ -62,24 +60,6 @@ impl<L: ToCss> ToCss for BorderRadiusSize<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: ToComputedValue> ToComputedValue for BorderRadiusSize<L> {
|
||||
type ComputedValue = BorderRadiusSize<L::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
let w = self.0.width.to_computed_value(context);
|
||||
let h = self.0.height.to_computed_value(context);
|
||||
BorderRadiusSize(Size2D::new(w, h))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
let w = ToComputedValue::from_computed_value(&computed.0.width);
|
||||
let h = ToComputedValue::from_computed_value(&computed.0.height);
|
||||
BorderRadiusSize(Size2D::new(w, h))
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-counter-styles/#typedef-counter-style
|
||||
///
|
||||
/// Since wherever <counter-style> is used, 'none' is a valid value as
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
//! Generic types for CSS handling of specified and computed values of
|
||||
//! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)
|
||||
|
||||
use values::computed::{Context, ToComputedValue};
|
||||
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
/// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
|
||||
pub struct Position<H, V> {
|
||||
|
@ -26,24 +24,3 @@ impl<H, V> Position<H, V> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: ToComputedValue, V: ToComputedValue> ToComputedValue for Position<H, V> {
|
||||
type ComputedValue = Position<<H as ToComputedValue>::ComputedValue,
|
||||
<V as ToComputedValue>::ComputedValue>;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
Position {
|
||||
horizontal: self.horizontal.to_computed_value(context),
|
||||
vertical: self.vertical.to_computed_value(context),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
Self {
|
||||
horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
|
||||
vertical: ToComputedValue::from_computed_value(&computed.vertical),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ use std::borrow::Cow;
|
|||
use std::fmt::{self, Debug};
|
||||
use std::hash;
|
||||
use style_traits::ToCss;
|
||||
pub use style_traits::HasViewportPercentage;
|
||||
|
||||
pub mod computed;
|
||||
pub mod generics;
|
||||
|
|
|
@ -11,9 +11,9 @@ use cssparser::{Parser, Token};
|
|||
use parser::ParserContext;
|
||||
use std::ascii::AsciiExt;
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use style_traits::values::specified::AllowedLengthType;
|
||||
use values::{CSSInteger, CSSFloat, HasViewportPercentage};
|
||||
use values::{CSSInteger, CSSFloat};
|
||||
use values::specified::{Angle, Time};
|
||||
use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength};
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ use cssparser::{Parser, Token, serialize_identifier};
|
|||
use parser::{Parse, ParserContext};
|
||||
use std::{fmt, mem, usize};
|
||||
use std::ascii::AsciiExt;
|
||||
use style_traits::ToCss;
|
||||
use values::{CSSFloat, CustomIdent, Either, HasViewportPercentage};
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::{CSSFloat, CustomIdent, Either};
|
||||
use values::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue};
|
||||
use values::specified::{Integer, LengthOrPercentage};
|
||||
|
||||
|
|
|
@ -14,11 +14,11 @@ use parser::{Parse, ParserContext};
|
|||
use std::{cmp, fmt, mem};
|
||||
use std::ascii::AsciiExt;
|
||||
use std::ops::Mul;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use style_traits::values::specified::{AllowedLengthType, AllowedNumericType};
|
||||
use stylesheets::CssRuleType;
|
||||
use super::{AllowQuirks, Number, ToComputedValue};
|
||||
use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal};
|
||||
use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, None_, Normal};
|
||||
use values::ExtremumLength;
|
||||
use values::computed::{ComputedValueAsSpecified, Context};
|
||||
use values::specified::calc::CalcNode;
|
||||
|
@ -620,7 +620,8 @@ impl Length {
|
|||
Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
|
||||
Length::parse_dimension(context, value.value, unit),
|
||||
Token::Number(ref value) if num_context.is_ok(value.value) => {
|
||||
if value.value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
|
||||
if value.value != 0. &&
|
||||
!context.parsing_mode.allows_unitless_lengths() &&
|
||||
!allow_quirks.allowed(context.quirks_mode) {
|
||||
return Err(())
|
||||
}
|
||||
|
@ -805,9 +806,14 @@ impl LengthOrPercentage {
|
|||
NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentage::Length),
|
||||
Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
|
||||
Ok(LengthOrPercentage::Percentage(Percentage(value.unit_value))),
|
||||
Token::Number(value) if value.value == 0. ||
|
||||
(num_context.is_ok(value.value) && allow_quirks.allowed(context.quirks_mode)) =>
|
||||
Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value.value))),
|
||||
Token::Number(value) if num_context.is_ok(value.value) => {
|
||||
if value.value != 0. &&
|
||||
!context.parsing_mode.allows_unitless_lengths() &&
|
||||
!allow_quirks.allowed(context.quirks_mode) {
|
||||
return Err(())
|
||||
}
|
||||
Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value.value)))
|
||||
}
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
let calc = try!(input.parse_nested_block(|i| {
|
||||
CalcNode::parse_length_or_percentage(context, i, num_context)
|
||||
|
@ -942,7 +948,8 @@ impl LengthOrPercentageOrAuto {
|
|||
Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
|
||||
Ok(LengthOrPercentageOrAuto::Percentage(Percentage(value.unit_value))),
|
||||
Token::Number(ref value) if num_context.is_ok(value.value) => {
|
||||
if value.value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
|
||||
if value.value != 0. &&
|
||||
!context.parsing_mode.allows_unitless_lengths() &&
|
||||
!allow_quirks.allowed(context.quirks_mode) {
|
||||
return Err(())
|
||||
}
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
use cssparser::Parser;
|
||||
use parser::{Parse, ParserContext};
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::HasViewportPercentage;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::computed::{CalcLengthOrPercentage, LengthOrPercentage as ComputedLengthOrPercentage};
|
||||
use values::computed::{Context, ToComputedValue};
|
||||
use values::generics::position::Position as GenericPosition;
|
||||
|
|
|
@ -10,9 +10,16 @@ extern crate synstructure;
|
|||
use proc_macro::TokenStream;
|
||||
|
||||
mod has_viewport_percentage;
|
||||
mod to_computed_value;
|
||||
|
||||
#[proc_macro_derive(HasViewportPercentage)]
|
||||
pub fn derive_has_viewport_percentage(stream: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_derive_input(&stream.to_string()).unwrap();
|
||||
has_viewport_percentage::derive(input).to_string().parse().unwrap()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ToComputedValue)]
|
||||
pub fn derive_to_computed_value(stream: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_derive_input(&stream.to_string()).unwrap();
|
||||
to_computed_value::derive(input).to_string().parse().unwrap()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/* 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/. */
|
||||
|
||||
use quote;
|
||||
use syn;
|
||||
use synstructure;
|
||||
|
||||
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
|
||||
let name = &input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let mut where_clause = where_clause.clone();
|
||||
for param in &input.generics.ty_params {
|
||||
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into()), None));
|
||||
}
|
||||
|
||||
let computed_value_type = syn::Path::from(syn::PathSegment {
|
||||
ident: name.clone(),
|
||||
parameters: syn::PathParameters::AngleBracketed(syn::AngleBracketedParameterData {
|
||||
lifetimes: input.generics.lifetimes.iter().map(|l| {
|
||||
l.lifetime.clone()
|
||||
}).collect(),
|
||||
types: input.generics.ty_params.iter().map(|ty| {
|
||||
syn::Ty::Path(
|
||||
Some(syn::QSelf {
|
||||
ty: Box::new(syn::Ty::Path(None, ty.ident.clone().into())),
|
||||
position: 3,
|
||||
}),
|
||||
syn::Path {
|
||||
global: true,
|
||||
segments: vec![
|
||||
"values".into(),
|
||||
"computed".into(),
|
||||
"ToComputedValue".into(),
|
||||
"ComputedValue".into(),
|
||||
],
|
||||
},
|
||||
)
|
||||
}).collect(),
|
||||
.. Default::default()
|
||||
}),
|
||||
});
|
||||
|
||||
let to_body = match_body(&input, |field| {
|
||||
quote!(::values::computed::ToComputedValue::to_computed_value(#field, context))
|
||||
});
|
||||
let from_body = match_body(&input, |field| {
|
||||
quote!(::values::computed::ToComputedValue::from_computed_value(#field))
|
||||
});
|
||||
|
||||
quote! {
|
||||
impl #impl_generics ::values::computed::ToComputedValue for #name #ty_generics #where_clause {
|
||||
type ComputedValue = #computed_value_type;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &::values::computed::Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
#to_body
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
#from_body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn match_body<F>(input: &syn::DeriveInput, f: F) -> quote::Tokens
|
||||
where F: Fn(&synstructure::BindingInfo) -> quote::Tokens,
|
||||
{
|
||||
let by_ref = synstructure::BindStyle::Ref.into();
|
||||
let by_value = synstructure::BindStyle::Move.into();
|
||||
|
||||
synstructure::each_variant(&input, &by_ref, |fields, variant| {
|
||||
let name = if let syn::Body::Enum(_) = input.body {
|
||||
format!("{}::{}", input.ident, variant.ident).into()
|
||||
} else {
|
||||
variant.ident.clone()
|
||||
};
|
||||
let (computed_value, computed_fields) = synstructure::match_pattern(&name, &variant.data, &by_value);
|
||||
let fields_pairs = fields.iter().zip(computed_fields.iter());
|
||||
let mut computations = quote!();
|
||||
computations.append_all(fields_pairs.map(|(field, computed_field)| {
|
||||
let expr = f(field);
|
||||
quote!(let #computed_field = #expr;)
|
||||
}));
|
||||
Some(quote!(
|
||||
#computations
|
||||
#computed_value
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// `#ty: ::values::computed::ToComputedValue<ComputedValue = #computed_value,>`
|
||||
fn where_predicate(ty: syn::Ty, computed_value: Option<syn::Ty>) -> syn::WherePredicate {
|
||||
syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
|
||||
bound_lifetimes: vec![],
|
||||
bounded_ty: ty,
|
||||
bounds: vec![syn::TyParamBound::Trait(
|
||||
syn::PolyTraitRef {
|
||||
bound_lifetimes: vec![],
|
||||
trait_ref: trait_ref(computed_value),
|
||||
},
|
||||
syn::TraitBoundModifier::None
|
||||
)],
|
||||
})
|
||||
}
|
||||
|
||||
/// `::values::computed::ToComputedValue<ComputedValue = #computed_value,>`
|
||||
fn trait_ref(computed_value: Option<syn::Ty>) -> syn::Path {
|
||||
syn::Path {
|
||||
global: true,
|
||||
segments: vec![
|
||||
"values".into(),
|
||||
"computed".into(),
|
||||
syn::PathSegment {
|
||||
ident: "ToComputedValue".into(),
|
||||
parameters: syn::PathParameters::AngleBracketed(
|
||||
syn::AngleBracketedParameterData {
|
||||
bindings: trait_bindings(computed_value),
|
||||
.. Default::default()
|
||||
}
|
||||
),
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// `ComputedValue = #computed_value,`
|
||||
fn trait_bindings(computed_value: Option<syn::Ty>) -> Vec<syn::TypeBinding> {
|
||||
computed_value.into_iter().map(|ty| {
|
||||
syn::TypeBinding {
|
||||
ident: "ComputedValue".into(),
|
||||
ty: ty,
|
||||
}
|
||||
}).collect()
|
||||
}
|
|
@ -103,7 +103,7 @@ use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
|||
use style::supports::parse_condition_or_declaration;
|
||||
use style::thread_state;
|
||||
use style::timer::Timer;
|
||||
use style::traversal::{ANIMATION_ONLY, FOR_RECONSTRUCT, UNSTYLED_CHILDREN_ONLY};
|
||||
use style::traversal::{ANIMATION_ONLY, FOR_CSS_RULE_CHANGES, FOR_RECONSTRUCT, UNSTYLED_CHILDREN_ONLY};
|
||||
use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
|
||||
use style::values::{CustomIdent, KeyframesName};
|
||||
use style_traits::ToCss;
|
||||
|
@ -256,6 +256,7 @@ pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
|||
(Root::UnstyledChildrenOnly, Restyle::Normal) |
|
||||
(Root::UnstyledChildrenOnly, Restyle::ForAnimationOnly)
|
||||
=> UNSTYLED_CHILDREN_ONLY,
|
||||
(Root::Normal, Restyle::ForCSSRuleChanges) => FOR_CSS_RULE_CHANGES,
|
||||
(Root::Normal, Restyle::ForReconstruct) => FOR_RECONSTRUCT,
|
||||
_ => panic!("invalid combination of TraversalRootBehavior and TraversalRestyleBehavior"),
|
||||
};
|
||||
|
|
|
@ -8,8 +8,8 @@ use media_queries::CSSErrorReporterTest;
|
|||
use style::context::QuirksMode;
|
||||
use style::parser::{PARSING_MODE_ALLOW_ALL_NUMERIC_VALUES, ParserContext};
|
||||
use style::stylesheets::{CssRuleType, Origin};
|
||||
use style::values::HasViewportPercentage;
|
||||
use style::values::specified::{AbsoluteLength, NoCalcLength, Number, ViewportPercentageLength};
|
||||
use style_traits::HasViewportPercentage;
|
||||
|
||||
#[test]
|
||||
fn length_has_viewport_percentage() {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
use app_units::Au;
|
||||
use style::properties::PropertyDeclaration;
|
||||
use style::properties::longhands::border_top_width;
|
||||
use style::values::HasViewportPercentage;
|
||||
use style::values::specified::{AbsoluteLength, Length, NoCalcLength, ViewportPercentageLength};
|
||||
use style_traits::HasViewportPercentage;
|
||||
|
||||
#[test]
|
||||
fn has_viewport_percentage_for_specified_value() {
|
||||
|
|
|
@ -5,6 +5,8 @@ server.registerDirectory("/data/", do_get_file("data"));
|
|||
|
||||
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
|
||||
|
||||
// ExtensionContent.jsm needs to know when it's running from xpcshell,
|
||||
// to use the right timeout for content scripts executed at document_idle.
|
||||
ExtensionTestUtils.mockAppInfo();
|
||||
|
||||
add_task(async function test_contentscript_runAt() {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
// ExtensionContent.jsm needs to know when it's running from xpcshell,
|
||||
// to use the right timeout for content scripts executed at document_idle.
|
||||
ExtensionTestUtils.mockAppInfo();
|
||||
|
||||
const server = createHttpServer();
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
// ExtensionContent.jsm needs to know when it's running from xpcshell,
|
||||
// to use the right timeout for content scripts executed at document_idle.
|
||||
ExtensionTestUtils.mockAppInfo();
|
||||
|
||||
const server = createHttpServer();
|
||||
|
|
Загрузка…
Ссылка в новой задаче