summaryrefslogtreecommitdiffstats
path: root/dom
diff options
context:
space:
mode:
Diffstat (limited to 'dom')
-rw-r--r--dom/animation/Animation.cpp42
-rw-r--r--dom/animation/Animation.h10
-rw-r--r--dom/animation/AnimationEffectReadOnly.cpp18
-rw-r--r--dom/animation/ComputedTiming.h6
-rw-r--r--dom/animation/test/css-animations/file_event-dispatch.html252
-rw-r--r--dom/animation/test/css-animations/file_event-order.html160
-rw-r--r--dom/animation/test/css-animations/test_event-dispatch.html15
-rw-r--r--dom/animation/test/css-animations/test_event-order.html (renamed from dom/animation/test/css-transitions/test_csstransition-events.html)3
-rw-r--r--dom/animation/test/css-transitions/file_animation-cancel.html182
-rw-r--r--dom/animation/test/css-transitions/file_csstransition-events.html223
-rw-r--r--dom/animation/test/css-transitions/file_event-dispatch.html474
-rw-r--r--dom/animation/test/css-transitions/file_setting-effect.html9
-rw-r--r--dom/animation/test/css-transitions/test_event-dispatch.html14
-rw-r--r--dom/animation/test/mochitest.ini6
-rw-r--r--dom/base/nsContentUtils.cpp12
-rw-r--r--dom/base/nsContentUtils.h9
-rw-r--r--dom/base/nsDOMNavigationTiming.cpp138
-rw-r--r--dom/base/nsDOMNavigationTiming.h125
-rw-r--r--dom/base/nsGkAtomList.h3
-rw-r--r--dom/base/nsGlobalWindow.cpp29
-rw-r--r--dom/base/nsISelectionPrivate.idl2
-rw-r--r--dom/base/test/file_simplecontentpolicy.js1
-rw-r--r--dom/bindings/BindingUtils.cpp20
-rw-r--r--dom/bindings/Codegen.py14
-rw-r--r--dom/bindings/ErrorResult.h2
-rwxr-xr-xdom/console/Console.cpp5
-rw-r--r--dom/console/Console.h10
-rwxr-xr-xdom/events/Event.cpp7
-rw-r--r--dom/events/EventNameList.h12
-rw-r--r--dom/events/EventStateManager.cpp66
-rw-r--r--dom/events/EventStateManager.h10
-rw-r--r--dom/events/WheelHandlingHelper.cpp1
-rw-r--r--dom/events/test/mochitest.ini1
-rw-r--r--dom/events/test/test_bug1304044.html133
-rw-r--r--dom/events/test/test_eventTimeStamp.html36
-rw-r--r--dom/events/test/test_legacy_event.html21
-rw-r--r--dom/jsurl/nsJSProtocolHandler.cpp49
-rw-r--r--dom/media/MediaStreamTrack.cpp12
-rwxr-xr-xdom/performance/Performance.cpp49
-rw-r--r--dom/performance/Performance.h36
-rw-r--r--dom/performance/PerformanceEntry.h21
-rw-r--r--dom/performance/PerformanceMainThread.cpp99
-rw-r--r--dom/performance/PerformanceMainThread.h21
-rw-r--r--dom/performance/PerformanceNavigationTiming.cpp96
-rw-r--r--dom/performance/PerformanceNavigationTiming.h71
-rw-r--r--dom/performance/PerformanceObserver.cpp14
-rw-r--r--dom/performance/PerformanceObserverEntryList.cpp15
-rw-r--r--dom/performance/PerformanceResourceTiming.cpp49
-rw-r--r--dom/performance/PerformanceResourceTiming.h12
-rw-r--r--dom/performance/PerformanceService.cpp46
-rw-r--r--dom/performance/PerformanceService.h48
-rwxr-xr-xdom/performance/PerformanceTiming.cpp71
-rwxr-xr-xdom/performance/PerformanceTiming.h18
-rw-r--r--dom/performance/PerformanceWorker.cpp35
-rw-r--r--dom/performance/PerformanceWorker.h14
-rw-r--r--dom/performance/moz.build4
-rw-r--r--dom/performance/tests/mochitest.ini3
-rw-r--r--dom/performance/tests/performance_observer.html74
-rw-r--r--dom/performance/tests/test_performance_observer.html52
-rw-r--r--dom/performance/tests/test_performance_user_timing.js21
-rw-r--r--dom/performance/tests/test_timeOrigin.html68
-rw-r--r--dom/performance/tests/test_worker_observer.html13
-rw-r--r--dom/performance/tests/worker_performance_observer.html32
-rw-r--r--dom/plugins/base/nsPluginInstanceOwner.cpp28
-rw-r--r--dom/plugins/test/mochitest/test_bug813906.html22
-rw-r--r--dom/security/nsContentSecurityManager.cpp74
-rw-r--r--dom/tests/mochitest/general/test_interfaces.html6
-rw-r--r--dom/webidl/EventHandler.webidl9
-rw-r--r--dom/webidl/Performance.webidl3
-rw-r--r--dom/webidl/PerformanceNavigationTiming.webidl33
-rw-r--r--dom/webidl/PerformanceObserver.webidl1
-rw-r--r--dom/webidl/PerformanceResourceTiming.webidl8
-rw-r--r--dom/webidl/PerformanceTiming.webidl6
-rw-r--r--dom/webidl/moz.build1
-rw-r--r--dom/workers/ServiceWorkerEvents.cpp10
-rw-r--r--dom/workers/ServiceWorkerPrivate.cpp45
-rw-r--r--dom/workers/ServiceWorkerPrivate.h1
-rw-r--r--dom/workers/WorkerPrivate.cpp14
-rw-r--r--dom/workers/WorkerPrivate.h13
-rw-r--r--dom/workers/test/serviceworkers/chrome.ini3
-rw-r--r--dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html168
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworker_interfaces.js4
-rw-r--r--dom/workers/test/test_worker_interfaces.js4
-rw-r--r--dom/xslt/xpath/txXPCOMExtensionFunction.cpp2
84 files changed, 2653 insertions, 916 deletions
diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp
index 6dd583ed1..bd318f79e 100644
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -230,6 +230,10 @@ Animation::SetTimelineNoUpdate(AnimationTimeline* aTimeline)
return;
}
+ StickyTimeDuration activeTime = mEffect
+ ? mEffect->GetComputedTiming().mActiveTime
+ : StickyTimeDuration();
+
RefPtr<AnimationTimeline> oldTimeline = mTimeline;
if (oldTimeline) {
oldTimeline->RemoveAnimation(this);
@@ -240,6 +244,9 @@ Animation::SetTimelineNoUpdate(AnimationTimeline* aTimeline)
mHoldTime.SetNull();
}
+ if (!aTimeline) {
+ MaybeQueueCancelEvent(activeTime);
+ }
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
}
@@ -722,8 +729,10 @@ TimeStamp
Animation::ElapsedTimeToTimeStamp(
const StickyTimeDuration& aElapsedTime) const
{
- return AnimationTimeToTimeStamp(aElapsedTime +
- mEffect->SpecifiedTiming().mDelay);
+ TimeDuration delay = mEffect
+ ? mEffect->SpecifiedTiming().mDelay
+ : TimeDuration();
+ return AnimationTimeToTimeStamp(aElapsedTime + delay);
}
@@ -771,14 +780,28 @@ Animation::CancelNoUpdate()
DispatchPlaybackEvent(NS_LITERAL_STRING("cancel"));
+ StickyTimeDuration activeTime = mEffect
+ ? mEffect->GetComputedTiming().mActiveTime
+ : StickyTimeDuration();
+
mHoldTime.SetNull();
mStartTime.SetNull();
- UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
-
if (mTimeline) {
mTimeline->RemoveAnimation(this);
}
+ MaybeQueueCancelEvent(activeTime);
+
+ // When an animation is cancelled it no longer needs further ticks from the
+ // timeline. However, if we queued a cancel event and this was the last
+ // animation attached to the timeline, the timeline will stop observing the
+ // refresh driver and there may be no subsequent refresh driver tick for
+ // dispatching the queued event.
+ //
+ // By calling UpdateTiming *after* removing ourselves from our timeline, we
+ // ensure the timeline will register with the refresh driver for at least one
+ // more tick.
+ UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
}
void
@@ -819,6 +842,17 @@ Animation::HasLowerCompositeOrderThan(const Animation& aOther) const
return thisTransition->HasLowerCompositeOrderThan(*otherTransition);
}
if (thisTransition || otherTransition) {
+ // Cancelled transitions no longer have an owning element. To be strictly
+ // correct we should store a strong reference to the owning element
+ // so that if we arrive here while sorting cancel events, we can sort
+ // them in the correct order.
+ //
+ // However, given that cancel events are almost always queued
+ // synchronously in some deterministic manner, we can be fairly sure
+ // that cancel events will be dispatched in a deterministic order
+ // (which is our only hard requirement until specs say otherwise).
+ // Furthermore, we only reach here when we have events with equal
+ // timestamps so this is an edge case we can probably ignore for now.
return thisTransition;
}
}
diff --git a/dom/animation/Animation.h b/dom/animation/Animation.h
index c59d7d6ce..3263b30c4 100644
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -326,6 +326,16 @@ public:
void NotifyEffectTimingUpdated();
+ /**
+ * Used by subclasses to synchronously queue a cancel event in situations
+ * where the Animation may have been cancelled.
+ *
+ * We need to do this synchronously because after a CSS animation/transition
+ * is canceled, it will be released by its owning element and may not still
+ * exist when we would normally go to queue events on the next tick.
+ */
+ virtual void MaybeQueueCancelEvent(StickyTimeDuration aActiveTime) {};
+
protected:
void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
void SilentlySetPlaybackRate(double aPlaybackRate);
diff --git a/dom/animation/AnimationEffectReadOnly.cpp b/dom/animation/AnimationEffectReadOnly.cpp
index aff28a37b..bf2e2197d 100644
--- a/dom/animation/AnimationEffectReadOnly.cpp
+++ b/dom/animation/AnimationEffectReadOnly.cpp
@@ -127,10 +127,6 @@ AnimationEffectReadOnly::GetComputedTimingAt(
}
const TimeDuration& localTime = aLocalTime.Value();
- // Calculate the time within the active interval.
- // https://w3c.github.io/web-animations/#active-time
- StickyTimeDuration activeTime;
-
StickyTimeDuration beforeActiveBoundary =
std::max(std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime),
zeroDuration);
@@ -148,7 +144,7 @@ AnimationEffectReadOnly::GetComputedTimingAt(
// The animation isn't active or filling at this time.
return result;
}
- activeTime =
+ result.mActiveTime =
std::max(std::min(StickyTimeDuration(localTime - aTiming.mDelay),
result.mActiveDuration),
zeroDuration);
@@ -159,13 +155,14 @@ AnimationEffectReadOnly::GetComputedTimingAt(
// The animation isn't active or filling at this time.
return result;
}
- activeTime = std::max(StickyTimeDuration(localTime - aTiming.mDelay),
- zeroDuration);
+ result.mActiveTime
+ = std::max(StickyTimeDuration(localTime - aTiming.mDelay),
+ zeroDuration);
} else {
MOZ_ASSERT(result.mActiveDuration != zeroDuration,
"How can we be in the middle of a zero-duration interval?");
result.mPhase = ComputedTiming::AnimationPhase::Active;
- activeTime = localTime - aTiming.mDelay;
+ result.mActiveTime = localTime - aTiming.mDelay;
}
// Convert active time to a multiple of iterations.
@@ -176,7 +173,7 @@ AnimationEffectReadOnly::GetComputedTimingAt(
? 0.0
: result.mIterations;
} else {
- overallProgress = activeTime / result.mDuration;
+ overallProgress = result.mActiveTime / result.mDuration;
}
// Factor in iteration start offset.
@@ -208,7 +205,8 @@ AnimationEffectReadOnly::GetComputedTimingAt(
if (result.mPhase == ComputedTiming::AnimationPhase::After &&
progress == 0.0 &&
result.mIterations != 0.0 &&
- (activeTime != zeroDuration || result.mDuration == zeroDuration)) {
+ (result.mActiveTime != zeroDuration ||
+ result.mDuration == zeroDuration)) {
// The only way we can be in the after phase with a progress of zero and
// a current iteration of zero, is if we have a zero iteration count or
// were clipped using a negative end delay--both of which we should have
diff --git a/dom/animation/ComputedTiming.h b/dom/animation/ComputedTiming.h
index 4a98e3933..b1c6674a5 100644
--- a/dom/animation/ComputedTiming.h
+++ b/dom/animation/ComputedTiming.h
@@ -29,6 +29,8 @@ struct ComputedTiming
// Will equal StickyTimeDuration::Forever() if the animation repeats
// indefinitely.
StickyTimeDuration mActiveDuration;
+ // The time within the active interval.
+ StickyTimeDuration mActiveTime;
// The effect end time in local time (i.e. an offset from the effect's
// start time). Will equal StickyTimeDuration::Forever() if the animation
// plays indefinitely.
@@ -62,12 +64,12 @@ struct ComputedTiming
}
enum class AnimationPhase {
- Null, // Not sampled (null sample time)
+ Idle, // Not sampled (null sample time)
Before, // Sampled prior to the start of the active interval
Active, // Sampled within the active interval
After // Sampled after (or at) the end of the active interval
};
- AnimationPhase mPhase = AnimationPhase::Null;
+ AnimationPhase mPhase = AnimationPhase::Idle;
ComputedTimingFunction::BeforeFlag mBeforeFlag =
ComputedTimingFunction::BeforeFlag::Unset;
diff --git a/dom/animation/test/css-animations/file_event-dispatch.html b/dom/animation/test/css-animations/file_event-dispatch.html
new file mode 100644
index 000000000..266205bc3
--- /dev/null
+++ b/dom/animation/test/css-animations/file_event-dispatch.html
@@ -0,0 +1,252 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Tests for CSS animation event dispatch</title>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
+<script src="../testcommon.js"></script>
+<style>
+ @keyframes anim {
+ from { margin-left: 0px; }
+ to { margin-left: 100px; }
+ }
+</style>
+<body>
+<script>
+'use strict';
+
+/**
+ * Helper class to record the elapsedTime member of each event.
+ * The EventWatcher class in testharness.js allows us to wait on
+ * multiple events in a certain order but only records the event
+ * parameters of the most recent event.
+ */
+function AnimationEventHandler(target) {
+ this.target = target;
+ this.target.onanimationstart = function(evt) {
+ this.animationstart = evt.elapsedTime;
+ }.bind(this);
+ this.target.onanimationiteration = function(evt) {
+ this.animationiteration = evt.elapsedTime;
+ }.bind(this);
+ this.target.onanimationend = function(evt) {
+ this.animationend = evt.elapsedTime;
+ }.bind(this);
+}
+AnimationEventHandler.prototype.clear = function() {
+ this.animationstart = undefined;
+ this.animationiteration = undefined;
+ this.animationend = undefined;
+}
+
+function setupAnimation(t, animationStyle) {
+ var div = addDiv(t, { style: "animation: " + animationStyle });
+ var watcher = new EventWatcher(t, div, [ 'animationstart',
+ 'animationiteration',
+ 'animationend' ]);
+ var handler = new AnimationEventHandler(div);
+ var animation = div.getAnimations()[0];
+
+ return [animation, watcher, handler, div];
+}
+
+promise_test(function(t) {
+ // Add 1ms delay to ensure that the delay is not included in the elapsedTime.
+ const [animation, watcher] = setupAnimation(t, 'anim 100s 1ms');
+
+ return watcher.wait_for('animationstart').then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Idle -> Active');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] = setupAnimation(t, 'anim 100s');
+
+ // Seek to After phase.
+ animation.finish();
+ return watcher.wait_for([ 'animationstart',
+ 'animationend' ]).then(function() {
+ assert_equals(handler.animationstart, 0.0);
+ assert_equals(handler.animationend, 100);
+ });
+}, 'Idle -> After');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] =
+ setupAnimation(t, 'anim 100s 100s paused');
+
+ return animation.ready.then(function() {
+ // Seek to Active phase.
+ animation.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('animationstart');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Before -> Active');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] =
+ setupAnimation(t, 'anim 100s 100s paused');
+
+ return animation.ready.then(function() {
+ // Seek to After phase.
+ animation.finish();
+ return watcher.wait_for([ 'animationstart', 'animationend' ]);
+ }).then(function(evt) {
+ assert_equals(handler.animationstart, 0.0);
+ assert_equals(handler.animationend, 100.0);
+ });
+}, 'Before -> After');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] =
+ setupAnimation(t, 'anim 100s 100s paused');
+
+ // Seek to Active phase.
+ animation.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('animationstart').then(function() {
+ // Seek to Before phase.
+ animation.currentTime = 0;
+ return watcher.wait_for('animationend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Before');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] = setupAnimation(t, 'anim 100s paused');
+
+ return watcher.wait_for('animationstart').then(function(evt) {
+ // Seek to After phase.
+ animation.finish();
+ return watcher.wait_for('animationend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'Active -> After');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] =
+ setupAnimation(t, 'anim 100s 100s paused');
+
+ // Seek to After phase.
+ animation.finish();
+ return watcher.wait_for([ 'animationstart',
+ 'animationend' ]).then(function() {
+ // Seek to Before phase.
+ animation.currentTime = 0;
+ handler.clear();
+ return watcher.wait_for([ 'animationstart', 'animationend' ]);
+ }).then(function() {
+ assert_equals(handler.animationstart, 100.0);
+ assert_equals(handler.animationend, 0.0);
+ });
+}, 'After -> Before');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] =
+ setupAnimation(t, 'anim 100s 100s paused');
+
+ // Seek to After phase.
+ animation.finish();
+ return watcher.wait_for([ 'animationstart',
+ 'animationend' ]).then(function() {
+ // Seek to Active phase.
+ animation.currentTime = 100 * MS_PER_SEC;
+ handler.clear();
+ return watcher.wait_for('animationstart');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'After -> Active');
+
+promise_test(function(t) {
+ const [animation, watcher, handler]
+ = setupAnimation(t, 'anim 100s 100s 3 paused');
+
+ return animation.ready.then(function() {
+ // Seek to iteration 0 (no animationiteration event should be dispatched)
+ animation.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('animationstart');
+ }).then(function(evt) {
+ // Seek to iteration 2
+ animation.currentTime = 300 * MS_PER_SEC;
+ handler.clear();
+ return watcher.wait_for('animationiteration');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 200);
+ // Seek to After phase (no animationiteration event should be dispatched)
+ animation.currentTime = 400 * MS_PER_SEC;
+ return watcher.wait_for('animationend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 300);
+ });
+}, 'Active -> Active (forwards)');
+
+promise_test(function(t) {
+ const [animation, watcher, handler] = setupAnimation(t, 'anim 100s 100s 3');
+
+ // Seek to After phase.
+ animation.finish();
+ return watcher.wait_for([ 'animationstart',
+ 'animationend' ]).then(function() {
+ // Seek to iteration 2 (no animationiteration event should be dispatched)
+ animation.pause();
+ animation.currentTime = 300 * MS_PER_SEC;
+ return watcher.wait_for('animationstart');
+ }).then(function() {
+ // Seek to mid of iteration 0 phase.
+ animation.currentTime = 200 * MS_PER_SEC;
+ return watcher.wait_for('animationiteration');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 200.0);
+ // Seek to before phase (no animationiteration event should be dispatched)
+ animation.currentTime = 0;
+ return watcher.wait_for('animationend');
+ });
+}, 'Active -> Active (backwards)');
+
+promise_test(function(t) {
+ const [animation, watcher, handler, div] =
+ setupAnimation(t, 'anim 100s paused');
+ return watcher.wait_for('animationstart').then(function(evt) {
+ // Seek to Idle phase.
+ div.style.display = 'none';
+ flushComputedStyle(div);
+
+ // FIXME: bug 1302648: Add test for animationcancel event here.
+
+ // Restart this animation.
+ div.style.display = '';
+ return watcher.wait_for('animationstart');
+ });
+}, 'Active -> Idle -> Active: animationstart is fired by restarting animation');
+
+promise_test(function(t) {
+ const [animation, watcher, handler, div] =
+ setupAnimation(t, 'anim 100s 100s 2 paused');
+
+ // Make After.
+ animation.finish();
+ return watcher.wait_for([ 'animationstart',
+ 'animationend' ]).then(function(evt) {
+ animation.playbackRate = -1;
+ return watcher.wait_for('animationstart');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 200);
+ // Seek to 1st iteration
+ animation.currentTime = 200 * MS_PER_SEC - 1;
+ return watcher.wait_for('animationiteration');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100);
+ // Seek to before
+ animation.currentTime = 100 * MS_PER_SEC - 1;
+ return watcher.wait_for('animationend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0);
+ assert_equals(animation.playState, 'running'); // delay
+ });
+}, 'Negative playbackRate sanity test(Before -> Active -> Before)');
+
+done();
+</script>
+</body>
+</html>
diff --git a/dom/animation/test/css-animations/file_event-order.html b/dom/animation/test/css-animations/file_event-order.html
new file mode 100644
index 000000000..da78b6541
--- /dev/null
+++ b/dom/animation/test/css-animations/file_event-order.html
@@ -0,0 +1,160 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Tests for CSS animation event order</title>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
+<script src="../testcommon.js"></script>
+<style>
+ @keyframes anim {
+ from { margin-left: 0px; }
+ to { margin-left: 100px; }
+ }
+</style>
+<body>
+<script type='text/javascript'>
+'use strict';
+
+/**
+ * Asserts that the set of actual and received events match.
+ * @param actualEvents An array of the received AnimationEvent objects.
+ * @param expectedEvents A series of array objects representing the expected
+ * events, each having the form:
+ * [ event type, target element, elapsed time ]
+ */
+function checkEvents(actualEvents, ...expectedEvents) {
+ assert_equals(actualEvents.length, expectedEvents.length,
+ `Number of actual events (${actualEvents.length}: \
+${actualEvents.map(event => event.type).join(', ')}) should match expected \
+events (${expectedEvents.map(event => event.type).join(', ')})`);
+
+ actualEvents.forEach((actualEvent, i) => {
+ assert_equals(expectedEvents[i][0], actualEvent.type,
+ 'Event type should match');
+ assert_equals(expectedEvents[i][1], actualEvent.target,
+ 'Event target should match');
+ assert_equals(expectedEvents[i][2], actualEvent.elapsedTime,
+ 'Event\'s elapsed time should match');
+ });
+}
+
+function setupAnimation(t, animationStyle, receiveEvents) {
+ const div = addDiv(t, { style: "animation: " + animationStyle });
+ const watcher = new EventWatcher(t, div, [ 'animationstart',
+ 'animationiteration',
+ 'animationend' ]);
+
+ ['start', 'iteration', 'end'].forEach(name => {
+ div['onanimation' + name] = function(evt) {
+ receiveEvents.push({ type: evt.type,
+ target: evt.target,
+ elapsedTime: evt.elapsedTime });
+ }.bind(this);
+ });
+
+ const animation = div.getAnimations()[0];
+
+ return [animation, watcher, div];
+}
+
+promise_test(function(t) {
+ let events = [];
+ const [animation1, watcher1, div1] =
+ setupAnimation(t, 'anim 100s 2 paused', events);
+ const [animation2, watcher2, div2] =
+ setupAnimation(t, 'anim 100s 2 paused', events);
+
+ return Promise.all([ watcher1.wait_for('animationstart'),
+ watcher2.wait_for('animationstart') ]).then(function() {
+ checkEvents(events, ['animationstart', div1, 0],
+ ['animationstart', div2, 0]);
+
+ events.length = 0; // Clear received event array
+
+ animation1.currentTime = 100 * MS_PER_SEC;
+ animation2.currentTime = 100 * MS_PER_SEC;
+ return Promise.all([ watcher1.wait_for('animationiteration'),
+ watcher2.wait_for('animationiteration') ]);
+ }).then(function() {
+ checkEvents(events, ['animationiteration', div1, 100],
+ ['animationiteration', div2, 100]);
+
+ events.length = 0; // Clear received event array
+
+ animation1.finish();
+ animation2.finish();
+
+ return Promise.all([ watcher1.wait_for('animationend'),
+ watcher2.wait_for('animationend') ]);
+ }).then(function() {
+ checkEvents(events, ['animationend', div1, 200],
+ ['animationend', div2, 200]);
+ });
+}, 'Test same events are ordered by elements.');
+
+promise_test(function(t) {
+ let events = [];
+ const [animation1, watcher1, div1] =
+ setupAnimation(t, 'anim 200s 400s', events);
+ const [animation2, watcher2, div2] =
+ setupAnimation(t, 'anim 300s 2', events);
+
+ return watcher2.wait_for('animationstart').then(function(evt) {
+ animation1.currentTime = 400 * MS_PER_SEC;
+ animation2.currentTime = 400 * MS_PER_SEC;
+
+ events.length = 0; // Clear received event array
+
+ return Promise.all([ watcher1.wait_for('animationstart'),
+ watcher2.wait_for('animationiteration') ]);
+ }).then(function() {
+ checkEvents(events, ['animationiteration', div2, 300],
+ ['animationstart', div1, 0]);
+ });
+}, 'Test start and iteration events are ordered by time.');
+
+promise_test(function(t) {
+ let events = [];
+ const [animation1, watcher1, div1] =
+ setupAnimation(t, 'anim 150s', events);
+ const [animation2, watcher2, div2] =
+ setupAnimation(t, 'anim 100s 2', events);
+
+ return Promise.all([ watcher1.wait_for('animationstart'),
+ watcher2.wait_for('animationstart') ]).then(function() {
+ animation1.currentTime = 150 * MS_PER_SEC;
+ animation2.currentTime = 150 * MS_PER_SEC;
+
+ events.length = 0; // Clear received event array
+
+ return Promise.all([ watcher1.wait_for('animationend'),
+ watcher2.wait_for('animationiteration') ]);
+ }).then(function() {
+ checkEvents(events, ['animationiteration', div2, 100],
+ ['animationend', div1, 150]);
+ });
+}, 'Test iteration and end events are ordered by time.');
+
+promise_test(function(t) {
+ let events = [];
+ const [animation1, watcher1, div1] =
+ setupAnimation(t, 'anim 100s 100s', events);
+ const [animation2, watcher2, div2] =
+ setupAnimation(t, 'anim 100s 2', events);
+
+ animation1.finish();
+ animation2.finish();
+
+ return Promise.all([ watcher1.wait_for([ 'animationstart',
+ 'animationend' ]),
+ watcher2.wait_for([ 'animationstart',
+ 'animationend' ]) ]).then(function() {
+ checkEvents(events, ['animationstart', div2, 0],
+ ['animationstart', div1, 0],
+ ['animationend', div1, 100],
+ ['animationend', div2, 200]);
+ });
+}, 'Test start and end events are sorted correctly when fired simultaneously');
+
+done();
+</script>
+</body>
+</html>
diff --git a/dom/animation/test/css-animations/test_event-dispatch.html b/dom/animation/test/css-animations/test_event-dispatch.html
new file mode 100644
index 000000000..de3be0301
--- /dev/null
+++ b/dom/animation/test/css-animations/test_event-dispatch.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_event-dispatch.html");
+ });
+</script>
+</html>
diff --git a/dom/animation/test/css-transitions/test_csstransition-events.html b/dom/animation/test/css-animations/test_event-order.html
index 92559ad67..57f1f3876 100644
--- a/dom/animation/test/css-transitions/test_csstransition-events.html
+++ b/dom/animation/test/css-animations/test_event-order.html
@@ -9,6 +9,7 @@ setup({explicit_done: true});
SpecialPowers.pushPrefEnv(
{ "set": [["dom.animations-api.core.enabled", true]]},
function() {
- window.open("file_csstransition-events.html");
+ window.open("file_event-order.html");
});
</script>
+</html>
diff --git a/dom/animation/test/css-transitions/file_animation-cancel.html b/dom/animation/test/css-transitions/file_animation-cancel.html
index 6094b383f..71f02fb11 100644
--- a/dom/animation/test/css-transitions/file_animation-cancel.html
+++ b/dom/animation/test/css-transitions/file_animation-cancel.html
@@ -11,13 +11,12 @@ promise_test(function(t) {
div.style.transition = 'margin-left 100s';
div.style.marginLeft = '1000px';
- flushComputedStyle(div);
- var animation = div.getAnimations()[0];
- return animation.ready.then(waitForFrame).then(function() {
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(waitForFrame).then(function() {
assert_not_equals(getComputedStyle(div).marginLeft, '1000px',
'transform style is animated before cancelling');
- animation.cancel();
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, div.style.marginLeft,
'transform style is no longer animated after cancelling');
});
@@ -29,45 +28,21 @@ promise_test(function(t) {
div.style.transition = 'margin-left 100s';
div.style.marginLeft = '1000px';
- flushComputedStyle(div);
-
- div.addEventListener('transitionend', function() {
- assert_unreached('Got unexpected end event on cancelled transition');
- });
-
- var animation = div.getAnimations()[0];
- return animation.ready.then(function() {
- // Seek to just before the end then cancel
- animation.currentTime = 99.9 * 1000;
- animation.cancel();
- // Then wait a couple of frames and check that no event was dispatched
- return waitForAnimationFrames(2);
- });
-}, 'Cancelled CSS transitions do not dispatch events');
-
-promise_test(function(t) {
- var div = addDiv(t, { style: 'margin-left: 0px' });
- flushComputedStyle(div);
-
- div.style.transition = 'margin-left 100s';
- div.style.marginLeft = '1000px';
- flushComputedStyle(div);
-
- var animation = div.getAnimations()[0];
- return animation.ready.then(function() {
- animation.cancel();
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is not animated after cancelling');
- animation.play();
+ transition.play();
assert_equals(getComputedStyle(div).marginLeft, '0px',
'margin-left style is animated after re-starting transition');
- return animation.ready;
+ return transition.ready;
}).then(function() {
- assert_equals(animation.playState, 'running',
+ assert_equals(transition.playState, 'running',
'Transition succeeds in running after being re-started');
});
-}, 'After cancelling a transition, it can still be re-used');
+}, 'After canceling a transition, it can still be re-used');
promise_test(function(t) {
var div = addDiv(t, { style: 'margin-left: 0px' });
@@ -75,20 +50,19 @@ promise_test(function(t) {
div.style.transition = 'margin-left 100s';
div.style.marginLeft = '1000px';
- flushComputedStyle(div);
- var animation = div.getAnimations()[0];
- return animation.ready.then(function() {
- animation.finish();
- animation.cancel();
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ transition.finish();
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is not animated after cancelling');
- animation.play();
+ transition.play();
assert_equals(getComputedStyle(div).marginLeft, '0px',
'margin-left style is animated after re-starting transition');
- return animation.ready;
+ return transition.ready;
}).then(function() {
- assert_equals(animation.playState, 'running',
+ assert_equals(transition.playState, 'running',
'Transition succeeds in running after being re-started');
});
}, 'After cancelling a finished transition, it can still be re-used');
@@ -99,10 +73,9 @@ test(function(t) {
div.style.transition = 'margin-left 100s';
div.style.marginLeft = '1000px';
- flushComputedStyle(div);
- var animation = div.getAnimations()[0];
- animation.cancel();
+ var transition = div.getAnimations()[0];
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is not animated after cancelling');
@@ -113,7 +86,7 @@ test(function(t) {
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is still not animated after updating'
+ ' transition-duration');
- assert_equals(animation.playState, 'idle',
+ assert_equals(transition.playState, 'idle',
'Transition is still idle after updating transition-duration');
}, 'After cancelling a transition, updating transition properties doesn\'t make'
+ ' it live again');
@@ -124,15 +97,14 @@ promise_test(function(t) {
div.style.transition = 'margin-left 100s';
div.style.marginLeft = '1000px';
- flushComputedStyle(div);
- var animation = div.getAnimations()[0];
- return animation.ready.then(function() {
- assert_equals(animation.playState, 'running');
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
div.style.display = 'none';
return waitForFrame();
}).then(function() {
- assert_equals(animation.playState, 'idle');
+ assert_equals(transition.playState, 'idle');
assert_equals(getComputedStyle(div).marginLeft, '1000px');
});
}, 'Setting display:none on an element cancels its transitions');
@@ -147,19 +119,115 @@ promise_test(function(t) {
childDiv.style.transition = 'margin-left 100s';
childDiv.style.marginLeft = '1000px';
- flushComputedStyle(childDiv);
- var animation = childDiv.getAnimations()[0];
- return animation.ready.then(function() {
- assert_equals(animation.playState, 'running');
+ var transition = childDiv.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
parentDiv.style.display = 'none';
return waitForFrame();
}).then(function() {
- assert_equals(animation.playState, 'idle');
+ assert_equals(transition.playState, 'idle');
assert_equals(getComputedStyle(childDiv).marginLeft, '1000px');
});
}, 'Setting display:none cancels transitions on a child element');
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ // Set an unrecognized property value
+ div.style.transitionProperty = 'none';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ assert_equals(getComputedStyle(div).marginLeft, '1000px');
+ });
+}, 'Removing a property from transition-property cancels transitions on that '+
+ 'property');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.transition = 'margin-top 10s -10s'; // combined duration is zero
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ assert_equals(getComputedStyle(div).marginLeft, '1000px');
+ });
+}, 'Setting zero combined duration');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.marginLeft = '2000px';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ });
+}, 'Changing style to another interpolable value cancels the original ' +
+ 'transition');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.marginLeft = 'auto';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(div.getAnimations().length, 0,
+ 'There should be no transitions');
+ assert_equals(transition.playState, 'idle');
+ });
+}, 'An after-change style value can\'t be interpolated');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.marginLeft = '0px';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ });
+}, 'Reversing a running transition cancels the original transition');
+
done();
</script>
</body>
diff --git a/dom/animation/test/css-transitions/file_csstransition-events.html b/dom/animation/test/css-transitions/file_csstransition-events.html
deleted file mode 100644
index 5011bc130..000000000
--- a/dom/animation/test/css-transitions/file_csstransition-events.html
+++ /dev/null
@@ -1,223 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>Tests for CSS-Transition events</title>
-<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#transition-events">
-<script src="../testcommon.js"></script>
-<body>
-<script>
-'use strict';
-
-/**
- * Helper class to record the elapsedTime member of each event.
- * The EventWatcher class in testharness.js allows us to wait on
- * multiple events in a certain order but only records the event
- * parameters of the most recent event.
- */
-function TransitionEventHandler(target) {
- this.target = target;
- this.target.ontransitionrun = function(evt) {
- this.transitionrun = evt.elapsedTime;
- }.bind(this);
- this.target.ontransitionstart = function(evt) {
- this.transitionstart = evt.elapsedTime;
- }.bind(this);
- this.target.ontransitionend = function(evt) {
- this.transitionend = evt.elapsedTime;
- }.bind(this);
-}
-
-TransitionEventHandler.prototype.clear = function() {
- this.transitionrun = undefined;
- this.transitionstart = undefined;
- this.transitionend = undefined;
-};
-
-function setupTransition(t, transitionStyle) {
- var div, watcher, handler, transition;
- transitionStyle = transitionStyle || 'transition: margin-left 100s 100s';
- div = addDiv(t, { style: transitionStyle });
- watcher = new EventWatcher(t, div, [ 'transitionrun',
- 'transitionstart',
- 'transitionend' ]);
- handler = new TransitionEventHandler(div);
- flushComputedStyle(div);
-
- div.style.marginLeft = '100px';
- flushComputedStyle(div);
-
- transition = div.getAnimations()[0];
-
- return [transition, watcher, handler];
-}
-
-// On the next frame (i.e. when events are queued), whether or not the
-// transition is still pending depends on the implementation.
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- return watcher.wait_for('transitionrun').then(function(evt) {
- assert_equals(evt.elapsedTime, 0.0);
- });
-}, 'Idle -> Pending or Before');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- // Force the transition to leave the idle phase
- transition.startTime = document.timeline.currentTime;
- return watcher.wait_for('transitionrun').then(function(evt) {
- assert_equals(evt.elapsedTime, 0.0);
- });
-}, 'Idle -> Before');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- // Seek to Active phase.
- transition.currentTime = 100 * MS_PER_SEC;
- transition.pause();
- return watcher.wait_for([ 'transitionrun',
- 'transitionstart' ]).then(function(evt) {
- assert_equals(handler.transitionrun, 0.0);
- assert_equals(handler.transitionstart, 0.0);
- });
-}, 'Idle or Pending -> Active');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- // Seek to After phase.
- transition.finish();
- return watcher.wait_for([ 'transitionrun',
- 'transitionstart',
- 'transitionend' ]).then(function(evt) {
- assert_equals(handler.transitionrun, 0.0);
- assert_equals(handler.transitionstart, 0.0);
- assert_equals(handler.transitionend, 100.0);
- });
-}, 'Idle or Pending -> After');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
-
- return Promise.all([ watcher.wait_for('transitionrun'),
- transition.ready ]).then(function() {
- transition.currentTime = 100 * MS_PER_SEC;
- return watcher.wait_for('transitionstart');
- }).then(function() {
- assert_equals(handler.transitionstart, 0.0);
- });
-}, 'Before -> Active');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- return Promise.all([ watcher.wait_for('transitionrun'),
- transition.ready ]).then(function() {
- // Seek to After phase.
- transition.currentTime = 200 * MS_PER_SEC;
- return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
- }).then(function(evt) {
- assert_equals(handler.transitionstart, 0.0);
- assert_equals(handler.transitionend, 100.0);
- });
-}, 'Before -> After');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- // Seek to Active phase.
- transition.currentTime = 100 * MS_PER_SEC;
- return watcher.wait_for([ 'transitionrun',
- 'transitionstart' ]).then(function(evt) {
- // Seek to Before phase.
- transition.currentTime = 0;
- return watcher.wait_for('transitionend');
- }).then(function(evt) {
- assert_equals(evt.elapsedTime, 0.0);
- });
-}, 'Active -> Before');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- // Seek to Active phase.
- transition.currentTime = 100 * MS_PER_SEC;
- return watcher.wait_for([ 'transitionrun',
- 'transitionstart' ]).then(function(evt) {
- // Seek to After phase.
- transition.currentTime = 200 * MS_PER_SEC;
- return watcher.wait_for('transitionend');
- }).then(function(evt) {
- assert_equals(evt.elapsedTime, 100.0);
- });
-}, 'Active -> After');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- // Seek to After phase.
- transition.finish();
- return watcher.wait_for([ 'transitionrun',
- 'transitionstart',
- 'transitionend' ]).then(function(evt) {
- // Seek to Before phase.
- transition.currentTime = 0;
- return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
- }).then(function(evt) {
- assert_equals(handler.transitionstart, 100.0);
- assert_equals(handler.transitionend, 0.0);
- });
-}, 'After -> Before');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
- // Seek to After phase.
- transition.finish();
- return watcher.wait_for([ 'transitionrun',
- 'transitionstart',
- 'transitionend' ]).then(function(evt) {
- // Seek to Active phase.
- transition.currentTime = 100 * MS_PER_SEC;
- return watcher.wait_for('transitionstart');
- }).then(function(evt) {
- assert_equals(evt.elapsedTime, 100.0);
- });
-}, 'After -> Active');
-
-promise_test(function(t) {
- var [transition, watcher, handler] =
- setupTransition(t, 'transition: margin-left 100s -50s');
-
- return watcher.wait_for([ 'transitionrun',
- 'transitionstart' ]).then(function() {
- assert_equals(handler.transitionrun, 50.0);
- assert_equals(handler.transitionstart, 50.0);
- transition.finish();
- return watcher.wait_for('transitionend');
- }).then(function(evt) {
- assert_equals(evt.elapsedTime, 100.0);
- });
-}, 'Calculating the interval start and end time with negative start delay.');
-
-promise_test(function(t) {
- var [transition, watcher, handler] = setupTransition(t);
-
- return watcher.wait_for('transitionrun').then(function(evt) {
- // We can't set the end delay via generated effect timing.
- // Because CSS-Transition use the AnimationEffectTimingReadOnly.
- transition.effect = new KeyframeEffect(handler.target,
- { marginleft: [ '0px', '100px' ]},
- { duration: 100 * MS_PER_SEC,
- endDelay: -50 * MS_PER_SEC });
- // Seek to Before and play.
- transition.cancel();
- transition.play();
- return watcher.wait_for('transitionstart');
- }).then(function() {
- assert_equals(handler.transitionstart, 0.0);
-
- // Seek to After phase.
- transition.finish();
- return watcher.wait_for('transitionend');
- }).then(function(evt) {
- assert_equals(evt.elapsedTime, 50.0);
- });
-}, 'Calculating the interval start and end time with negative end delay.');
-
-done();
-</script>
-</body>
-</html>
diff --git a/dom/animation/test/css-transitions/file_event-dispatch.html b/dom/animation/test/css-transitions/file_event-dispatch.html
new file mode 100644
index 000000000..7140cda36
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_event-dispatch.html
@@ -0,0 +1,474 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Tests for CSS-Transition events</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#transition-events">
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+/**
+ * Helper class to record the elapsedTime member of each event.
+ * The EventWatcher class in testharness.js allows us to wait on
+ * multiple events in a certain order but only records the event
+ * parameters of the most recent event.
+ */
+function TransitionEventHandler(target) {
+ this.target = target;
+ this.target.ontransitionrun = function(evt) {
+ this.transitionrun = evt.elapsedTime;
+ }.bind(this);
+ this.target.ontransitionstart = function(evt) {
+ this.transitionstart = evt.elapsedTime;
+ }.bind(this);
+ this.target.ontransitionend = function(evt) {
+ this.transitionend = evt.elapsedTime;
+ }.bind(this);
+ this.target.ontransitioncancel = function(evt) {
+ this.transitioncancel = evt.elapsedTime;
+ }.bind(this);
+}
+
+TransitionEventHandler.prototype.clear = function() {
+ this.transitionrun = undefined;
+ this.transitionstart = undefined;
+ this.transitionend = undefined;
+ this.transitioncancel = undefined;
+};
+
+function setupTransition(t, transitionStyle) {
+ var div = addDiv(t, { style: 'transition: ' + transitionStyle });
+ var watcher = new EventWatcher(t, div, [ 'transitionrun',
+ 'transitionstart',
+ 'transitionend',
+ 'transitioncancel' ]);
+ flushComputedStyle(div);
+
+ div.style.marginLeft = '100px';
+ var transition = div.getAnimations()[0];
+
+ return [transition, watcher, div];
+}
+
+// On the next frame (i.e. when events are queued), whether or not the
+// transition is still pending depends on the implementation.
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Idle -> Pending or Before');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // Force the transition to leave the idle phase
+ transition.startTime = document.timeline.currentTime;
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Idle -> Before');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ transition.pause();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ assert_equals(handler.transitionrun, 0.0);
+ assert_equals(handler.transitionstart, 0.0);
+ });
+}, 'Idle or Pending -> Active');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart',
+ 'transitionend' ]).then(function(evt) {
+ assert_equals(handler.transitionrun, 0.0);
+ assert_equals(handler.transitionstart, 0.0);
+ assert_equals(handler.transitionend, 100.0);
+ });
+}, 'Idle or Pending -> After');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Before -> Idle (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ // Make idle
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Before -> Idle (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('transitionstart');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Before -> Active');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ // Seek to After phase.
+ transition.currentTime = 200 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
+ }).then(function(evt) {
+ assert_equals(handler.transitionstart, 0.0);
+ assert_equals(handler.transitionend, 100.0);
+ });
+}, 'Before -> After');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s');
+
+ // Seek to Active start position.
+ transition.pause();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, no delay (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.currentTime = 0;
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, no delay (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // Pause so the currentTime is fixed and we can accurately compare the event
+ // time in transition cancel events.
+ transition.pause();
+
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, with positive delay (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.currentTime = 100 * MS_PER_SEC;
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, with positive delay (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s -50s');
+
+ // Pause so the currentTime is fixed and we can accurately compare the event
+ // time in transition cancel events.
+ transition.pause();
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 50.0);
+ });
+}, 'Active -> Idle, with negative delay (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s -50s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.currentTime = 50 * MS_PER_SEC;
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, with negative delay (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Seek to Before phase.
+ transition.currentTime = 0;
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Before');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Seek to After phase.
+ transition.currentTime = 200 * MS_PER_SEC;
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'Active -> After');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart',
+ 'transitionend' ]).then(function(evt) {
+ // Seek to Before phase.
+ transition.currentTime = 0;
+ return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
+ }).then(function(evt) {
+ assert_equals(handler.transitionstart, 100.0);
+ assert_equals(handler.transitionend, 0.0);
+ });
+}, 'After -> Before');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart',
+ 'transitionend' ]).then(function(evt) {
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('transitionstart');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'After -> Active');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s -50s');
+ var handler = new TransitionEventHandler(div);
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function() {
+ assert_equals(handler.transitionrun, 50.0);
+ assert_equals(handler.transitionstart, 50.0);
+ transition.finish();
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'Calculating the interval start and end time with negative start delay.');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ // We can't set the end delay via generated effect timing.
+ // Because CSS-Transition use the AnimationEffectTimingReadOnly.
+ transition.effect = new KeyframeEffect(div,
+ { marginleft: [ '0px', '100px' ]},
+ { duration: 100 * MS_PER_SEC,
+ endDelay: -50 * MS_PER_SEC });
+ // Seek to Before and play.
+ transition.cancel();
+ transition.play();
+ return watcher.wait_for([ 'transitioncancel',
+ 'transitionrun',
+ 'transitionstart' ]);
+ }).then(function() {
+ assert_equals(handler.transitionstart, 0.0);
+
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 50.0);
+ });
+}, 'Calculating the interval start and end time with negative end delay.');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return watcher.wait_for('transitionrun').then(function() {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function() {
+ transition.cancel();
+ // Then wait a couple of frames and check that no event was dispatched
+ return waitForAnimationFrames(2);
+ });
+}, 'Call Animation.cancel after cancelling transition.');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ transition.play();
+ watcher.wait_for([ 'transitioncancel',
+ 'transitionrun',
+ 'transitionstart' ]);
+ });
+}, 'Restart transition after cancelling transition immediately');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ transition.play();
+ transition.cancel();
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ // Then wait a couple of frames and check that no event was dispatched
+ return waitForAnimationFrames(2);
+ });
+}, 'Call Animation.cancel after restarting transition immediately');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ transition.timeline = document.timeline;
+ transition.play();
+
+ return watcher.wait_for(['transitionrun', 'transitionstart']);
+ });
+}, 'Set timeline and play transition after clear the timeline');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function() {
+ transition.cancel();
+ return watcher.wait_for('transitioncancel');
+ }).then(function() {
+ // Make After phase
+ transition.effect = null;
+
+ // Then wait a couple of frames and check that no event was dispatched
+ return waitForAnimationFrames(2);
+ });
+}, 'Set null target effect after cancel the transition');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ transition.effect = null;
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ transition.cancel();
+ return watcher.wait_for('transitioncancel');
+ });
+}, 'Cancel the transition after clearing the target effect');
+
+done();
+</script>
+</body>
+</html>
diff --git a/dom/animation/test/css-transitions/file_setting-effect.html b/dom/animation/test/css-transitions/file_setting-effect.html
index c61877194..81279ea55 100644
--- a/dom/animation/test/css-transitions/file_setting-effect.html
+++ b/dom/animation/test/css-transitions/file_setting-effect.html
@@ -7,6 +7,8 @@
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.transition = 'left 100s';
@@ -20,11 +22,14 @@ promise_test(function(t) {
assert_equals(transition.transitionProperty, 'left');
assert_equals(transition.playState, 'finished');
assert_equals(window.getComputedStyle(div).left, '100px');
+ return watcher.wait_for('transitionend');
});
}, 'Test for removing a transition effect');
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.transition = 'left 100s';
@@ -46,6 +51,8 @@ promise_test(function(t) {
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.width = '0px';
@@ -65,6 +72,8 @@ promise_test(function(t) {
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.width = '0px';
diff --git a/dom/animation/test/css-transitions/test_event-dispatch.html b/dom/animation/test/css-transitions/test_event-dispatch.html
new file mode 100644
index 000000000..c90431cd1
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_event-dispatch.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_event-dispatch.html");
+ });
+</script>
diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini
index feb424518..db6dffada 100644
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -19,6 +19,8 @@ support-files =
css-animations/file_document-get-animations.html
css-animations/file_effect-target.html
css-animations/file_element-get-animations.html
+ css-animations/file_event-dispatch.html
+ css-animations/file_event-order.html
css-animations/file_keyframeeffect-getkeyframes.html
css-animations/file_pseudoElement-get-animations.html
css-transitions/file_animation-cancel.html
@@ -32,6 +34,7 @@ support-files =
css-transitions/file_document-get-animations.html
css-transitions/file_effect-target.html
css-transitions/file_element-get-animations.html
+ css-transitions/file_event-dispatch.html
css-transitions/file_keyframeeffect-getkeyframes.html
css-transitions/file_pseudoElement-get-animations.html
css-transitions/file_setting-effect.html
@@ -72,6 +75,8 @@ support-files =
[css-animations/test_document-get-animations.html]
[css-animations/test_effect-target.html]
[css-animations/test_element-get-animations.html]
+[css-animations/test_event-dispatch.html]
+[css-animations/test_event-order.html]
[css-animations/test_keyframeeffect-getkeyframes.html]
[css-animations/test_pseudoElement-get-animations.html]
[css-transitions/test_animation-cancel.html]
@@ -85,6 +90,7 @@ support-files =
[css-transitions/test_document-get-animations.html]
[css-transitions/test_effect-target.html]
[css-transitions/test_element-get-animations.html]
+[css-transitions/test_event-dispatch.html]
[css-transitions/test_keyframeeffect-getkeyframes.html]
[css-transitions/test_pseudoElement-get-animations.html]
[css-transitions/test_setting-effect.html]
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index ef87a250e..34c7d23b8 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -281,6 +281,7 @@ bool nsContentUtils::sIsCutCopyAllowed = true;
bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
bool nsContentUtils::sIsPerformanceTimingEnabled = false;
bool nsContentUtils::sIsResourceTimingEnabled = false;
+bool nsContentUtils::sIsPerformanceNavigationTimingEnabled = false;
bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
bool nsContentUtils::sEncodeDecodeURLHash = false;
@@ -571,6 +572,9 @@ nsContentUtils::Init()
Preferences::AddBoolVarCache(&sIsResourceTimingEnabled,
"dom.enable_resource_timing", true);
+ Preferences::AddBoolVarCache(&sIsPerformanceNavigationTimingEnabled,
+ "dom.enable_performance_navigation_timing", true);
+
Preferences::AddBoolVarCache(&sIsUserTimingLoggingEnabled,
"dom.performance.enable_user_timing_logging", false);
@@ -5100,7 +5104,7 @@ nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
handler->OnLinkClick(aContent, aLinkURI,
fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
- fileName, nullptr, nullptr, aIsTrusted);
+ fileName, nullptr, nullptr, aIsTrusted, aContent->NodePrincipal());
}
}
@@ -9772,9 +9776,13 @@ nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel)
rv = aChannel->GetReferrer(getter_AddRefs(referrer));
NS_ENSURE_SUCCESS(rv, false);
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
+
// Actually perform the cross process load
bool reloadSucceeded = false;
- rv = wbc3->ReloadInFreshProcess(docShell, uri, referrer, &reloadSucceeded);
+ rv = wbc3->ReloadInFreshProcess(docShell, uri, referrer,
+ triggeringPrincipal, &reloadSucceeded);
NS_ENSURE_SUCCESS(rv, false);
return reloadSucceeded;
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 0932f451e..9ae6d2155 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2033,6 +2033,14 @@ public:
}
/*
+ * Returns true if the performance timing APIs are enabled.
+ */
+ static bool IsPerformanceNavigationTimingEnabled()
+ {
+ return sIsPerformanceNavigationTimingEnabled;
+ }
+
+ /*
* Returns true if notification should be sent for peformance timing events.
*/
static bool SendPerformanceTimingNotifications()
@@ -2825,6 +2833,7 @@ private:
static uint32_t sHandlingInputTimeout;
static bool sIsPerformanceTimingEnabled;
static bool sIsResourceTimingEnabled;
+ static bool sIsPerformanceNavigationTimingEnabled;
static bool sIsUserTimingLoggingEnabled;
static bool sIsFrameTimingPrefEnabled;
static bool sIsExperimentalAutocompleteEnabled;
diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp
index 31b2932fb..32ce8a8cb 100644
--- a/dom/base/nsDOMNavigationTiming.cpp
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -15,6 +15,9 @@
#include "nsPrintfCString.h"
#include "mozilla/dom/PerformanceNavigation.h"
#include "mozilla/TimeStamp.h"
+#include "mozilla/Telemetry.h"
+
+using namespace mozilla;
nsDOMNavigationTiming::nsDOMNavigationTiming()
{
@@ -30,47 +33,36 @@ nsDOMNavigationTiming::Clear()
{
mNavigationType = TYPE_RESERVED;
mNavigationStartHighRes = 0;
- mBeforeUnloadStart = 0;
- mUnloadStart = 0;
- mUnloadEnd = 0;
- mLoadEventStart = 0;
- mLoadEventEnd = 0;
- mDOMLoading = 0;
- mDOMInteractive = 0;
- mDOMContentLoadedEventStart = 0;
- mDOMContentLoadedEventEnd = 0;
- mDOMComplete = 0;
-
- mLoadEventStartSet = false;
- mLoadEventEndSet = false;
- mDOMLoadingSet = false;
- mDOMInteractiveSet = false;
- mDOMContentLoadedEventStartSet = false;
- mDOMContentLoadedEventEndSet = false;
- mDOMCompleteSet = false;
+ mBeforeUnloadStart = TimeStamp();
+ mUnloadStart = TimeStamp();
+ mUnloadEnd = TimeStamp();
+ mLoadEventStart = TimeStamp();
+ mLoadEventEnd = TimeStamp();
+ mDOMLoading = TimeStamp();
+ mDOMInteractive = TimeStamp();
+ mDOMContentLoadedEventStart = TimeStamp();
+ mDOMContentLoadedEventEnd = TimeStamp();
+ mDOMComplete = TimeStamp();
+
mDocShellHasBeenActiveSinceNavigationStart = false;
}
DOMTimeMilliSec
-nsDOMNavigationTiming::TimeStampToDOM(mozilla::TimeStamp aStamp) const
+nsDOMNavigationTiming::TimeStampToDOM(TimeStamp aStamp) const
{
if (aStamp.IsNull()) {
return 0;
}
- mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp;
- return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
-}
-DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart()
-{
- return TimeStampToDOM(mozilla::TimeStamp::Now());
+ TimeDuration duration = aStamp - mNavigationStart;
+ return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
}
void
nsDOMNavigationTiming::NotifyNavigationStart(DocShellState aDocShellState)
{
mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
- mNavigationStartTimeStamp = mozilla::TimeStamp::Now();
+ mNavigationStart = TimeStamp::Now();
mDocShellHasBeenActiveSinceNavigationStart = (aDocShellState == DocShellState::eActive);
}
@@ -86,7 +78,7 @@ nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, Type aNavigationType)
void
nsDOMNavigationTiming::NotifyBeforeUnload()
{
- mBeforeUnloadStart = DurationFromStart();
+ mBeforeUnloadStart = TimeStamp::Now();
}
void
@@ -99,105 +91,107 @@ nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI)
void
nsDOMNavigationTiming::NotifyUnloadEventStart()
{
- mUnloadStart = DurationFromStart();
+ mUnloadStart = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyUnloadEventEnd()
{
- mUnloadEnd = DurationFromStart();
+ mUnloadEnd = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyLoadEventStart()
{
- if (!mLoadEventStartSet) {
- mLoadEventStart = DurationFromStart();
- mLoadEventStartSet = true;
+ if (!mLoadEventStart.IsNull()) {
+ return;
}
+ mLoadEventStart = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyLoadEventEnd()
{
- if (!mLoadEventEndSet) {
- mLoadEventEnd = DurationFromStart();
- mLoadEventEndSet = true;
+ if (!mLoadEventEnd.IsNull()) {
+ return;
}
+ mLoadEventEnd = TimeStamp::Now();
}
void
-nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue)
+nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, TimeStamp aValue)
{
- if (!mDOMLoadingSet) {
- mLoadedURI = aURI;
- mDOMLoading = TimeStampToDOM(aValue);
- mDOMLoadingSet = true;
+ if (!mDOMLoading.IsNull()) {
+ return;
}
+ mLoadedURI = aURI;
+ mDOMLoading = aValue;
}
void
nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI)
{
- if (!mDOMLoadingSet) {
- mLoadedURI = aURI;
- mDOMLoading = DurationFromStart();
- mDOMLoadingSet = true;
+ if (!mDOMLoading.IsNull()) {
+ return;
}
+ mLoadedURI = aURI;
+ mDOMLoading = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI)
{
- if (!mDOMInteractiveSet) {
- mLoadedURI = aURI;
- mDOMInteractive = DurationFromStart();
- mDOMInteractiveSet = true;
+ if (!mDOMInteractive.IsNull()) {
+ return;
}
+ mLoadedURI = aURI;
+ mDOMInteractive = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI)
{
- if (!mDOMCompleteSet) {
- mLoadedURI = aURI;
- mDOMComplete = DurationFromStart();
- mDOMCompleteSet = true;
+ if (!mDOMComplete.IsNull()) {
+ return;
}
+ mLoadedURI = aURI;
+ mDOMComplete = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI)
{
- if (!mDOMContentLoadedEventStartSet) {
- mLoadedURI = aURI;
- mDOMContentLoadedEventStart = DurationFromStart();
- mDOMContentLoadedEventStartSet = true;
+ if (!mDOMContentLoadedEventStart.IsNull()) {
+ return;
}
+
+ mLoadedURI = aURI;
+ mDOMContentLoadedEventStart = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI)
{
- if (!mDOMContentLoadedEventEndSet) {
- mLoadedURI = aURI;
- mDOMContentLoadedEventEnd = DurationFromStart();
- mDOMContentLoadedEventEndSet = true;
+ if (!mDOMContentLoadedEventEnd.IsNull()) {
+ return;
}
+
+ mLoadedURI = aURI;
+ mDOMContentLoadedEventEnd = TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument()
{
MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(!mNavigationStartTimeStamp.IsNull());
+ MOZ_ASSERT(!mNavigationStart.IsNull());
- if (!mNonBlankPaintTimeStamp.IsNull()) {
+ if (!mNonBlankPaint.IsNull()) {
return;
}
- mNonBlankPaintTimeStamp = TimeStamp::Now();
- TimeDuration elapsed = mNonBlankPaintTimeStamp - mNavigationStartTimeStamp;
+ mNonBlankPaint = TimeStamp::Now();
+ TimeDuration elapsed = mNonBlankPaint - mNavigationStart;
if (profiler_is_active()) {
nsAutoCString spec;
@@ -212,8 +206,8 @@ nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument()
if (mDocShellHasBeenActiveSinceNavigationStart) {
Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_MS,
- mNavigationStartTimeStamp,
- mNonBlankPaintTimeStamp);
+ mNavigationStart,
+ mNonBlankPaint);
}
}
@@ -224,24 +218,24 @@ nsDOMNavigationTiming::NotifyDocShellStateChanged(DocShellState aDocShellState)
(aDocShellState == DocShellState::eActive);
}
-DOMTimeMilliSec
-nsDOMNavigationTiming::GetUnloadEventStart()
+mozilla::TimeStamp
+nsDOMNavigationTiming::GetUnloadEventStartTimeStamp() const
{
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
if (NS_SUCCEEDED(rv)) {
return mUnloadStart;
}
- return 0;
+ return mozilla::TimeStamp();
}
-DOMTimeMilliSec
-nsDOMNavigationTiming::GetUnloadEventEnd()
+mozilla::TimeStamp
+nsDOMNavigationTiming::GetUnloadEventEndTimeStamp() const
{
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
if (NS_SUCCEEDED(rv)) {
return mUnloadEnd;
}
- return 0;
+ return mozilla::TimeStamp();
}
diff --git a/dom/base/nsDOMNavigationTiming.h b/dom/base/nsDOMNavigationTiming.h
index 9babece96..3be2527ca 100644
--- a/dom/base/nsDOMNavigationTiming.h
+++ b/dom/base/nsDOMNavigationTiming.h
@@ -47,38 +47,91 @@ public:
mozilla::TimeStamp GetNavigationStartTimeStamp() const
{
- return mNavigationStartTimeStamp;
+ return mNavigationStart;
+ }
+
+ DOMTimeMilliSec GetUnloadEventStart()
+ {
+ return TimeStampToDOM(GetUnloadEventStartTimeStamp());
+ }
+
+ DOMTimeMilliSec GetUnloadEventEnd()
+ {
+ return TimeStampToDOM(GetUnloadEventEndTimeStamp());
}
- DOMTimeMilliSec GetUnloadEventStart();
- DOMTimeMilliSec GetUnloadEventEnd();
DOMTimeMilliSec GetDomLoading() const
{
- return mDOMLoading;
+ return TimeStampToDOM(mDOMLoading);
}
DOMTimeMilliSec GetDomInteractive() const
{
- return mDOMInteractive;
+ return TimeStampToDOM(mDOMInteractive);
}
DOMTimeMilliSec GetDomContentLoadedEventStart() const
{
- return mDOMContentLoadedEventStart;
+ return TimeStampToDOM(mDOMContentLoadedEventStart);
}
DOMTimeMilliSec GetDomContentLoadedEventEnd() const
{
- return mDOMContentLoadedEventEnd;
+ return TimeStampToDOM(mDOMContentLoadedEventEnd);
}
DOMTimeMilliSec GetDomComplete() const
{
- return mDOMComplete;
+ return TimeStampToDOM(mDOMComplete);
}
DOMTimeMilliSec GetLoadEventStart() const
{
- return mLoadEventStart;
+ return TimeStampToDOM(mLoadEventStart);
}
DOMTimeMilliSec GetLoadEventEnd() const
{
- return mLoadEventEnd;
+ return TimeStampToDOM(mLoadEventEnd);
+ }
+ DOMTimeMilliSec GetTimeToNonBlankPaint() const
+ {
+ return TimeStampToDOM(mNonBlankPaint);
+ }
+
+ DOMHighResTimeStamp GetUnloadEventStartHighRes()
+ {
+ mozilla::TimeStamp stamp = GetUnloadEventStartTimeStamp();
+ if (stamp.IsNull()) {
+ return 0;
+ }
+ return TimeStampToDOMHighRes(stamp);
+ }
+ DOMHighResTimeStamp GetUnloadEventEndHighRes()
+ {
+ mozilla::TimeStamp stamp = GetUnloadEventEndTimeStamp();
+ if (stamp.IsNull()) {
+ return 0;
+ }
+ return TimeStampToDOMHighRes(stamp);
+ }
+ DOMHighResTimeStamp GetDomInteractiveHighRes() const
+ {
+ return TimeStampToDOMHighRes(mDOMInteractive);
+ }
+ DOMHighResTimeStamp GetDomContentLoadedEventStartHighRes() const
+ {
+ return TimeStampToDOMHighRes(mDOMContentLoadedEventStart);
+ }
+ DOMHighResTimeStamp GetDomContentLoadedEventEndHighRes() const
+ {
+ return TimeStampToDOMHighRes(mDOMContentLoadedEventEnd);
+ }
+ DOMHighResTimeStamp GetDomCompleteHighRes() const
+ {
+ return TimeStampToDOMHighRes(mDOMComplete);
+ }
+ DOMHighResTimeStamp GetLoadEventStartHighRes() const
+ {
+ return TimeStampToDOMHighRes(mLoadEventStart);
+ }
+ DOMHighResTimeStamp GetLoadEventEndHighRes() const
+ {
+ return TimeStampToDOMHighRes(mLoadEventEnd);
}
enum class DocShellState : uint8_t {
@@ -108,9 +161,13 @@ public:
DOMTimeMilliSec TimeStampToDOM(mozilla::TimeStamp aStamp) const;
- inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp)
+ inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp) const
{
- mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp;
+ MOZ_ASSERT(!aStamp.IsNull(), "The timestamp should not be null");
+ if (aStamp.IsNull()) {
+ return 0;
+ }
+ mozilla::TimeDuration duration = aStamp - mNavigationStart;
return duration.ToMilliseconds();
}
@@ -120,37 +177,29 @@ private:
void Clear();
+ mozilla::TimeStamp GetUnloadEventStartTimeStamp() const;
+ mozilla::TimeStamp GetUnloadEventEndTimeStamp() const;
+
nsCOMPtr<nsIURI> mUnloadedURI;
nsCOMPtr<nsIURI> mLoadedURI;
Type mNavigationType;
DOMHighResTimeStamp mNavigationStartHighRes;
- mozilla::TimeStamp mNavigationStartTimeStamp;
- mozilla::TimeStamp mNonBlankPaintTimeStamp;
- DOMTimeMilliSec DurationFromStart();
-
- DOMTimeMilliSec mBeforeUnloadStart;
- DOMTimeMilliSec mUnloadStart;
- DOMTimeMilliSec mUnloadEnd;
- DOMTimeMilliSec mLoadEventStart;
- DOMTimeMilliSec mLoadEventEnd;
-
- DOMTimeMilliSec mDOMLoading;
- DOMTimeMilliSec mDOMInteractive;
- DOMTimeMilliSec mDOMContentLoadedEventStart;
- DOMTimeMilliSec mDOMContentLoadedEventEnd;
- DOMTimeMilliSec mDOMComplete;
-
- // Booleans to keep track of what things we've already been notified
- // about. We don't update those once we've been notified about them
- // once.
- bool mLoadEventStartSet : 1;
- bool mLoadEventEndSet : 1;
- bool mDOMLoadingSet : 1;
- bool mDOMInteractiveSet : 1;
- bool mDOMContentLoadedEventStartSet : 1;
- bool mDOMContentLoadedEventEndSet : 1;
- bool mDOMCompleteSet : 1;
+ mozilla::TimeStamp mNavigationStart;
+ mozilla::TimeStamp mNonBlankPaint;
+
+ mozilla::TimeStamp mBeforeUnloadStart;
+ mozilla::TimeStamp mUnloadStart;
+ mozilla::TimeStamp mUnloadEnd;
+ mozilla::TimeStamp mLoadEventStart;
+ mozilla::TimeStamp mLoadEventEnd;
+
+ mozilla::TimeStamp mDOMLoading;
+ mozilla::TimeStamp mDOMInteractive;
+ mozilla::TimeStamp mDOMContentLoadedEventStart;
+ mozilla::TimeStamp mDOMContentLoadedEventEnd;
+ mozilla::TimeStamp mDOMComplete;
+
bool mDocShellHasBeenActiveSinceNavigationStart : 1;
};
diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h
index e4ae7ede8..50b4449ec 100644
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -694,6 +694,7 @@ GK_ATOM(onadapterremoved, "onadapterremoved")
GK_ATOM(onafterprint, "onafterprint")
GK_ATOM(onafterscriptexecute, "onafterscriptexecute")
GK_ATOM(onalerting, "onalerting")
+GK_ATOM(onanimationcancel, "onanimationcancel")
GK_ATOM(onanimationend, "onanimationend")
GK_ATOM(onanimationiteration, "onanimationiteration")
GK_ATOM(onanimationstart, "onanimationstart")
@@ -704,6 +705,7 @@ GK_ATOM(onattributechanged, "onattributechanged")
GK_ATOM(onattributereadreq, "onattributereadreq")
GK_ATOM(onattributewritereq, "onattributewritereq")
GK_ATOM(onaudioprocess, "onaudioprocess")
+GK_ATOM(onauxclick, "onauxclick")
GK_ATOM(onbeforecopy, "onbeforecopy")
GK_ATOM(onbeforecut, "onbeforecut")
GK_ATOM(onbeforepaste, "onbeforepaste")
@@ -941,6 +943,7 @@ GK_ATOM(ontouchstart, "ontouchstart")
GK_ATOM(ontouchend, "ontouchend")
GK_ATOM(ontouchmove, "ontouchmove")
GK_ATOM(ontouchcancel, "ontouchcancel")
+GK_ATOM(ontransitioncancel, "ontransitioncancel")
GK_ATOM(ontransitionend, "ontransitionend")
GK_ATOM(ontransitionrun, "ontransitionrun")
GK_ATOM(ontransitionstart, "ontransitionstart")
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index 3d5c44a78..4ffccde9d 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2521,8 +2521,13 @@ nsGlobalWindow::WouldReuseInnerWindow(nsIDocument* aNewDocument)
return false;
}
- NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()),
- "How'd this happen?");
+#ifdef DEBUG
+{
+ nsCOMPtr<nsIURI> uri;
+ mDoc->GetDocumentURI()->CloneIgnoringRef(getter_AddRefs(uri));
+ NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
+}
+#endif
// Great, we're the original document, check for one of the other
// conditions.
@@ -3124,8 +3129,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
newInnerWindow->mPerformance =
Performance::CreateForMainThread(newInnerWindow->AsInner(),
currentInner->mPerformance->GetDOMTiming(),
- currentInner->mPerformance->GetChannel(),
- currentInner->mPerformance->GetParentPerformance());
+ currentInner->mPerformance->GetChannel());
}
}
@@ -4339,22 +4343,7 @@ nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded()
timedChannel = nullptr;
}
if (timing) {
- // If we are dealing with an iframe, we will need the parent's performance
- // object (so we can add the iframe as a resource of that page).
- Performance* parentPerformance = nullptr;
- nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetScriptableParentOrNull();
- if (parentWindow) {
- nsPIDOMWindowInner* parentInnerWindow = nullptr;
- if (parentWindow) {
- parentInnerWindow = parentWindow->GetCurrentInnerWindow();
- }
- if (parentInnerWindow) {
- parentPerformance = parentInnerWindow->GetPerformance();
- }
- }
- mPerformance =
- Performance::CreateForMainThread(this, timing, timedChannel,
- parentPerformance);
+ mPerformance = Performance::CreateForMainThread(this, timing, timedChannel);
}
}
diff --git a/dom/base/nsISelectionPrivate.idl b/dom/base/nsISelectionPrivate.idl
index 68412885e..049873b28 100644
--- a/dom/base/nsISelectionPrivate.idl
+++ b/dom/base/nsISelectionPrivate.idl
@@ -29,7 +29,7 @@ native nsDirection(nsDirection);
native ScrollAxis(nsIPresShell::ScrollAxis);
[scriptable, builtinclass, uuid(0c9f4f74-ee7e-4fe9-be6b-0ba856368178)]
-interface nsISelectionPrivate : nsISelection
+interface nsISelectionPrivate : nsISupports
{
const short ENDOFPRECEDINGLINE=0;
const short STARTOFNEXTLINE=1;
diff --git a/dom/base/test/file_simplecontentpolicy.js b/dom/base/test/file_simplecontentpolicy.js
index 1f9606c49..2727b9530 100644
--- a/dom/base/test/file_simplecontentpolicy.js
+++ b/dom/base/test/file_simplecontentpolicy.js
@@ -39,7 +39,6 @@ var policy = {
{
// Remember last content type seen for the test url
if (contentLocation.spec.endsWith(urlSuffix)) {
- assert.ok(frame === browserElement, "correct <browser> element");
sendAsyncMessage("shouldLoad", {contentType: contentType, isTopLevel: isTopLevel});
return Ci.nsIContentPolicy.REJECT_REQUEST;
}
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index 33f5f7a44..7056658a7 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -259,8 +259,8 @@ TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx, JS::Handle<JS::Valu
// Make sure mJSException is initialized _before_ we try to root it. But
// don't set it to exn yet, because we don't want to do that until after we
// root.
- mJSException.setUndefined();
- if (!js::AddRawValueRoot(cx, &mJSException, "TErrorResult::mJSException")) {
+ mJSException.asValueRef().setUndefined();
+ if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) {
// Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
// in fact rooted mJSException.
mResult = NS_ERROR_OUT_OF_MEMORY;
@@ -289,7 +289,7 @@ TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx)
mJSException = exception;
// If JS_WrapValue failed, not much we can do about it... No matter
// what, go ahead and unroot mJSException.
- js::RemoveRawValueRoot(cx, &mJSException);
+ js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
mResult = NS_OK;
#ifdef DEBUG
@@ -395,8 +395,8 @@ TErrorResult<CleanupPolicy>::ClearUnionData()
if (IsJSException()) {
JSContext* cx = dom::danger::GetJSContext();
MOZ_ASSERT(cx);
- mJSException.setUndefined();
- js::RemoveRawValueRoot(cx, &mJSException);
+ mJSException.asValueRef().setUndefined();
+ js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
#ifdef DEBUG
mUnionState = HasNothing;
#endif // DEBUG
@@ -439,13 +439,13 @@ TErrorResult<CleanupPolicy>::operator=(TErrorResult<CleanupPolicy>&& aRHS)
} else if (aRHS.IsJSException()) {
JSContext* cx = dom::danger::GetJSContext();
MOZ_ASSERT(cx);
- mJSException.setUndefined();
- if (!js::AddRawValueRoot(cx, &mJSException, "TErrorResult::mJSException")) {
+ mJSException.asValueRef().setUndefined();
+ if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) {
MOZ_CRASH("Could not root mJSException, we're about to OOM");
}
mJSException = aRHS.mJSException;
- aRHS.mJSException.setUndefined();
- js::RemoveRawValueRoot(cx, &aRHS.mJSException);
+ aRHS.mJSException.asValueRef().setUndefined();
+ js::RemoveRawValueRoot(cx, &aRHS.mJSException.asValueRef());
} else if (aRHS.IsDOMException()) {
mDOMExceptionInfo = aRHS.mDOMExceptionInfo;
aRHS.mDOMExceptionInfo = nullptr;
@@ -497,7 +497,7 @@ TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const
aRv.mUnionState = HasJSException;
#endif
JSContext* cx = dom::danger::GetJSContext();
- JS::Rooted<JS::Value> exception(cx, mJSException);
+ JS::Rooted<JS::Value> exception(cx, mJSException.asValueRef());
aRv.ThrowJSException(cx, exception);
}
}
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
index 3174c37dd..7a6668687 100644
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1069,6 +1069,20 @@ class CGHeaders(CGWrapper):
if parent:
ancestors.append(parent)
interfaceDeps.extend(ancestors)
+
+ # Include parent interface headers needed for jsonifier code.
+ jsonInterfaceParents = []
+ for desc in descriptors:
+ if not desc.operations['Jsonifier']:
+ continue
+ parent = desc.interface.parent
+ while parent:
+ parentDesc = desc.getDescriptor(parent.identifier.name)
+ if parentDesc.operations['Jsonifier']:
+ jsonInterfaceParents.append(parentDesc.interface)
+ parent = parent.parent
+ interfaceDeps.extend(jsonInterfaceParents)
+
bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
# Grab all the implementation declaration files we need.
diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h
index c45e7ea3b..7c3fc9e2f 100644
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -461,7 +461,7 @@ private:
// (and deallocated) by SetPendingDOMException.
union {
Message* mMessage; // valid when IsErrorWithMessage()
- JS::Value mJSException; // valid when IsJSException()
+ JS::UninitializedValue mJSException; // valid when IsJSException()
DOMExceptionInfo* mDOMExceptionInfo; // valid when IsDOMException()
};
diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp
index 79e3eadc5..ff5a92167 100755
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -1336,10 +1336,7 @@ Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
- TimeDuration duration =
- mozilla::TimeStamp::Now() - workerPrivate->NowBaseTimeStamp();
-
- monotonicTimer = TimerClamping::ReduceMsTimeValue(duration.ToMilliseconds());
+ monotonicTimer = workerPrivate->TimeStampToDOMHighRes(TimeStamp::Now());
}
}
diff --git a/dom/console/Console.h b/dom/console/Console.h
index b334d79f9..2f375c8eb 100644
--- a/dom/console/Console.h
+++ b/dom/console/Console.h
@@ -258,9 +258,8 @@ private:
// the max number of timers is reached.
// * aCx - the JSContext rooting aName.
// * aName - this is (should be) the name of the timer as JS::Value.
- // * aTimestamp - the monotonicTimer for this context (taken from
- // window->performance.now() or from Now() -
- // workerPrivate->NowBaseTimeStamp() in workers.
+ // * aTimestamp - the monotonicTimer for this context taken from
+ // performance.now().
// * aTimerLabel - This label will be populated with the aName converted to a
// string.
// * aTimerValue - the StartTimer value stored into (or taken from)
@@ -290,9 +289,8 @@ private:
// the aName timer doesn't exist in the mTimerRegistry.
// * aCx - the JSContext rooting aName.
// * aName - this is (should be) the name of the timer as JS::Value.
- // * aTimestamp - the monotonicTimer for this context (taken from
- // window->performance.now() or from Now() -
- // workerPrivate->NowBaseTimeStamp() in workers.
+ // * aTimestamp - the monotonicTimer for this context taken from
+ // performance.now().
// * aTimerLabel - This label will be populated with the aName converted to a
// string.
// * aTimerDuration - the difference between aTimestamp and when the timer
diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp
index 7e19cd74d..4b9776c0a 100755
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -1146,16 +1146,11 @@ Event::TimeStampImpl() const
return perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->mTimeStamp);
}
- // For dedicated workers, we should make times relative to the navigation
- // start of the document that created the worker, which is the same as the
- // timebase for performance.now().
workers::WorkerPrivate* workerPrivate =
workers::GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
- TimeDuration duration =
- mEvent->mTimeStamp - workerPrivate->NowBaseTimeStamp();
- return duration.ToMilliseconds();
+ return workerPrivate->TimeStampToDOMHighRes(mEvent->mTimeStamp);
}
bool
diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h
index ba2427623..509863e6c 100644
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -164,6 +164,10 @@ EVENT(change,
eFormChange,
EventNameType_HTMLXUL,
eBasicEventClass)
+EVENT(auxclick,
+ eMouseAuxClick,
+ EventNameType_All,
+ eMouseEventClass)
EVENT(click,
eMouseClick,
EventNameType_All,
@@ -1003,6 +1007,10 @@ EVENT(transitionend,
eTransitionEnd,
EventNameType_All,
eTransitionEventClass)
+EVENT(transitioncancel,
+ eTransitionCancel,
+ EventNameType_All,
+ eTransitionEventClass)
EVENT(animationstart,
eAnimationStart,
EventNameType_All,
@@ -1015,6 +1023,10 @@ EVENT(animationiteration,
eAnimationIteration,
EventNameType_All,
eAnimationEventClass)
+EVENT(animationcancel,
+ eAnimationCancel,
+ EventNameType_All,
+ eAnimationEventClass)
// Webkit-prefixed versions of Transition & Animation events, for web compat:
EVENT(webkitAnimationEnd,
diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
index c6b304183..7bbfe21b7 100644
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -492,6 +492,7 @@ IsMessageMouseUserActivity(EventMessage aMessage)
return aMessage == eMouseMove ||
aMessage == eMouseUp ||
aMessage == eMouseDown ||
+ aMessage == eMouseAuxClick ||
aMessage == eMouseDoubleClick ||
aMessage == eMouseClick ||
aMessage == eMouseActivate ||
@@ -4633,6 +4634,32 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
}
nsresult
+EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+ nsEventStatus* aStatus,
+ EventMessage aMessage,
+ nsIPresShell* aPresShell,
+ nsIContent* aMouseTarget,
+ nsWeakFrame aCurrentTarget,
+ bool aNoContentDispatch)
+{
+ WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
+ aEvent->mWidget, WidgetMouseEvent::eReal);
+
+ event.mRefPoint = aEvent->mRefPoint;
+ event.mClickCount = aEvent->mClickCount;
+ event.mModifiers = aEvent->mModifiers;
+ event.buttons = aEvent->buttons;
+ event.mTime = aEvent->mTime;
+ event.mTimeStamp = aEvent->mTimeStamp;
+ event.mFlags.mNoContentDispatch = aNoContentDispatch;
+ event.button = aEvent->button;
+ event.inputSource = aEvent->inputSource;
+
+ return aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
+ aMouseTarget, aStatus);
+}
+
+nsresult
EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus)
{
@@ -4651,17 +4678,7 @@ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
(aEvent->button == WidgetMouseEvent::eMiddleButton ||
aEvent->button == WidgetMouseEvent::eRightButton);
- WidgetMouseEvent event(aEvent->IsTrusted(), eMouseClick,
- aEvent->mWidget, WidgetMouseEvent::eReal);
- event.mRefPoint = aEvent->mRefPoint;
- event.mClickCount = aEvent->mClickCount;
- event.mModifiers = aEvent->mModifiers;
- event.buttons = aEvent->buttons;
- event.mTime = aEvent->mTime;
- event.mTimeStamp = aEvent->mTimeStamp;
- event.mFlags.mNoContentDispatch = notDispatchToContents;
- event.button = aEvent->button;
- event.inputSource = aEvent->inputSource;
+ bool fireAuxClick = notDispatchToContents;
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (presShell) {
@@ -4680,23 +4697,22 @@ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
// HandleEvent clears out mCurrentTarget which we might need again
nsWeakFrame currentTarget = mCurrentTarget;
- ret = presShell->HandleEventWithTarget(&event, currentTarget,
- mouseContent, aStatus);
+ ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
+ presShell, mouseContent, currentTarget,
+ notDispatchToContents);
+
if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
mouseContent && mouseContent->IsInComposedDoc()) {
//fire double click
- WidgetMouseEvent event2(aEvent->IsTrusted(), eMouseDoubleClick,
- aEvent->mWidget, WidgetMouseEvent::eReal);
- event2.mRefPoint = aEvent->mRefPoint;
- event2.mClickCount = aEvent->mClickCount;
- event2.mModifiers = aEvent->mModifiers;
- event2.buttons = aEvent->buttons;
- event2.mFlags.mNoContentDispatch = notDispatchToContents;
- event2.button = aEvent->button;
- event2.inputSource = aEvent->inputSource;
-
- ret = presShell->HandleEventWithTarget(&event2, currentTarget,
- mouseContent, aStatus);
+ ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
+ presShell, mouseContent, currentTarget,
+ notDispatchToContents);
+ }
+ if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
+ mouseContent->IsInComposedDoc()) {
+ ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
+ presShell, mouseContent, currentTarget,
+ false);
}
}
}
diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h
index 49ecf0586..d0461e7fa 100644
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -415,6 +415,13 @@ protected:
*/
void UpdateDragDataTransfer(WidgetDragEvent* dragEvent);
+ static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+ nsEventStatus* aStatus,
+ EventMessage aMessage,
+ nsIPresShell* aPresShell,
+ nsIContent* aMouseTarget,
+ nsWeakFrame aCurrentTarget,
+ bool aNoContentDispatch);
nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus);
@@ -1046,6 +1053,7 @@ private:
#define NS_EVENT_NEEDS_FRAME(event) \
(!(event)->HasPluginActivationEventMessage() && \
(event)->mMessage != eMouseClick && \
- (event)->mMessage != eMouseDoubleClick)
+ (event)->mMessage != eMouseDoubleClick && \
+ (event)->mMessage != eMouseAuxClick)
#endif // mozilla_EventStateManager_h_
diff --git a/dom/events/WheelHandlingHelper.cpp b/dom/events/WheelHandlingHelper.cpp
index 7665ee922..81f2b6bfa 100644
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -257,6 +257,7 @@ WheelTransaction::OnEvent(WidgetEvent* aEvent)
case eMouseUp:
case eMouseDown:
case eMouseDoubleClick:
+ case eMouseAuxClick:
case eMouseClick:
case eContextMenu:
case eDrop:
diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini
index e100e60a1..0397487bb 100644
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -184,3 +184,4 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
[test_wheel_default_action.html]
[test_bug687787.html]
[test_bug1298970.html]
+[test_bug1304044.html]
diff --git a/dom/events/test/test_bug1304044.html b/dom/events/test/test_bug1304044.html
new file mode 100644
index 000000000..0911dcf73
--- /dev/null
+++ b/dom/events/test/test_bug1304044.html
@@ -0,0 +1,133 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1304044
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1304044</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ var eventsFired = [];
+ var target;
+ var eventsExpected;
+
+ function GetNodeString(node) {
+ if (node == window)
+ return "window";
+ if (node == document)
+ return "document";
+ if (node.id)
+ return node.id;
+ if (node.nodeName)
+ return node.nodeName;
+ return node;
+ }
+
+ function TargetAndListener(listener, target) {
+ this.listener = listener;
+ this.target = target;
+ }
+
+ TargetAndListener.prototype.toString = function() {
+ var targetName = GetNodeString(this.target);
+ var listenerName = GetNodeString(this.listener);
+ return "(listener: " + listenerName + ", target: " + targetName + ")";
+ }
+
+ var tests = [
+ TestAuxClickBubblesForEventListener,
+ TestAuxClickBubblesForOnAuxClick,
+ ];
+
+ function CompareEvents(evt, expected) {
+ return evt && expected && evt.listener == expected.listener &&
+ evt.target == expected.target;
+ }
+
+ function ResetEventsFired() {
+ eventsFired = [];
+ }
+
+ function ClearEventListeners() {
+ for (i in arguments) {
+ arguments[i].removeEventListener("auxclick", log_event);
+ }
+ }
+
+ function ClickTarget(tgt) {
+ synthesizeMouseAtCenter(tgt, {type : "mousedown", button: 2}, window);
+ synthesizeMouseAtCenter(tgt, {type : "mouseup", button: 2}, window);
+ }
+
+ function log_event(e) {
+ eventsFired[eventsFired.length] = new TargetAndListener(this, e.target);
+ }
+
+ function CompareEventsToExpected(expected, actual) {
+ for (var i = 0; i < expected.length || i < actual.length; i++) {
+ ok(CompareEvents(actual[i], expected[i]),
+ "Auxclick receiver's don't match: TargetAndListener " +
+ i + ": Expected: " + expected[i] + ", Actual: " + actual[i]);
+ }
+ }
+
+ function TestAuxClickBubblesForEventListener() {
+ target.addEventListener("auxclick", log_event);
+ document.addEventListener("auxclick", log_event);
+ window.addEventListener("auxclick", log_event);
+
+ ClickTarget(target)
+ CompareEventsToExpected(eventsExpected, eventsFired);
+ ResetEventsFired();
+ ClearEventListeners(target, document, window);
+ }
+
+ function TestAuxClickBubblesForOnAuxClick() {
+ target.onauxclick = log_event;
+ document.onauxclick = log_event;
+ window.onauxclick = log_event;
+
+ ClickTarget(target);
+ CompareEventsToExpected(eventsExpected, eventsFired);
+ ResetEventsFired();
+ }
+
+ function RunTests(){
+ for (var i = 0; i < tests.length; i++) {
+ tests[i]();
+ }
+ }
+
+ function Begin() {
+ target = document.getElementById("target");
+ eventsExpected = [
+ new TargetAndListener(target, target),
+ new TargetAndListener(document, target),
+ new TargetAndListener(window, target),
+ ];
+ RunTests();
+ target.remove();
+ SimpleTest.finish();
+ }
+
+ window.onload = function() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.executeSoon(Begin);
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1304044">Mozilla Bug 1304044</a>
+<p id="display">
+ <div id="target">Target</div>
+</p>
+<div id="content" style:"display:none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_eventTimeStamp.html b/dom/events/test/test_eventTimeStamp.html
index 107a21f87..056203e92 100644
--- a/dom/events/test/test_eventTimeStamp.html
+++ b/dom/events/test/test_eventTimeStamp.html
@@ -17,7 +17,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=77992
<script type="text/js-worker" id="worker-src">
// Simply returns the event timestamp
onmessage = function(evt) {
- postMessage(evt.timeStamp);
+ postMessage(evt.timeStamp + performance.timeOrigin);
}
</script>
<script type="text/js-worker" id="shared-worker-src">
@@ -25,7 +25,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=77992
onconnect = function(evt) {
var port = evt.ports[0];
port.onmessage = function(messageEvt) {
- port.postMessage(messageEvt.timeStamp);
+ port.postMessage(messageEvt.timeStamp + performance.timeOrigin);
};
};
</script>
@@ -57,9 +57,9 @@ function testRegularEvents() {
finishTests();
return;
}
- var timeBeforeEvent = window.performance.now();
- window.addEventListener("load", function(evt) {
- var timeAfterEvent = window.performance.now();
+ var timeBeforeEvent = performance.now();
+ addEventListener("load", function(evt) {
+ var timeAfterEvent = performance.now();
ok(evt.timeStamp >= timeBeforeEvent &&
evt.timeStamp <= timeAfterEvent,
"Event timestamp (" + evt.timeStamp + ") is in expected range: (" +
@@ -71,19 +71,18 @@ function testRegularEvents() {
function testWorkerEvents() {
var blob = new Blob([ document.getElementById("worker-src").textContent ],
{ type: "text/javascript" });
- var worker = new Worker(window.URL.createObjectURL(blob));
+ var worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = function(evt) {
- var timeAfterEvent = window.performance.now();
- // Comparing times across timelines may break now
- // ok(evt.data >= timeBeforeEvent &&
- // evt.data <= timeAfterEvent,
+ var timeAfterEvent = performance.now() + performance.timeOrigin;
+ ok(evt.data >= timeBeforeEvent &&
+ evt.data <= timeAfterEvent,
// "Event timestamp in dedicated worker (" + evt.data +
// ") is in expected range: (" +
// timeBeforeEvent + ", " + timeAfterEvent + ")");
worker.terminate();
testSharedWorkerEvents();
};
- var timeBeforeEvent = window.performance.now();
+ var timeBeforeEvent = performance.now() + performance.timeOrigin;
worker.postMessage("");
}
@@ -93,17 +92,16 @@ function testSharedWorkerEvents() {
{ type: "text/javascript" });
// Delay creation of worker slightly so it is easier to distinguish
// shared worker creation time from this document's navigation start
- window.setTimeout(function() {
- var timeBeforeWorkerCreation = window.performance.now();
- var worker = new SharedWorker(window.URL.createObjectURL(blob));
+ setTimeout(function() {
+ var timeBeforeEvent = performance.now() + performance.timeOrigin;
+ var worker = new SharedWorker(URL.createObjectURL(blob));
worker.port.onmessage = function(evt) {
- var timeAfterEvent = window.performance.now();
- // Comparing times across timelines may break now
- // ok(evt.data >= 0 &&
- // evt.data <= timeAfterEvent - timeBeforeWorkerCreation,
+ var timeAfterEvent = performance.now() + performance.timeOrigin;
+ ok(evt.data >= timeBeforeEvent &&
+ evt.data <= timeAfterEvent,
// "Event timestamp in shared worker (" + evt.data +
// ") is in expected range: (0, " +
- // (timeAfterEvent - timeBeforeWorkerCreation) + ")");
+ // timeBeforeEvent + ", " + timeAfterEvent + ")");
worker.port.close();
finishTests();
};
diff --git a/dom/events/test/test_legacy_event.html b/dom/events/test/test_legacy_event.html
index d772be106..b2105a6df 100644
--- a/dom/events/test/test_legacy_event.html
+++ b/dom/events/test/test_legacy_event.html
@@ -73,22 +73,15 @@ function triggerShortAnimation(node) {
node.style.animation = "anim1 1ms linear";
}
-// This function triggers a long animation with two iterations, which is
-// *nearly* at the end of its first iteration. It will hit the end of that
-// iteration (firing an event) almost immediately, 1ms in the future.
+// This function triggers a very short (10ms long) animation with many
+// iterations, which will cause a start event followed by an iteration event
+// on each subsequent tick, to fire.
//
-// NOTE: It's important that this animation have a *long* duration. If it were
-// short (e.g. 1ms duration), then we might jump past all its iterations in
-// a single refresh-driver tick. And if that were to happens, we'd *never* fire
-// any animationiteration events -- the CSS Animations spec says this event
-// must not be fired "...when an animationend event would fire at the same time"
-// (which would be the case in this example with a 1ms duration). So, to make
-// sure our event does fire, we use a long duration and a nearly-as-long
-// negative delay. This ensures we hit the end of the first iteration right
-// away, and that we don't risk hitting the end of the second iteration at the
-// same time.
+// NOTE: We need the many iterations since if an animation frame coincides
+// with the animation starting or ending we dispatch only the start or end
+// event and not the iteration event.
function triggerAnimationIteration(node) {
- node.style.animation = "anim1 300s -299.999s linear 2";
+ node.style.animation = "anim1 10ms linear 20000";
}
// GENERAL UTILITY FUNCTIONS
diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp
index cdb63f890..90171db10 100644
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -36,7 +36,6 @@
#include "nsIContentViewer.h"
#include "nsIXPConnect.h"
#include "nsContentUtils.h"
-#include "nsNullPrincipal.h"
#include "nsJSUtils.h"
#include "nsThreadUtils.h"
#include "nsIScriptChannel.h"
@@ -336,7 +335,7 @@ public:
NS_FORWARD_SAFE_NSIPROPERTYBAG(mPropertyBag)
NS_FORWARD_SAFE_NSIPROPERTYBAG2(mPropertyBag)
- nsresult Init(nsIURI *aURI);
+ nsresult Init(nsIURI *aURI, nsILoadInfo* aLoadInfo);
// Actually evaluate the script.
void EvaluateScript();
@@ -354,17 +353,16 @@ protected:
nsCOMPtr<nsIChannel> mStreamChannel;
nsCOMPtr<nsIPropertyBag2> mPropertyBag;
nsCOMPtr<nsIStreamListener> mListener; // Our final listener
- nsCOMPtr<nsISupports> mContext; // The context passed to AsyncOpen
nsCOMPtr<nsPIDOMWindowInner> mOriginalInnerWindow; // The inner window our load
// started against.
- // If we blocked onload on a document in AsyncOpen, this is the document we
+ // If we blocked onload on a document in AsyncOpen2, this is the document we
// did it on.
nsCOMPtr<nsIDocument> mDocumentOnloadBlockedOn;
nsresult mStatus; // Our status
nsLoadFlags mLoadFlags;
- nsLoadFlags mActualLoadFlags; // See AsyncOpen
+ nsLoadFlags mActualLoadFlags; // See AsyncOpen2
RefPtr<nsJSThunk> mIOThunk;
PopupControlState mPopupState;
@@ -404,7 +402,7 @@ nsresult nsJSChannel::StopAll()
return rv;
}
-nsresult nsJSChannel::Init(nsIURI *aURI)
+nsresult nsJSChannel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo)
{
RefPtr<nsJSURI> jsURI;
nsresult rv = aURI->QueryInterface(kJSURICID,
@@ -418,21 +416,13 @@ nsresult nsJSChannel::Init(nsIURI *aURI)
// Remember, until AsyncOpen is called, the script will not be evaluated
// and the underlying Input Stream will not be created...
nsCOMPtr<nsIChannel> channel;
-
- nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
-
- // If the resultant script evaluation actually does return a value, we
- // treat it as html.
- // The following channel is never openend, so it does not matter what
- // securityFlags we pass; let's follow the principle of least privilege.
- rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
- aURI,
- mIOThunk,
- nullPrincipal,
- nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
- nsIContentPolicy::TYPE_OTHER,
- NS_LITERAL_CSTRING("text/html"));
- if (NS_FAILED(rv)) return rv;
+ rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
+ aURI,
+ mIOThunk,
+ NS_LITERAL_CSTRING("text/html"),
+ EmptyCString(),
+ aLoadInfo);
+ NS_ENSURE_SUCCESS(rv, rv);
rv = mIOThunk->Init(aURI);
if (NS_SUCCEEDED(rv)) {
@@ -563,6 +553,7 @@ nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
"security flags in loadInfo but asyncOpen2() not called");
}
#endif
+ MOZ_RELEASE_ASSERT(!aContext, "please call AsyncOpen2()");
NS_ENSURE_ARG(aListener);
@@ -584,7 +575,6 @@ nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
}
mListener = aListener;
- mContext = aContext;
mIsActive = true;
@@ -655,7 +645,7 @@ nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
return mStatus;
}
- // We're returning success from asyncOpen(), but we didn't open a
+ // We're returning success from asyncOpen2(), but we didn't open a
// stream channel. We'll have to notify ourselves, but make sure to do
// it asynchronously.
method = &nsJSChannel::NotifyListener;
@@ -772,7 +762,7 @@ nsJSChannel::EvaluateScript()
return;
}
- mStatus = mStreamChannel->AsyncOpen(this, mContext);
+ mStatus = mStreamChannel->AsyncOpen2(this);
if (NS_SUCCEEDED(mStatus)) {
// mStreamChannel will call OnStartRequest and OnStopRequest on
// us, so we'll be sure to call them on our listener.
@@ -800,8 +790,8 @@ nsJSChannel::EvaluateScript()
void
nsJSChannel::NotifyListener()
{
- mListener->OnStartRequest(this, mContext);
- mListener->OnStopRequest(this, mContext, mStatus);
+ mListener->OnStartRequest(this, nullptr);
+ mListener->OnStopRequest(this, nullptr, mStatus);
CleanupStrongRefs();
}
@@ -810,7 +800,6 @@ void
nsJSChannel::CleanupStrongRefs()
{
mListener = nullptr;
- mContext = nullptr;
mOriginalInnerWindow = nullptr;
if (mDocumentOnloadBlockedOn) {
mDocumentOnloadBlockedOn->UnblockOnload(false);
@@ -1240,11 +1229,7 @@ nsJSProtocolHandler::NewChannel2(nsIURI* uri,
return NS_ERROR_OUT_OF_MEMORY;
}
- rv = channel->Init(uri);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // set the loadInfo on the new channel
- rv = channel->SetLoadInfo(aLoadInfo);
+ rv = channel->Init(uri, aLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_SUCCEEDED(rv)) {
diff --git a/dom/media/MediaStreamTrack.cpp b/dom/media/MediaStreamTrack.cpp
index 8ccdeb90c..75cdeb1d1 100644
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -165,11 +165,15 @@ MediaStreamTrack::Destroy()
mPrincipalHandleListener->Forget();
mPrincipalHandleListener = nullptr;
}
- for (auto l : mTrackListeners) {
- RemoveListener(l);
+ // Remove all listeners -- avoid iterating over the list we're removing from
+ const nsTArray<RefPtr<MediaStreamTrackListener>> trackListeners(mTrackListeners);
+ for (auto listener : trackListeners) {
+ RemoveListener(listener);
}
- for (auto l : mDirectTrackListeners) {
- RemoveDirectListener(l);
+ // Do the same as above for direct listeners
+ const nsTArray<RefPtr<DirectMediaStreamTrackListener>> directTrackListeners(mDirectTrackListeners);
+ for (auto listener : directTrackListeners) {
+ RemoveDirectListener(listener);
}
}
diff --git a/dom/performance/Performance.cpp b/dom/performance/Performance.cpp
index 8dc239b05..93a6b7313 100755
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -13,12 +13,14 @@
#include "PerformanceMeasure.h"
#include "PerformanceObserver.h"
#include "PerformanceResourceTiming.h"
+#include "PerformanceService.h"
#include "PerformanceWorker.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/PerformanceBinding.h"
#include "mozilla/dom/PerformanceEntryEvent.h"
#include "mozilla/dom/PerformanceNavigationBinding.h"
#include "mozilla/dom/PerformanceObserverBinding.h"
+#include "mozilla/dom/PerformanceNavigationTiming.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Preferences.h"
#include "mozilla/TimerClamping.h"
@@ -38,27 +40,6 @@ using namespace workers;
namespace {
-// Helper classes
-class MOZ_STACK_CLASS PerformanceEntryComparator final
-{
-public:
- bool Equals(const PerformanceEntry* aElem1,
- const PerformanceEntry* aElem2) const
- {
- MOZ_ASSERT(aElem1 && aElem2,
- "Trying to compare null performance entries");
- return aElem1->StartTime() == aElem2->StartTime();
- }
-
- bool LessThan(const PerformanceEntry* aElem1,
- const PerformanceEntry* aElem2) const
- {
- MOZ_ASSERT(aElem1 && aElem2,
- "Trying to compare null performance entries");
- return aElem1->StartTime() < aElem2->StartTime();
- }
-};
-
class PrefEnabledRunnable final
: public WorkerCheckAPIExposureOnMainThreadRunnable
{
@@ -103,14 +84,12 @@ NS_IMPL_RELEASE_INHERITED(Performance, DOMEventTargetHelper)
/* static */ already_AddRefed<Performance>
Performance::CreateForMainThread(nsPIDOMWindowInner* aWindow,
nsDOMNavigationTiming* aDOMTiming,
- nsITimedChannel* aChannel,
- Performance* aParentPerformance)
+ nsITimedChannel* aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Performance> performance =
- new PerformanceMainThread(aWindow, aDOMTiming, aChannel,
- aParentPerformance);
+ new PerformanceMainThread(aWindow, aDOMTiming, aChannel);
return performance.forget();
}
@@ -142,6 +121,24 @@ Performance::Performance(nsPIDOMWindowInner* aWindow)
Performance::~Performance()
{}
+DOMHighResTimeStamp
+Performance::Now() const
+{
+ TimeDuration duration = TimeStamp::Now() - CreationTimeStamp();
+ return RoundTime(duration.ToMilliseconds());
+}
+
+DOMHighResTimeStamp
+Performance::TimeOrigin()
+{
+ if (!mPerformanceService) {
+ mPerformanceService = PerformanceService::GetOrCreate();
+ }
+
+ MOZ_ASSERT(mPerformanceService);
+ return mPerformanceService->TimeOrigin(CreationTimeStamp());
+}
+
JSObject*
Performance::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
@@ -266,7 +263,7 @@ Performance::ClearMarks(const Optional<nsAString>& aName)
DOMHighResTimeStamp
Performance::ResolveTimestampFromName(const nsAString& aName,
- ErrorResult& aRv)
+ ErrorResult& aRv)
{
AutoTArray<RefPtr<PerformanceEntry>, 1> arr;
DOMHighResTimeStamp ts;
diff --git a/dom/performance/Performance.h b/dom/performance/Performance.h
index bc70589a5..4debecc90 100644
--- a/dom/performance/Performance.h
+++ b/dom/performance/Performance.h
@@ -24,6 +24,7 @@ namespace dom {
class PerformanceEntry;
class PerformanceNavigation;
class PerformanceObserver;
+class PerformanceService;
class PerformanceTiming;
namespace workers {
@@ -45,8 +46,7 @@ public:
static already_AddRefed<Performance>
CreateForMainThread(nsPIDOMWindowInner* aWindow,
nsDOMNavigationTiming* aDOMTiming,
- nsITimedChannel* aChannel,
- Performance* aParentPerformance);
+ nsITimedChannel* aChannel);
static already_AddRefed<Performance>
CreateForWorker(workers::WorkerPrivate* aWorkerPrivate);
@@ -54,21 +54,23 @@ public:
JSObject* WrapObject(JSContext *cx,
JS::Handle<JSObject*> aGivenProto) override;
- void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval);
+ virtual void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval);
- void GetEntriesByType(const nsAString& aEntryType,
- nsTArray<RefPtr<PerformanceEntry>>& aRetval);
+ virtual void GetEntriesByType(const nsAString& aEntryType,
+ nsTArray<RefPtr<PerformanceEntry>>& aRetval);
- void GetEntriesByName(const nsAString& aName,
- const Optional<nsAString>& aEntryType,
- nsTArray<RefPtr<PerformanceEntry>>& aRetval);
+ virtual void GetEntriesByName(const nsAString& aName,
+ const Optional<nsAString>& aEntryType,
+ nsTArray<RefPtr<PerformanceEntry>>& aRetval);
virtual void AddEntry(nsIHttpChannel* channel,
nsITimedChannel* timedChannel) = 0;
void ClearResourceTimings();
- virtual DOMHighResTimeStamp Now() const = 0;
+ DOMHighResTimeStamp Now() const;
+
+ DOMHighResTimeStamp TimeOrigin();
void Mark(const nsAString& aName, ErrorResult& aRv);
@@ -101,8 +103,6 @@ public:
virtual nsITimedChannel* GetChannel() const = 0;
- virtual Performance* GetParentPerformance() const = 0;
-
protected:
Performance();
explicit Performance(nsPIDOMWindowInner* aWindow);
@@ -126,10 +126,16 @@ protected:
virtual DOMHighResTimeStamp CreationTime() const = 0;
- virtual bool IsPerformanceTimingAttribute(const nsAString& aName) = 0;
+ virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
+ {
+ return false;
+ }
virtual DOMHighResTimeStamp
- GetPerformanceTimingFromString(const nsAString& aTimingName) = 0;
+ GetPerformanceTimingFromString(const nsAString& aTimingName)
+ {
+ return 0;
+ }
bool IsResourceEntryLimitReached() const
{
@@ -147,13 +153,15 @@ protected:
nsTObserverArray<PerformanceObserver*> mObservers;
-private:
+protected:
nsTArray<RefPtr<PerformanceEntry>> mUserEntries;
nsTArray<RefPtr<PerformanceEntry>> mResourceEntries;
uint64_t mResourceTimingBufferSize;
static const uint64_t kDefaultResourceTimingBufferSize = 150;
bool mPendingNotificationObserversTask;
+
+ RefPtr<PerformanceService> mPerformanceService;
};
} // namespace dom
diff --git a/dom/performance/PerformanceEntry.h b/dom/performance/PerformanceEntry.h
index bc4f84f1c..0af9f669e 100644
--- a/dom/performance/PerformanceEntry.h
+++ b/dom/performance/PerformanceEntry.h
@@ -90,6 +90,27 @@ protected:
nsString mEntryType;
};
+// Helper classes
+class MOZ_STACK_CLASS PerformanceEntryComparator final
+{
+public:
+ bool Equals(const PerformanceEntry* aElem1,
+ const PerformanceEntry* aElem2) const
+ {
+ MOZ_ASSERT(aElem1 && aElem2,
+ "Trying to compare null performance entries");
+ return aElem1->StartTime() == aElem2->StartTime();
+ }
+
+ bool LessThan(const PerformanceEntry* aElem1,
+ const PerformanceEntry* aElem2) const
+ {
+ MOZ_ASSERT(aElem1 && aElem2,
+ "Trying to compare null performance entries");
+ return aElem1->StartTime() < aElem2->StartTime();
+ }
+};
+
} // namespace dom
} // namespace mozilla
diff --git a/dom/performance/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp
index 86d42c5f8..64c06d3ea 100644
--- a/dom/performance/PerformanceMainThread.cpp
+++ b/dom/performance/PerformanceMainThread.cpp
@@ -6,6 +6,7 @@
#include "PerformanceMainThread.h"
#include "PerformanceNavigation.h"
+#include "nsICacheInfoChannel.h"
namespace mozilla {
namespace dom {
@@ -16,7 +17,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
Performance)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming,
mNavigation,
- mParentPerformance)
+ mDocEntry)
tmp->mMozMemory = nullptr;
mozilla::DropJSObjects(this);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -25,7 +26,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
Performance)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming,
mNavigation,
- mParentPerformance)
+ mDocEntry)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -45,12 +46,10 @@ NS_INTERFACE_MAP_END_INHERITING(Performance)
PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow,
nsDOMNavigationTiming* aDOMTiming,
- nsITimedChannel* aChannel,
- Performance* aParentPerformance)
+ nsITimedChannel* aChannel)
: Performance(aWindow)
, mDOMTiming(aDOMTiming)
, mChannel(aChannel)
- , mParentPerformance(aParentPerformance)
{
MOZ_ASSERT(aWindow, "Parent window object should be provided");
}
@@ -78,7 +77,7 @@ PerformanceTiming*
PerformanceMainThread::Timing()
{
if (!mTiming) {
- // For navigation timing, the third argument (an nsIHtttpChannel) is null
+ // For navigation timing, the third argument (an nsIHttpChannel) is null
// since the cross-domain redirect were already checked. The last argument
// (zero time) for performance.timing is the navigation start value.
mTiming = new PerformanceTiming(this, mChannel, nullptr,
@@ -108,12 +107,6 @@ PerformanceMainThread::Navigation()
return mNavigation;
}
-DOMHighResTimeStamp
-PerformanceMainThread::Now() const
-{
- return RoundTime(GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now()));
-}
-
/**
* An entry should be added only after the resource is loaded.
* This method is not thread safe and can only be called on the main thread.
@@ -161,27 +154,7 @@ PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
// The PerformanceResourceTiming object will use the PerformanceTiming
// object to get all the required timings.
RefPtr<PerformanceResourceTiming> performanceEntry =
- new PerformanceResourceTiming(performanceTiming, this, entryName);
-
- nsAutoCString protocol;
- channel->GetProtocolVersion(protocol);
- performanceEntry->SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol));
-
- uint64_t encodedBodySize = 0;
- channel->GetEncodedBodySize(&encodedBodySize);
- performanceEntry->SetEncodedBodySize(encodedBodySize);
-
- uint64_t transferSize = 0;
- channel->GetTransferSize(&transferSize);
- performanceEntry->SetTransferSize(transferSize);
-
- uint64_t decodedBodySize = 0;
- channel->GetDecodedBodySize(&decodedBodySize);
- if (decodedBodySize == 0) {
- decodedBodySize = encodedBodySize;
- }
- performanceEntry->SetDecodedBodySize(decodedBodySize);
-
+ new PerformanceResourceTiming(performanceTiming, this, entryName, channel);
// If the initiator type had no valid value, then set it to the default
// ("other") value.
if (initiatorType.IsEmpty()) {
@@ -335,5 +308,65 @@ PerformanceMainThread::CreationTime() const
return GetDOMTiming()->GetNavigationStart();
}
+void
+PerformanceMainThread::EnsureDocEntry()
+{
+ if (!mDocEntry && nsContentUtils::IsPerformanceNavigationTimingEnabled()) {
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
+ RefPtr<PerformanceTiming> timing =
+ new PerformanceTiming(this, mChannel, nullptr, 0);
+ mDocEntry = new PerformanceNavigationTiming(timing, this,
+ httpChannel);
+ }
+}
+
+
+void
+PerformanceMainThread::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
+{
+ aRetval = mResourceEntries;
+ aRetval.AppendElements(mUserEntries);
+
+ EnsureDocEntry();
+ if (mDocEntry) {
+ aRetval.AppendElement(mDocEntry);
+ }
+
+ aRetval.Sort(PerformanceEntryComparator());
+}
+
+void
+PerformanceMainThread::GetEntriesByType(const nsAString& aEntryType,
+ nsTArray<RefPtr<PerformanceEntry>>& aRetval)
+{
+ if (aEntryType.EqualsLiteral("navigation")) {
+ aRetval.Clear();
+ EnsureDocEntry();
+ if (mDocEntry) {
+ aRetval.AppendElement(mDocEntry);
+ }
+ return;
+ }
+
+ Performance::GetEntriesByType(aEntryType, aRetval);
+}
+
+void
+PerformanceMainThread::GetEntriesByName(const nsAString& aName,
+ const Optional<nsAString>& aEntryType,
+ nsTArray<RefPtr<PerformanceEntry>>& aRetval)
+{
+ if (aName.EqualsLiteral("document")) {
+ aRetval.Clear();
+ EnsureDocEntry();
+ if (mDocEntry) {
+ aRetval.AppendElement(mDocEntry);
+ }
+ return;
+ }
+
+ Performance::GetEntriesByName(aName, aEntryType, aRetval);
+}
+
} // dom namespace
} // mozilla namespace
diff --git a/dom/performance/PerformanceMainThread.h b/dom/performance/PerformanceMainThread.h
index 84773f29b..9f0e185fc 100644
--- a/dom/performance/PerformanceMainThread.h
+++ b/dom/performance/PerformanceMainThread.h
@@ -17,16 +17,12 @@ class PerformanceMainThread final : public Performance
public:
PerformanceMainThread(nsPIDOMWindowInner* aWindow,
nsDOMNavigationTiming* aDOMTiming,
- nsITimedChannel* aChannel,
- Performance* aParentPerformance);
+ nsITimedChannel* aChannel);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMainThread,
Performance)
- // Performance WebIDL methods
- DOMHighResTimeStamp Now() const override;
-
virtual PerformanceTiming* Timing() override;
virtual PerformanceNavigation* Navigation() override;
@@ -51,10 +47,14 @@ public:
return mChannel;
}
- virtual Performance* GetParentPerformance() const override
- {
- return mParentPerformance;
- }
+ // The GetEntries* methods need to be overriden in order to add the
+ // the document entry of type navigation.
+ virtual void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
+ virtual void GetEntriesByType(const nsAString& aEntryType,
+ nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
+ virtual void GetEntriesByName(const nsAString& aName,
+ const Optional<nsAString>& aEntryType,
+ nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
protected:
~PerformanceMainThread();
@@ -72,12 +72,13 @@ protected:
GetPerformanceTimingFromString(const nsAString& aTimingName) override;
void DispatchBufferFullEvent() override;
+ void EnsureDocEntry();
+ RefPtr<PerformanceEntry> mDocEntry;
RefPtr<nsDOMNavigationTiming> mDOMTiming;
nsCOMPtr<nsITimedChannel> mChannel;
RefPtr<PerformanceTiming> mTiming;
RefPtr<PerformanceNavigation> mNavigation;
- RefPtr<Performance> mParentPerformance;
JS::Heap<JSObject*> mMozMemory;
};
diff --git a/dom/performance/PerformanceNavigationTiming.cpp b/dom/performance/PerformanceNavigationTiming.cpp
new file mode 100644
index 000000000..4e00b2bb2
--- /dev/null
+++ b/dom/performance/PerformanceNavigationTiming.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/PerformanceNavigationTiming.h"
+#include "mozilla/dom/PerformanceNavigationTimingBinding.h"
+
+using namespace mozilla::dom;
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceNavigationTiming)
+NS_INTERFACE_MAP_END_INHERITING(PerformanceResourceTiming)
+
+NS_IMPL_ADDREF_INHERITED(PerformanceNavigationTiming, PerformanceResourceTiming)
+NS_IMPL_RELEASE_INHERITED(PerformanceNavigationTiming, PerformanceResourceTiming)
+
+JSObject*
+PerformanceNavigationTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return PerformanceNavigationTimingBinding::Wrap(aCx, this, aGivenProto);
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::UnloadEventStart() const
+{
+ return mTiming->GetDOMTiming()->GetUnloadEventStartHighRes();
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::UnloadEventEnd() const
+{
+ return mTiming->GetDOMTiming()->GetUnloadEventEndHighRes();
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::DomInteractive() const
+{
+ return mTiming->GetDOMTiming()->GetDomInteractiveHighRes();
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::DomContentLoadedEventStart() const
+{
+ return mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes();
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::DomContentLoadedEventEnd() const
+{
+ return mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes();
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::DomComplete() const
+{
+ return mTiming->GetDOMTiming()->GetDomCompleteHighRes();
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::LoadEventStart() const
+{
+ return mTiming->GetDOMTiming()->GetLoadEventStartHighRes();
+}
+
+DOMHighResTimeStamp
+PerformanceNavigationTiming::LoadEventEnd() const
+{
+ return mTiming->GetDOMTiming()->GetLoadEventEndHighRes();
+}
+
+NavigationType
+PerformanceNavigationTiming::Type() const
+{
+ switch(mTiming->GetDOMTiming()->GetType()) {
+ case nsDOMNavigationTiming::TYPE_NAVIGATE:
+ return NavigationType::Navigate;
+ break;
+ case nsDOMNavigationTiming::TYPE_RELOAD:
+ return NavigationType::Reload;
+ break;
+ case nsDOMNavigationTiming::TYPE_BACK_FORWARD:
+ return NavigationType::Back_forward;
+ break;
+ default:
+ // The type is TYPE_RESERVED or some other value that was later added.
+ // We fallback to the default of Navigate.
+ return NavigationType::Navigate;
+ }
+}
+
+uint16_t
+PerformanceNavigationTiming::RedirectCount() const
+{
+ return mTiming->GetRedirectCount();
+}
diff --git a/dom/performance/PerformanceNavigationTiming.h b/dom/performance/PerformanceNavigationTiming.h
new file mode 100644
index 000000000..8555f1987
--- /dev/null
+++ b/dom/performance/PerformanceNavigationTiming.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PerformanceNavigationTiming_h___
+#define mozilla_dom_PerformanceNavigationTiming_h___
+
+#include "nsCOMPtr.h"
+#include "nsIChannel.h"
+#include "nsITimedChannel.h"
+#include "mozilla/dom/PerformanceResourceTiming.h"
+#include "mozilla/dom/PerformanceNavigationTimingBinding.h"
+#include "nsIHttpChannel.h"
+
+namespace mozilla {
+namespace dom {
+
+// https://www.w3.org/TR/navigation-timing-2/#sec-PerformanceNavigationTiming
+class PerformanceNavigationTiming final
+ : public PerformanceResourceTiming
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Note that aPerformanceTiming must be initalized with zeroTime = 0
+ // so that timestamps are relative to startTime, as opposed to the
+ // performance.timing object for which timestamps are absolute and has a
+ // zeroTime initialized to navigationStart
+ explicit PerformanceNavigationTiming(PerformanceTiming* aPerformanceTiming,
+ Performance* aPerformance,
+ nsIHttpChannel* aChannel)
+ : PerformanceResourceTiming(aPerformanceTiming, aPerformance,
+ NS_LITERAL_STRING("document"), aChannel) {
+ SetEntryType(NS_LITERAL_STRING("navigation"));
+ SetInitiatorType(NS_LITERAL_STRING("navigation"));
+ }
+
+ DOMHighResTimeStamp Duration() const override
+ {
+ return LoadEventEnd() - StartTime();
+ }
+
+ DOMHighResTimeStamp StartTime() const override
+ {
+ return 0;
+ }
+
+ JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ DOMHighResTimeStamp UnloadEventStart() const;
+ DOMHighResTimeStamp UnloadEventEnd() const;
+
+ DOMHighResTimeStamp DomInteractive() const;
+ DOMHighResTimeStamp DomContentLoadedEventStart() const;
+ DOMHighResTimeStamp DomContentLoadedEventEnd() const;
+ DOMHighResTimeStamp DomComplete() const;
+ DOMHighResTimeStamp LoadEventStart() const;
+ DOMHighResTimeStamp LoadEventEnd() const;
+ NavigationType Type() const;
+ uint16_t RedirectCount() const;
+
+private:
+ ~PerformanceNavigationTiming() {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PerformanceNavigationTiming_h___
diff --git a/dom/performance/PerformanceObserver.cpp b/dom/performance/PerformanceObserver.cpp
index 11dd30ac2..d02acfb09 100644
--- a/dom/performance/PerformanceObserver.cpp
+++ b/dom/performance/PerformanceObserver.cpp
@@ -114,12 +114,13 @@ PerformanceObserver::Notify()
RefPtr<PerformanceObserverEntryList> list =
new PerformanceObserverEntryList(this, mQueuedEntries);
+ mQueuedEntries.Clear();
+
ErrorResult rv;
mCallback->Call(this, *list, *this, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
- mQueuedEntries.Clear();
}
void
@@ -170,6 +171,17 @@ PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
mEntryTypes.SwapElements(validEntryTypes);
mPerformance->AddObserver(this);
+
+ if (aOptions.mBuffered) {
+ for (auto entryType : mEntryTypes) {
+ nsTArray<RefPtr<PerformanceEntry>> existingEntries;
+ mPerformance->GetEntriesByType(entryType, existingEntries);
+ if (!existingEntries.IsEmpty()) {
+ mQueuedEntries.AppendElements(existingEntries);
+ }
+ }
+ }
+
mConnected = true;
}
diff --git a/dom/performance/PerformanceObserverEntryList.cpp b/dom/performance/PerformanceObserverEntryList.cpp
index 349103f08..20e818f3d 100644
--- a/dom/performance/PerformanceObserverEntryList.cpp
+++ b/dom/performance/PerformanceObserverEntryList.cpp
@@ -66,6 +66,7 @@ PerformanceObserverEntryList::GetEntries(
aRetval.AppendElement(entry);
}
+ aRetval.Sort(PerformanceEntryComparator());
}
void
@@ -79,6 +80,7 @@ PerformanceObserverEntryList::GetEntriesByType(
aRetval.AppendElement(entry);
}
}
+ aRetval.Sort(PerformanceEntryComparator());
}
void
@@ -88,9 +90,18 @@ PerformanceObserverEntryList::GetEntriesByName(
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
{
aRetval.Clear();
+ const bool typePassed = aEntryType.WasPassed();
for (const RefPtr<PerformanceEntry>& entry : mEntries) {
- if (entry->GetName().Equals(aName)) {
- aRetval.AppendElement(entry);
+ if (!entry->GetName().Equals(aName)) {
+ continue;
}
+
+ if (typePassed &&
+ !entry->GetEntryType().Equals(aEntryType.Value())) {
+ continue;
+ }
+
+ aRetval.AppendElement(entry);
}
+ aRetval.Sort(PerformanceEntryComparator());
}
diff --git a/dom/performance/PerformanceResourceTiming.cpp b/dom/performance/PerformanceResourceTiming.cpp
index 60a20ca28..2eaa4eb9a 100644
--- a/dom/performance/PerformanceResourceTiming.cpp
+++ b/dom/performance/PerformanceResourceTiming.cpp
@@ -25,7 +25,8 @@ NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry)
PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming,
Performance* aPerformance,
- const nsAString& aName)
+ const nsAString& aName,
+ nsIHttpChannel* aChannel)
: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("resource")),
mTiming(aPerformanceTiming),
mEncodedBodySize(0),
@@ -33,6 +34,34 @@ PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerform
mDecodedBodySize(0)
{
MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
+ SetPropertiesFromChannel(aChannel);
+}
+
+void
+PerformanceResourceTiming::SetPropertiesFromChannel(nsIHttpChannel* aChannel)
+{
+ if (!aChannel) {
+ return;
+ }
+
+ nsAutoCString protocol;
+ Unused << aChannel->GetProtocolVersion(protocol);
+ SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol));
+
+ uint64_t encodedBodySize = 0;
+ Unused << aChannel->GetEncodedBodySize(&encodedBodySize);
+ SetEncodedBodySize(encodedBodySize);
+
+ uint64_t transferSize = 0;
+ Unused << aChannel->GetTransferSize(&transferSize);
+ SetTransferSize(transferSize);
+
+ uint64_t decodedBodySize = 0;
+ Unused << aChannel->GetDecodedBodySize(&decodedBodySize);
+ if (decodedBodySize == 0) {
+ decodedBodySize = encodedBodySize;
+ }
+ SetDecodedBodySize(decodedBodySize);
}
PerformanceResourceTiming::~PerformanceResourceTiming()
@@ -42,8 +71,22 @@ PerformanceResourceTiming::~PerformanceResourceTiming()
DOMHighResTimeStamp
PerformanceResourceTiming::StartTime() const
{
- DOMHighResTimeStamp startTime = mTiming->RedirectStartHighRes();
- return startTime ? startTime : mTiming->FetchStartHighRes();
+ // Force the start time to be the earliest of:
+ // - RedirectStart
+ // - WorkerStart
+ // - AsyncOpen
+ // Ignore zero values. The RedirectStart and WorkerStart values
+ // can come from earlier redirected channels prior to the AsyncOpen
+ // time being recorded.
+ DOMHighResTimeStamp redirect = mTiming->RedirectStartHighRes();
+ redirect = redirect ? redirect : DBL_MAX;
+
+ DOMHighResTimeStamp worker = mTiming->WorkerStartHighRes();
+ worker = worker ? worker : DBL_MAX;
+
+ DOMHighResTimeStamp asyncOpen = mTiming->AsyncOpenHighRes();
+
+ return std::min(asyncOpen, std::min(redirect, worker));
}
JSObject*
diff --git a/dom/performance/PerformanceResourceTiming.h b/dom/performance/PerformanceResourceTiming.h
index 2dd6b4a06..98a03327e 100644
--- a/dom/performance/PerformanceResourceTiming.h
+++ b/dom/performance/PerformanceResourceTiming.h
@@ -18,7 +18,7 @@ namespace mozilla {
namespace dom {
// http://www.w3.org/TR/resource-timing/#performanceresourcetiming
-class PerformanceResourceTiming final : public PerformanceEntry
+class PerformanceResourceTiming : public PerformanceEntry
{
public:
typedef mozilla::TimeStamp TimeStamp;
@@ -30,7 +30,8 @@ public:
PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming,
Performance* aPerformance,
- const nsAString& aName);
+ const nsAString& aName,
+ nsIHttpChannel* aChannel = nullptr);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -62,6 +63,12 @@ public:
mNextHopProtocol = aNextHopProtocol;
}
+ DOMHighResTimeStamp WorkerStart() const {
+ return mTiming && mTiming->TimingAllowed()
+ ? mTiming->WorkerStartHighRes()
+ : 0;
+ }
+
DOMHighResTimeStamp FetchStart() const {
return mTiming
? mTiming->FetchStartHighRes()
@@ -170,6 +177,7 @@ public:
protected:
virtual ~PerformanceResourceTiming();
+ void SetPropertiesFromChannel(nsIHttpChannel* aChannel);
nsString mInitiatorType;
nsString mNextHopProtocol;
diff --git a/dom/performance/PerformanceService.cpp b/dom/performance/PerformanceService.cpp
new file mode 100644
index 000000000..cf119af89
--- /dev/null
+++ b/dom/performance/PerformanceService.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PerformanceService.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+static StaticRefPtr<PerformanceService> gPerformanceService;
+static StaticMutex gPerformanceServiceMutex;
+
+/* static */ PerformanceService*
+PerformanceService::GetOrCreate()
+{
+ StaticMutexAutoLock al(gPerformanceServiceMutex);
+
+ if (!gPerformanceService) {
+ gPerformanceService = new PerformanceService();
+ ClearOnShutdown(&gPerformanceService);
+ }
+
+ return gPerformanceService;
+}
+
+DOMHighResTimeStamp
+PerformanceService::TimeOrigin(const TimeStamp& aCreationTimeStamp) const
+{
+ return (aCreationTimeStamp - mCreationTimeStamp).ToMilliseconds() +
+ (mCreationEpochTime / PR_USEC_PER_MSEC);
+}
+
+PerformanceService::PerformanceService()
+{
+ mCreationTimeStamp = TimeStamp::Now();
+ mCreationEpochTime = PR_Now();
+}
+
+} // dom namespace
+} // mozilla namespace
diff --git a/dom/performance/PerformanceService.h b/dom/performance/PerformanceService.h
new file mode 100644
index 000000000..9abbd674d
--- /dev/null
+++ b/dom/performance/PerformanceService.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef dom_performance_PerformanceService_h
+#define dom_performance_PerformanceService_h
+
+#include "mozilla/TimeStamp.h"
+#include "nsCOMPtr.h"
+#include "nsDOMNavigationTiming.h"
+
+namespace mozilla {
+namespace dom {
+
+// This class is thread-safe.
+
+// We use this singleton for having the correct value of performance.timeOrigin.
+// This value must be calculated on top of the pair:
+// - mCreationTimeStamp (monotonic clock)
+// - mCreationEpochTime (unix epoch time)
+// These 2 values must be taken "at the same time" in order to be used
+// correctly.
+
+class PerformanceService
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PerformanceService)
+
+ static PerformanceService*
+ GetOrCreate();
+
+ DOMHighResTimeStamp
+ TimeOrigin(const TimeStamp& aCreationTimeStamp) const;
+
+private:
+ PerformanceService();
+ ~PerformanceService() = default;
+
+ TimeStamp mCreationTimeStamp;
+ PRTime mCreationEpochTime;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // dom_performance_PerformanceService_h
diff --git a/dom/performance/PerformanceTiming.cpp b/dom/performance/PerformanceTiming.cpp
index e2f76a21f..887a23938 100755
--- a/dom/performance/PerformanceTiming.cpp
+++ b/dom/performance/PerformanceTiming.cpp
@@ -73,6 +73,7 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
{
if (aChannel) {
aChannel->GetAsyncOpen(&mAsyncOpen);
+ aChannel->GetDispatchFetchEventStart(&mWorkerStart);
aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
aChannel->GetRedirectCount(&mRedirectCount);
aChannel->GetRedirectStart(&mRedirectStart);
@@ -88,31 +89,39 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
aChannel->GetResponseEnd(&mResponseEnd);
aChannel->GetCacheReadEnd(&mCacheReadEnd);
- // the performance timing api essentially requires that the event timestamps
- // are >= asyncOpen().. but in truth the browser engages in a number of
- // speculative activities that sometimes mean connections and lookups begin
- // earlier. Workaround that here by just using asyncOpen as the minimum
- // timestamp for dns and connection info.
+ // The performance timing api essentially requires that the event timestamps
+ // have a strict relation with each other. The truth, however, is the browser
+ // engages in a number of speculative activities that sometimes mean connections
+ // and lookups begin at different times. Workaround that here by clamping
+ // these values to what we expect FetchStart to be. This means the later of
+ // AsyncOpen or WorkerStart times.
if (!mAsyncOpen.IsNull()) {
- if (!mDomainLookupStart.IsNull() && mDomainLookupStart < mAsyncOpen) {
- mDomainLookupStart = mAsyncOpen;
+ // We want to clamp to the expected FetchStart value. This is later of
+ // the AsyncOpen and WorkerStart values.
+ const TimeStamp* clampTime = &mAsyncOpen;
+ if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
+ clampTime = &mWorkerStart;
}
- if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < mAsyncOpen) {
- mDomainLookupEnd = mAsyncOpen;
+ if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) {
+ mDomainLookupStart = *clampTime;
}
- if (!mConnectStart.IsNull() && mConnectStart < mAsyncOpen) {
- mConnectStart = mAsyncOpen;
+ if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) {
+ mDomainLookupEnd = *clampTime;
+ }
+
+ if (!mConnectStart.IsNull() && mConnectStart < *clampTime) {
+ mConnectStart = *clampTime;
}
if (mSecureConnection && !mSecureConnectionStart.IsNull() &&
- mSecureConnectionStart < mAsyncOpen) {
- mSecureConnectionStart = mAsyncOpen;
+ mSecureConnectionStart < *clampTime) {
+ mSecureConnectionStart = *clampTime;
}
- if (!mConnectEnd.IsNull() && mConnectEnd < mAsyncOpen) {
- mConnectEnd = mAsyncOpen;
+ if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) {
+ mConnectEnd = *clampTime;
}
}
}
@@ -131,9 +140,13 @@ PerformanceTiming::FetchStartHighRes()
}
MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
"valid if the performance timing is enabled");
- mFetchStart = (!mAsyncOpen.IsNull())
- ? TimeStampToDOMHighRes(mAsyncOpen)
- : 0.0;
+ if (!mAsyncOpen.IsNull()) {
+ if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
+ mFetchStart = TimeStampToDOMHighRes(mWorkerStart);
+ } else {
+ mFetchStart = TimeStampToDOMHighRes(mAsyncOpen);
+ }
+ }
}
return TimerClamping::ReduceMsTimeValue(mFetchStart);
}
@@ -180,7 +193,7 @@ PerformanceTiming::TimingAllowed() const
return mTimingAllowed;
}
-uint16_t
+uint8_t
PerformanceTiming::GetRedirectCount() const
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
@@ -205,6 +218,26 @@ PerformanceTiming::ShouldReportCrossOriginRedirect() const
return (mRedirectCount != 0) && mReportCrossOriginRedirect;
}
+DOMHighResTimeStamp
+PerformanceTiming::AsyncOpenHighRes()
+{
+ if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
+ mAsyncOpen.IsNull()) {
+ return mZeroTime;
+ }
+ return TimeStampToReducedDOMHighResOrFetchStart(mAsyncOpen);
+}
+
+DOMHighResTimeStamp
+PerformanceTiming::WorkerStartHighRes()
+{
+ if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
+ mWorkerStart.IsNull()) {
+ return mZeroTime;
+ }
+ return TimeStampToReducedDOMHighResOrFetchStart(mWorkerStart);
+}
+
/**
* RedirectStartHighRes() is used by both the navigation timing and the
* resource timing. Since, navigation timing and resource timing check and
diff --git a/dom/performance/PerformanceTiming.h b/dom/performance/PerformanceTiming.h
index fc7e7d5bd..435e1bca1 100755
--- a/dom/performance/PerformanceTiming.h
+++ b/dom/performance/PerformanceTiming.h
@@ -139,7 +139,7 @@ public:
return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetUnloadEventEnd());
}
- uint16_t GetRedirectCount() const;
+ uint8_t GetRedirectCount() const;
// Checks if the resource is either same origin as the page that started
// the load, or if the response contains the Timing-Allow-Origin header
@@ -155,7 +155,12 @@ public:
// the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck
bool ShouldReportCrossOriginRedirect() const;
+ // The last channel's AsyncOpen time. This may occur before the FetchStart
+ // in some cases.
+ DOMHighResTimeStamp AsyncOpenHighRes();
+
// High resolution (used by resource timing)
+ DOMHighResTimeStamp WorkerStartHighRes();
DOMHighResTimeStamp FetchStartHighRes();
DOMHighResTimeStamp RedirectStartHighRes();
DOMHighResTimeStamp RedirectEndHighRes();
@@ -237,6 +242,14 @@ public:
return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetLoadEventEnd());
}
+ DOMTimeMilliSec TimeToNonBlankPaint() const
+ {
+ if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+ return 0;
+ }
+ return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetTimeToNonBlankPaint());
+ }
+
private:
~PerformanceTiming();
@@ -253,6 +266,7 @@ private:
DOMHighResTimeStamp mZeroTime;
TimeStamp mAsyncOpen;
+ TimeStamp mWorkerStart;
TimeStamp mRedirectStart;
TimeStamp mRedirectEnd;
TimeStamp mDomainLookupStart;
@@ -265,7 +279,7 @@ private:
TimeStamp mCacheReadStart;
TimeStamp mResponseEnd;
TimeStamp mCacheReadEnd;
- uint16_t mRedirectCount;
+ uint8_t mRedirectCount;
bool mTimingAllowed;
bool mAllRedirectsSameOrigin;
bool mInitialized;
diff --git a/dom/performance/PerformanceWorker.cpp b/dom/performance/PerformanceWorker.cpp
index 85ca2ccd8..f10c58446 100644
--- a/dom/performance/PerformanceWorker.cpp
+++ b/dom/performance/PerformanceWorker.cpp
@@ -23,37 +23,6 @@ PerformanceWorker::~PerformanceWorker()
mWorkerPrivate->AssertIsOnWorkerThread();
}
-DOMHighResTimeStamp
-PerformanceWorker::Now() const
-{
- TimeDuration duration =
- TimeStamp::Now() - mWorkerPrivate->NowBaseTimeStamp();
- return RoundTime(duration.ToMilliseconds());
-}
-
-// To be removed once bug 1124165 lands
-bool
-PerformanceWorker::IsPerformanceTimingAttribute(const nsAString& aName)
-{
- // In workers we just support navigationStart.
- return aName.EqualsASCII("navigationStart");
-}
-
-DOMHighResTimeStamp
-PerformanceWorker::GetPerformanceTimingFromString(const nsAString& aProperty)
-{
- if (!IsPerformanceTimingAttribute(aProperty)) {
- return 0;
- }
-
- if (aProperty.EqualsLiteral("navigationStart")) {
- return mWorkerPrivate->NowBaseTime();
- }
-
- MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
- return 0;
-}
-
void
PerformanceWorker::InsertUserEntry(PerformanceEntry* aEntry)
{
@@ -72,13 +41,13 @@ PerformanceWorker::InsertUserEntry(PerformanceEntry* aEntry)
TimeStamp
PerformanceWorker::CreationTimeStamp() const
{
- return mWorkerPrivate->NowBaseTimeStamp();
+ return mWorkerPrivate->CreationTimeStamp();
}
DOMHighResTimeStamp
PerformanceWorker::CreationTime() const
{
- return mWorkerPrivate->NowBaseTime();
+ return mWorkerPrivate->CreationTime();
}
} // dom namespace
diff --git a/dom/performance/PerformanceWorker.h b/dom/performance/PerformanceWorker.h
index 7eef0d974..346bdd026 100644
--- a/dom/performance/PerformanceWorker.h
+++ b/dom/performance/PerformanceWorker.h
@@ -21,9 +21,6 @@ class PerformanceWorker final : public Performance
public:
explicit PerformanceWorker(workers::WorkerPrivate* aWorkerPrivate);
- // Performance WebIDL methods
- DOMHighResTimeStamp Now() const override;
-
virtual PerformanceTiming* Timing() override
{
MOZ_CRASH("This should not be called on workers.");
@@ -64,12 +61,6 @@ public:
return nullptr;
}
- virtual Performance* GetParentPerformance() const override
- {
- MOZ_CRASH("This should not be called on workers.");
- return nullptr;
- }
-
protected:
~PerformanceWorker();
@@ -80,11 +71,6 @@ protected:
void InsertUserEntry(PerformanceEntry* aEntry) override;
- bool IsPerformanceTimingAttribute(const nsAString& aName) override;
-
- DOMHighResTimeStamp
- GetPerformanceTimingFromString(const nsAString& aTimingName) override;
-
void DispatchBufferFullEvent() override
{
MOZ_CRASH("This should not be called on workers.");
diff --git a/dom/performance/moz.build b/dom/performance/moz.build
index 3286a0a4c..e1f96fec8 100644
--- a/dom/performance/moz.build
+++ b/dom/performance/moz.build
@@ -10,9 +10,11 @@ EXPORTS.mozilla.dom += [
'PerformanceMark.h',
'PerformanceMeasure.h',
'PerformanceNavigation.h',
+ 'PerformanceNavigationTiming.h',
'PerformanceObserver.h',
'PerformanceObserverEntryList.h',
'PerformanceResourceTiming.h',
+ 'PerformanceService.h',
'PerformanceTiming.h',
]
@@ -23,9 +25,11 @@ UNIFIED_SOURCES += [
'PerformanceMark.cpp',
'PerformanceMeasure.cpp',
'PerformanceNavigation.cpp',
+ 'PerformanceNavigationTiming.cpp',
'PerformanceObserver.cpp',
'PerformanceObserverEntryList.cpp',
'PerformanceResourceTiming.cpp',
+ 'PerformanceService.cpp',
'PerformanceTiming.cpp',
'PerformanceWorker.cpp',
]
diff --git a/dom/performance/tests/mochitest.ini b/dom/performance/tests/mochitest.ini
index 18f7f0e45..bee0b2e70 100644
--- a/dom/performance/tests/mochitest.ini
+++ b/dom/performance/tests/mochitest.ini
@@ -1,13 +1,11 @@
[DEFAULT]
support-files =
- performance_observer.html
test_performance_observer.js
test_performance_user_timing.js
test_worker_performance_now.js
worker_performance_user_timing.js
worker_performance_observer.js
sharedworker_performance_user_timing.js
- worker_performance_observer.html
[test_performance_observer.html]
[test_performance_user_timing.html]
@@ -15,3 +13,4 @@ support-files =
[test_worker_observer.html]
[test_sharedWorker_performance_user_timing.html]
[test_worker_performance_now.html]
+[test_timeOrigin.html]
diff --git a/dom/performance/tests/performance_observer.html b/dom/performance/tests/performance_observer.html
deleted file mode 100644
index b8ced9296..000000000
--- a/dom/performance/tests/performance_observer.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<!--
- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE html>
-<meta charset=utf-8>
-<html>
-<head>
-<title>Test for performance observer</title>
-<script>
-'use strict';
-[
- "promise_test", "test", "setup",
- "assert_true", "assert_equals", "assert_array_equals",
- "assert_throws", "assert_unreached"
-].forEach(func => {
- window[func] = opener[func].bind(opener);
-});
-function done() {
- opener.add_completion_callback(() => {
- self.close();
- });
- opener.done();
-}
-
-</script>
-<script src="test_performance_observer.js"></script>
-</head>
-<body>
-<div id="log"></div>
-<script>
-function makeXHR(aUrl) {
- var xmlhttp = new XMLHttpRequest();
- xmlhttp.open("get", aUrl, true);
- xmlhttp.send();
-}
-
-promise_test(t => {
- var promise = new Promise(resolve => {
- performance.clearResourceTimings();
-
- var observer = new PerformanceObserver(list => resolve(list));
- observer.observe({entryTypes: ['resource']});
- t.add_cleanup(() => observer.disconnect());
- });
-
- makeXHR("test-data.json");
-
- return promise.then(list => {
- assert_equals(list.getEntries().length, 1);
- assert_array_equals(list.getEntries(),
- performance.getEntriesByType("resource"),
- "Observed 'resource' entries should equal to entries obtained by getEntriesByType.");
-
- // getEntries filtering tests
- assert_array_equals(list.getEntries({name: "http://mochi.test:8888/tests/dom/base/test/test-data.json"}),
- performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/test-data.json"),
- "getEntries with name filter should return correct results.");
- assert_array_equals(list.getEntries({entryType: "resource"}),
- performance.getEntriesByType("resource"),
- "getEntries with entryType filter should return correct results.");
- assert_array_equals(list.getEntries({initiatorType: "xmlhttprequest"}),
- performance.getEntriesByType("resource"),
- "getEntries with initiatorType filter should return correct results.");
- assert_array_equals(list.getEntries({initiatorType: "link"}),
- [],
- "getEntries with non-existent initiatorType filter should return an empty array.");
- });
-}, "resource-timing test");
-
-done();
-
-</script>
-</body>
diff --git a/dom/performance/tests/test_performance_observer.html b/dom/performance/tests/test_performance_observer.html
index d36878315..7df881bd4 100644
--- a/dom/performance/tests/test_performance_observer.html
+++ b/dom/performance/tests/test_performance_observer.html
@@ -3,15 +3,55 @@
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
+<html>
+<head>
<meta charset=utf-8>
<title>Test for performance observer</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
-<div id=log></div>
+</head>
+<body>
+<div id="log"></div>
+<script src="test_performance_observer.js"></script>
<script>
-'use strict';
-SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
- function() {
- window.open("performance_observer.html");
- });
+function makeXHR(aUrl) {
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.open("get", aUrl, true);
+ xmlhttp.send();
+}
+
+promise_test(t => {
+ var promise = new Promise(resolve => {
+ performance.clearResourceTimings();
+
+ var observer = new PerformanceObserver(list => resolve(list));
+ observer.observe({entryTypes: ['resource']});
+ t.add_cleanup(() => observer.disconnect());
+ });
+
+ makeXHR("test-data.json");
+
+ return promise.then(list => {
+ assert_equals(list.getEntries().length, 1);
+ assert_array_equals(list.getEntries(),
+ performance.getEntriesByType("resource"),
+ "Observed 'resource' entries should equal to entries obtained by getEntriesByType.");
+
+ // getEntries filtering tests
+ assert_array_equals(list.getEntries({name: "http://mochi.test:8888/tests/dom/base/test/test-data.json"}),
+ performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/test-data.json"),
+ "getEntries with name filter should return correct results.");
+ assert_array_equals(list.getEntries({entryType: "resource"}),
+ performance.getEntriesByType("resource"),
+ "getEntries with entryType filter should return correct results.");
+ assert_array_equals(list.getEntries({initiatorType: "xmlhttprequest"}),
+ performance.getEntriesByType("resource"),
+ "getEntries with initiatorType filter should return correct results.");
+ assert_array_equals(list.getEntries({initiatorType: "link"}),
+ [],
+ "getEntries with non-existent initiatorType filter should return an empty array.");
+ });
+}, "resource-timing test");
+
</script>
+</body>
diff --git a/dom/performance/tests/test_performance_user_timing.js b/dom/performance/tests/test_performance_user_timing.js
index cd8261bbd..a15dbebb6 100644
--- a/dom/performance/tests/test_performance_user_timing.js
+++ b/dom/performance/tests/test_performance_user_timing.js
@@ -126,15 +126,18 @@ var steps = [
},
// Test measure
function () {
- ok(true, "Running measure addition with no start/end time test");
- performance.measure("test");
- var measures = performance.getEntriesByType("measure");
- is(measures.length, 1, "number of measures should be 1");
- var measure = measures[0];
- is(measure.name, "test", "measure name should be 'test'");
- is(measure.entryType, "measure", "measure type should be 'measure'");
- is(measure.startTime, 0, "measure start time should be zero");
- ok(measure.duration >= 0, "measure duration should not be negative");
+ // We don't have navigationStart in workers.
+ if ("window" in self) {
+ ok(true, "Running measure addition with no start/end time test");
+ performance.measure("test", "navigationStart");
+ var measures = performance.getEntriesByType("measure");
+ is(measures.length, 1, "number of measures should be 1");
+ var measure = measures[0];
+ is(measure.name, "test", "measure name should be 'test'");
+ is(measure.entryType, "measure", "measure type should be 'measure'");
+ is(measure.startTime, 0, "measure start time should be zero");
+ ok(measure.duration >= 0, "measure duration should not be negative");
+ }
},
function () {
ok(true, "Running measure addition with only start time test");
diff --git a/dom/performance/tests/test_timeOrigin.html b/dom/performance/tests/test_timeOrigin.html
new file mode 100644
index 000000000..5a8a461f3
--- /dev/null
+++ b/dom/performance/tests/test_timeOrigin.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for performance.timeOrigin</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_performance_user_timing.js"></script>
+ </head>
+ <body>
+ <script type="text/js-worker" id="worker-src">
+ postMessage({ now: performance.now(), timeOrigin: performance.timeOrigin });
+ </script>
+
+ <script type="text/js-worker" id="shared-worker-src">
+ onconnect = function(evt) {
+ evt.ports[0].postMessage({ now: performance.now(), timeOrigin: performance.timeOrigin });
+ };
+ </script>
+
+ <script class="testbody" type="text/javascript">
+
+function testBasic() {
+ ok("timeOrigin" in performance, "Performance.timeOrigin exists.");
+ ok(performance.timeOrigin > 0, "TimeOrigin must be greater than 0.");
+ next();
+}
+
+function testWorker() {
+ var now = performance.now();
+
+ var blob = new Blob([ document.getElementById("worker-src").textContent ],
+ { type: "text/javascript" });
+ var w = new Worker(URL.createObjectURL(blob));
+ w.onmessage = function(e) {
+ ok (e.now + e.timeOrigin > now + performance.now, "Comparing worker.now and window.now");
+ next();
+ }
+}
+
+function testSharedWorker() {
+ var now = performance.now();
+
+ var blob = new Blob([ document.getElementById("shared-worker-src").textContent ],
+ { type: "text/javascript" });
+ var w = new SharedWorker(URL.createObjectURL(blob));
+ w.port.onmessage = function(e) {
+ ok (e.now + e.timeOrigin > now + performance.now, "Comparing worker.now and window.now");
+ next();
+ }
+}
+
+var tests = [ testBasic, testWorker, testSharedWorker ];
+function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(next);
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/performance/tests/test_worker_observer.html b/dom/performance/tests/test_worker_observer.html
index b9ed0c964..9a55ef1d5 100644
--- a/dom/performance/tests/test_worker_observer.html
+++ b/dom/performance/tests/test_worker_observer.html
@@ -3,15 +3,16 @@
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
+<html>
+<head>
<meta charset=utf-8>
<title>Test for performance observer in worker</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
-<div id=log></div>
+</head>
+<body>
+<div id="log"></div>
<script>
-'use strict';
-SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
- function() {
- window.open("worker_performance_observer.html");
- });
+fetch_tests_from_worker(new Worker("worker_performance_observer.js"));
</script>
+</body>
diff --git a/dom/performance/tests/worker_performance_observer.html b/dom/performance/tests/worker_performance_observer.html
deleted file mode 100644
index 613762f52..000000000
--- a/dom/performance/tests/worker_performance_observer.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE html>
-<meta charset=utf-8>
-<html>
-<head>
-<title>Test for performance observer in worker</title>
-</head>
-<body>
-<div id="log"></div>
-<script>
-[
- "async_test", "test", "setup",
- "assert_true", "assert_equals", "assert_array_equals",
- "assert_throws", "fetch_tests_from_worker"
-].forEach(func => {
- window[func] = opener[func].bind(opener);
-});
-
-function done() {
- opener.add_completion_callback(() => {
- self.close();
- });
- opener.done();
-}
-
-fetch_tests_from_worker(new Worker("worker_performance_observer.js"));
-done();
-</script>
-</body>
diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp
index b7651be1a..d5b1eb9ea 100644
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -535,16 +535,6 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL,
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, baseURI);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
- if (aDoCheckLoadURIChecks) {
- nsCOMPtr<nsIScriptSecurityManager> secMan(
- do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
- NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
-
- rv = secMan->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
- nsIScriptSecurityManager::STANDARD);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
nsCOMPtr<nsIInputStream> headersDataStream;
if (aPostStream && aHeadersData) {
if (!aHeadersDataLen)
@@ -563,8 +553,21 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL,
Preferences::GetInt("privacy.popups.disable_from_plugins");
nsAutoPopupStatePusher popupStatePusher((PopupControlState)blockPopups);
+
+ // if security checks (in particular CheckLoadURIWithPrincipal) needs
+ // to be skipped we are creating a codebasePrincipal to make sure
+ // that security check succeeds. Please note that we do not want to
+ // fall back to using the systemPrincipal, because that would also
+ // bypass ContentPolicy checks which should still be enforced.
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ if (!aDoCheckLoadURIChecks) {
+ mozilla::PrincipalOriginAttributes attrs =
+ BasePrincipal::Cast(content->NodePrincipal())->OriginAttributesRef();
+ triggeringPrincipal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+ }
+
rv = lh->OnLinkClick(content, uri, unitarget.get(), NullString(),
- aPostStream, headersDataStream, true);
+ aPostStream, headersDataStream, true, triggeringPrincipal);
return rv;
}
@@ -2532,6 +2535,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
NS_ASSERTION(anEvent.mMessage == eMouseDown ||
anEvent.mMessage == eMouseUp ||
anEvent.mMessage == eMouseDoubleClick ||
+ anEvent.mMessage == eMouseAuxClick ||
anEvent.mMessage == eMouseOver ||
anEvent.mMessage == eMouseOut ||
anEvent.mMessage == eMouseMove ||
@@ -2594,6 +2598,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
switch (anEvent.mMessage) {
case eMouseClick:
case eMouseDoubleClick:
+ case eMouseAuxClick:
// Button up/down events sent instead.
return rv;
default:
@@ -2797,6 +2802,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
switch (anEvent.mMessage) {
case eMouseClick:
case eMouseDoubleClick:
+ case eMouseAuxClick:
// Button up/down events sent instead.
return rv;
default:
diff --git a/dom/plugins/test/mochitest/test_bug813906.html b/dom/plugins/test/mochitest/test_bug813906.html
index 04c34daaf..d18dbbff2 100644
--- a/dom/plugins/test/mochitest/test_bug813906.html
+++ b/dom/plugins/test/mochitest/test_bug813906.html
@@ -18,21 +18,35 @@ function f() {
</script>
<script type="application/javascript">
+SimpleTest.requestFlakyTimeout(
+ "Blocking an iframe does not cause the onerror event to be fired");
+
SimpleTest.waitForExplicitFinish();
setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
var frameLoadCount = 0;
+
+function frameNavBlocked() {
+ isnot(SpecialPowers.wrap(window.frame1).location.href.indexOf('chrome://'),
+ 0, 'plugin shouldnt be able to cause navigation to chrome URLs');
+ SimpleTest.finish();
+}
+
function frameLoaded() {
frameLoadCount++;
if (frameLoadCount == 1) {
document.getElementsByTagName("object")[0].type = "application/x-test";
document.getElementsByTagName("use")[0].setAttributeNS("http://www.w3.org/1999/xlink", "href", location.href + "#a");
- } else if (frameLoadCount == 2) {
- isnot(SpecialPowers.wrap(window.frame1).location.href.indexOf('chrome://'),
- 0, 'plugin shouldnt be able to cause navigation to chrome URLs');
- SimpleTest.finish();
+
+ // wait two seconds and verify that frame navigation did not succeed
+ setTimeout(frameNavBlocked, 2000);
+ return;
}
+ // we should never get here, but just in case, make sure the test fails in that case.
+ ok(false, "onload() event should not fire for blocked navigation");
+ SimpleTest.finish();
}
+
</script>
<!-- Note that <svg:use> ends up creating an anonymous subtree, which means that the plugin
diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp
index 0cc4933fe..c6558fc93 100644
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -10,6 +10,8 @@
#include "nsIStreamListener.h"
#include "nsIDocument.h"
#include "nsMixedContentBlocker.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsIURIFixup.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabChild.h"
@@ -244,10 +246,6 @@ DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
static nsresult
DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
{
- nsCOMPtr<nsIURI> uri;
- nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
- NS_ENSURE_SUCCESS(rv, rv);
-
nsContentPolicyType contentPolicyType =
aLoadInfo->GetExternalContentPolicyType();
nsContentPolicyType internalContentPolicyType =
@@ -255,12 +253,24 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
nsCString mimeTypeGuess;
nsCOMPtr<nsISupports> requestingContext = nullptr;
-#ifdef DEBUG
- // Don't enforce TYPE_DOCUMENT assertions for loads
- // initiated by javascript tests.
- bool skipContentTypeCheck = false;
- skipContentTypeCheck = Preferences::GetBool("network.loadinfo.skip_type_assertion");
-#endif
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+ contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+ // TYPE_DOCUMENT and TYPE_SUBDOCUMENT loads might potentially
+ // be wyciwyg:// channels. Let's fix up the URI so we can
+ // perform proper security checks.
+ nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && urifixup) {
+ nsCOMPtr<nsIURI> fixedURI;
+ rv = urifixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
+ if (NS_SUCCEEDED(rv)) {
+ uri = fixedURI;
+ }
+ }
+ }
switch(contentPolicyType) {
case nsIContentPolicy::TYPE_OTHER: {
@@ -294,16 +304,14 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
}
case nsIContentPolicy::TYPE_DOCUMENT: {
- MOZ_ASSERT(skipContentTypeCheck || false, "contentPolicyType not supported yet");
+ mimeTypeGuess = EmptyCString();
+ requestingContext = aLoadInfo->LoadingNode();
break;
}
case nsIContentPolicy::TYPE_SUBDOCUMENT: {
mimeTypeGuess = NS_LITERAL_CSTRING("text/html");
requestingContext = aLoadInfo->LoadingNode();
- MOZ_ASSERT(!requestingContext ||
- requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
- "type_subdocument requires requestingContext of type Document");
break;
}
@@ -470,18 +478,32 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
MOZ_ASSERT(false, "can not perform security check without a valid contentType");
}
+ // For document loads we use the triggeringPrincipal as the originPrincipal.
+ // Note the the loadingPrincipal for loads of TYPE_DOCUMENT is a nullptr.
+ nsCOMPtr<nsIPrincipal> principal =
+ (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+ contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT)
+ ? aLoadInfo->TriggeringPrincipal()
+ : aLoadInfo->LoadingPrincipal();
+
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(internalContentPolicyType,
uri,
- aLoadInfo->LoadingPrincipal(),
+ principal,
requestingContext,
mimeTypeGuess,
nullptr, //extra,
&shouldLoad,
nsContentUtils::GetContentPolicy(),
nsContentUtils::GetSecurityManager());
- NS_ENSURE_SUCCESS(rv, rv);
- if (NS_CP_REJECTED(shouldLoad)) {
+
+ if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+ if ((NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) &&
+ (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+ contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT)) {
+ // for docshell loads we might have to return SHOW_ALT.
+ return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
+ }
return NS_ERROR_CONTENT_BLOCKED;
}
@@ -629,6 +651,24 @@ nsContentSecurityManager::CheckChannel(nsIChannel* aChannel)
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
+ nsContentPolicyType contentPolicyType =
+ loadInfo->GetExternalContentPolicyType();
+
+ if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+ contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+ // TYPE_DOCUMENT and TYPE_SUBDOCUMENT loads might potentially
+ // be wyciwyg:// channels. Let's fix up the URI so we can
+ // perform proper security checks.
+ nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && urifixup) {
+ nsCOMPtr<nsIURI> fixedURI;
+ rv = urifixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
+ if (NS_SUCCEEDED(rv)) {
+ uri = fixedURI;
+ }
+ }
+ }
+
// Handle cookie policies
uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html
index acbc12e07..4b47d2b4f 100644
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -751,9 +751,11 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceNavigation",
// IMPORTANT: Do not change this list without review from a DOM peer!
- {name: "PerformanceObserver", nightly: true},
+ "PerformanceNavigationTiming",
// IMPORTANT: Do not change this list without review from a DOM peer!
- {name: "PerformanceObserverEntryList", nightly: true},
+ "PerformanceObserver"
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ "PerformanceObserverEntryList"
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceResourceTiming",
// IMPORTANT: Do not change this list without review from a DOM peer!
diff --git a/dom/webidl/EventHandler.webidl b/dom/webidl/EventHandler.webidl
index e65a75787..1edc45ac9 100644
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -33,6 +33,7 @@ interface GlobalEventHandlers {
// attribute OnErrorEventHandler onerror;
attribute EventHandler onfocus;
//(Not implemented)attribute EventHandler oncancel;
+ attribute EventHandler onauxclick;
attribute EventHandler oncanplay;
attribute EventHandler oncanplaythrough;
attribute EventHandler onchange;
@@ -128,14 +129,14 @@ interface GlobalEventHandlers {
attribute EventHandler onmozpointerlockerror;
// CSS-Animation and CSS-Transition handlers.
+ attribute EventHandler onanimationcancel;
attribute EventHandler onanimationend;
attribute EventHandler onanimationiteration;
attribute EventHandler onanimationstart;
+ attribute EventHandler ontransitioncancel;
attribute EventHandler ontransitionend;
- // We will ship transitionrun and transitionstart events
- // on Firefox 53. (For detail, see bug 1324985)
-// attribute EventHandler ontransitionrun;
-// attribute EventHandler ontransitionstart;
+ attribute EventHandler ontransitionrun;
+ attribute EventHandler ontransitionstart;
// CSS-Animation and CSS-Transition legacy handlers.
// This handler isn't standard.
diff --git a/dom/webidl/Performance.webidl b/dom/webidl/Performance.webidl
index eaede253c..0bd2677df 100644
--- a/dom/webidl/Performance.webidl
+++ b/dom/webidl/Performance.webidl
@@ -17,6 +17,9 @@ typedef sequence <PerformanceEntry> PerformanceEntryList;
interface Performance {
[DependsOn=DeviceState, Affects=Nothing]
DOMHighResTimeStamp now();
+
+ [Constant]
+ readonly attribute DOMHighResTimeStamp timeOrigin;
};
[Exposed=Window]
diff --git a/dom/webidl/PerformanceNavigationTiming.webidl b/dom/webidl/PerformanceNavigationTiming.webidl
new file mode 100644
index 000000000..fa3ecaec4
--- /dev/null
+++ b/dom/webidl/PerformanceNavigationTiming.webidl
@@ -0,0 +1,33 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://www.w3.org/TR/navigation-timing-2/#sec-PerformanceNavigationTiming
+ *
+ * Copyright © 2016 W3C® (MIT, ERCIM, Keio, Beihang).
+ * W3C liability, trademark and document use rules apply.
+ */
+
+enum NavigationType {
+ "navigate",
+ "reload",
+ "back_forward",
+ "prerender"
+};
+
+interface PerformanceNavigationTiming : PerformanceResourceTiming {
+ readonly attribute DOMHighResTimeStamp unloadEventStart;
+ readonly attribute DOMHighResTimeStamp unloadEventEnd;
+ readonly attribute DOMHighResTimeStamp domInteractive;
+ readonly attribute DOMHighResTimeStamp domContentLoadedEventStart;
+ readonly attribute DOMHighResTimeStamp domContentLoadedEventEnd;
+ readonly attribute DOMHighResTimeStamp domComplete;
+ readonly attribute DOMHighResTimeStamp loadEventStart;
+ readonly attribute DOMHighResTimeStamp loadEventEnd;
+ readonly attribute NavigationType type;
+ readonly attribute unsigned short redirectCount;
+
+ jsonifier;
+};
diff --git a/dom/webidl/PerformanceObserver.webidl b/dom/webidl/PerformanceObserver.webidl
index a3a14cb1e..4cebecbeb 100644
--- a/dom/webidl/PerformanceObserver.webidl
+++ b/dom/webidl/PerformanceObserver.webidl
@@ -9,6 +9,7 @@
dictionary PerformanceObserverInit {
required sequence<DOMString> entryTypes;
+ boolean buffered = false;
};
callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer);
diff --git a/dom/webidl/PerformanceResourceTiming.webidl b/dom/webidl/PerformanceResourceTiming.webidl
index 021b84ae2..112228325 100644
--- a/dom/webidl/PerformanceResourceTiming.webidl
+++ b/dom/webidl/PerformanceResourceTiming.webidl
@@ -4,7 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
- * http://w3c-test.org/webperf/specs/ResourceTiming/#performanceresourcetiming
+ * https://w3c.github.io/resource-timing/#performanceresourcetiming
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
@@ -12,14 +12,10 @@
interface PerformanceResourceTiming : PerformanceEntry
{
- // A string with the name of that element that initiated the load.
- // If the initiator is a CSS resource, the initiatorType attribute must return
- // the string "css".
- // If the initiator is an XMLHttpRequest object, the initiatorType attribute
- // must return the string "xmlhttprequest".
readonly attribute DOMString initiatorType;
readonly attribute DOMString nextHopProtocol;
+ readonly attribute DOMHighResTimeStamp workerStart;
readonly attribute DOMHighResTimeStamp redirectStart;
readonly attribute DOMHighResTimeStamp redirectEnd;
readonly attribute DOMHighResTimeStamp fetchStart;
diff --git a/dom/webidl/PerformanceTiming.webidl b/dom/webidl/PerformanceTiming.webidl
index e14201440..4aa403a50 100644
--- a/dom/webidl/PerformanceTiming.webidl
+++ b/dom/webidl/PerformanceTiming.webidl
@@ -33,5 +33,11 @@ interface PerformanceTiming {
readonly attribute unsigned long long loadEventStart;
readonly attribute unsigned long long loadEventEnd;
+ // This is a Chrome proprietary extension and not part of the
+ // performance/navigation timing specification.
+ // Returns 0 if a non-blank paint has not happened.
+ [Pref="dom.performance.time_to_non_blank_paint.enabled"]
+ readonly attribute unsigned long long timeToNonBlankPaint;
+
jsonifier;
};
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index 8682aee97..8469c9001 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -347,6 +347,7 @@ WEBIDL_FILES = [
'PerformanceMark.webidl',
'PerformanceMeasure.webidl',
'PerformanceNavigation.webidl',
+ 'PerformanceNavigationTiming.webidl',
'PerformanceObserver.webidl',
'PerformanceObserverEntryList.webidl',
'PerformanceResourceTiming.webidl',
diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp
index 780b2f5f8..1f79e2c92 100644
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -12,6 +12,7 @@
#include "nsINetworkInterceptController.h"
#include "nsIOutputStream.h"
#include "nsIScriptError.h"
+#include "nsITimedChannel.h"
#include "nsIUnicodeDecoder.h"
#include "nsIUnicodeEncoder.h"
#include "nsContentPolicyUtils.h"
@@ -108,6 +109,12 @@ NS_IMETHODIMP
CancelChannelRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
+
+ // TODO: When bug 1204254 is implemented, this time marker should be moved to
+ // the point where the body of the network request is complete.
+ mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
+ mChannel->SaveTimeStampsToUnderlyingChannel();
+
mChannel->Cancel(mStatus);
mRegistration->MaybeScheduleUpdate();
return NS_OK;
@@ -230,6 +237,9 @@ public:
return NS_OK;
}
+ mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
+ mChannel->SaveTimeStampsToUnderlyingChannel();
+
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
if (obsService) {
obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr);
diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp
index eaa548f95..24b2e11e6 100644
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -13,6 +13,7 @@
#include "nsINetworkInterceptController.h"
#include "nsIPushErrorReporter.h"
#include "nsISupportsImpl.h"
+#include "nsITimedChannel.h"
#include "nsIUploadChannel2.h"
#include "nsNetUtil.h"
#include "nsProxyRelease.h"
@@ -1255,6 +1256,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable
nsCString mMethod;
nsString mClientId;
bool mIsReload;
+ bool mMarkLaunchServiceWorkerEnd;
RequestCache mCacheMode;
RequestMode mRequestMode;
RequestRedirect mRequestRedirect;
@@ -1273,13 +1275,15 @@ public:
const nsACString& aScriptSpec,
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
const nsAString& aDocumentId,
- bool aIsReload)
+ bool aIsReload,
+ bool aMarkLaunchServiceWorkerEnd)
: ExtendableFunctionalEventWorkerRunnable(
aWorkerPrivate, aKeepAliveToken, aRegistration)
, mInterceptedChannel(aChannel)
, mScriptSpec(aScriptSpec)
, mClientId(aDocumentId)
, mIsReload(aIsReload)
+ , mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd)
, mCacheMode(RequestCache::Default)
, mRequestMode(RequestMode::No_cors)
, mRequestRedirect(RequestRedirect::Follow)
@@ -1417,6 +1421,12 @@ public:
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
MOZ_ASSERT(aWorkerPrivate);
+
+ if (mMarkLaunchServiceWorkerEnd) {
+ mInterceptedChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
+ }
+
+ mInterceptedChannel->SetDispatchFetchEventEnd(TimeStamp::Now());
return DispatchFetchEvent(aCx, aWorkerPrivate);
}
@@ -1445,6 +1455,10 @@ private:
NS_IMETHOD Run() override
{
AssertIsOnMainThread();
+
+ mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
+ mChannel->SaveTimeStampsToUnderlyingChannel();
+
nsresult rv = mChannel->ResetInterception();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to resume intercepted network request");
@@ -1520,6 +1534,8 @@ private:
event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec);
event->SetTrusted(true);
+ mInterceptedChannel->SetHandleFetchEventStart(TimeStamp::Now());
+
RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
@@ -1614,9 +1630,21 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
nsCOMPtr<nsIRunnable> failRunnable =
NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
- nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup);
+ aChannel->SetLaunchServiceWorkerStart(TimeStamp::Now());
+ aChannel->SetDispatchFetchEventStart(TimeStamp::Now());
+
+ bool newWorkerCreated = false;
+ nsresult rv = SpawnWorkerIfNeeded(FetchEvent,
+ failRunnable,
+ &newWorkerCreated,
+ aLoadGroup);
+
NS_ENSURE_SUCCESS(rv, rv);
+ if (!newWorkerCreated) {
+ aChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
+ }
+
nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
@@ -1646,7 +1674,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
RefPtr<FetchEventRunnable> r =
new FetchEventRunnable(mWorkerPrivate, token, handle,
mInfo->ScriptSpec(), regInfo,
- aDocumentId, aIsReload);
+ aDocumentId, aIsReload, newWorkerCreated);
rv = r->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@@ -1669,6 +1697,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
nsresult
ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
nsIRunnable* aLoadFailedRunnable,
+ bool* aNewWorkerCreated,
nsILoadGroup* aLoadGroup)
{
AssertIsOnMainThread();
@@ -1679,6 +1708,12 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
// the overriden load group when intercepting a fetch.
MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup);
+ // Defaults to no new worker created, but if there is one, we'll set the value
+ // to true at the end of this function.
+ if (aNewWorkerCreated) {
+ *aNewWorkerCreated = false;
+ }
+
if (mWorkerPrivate) {
mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
RenewKeepAliveToken(aWhy);
@@ -1762,6 +1797,10 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
RenewKeepAliveToken(aWhy);
+ if (aNewWorkerCreated) {
+ *aNewWorkerCreated = true;
+ }
+
return NS_OK;
}
diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h
index 8d59ea1d0..911b07a11 100644
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -189,6 +189,7 @@ private:
nsresult
SpawnWorkerIfNeeded(WakeUpReason aWhy,
nsIRunnable* aLoadFailedRunnable,
+ bool* aNewWorkerCreated = nullptr,
nsILoadGroup* aLoadGroup = nullptr);
~ServiceWorkerPrivate();
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index bd8a33032..8848e881a 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2419,8 +2419,6 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
MOZ_ASSERT(IsDedicatedWorker());
- mNowBaseTimeStamp = aParent->NowBaseTimeStamp();
- mNowBaseTimeHighRes = aParent->NowBaseTime();
if (aParent->mParentFrozen) {
Freeze(nullptr);
@@ -2451,18 +2449,6 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
.creationOptions().setSecureContext(true);
}
- if (IsDedicatedWorker() && mLoadInfo.mWindow &&
- mLoadInfo.mWindow->GetPerformance()) {
- mNowBaseTimeStamp = mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()->
- GetNavigationStartTimeStamp();
- mNowBaseTimeHighRes =
- mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()->
- GetNavigationStartHighRes();
- } else {
- mNowBaseTimeStamp = CreationTimeStamp();
- mNowBaseTimeHighRes = CreationTime();
- }
-
// Our parent can get suspended after it initiates the async creation
// of a new worker thread. In this case suspend the new worker as well.
if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
index 465c0f9a3..28283bed7 100644
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -216,8 +216,6 @@ private:
WorkerType mWorkerType;
TimeStamp mCreationTimeStamp;
DOMHighResTimeStamp mCreationTimeHighRes;
- TimeStamp mNowBaseTimeStamp;
- DOMHighResTimeStamp mNowBaseTimeHighRes;
protected:
// The worker is owned by its thread, which is represented here. This is set
@@ -579,14 +577,11 @@ public:
return mCreationTimeHighRes;
}
- TimeStamp NowBaseTimeStamp() const
+ DOMHighResTimeStamp TimeStampToDOMHighRes(const TimeStamp& aTimeStamp) const
{
- return mNowBaseTimeStamp;
- }
-
- DOMHighResTimeStamp NowBaseTime() const
- {
- return mNowBaseTimeHighRes;
+ MOZ_ASSERT(!aTimeStamp.IsNull());
+ TimeDuration duration = aTimeStamp - mCreationTimeStamp;
+ return duration.ToMilliseconds();
}
nsIPrincipal*
diff --git a/dom/workers/test/serviceworkers/chrome.ini b/dom/workers/test/serviceworkers/chrome.ini
index e064e7fd0..6d7dbebd0 100644
--- a/dom/workers/test/serviceworkers/chrome.ini
+++ b/dom/workers/test/serviceworkers/chrome.ini
@@ -3,6 +3,8 @@ skip-if = os == 'android'
support-files =
chrome_helpers.js
empty.js
+ fetch.js
+ hello.html
serviceworker.html
serviceworkerinfo_iframe.html
serviceworkermanager_iframe.html
@@ -10,6 +12,7 @@ support-files =
worker.js
worker2.js
+[test_devtools_serviceworker_interception.html]
[test_privateBrowsing.html]
[test_serviceworkerinfo.xul]
[test_serviceworkermanager.xul]
diff --git a/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
new file mode 100644
index 000000000..d49ebb2c9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
@@ -0,0 +1,168 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1168875 - test devtools serviceworker interception.</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet"
+ type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+// Constants
+const Ci = Components.interfaces;
+const workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/";
+const workerURL = workerScope + "fetch.js";
+const contentPage = workerScope + "hello.html";
+
+function createTestWindow(aURL) {
+ var mainwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ var win = mainwindow.OpenBrowserWindow(contentPage);
+
+ return new Promise(aResolve => {
+ win.addEventListener("DOMContentLoaded", function callback() {
+ if (win.content.location.href != aURL) {
+ win.gBrowser.loadURI(aURL);
+ return;
+ }
+
+ win.removeEventListener("DOMContentLoaded", callback);
+ aResolve(win.content);
+ });
+ });
+}
+
+function executeTest(aWindow) {
+ var registration;
+
+ return Promise.resolve()
+ // Should not be intercepted.
+ .then(_ => fetchAndCheckTimedChannel(aWindow, false, true, "hello.html"))
+
+ // Regist a service worker.
+ .then(_ => register(aWindow, workerURL, workerScope))
+ .then(r => registration = r)
+
+ // Should be intercpeted and synthesized.
+ .then(_ => fetchAndCheckTimedChannel(aWindow, true, false, "fake.html"))
+
+ // Should be intercepted but still fetch from network.
+ .then(_ => fetchAndCheckTimedChannel(aWindow, true, true,
+ "hello.html?ForBypassingHttpCache"))
+
+ // Tear down
+ .then(_ => registration.unregister());
+}
+
+function register(aWindow, aURL, aScope) {
+ return aWindow.navigator.serviceWorker.register(aURL, {scope: aScope})
+ .then(r => {
+ var worker = r.installing;
+ return new Promise(function(aResolve) {
+ worker.onstatechange = function() {
+ if (worker.state == "activated") {
+ aResolve(r);
+ }
+ }
+ });
+ });
+}
+
+function fetchAndCheckTimedChannel(aWindow, aIntercepted, aFetch, aURL) {
+ var resolveFunction;
+ var promise = new Promise(aResolve => resolveFunction = aResolve);
+
+ var topic = aFetch ? "http-on-examine-response"
+ : "service-worker-synthesized-response";
+
+ function observer(aSubject) {
+ var channel = aSubject.QueryInterface(Ci.nsIChannel);
+ ok(channel.URI.spec.endsWith(aURL));
+
+ var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
+
+ // Check service worker related timings.
+ var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
+ end: tc.launchServiceWorkerEndTime},
+ {start: tc.dispatchFetchEventStartTime,
+ end: tc.dispatchFetchEventEndTime},
+ {start: tc.handleFetchEventStartTime,
+ end: tc.handleFetchEventEndTime}];
+ if (aIntercepted) {
+ serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
+ ok(aPreviousTimings.start <= aCurrentTimings.start,
+ "Start time order check.");
+ ok(aPreviousTimings.end <= aCurrentTimings.end,
+ "End time order check.");
+ ok(aCurrentTimings.start <= aCurrentTimings.end,
+ "Start time should be smaller than end time.");
+ return aCurrentTimings;
+ });
+ } else {
+ serviceWorkerTimings.forEach(aTimings => {
+ is(aTimings.start, 0);
+ is(aTimings.end, 0);
+ });
+ }
+
+ // Check network related timings.
+ var networkTimings = [tc.domainLookupStartTime,
+ tc.domainLookupEndTime,
+ tc.connectStartTime,
+ tc.connectEndTime,
+ tc.requestStartTime,
+ tc.responseStartTime,
+ tc.responseEndTime];
+ if (aFetch) {
+ networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
+ ok(aPreviousTiming <= aCurrentTiming);
+ return aCurrentTiming;
+ });
+ } else {
+ networkTimings.forEach(aTiming => is(aTiming, 0));
+ }
+
+ SpecialPowers.removeObserver(observer, topic);
+ resolveFunction();
+ }
+
+ SpecialPowers.addObserver(observer, topic, false);
+
+ // return promise;
+ return Promise.all([aWindow.fetch(aURL), promise]);
+}
+
+function runTest() {
+ return Promise.resolve()
+ .then(_ => createTestWindow(contentPage))
+ .then(w => executeTest(w))
+ .catch(e => ok(false, "Some test failed with error " + e))
+ .then(_ => SimpleTest.finish());
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
index 9dbfcc099..a4d498fb8 100644
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -172,9 +172,9 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceMeasure",
// IMPORTANT: Do not change this list without review from a DOM peer!
- { name: "PerformanceObserver", nightly: true },
+ "PerformanceObserver",
// IMPORTANT: Do not change this list without review from a DOM peer!
- { name: "PerformanceObserverEntryList", nightly: true },
+ "PerformanceObserverEntryList",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Request",
// IMPORTANT: Do not change this list without review from a DOM peer!
diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js
index e0647682c..6fe5fcaff 100644
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -163,9 +163,9 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceMeasure",
// IMPORTANT: Do not change this list without review from a DOM peer!
- { name: "PerformanceObserver", nightly: true },
+ "PerformanceObserver",
// IMPORTANT: Do not change this list without review from a DOM peer!
- { name: "PerformanceObserverEntryList", nightly: true },
+ "PerformanceObserverEntryList",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Request",
// IMPORTANT: Do not change this list without review from a DOM peer!
diff --git a/dom/xslt/xpath/txXPCOMExtensionFunction.cpp b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp
index 4913702aa..032161722 100644
--- a/dom/xslt/xpath/txXPCOMExtensionFunction.cpp
+++ b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp
@@ -322,7 +322,7 @@ public:
void trace(JSTracer* trc) {
for (uint8_t i = 0; i < mCount; ++i) {
if (mArray[i].type == nsXPTType::T_JSVAL) {
- JS::UnsafeTraceRoot(trc, &mArray[i].val.j, "txParam value");
+ JS::UnsafeTraceRoot(trc, &mArray[i].val.j.asValueRef(), "txParam value");
}
}
}