mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Merge m-c to autoland
MozReview-Commit-ID: 4PrtfU4e6kL
This commit is contained in:
commit
c62e9f1e9d
@ -102,16 +102,17 @@ var SessionCookiesInternal = {
|
||||
* cookies service and puts them into the store if they're session cookies.
|
||||
*/
|
||||
_ensureInitialized() {
|
||||
if (!this._initialized) {
|
||||
this._reloadCookies();
|
||||
this._initialized = true;
|
||||
Services.obs.addObserver(this, "cookie-changed");
|
||||
|
||||
// Listen for privacy level changes to reload cookies when needed.
|
||||
Services.prefs.addObserver("browser.sessionstore.privacy_level", () => {
|
||||
this._reloadCookies();
|
||||
});
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._reloadCookies();
|
||||
this._initialized = true;
|
||||
Services.obs.addObserver(this, "cookie-changed");
|
||||
|
||||
// Listen for privacy level changes to reload cookies when needed.
|
||||
Services.prefs.addObserver("browser.sessionstore.privacy_level", () => {
|
||||
this._reloadCookies();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -172,7 +173,7 @@ var SessionCookiesInternal = {
|
||||
return;
|
||||
}
|
||||
|
||||
let iter = Services.cookies.enumerator;
|
||||
let iter = Services.cookies.sessionEnumerator;
|
||||
while (iter.hasMoreElements()) {
|
||||
this._addCookie(iter.getNext());
|
||||
}
|
||||
|
@ -569,6 +569,12 @@ JS_IsDeadWrapper(JSObject* obj)
|
||||
return IsDeadProxyObject(obj);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject*)
|
||||
JS_NewDeadWrapper(JSContext* cx, JSObject* origObj)
|
||||
{
|
||||
return NewDeadProxyObject(cx, origObj);
|
||||
}
|
||||
|
||||
void
|
||||
js::TraceWeakMaps(WeakMapTracer* trc)
|
||||
{
|
||||
|
@ -96,6 +96,16 @@ JS_PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp = nullptr)
|
||||
extern JS_FRIEND_API(bool)
|
||||
JS_IsDeadWrapper(JSObject* obj);
|
||||
|
||||
/**
|
||||
* Creates a new dead wrapper object in the given scope. To be used when
|
||||
* attempting to wrap objects from scopes which are already dead.
|
||||
*
|
||||
* If origObject is passed, it must be an proxy object, and will be
|
||||
* used to determine the characteristics of the new dead wrapper.
|
||||
*/
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
JS_NewDeadWrapper(JSContext* cx, JSObject* origObject = nullptr);
|
||||
|
||||
/*
|
||||
* Used by the cycle collector to trace through a shape or object group and
|
||||
* all cycle-participating data it reaches, using bounded stack space.
|
||||
|
@ -185,3 +185,37 @@ js::IsDeadProxyObject(JSObject* obj)
|
||||
IsDerivedProxyObject(obj, DeadObjectProxy<DeadProxyIsCallableNotConstructor>::singleton()) ||
|
||||
IsDerivedProxyObject(obj, DeadObjectProxy<DeadProxyNotCallableIsConstructor>::singleton());
|
||||
}
|
||||
|
||||
|
||||
const BaseProxyHandler*
|
||||
js::SelectDeadProxyHandler(ProxyObject* obj)
|
||||
{
|
||||
// When nuking scripted proxies, isCallable and isConstructor values for
|
||||
// the proxy needs to be preserved.
|
||||
uint32_t callable = obj->handler()->isCallable(obj);
|
||||
uint32_t constructor = obj->handler()->isConstructor(obj);
|
||||
|
||||
if (callable) {
|
||||
if (constructor)
|
||||
return DeadObjectProxy<DeadProxyIsCallableIsConstructor>::singleton();
|
||||
return DeadObjectProxy<DeadProxyIsCallableNotConstructor>::singleton();
|
||||
}
|
||||
|
||||
if (constructor)
|
||||
return DeadObjectProxy<DeadProxyNotCallableIsConstructor>::singleton();
|
||||
return DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewDeadProxyObject(JSContext* cx, JSObject* origObj)
|
||||
{
|
||||
MOZ_ASSERT_IF(origObj, origObj->is<ProxyObject>());
|
||||
|
||||
const BaseProxyHandler* handler;
|
||||
if (origObj && origObj->is<ProxyObject>())
|
||||
handler = SelectDeadProxyHandler(&origObj->as<ProxyObject>());
|
||||
else
|
||||
handler = DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton();
|
||||
|
||||
return NewProxyObject(cx, handler, NullHandleValue, nullptr, ProxyOptions());
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
class ProxyObject;
|
||||
|
||||
enum DeadProxyIsCallableIsConstructorOption
|
||||
{
|
||||
DeadProxyNotCallableNotConstructor,
|
||||
@ -79,6 +81,12 @@ class DeadObjectProxy : public BaseProxyHandler
|
||||
bool
|
||||
IsDeadProxyObject(JSObject* obj);
|
||||
|
||||
const BaseProxyHandler*
|
||||
SelectDeadProxyHandler(ProxyObject* obj);
|
||||
|
||||
JSObject*
|
||||
NewDeadProxyObject(JSContext* cx, JSObject* origObj = nullptr);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* proxy_DeadObjectProxy_h */
|
||||
|
@ -125,26 +125,15 @@ ProxyObject::setSameCompartmentPrivate(const Value& priv)
|
||||
void
|
||||
ProxyObject::nuke()
|
||||
{
|
||||
// When nuking scripted proxies, isCallable and isConstructor values for
|
||||
// the proxy needs to be preserved. Do this before clearing the target.
|
||||
uint32_t callable = handler()->isCallable(this);
|
||||
uint32_t constructor = handler()->isConstructor(this);
|
||||
// Select a dead proxy handler based on the properties of this wrapper.
|
||||
// Do this before clearing the target.
|
||||
const BaseProxyHandler* handler = SelectDeadProxyHandler(this);
|
||||
|
||||
// Clear the target reference.
|
||||
setSameCompartmentPrivate(NullValue());
|
||||
|
||||
// Update the handler to make this a DeadObjectProxy.
|
||||
if (callable) {
|
||||
if (constructor)
|
||||
setHandler(DeadObjectProxy<DeadProxyIsCallableIsConstructor>::singleton());
|
||||
else
|
||||
setHandler(DeadObjectProxy<DeadProxyIsCallableNotConstructor>::singleton());
|
||||
} else {
|
||||
if (constructor)
|
||||
setHandler(DeadObjectProxy<DeadProxyNotCallableIsConstructor>::singleton());
|
||||
else
|
||||
setHandler(DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton());
|
||||
}
|
||||
setHandler(handler);
|
||||
|
||||
// The proxy's reserved slots are not cleared and will continue to be
|
||||
// traced. This avoids the possibility of triggering write barriers while
|
||||
|
@ -2841,10 +2841,11 @@ nsXPCComponents_Utils::IsDeadWrapper(HandleValue obj, bool* out)
|
||||
if (obj.isPrimitive())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Make sure to unwrap first. Once a proxy is nuked, it ceases to be a
|
||||
// wrapper, meaning that, if passed to another compartment, we'll generate
|
||||
// a CCW for it. Make sure that IsDeadWrapper sees through the confusion.
|
||||
*out = JS_IsDeadWrapper(js::CheckedUnwrap(&obj.toObject()));
|
||||
// We should never have cross-compartment wrappers for dead wrappers.
|
||||
MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(&obj.toObject()),
|
||||
!JS_IsDeadWrapper(js::UncheckedUnwrap(&obj.toObject())));
|
||||
|
||||
*out = JS_IsDeadWrapper(&obj.toObject());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
33
js/xpconnect/tests/unit/test_rewrap_dead_wrapper.js
Normal file
33
js/xpconnect/tests/unit/test_rewrap_dead_wrapper.js
Normal file
@ -0,0 +1,33 @@
|
||||
/* 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/. */
|
||||
|
||||
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1354733 */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
const global = this;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
var sb = Cu.Sandbox(global);
|
||||
let obj = new sb.Object();
|
||||
Cu.nukeSandbox(sb);
|
||||
|
||||
ok(Cu.isDeadWrapper(obj), "object should be a dead wrapper");
|
||||
|
||||
// Create a new sandbox to wrap objects for.
|
||||
|
||||
var sb = Cu.Sandbox(global);
|
||||
Cu.evalInSandbox(function echo(val) { return val; },
|
||||
sb);
|
||||
|
||||
let echoed = sb.echo(obj);
|
||||
ok(Cu.isDeadWrapper(echoed), "Rewrapped object should be a dead wrapper");
|
||||
ok(echoed !== obj, "Rewrapped object should be a new dead wrapper");
|
||||
|
||||
ok(obj === obj, "Dead wrapper object should be equal to itself");
|
||||
|
||||
let liveObj = {};
|
||||
ok(liveObj === sb.echo(liveObj), "Rewrapped live object should be equal to itself");
|
||||
}
|
@ -99,6 +99,7 @@ skip-if = toolkit == "android" # bug 1347431
|
||||
[test_nuke_sandbox.js]
|
||||
[test_nuke_sandbox_event_listeners.js]
|
||||
[test_nuke_webextension_wrappers.js]
|
||||
[test_rewrap_dead_wrapper.js]
|
||||
[test_sandbox_metadata.js]
|
||||
[test_exportFunction.js]
|
||||
[test_promise.js]
|
||||
|
@ -181,6 +181,13 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
|
||||
ExposeObjectToActiveJS(obj);
|
||||
}
|
||||
|
||||
// If the object is a dead wrapper, return a new dead wrapper rather than
|
||||
// trying to wrap it for a different compartment.
|
||||
if (JS_IsDeadWrapper(obj)) {
|
||||
retObj.set(JS_NewDeadWrapper(cx, obj));
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've somehow gotten to this point after either the source or target
|
||||
// compartment has been nuked, return a DeadObjectProxy to prevent further
|
||||
// access.
|
||||
@ -190,11 +197,7 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
|
||||
CompartmentPrivate::Get(target)->wasNuked) {
|
||||
NS_WARNING("Trying to create a wrapper into or out of a nuked compartment");
|
||||
|
||||
RootedObject ccw(cx, Wrapper::New(cx, obj, &CrossCompartmentWrapper::singleton));
|
||||
|
||||
NukeCrossCompartmentWrapper(cx, ccw);
|
||||
|
||||
retObj.set(ccw);
|
||||
retObj.set(JS_NewDeadWrapper(cx));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ nsAbsoluteContainingBlock::RemoveFrame(nsIFrame* aDelegatingFrame,
|
||||
void
|
||||
nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
|
||||
nsPresContext* aPresContext,
|
||||
const ReflowInput& aReflowInput,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aReflowStatus,
|
||||
const nsRect& aContainingBlock,
|
||||
AbsPosReflowFlags aFlags,
|
||||
@ -130,11 +130,37 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
|
||||
FrameDependsOnContainer(kidFrame,
|
||||
!!(aFlags & AbsPosReflowFlags::eCBWidthChanged),
|
||||
!!(aFlags & AbsPosReflowFlags::eCBHeightChanged));
|
||||
nscoord availBSize = aReflowInput.AvailableBSize();
|
||||
const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame)
|
||||
: aContainingBlock;
|
||||
WritingMode containerWM = aReflowInput.GetWritingMode();
|
||||
if (!kidNeedsReflow && availBSize != NS_UNCONSTRAINEDSIZE) {
|
||||
// If we need to redo pagination on the kid, we need to reflow it.
|
||||
// This can happen either if the available height shrunk and the
|
||||
// kid (or its overflow that creates overflow containers) is now
|
||||
// too large to fit in the available height, or if the available
|
||||
// height has increased and the kid has a next-in-flow that we
|
||||
// might need to pull from.
|
||||
WritingMode kidWM = kidFrame->GetWritingMode();
|
||||
if (containerWM.GetBlockDir() != kidWM.GetBlockDir()) {
|
||||
// Not sure what the right test would be here.
|
||||
kidNeedsReflow = true;
|
||||
} else {
|
||||
nscoord kidBEnd = kidFrame->GetLogicalRect(cb.Size()).BEnd(kidWM);
|
||||
nscoord kidOverflowBEnd =
|
||||
LogicalRect(containerWM,
|
||||
kidFrame->GetScrollableOverflowRectRelativeToParent(),
|
||||
aContainingBlock.Size()).BEnd(containerWM);
|
||||
MOZ_ASSERT(kidOverflowBEnd >= kidBEnd);
|
||||
if (kidOverflowBEnd > availBSize ||
|
||||
(kidBEnd < availBSize && kidFrame->GetNextInFlow())) {
|
||||
kidNeedsReflow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) {
|
||||
// Reflow the frame
|
||||
nsReflowStatus kidStatus;
|
||||
const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame)
|
||||
: aContainingBlock;
|
||||
ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowInput, cb,
|
||||
aFlags, kidFrame, kidStatus, aOverflowAreas);
|
||||
nsIFrame* nextFrame = kidFrame->GetNextInFlow();
|
||||
|
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Test for dynamic re-pagination of absolutely positioned elements</title>
|
||||
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
|
||||
<style>
|
||||
|
||||
#multicol {
|
||||
height: 100px;
|
||||
width: 300px;
|
||||
background: yellow;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#relpos {
|
||||
position: absolute;
|
||||
background: aqua;
|
||||
height: 100px;
|
||||
width: 90px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#abspos {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 80px;
|
||||
width: 50px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="multicol">
|
||||
<div id="relpos" style="left: 0">
|
||||
<div id="abspos" style="top: 80px; height: 20px"></div>
|
||||
</div>
|
||||
<div id="relpos" style="left: 105px">
|
||||
<div id="abspos" style="top: 0px; height: 60px"></div>
|
||||
</div>
|
||||
<div id="relpos" style="left: 210px; height: 50px">
|
||||
</div>
|
||||
</div>
|
44
layout/reftests/pagination/abspos-breaking-dynamic-001.html
Normal file
44
layout/reftests/pagination/abspos-breaking-dynamic-001.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Test for dynamic re-pagination of absolutely positioned elements</title>
|
||||
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
|
||||
<style>
|
||||
|
||||
#multicol {
|
||||
columns: 3;
|
||||
column-fill: auto;
|
||||
column-gap: 15px;
|
||||
height: 150px;
|
||||
width: 300px;
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
#relpos {
|
||||
position: relative;
|
||||
background: aqua;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
#abspos {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
right: 0;
|
||||
height: 80px;
|
||||
width: 50px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="multicol">
|
||||
<div id="relpos">
|
||||
<div id="abspos"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var mc = document.getElementById("multicol");
|
||||
mc.offsetHeight; // flush layout
|
||||
mc.style.height = "100px";
|
||||
|
||||
</script>
|
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Test for dynamic re-pagination of absolutely positioned elements</title>
|
||||
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
|
||||
<style>
|
||||
|
||||
#multicol {
|
||||
height: 150px;
|
||||
width: 300px;
|
||||
background: yellow;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#relpos {
|
||||
position: absolute;
|
||||
background: aqua;
|
||||
height: 150px;
|
||||
width: 90px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#abspos {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 80px;
|
||||
width: 50px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="multicol">
|
||||
<div id="relpos" style="left: 0">
|
||||
<div id="abspos" style="top: 80px; height: 70px"></div>
|
||||
</div>
|
||||
<div id="relpos" style="left: 105px; height: 100px;">
|
||||
<div id="abspos" style="top: 0px; height: 10px"></div>
|
||||
</div>
|
||||
</div>
|
44
layout/reftests/pagination/abspos-breaking-dynamic-002.html
Normal file
44
layout/reftests/pagination/abspos-breaking-dynamic-002.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Test for dynamic re-pagination of absolutely positioned elements</title>
|
||||
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
|
||||
<style>
|
||||
|
||||
#multicol {
|
||||
columns: 3;
|
||||
column-fill: auto;
|
||||
column-gap: 15px;
|
||||
height: 100px;
|
||||
width: 300px;
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
#relpos {
|
||||
position: relative;
|
||||
background: aqua;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
#abspos {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
right: 0;
|
||||
height: 80px;
|
||||
width: 50px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="multicol">
|
||||
<div id="relpos">
|
||||
<div id="abspos"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var mc = document.getElementById("multicol");
|
||||
mc.offsetHeight; // flush layout
|
||||
mc.style.height = "150px";
|
||||
|
||||
</script>
|
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Test for dynamic re-pagination of absolutely positioned elements</title>
|
||||
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
|
||||
<style>
|
||||
|
||||
#multicol {
|
||||
height: 140px;
|
||||
width: 300px;
|
||||
background: yellow;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#relpos {
|
||||
position: absolute;
|
||||
background: aqua;
|
||||
height: 140px;
|
||||
width: 90px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#abspos {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 80px;
|
||||
width: 50px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
#overflow {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
background: grey;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="multicol">
|
||||
<div id="relpos" style="left: 0">
|
||||
<div id="abspos" style="top: 60px; height: 80px">
|
||||
<div id="overflow" style="height: 80px"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="relpos" style="left: 105px; height: 110px;">
|
||||
<div id="abspos" style="top: 0px; height: 0px">
|
||||
<div id="overflow" style="height: 20px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
49
layout/reftests/pagination/abspos-breaking-dynamic-003.html
Normal file
49
layout/reftests/pagination/abspos-breaking-dynamic-003.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Test for dynamic re-pagination of absolutely positioned elements</title>
|
||||
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
|
||||
<style>
|
||||
|
||||
#multicol {
|
||||
columns: 3;
|
||||
column-fill: auto;
|
||||
column-gap: 15px;
|
||||
height: 500px;
|
||||
width: 300px;
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
#relpos {
|
||||
position: relative;
|
||||
background: aqua;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
#abspos {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 0;
|
||||
height: 80px;
|
||||
width: 50px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
#overflow {
|
||||
height: 100px;
|
||||
width: 30px;
|
||||
background: grey;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="multicol">
|
||||
<div id="relpos">
|
||||
<div id="abspos"><div id="overflow"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var mc = document.getElementById("multicol");
|
||||
mc.offsetHeight; // flush layout
|
||||
mc.style.height = "140px";
|
||||
|
||||
</script>
|
@ -13,6 +13,9 @@ pref(layout.css.box-decoration-break.enabled,true) == abspos-breaking-008.html a
|
||||
pref(layout.css.box-decoration-break.enabled,true) == abspos-breaking-009.html abspos-breaking-009-ref.html
|
||||
pref(layout.css.box-decoration-break.enabled,true) == abspos-breaking-010.html abspos-breaking-010-ref.html
|
||||
== abspos-breaking-011.html abspos-breaking-011-ref.html
|
||||
== abspos-breaking-dynamic-001.html abspos-breaking-dynamic-001-ref.html
|
||||
== abspos-breaking-dynamic-002.html abspos-breaking-dynamic-002-ref.html
|
||||
== abspos-breaking-dynamic-003.html abspos-breaking-dynamic-003-ref.html
|
||||
== abspos-overflow-01.xhtml abspos-overflow-01.ref.xhtml
|
||||
== abspos-overflow-01-cols.xhtml abspos-overflow-01-cols.ref.xhtml
|
||||
== border-breaking-000-cols.xhtml border-breaking-000-cols.ref.xhtml
|
||||
|
@ -93,6 +93,10 @@ to mochitest command.
|
||||
* test_value_storage.html `font-variant` [167]
|
||||
* test_specified_value_serialization.html `bug-721136` [1]
|
||||
* Unsupported prefixed values
|
||||
* moz-prefixed gradient functions bug 1337655
|
||||
* test_value_storage.html `-moz-linear-gradient` [322]
|
||||
* ... `-moz-radial-gradient` [309]
|
||||
* ... `-moz-repeating-` [298]
|
||||
* serialization of prefixed gradient functions bug 1358710
|
||||
* test_specified_value_serialization.html `-webkit-radial-gradient` [1]
|
||||
* moz-prefixed intrinsic width values bug 1355402
|
||||
|
@ -1221,6 +1221,14 @@ pref("dom.send_after_paint_to_content", false);
|
||||
pref("dom.min_timeout_value", 4);
|
||||
// And for background windows
|
||||
pref("dom.min_background_timeout_value", 1000);
|
||||
// Timeout clamp in ms for tracking timeouts we clamp
|
||||
// Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
|
||||
pref("dom.min_tracking_timeout_value", 4);
|
||||
// And for background windows
|
||||
// Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
|
||||
pref("dom.min_tracking_background_timeout_value", 10000);
|
||||
// Delay in ms from document load until we start throttling tracking timeouts.
|
||||
pref("dom.timeout.tracking_throttling_delay", 30000);
|
||||
|
||||
// Don't use new input types
|
||||
pref("dom.experimental_forms", false);
|
||||
@ -2793,11 +2801,7 @@ pref("layout.css.prefixes.box-sizing", true);
|
||||
pref("layout.css.prefixes.font-features", true);
|
||||
|
||||
// Is -moz-prefixed gradient functions enabled?
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("layout.css.prefixes.gradients", false);
|
||||
#else
|
||||
pref("layout.css.prefixes.gradients", true);
|
||||
#endif
|
||||
|
||||
// Are webkit-prefixed properties & property-values supported?
|
||||
pref("layout.css.prefixes.webkit", true);
|
||||
|
@ -2361,6 +2361,31 @@ nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
|
||||
return NS_NewArrayEnumerator(aEnumerator, cookieList);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCookieService::GetSessionEnumerator(nsISimpleEnumerator **aEnumerator)
|
||||
{
|
||||
if (!mDBState) {
|
||||
NS_WARNING("No DBState! Profile already closed?");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
EnsureReadComplete();
|
||||
|
||||
nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
|
||||
for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
const nsCookieEntry::ArrayType& cookies = iter.Get()->GetCookies();
|
||||
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
|
||||
nsCookie* cookie = cookies[i];
|
||||
// Filter out non-session cookies.
|
||||
if (cookie->IsSession()) {
|
||||
cookieList.AppendObject(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_NewArrayEnumerator(aEnumerator, cookieList);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
InitializeOriginAttributes(OriginAttributes* aAttrs,
|
||||
JS::HandleValue aOriginAttributes,
|
||||
|
@ -41,6 +41,12 @@ interface nsICookieManager : nsISupports
|
||||
*/
|
||||
readonly attribute nsISimpleEnumerator enumerator;
|
||||
|
||||
/**
|
||||
* Called to enumerate through each session cookie in the cookie list.
|
||||
* The objects enumerated over are of type nsICookie
|
||||
*/
|
||||
readonly attribute nsISimpleEnumerator sessionEnumerator;
|
||||
|
||||
/**
|
||||
* Called to remove an individual cookie from the cookie list, specified
|
||||
* by host, name, and path. If the cookie cannot be found, no exception
|
||||
|
@ -156,7 +156,6 @@ jobs:
|
||||
treeherder:
|
||||
symbol: SM-tc(tsan)
|
||||
tier: 3
|
||||
run-on-projects: []
|
||||
run:
|
||||
spidermonkey-variant: tsan
|
||||
tooltool-manifest: browser/config/tooltool-manifests/linux64/tsan.manifest
|
||||
|
@ -58563,6 +58563,36 @@
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/observer-in-iframe.html": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/resources/cross-origin-subframe.html": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/resources/iframe-no-root-subframe.html": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/resources/intersection-observer-test-utils.js": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/resources/observer-in-iframe-subframe.html": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/resources/timestamp-subframe.html": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"js/builtins/Math.maxmin.js": [
|
||||
[
|
||||
{}
|
||||
@ -104111,6 +104141,138 @@
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/client-rect.html": [
|
||||
[
|
||||
"/intersection-observer/client-rect.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/containing-block.html": [
|
||||
[
|
||||
"/intersection-observer/containing-block.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/cross-origin-iframe.html": [
|
||||
[
|
||||
"/intersection-observer/cross-origin-iframe.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/disconnect.html": [
|
||||
[
|
||||
"/intersection-observer/disconnect.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/display-none.html": [
|
||||
[
|
||||
"/intersection-observer/display-none.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/edge-inclusive-intersection.html": [
|
||||
[
|
||||
"/intersection-observer/edge-inclusive-intersection.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/iframe-no-root.html": [
|
||||
[
|
||||
"/intersection-observer/iframe-no-root.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/multiple-targets.html": [
|
||||
[
|
||||
"/intersection-observer/multiple-targets.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/multiple-thresholds.html": [
|
||||
[
|
||||
"/intersection-observer/multiple-thresholds.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/observer-attributes.html": [
|
||||
[
|
||||
"/intersection-observer/observer-attributes.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/observer-exceptions.html": [
|
||||
[
|
||||
"/intersection-observer/observer-exceptions.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/observer-without-js-reference.html": [
|
||||
[
|
||||
"/intersection-observer/observer-without-js-reference.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/remove-element.html": [
|
||||
[
|
||||
"/intersection-observer/remove-element.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/root-margin.html": [
|
||||
[
|
||||
"/intersection-observer/root-margin.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/same-document-no-root.html": [
|
||||
[
|
||||
"/intersection-observer/same-document-no-root.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/same-document-root.html": [
|
||||
[
|
||||
"/intersection-observer/same-document-root.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/same-document-zero-size-target.html": [
|
||||
[
|
||||
"/intersection-observer/same-document-zero-size-target.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/shadow-content.html": [
|
||||
[
|
||||
"/intersection-observer/shadow-content.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/timestamp.html": [
|
||||
[
|
||||
"/intersection-observer/timestamp.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/unclipped-root.html": [
|
||||
[
|
||||
"/intersection-observer/unclipped-root.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/zero-area-element-hidden.html": [
|
||||
[
|
||||
"/intersection-observer/zero-area-element-hidden.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"intersection-observer/zero-area-element-visible.html": [
|
||||
[
|
||||
"/intersection-observer/zero-area-element-visible.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"js/behaviours/SetPrototypeOf-window.html": [
|
||||
[
|
||||
"/js/behaviours/SetPrototypeOf-window.html",
|
||||
@ -191666,6 +191828,118 @@
|
||||
"4f94c4236168ed722f71d81bd957e0da72b29c71",
|
||||
"support"
|
||||
],
|
||||
"intersection-observer/client-rect.html": [
|
||||
"acec9a4f59ebee1840950cf766a45676490eef84",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/containing-block.html": [
|
||||
"8bdf6fa6a3ee09130981bf83728aa9f61a6ebc54",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/cross-origin-iframe.html": [
|
||||
"98ac8bd3447bc16e86c4b15708dcd86aa4b5ed92",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/disconnect.html": [
|
||||
"d8206b91756a367394ea9d444deefc0f37589427",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/display-none.html": [
|
||||
"3dc956e76b23ae44f2694f4b24579f896a1086b5",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/edge-inclusive-intersection.html": [
|
||||
"97921caeabdafb402de1a6f64fc28176eda3e6db",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/iframe-no-root.html": [
|
||||
"6cbea44f209e59ea0b901b0fe1cec7ac1aee0b64",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/multiple-targets.html": [
|
||||
"83173248b825b97063c61dd31c64d8fe7ba10aac",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/multiple-thresholds.html": [
|
||||
"dcfa7d005d1f94186b173b20d4f9c367abc5a77b",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/observer-attributes.html": [
|
||||
"69c802af77a38c450ce81c4c010588b0e4f240db",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/observer-exceptions.html": [
|
||||
"28ccc6905713894b43033e30949170439215bf2e",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/observer-in-iframe.html": [
|
||||
"3e6dcbc7aac43899186395f50649195b2ecc4c74",
|
||||
"support"
|
||||
],
|
||||
"intersection-observer/observer-without-js-reference.html": [
|
||||
"4211529e1fdd3bfb9b3b64c29b7d000b3c706408",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/remove-element.html": [
|
||||
"f2b3688fd8e069a41e16086baad74bda0fe2f180",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/resources/cross-origin-subframe.html": [
|
||||
"91073aed8b250177d69ee36e9be95c30c9cf1c95",
|
||||
"support"
|
||||
],
|
||||
"intersection-observer/resources/iframe-no-root-subframe.html": [
|
||||
"a9ea444d6bb15cda784871e2d495dc204b0e3d50",
|
||||
"support"
|
||||
],
|
||||
"intersection-observer/resources/intersection-observer-test-utils.js": [
|
||||
"889d403e334e3c0fc4ae7a6af06ef9504145fa2b",
|
||||
"support"
|
||||
],
|
||||
"intersection-observer/resources/observer-in-iframe-subframe.html": [
|
||||
"5f390b2e969132bce9ab486cbf2292765d5c62f2",
|
||||
"support"
|
||||
],
|
||||
"intersection-observer/resources/timestamp-subframe.html": [
|
||||
"7b590da7cf426cc7d2e3f25fac30f4533e36153c",
|
||||
"support"
|
||||
],
|
||||
"intersection-observer/root-margin.html": [
|
||||
"be98d804610d2c08ee516a4aecbe59ffc58c22bd",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/same-document-no-root.html": [
|
||||
"0f29996e4542db243c3fcd9c108463ba7260cb8e",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/same-document-root.html": [
|
||||
"71876c5290184f77223843c7d3c0bed670e2ee3f",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/same-document-zero-size-target.html": [
|
||||
"c1d1054447e116becb50aaf96aad00a25f0a6752",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/shadow-content.html": [
|
||||
"11681640d5c8b2c62229ed5a717172f917d75ba4",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/timestamp.html": [
|
||||
"cf4e91716ed1d02935c3c73ee639e566cf5b60a4",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/unclipped-root.html": [
|
||||
"67dab96304c745f1b5462bb1074753b09d77fbd1",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/zero-area-element-hidden.html": [
|
||||
"59d854e89ca0d7b035a87376566775ca2f3420e5",
|
||||
"testharness"
|
||||
],
|
||||
"intersection-observer/zero-area-element-visible.html": [
|
||||
"f3e1fbeb1a912be412724cec47a6aa981664ff7d",
|
||||
"testharness"
|
||||
],
|
||||
"js/behaviours/SetPrototypeOf-window.html": [
|
||||
"92efe1a4f3910a32097fb3cbeef0019d82a0e78a",
|
||||
"testharness"
|
||||
|
@ -0,0 +1 @@
|
||||
prefs: [dom.IntersectionObserver.enabled:true]
|
@ -0,0 +1,2 @@
|
||||
[client-rect.html]
|
||||
type: testharness
|
@ -0,0 +1,3 @@
|
||||
[containing-block.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1363650
|
@ -0,0 +1,3 @@
|
||||
[cross-origin-iframe.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,3 @@
|
||||
[disconnect.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,3 @@
|
||||
[display-none.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1358668
|
@ -0,0 +1,5 @@
|
||||
[edge-inclusive-intersection.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359317
|
||||
[First rAF.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,5 @@
|
||||
[iframe-no-root.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
||||
[First rAF.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,5 @@
|
||||
[multiple-targets.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359311
|
||||
[First rAF.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,3 @@
|
||||
[multiple-thresholds.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,2 @@
|
||||
[observer-attributes.html]
|
||||
type: testharness
|
@ -0,0 +1,2 @@
|
||||
[observer-exceptions.html]
|
||||
type: testharness
|
@ -0,0 +1,2 @@
|
||||
[observer-in-iframe.html]
|
||||
type: testharness
|
@ -0,0 +1,3 @@
|
||||
[observer-without-js-reference.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,5 @@
|
||||
[remove-element.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1363650
|
||||
[First rAF.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,3 @@
|
||||
[root-margin.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,3 @@
|
||||
[same-document-no-root.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,5 @@
|
||||
[same-document-root.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1363650
|
||||
[First rAF.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,3 @@
|
||||
[same-document-zero-size-target.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,4 @@
|
||||
[shadow-content.html]
|
||||
type: testharness
|
||||
prefs: [dom.webcomponents.enabled:true]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
|
@ -0,0 +1,3 @@
|
||||
[timestamp.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,5 @@
|
||||
[unclipped-root.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359317
|
||||
[First rAF.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,3 @@
|
||||
[zero-area-element-hidden.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
@ -0,0 +1,5 @@
|
||||
[zero-area-element-visible.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359316
|
||||
[First rAF should generate a notification.]
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
|
1
testing/web-platform/meta/server-timing/__dir__.ini
Normal file
1
testing/web-platform/meta/server-timing/__dir__.ini
Normal file
@ -0,0 +1 @@
|
||||
prefs: [dom.enable_performance_observer:true]
|
@ -1,6 +1,5 @@
|
||||
[storagemanager-persist.https.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[navigator.storage.persist() returns a promise that resolves.]
|
||||
expected: TIMEOUT
|
||||
prefs: [browser.storageManager.enabled:true,
|
||||
dom.storageManager.prompt.testing:true]
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
iframe {
|
||||
width: 180px;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<iframe id="iframe" srcdoc="<div id='target' style='width:1000px;height:1000px;'></div>"></iframe>
|
||||
|
||||
<script>
|
||||
var target;
|
||||
var entries = [];
|
||||
var observer;
|
||||
var iframe = document.getElementById("iframe");
|
||||
|
||||
iframe.onload = function() {
|
||||
runTestCycle(function() {
|
||||
target = iframe.contentDocument.getElementById("target");
|
||||
assert_true(!!target, "Target element exists.");
|
||||
observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes);
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(test0, "First rAF should generate notification.");
|
||||
}, "IntersectionObserverEntry.boundingClientRect should match target.boundingClientRect()");
|
||||
};
|
||||
|
||||
function test0() {
|
||||
assert_equals(entries.length, 1, "One notification.");
|
||||
var bcr = target.getBoundingClientRect();
|
||||
checkLastEntry(entries, 0, [bcr.left, bcr.right, bcr.top, bcr.bottom]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#root {
|
||||
width: 170px;
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="root" style="position: absolute">
|
||||
<div id="target" style="left: 50px; top: 250px"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
var root, target;
|
||||
|
||||
runTestCycle(function() {
|
||||
root = document.getElementById("root");
|
||||
assert_true(!!root, "root element exists.");
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "target element exists.");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes);
|
||||
}, { root: root });
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
target.style.top = "10px";
|
||||
runTestCycle(test1, "In containing block and intersecting.");
|
||||
}, "IntersectionObserver should only report intersections if root is a containing block ancestor of target.");
|
||||
|
||||
function test1() {
|
||||
runTestCycle(test2, "In containing block and not intersecting.");
|
||||
var rootBounds = contentBounds(root);
|
||||
checkLastEntry(entries, 0, [58, 158, 18, 118, 58, 158, 18, 118].concat(rootBounds));
|
||||
target.style.top = "250px";
|
||||
}
|
||||
|
||||
function test2() {
|
||||
runTestCycle(test3, "Not in containing block and intersecting.");
|
||||
var rootBounds = contentBounds(root);
|
||||
checkLastEntry(entries, 1, [58, 158, 258, 358, 0, 0, 0, 0].concat(rootBounds));
|
||||
root.style.position = "static";
|
||||
target.style.top = "10px";
|
||||
}
|
||||
|
||||
function test3() {
|
||||
runTestCycle(test4, "Not in containing block and not intersecting.");
|
||||
checkLastEntry(entries, 1);
|
||||
target.style.top = "250px";
|
||||
}
|
||||
|
||||
function test4() {
|
||||
checkLastEntry(entries, 1);
|
||||
target.style.top = "0";
|
||||
}
|
||||
</script>
|
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
iframe {
|
||||
width: 160px;
|
||||
height: 100px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<iframe src="resources/cross-origin-subframe.html" sandbox="allow-scripts"></iframe>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
async_test(function(t) {
|
||||
var iframe = document.querySelector("iframe");
|
||||
|
||||
function handleMessage(event) {
|
||||
if (event.data.hasOwnProperty('scrollTo')) {
|
||||
document.scrollingElement.scrollTop = event.data.scrollTo;
|
||||
waitForNotification(t, function() { iframe.contentWindow.postMessage("", "*"); },
|
||||
"document.scrollingElement.scrollTop = " + event.data.scrollTo);
|
||||
} else if (event.data.hasOwnProperty('actual')) {
|
||||
checkJsonEntries(event.data.actual, event.data.expected, event.data.description);
|
||||
} else if (event.data.hasOwnProperty('DONE')) {
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
t.done();
|
||||
} else {
|
||||
var description = event.data.description;
|
||||
waitForNotification(t, function() { iframe.contentWindow.postMessage("", "*"); }, description);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", t.step_func(handleMessage));
|
||||
|
||||
iframe.onload = t.step_func(function() {
|
||||
waitForNotification(t, function() { iframe.contentWindow.postMessage("", "*") }, "setup");
|
||||
});
|
||||
}, "Intersection observer test with no explicit root and target in a cross-origin iframe.");
|
||||
</script>
|
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
var observer;
|
||||
var target;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "target exists");
|
||||
observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "IntersectionObserver should not deliver pending notifications after disconnect().");
|
||||
|
||||
function step0() {
|
||||
runTestCycle(step1, "observer.disconnect()");
|
||||
document.scrollingElement.scrollTop = 300;
|
||||
observer.disconnect();
|
||||
assert_equals(entries.length, 1, "Initial notification.");
|
||||
}
|
||||
|
||||
function step1() {
|
||||
assert_equals(entries.length, 1, "No new notifications.");
|
||||
}
|
||||
</script>
|
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#target {
|
||||
background-color: green;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="target"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
|
||||
runTestCycle(function() {
|
||||
var target = document.getElementById("target");
|
||||
var root = document.getElementById("root");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "Intersecting notification after first rAF.");
|
||||
}, "IntersectionObserver should send a not-intersecting notification for a target that gets display:none.");
|
||||
|
||||
function step0() {
|
||||
runTestCycle(step1, "Not-intersecting notification after setting display:none on target.");
|
||||
checkLastEntry(entries, 0, [8, 108, 8, 108, 8, 108, 8, 108, 0, vw, 0, vh, true]);
|
||||
target.style.display = "none";
|
||||
}
|
||||
|
||||
function step1() {
|
||||
runTestCycle(step2, "Intersecting notification after removing display:none on target.");
|
||||
checkLastEntry(entries, 1, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false]);
|
||||
target.style.display = "";
|
||||
}
|
||||
|
||||
function step2() {
|
||||
checkLastEntry(entries, 2, [8, 108, 8, 108, 8, 108, 8, 108, 0, vw, 0, vh, true]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#root {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: visible;
|
||||
}
|
||||
#target {
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="root">
|
||||
<div id="target" style="width: 100px; height: 100px; transform: translateY(250px)"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
|
||||
runTestCycle(function() {
|
||||
var root = document.getElementById('root');
|
||||
assert_true(!!root, "root element exists.");
|
||||
var target = document.getElementById('target');
|
||||
assert_true(!!target, "target element exists.");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes);
|
||||
}, { root: root });
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "IntersectionObserver should detect and report edge-adjacent and zero-area intersections.");
|
||||
|
||||
function step0() {
|
||||
runTestCycle(step1, "Set transform=translateY(200px) on target.");
|
||||
checkLastEntry(entries, 0, [8, 108, 258, 358, 0, 0, 0, 0, 8, 208, 8, 208, false]);
|
||||
target.style.transform = "translateY(200px)";
|
||||
}
|
||||
|
||||
function step1() {
|
||||
runTestCycle(step2, "Set transform=translateY(201px) on target.");
|
||||
checkLastEntry(entries, 1, [8, 108, 208, 308, 8, 108, 208, 208, 8, 208, 8, 208, true]);
|
||||
target.style.transform = "translateY(201px)";
|
||||
}
|
||||
|
||||
function step2() {
|
||||
runTestCycle(step3, "Set transform=translateY(185px) on target.");
|
||||
checkLastEntry(entries, 2);
|
||||
target.style.height = "0px";
|
||||
target.style.width = "300px";
|
||||
target.style.transform = "translateY(185px)";
|
||||
}
|
||||
|
||||
function step3() {
|
||||
checkLastEntry(entries, 3, [8, 308, 193, 193, 8, 208, 193, 193, 8, 208, 8, 208, true]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
iframe {
|
||||
height: 100px;
|
||||
width: 150px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<iframe id="target-iframe" src="resources/iframe-no-root-subframe.html"></iframe>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var iframe = document.getElementById("target-iframe");
|
||||
var target;
|
||||
var entries = [];
|
||||
|
||||
iframe.onload = function() {
|
||||
runTestCycle(function() {
|
||||
assert_true(!!iframe, "iframe exists");
|
||||
|
||||
target = iframe.contentDocument.getElementById("target");
|
||||
assert_true(!!target, "Target element exists.");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "Observer with the implicit root; target in a same-origin iframe.");
|
||||
};
|
||||
|
||||
function step0() {
|
||||
document.scrollingElement.scrollTop = 200;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollTop = 200");
|
||||
checkLastEntry(entries, 0, [8, 108, 208, 308, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
}
|
||||
|
||||
function step1() {
|
||||
iframe.contentDocument.scrollingElement.scrollTop = 250;
|
||||
runTestCycle(step2, "iframe.contentDocument.scrollingElement.scrollTop = 250");
|
||||
assert_equals(entries.length, 1, "entries.length == 1");
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = 100;
|
||||
runTestCycle(step3, "document.scrollingElement.scrollTop = 100");
|
||||
checkLastEntry(entries, 1, [8, 108, -42, 58, 8, 108, 0, 58, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step3() {
|
||||
checkLastEntry(entries, 2, [8, 108, -42, 58, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
}
|
||||
</script>
|
@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
.target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 10px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div id="target1" class="target"></div>
|
||||
<div id="target2" class="target"></div>
|
||||
<div id="target3" class="target"></div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
var target1, target2, target3;
|
||||
|
||||
runTestCycle(function() {
|
||||
target1 = document.getElementById("target1");
|
||||
assert_true(!!target1, "target1 exists.");
|
||||
target2 = document.getElementById("target2");
|
||||
assert_true(!!target2, "target2 exists.");
|
||||
target3 = document.getElementById("target3");
|
||||
assert_true(!!target3, "target3 exists.");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target1);
|
||||
observer.observe(target2);
|
||||
observer.observe(target3);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "One observer with multiple targets.");
|
||||
|
||||
function step0() {
|
||||
document.scrollingElement.scrollTop = 150;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollTop = 150");
|
||||
assert_equals(entries.length, 3, "Three initial notifications.");
|
||||
assert_equals(entries[0].target, target1, "entries[0].target === target1");
|
||||
assert_equals(entries[1].target, target2, "entries[1].target === target2");
|
||||
assert_equals(entries[2].target, target3, "entries[2].target === target3");
|
||||
}
|
||||
|
||||
function step1() {
|
||||
document.scrollingElement.scrollTop = 10000;
|
||||
runTestCycle(step2, "document.scrollingElement.scrollTop = 10000");
|
||||
assert_equals(entries.length, 4, "Four notifications.");
|
||||
assert_equals(entries[3].target, target1, "entries[3].target === target1");
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
runTestCycle(step3, "document.scrollingElement.scrollTop = 0");
|
||||
assert_equals(entries.length, 6, "Six notifications.");
|
||||
assert_equals(entries[4].target, target2, "entries[4].target === target2");
|
||||
assert_equals(entries[5].target, target3, "entries[5].target === target3");
|
||||
}
|
||||
|
||||
function step3() {
|
||||
assert_equals(entries.length, 9, "Nine notifications.");
|
||||
assert_equals(entries[6].target, target1, "entries[6].target === target1");
|
||||
assert_equals(entries[7].target, target2, "entries[7].target === target2");
|
||||
assert_equals(entries[8].target, target3, "entries[8].target === target3");
|
||||
}
|
||||
</script>
|
@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
var target;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}, { threshold: [0, 0.25, 0.5, 0.75, 1] });
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "Observer with multiple thresholds.");
|
||||
|
||||
function step0() {
|
||||
document.scrollingElement.scrollTop = 120;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollTop = 120");
|
||||
checkLastEntry(entries, 0, [8, 108, vh + 108, vh + 208, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
}
|
||||
|
||||
function step1() {
|
||||
document.scrollingElement.scrollTop = 160;
|
||||
runTestCycle(step2, "document.scrollingElement.scrollTop = 160");
|
||||
checkLastEntry(entries, 1, [8, 108, vh - 12, vh + 88, 8, 108, vh - 12, vh, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = 200;
|
||||
runTestCycle(step3, "document.scrollingElement.scrollTop = 200");
|
||||
checkLastEntry(entries, 2, [8, 108, vh - 52, vh + 48, 8, 108, vh - 52, vh, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step3() {
|
||||
document.scrollingElement.scrollTop = 240;
|
||||
runTestCycle(step4, "document.scrollingElement.scrollTop = 240");
|
||||
checkLastEntry(entries, 3, [8, 108, vh - 92, vh + 8, 8, 108, vh - 92, vh, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step4() {
|
||||
document.scrollingElement.scrollTop = vh + 140;
|
||||
runTestCycle(step5, "document.scrollingElement.scrollTop = window.innerHeight + 140");
|
||||
checkLastEntry(entries, 4, [8, 108, vh - 132, vh - 32, 8, 108, vh - 132, vh - 32, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step5() {
|
||||
document.scrollingElement.scrollTop = vh + 160;
|
||||
runTestCycle(step6, "document.scrollingElement.scrollTop = window.innerHeight + 160");
|
||||
checkLastEntry(entries, 5, [8, 108, -32, 68, 8, 108, 0, 68, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step6() {
|
||||
document.scrollingElement.scrollTop = vh + 200;
|
||||
runTestCycle(step7, "document.scrollingElement.scrollTop = window.innerHeight + 200");
|
||||
checkLastEntry(entries, 6, [8, 108, -52, 48, 8, 108, 0, 48, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step7() {
|
||||
checkLastEntry(entries, 7, [8, 108, -92, 8, 8, 108, 0, 8, 0, vw, 0, vh, true]);
|
||||
document.scrollingElement.scrollTop = vh + 220;
|
||||
runTestCycle(step8, "document.scrollingElement.scrollTop = window.innerHeight + 220");
|
||||
}
|
||||
|
||||
function step8() {
|
||||
checkLastEntry(entries, 8, [8, 108, -112, -12, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
}
|
||||
</script>
|
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<div id="root"></div>
|
||||
|
||||
<script>
|
||||
test(function() {
|
||||
var observer = new IntersectionObserver(function(e) {}, {});
|
||||
test(function() { assert_equals(observer.root, null) },
|
||||
"observer.root");
|
||||
test(function() { assert_array_equals(observer.thresholds, [0]) },
|
||||
"observer.thresholds");
|
||||
test(function() { assert_equals(observer.rootMargin, "0px 0px 0px 0px") },
|
||||
"observer.rootMargin");
|
||||
|
||||
var rootDiv = document.getElementById("root");
|
||||
observer = new IntersectionObserver(function(e) {}, {
|
||||
root: rootDiv,
|
||||
threshold: [0, 0.25, 0.5, 1.0],
|
||||
rootMargin: "10% 20px"
|
||||
});
|
||||
test(function() { assert_equals(observer.root, rootDiv) },
|
||||
"set observer.root");
|
||||
test(function() { assert_array_equals(observer.thresholds, [0, 0.25, 0.5, 1.0]) },
|
||||
"set observer.thresholds");
|
||||
test(function() { assert_equals(observer.rootMargin, "10% 20px 10% 20px") },
|
||||
"set observer.rootMargin");
|
||||
}, "Observer attribute getters.");
|
||||
|
||||
</script>
|
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
test(function () {
|
||||
assert_throws(RangeError(), function() {
|
||||
new IntersectionObserver(e => {}, {threshold: [1.1]})
|
||||
})
|
||||
}, "IntersectionObserver constructor with { threshold: [1.1] }");
|
||||
|
||||
test(function () {
|
||||
assert_throws(TypeError(), function() {
|
||||
new IntersectionObserver(e => {}, {threshold: ["foo"]})
|
||||
})
|
||||
}, 'IntersectionObserver constructor with { threshold: ["foo"] }');
|
||||
|
||||
test(function () {
|
||||
assert_throws("SYNTAX_ERR", function() {
|
||||
new IntersectionObserver(e => {}, {rootMargin: "1"})
|
||||
})
|
||||
}, 'IntersectionObserver constructor witth { rootMargin: "1" }');
|
||||
|
||||
test(function () {
|
||||
assert_throws("SYNTAX_ERR", function() {
|
||||
new IntersectionObserver(e => {}, {rootMargin: "2em"})
|
||||
})
|
||||
}, 'IntersectionObserver constructor with { rootMargin: "2em" }');
|
||||
|
||||
test(function () {
|
||||
assert_throws("SYNTAX_ERR", function() {
|
||||
new IntersectionObserver(e => {}, {rootMargin: "auto"})
|
||||
})
|
||||
}, 'IntersectionObserver constructor width { rootMargin: "auto" }');
|
||||
|
||||
test(function () {
|
||||
assert_throws("SYNTAX_ERR", function() {
|
||||
new IntersectionObserver(e => {}, {rootMargin: "1px 1px 1px 1px 1px"})
|
||||
})
|
||||
}, 'IntersectionObserver constructor with { rootMargin: "1px 1px 1px 1px 1px" }');
|
||||
|
||||
test(function () {
|
||||
assert_throws(TypeError(), function() {
|
||||
let observer = new IntersectionObserver(c => {}, {});
|
||||
observer.observe("foo");
|
||||
})
|
||||
}, 'IntersectionObserver.observe("foo")');
|
||||
</script>
|
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
</style>
|
||||
<iframe id="target-iframe" src="resources/observer-in-iframe-subframe.html" width="150px" height="150px"></iframe>
|
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
<div class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
|
||||
runTestCycle(function() {
|
||||
var target = document.getElementById("target");
|
||||
assert_true(!!target, "Target exists");
|
||||
function createObserver() {
|
||||
new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}).observe(target);
|
||||
}
|
||||
createObserver();
|
||||
runTestCycle(step0, "First rAF");
|
||||
}, "IntersectionObserver that is unreachable in js should still generate notifications.");
|
||||
|
||||
function step0() {
|
||||
document.scrollingElement.scrollTop = 300;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollTop = 300");
|
||||
assert_equals(entries.length, 1, "One notification.");
|
||||
}
|
||||
|
||||
function step1() {
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
assert_equals(entries.length, 2, "Two notifications.");
|
||||
}
|
||||
</script>
|
@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#root {
|
||||
display: inline-block;
|
||||
overflow-y: scroll;
|
||||
height: 200px;
|
||||
border: 3px solid black;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
.spacer {
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="root">
|
||||
<div id="leading-space" class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div id="trailing-space" class="spacer"</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
var root, target, trailingSpace;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "Target exists");
|
||||
trailingSpace = document.getElementById("trailing-space");
|
||||
assert_true(!!trailingSpace, "TrailingSpace exists");
|
||||
root = document.getElementById("root");
|
||||
assert_true(!!root, "Root exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}, {root: root});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF");
|
||||
}, "Verify that not-intersecting notifications are sent when a target is removed from the DOM tree.");
|
||||
|
||||
function step0() {
|
||||
root.scrollTop = 150;
|
||||
runTestCycle(step1, "root.scrollTop = 150");
|
||||
checkLastEntry(entries, 0, [11, 111, 311, 411, 0, 0, 0, 0, 11, 111, 11, 211, false]);
|
||||
}
|
||||
|
||||
function step1() {
|
||||
root.removeChild(target);
|
||||
runTestCycle(step2, "root.removeChild(target).");
|
||||
checkLastEntry(entries, 1, [11, 111, 161, 261, 11, 111, 161, 211, 11, 111, 11, 211, true]);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
root.scrollTop = 0;
|
||||
root.insertBefore(target, trailingSpace);
|
||||
runTestCycle(step3, "root.insertBefore(target, trailingSpace).");
|
||||
checkLastEntry(entries, 2, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false]);
|
||||
}
|
||||
|
||||
function step3() {
|
||||
root.scrollTop = 150;
|
||||
runTestCycle(step4, "root.scrollTop = 150 after reinserting target.");
|
||||
checkLastEntry(entries, 2);
|
||||
}
|
||||
|
||||
function step4() {
|
||||
checkLastEntry(entries, 3, [11, 111, 161, 261, 11, 111, 161, 211, 11, 111, 11, 211, true]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<div style="height: 200px; width: 100px;"></div>
|
||||
<div id="target" style="background-color: green; width:100px; height:100px"></div>
|
||||
<div style="height: 200px; width: 100px;"></div>
|
||||
|
||||
<script>
|
||||
var port;
|
||||
var entries = [];
|
||||
var target = document.getElementById('target');
|
||||
var scroller = document.scrollingElement;
|
||||
var nextStep;
|
||||
|
||||
function clientRectToJson(rect) {
|
||||
if (!rect)
|
||||
return "null";
|
||||
return {
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
left: rect.left
|
||||
};
|
||||
}
|
||||
|
||||
function entryToJson(entry) {
|
||||
return {
|
||||
boundingClientRect: clientRectToJson(entry.boundingClientRect),
|
||||
intersectionRect: clientRectToJson(entry.intersectionRect),
|
||||
rootBounds: clientRectToJson(entry.rootBounds),
|
||||
target: entry.target.id
|
||||
};
|
||||
}
|
||||
|
||||
function coordinatesToClientRectJson(top, right, bottom, left) {
|
||||
return {
|
||||
top: top,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
left: left
|
||||
};
|
||||
}
|
||||
|
||||
// Note that we never use RAF in this code, because this frame might get render-throttled.
|
||||
// Instead of RAF-ing, we just post an empty message to the parent window, which will
|
||||
// RAF when it is received, and then send us a message to cause the next step to run.
|
||||
|
||||
// Use a rootMargin here, and verify it does NOT get applied for the cross-origin case.
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}, { rootMargin: "7px" });
|
||||
observer.observe(target);
|
||||
|
||||
function step0() {
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
nextStep = step1;
|
||||
var expected = [{
|
||||
boundingClientRect: coordinatesToClientRectJson(8, 208, 108, 308),
|
||||
intersectionRect: coordinatesToClientRectJson(0, 0, 0, 0),
|
||||
rootBounds: "null",
|
||||
target: target.id
|
||||
}];
|
||||
port.postMessage({
|
||||
actual: entries.map(entryToJson),
|
||||
expected: expected,
|
||||
description: "First rAF"
|
||||
}, "*");
|
||||
entries = [];
|
||||
port.postMessage({scrollTo: 200}, "*");
|
||||
}
|
||||
|
||||
function step1() {
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
port.postMessage({
|
||||
actual: entries.map(entryToJson),
|
||||
expected: [],
|
||||
description: "topDocument.scrollingElement.scrollTop = 200"
|
||||
}, "*");
|
||||
entries = [];
|
||||
scroller.scrollTop = 250;
|
||||
nextStep = step2;
|
||||
port.postMessage({}, "*");
|
||||
}
|
||||
|
||||
function step2() {
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
var expected = [{
|
||||
boundingClientRect: coordinatesToClientRectJson(-42, 108, 58, 8),
|
||||
intersectionRect: coordinatesToClientRectJson(0, 108, 58, 8),
|
||||
rootBounds: "null",
|
||||
target: target.id
|
||||
}];
|
||||
port.postMessage({
|
||||
actual: entries.map(entryToJson),
|
||||
expected: expected,
|
||||
description: "iframeDocument.scrollingElement.scrollTop = 250"
|
||||
}, "*");
|
||||
entries = [];
|
||||
nextStep = step3;
|
||||
port.postMessage({scrollTo: 100}, "*");
|
||||
}
|
||||
|
||||
function step3() {
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
var expected = [{
|
||||
boundingClientRect: coordinatesToClientRectJson(-42, 108, 58, 8),
|
||||
intersectionRect: coordinatesToClientRectJson(0, 0, 0, 0),
|
||||
rootBounds: "null",
|
||||
target: target.id
|
||||
}];
|
||||
port.postMessage({
|
||||
actual: entries.map(entryToJson),
|
||||
expected: expected,
|
||||
description: "topDocument.scrollingElement.scrollTop = 100"
|
||||
}, "*");
|
||||
port.postMessage({DONE: 1}, "*");
|
||||
}
|
||||
|
||||
function handleMessage(event)
|
||||
{
|
||||
port = event.source;
|
||||
nextStep();
|
||||
}
|
||||
|
||||
nextStep = step0;
|
||||
window.addEventListener("message", handleMessage);
|
||||
</script>
|
@ -0,0 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<div style="height: 200px; width: 100px;"></div>
|
||||
<div id="target" style="background-color: green; width:100px; height:100px"></div>
|
||||
<div style="height: 200px; width: 100px;"></div>
|
@ -0,0 +1,122 @@
|
||||
// Here's how waitForNotification works:
|
||||
//
|
||||
// - myTestFunction0()
|
||||
// - waitForNotification(myTestFunction1)
|
||||
// - requestAnimationFrame()
|
||||
// - Modify DOM in a way that should trigger an IntersectionObserver callback.
|
||||
// - BeginFrame
|
||||
// - requestAnimationFrame handler runs
|
||||
// - First step_timeout()
|
||||
// - Style, layout, paint
|
||||
// - IntersectionObserver generates new notifications
|
||||
// - Posts a task to deliver notifications
|
||||
// - First step_timeout handler runs
|
||||
// - Second step_timeout()
|
||||
// - Task to deliver IntersectionObserver notifications runs
|
||||
// - IntersectionObserver callbacks run
|
||||
// - Second step_timeout handler runs
|
||||
// - myTestFunction1()
|
||||
// - [optional] waitForNotification(myTestFunction2)
|
||||
// - requestAnimationFrame()
|
||||
// - Verify newly-arrived IntersectionObserver notifications
|
||||
// - [optional] Modify DOM to trigger new notifications
|
||||
function waitForNotification(t, f) {
|
||||
requestAnimationFrame(function() {
|
||||
t.step_timeout(function() { t.step_timeout(f); });
|
||||
});
|
||||
}
|
||||
|
||||
// The timing of when runTestCycle is called is important. It should be
|
||||
// called:
|
||||
//
|
||||
// - Before or during the window load event, or
|
||||
// - Inside of a prior runTestCycle callback, *before* any assert_* methods
|
||||
// are called.
|
||||
//
|
||||
// Following these rules will ensure that the test suite will not abort before
|
||||
// all test steps have run.
|
||||
function runTestCycle(f, description) {
|
||||
async_test(function(t) {
|
||||
waitForNotification(t, t.step_func_done(f));
|
||||
}, description);
|
||||
}
|
||||
|
||||
// Root bounds for a root with an overflow clip as defined by:
|
||||
// http://wicg.github.io/IntersectionObserver/#intersectionobserver-root-intersection-rectangle
|
||||
function contentBounds(root) {
|
||||
var left = root.offsetLeft + root.clientLeft;
|
||||
var right = left + root.clientWidth;
|
||||
var top = root.offsetTop + root.clientTop;
|
||||
var bottom = top + root.clientHeight;
|
||||
return [left, right, top, bottom];
|
||||
}
|
||||
|
||||
// Root bounds for a root without an overflow clip as defined by:
|
||||
// http://wicg.github.io/IntersectionObserver/#intersectionobserver-root-intersection-rectangle
|
||||
function borderBoxBounds(root) {
|
||||
var left = root.offsetLeft;
|
||||
var right = left + root.offsetWidth;
|
||||
var top = root.offsetTop;
|
||||
var bottom = top + root.offsetHeight;
|
||||
return [left, right, top, bottom];
|
||||
}
|
||||
|
||||
function clientBounds(element) {
|
||||
var rect = element.getBoundingClientRect();
|
||||
return [rect.left, rect.right, rect.top, rect.bottom];
|
||||
}
|
||||
|
||||
function rectArea(rect) {
|
||||
return (rect.left - rect.right) * (rect.bottom - rect.top);
|
||||
}
|
||||
|
||||
function checkRect(actual, expected, description, all) {
|
||||
if (!expected.length)
|
||||
return;
|
||||
assert_equals(actual.left | 0, expected[0] | 0, description + '.left');
|
||||
assert_equals(actual.right | 0, expected[1] | 0, description + '.right');
|
||||
assert_equals(actual.top | 0, expected[2] | 0, description + '.top');
|
||||
assert_equals(actual.bottom | 0, expected[3] | 0, description + '.bottom');
|
||||
}
|
||||
|
||||
function checkLastEntry(entries, i, expected) {
|
||||
assert_equals(entries.length, i + 1, 'entries.length');
|
||||
if (expected) {
|
||||
checkRect(
|
||||
entries[i].boundingClientRect, expected.slice(0, 4),
|
||||
'entries[' + i + '].boundingClientRect', entries[i]);
|
||||
checkRect(
|
||||
entries[i].intersectionRect, expected.slice(4, 8),
|
||||
'entries[' + i + '].intersectionRect', entries[i]);
|
||||
checkRect(
|
||||
entries[i].rootBounds, expected.slice(8, 12),
|
||||
'entries[' + i + '].rootBounds', entries[i]);
|
||||
if (expected.length > 12) {
|
||||
assert_equals(
|
||||
entries[i].isIntersecting, expected[12],
|
||||
'entries[' + i + '].isIntersecting');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkJsonEntry(actual, expected) {
|
||||
checkRect(
|
||||
actual.boundingClientRect, expected.boundingClientRect,
|
||||
'entry.boundingClientRect');
|
||||
checkRect(
|
||||
actual.intersectionRect, expected.intersectionRect,
|
||||
'entry.intersectionRect');
|
||||
if (actual.rootBounds == 'null')
|
||||
assert_equals(expected.rootBounds, 'null', 'rootBounds is null');
|
||||
else
|
||||
checkRect(actual.rootBounds, expected.rootBounds, 'entry.rootBounds');
|
||||
assert_equals(actual.target, expected.target);
|
||||
}
|
||||
|
||||
function checkJsonEntries(actual, expected, description) {
|
||||
test(function() {
|
||||
assert_equals(actual.length, expected.length);
|
||||
for (var i = 0; i < actual.length; i++)
|
||||
checkJsonEntry(actual[i], expected[i]);
|
||||
}, description);
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
#scroller {
|
||||
width: 160px;
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
.spacer {
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="root">
|
||||
<div id="scroller">
|
||||
<div class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setup({message_events: [], output_document: window.parent.document});
|
||||
|
||||
var entries = [];
|
||||
var root, scroller, target;
|
||||
|
||||
runTestCycle(function() {
|
||||
root = document.getElementById("root");
|
||||
assert_true(!!root, "Root element exists.");
|
||||
scroller = document.getElementById("scroller");
|
||||
assert_true(!!scroller, "Scroller element exists.");
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "Target element exists.");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}, {root: root});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.")
|
||||
runTestCycle(step1, "First rAF.");
|
||||
}, "IntersectionObserver in iframe with explicit root.");
|
||||
|
||||
function step1() {
|
||||
scroller.scrollTop = 250;
|
||||
runTestCycle(step2, "scroller.scrollTop = 250");
|
||||
checkLastEntry(entries, 0, [8, 108, 308, 408, 0, 0, 0, 0, 8, 208, 8, 208, false]);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
checkLastEntry(entries, 1, [8, 108, 58, 158, 8, 108, 58, 158, 8, 208, 8, 208, true]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
.spacer {
|
||||
width: height: 100px
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
document.createObserverCallback = function(entries) {
|
||||
return function(newEntries) {
|
||||
for (var i in newEntries) {
|
||||
entries.push(newEntries[i]);
|
||||
}
|
||||
};
|
||||
}
|
||||
document.createObserver = function(callback) {
|
||||
return new IntersectionObserver(callback, {});
|
||||
};
|
||||
</script>
|
@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#target {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
.vertical-spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
.horizontal-spacer {
|
||||
display: inline-block;
|
||||
width: 120vw;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="vertical-spacer"></div>
|
||||
<div style="white-space:nowrap;">
|
||||
<div class="horizontal-spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="horizontal-spacer"></div>
|
||||
</div>
|
||||
<div class="vertical-spacer"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
var target;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "Target exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}, { rootMargin: "10px 20% 40% 30px" });
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "Root margin tests");
|
||||
|
||||
function step0() {
|
||||
var targetBounds = clientBounds(target);
|
||||
document.scrollingElement.scrollLeft = 100;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollLeft = 100");
|
||||
checkLastEntry(entries, 0, targetBounds.concat(0, 0, 0, 0, -30, vw * 1.2, -10, vh * 1.4, false));
|
||||
}
|
||||
|
||||
function step1() {
|
||||
var targetBounds = clientBounds(target);
|
||||
var sw = window.innerWidth - document.documentElement.clientWidth;
|
||||
var sh = window.innerHeight - document.documentElement.clientHeight;
|
||||
document.scrollingElement.scrollTop = vh + 200;
|
||||
runTestCycle(step2, "document.scrollingElement.scrollTop = document.documentElement.clientHeight + 200");
|
||||
checkLastEntry(entries, 1, targetBounds.concat(
|
||||
targetBounds[0], Math.min(targetBounds[1], vw * 1.2), vh + 108 + sh, Math.min(vh + 208 + sw, vh * 1.4),
|
||||
-30, vw * 1.2, -10, vh * 1.4,
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = vh + 300;
|
||||
runTestCycle(step3, "document.scrollingElement.scrollTop = document.documentElement.clientHeight + 300");
|
||||
checkLastEntry(entries, 1);
|
||||
}
|
||||
|
||||
function step3() {
|
||||
var targetBounds = clientBounds(target);
|
||||
document.scrollingElement.scrollLeft = 0;
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
checkLastEntry(entries, 2, targetBounds.concat(0, 0, 0, 0, -30, vw * 1.2, -10, vh * 1.4, false));
|
||||
}
|
||||
</script>
|
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
var target;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "target exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "IntersectionObserver in a single document using the implicit root.");
|
||||
|
||||
function step0() {
|
||||
document.scrollingElement.scrollTop = 300;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollTop = 300");
|
||||
checkLastEntry(entries, 0, [8, 108, vh + 108, vh + 208, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
}
|
||||
|
||||
function step1() {
|
||||
document.scrollingElement.scrollTop = 100;
|
||||
runTestCycle(step2, "document.scrollingElement.scrollTop = 100");
|
||||
checkLastEntry(entries, 1, [8, 108, vh - 192, vh - 92, 8, 108, vh - 192, vh - 92, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
checkLastEntry(entries, 2, [8, 108, vh + 8, vh + 108, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
#root {
|
||||
display: inline-block;
|
||||
overflow-y: scroll;
|
||||
height: 200px;
|
||||
border: 3px solid black;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div id="root">
|
||||
<div style="height: 300px;"></div>
|
||||
<div id="target"></div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
var root, target;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "target exists");
|
||||
root = document.getElementById("root");
|
||||
assert_true(!!root, "root exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}, { root: root });
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF");
|
||||
}, "IntersectionObserver in a single document with explicit root.");
|
||||
|
||||
function step0() {
|
||||
document.scrollingElement.scrollTop = vh;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollTop = window.innerHeight.");
|
||||
checkLastEntry(entries, 0, [ 11, 111, vh + 411, vh + 511, 0, 0, 0, 0, 11, 111, vh + 111, vh + 311, false]);
|
||||
}
|
||||
|
||||
function step1() {
|
||||
root.scrollTop = 150;
|
||||
runTestCycle(step2, "root.scrollTop = 150 with root scrolled into view.");
|
||||
assert_equals(entries.length, 1, "No notifications after scrolling frame.");
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
runTestCycle(step3, "document.scrollingElement.scrollTop = 0.");
|
||||
checkLastEntry(entries, 1, [11, 111, 261, 361, 11, 111, 261, 311, 11, 111, 111, 311, true]);
|
||||
}
|
||||
|
||||
function step3() {
|
||||
root.scrollTop = 0;
|
||||
runTestCycle(step4, "root.scrollTop = 0");
|
||||
checkLastEntry(entries, 1);
|
||||
}
|
||||
|
||||
function step4() {
|
||||
root.scrollTop = 150;
|
||||
runTestCycle(step5, "root.scrollTop = 150 with root scrolled out of view.");
|
||||
checkLastEntry(entries, 2, [11, 111, vh + 411, vh + 511, 0, 0, 0, 0, 11, 111, vh + 111, vh + 311, false]);
|
||||
}
|
||||
|
||||
// This tests that notifications are generated even when the root element is off screen.
|
||||
function step5() {
|
||||
checkLastEntry(entries, 3, [11, 111, vh + 261, vh + 361, 11, 111, vh + 261, vh + 311, 11, 111, vh + 111, vh + 311, true]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
#target {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
var target;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "Target exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF");
|
||||
}, "Observing a zero-area target.");
|
||||
|
||||
function step0() {
|
||||
document.scrollingElement.scrollTop = 300;
|
||||
runTestCycle(step1, "document.scrollingElement.scrollTop = 300");
|
||||
checkLastEntry(entries, 0, [8, 8, vh + 108, vh + 108, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
}
|
||||
|
||||
function step1() {
|
||||
document.scrollingElement.scrollTop = 100;
|
||||
runTestCycle(step2, "document.scrollingElement.scrollTop = 100");
|
||||
checkLastEntry(entries, 1, [8, 8, vh - 192, vh - 192, 8, 8, vh - 192, vh - 192, 0, vw, 0, vh, true]);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
checkLastEntry(entries, 2, [8, 8, vh + 8, vh + 8, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="host"></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
var target;
|
||||
|
||||
runTestCycle(function() {
|
||||
var shadowHost = document.getElementById("host");
|
||||
assert_true(!!shadowHost, "Host exists");
|
||||
var shadowRoot = shadowHost.createShadowRoot();
|
||||
assert_true(!!shadowRoot, "Shadow root exists");
|
||||
shadowRoot.innerHTML = "<div id='target' style='width: 100px; height: 100px; background-color: green;'></div>";
|
||||
target = shadowRoot.getElementById("target");
|
||||
assert_true(!!target, "target exists");
|
||||
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF after creating shadow DOM.");
|
||||
}, "Observing a target inside shadow DOM.");
|
||||
|
||||
function step0() {
|
||||
checkLastEntry(entries, 0, [8, 108, 8, 108, 8, 108, 8, 108, 0, vw, 0, vh, true]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
.spacer {
|
||||
height: calc(100vh + 100px);
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="leading-space" class="spacer"></div>
|
||||
<div id="trailing-space" class="spacer"></div>
|
||||
|
||||
<script>
|
||||
// Pick this number to be comfortably greater than the length of two frames at 60Hz.
|
||||
var timeSkew = 40;
|
||||
|
||||
var topWindowEntries = [];
|
||||
var iframeWindowEntries = [];
|
||||
var targetIframe;
|
||||
var topWindowTimeBeforeNotification;
|
||||
var iframeWindowTimeBeforeNotification;
|
||||
|
||||
async_test(function(t) {
|
||||
// assert_equals(window.innerWidth, 800, "Window must be 800 pixels wide.");
|
||||
// assert_equals(window.innerHeight, 600, "Window must be 600 pixels high.");
|
||||
|
||||
t.step_timeout(function() {
|
||||
targetIframe = document.createElement("iframe");
|
||||
assert_true(!!targetIframe, "iframe exists");
|
||||
targetIframe.src = "resources/timestamp-subframe.html";
|
||||
var trailingSpace = document.getElementById("trailing-space");
|
||||
assert_true(!!trailingSpace, "trailing-space exists");
|
||||
trailingSpace.parentNode.insertBefore(targetIframe, trailingSpace);
|
||||
targetIframe.onload = function() {
|
||||
var target = targetIframe.contentDocument.getElementById("target");
|
||||
var iframeScroller = targetIframe.contentDocument.scrollingElement;
|
||||
|
||||
// Observer created here, callback created in iframe context. Timestamps should be
|
||||
// from this window.
|
||||
var observer = new IntersectionObserver(
|
||||
targetIframe.contentDocument.createObserverCallback(topWindowEntries), {});
|
||||
assert_true(!!observer, "Observer exists");
|
||||
observer.observe(target);
|
||||
|
||||
// Callback created here, observer created in iframe. Timestamps should be
|
||||
// from iframe window.
|
||||
observer = targetIframe.contentDocument.createObserver(function(newEntries) {
|
||||
iframeWindowEntries = iframeWindowEntries.concat(newEntries);
|
||||
});
|
||||
observer.observe(target);
|
||||
runTestCycle(step1, "First rAF after iframe is loaded.");
|
||||
t.done();
|
||||
};
|
||||
}, timeSkew);
|
||||
}, "Check that timestamps correspond to the to execution context that created the observer.");
|
||||
|
||||
function step1() {
|
||||
document.scrollingElement.scrollTop = 200;
|
||||
targetIframe.contentDocument.scrollingElement.scrollTop = 250;
|
||||
topWindowTimeBeforeNotification = performance.now();
|
||||
iframeWindowTimeBeforeNotification = targetIframe.contentWindow.performance.now();
|
||||
runTestCycle(step2, "Generate notifications.");
|
||||
assert_equals(topWindowEntries.length, 1, "One notification to top window observer.");
|
||||
assert_equals(iframeWindowEntries.length, 1, "One notification to iframe observer.");
|
||||
}
|
||||
|
||||
function step2() {
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
var topWindowTimeAfterNotification = performance.now();
|
||||
var iframeWindowTimeAfterNotification = targetIframe.contentWindow.performance.now();
|
||||
|
||||
// Test results are only significant if there's a gap between
|
||||
// top window time and iframe window time.
|
||||
assert_greater_than(topWindowTimeBeforeNotification, iframeWindowTimeAfterNotification,
|
||||
"Time ranges for top and iframe windows are disjoint.");
|
||||
|
||||
assert_equals(topWindowEntries.length, 2, "Top window observer has two notifications.");
|
||||
assert_between_inclusive(
|
||||
topWindowEntries[1].time,
|
||||
topWindowTimeBeforeNotification,
|
||||
topWindowTimeAfterNotification,
|
||||
"Notification to top window observer is within the expected range.");
|
||||
|
||||
assert_equals(iframeWindowEntries.length, 2, "Iframe observer has two notifications.");
|
||||
assert_between_inclusive(
|
||||
iframeWindowEntries[1].time,
|
||||
iframeWindowTimeBeforeNotification,
|
||||
iframeWindowTimeAfterNotification,
|
||||
"Notification to iframe observer is within the expected range.");
|
||||
}
|
||||
</script>
|
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#root {
|
||||
overflow: visible;
|
||||
height: 200px;
|
||||
width: 160px;
|
||||
border: 7px solid black;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="root">
|
||||
<div id="target" style="transform: translateY(300px)"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
var target;
|
||||
|
||||
runTestCycle(function() {
|
||||
target = document.getElementById("target");
|
||||
assert_true(!!target, "target exists");
|
||||
var root = document.getElementById("root");
|
||||
assert_true(!!root, "root exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
}, {root: root});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "Test that border bounding box is used to calculate intersection with a non-scrolling root.");
|
||||
|
||||
function step0() {
|
||||
target.style.transform = "translateY(195px)";
|
||||
runTestCycle(step1, "target.style.transform = 'translateY(195px)'");
|
||||
checkLastEntry(entries, 0, [15, 115, 315, 415, 0, 0, 0, 0, 8, 182, 8, 222, false]);
|
||||
}
|
||||
|
||||
function step1() {
|
||||
target.style.transform = "";
|
||||
checkLastEntry(entries, 1, [15, 115, 210, 310, 15, 115, 210, 222, 8, 182, 8, 222, true]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#target {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
position: fixed;
|
||||
top: -1000px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id='target'></div>
|
||||
|
||||
<script>
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var vh = document.documentElement.clientHeight;
|
||||
|
||||
var entries = [];
|
||||
|
||||
runTestCycle(function() {
|
||||
var target = document.getElementById('target');
|
||||
assert_true(!!target, "target exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF.");
|
||||
}, "A zero-area hidden target should not be intersecting.");
|
||||
|
||||
function step0() {
|
||||
checkLastEntry(entries, 0, [8, 8, -1000, -1000, 0, 0, 0, 0, 0, vw, 0, vh, false]);
|
||||
}
|
||||
</script>
|
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/intersection-observer-test-utils.js"></script>
|
||||
|
||||
<style>
|
||||
pre, #log {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 200px;
|
||||
}
|
||||
#target {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id='target'></div>
|
||||
|
||||
<script>
|
||||
var entries = [];
|
||||
|
||||
runTestCycle(function() {
|
||||
var target = document.getElementById('target');
|
||||
assert_true(!!target, "target exists");
|
||||
var observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes)
|
||||
});
|
||||
observer.observe(target);
|
||||
entries = entries.concat(observer.takeRecords());
|
||||
assert_equals(entries.length, 0, "No initial notifications.");
|
||||
runTestCycle(step0, "First rAF should generate a notification.");
|
||||
}, "Ensure that a zero-area target intersecting root generates a notification with intersectionRatio == 1");
|
||||
|
||||
function step0() {
|
||||
assert_equals(entries.length, 1, "One notification.");
|
||||
assert_equals(entries[0].intersectionRatio, 1, "intersectionRatio == 1");
|
||||
}
|
||||
</script>
|
@ -9,16 +9,30 @@ this.topSites = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
return {
|
||||
topSites: {
|
||||
get: function() {
|
||||
let urls = NewTabUtils.links.getLinks()
|
||||
.filter(link => !!link)
|
||||
.map(link => {
|
||||
return {
|
||||
url: link.url,
|
||||
title: link.title,
|
||||
};
|
||||
});
|
||||
return Promise.resolve(urls);
|
||||
get: function(options) {
|
||||
return new Promise(function(resolve) {
|
||||
NewTabUtils.links.populateCache(function() {
|
||||
let urls;
|
||||
|
||||
if (options && options.providers && options.providers.length > 0) {
|
||||
let urlLists = options.providers.map(function(p) {
|
||||
let provider = NewTabUtils[`${p}Provider`];
|
||||
return provider ? NewTabUtils.getProviderLinks(provider).slice() : [];
|
||||
});
|
||||
urls = NewTabUtils.links.mergeLinkLists(urlLists);
|
||||
} else {
|
||||
urls = NewTabUtils.links.getLinks();
|
||||
}
|
||||
|
||||
resolve(urls.filter(link => !!link)
|
||||
.map(link => {
|
||||
return {
|
||||
url: link.url,
|
||||
title: link.title,
|
||||
};
|
||||
}));
|
||||
}, false);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -46,6 +46,19 @@
|
||||
"description": "Gets a list of top sites.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "options",
|
||||
"properties": {
|
||||
"providers": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Which providers to get top sites from. Possible values are \"places\" and \"activityStream\".",
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
|
@ -28,30 +28,29 @@ TestProvider.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
function makeLinks(links) {
|
||||
add_task(async function test_topSites() {
|
||||
// Important: To avoid test failures due to clock jitter on Windows XP, call
|
||||
// Date.now() once here, not each time through the loop.
|
||||
let frecency = 0;
|
||||
let now = Date.now() * 1000;
|
||||
let places = [];
|
||||
links.map((link, i) => {
|
||||
places.push({
|
||||
url: link.url,
|
||||
title: link.title,
|
||||
lastVisitDate: now - i,
|
||||
frecency: frecency++,
|
||||
});
|
||||
let provider1 = new TestProvider(done => {
|
||||
let data = [{url: "http://example.com/", title: "site#-1", frecency: 9, lastVisitDate: now},
|
||||
{url: "http://example0.com/", title: "site#0", frecency: 8, lastVisitDate: now},
|
||||
{url: "http://example3.com/", title: "site#3", frecency: 5, lastVisitDate: now}];
|
||||
done(data);
|
||||
});
|
||||
let provider2 = new TestProvider(done => {
|
||||
let data = [{url: "http://example1.com/", title: "site#1", frecency: 7, lastVisitDate: now},
|
||||
{url: "http://example2.com/", title: "site#2", frecency: 6, lastVisitDate: now}];
|
||||
done(data);
|
||||
});
|
||||
return places;
|
||||
}
|
||||
|
||||
add_task(async function test_topSites() {
|
||||
let expect = [{url: "http://example.com/", title: "site#-1"},
|
||||
{url: "http://example0.com/", title: "site#0"},
|
||||
{url: "http://example1.com/", title: "site#1"},
|
||||
{url: "http://example2.com/", title: "site#2"},
|
||||
{url: "http://example3.com/", title: "site#3"}];
|
||||
NewTabUtils.initWithoutProviders();
|
||||
NewTabUtils.links.addProvider(provider1);
|
||||
NewTabUtils.links.addProvider(provider2);
|
||||
NewTabUtils.test1Provider = provider1;
|
||||
NewTabUtils.test2Provider = provider2;
|
||||
|
||||
// Test that results from all providers are returned by default.
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": [
|
||||
@ -59,27 +58,64 @@ add_task(async function test_topSites() {
|
||||
],
|
||||
},
|
||||
background() {
|
||||
// Tests consistent behaviour when no providers are specified.
|
||||
browser.topSites.get(result => {
|
||||
browser.test.sendMessage("done", result);
|
||||
browser.test.sendMessage("done1", result);
|
||||
});
|
||||
browser.topSites.get({}, result => {
|
||||
browser.test.sendMessage("done2", result);
|
||||
});
|
||||
browser.topSites.get({providers: []}, result => {
|
||||
browser.test.sendMessage("done3", result);
|
||||
});
|
||||
// Tests that results are merged correctly.
|
||||
browser.topSites.get({providers: ["test2", "test1"]}, result => {
|
||||
browser.test.sendMessage("done4", result);
|
||||
});
|
||||
// Tests that only the specified provider is used.
|
||||
browser.topSites.get({providers: ["test2"]}, result => {
|
||||
browser.test.sendMessage("done5", result);
|
||||
});
|
||||
// Tests that specifying a non-existent provider returns an empty array.
|
||||
browser.topSites.get({providers: ["fake"]}, result => {
|
||||
browser.test.sendMessage("done6", result);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
let expectedLinks = makeLinks(expect);
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
|
||||
NewTabUtils.initWithoutProviders();
|
||||
NewTabUtils.links.addProvider(provider);
|
||||
|
||||
await NewTabUtils.links.populateCache();
|
||||
|
||||
await extension.startup();
|
||||
|
||||
let result = await extension.awaitMessage("done");
|
||||
Assert.deepEqual(expect, result, "got topSites");
|
||||
let expected1 = [{url: "http://example.com/", title: "site#-1"},
|
||||
{url: "http://example0.com/", title: "site#0"},
|
||||
{url: "http://example1.com/", title: "site#1"},
|
||||
{url: "http://example2.com/", title: "site#2"},
|
||||
{url: "http://example3.com/", title: "site#3"}];
|
||||
|
||||
let actual1 = await extension.awaitMessage("done1");
|
||||
Assert.deepEqual(expected1, actual1, "got topSites");
|
||||
|
||||
let actual2 = await extension.awaitMessage("done2");
|
||||
Assert.deepEqual(expected1, actual2, "got topSites");
|
||||
|
||||
let actual3 = await extension.awaitMessage("done3");
|
||||
Assert.deepEqual(expected1, actual3, "got topSites");
|
||||
|
||||
let actual4 = await extension.awaitMessage("done4");
|
||||
Assert.deepEqual(expected1, actual4, "got topSites");
|
||||
|
||||
let expected5 = [{url: "http://example1.com/", title: "site#1"},
|
||||
{url: "http://example2.com/", title: "site#2"}];
|
||||
|
||||
let actual5 = await extension.awaitMessage("done5");
|
||||
Assert.deepEqual(expected5, actual5, "got topSites");
|
||||
|
||||
let actual6 = await extension.awaitMessage("done6");
|
||||
Assert.deepEqual([], actual6, "got topSites");
|
||||
|
||||
await extension.unload();
|
||||
|
||||
NewTabUtils.links.removeProvider(provider);
|
||||
NewTabUtils.links.removeProvider(provider1);
|
||||
NewTabUtils.links.removeProvider(provider2);
|
||||
delete NewTabUtils.test1Provider;
|
||||
delete NewTabUtils.test2Provider;
|
||||
});
|
||||
|
@ -1453,6 +1453,14 @@ var Links = {
|
||||
}
|
||||
}
|
||||
|
||||
return this.mergeLinkLists(linkLists);
|
||||
},
|
||||
|
||||
mergeLinkLists: function Links_mergeLinkLists(linkLists) {
|
||||
if (linkLists.length == 1) {
|
||||
return linkLists[0];
|
||||
}
|
||||
|
||||
function getNextLink() {
|
||||
let minLinks = null;
|
||||
for (let links of linkLists) {
|
||||
|
Loading…
Reference in New Issue
Block a user