зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to b2g-inbound
This commit is contained in:
Коммит
a2700c231c
|
@ -137,6 +137,7 @@ const Panel = Class({
|
|||
position: Object.freeze({}),
|
||||
contextMenu: false
|
||||
}, panelContract(options));
|
||||
model.ready = false;
|
||||
models.set(this, model);
|
||||
|
||||
if (model.contentStyle || model.contentStyleFile) {
|
||||
|
@ -300,15 +301,30 @@ let ready = filter(panelEvents, ({type, target}) =>
|
|||
let start = filter(panelEvents, ({type}) => type === "document-element-inserted");
|
||||
|
||||
// Forward panel show / hide events to panel's own event listeners.
|
||||
on(shows, "data", ({target}) => emit(panelFor(target), "show"));
|
||||
on(shows, "data", ({target}) => {
|
||||
let panel = panelFor(target);
|
||||
if (modelFor(panel).ready)
|
||||
emit(panel, "show");
|
||||
});
|
||||
|
||||
on(hides, "data", ({target}) => emit(panelFor(target), "hide"));
|
||||
on(hides, "data", ({target}) => {
|
||||
let panel = panelFor(target);
|
||||
if (modelFor(panel).ready)
|
||||
emit(panel, "hide");
|
||||
});
|
||||
|
||||
on(ready, "data", ({target}) => {
|
||||
let panel = panelFor(target);
|
||||
let window = domPanel.getContentDocument(target).defaultView;
|
||||
|
||||
workerFor(panel).attach(window);
|
||||
|
||||
if (!modelFor(panel).ready) {
|
||||
modelFor(panel).ready = true;
|
||||
|
||||
if (viewFor(panel).state == "open")
|
||||
emit(panel, "show");
|
||||
}
|
||||
});
|
||||
|
||||
on(start, "data", ({target}) => {
|
||||
|
|
|
@ -281,17 +281,28 @@ exports["test Hide Before Show"] = function(assert, done) {
|
|||
const { Panel } = require('sdk/panel');
|
||||
|
||||
let showCalled = false;
|
||||
let panel = Panel({
|
||||
let hideCalled = false;
|
||||
let panel1 = Panel({
|
||||
onShow: function () {
|
||||
showCalled = true;
|
||||
},
|
||||
onHide: function () {
|
||||
assert.ok(!showCalled, 'must not emit show if was hidden before');
|
||||
done();
|
||||
hideCalled = true;
|
||||
}
|
||||
});
|
||||
panel.show();
|
||||
panel.hide();
|
||||
panel1.show();
|
||||
panel1.hide();
|
||||
|
||||
let panel2 = Panel({
|
||||
onShow: function () {
|
||||
assert.ok(!showCalled, 'should not emit show');
|
||||
assert.ok(!hideCalled, 'should not emit hide');
|
||||
panel1.destroy();
|
||||
panel2.destroy();
|
||||
done();
|
||||
},
|
||||
});
|
||||
panel2.show();
|
||||
};
|
||||
|
||||
exports["test Several Show Hides"] = function(assert, done) {
|
||||
|
@ -1279,6 +1290,47 @@ exports["test panel addon global object"] = function*(assert) {
|
|||
loader.unload();
|
||||
}
|
||||
|
||||
exports["test panel load doesn't show"] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
|
||||
let showCount = 0;
|
||||
let panel = loader.require("sdk/panel").Panel({
|
||||
contentScript: "addEventListener('load', function(event) { self.postMessage('load'); });",
|
||||
contentScriptWhen: "start",
|
||||
contentURL: "data:text/html;charset=utf-8,",
|
||||
});
|
||||
|
||||
let shown = defer();
|
||||
let messaged = defer();
|
||||
|
||||
panel.once("show", function() {
|
||||
shown.resolve();
|
||||
});
|
||||
|
||||
panel.once("message", function() {
|
||||
messaged.resolve();
|
||||
});
|
||||
|
||||
panel.show();
|
||||
yield all([shown.promise, messaged.promise]);
|
||||
assert.ok(true, "Saw panel display");
|
||||
|
||||
panel.on("show", function() {
|
||||
assert.fail("Should not have seen another show event")
|
||||
});
|
||||
|
||||
messaged = defer();
|
||||
panel.once("message", function() {
|
||||
assert.ok(true, "Saw panel reload");
|
||||
messaged.resolve();
|
||||
});
|
||||
|
||||
panel.contentURL = "data:text/html;charset=utf-8,<html/>";
|
||||
|
||||
yield messaged.promise;
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
if (isWindowPBSupported) {
|
||||
exports.testGetWindow = function(assert, done) {
|
||||
let activeWindow = getMostRecentBrowserWindow();
|
||||
|
|
|
@ -183,7 +183,7 @@ skip-if = e10s # Bug ????? - This bug attached an event listener directly to the
|
|||
[browser_bug484315.js]
|
||||
skip-if = e10s
|
||||
[browser_bug491431.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 918634 - swapFrameLoaders (and thus replaceTabWithWindow) not implemented for e10s
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_bug495058.js]
|
||||
skip-if = e10s # Bug 918634 - swapFrameLoaders (and thus replaceTabWithWindow) not implemented for e10s
|
||||
[browser_bug517902.js]
|
||||
|
|
|
@ -114,22 +114,22 @@ p {
|
|||
.btn-accept,
|
||||
.btn-success,
|
||||
.btn-accept + .btn-chevron {
|
||||
background-color: #74bf43;
|
||||
border: 1px solid #74bf43;
|
||||
background-color: #5bc0a4;
|
||||
border: 1px solid #5bc0a4;
|
||||
}
|
||||
|
||||
.btn-accept:hover,
|
||||
.btn-success:hover,
|
||||
.btn-accept + .btn-chevron:hover {
|
||||
background-color: #6cb23e;
|
||||
border: 1px solid #6cb23e;
|
||||
background-color: #47b396;
|
||||
border: 1px solid #47b396;
|
||||
}
|
||||
|
||||
.btn-accept:active,
|
||||
.btn-success:active,
|
||||
.btn-accept + .btn-chevron:active {
|
||||
background-color: #64a43a;
|
||||
border: 1px solid #64a43a;
|
||||
background-color: #3aa689;
|
||||
border: 1px solid #3aa689;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
|
@ -153,7 +153,7 @@ p {
|
|||
.btn-decline + .btn-chevron:hover,
|
||||
.btn-error + .btn-chevron:hover {
|
||||
background-color: #c53436;
|
||||
border: 1px solid #c53436;
|
||||
border-color: #c53436;
|
||||
}
|
||||
|
||||
.btn-cancel:active,
|
||||
|
@ -163,7 +163,7 @@ p {
|
|||
.btn-decline + .btn-chevron:active,
|
||||
.btn-error + .btn-chevron:active {
|
||||
background-color: #ae2325;
|
||||
border: 1px solid #ae2325;
|
||||
border-color: #ae2325;
|
||||
}
|
||||
|
||||
.btn-chevron {
|
||||
|
|
|
@ -136,13 +136,18 @@
|
|||
display: none;
|
||||
margin-left: auto;
|
||||
padding: 12px 10px;
|
||||
border-radius: 30px;
|
||||
background: #7ed321;
|
||||
border-radius: 2px;
|
||||
background: #5bc0a4;
|
||||
color: #fff;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.icons:hover {
|
||||
background: #89e029;
|
||||
background-color: #47b396;
|
||||
}
|
||||
|
||||
.icons:hover:active {
|
||||
background-color: #3aa689;
|
||||
}
|
||||
|
||||
.icons i {
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
border-left: 1px solid rgba(255,255,255,.4);
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
background-color: #74BF43;
|
||||
background-color: #5bc0a4;
|
||||
background-position: center;
|
||||
background-size: 1rem;
|
||||
background-repeat: no-repeat;
|
||||
|
|
|
@ -243,20 +243,20 @@ body {
|
|||
}
|
||||
|
||||
.button.button-accept {
|
||||
background-color: #74bf43;
|
||||
border-color: #74bf43;
|
||||
background-color: #5bc0a4;
|
||||
border-color: #5bc0a4;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button.button-accept:hover {
|
||||
background-color: #6cb23e;
|
||||
border-color: #6cb23e;
|
||||
background-color: #47b396;
|
||||
border-color: #47b396;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button.button-accept:hover:active {
|
||||
background-color: #64a43a;
|
||||
border-color: #64a43a;
|
||||
background-color: #3aa689;
|
||||
border-color: #3aa689;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
|
|
@ -1908,6 +1908,10 @@ toolbarbutton.chevron {
|
|||
list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
|
||||
}
|
||||
|
||||
toolbar[brighttext] toolbarbutton.chevron:not(:hover):not([open="true"]) {
|
||||
list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
|
||||
}
|
||||
|
||||
toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
|
|
@ -214,6 +214,10 @@ toolbarbutton.chevron {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
toolbar[brighttext] toolbarbutton.chevron {
|
||||
list-style-image: url("chrome://global/skin/icons/chevron-inverted.png");
|
||||
}
|
||||
|
||||
toolbarbutton.chevron > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
@ -227,6 +231,10 @@ toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
|
|||
list-style-image: url("chrome://global/skin/icons/chevron@2x.png");
|
||||
}
|
||||
|
||||
toolbar[brighttext] toolbarbutton.chevron {
|
||||
list-style-image: url("chrome://global/skin/icons/chevron-inverted@2x.png");
|
||||
}
|
||||
|
||||
toolbarbutton.chevron > .toolbarbutton-icon {
|
||||
width: 13px;
|
||||
}
|
||||
|
|
|
@ -1975,6 +1975,10 @@ toolbarbutton.chevron {
|
|||
list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
|
||||
}
|
||||
|
||||
toolbar[brighttext] toolbarbutton.chevron {
|
||||
list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
|
||||
}
|
||||
|
||||
toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
|
|
@ -1536,6 +1536,7 @@ if test "$GNU_CXX"; then
|
|||
# -Wreturn-type - catches missing returns, zero false positives
|
||||
# -Wsequence-point - catches undefined order behavior like `a = a++`
|
||||
# -Wsign-compare - catches comparison of signed and unsigned types
|
||||
# -Wswitch - catches switches without all enum cases or default case
|
||||
# -Wtrigraphs - catches unlikely use of trigraphs
|
||||
# -Wtype-limits - catches overflow bugs, few false positives
|
||||
# -Wunused-label - catches unused goto labels
|
||||
|
@ -1554,6 +1555,7 @@ if test "$GNU_CXX"; then
|
|||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=pointer-arith"
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=switch"
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=trigraphs"
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=type-limits"
|
||||
|
|
|
@ -57,6 +57,14 @@ class nsGlobalWindow;
|
|||
class nsICSSDeclaration;
|
||||
class nsISMILAttr;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
struct ScrollIntoViewOptions;
|
||||
struct ScrollToOptions;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
already_AddRefed<nsContentList>
|
||||
NS_GetContentList(nsINode* aRootNode,
|
||||
int32_t aMatchNameSpaceId,
|
||||
|
@ -737,38 +745,23 @@ public:
|
|||
already_AddRefed<DestinationInsertionPointList> GetDestinationInsertionPoints();
|
||||
|
||||
void ScrollIntoView();
|
||||
void ScrollIntoView(bool aTop, const ScrollOptions &aOptions);
|
||||
int32_t ScrollTop()
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? sf->GetScrollPositionCSSPixels().y : 0;
|
||||
}
|
||||
void SetScrollTop(int32_t aScrollTop)
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
sf->ScrollToCSSPixels(CSSIntPoint(sf->GetScrollPositionCSSPixels().x,
|
||||
aScrollTop));
|
||||
}
|
||||
}
|
||||
int32_t ScrollLeft()
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? sf->GetScrollPositionCSSPixels().x : 0;
|
||||
}
|
||||
void SetScrollLeft(int32_t aScrollLeft)
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
sf->ScrollToCSSPixels(CSSIntPoint(aScrollLeft,
|
||||
sf->GetScrollPositionCSSPixels().y));
|
||||
}
|
||||
}
|
||||
void ScrollIntoView(bool aTop);
|
||||
void ScrollIntoView(const ScrollIntoViewOptions &aOptions);
|
||||
void Scroll(double aXScroll, double aYScroll);
|
||||
void Scroll(const ScrollToOptions& aOptions);
|
||||
void ScrollTo(double aXScroll, double aYScroll);
|
||||
void ScrollTo(const ScrollToOptions& aOptions);
|
||||
void ScrollBy(double aXScrollDif, double aYScrollDif);
|
||||
void ScrollBy(const ScrollToOptions& aOptions);
|
||||
/* Scrolls without flushing the layout.
|
||||
* aDx is the x offset, aDy the y offset in CSS pixels.
|
||||
* Returns true if we actually scrolled.
|
||||
*/
|
||||
bool ScrollByNoFlush(int32_t aDx, int32_t aDy);
|
||||
int32_t ScrollTop();
|
||||
void SetScrollTop(int32_t aScrollTop);
|
||||
int32_t ScrollLeft();
|
||||
void SetScrollLeft(int32_t aScrollLeft);
|
||||
int32_t ScrollWidth();
|
||||
int32_t ScrollHeight();
|
||||
int32_t ClientTop()
|
||||
|
@ -1115,6 +1108,17 @@ protected:
|
|||
bool aNotify,
|
||||
bool aCallAfterSetAttr);
|
||||
|
||||
/**
|
||||
* Scroll to a new position using behavior evaluated from CSS and
|
||||
* a CSSOM-View DOM method ScrollOptions dictionary. The scrolling may
|
||||
* be performed asynchronously or synchronously depending on the resolved
|
||||
* scroll-behavior.
|
||||
*
|
||||
* @param aScroll Destination of scroll, in CSS pixels
|
||||
* @param aOptions Dictionary of options to be evaluated
|
||||
*/
|
||||
void Scroll(const CSSIntPoint& aScroll, const ScrollOptions& aOptions);
|
||||
|
||||
/**
|
||||
* Convert an attribute string value to attribute type based on the type of
|
||||
* attribute. Called by SetAttr(). Note that at the moment we only do this
|
||||
|
|
|
@ -286,6 +286,23 @@ DOMMatrixReadOnly::ToFloat64Array(JSContext* aCx, JS::MutableHandle<JSObject*> a
|
|||
aResult.set(&value.toObject());
|
||||
}
|
||||
|
||||
void
|
||||
DOMMatrixReadOnly::Stringify(nsAString& aResult)
|
||||
{
|
||||
nsAutoString matrixStr;
|
||||
if (mMatrix3D) {
|
||||
matrixStr.AppendPrintf("matrix3d(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g)",
|
||||
M11(), M12(), M13(), M14(),
|
||||
M21(), M22(), M23(), M24(),
|
||||
M31(), M32(), M33(), M34(),
|
||||
M41(), M42(), M43(), M44());
|
||||
} else {
|
||||
matrixStr.AppendPrintf("matrix(%g, %g, %g, %g, %g, %g)", A(), B(), C(), D(), E(), F());
|
||||
}
|
||||
|
||||
aResult = matrixStr;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrix, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMatrix, AddRef)
|
||||
|
|
|
@ -124,6 +124,7 @@ public:
|
|||
void ToFloat64Array(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aResult,
|
||||
ErrorResult& aRv) const;
|
||||
void Stringify(nsAString& aResult);
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
nsAutoPtr<gfx::Matrix> mMatrix2D;
|
||||
|
|
|
@ -135,6 +135,8 @@
|
|||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/dom/DocumentFragment.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -587,11 +589,21 @@ Element::GetScrollFrame(nsIFrame **aStyledFrame, bool aFlushLayout)
|
|||
void
|
||||
Element::ScrollIntoView()
|
||||
{
|
||||
ScrollIntoView(true, ScrollOptions());
|
||||
ScrollIntoView(ScrollIntoViewOptions());
|
||||
}
|
||||
|
||||
void
|
||||
Element::ScrollIntoView(bool aTop, const ScrollOptions &aOptions)
|
||||
Element::ScrollIntoView(bool aTop)
|
||||
{
|
||||
ScrollIntoViewOptions options;
|
||||
if (!aTop) {
|
||||
options.mBlock = ScrollLogicalPosition::End;
|
||||
}
|
||||
ScrollIntoView(options);
|
||||
}
|
||||
|
||||
void
|
||||
Element::ScrollIntoView(const ScrollIntoViewOptions &aOptions)
|
||||
{
|
||||
nsIDocument *document = GetComposedDoc();
|
||||
if (!document) {
|
||||
|
@ -604,12 +616,15 @@ Element::ScrollIntoView(bool aTop, const ScrollOptions &aOptions)
|
|||
return;
|
||||
}
|
||||
|
||||
int16_t vpercent = aTop ? nsIPresShell::SCROLL_TOP :
|
||||
nsIPresShell::SCROLL_BOTTOM;
|
||||
int16_t vpercent = (aOptions.mBlock == ScrollLogicalPosition::Start)
|
||||
? nsIPresShell::SCROLL_TOP
|
||||
: nsIPresShell::SCROLL_BOTTOM;
|
||||
|
||||
uint32_t flags = nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
|
||||
if (aOptions.mBehavior == ScrollBehavior::Smooth) {
|
||||
flags |= nsIPresShell::SCROLL_SMOOTH;
|
||||
} else if (aOptions.mBehavior == ScrollBehavior::Auto) {
|
||||
flags |= nsIPresShell::SCROLL_SMOOTH_AUTO;
|
||||
}
|
||||
|
||||
presShell->ScrollContentIntoView(this,
|
||||
|
@ -620,6 +635,137 @@ Element::ScrollIntoView(bool aTop, const ScrollOptions &aOptions)
|
|||
flags);
|
||||
}
|
||||
|
||||
void
|
||||
Element::Scroll(const CSSIntPoint& aScroll, const ScrollOptions& aOptions)
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
|
||||
if (aOptions.mBehavior == ScrollBehavior::Smooth) {
|
||||
scrollMode = nsIScrollableFrame::SMOOTH_MSD;
|
||||
} else if (aOptions.mBehavior == ScrollBehavior::Auto) {
|
||||
ScrollbarStyles styles = sf->GetScrollbarStyles();
|
||||
if (styles.mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
|
||||
scrollMode = nsIScrollableFrame::SMOOTH_MSD;
|
||||
}
|
||||
}
|
||||
|
||||
sf->ScrollToCSSPixels(aScroll, scrollMode);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Element::Scroll(double aXScroll, double aYScroll)
|
||||
{
|
||||
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
|
||||
CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
|
||||
mozilla::ToZeroIfNonfinite(aYScroll));
|
||||
|
||||
Scroll(scrollPos, ScrollOptions());
|
||||
}
|
||||
|
||||
void
|
||||
Element::Scroll(const ScrollToOptions& aOptions)
|
||||
{
|
||||
nsIScrollableFrame *sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
|
||||
if (aOptions.mLeft.WasPassed()) {
|
||||
scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
|
||||
}
|
||||
if (aOptions.mTop.WasPassed()) {
|
||||
scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
|
||||
}
|
||||
Scroll(scrollPos, aOptions);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Element::ScrollTo(double aXScroll, double aYScroll)
|
||||
{
|
||||
Scroll(aXScroll, aYScroll);
|
||||
}
|
||||
|
||||
void
|
||||
Element::ScrollTo(const ScrollToOptions& aOptions)
|
||||
{
|
||||
Scroll(aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
Element::ScrollBy(double aXScrollDif, double aYScrollDif)
|
||||
{
|
||||
nsIScrollableFrame *sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
|
||||
scrollPos += CSSIntPoint(mozilla::ToZeroIfNonfinite(aXScrollDif),
|
||||
mozilla::ToZeroIfNonfinite(aYScrollDif));
|
||||
Scroll(scrollPos, ScrollOptions());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Element::ScrollBy(const ScrollToOptions& aOptions)
|
||||
{
|
||||
nsIScrollableFrame *sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
|
||||
if (aOptions.mLeft.WasPassed()) {
|
||||
scrollPos.x += mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
|
||||
}
|
||||
if (aOptions.mTop.WasPassed()) {
|
||||
scrollPos.y += mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
|
||||
}
|
||||
Scroll(scrollPos, aOptions);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
Element::ScrollTop()
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? sf->GetScrollPositionCSSPixels().y : 0;
|
||||
}
|
||||
|
||||
void
|
||||
Element::SetScrollTop(int32_t aScrollTop)
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
|
||||
if (sf->GetScrollbarStyles().mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
|
||||
scrollMode = nsIScrollableFrame::SMOOTH_MSD;
|
||||
}
|
||||
sf->ScrollToCSSPixels(CSSIntPoint(sf->GetScrollPositionCSSPixels().x,
|
||||
aScrollTop),
|
||||
scrollMode);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
Element::ScrollLeft()
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
return sf ? sf->GetScrollPositionCSSPixels().x : 0;
|
||||
}
|
||||
|
||||
void
|
||||
Element::SetScrollLeft(int32_t aScrollLeft)
|
||||
{
|
||||
nsIScrollableFrame* sf = GetScrollFrame();
|
||||
if (sf) {
|
||||
nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
|
||||
if (sf->GetScrollbarStyles().mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
|
||||
scrollMode = nsIScrollableFrame::SMOOTH_MSD;
|
||||
}
|
||||
|
||||
sf->ScrollToCSSPixels(CSSIntPoint(aScrollLeft,
|
||||
sf->GetScrollPositionCSSPixels().y),
|
||||
scrollMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Element::ScrollByNoFlush(int32_t aDx, int32_t aDy)
|
||||
{
|
||||
|
|
|
@ -885,7 +885,7 @@ class CSPReportSenderRunnable MOZ_FINAL : public nsRunnable
|
|||
nsString mScriptSample;
|
||||
uint32_t mLineNum;
|
||||
uint64_t mInnerWindowID;
|
||||
nsCSPContext* mCSPContext;
|
||||
nsRefPtr<nsCSPContext> mCSPContext;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#include "nsInProcessTabChildGlobal.h"
|
||||
|
||||
#include "Layers.h"
|
||||
#include "ClientLayerManager.h"
|
||||
|
||||
#include "AppProcessChecker.h"
|
||||
#include "ContentParent.h"
|
||||
|
@ -940,6 +941,93 @@ nsFrameLoader::Hide()
|
|||
baseWin->SetParentWidget(nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
|
||||
nsRefPtr<nsFrameLoader>& aFirstToSwap,
|
||||
nsRefPtr<nsFrameLoader>& aSecondToSwap)
|
||||
{
|
||||
Element* ourContent = mOwnerContent;
|
||||
Element* otherContent = aOther->mOwnerContent;
|
||||
|
||||
if (!ourContent || !otherContent) {
|
||||
// Can't handle this
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// Make sure there are no same-origin issues
|
||||
bool equal;
|
||||
nsresult rv =
|
||||
ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
|
||||
if (NS_FAILED(rv) || !equal) {
|
||||
// Security problems loom. Just bail on it all
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsIDocument* ourDoc = ourContent->GetCurrentDoc();
|
||||
nsIDocument* otherDoc = otherContent->GetCurrentDoc();
|
||||
if (!ourDoc || !otherDoc) {
|
||||
// Again, how odd, given that we had docshells
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsIPresShell* ourShell = ourDoc->GetShell();
|
||||
nsIPresShell* otherShell = otherDoc->GetShell();
|
||||
if (!ourShell || !otherShell) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if (mInSwap || aOther->mInSwap) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
mInSwap = aOther->mInSwap = true;
|
||||
|
||||
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
|
||||
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
|
||||
if (!ourFrame || !otherFrame) {
|
||||
mInSwap = aOther->mInSwap = false;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
|
||||
if (!ourFrameFrame) {
|
||||
mInSwap = aOther->mInSwap = false;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
|
||||
if (NS_FAILED(rv)) {
|
||||
mInSwap = aOther->mInSwap = false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
SetOwnerContent(otherContent);
|
||||
aOther->SetOwnerContent(ourContent);
|
||||
|
||||
mRemoteBrowser->SetOwnerElement(otherContent);
|
||||
aOther->mRemoteBrowser->SetOwnerElement(ourContent);
|
||||
|
||||
nsRefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
|
||||
nsRefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
|
||||
// Swap and setup things in parent message managers.
|
||||
if (mMessageManager) {
|
||||
mMessageManager->SetCallback(aOther);
|
||||
}
|
||||
if (aOther->mMessageManager) {
|
||||
aOther->mMessageManager->SetCallback(this);
|
||||
}
|
||||
mMessageManager.swap(aOther->mMessageManager);
|
||||
|
||||
aFirstToSwap.swap(aSecondToSwap);
|
||||
|
||||
ourFrameFrame->EndSwapDocShells(otherFrame);
|
||||
|
||||
ourDoc->FlushPendingNotifications(Flush_Layout);
|
||||
otherDoc->FlushPendingNotifications(Flush_Layout);
|
||||
|
||||
mInSwap = aOther->mInSwap = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
|
||||
nsRefPtr<nsFrameLoader>& aFirstToSwap,
|
||||
|
@ -950,6 +1038,15 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
|
|||
"Swapping some sort of random loaders?");
|
||||
NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
|
||||
|
||||
if (mRemoteFrame && aOther->mRemoteFrame) {
|
||||
return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
|
||||
}
|
||||
|
||||
if (mRemoteFrame || aOther->mRemoteFrame) {
|
||||
NS_WARNING("Swapping remote and non-remote frames is not currently supported");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
Element* ourContent = mOwnerContent;
|
||||
Element* otherContent = aOther->mOwnerContent;
|
||||
|
||||
|
|
|
@ -131,6 +131,10 @@ public:
|
|||
nsRefPtr<nsFrameLoader>& aFirstToSwap,
|
||||
nsRefPtr<nsFrameLoader>& aSecondToSwap);
|
||||
|
||||
nsresult SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
|
||||
nsRefPtr<nsFrameLoader>& aFirstToSwap,
|
||||
nsRefPtr<nsFrameLoader>& aSecondToSwap);
|
||||
|
||||
// When IPC is enabled, destroy any associated child process.
|
||||
void DestroyChild();
|
||||
|
||||
|
|
|
@ -469,7 +469,7 @@ public:
|
|||
if (!_argc) {
|
||||
top = true;
|
||||
}
|
||||
mozilla::dom::Element::ScrollIntoView(top, mozilla::dom::ScrollOptions());
|
||||
mozilla::dom::Element::ScrollIntoView(top);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD GetOffsetParent(nsIDOMElement** aOffsetParent) MOZ_FINAL {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "GMPCallbackBase.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mp4_demuxer {
|
||||
class CryptoSample;
|
||||
|
|
|
@ -60,6 +60,7 @@ GMPParent::GMPParent()
|
|||
, mAbnormalShutdownInProgress(false)
|
||||
, mAsyncShutdownRequired(false)
|
||||
, mAsyncShutdownInProgress(false)
|
||||
, mHasAccessedStorage(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -218,6 +219,10 @@ GMPParent::AbortAsyncShutdown()
|
|||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
|
||||
if (!mAsyncShutdownRequired || !mAsyncShutdownInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<GMPParent> kungFuDeathGrip(this);
|
||||
mService->AsyncShutdownComplete(this);
|
||||
mAsyncShutdownRequired = false;
|
||||
|
@ -611,6 +616,12 @@ GMPParent::ActorDestroy(ActorDestroyReason aWhy)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GMPParent::HasAccessedStorage() const
|
||||
{
|
||||
return mHasAccessedStorage;
|
||||
}
|
||||
|
||||
mozilla::dom::PCrashReporterParent*
|
||||
GMPParent::AllocPCrashReporterParent(const NativeThreadId& aThread)
|
||||
{
|
||||
|
@ -717,6 +728,7 @@ GMPParent::RecvPGMPStorageConstructor(PGMPStorageParent* aActor)
|
|||
if (NS_WARN_IF(NS_FAILED(p->Init()))) {
|
||||
return false;
|
||||
}
|
||||
mHasAccessedStorage = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,8 @@ public:
|
|||
|
||||
void AbortAsyncShutdown();
|
||||
|
||||
bool HasAccessedStorage() const;
|
||||
|
||||
private:
|
||||
~GMPParent();
|
||||
nsRefPtr<GeckoMediaPluginService> mService;
|
||||
|
@ -192,6 +194,7 @@ private:
|
|||
|
||||
bool mAsyncShutdownRequired;
|
||||
bool mAsyncShutdownInProgress;
|
||||
bool mHasAccessedStorage;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -172,6 +172,7 @@ GeckoMediaPluginService::Init()
|
|||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "last-pb-context-exited", false)));
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "gmp-clear-storage", false)));
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (prefs) {
|
||||
|
@ -325,6 +326,15 @@ GeckoMediaPluginService::Observe(nsISupports* aSubject,
|
|||
// mode, we'll get the NodeId salt stored on-disk, and if we try to
|
||||
// open a PB mode origin-pair, we'll re-generate new salt.
|
||||
mTempNodeIds.Clear();
|
||||
} else if (!strcmp("gmp-clear-storage", aTopic)) {
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsresult rv = GetThread(getter_AddRefs(thread));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
thread->Dispatch(
|
||||
NS_NewRunnableMethod(this, &GeckoMediaPluginService::ClearStorage),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1187,5 +1197,66 @@ GeckoMediaPluginService::GetNodeId(const nsAString& aOrigin,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
class StorageClearedTask : public nsRunnable {
|
||||
public:
|
||||
NS_IMETHOD Run() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(obsService);
|
||||
if (obsService) {
|
||||
obsService->NotifyObservers(nullptr, "gmp-clear-storage-complete", nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
GeckoMediaPluginService::ClearStorage()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
LOGD(("%s::%s", __CLASS__, __FUNCTION__));
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
NS_WARNING("GeckoMediaPluginService::ClearStorage not implemented on B2G");
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Shutdown plugins that have touched storage, as they could have
|
||||
// state that depends on storage. We don't want them to write data
|
||||
// after we've cleared storage, as they could end up in an inconsistent
|
||||
// state, so we must ensure they're shutdown before we actually clear
|
||||
// storage. Note: we can't shut them down while holding the lock,
|
||||
// as the lock is not re-entrant and shutdown requires taking the lock.
|
||||
// The plugin list is only edited on the GMP thread, so this should be OK.
|
||||
nsTArray<nsRefPtr<GMPParent>> pluginsToKill;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (size_t i = 0; i < mPlugins.Length(); i++) {
|
||||
nsRefPtr<GMPParent> parent(mPlugins[i]);
|
||||
if (parent->HasAccessedStorage()) {
|
||||
pluginsToKill.AppendElement(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < pluginsToKill.Length(); i++) {
|
||||
pluginsToKill[i]->CloseActive(false);
|
||||
// Abort async shutdown because we're going to wipe the plugin's storage,
|
||||
// so we don't want it writing more data in its async shutdown path.
|
||||
pluginsToKill[i]->AbortAsyncShutdown();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> path; // $profileDir/gmp/
|
||||
nsresult rv = GetStorageDir(getter_AddRefs(path));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_FAILED(path->Remove(true))) {
|
||||
NS_WARNING("Failed to delete GMP storage directory");
|
||||
}
|
||||
NS_DispatchToMainThread(new StorageClearedTask(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -48,6 +48,8 @@ public:
|
|||
private:
|
||||
~GeckoMediaPluginService();
|
||||
|
||||
void ClearStorage();
|
||||
|
||||
GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags);
|
||||
|
|
|
@ -5,15 +5,20 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "GMPVideoDecoderProxy.h"
|
||||
#include "GMPVideoEncoderProxy.h"
|
||||
#include "GMPDecryptorProxy.h"
|
||||
#include "GMPService.h"
|
||||
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "nsNSSComponent.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gmp;
|
||||
|
@ -101,6 +106,433 @@ GMPTestRunner::RunTestGMPCrossOrigin()
|
|||
if (encoder2) encoder2->Close();
|
||||
}
|
||||
|
||||
class NotifyObserversTask : public nsRunnable {
|
||||
public:
|
||||
NotifyObserversTask(const char* aTopic)
|
||||
: mTopic(aTopic)
|
||||
{}
|
||||
NS_IMETHOD Run() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->NotifyObservers(nullptr, mTopic, nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
const char* mTopic;
|
||||
};
|
||||
|
||||
class ClearGMPStorageTask : public nsIRunnable
|
||||
, public nsIObserver {
|
||||
public:
|
||||
ClearGMPStorageTask(nsIRunnable* Continuation,
|
||||
nsIThread* aTarget)
|
||||
: mContinuation(Continuation)
|
||||
, mTarget(aTarget)
|
||||
{}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
EXPECT_TRUE(observerService);
|
||||
observerService->AddObserver(this, "gmp-clear-storage-complete", false);
|
||||
if (observerService) {
|
||||
observerService->NotifyObservers(nullptr, "gmp-clear-storage", nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aSomeData) MOZ_OVERRIDE
|
||||
{
|
||||
if (!strcmp(aTopic, "gmp-clear-storage-complete")) {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
EXPECT_TRUE(observerService);
|
||||
observerService->RemoveObserver(this, "gmp-clear-storage-complete");
|
||||
mTarget->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~ClearGMPStorageTask() {}
|
||||
nsRefPtr<nsIRunnable> mContinuation;
|
||||
nsCOMPtr<nsIThread> mTarget;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ClearGMPStorageTask, nsIRunnable, nsIObserver)
|
||||
|
||||
static void
|
||||
ClearGMPStorage(nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget)
|
||||
{
|
||||
nsRefPtr<ClearGMPStorageTask> task(new ClearGMPStorageTask(aContinuation, aTarget));
|
||||
NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
SimulatePBModeExit()
|
||||
{
|
||||
NS_DispatchToMainThread(new NotifyObserversTask("last-pb-context-exited"), NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
static nsCString
|
||||
GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
bool aInPBMode)
|
||||
{
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
EXPECT_TRUE(service);
|
||||
nsCString nodeId;
|
||||
nsresult rv = service->GetNodeId(aOrigin,
|
||||
aTopLevelOrigin,
|
||||
aInPBMode,
|
||||
nodeId);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsGMPStorageIsEmpty()
|
||||
{
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
MOZ_ASSERT(service);
|
||||
nsCOMPtr<nsIFile> storage;
|
||||
nsresult rv = service->GetStorageDir(getter_AddRefs(storage));
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
bool exists = false;
|
||||
if (storage) {
|
||||
storage->Exists(&exists);
|
||||
}
|
||||
return !exists;
|
||||
}
|
||||
|
||||
static void
|
||||
AssertIsOnGMPThread()
|
||||
{
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
MOZ_ASSERT(service);
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
service->GetThread(getter_AddRefs(thread));
|
||||
MOZ_ASSERT(thread);
|
||||
nsCOMPtr<nsIThread> currentThread;
|
||||
DebugOnly<nsresult> rv = NS_GetCurrentThread(getter_AddRefs(currentThread));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
MOZ_ASSERT(currentThread == thread);
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIThread>
|
||||
GetGMPThread()
|
||||
{
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread))));
|
||||
return thread.forget();
|
||||
}
|
||||
class GMPStorageTest : public GMPDecryptorProxyCallback
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPStorageTest)
|
||||
|
||||
void DoTest(void (GMPStorageTest::*aTestMethod)()) {
|
||||
EnsureNSSInitializedChromeOrContent();
|
||||
nsCOMPtr<nsIThread> thread(GetGMPThread());
|
||||
ClearGMPStorage(NS_NewRunnableMethod(this, aTestMethod), thread);
|
||||
AwaitFinished();
|
||||
}
|
||||
|
||||
GMPStorageTest()
|
||||
: mDecryptor(nullptr)
|
||||
, mMonitor("GMPStorageTest")
|
||||
, mFinished(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Update(const nsCString& aMessage)
|
||||
{
|
||||
nsTArray<uint8_t> msg;
|
||||
msg.AppendElements(aMessage.get(), aMessage.Length());
|
||||
mDecryptor->UpdateSession(1, NS_LITERAL_CSTRING("fake-session-id"), msg);
|
||||
}
|
||||
|
||||
void TestGetNodeId()
|
||||
{
|
||||
AssertIsOnGMPThread();
|
||||
|
||||
EXPECT_TRUE(IsGMPStorageIsEmpty());
|
||||
|
||||
const nsString origin1 = NS_LITERAL_STRING("example1.com");
|
||||
const nsString origin2 = NS_LITERAL_STRING("example2.org");
|
||||
|
||||
nsCString PBnodeId1 = GetNodeId(origin1, origin2, true);
|
||||
nsCString PBnodeId2 = GetNodeId(origin1, origin2, true);
|
||||
|
||||
// Node ids for the same origins should be the same in PB mode.
|
||||
EXPECT_TRUE(PBnodeId1.Equals(PBnodeId2));
|
||||
|
||||
nsCString PBnodeId3 = GetNodeId(origin2, origin1, true);
|
||||
|
||||
// Node ids with origin and top level origin swapped should be different.
|
||||
EXPECT_TRUE(!PBnodeId3.Equals(PBnodeId1));
|
||||
|
||||
// Getting node ids in PB mode should not result in the node id being stored.
|
||||
EXPECT_TRUE(IsGMPStorageIsEmpty());
|
||||
|
||||
nsCString nodeId1 = GetNodeId(origin1, origin2, false);
|
||||
nsCString nodeId2 = GetNodeId(origin1, origin2, false);
|
||||
|
||||
// NodeIds for the same origin pair in non-pb mode should be the same.
|
||||
EXPECT_TRUE(nodeId1.Equals(nodeId2));
|
||||
|
||||
// Node ids for a given origin pair should be different for the PB origins should be the same in PB mode.
|
||||
EXPECT_TRUE(!PBnodeId1.Equals(nodeId1));
|
||||
EXPECT_TRUE(!PBnodeId2.Equals(nodeId2));
|
||||
|
||||
nsCOMPtr<nsIThread> thread(GetGMPThread());
|
||||
ClearGMPStorage(NS_NewRunnableMethodWithArg<nsCString>(
|
||||
this, &GMPStorageTest::TestGetNodeId_Continuation, nodeId1), thread);
|
||||
}
|
||||
|
||||
void TestGetNodeId_Continuation(nsCString aNodeId1) {
|
||||
EXPECT_TRUE(IsGMPStorageIsEmpty());
|
||||
|
||||
// Once we clear storage, the node ids generated for the same origin-pair
|
||||
// should be different.
|
||||
const nsString origin1 = NS_LITERAL_STRING("example1.com");
|
||||
const nsString origin2 = NS_LITERAL_STRING("example2.org");
|
||||
nsCString nodeId3 = GetNodeId(origin1, origin2, false);
|
||||
EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
|
||||
|
||||
SetFinished();
|
||||
}
|
||||
|
||||
void CreateDecryptor(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
bool aInPBMode) {
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
EXPECT_TRUE(service);
|
||||
|
||||
const nsCString nodeId = GetNodeId(aOrigin,
|
||||
aTopLevelOrigin,
|
||||
aInPBMode);
|
||||
EXPECT_TRUE(!nodeId.IsEmpty());
|
||||
|
||||
nsTArray<nsCString> tags;
|
||||
tags.AppendElement(NS_LITERAL_CSTRING("fake"));
|
||||
|
||||
nsresult rv = service->GetGMPDecryptor(&tags, nodeId, &mDecryptor);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_TRUE(!!mDecryptor);
|
||||
|
||||
if (mDecryptor) {
|
||||
mDecryptor->Init(this);
|
||||
}
|
||||
}
|
||||
|
||||
void TestBasicStorage() {
|
||||
AssertIsOnGMPThread();
|
||||
EXPECT_TRUE(IsGMPStorageIsEmpty());
|
||||
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
|
||||
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
|
||||
NS_LITERAL_STRING("example2.com"),
|
||||
false);
|
||||
|
||||
// Send a message to the fake GMP for it to run its own tests internally.
|
||||
// It sends us a "test-storage complete" message when its passed, or
|
||||
// some other message if its tests fail.
|
||||
Expect(NS_LITERAL_CSTRING("test-storage complete"),
|
||||
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
|
||||
Update(NS_LITERAL_CSTRING("test-storage"));
|
||||
}
|
||||
|
||||
void TestCrossOriginStorage() {
|
||||
EXPECT_TRUE(!mDecryptor);
|
||||
|
||||
// Open decryptor on one, origin, write a record, and test that that
|
||||
// record can't be read on another origin.
|
||||
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
|
||||
NS_LITERAL_STRING("example4.com"),
|
||||
false);
|
||||
|
||||
// Send the decryptor the message "store recordid $time"
|
||||
// Wait for the decrytor to send us "stored recordid $time"
|
||||
auto t = time(0);
|
||||
nsCString response("stored crossOriginTestRecordId ");
|
||||
response.AppendInt((int64_t)t);
|
||||
Expect(response, NS_NewRunnableMethod(this,
|
||||
&GMPStorageTest::TestCrossOriginStorage_RecordStoredContinuation));
|
||||
|
||||
nsCString update("store crossOriginTestRecordId ");
|
||||
update.AppendInt((int64_t)t);
|
||||
Update(update);
|
||||
}
|
||||
|
||||
void TestCrossOriginStorage_RecordStoredContinuation() {
|
||||
// Close the old decryptor, and create a new one on a different origin,
|
||||
// and try to read the record.
|
||||
Shutdown();
|
||||
|
||||
CreateDecryptor(NS_LITERAL_STRING("example5.com"),
|
||||
NS_LITERAL_STRING("example6.com"),
|
||||
false);
|
||||
|
||||
Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
|
||||
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
|
||||
Update(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
|
||||
}
|
||||
|
||||
void TestPBStorage() {
|
||||
|
||||
// Open decryptor on one, origin, write a record, close decryptor,
|
||||
// open another, and test that record can be read, close decryptor,
|
||||
// then send pb-last-context-closed notification, then open decryptor
|
||||
// and check that it can't read that data; it should have been purged.
|
||||
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
|
||||
NS_LITERAL_STRING("pb2.com"),
|
||||
true);
|
||||
|
||||
// Send the decryptor the message "store recordid $time"
|
||||
// Wait for the decrytor to send us "stored recordid $time"
|
||||
nsCString response("stored pbdata test-pb-data");
|
||||
Expect(response, NS_NewRunnableMethod(this,
|
||||
&GMPStorageTest::TestPBStorage_RecordStoredContinuation));
|
||||
|
||||
nsCString update("store pbdata test-pb-data");
|
||||
Update(update);
|
||||
}
|
||||
|
||||
void TestPBStorage_RecordStoredContinuation() {
|
||||
Shutdown();
|
||||
|
||||
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
|
||||
NS_LITERAL_STRING("pb2.com"),
|
||||
true);
|
||||
|
||||
Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"),
|
||||
NS_NewRunnableMethod(this,
|
||||
&GMPStorageTest::TestPBStorage_RecordRetrievedContinuation));
|
||||
Update(NS_LITERAL_CSTRING("retrieve pbdata"));
|
||||
}
|
||||
|
||||
void TestPBStorage_RecordRetrievedContinuation() {
|
||||
Shutdown();
|
||||
SimulatePBModeExit();
|
||||
|
||||
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
|
||||
NS_LITERAL_STRING("pb2.com"),
|
||||
true);
|
||||
|
||||
Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
|
||||
NS_NewRunnableMethod(this,
|
||||
&GMPStorageTest::SetFinished));
|
||||
Update(NS_LITERAL_CSTRING("retrieve pbdata"));
|
||||
|
||||
}
|
||||
|
||||
void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
|
||||
mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
|
||||
}
|
||||
|
||||
void AwaitFinished() {
|
||||
while (!mFinished) {
|
||||
NS_ProcessNextEvent(nullptr, true);
|
||||
}
|
||||
mFinished = false;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
if (mDecryptor) {
|
||||
mDecryptor->Close();
|
||||
mDecryptor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Dummy() {
|
||||
}
|
||||
|
||||
void SetFinished() {
|
||||
mFinished = true;
|
||||
Shutdown();
|
||||
NS_DispatchToMainThread(NS_NewRunnableMethod(this, &GMPStorageTest::Dummy));
|
||||
}
|
||||
|
||||
virtual void SessionMessage(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
const nsCString& aDestinationURL) MOZ_OVERRIDE
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
|
||||
nsCString msg((const char*)aMessage.Elements(), aMessage.Length());
|
||||
EXPECT_TRUE(mExpected.Length() > 0);
|
||||
bool matches = mExpected[0].mMessage.Equals(msg);
|
||||
EXPECT_TRUE(matches);
|
||||
if (mExpected.Length() > 0 && matches) {
|
||||
nsRefPtr<nsIRunnable> continuation = mExpected[0].mContinuation;
|
||||
mExpected.RemoveElementAt(0);
|
||||
if (continuation) {
|
||||
NS_DispatchToCurrentThread(continuation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) MOZ_OVERRIDE { }
|
||||
virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
|
||||
bool aSuccess) MOZ_OVERRIDE {}
|
||||
virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE {}
|
||||
virtual void RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aSessionId) MOZ_OVERRIDE { }
|
||||
virtual void ExpirationChange(const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime) MOZ_OVERRIDE {}
|
||||
virtual void SessionClosed(const nsCString& aSessionId) MOZ_OVERRIDE {}
|
||||
virtual void SessionError(const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage) MOZ_OVERRIDE {}
|
||||
virtual void KeyIdUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE { }
|
||||
virtual void KeyIdNotUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE {}
|
||||
virtual void SetCaps(uint64_t aCaps) MOZ_OVERRIDE {}
|
||||
virtual void Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) MOZ_OVERRIDE { }
|
||||
virtual void Terminated() MOZ_OVERRIDE { }
|
||||
|
||||
private:
|
||||
~GMPStorageTest() { }
|
||||
|
||||
struct ExpectedMessage {
|
||||
ExpectedMessage(const nsCString& aMessage, nsIRunnable* aContinuation)
|
||||
: mMessage(aMessage)
|
||||
, mContinuation(aContinuation)
|
||||
{}
|
||||
nsCString mMessage;
|
||||
nsRefPtr<nsIRunnable> mContinuation;
|
||||
};
|
||||
|
||||
nsTArray<ExpectedMessage> mExpected;
|
||||
|
||||
GMPDecryptorProxy* mDecryptor;
|
||||
Monitor mMonitor;
|
||||
Atomic<bool> mFinished;
|
||||
};
|
||||
|
||||
void
|
||||
GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)())
|
||||
{
|
||||
|
@ -121,3 +553,23 @@ TEST(GeckoMediaPlugins, GMPCrossOrigin) {
|
|||
nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
|
||||
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin);
|
||||
}
|
||||
|
||||
TEST(GeckoMediaPlugins, GMPStorageGetNodeId) {
|
||||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||
runner->DoTest(&GMPStorageTest::TestGetNodeId);
|
||||
}
|
||||
|
||||
TEST(GeckoMediaPlugins, GMPStorageBasic) {
|
||||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||
runner->DoTest(&GMPStorageTest::TestBasicStorage);
|
||||
}
|
||||
|
||||
TEST(GeckoMediaPlugins, GMPStorageCrossOrigin) {
|
||||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||
runner->DoTest(&GMPStorageTest::TestCrossOriginStorage);
|
||||
}
|
||||
|
||||
TEST(GeckoMediaPlugins, GMPStoragePrivateBrowsing) {
|
||||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||
runner->DoTest(&GMPStorageTest::TestPBStorage);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ include('/ipc/chromium/chromium-config.mozbuild')
|
|||
LOCAL_INCLUDES += [
|
||||
'/content/media/encoder',
|
||||
'/content/media/gmp',
|
||||
'/security/certverifier',
|
||||
'/security/pkix/include',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
|
|
|
@ -10730,7 +10730,8 @@ nsDocShell::ScrollToAnchor(nsACString & aCurHash, nsACString & aNewHash,
|
|||
nsresult rv = NS_ERROR_FAILURE;
|
||||
NS_ConvertUTF8toUTF16 uStr(str);
|
||||
if (!uStr.IsEmpty()) {
|
||||
rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll);
|
||||
rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll,
|
||||
nsIPresShell::SCROLL_SMOOTH_AUTO);
|
||||
}
|
||||
nsMemory::Free(str);
|
||||
|
||||
|
@ -10764,7 +10765,8 @@ nsDocShell::ScrollToAnchor(nsACString & aCurHash, nsACString & aNewHash,
|
|||
//
|
||||
// When newHashName contains "%00", unescaped string may be empty.
|
||||
// And GoToAnchor asserts if we ask it to scroll to an empty ref.
|
||||
shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty());
|
||||
shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
|
||||
nsIPresShell::SCROLL_SMOOTH_AUTO);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -860,7 +860,7 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
|||
loadContext->GetUsePrivateBrowsing(&callData->mPrivate);
|
||||
}
|
||||
|
||||
uint32_t maxDepth = ShouldIncludeStackrace(aMethodName) ?
|
||||
uint32_t maxDepth = ShouldIncludeStackTrace(aMethodName) ?
|
||||
DEFAULT_MAX_STACKTRACE_DEPTH : 1;
|
||||
nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
|
||||
|
||||
|
@ -1133,7 +1133,7 @@ Console::ProcessCallData(ConsoleCallData* aData)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ShouldIncludeStackrace(aData->mMethodName)) {
|
||||
if (ShouldIncludeStackTrace(aData->mMethodName)) {
|
||||
// Now define the "stacktrace" property on eventObj. There are two cases
|
||||
// here. Either we came from a worker and have a reified stack, or we want
|
||||
// to define a getter that will lazily reify the stack.
|
||||
|
@ -1642,7 +1642,7 @@ Console::ClearConsoleData()
|
|||
}
|
||||
|
||||
bool
|
||||
Console::ShouldIncludeStackrace(MethodName aMethodName)
|
||||
Console::ShouldIncludeStackTrace(MethodName aMethodName)
|
||||
{
|
||||
switch (aMethodName) {
|
||||
case MethodError:
|
||||
|
|
|
@ -195,7 +195,7 @@ private:
|
|||
ClearConsoleData();
|
||||
|
||||
bool
|
||||
ShouldIncludeStackrace(MethodName aMethodName);
|
||||
ShouldIncludeStackTrace(MethodName aMethodName);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
|
|
@ -7131,23 +7131,46 @@ nsGlobalWindow::GetTopWindowRoot()
|
|||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::Scroll(double aXScroll, double aYScroll,
|
||||
const ScrollOptions& aOptions)
|
||||
nsGlobalWindow::Scroll(double aXScroll, double aYScroll)
|
||||
{
|
||||
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
|
||||
CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
|
||||
mozilla::ToZeroIfNonfinite(aYScroll));
|
||||
ScrollTo(scrollPos, aOptions);
|
||||
ScrollTo(scrollPos, ScrollOptions());
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::ScrollTo(double aXScroll, double aYScroll,
|
||||
const ScrollOptions& aOptions)
|
||||
nsGlobalWindow::ScrollTo(double aXScroll, double aYScroll)
|
||||
{
|
||||
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
|
||||
CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
|
||||
mozilla::ToZeroIfNonfinite(aYScroll));
|
||||
ScrollTo(scrollPos, aOptions);
|
||||
ScrollTo(scrollPos, ScrollOptions());
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::ScrollTo(const ScrollToOptions& aOptions)
|
||||
{
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
nsIScrollableFrame *sf = GetScrollFrame();
|
||||
|
||||
if (sf) {
|
||||
CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
|
||||
if (aOptions.mLeft.WasPassed()) {
|
||||
scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
|
||||
}
|
||||
if (aOptions.mTop.WasPassed()) {
|
||||
scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
|
||||
}
|
||||
|
||||
ScrollTo(scrollPos, aOptions);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::Scroll(const ScrollToOptions& aOptions)
|
||||
{
|
||||
ScrollTo(aOptions);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -7188,8 +7211,9 @@ nsGlobalWindow::ScrollTo(const CSSIntPoint& aScroll,
|
|||
scroll.y = maxpx;
|
||||
}
|
||||
|
||||
sf->ScrollToCSSPixels(scroll,
|
||||
aOptions.mBehavior == ScrollBehavior::Smooth
|
||||
bool smoothScroll = sf->GetScrollbarStyles().IsSmoothScroll(aOptions.mBehavior);
|
||||
|
||||
sf->ScrollToCSSPixels(scroll, smoothScroll
|
||||
? nsIScrollableFrame::SMOOTH_MSD
|
||||
: nsIScrollableFrame::INSTANT);
|
||||
}
|
||||
|
@ -7198,14 +7222,13 @@ nsGlobalWindow::ScrollTo(const CSSIntPoint& aScroll,
|
|||
NS_IMETHODIMP
|
||||
nsGlobalWindow::ScrollBy(int32_t aXScrollDif, int32_t aYScrollDif)
|
||||
{
|
||||
ScrollBy(aXScrollDif, aYScrollDif, ScrollOptions());
|
||||
ScrollBy(aXScrollDif, aYScrollDif);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::ScrollBy(double aXScrollDif, double aYScrollDif,
|
||||
const ScrollOptions& aOptions)
|
||||
nsGlobalWindow::ScrollBy(double aXScrollDif, double aYScrollDif)
|
||||
{
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
nsIScrollableFrame *sf = GetScrollFrame();
|
||||
|
@ -7217,7 +7240,26 @@ nsGlobalWindow::ScrollBy(double aXScrollDif, double aYScrollDif,
|
|||
// It seems like it would make more sense for ScrollBy to use
|
||||
// SMOOTH mode, but tests seem to depend on the synchronous behaviour.
|
||||
// Perhaps Web content does too.
|
||||
ScrollTo(sf->GetScrollPositionCSSPixels() + scrollDif, aOptions);
|
||||
ScrollTo(sf->GetScrollPositionCSSPixels() + scrollDif, ScrollOptions());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::ScrollBy(const ScrollToOptions& aOptions)
|
||||
{
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
nsIScrollableFrame *sf = GetScrollFrame();
|
||||
|
||||
if (sf) {
|
||||
CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
|
||||
if (aOptions.mLeft.WasPassed()) {
|
||||
scrollPos.x += mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
|
||||
}
|
||||
if (aOptions.mTop.WasPassed()) {
|
||||
scrollPos.y += mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
|
||||
}
|
||||
|
||||
ScrollTo(scrollPos, aOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7239,8 +7281,10 @@ nsGlobalWindow::ScrollByLines(int32_t numLines,
|
|||
// It seems like it would make more sense for ScrollByLines to use
|
||||
// SMOOTH mode, but tests seem to depend on the synchronous behaviour.
|
||||
// Perhaps Web content does too.
|
||||
bool smoothScroll = sf->GetScrollbarStyles().IsSmoothScroll(aOptions.mBehavior);
|
||||
|
||||
sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
|
||||
aOptions.mBehavior == ScrollBehavior::Smooth
|
||||
smoothScroll
|
||||
? nsIScrollableFrame::SMOOTH_MSD
|
||||
: nsIScrollableFrame::INSTANT);
|
||||
}
|
||||
|
@ -7264,8 +7308,10 @@ nsGlobalWindow::ScrollByPages(int32_t numPages,
|
|||
// It seems like it would make more sense for ScrollByPages to use
|
||||
// SMOOTH mode, but tests seem to depend on the synchronous behaviour.
|
||||
// Perhaps Web content does too.
|
||||
bool smoothScroll = sf->GetScrollbarStyles().IsSmoothScroll(aOptions.mBehavior);
|
||||
|
||||
sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
|
||||
aOptions.mBehavior == ScrollBehavior::Smooth
|
||||
smoothScroll
|
||||
? nsIScrollableFrame::SMOOTH_MSD
|
||||
: nsIScrollableFrame::INSTANT);
|
||||
}
|
||||
|
|
|
@ -910,12 +910,12 @@ public:
|
|||
mozilla::ErrorResult& aError);
|
||||
void ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
|
||||
mozilla::ErrorResult& aError);
|
||||
void Scroll(double aXScroll, double aYScroll,
|
||||
const mozilla::dom::ScrollOptions& aOptions);
|
||||
void ScrollTo(double aXScroll, double aYScroll,
|
||||
const mozilla::dom::ScrollOptions& aOptions);
|
||||
void ScrollBy(double aXScrollDif, double aYScrollDif,
|
||||
const mozilla::dom::ScrollOptions& aOptions);
|
||||
void Scroll(double aXScroll, double aYScroll);
|
||||
void Scroll(const mozilla::dom::ScrollToOptions& aOptions);
|
||||
void ScrollTo(double aXScroll, double aYScroll);
|
||||
void ScrollTo(const mozilla::dom::ScrollToOptions& aOptions);
|
||||
void ScrollBy(double aXScrollDif, double aYScrollDif);
|
||||
void ScrollBy(const mozilla::dom::ScrollToOptions& aOptions);
|
||||
void ScrollByLines(int32_t numLines,
|
||||
const mozilla::dom::ScrollOptions& aOptions);
|
||||
void ScrollByPages(int32_t numPages,
|
||||
|
|
|
@ -59,53 +59,6 @@ function sendSyncMsg(msg, data) {
|
|||
|
||||
let CERTIFICATE_ERROR_PAGE_PREF = 'security.alternate_certificate_error_page';
|
||||
|
||||
let NS_ERROR_MODULE_BASE_OFFSET = 0x45;
|
||||
let NS_ERROR_MODULE_SECURITY= 21;
|
||||
function NS_ERROR_GET_MODULE(err) {
|
||||
return ((((err) >> 16) - NS_ERROR_MODULE_BASE_OFFSET) & 0x1fff);
|
||||
}
|
||||
|
||||
function NS_ERROR_GET_CODE(err) {
|
||||
return ((err) & 0xffff);
|
||||
}
|
||||
|
||||
let SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
|
||||
let SEC_ERROR_UNKNOWN_ISSUER = (SEC_ERROR_BASE + 13);
|
||||
let SEC_ERROR_CA_CERT_INVALID = (SEC_ERROR_BASE + 36);
|
||||
let SEC_ERROR_UNTRUSTED_ISSUER = (SEC_ERROR_BASE + 20);
|
||||
let SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = (SEC_ERROR_BASE + 30);
|
||||
let SEC_ERROR_UNTRUSTED_CERT = (SEC_ERROR_BASE + 21);
|
||||
let SEC_ERROR_EXPIRED_CERTIFICATE = (SEC_ERROR_BASE + 11);
|
||||
let SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = (SEC_ERROR_BASE + 176);
|
||||
|
||||
let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
|
||||
let SSL_ERROR_BAD_CERT_DOMAIN = (SSL_ERROR_BASE + 12);
|
||||
|
||||
let MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
|
||||
let MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = (MOZILLA_PKIX_ERROR_BASE + 1);
|
||||
let MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = (MOZILLA_PKIX_ERROR_BASE + 3);
|
||||
|
||||
function getErrorClass(errorCode) {
|
||||
let NSPRCode = -1 * NS_ERROR_GET_CODE(errorCode);
|
||||
|
||||
switch (NSPRCode) {
|
||||
case SEC_ERROR_UNKNOWN_ISSUER:
|
||||
case SEC_ERROR_UNTRUSTED_ISSUER:
|
||||
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
|
||||
case SEC_ERROR_UNTRUSTED_CERT:
|
||||
case SSL_ERROR_BAD_CERT_DOMAIN:
|
||||
case SEC_ERROR_EXPIRED_CERTIFICATE:
|
||||
case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
|
||||
case MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY:
|
||||
case MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA:
|
||||
return Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT;
|
||||
default:
|
||||
return Ci.nsINSSErrorsService.ERROR_CLASS_SSL_PROTOCOL;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const OBSERVED_EVENTS = [
|
||||
'fullscreen-origin-change',
|
||||
'ask-parent-to-exit-fullscreen',
|
||||
|
@ -1252,23 +1205,28 @@ BrowserElementChild.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (NS_ERROR_GET_MODULE(status) == NS_ERROR_MODULE_SECURITY &&
|
||||
getErrorClass(status) == Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
|
||||
// getErrorClass() will throw if the error code passed in is not a NSS
|
||||
// error code.
|
||||
try {
|
||||
let nssErrorsService = Cc['@mozilla.org/nss_errors_service;1']
|
||||
.getService(Ci.nsINSSErrorsService);
|
||||
if (nssErrorsService.getErrorClass(status)
|
||||
== Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
|
||||
// XXX Is there a point firing the event if the error page is not
|
||||
// certerror? If yes, maybe we should add a property to the
|
||||
// event to to indicate whether there is a custom page. That would
|
||||
// let the embedder have more control over the desired behavior.
|
||||
let errorPage = null;
|
||||
try {
|
||||
errorPage = Services.prefs.getCharPref(CERTIFICATE_ERROR_PAGE_PREF);
|
||||
} catch (e) {}
|
||||
|
||||
// XXX Is there a point firing the event if the error page is not
|
||||
// certerror? If yes, maybe we should add a property to the
|
||||
// event to to indicate whether there is a custom page. That would
|
||||
// let the embedder have more control over the desired behavior.
|
||||
var errorPage = null;
|
||||
try {
|
||||
errorPage = Services.prefs.getCharPref(CERTIFICATE_ERROR_PAGE_PREF);
|
||||
} catch(e) {}
|
||||
|
||||
if (errorPage == 'certerror') {
|
||||
sendAsyncMsg('error', { type: 'certerror' });
|
||||
return;
|
||||
if (errorPage == 'certerror') {
|
||||
sendAsyncMsg('error', { type: 'certerror' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// TODO See nsDocShell::DisplayLoadError for a list of all the error
|
||||
// codes (the status param) we should eventually handle here.
|
||||
|
|
|
@ -3061,7 +3061,6 @@ public:
|
|||
ClearInputStream()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(mBlobImpl);
|
||||
|
||||
mBlobImpl = nullptr;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ FileImplSnapshot::AssertSanity()
|
|||
|
||||
#endif // DEBUG
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(FileImplSnapshot, FileImpl)
|
||||
NS_IMPL_ISUPPORTS_INHERITED(FileImplSnapshot, FileImpl, PIFileImplSnapshot)
|
||||
|
||||
void
|
||||
FileImplSnapshot::Unlink()
|
||||
|
|
|
@ -11,6 +11,18 @@
|
|||
#include "mozilla/dom/File.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupports.h"
|
||||
|
||||
#define FILEIMPLSNAPSHOT_IID \
|
||||
{0x0dfc11b1, 0x75d3, 0x473b, {0x8c, 0x67, 0xb7, 0x23, 0xf4, 0x67, 0xd6, 0x73}}
|
||||
|
||||
class PIFileImplSnapshot : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(FILEIMPLSNAPSHOT_IID)
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(PIFileImplSnapshot, FILEIMPLSNAPSHOT_IID)
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -23,6 +35,7 @@ class IDBFileHandle;
|
|||
|
||||
class FileImplSnapshot MOZ_FINAL
|
||||
: public FileImplBase
|
||||
, public PIFileImplSnapshot
|
||||
{
|
||||
typedef mozilla::dom::MetadataParameters MetadataParameters;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ support-files =
|
|||
unit/test_advance.js
|
||||
unit/test_autoIncrement.js
|
||||
unit/test_autoIncrement_indexes.js
|
||||
unit/test_blob_file_backed.js
|
||||
unit/test_blocked_order.js
|
||||
unit/test_clear.js
|
||||
unit/test_complex_keyPaths.js
|
||||
|
@ -111,6 +112,9 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
|||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_blob_archive.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_blob_file_backed.html]
|
||||
# This test can only run in the main process.
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
|
||||
[test_blob_simple.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_blob_worker_crash.html]
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>IndexedDB Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7" src="unit/test_blob_file_backed.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
let testGenerator = testSteps();
|
||||
|
||||
function createFileReader() {
|
||||
return SpecialPowers.Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(SpecialPowers.Ci.nsIDOMFileReader);
|
||||
}
|
||||
|
||||
function testSteps()
|
||||
{
|
||||
const fileIOFlags = 0x02 | // PR_WRONLY
|
||||
0x08 | // PR_CREATEFILE
|
||||
0x20; // PR_TRUNCATE
|
||||
const filePerms = 0664;
|
||||
const fileData = "abcdefghijklmnopqrstuvwxyz";
|
||||
const fileType = "text/plain";
|
||||
|
||||
const databaseName =
|
||||
("window" in this) ? window.location.pathname : "Test";
|
||||
const objectStoreName = "foo";
|
||||
const objectStoreKey = "10";
|
||||
|
||||
info("Creating temp file");
|
||||
|
||||
let dirSvc =
|
||||
SpecialPowers.Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIProperties);
|
||||
let testFile = dirSvc.get("ProfD", SpecialPowers.Ci.nsIFile);
|
||||
testFile.createUnique(SpecialPowers.Ci.nsIFile.NORMAL_FILE_TYPE, filePerms);
|
||||
|
||||
info("Writing temp file");
|
||||
|
||||
let outStream =
|
||||
SpecialPowers.Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(SpecialPowers.Ci.nsIFileOutputStream);
|
||||
outStream.init(testFile, fileIOFlags, filePerms, 0);
|
||||
outStream.write(fileData, fileData.length);
|
||||
outStream.close();
|
||||
|
||||
let file = SpecialPowers.createDOMFile(testFile.path, { type: fileType });
|
||||
ok(file instanceof File, "Got a File object");
|
||||
is(file.size, fileData.length, "Correct size");
|
||||
is(file.type, fileType, "Correct type");
|
||||
|
||||
let fileReader = createFileReader();
|
||||
fileReader.onload = grabEventAndContinueHandler;
|
||||
fileReader.readAsText(file);
|
||||
|
||||
let event = yield undefined;
|
||||
|
||||
is(fileReader.result, fileData, "Correct data");
|
||||
|
||||
let request = indexedDB.open(databaseName, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
event = yield undefined;
|
||||
|
||||
let db = event.target.result;
|
||||
let objectStore = db.createObjectStore(objectStoreName);
|
||||
objectStore.put(file, objectStoreKey);
|
||||
|
||||
event = yield undefined;
|
||||
|
||||
db = event.target.result;
|
||||
|
||||
file = null;
|
||||
testFile.remove(false);
|
||||
|
||||
objectStore = db.transaction(objectStoreName).objectStore(objectStoreName);
|
||||
objectStore.get(objectStoreKey).onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
event = yield undefined;
|
||||
|
||||
file = event.target.result;
|
||||
|
||||
ok(file instanceof File, "Got a File object");
|
||||
is(file.size, fileData.length, "Correct size");
|
||||
is(file.type, fileType, "Correct type");
|
||||
|
||||
fileReader = createFileReader();
|
||||
fileReader.onload = grabEventAndContinueHandler;
|
||||
fileReader.readAsText(file);
|
||||
|
||||
event = yield undefined;
|
||||
|
||||
is(fileReader.result, fileData, "Correct data");
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
|
@ -46,7 +46,7 @@ if (!this.runTest) {
|
|||
enableExperimental();
|
||||
}
|
||||
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
Cu.importGlobalProperties(["indexedDB", "Blob", "File"]);
|
||||
|
||||
do_test_pending();
|
||||
testGenerator.next();
|
||||
|
@ -332,5 +332,21 @@ var SpecialPowers = {
|
|||
var prefService =
|
||||
Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService);
|
||||
return prefService.getBranch(null);
|
||||
}
|
||||
},
|
||||
|
||||
get Cc() {
|
||||
return Cc;
|
||||
},
|
||||
|
||||
get Ci() {
|
||||
return Ci;
|
||||
},
|
||||
|
||||
get Cu() {
|
||||
return Cu;
|
||||
},
|
||||
|
||||
createDOMFile: function(file, options) {
|
||||
return new File(file, options);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ support-files =
|
|||
|
||||
[include:xpcshell-shared.ini]
|
||||
|
||||
[test_blob_file_backed.js]
|
||||
[test_bug1056939.js]
|
||||
[test_globalObjects_ipc.js]
|
||||
[test_invalidate.js]
|
||||
|
|
577
dom/ipc/Blob.cpp
577
dom/ipc/Blob.cpp
|
@ -21,6 +21,7 @@
|
|||
#include "mozilla/dom/nsIContentChild.h"
|
||||
#include "mozilla/dom/PBlobStreamChild.h"
|
||||
#include "mozilla/dom/PBlobStreamParent.h"
|
||||
#include "mozilla/dom/indexedDB/FileSnapshot.h"
|
||||
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
|
@ -837,13 +838,13 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata MOZ_FINAL
|
|||
uint64_t mLength;
|
||||
uint64_t mLastModifiedDate;
|
||||
bool mHasRecursed;
|
||||
bool mIsSameProcessActor;
|
||||
const bool mIsSameProcessActor;
|
||||
|
||||
CreateBlobImplMetadata()
|
||||
CreateBlobImplMetadata(bool aIsSameProcessActor)
|
||||
: mLength(0)
|
||||
, mLastModifiedDate(0)
|
||||
, mHasRecursed(false)
|
||||
, mIsSameProcessActor(false)
|
||||
, mIsSameProcessActor(aIsSameProcessActor)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CreateBlobImplMetadata);
|
||||
|
||||
|
@ -863,10 +864,30 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata MOZ_FINAL
|
|||
};
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImplFromParams(const StringInputStreamParams& aParams,
|
||||
const CreateBlobImplMetadata& aMetadata)
|
||||
CreateBlobImpl(const nsID& aKnownBlobIDData,
|
||||
const CreateBlobImplMetadata& aMetadata)
|
||||
{
|
||||
static_assert(sizeof(aParams.data().Length()) <= sizeof(size_t),
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
MOZ_ASSERT(aMetadata.mHasRecursed);
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl = BlobParent::GetBlobImplForID(aKnownBlobIDData);
|
||||
if (NS_WARN_IF(!blobImpl)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImpl(const nsTArray<uint8_t>& aMemoryData,
|
||||
const CreateBlobImplMetadata& aMetadata)
|
||||
{
|
||||
static_assert(sizeof(aMemoryData.Length()) <= sizeof(size_t),
|
||||
"String length won't fit in size_t!");
|
||||
static_assert(sizeof(size_t) <= sizeof(uint64_t),
|
||||
"size_t won't fit in uint64_t!");
|
||||
|
@ -875,19 +896,22 @@ CreateBlobImplFromParams(const StringInputStreamParams& aParams,
|
|||
|
||||
nsRefPtr<FileImpl> blobImpl;
|
||||
|
||||
if (auto length = static_cast<size_t>(aParams.data().Length())) {
|
||||
if (auto length = static_cast<size_t>(aMemoryData.Length())) {
|
||||
static MOZ_CONSTEXPR_VAR size_t elementSizeMultiplier =
|
||||
sizeof(aMemoryData[0]) / sizeof(char);
|
||||
|
||||
if (!aMetadata.mHasRecursed &&
|
||||
NS_WARN_IF(aMetadata.mLength != uint64_t(length))) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* buffer = moz_malloc(aParams.data().Length());
|
||||
void* buffer = moz_malloc(length * elementSizeMultiplier);
|
||||
if (NS_WARN_IF(!buffer)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(buffer, aParams.data().get(), length);
|
||||
memcpy(buffer, aMemoryData.Elements(), length * elementSizeMultiplier);
|
||||
|
||||
if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
|
||||
blobImpl =
|
||||
|
@ -915,36 +939,16 @@ CreateBlobImplFromParams(const StringInputStreamParams& aParams,
|
|||
}
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImplFromParams(const RemoteInputStreamParams& aParams,
|
||||
const CreateBlobImplMetadata& aMetadata)
|
||||
{
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
MOZ_ASSERT(aMetadata.mHasRecursed);
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl = BlobParent::GetBlobImplForID(aParams.id());
|
||||
if (NS_WARN_IF(!blobImpl)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImplFromParams(const SameProcessInputStreamParams& aParams,
|
||||
const CreateBlobImplMetadata& aMetadata)
|
||||
CreateBlobImpl(intptr_t aAddRefedInputStream,
|
||||
const CreateBlobImplMetadata& aMetadata)
|
||||
{
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
MOZ_ASSERT(aMetadata.mIsSameProcessActor);
|
||||
MOZ_ASSERT(aParams.addRefedInputStream());
|
||||
MOZ_ASSERT(aAddRefedInputStream);
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream =
|
||||
dont_AddRef(
|
||||
reinterpret_cast<nsIInputStream*>(aParams.addRefedInputStream()));
|
||||
reinterpret_cast<nsIInputStream*>(aAddRefedInputStream));
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl;
|
||||
if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
|
||||
|
@ -969,73 +973,35 @@ CreateBlobImplFromParams(const SameProcessInputStreamParams& aParams,
|
|||
}
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams,
|
||||
CreateBlobImplMetadata& aMetadata);
|
||||
CreateBlobImpl(const nsTArray<BlobData>& aBlobData,
|
||||
CreateBlobImplMetadata& aMetadata);
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImplFromInputStreamParams(const InputStreamParams& aParams,
|
||||
CreateBlobImplMetadata& aMetadata)
|
||||
CreateBlobImplFromBlobData(const BlobData& aBlobData,
|
||||
CreateBlobImplMetadata& aMetadata)
|
||||
{
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl;
|
||||
|
||||
switch (aParams.type()) {
|
||||
case InputStreamParams::TStringInputStreamParams: {
|
||||
const StringInputStreamParams& params =
|
||||
aParams.get_StringInputStreamParams();
|
||||
blobImpl = CreateBlobImplFromParams(params, aMetadata);
|
||||
switch (aBlobData.type()) {
|
||||
case BlobData::TnsID: {
|
||||
blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata);
|
||||
break;
|
||||
}
|
||||
|
||||
case InputStreamParams::TFileInputStreamParams: {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
case InputStreamParams::TPartialFileInputStreamParams: {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
case InputStreamParams::TBufferedInputStreamParams: {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
case InputStreamParams::TMIMEInputStreamParams: {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
case InputStreamParams::TMultiplexInputStreamParams: {
|
||||
const MultiplexInputStreamParams& params =
|
||||
aParams.get_MultiplexInputStreamParams();
|
||||
blobImpl = CreateBlobImplFromParams(params, aMetadata);
|
||||
case BlobData::TArrayOfuint8_t: {
|
||||
blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfuint8_t(), aMetadata);
|
||||
break;
|
||||
}
|
||||
|
||||
case InputStreamParams::TRemoteInputStreamParams: {
|
||||
if (NS_WARN_IF(!aMetadata.mHasRecursed)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RemoteInputStreamParams& params =
|
||||
aParams.get_RemoteInputStreamParams();
|
||||
blobImpl = CreateBlobImplFromParams(params, aMetadata);
|
||||
case BlobData::Tintptr_t: {
|
||||
blobImpl = CreateBlobImpl(aBlobData.get_intptr_t(), aMetadata);
|
||||
break;
|
||||
}
|
||||
|
||||
case InputStreamParams::TSameProcessInputStreamParams: {
|
||||
if (NS_WARN_IF(!aMetadata.mIsSameProcessActor)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SameProcessInputStreamParams& params =
|
||||
aParams.get_SameProcessInputStreamParams();
|
||||
blobImpl = CreateBlobImplFromParams(params, aMetadata);
|
||||
case BlobData::TArrayOfBlobData: {
|
||||
blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1047,34 +1013,17 @@ CreateBlobImplFromInputStreamParams(const InputStreamParams& aParams,
|
|||
}
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams,
|
||||
CreateBlobImplMetadata& aMetadata)
|
||||
CreateBlobImpl(const nsTArray<BlobData>& aBlobDatas,
|
||||
CreateBlobImplMetadata& aMetadata)
|
||||
{
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
if (NS_WARN_IF(aParams.currentStream())) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(aParams.status()))) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aParams.startedReadingCurrent())) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const nsTArray<InputStreamParams>& streams = aParams.streams();
|
||||
|
||||
// Special case for a multipart blob with only one part.
|
||||
if (streams.Length() == 1) {
|
||||
const InputStreamParams& params = streams[0];
|
||||
if (aBlobDatas.Length() == 1) {
|
||||
const BlobData& blobData = aBlobDatas[0];
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl =
|
||||
CreateBlobImplFromInputStreamParams(params, aMetadata);
|
||||
CreateBlobImplFromBlobData(blobData, aMetadata);
|
||||
if (NS_WARN_IF(!blobImpl)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1087,7 +1036,7 @@ CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams,
|
|||
}
|
||||
|
||||
FallibleTArray<nsRefPtr<FileImpl>> fallibleBlobImpls;
|
||||
if (NS_WARN_IF(!fallibleBlobImpls.SetLength(streams.Length()))) {
|
||||
if (NS_WARN_IF(!fallibleBlobImpls.SetLength(aBlobDatas.Length()))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1097,11 +1046,13 @@ CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams,
|
|||
const bool hasRecursed = aMetadata.mHasRecursed;
|
||||
aMetadata.mHasRecursed = true;
|
||||
|
||||
for (uint32_t count = streams.Length(), index = 0; index < count; index++) {
|
||||
const InputStreamParams& params = streams[index];
|
||||
for (uint32_t count = aBlobDatas.Length(), index = 0;
|
||||
index < count;
|
||||
index++) {
|
||||
const BlobData& blobData = aBlobDatas[index];
|
||||
nsRefPtr<FileImpl>& blobImpl = blobImpls[index];
|
||||
|
||||
blobImpl = CreateBlobImplFromParams(params, aMetadata);
|
||||
blobImpl = CreateBlobImplFromBlobData(blobData, aMetadata);
|
||||
if (NS_WARN_IF(!blobImpl)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1126,18 +1077,17 @@ CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams,
|
|||
}
|
||||
|
||||
already_AddRefed<FileImpl>
|
||||
CreateBlobImplFromParams(const ParentBlobConstructorParams& aParams,
|
||||
bool aIsSameProcessActor)
|
||||
CreateBlobImpl(const ParentBlobConstructorParams& aParams,
|
||||
const BlobData& aBlobData,
|
||||
bool aIsSameProcessActor)
|
||||
{
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
MOZ_ASSERT(aParams.blobParams().type() ==
|
||||
AnyBlobConstructorParams::TNormalBlobConstructorParams ||
|
||||
aParams.blobParams().type() ==
|
||||
AnyBlobConstructorParams::TFileBlobConstructorParams);
|
||||
MOZ_ASSERT(aParams.optionalInputStreamParams().type() ==
|
||||
OptionalInputStreamParams::TInputStreamParams);
|
||||
|
||||
CreateBlobImplMetadata metadata;
|
||||
CreateBlobImplMetadata metadata(aIsSameProcessActor);
|
||||
|
||||
if (aParams.blobParams().type() ==
|
||||
AnyBlobConstructorParams::TNormalBlobConstructorParams) {
|
||||
|
@ -1171,16 +1121,71 @@ CreateBlobImplFromParams(const ParentBlobConstructorParams& aParams,
|
|||
metadata.mLastModifiedDate = params.modDate();
|
||||
}
|
||||
|
||||
metadata.mIsSameProcessActor = aIsSameProcessActor;
|
||||
|
||||
const InputStreamParams& inputStreamParams =
|
||||
aParams.optionalInputStreamParams().get_InputStreamParams();
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl =
|
||||
CreateBlobImplFromInputStreamParams(inputStreamParams, metadata);
|
||||
CreateBlobImplFromBlobData(aBlobData, metadata);
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
void
|
||||
BlobDataFromBlobImpl(FileImpl* aBlobImpl, BlobData& aBlobData)
|
||||
{
|
||||
MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
|
||||
MOZ_ASSERT(aBlobImpl);
|
||||
|
||||
const nsTArray<nsRefPtr<FileImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
|
||||
|
||||
if (subBlobs) {
|
||||
aBlobData = nsTArray<BlobData>();
|
||||
|
||||
nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
|
||||
subBlobDatas.SetLength(subBlobs->Length());
|
||||
|
||||
for (uint32_t count = subBlobs->Length(), index = 0;
|
||||
index < count;
|
||||
index++) {
|
||||
BlobDataFromBlobImpl(subBlobs->ElementAt(index), subBlobDatas[index]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl);
|
||||
if (remoteBlob) {
|
||||
BlobChild* actor = remoteBlob->GetBlobChild();
|
||||
MOZ_ASSERT(actor);
|
||||
|
||||
aBlobData = actor->ParentID();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aBlobImpl->IsMemoryFile());
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
aBlobImpl->GetInternalStream(getter_AddRefs(inputStream))));
|
||||
|
||||
DebugOnly<bool> isNonBlocking;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(inputStream->IsNonBlocking(&isNonBlocking)));
|
||||
MOZ_ASSERT(isNonBlocking);
|
||||
|
||||
uint64_t available;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(inputStream->Available(&available)));
|
||||
|
||||
MOZ_ASSERT(available <= uint64_t(UINT32_MAX));
|
||||
|
||||
aBlobData = nsTArray<uint8_t>();
|
||||
|
||||
nsTArray<uint8_t>& blobData = aBlobData.get_ArrayOfuint8_t();
|
||||
|
||||
blobData.SetLength(size_t(available));
|
||||
|
||||
uint32_t readCount;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
inputStream->Read(reinterpret_cast<char*>(blobData.Elements()),
|
||||
uint32_t(available),
|
||||
&readCount)));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable;
|
||||
|
@ -1548,6 +1553,9 @@ protected:
|
|||
|
||||
BlobChild* mActor;
|
||||
nsCOMPtr<nsIEventTarget> mActorTarget;
|
||||
|
||||
nsRefPtr<FileImpl> mSameProcessFileImpl;
|
||||
|
||||
const bool mIsSlice;
|
||||
|
||||
public:
|
||||
|
@ -1563,6 +1571,20 @@ public:
|
|||
const nsAString& aContentType,
|
||||
uint64_t aLength);
|
||||
|
||||
// For same-process blobs.
|
||||
RemoteBlobImpl(BlobChild* aActor,
|
||||
FileImpl* aSameProcessBlobImpl,
|
||||
const nsAString& aName,
|
||||
const nsAString& aContentType,
|
||||
uint64_t aLength,
|
||||
uint64_t aModDate);
|
||||
|
||||
// For same-process blobs.
|
||||
RemoteBlobImpl(BlobChild* aActor,
|
||||
FileImpl* aSameProcessBlobImpl,
|
||||
const nsAString& aContentType,
|
||||
uint64_t aLength);
|
||||
|
||||
// For mystery blobs.
|
||||
explicit
|
||||
RemoteBlobImpl(BlobChild* aActor);
|
||||
|
@ -1853,6 +1875,38 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
|
|||
CommonInit(aActor);
|
||||
}
|
||||
|
||||
BlobChild::
|
||||
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
|
||||
FileImpl* aSameProcessBlobImpl,
|
||||
const nsAString& aName,
|
||||
const nsAString& aContentType,
|
||||
uint64_t aLength,
|
||||
uint64_t aModDate)
|
||||
: FileImplBase(aName, aContentType, aLength, aModDate)
|
||||
, mSameProcessFileImpl(aSameProcessBlobImpl)
|
||||
, mIsSlice(false)
|
||||
{
|
||||
MOZ_ASSERT(aSameProcessBlobImpl);
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
CommonInit(aActor);
|
||||
}
|
||||
|
||||
BlobChild::
|
||||
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
|
||||
FileImpl* aSameProcessBlobImpl,
|
||||
const nsAString& aContentType,
|
||||
uint64_t aLength)
|
||||
: FileImplBase(aContentType, aLength)
|
||||
, mSameProcessFileImpl(aSameProcessBlobImpl)
|
||||
, mIsSlice(false)
|
||||
{
|
||||
MOZ_ASSERT(aSameProcessBlobImpl);
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
CommonInit(aActor);
|
||||
}
|
||||
|
||||
BlobChild::
|
||||
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
|
||||
: FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
|
||||
|
@ -1954,6 +2008,13 @@ RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFilePath,
|
|||
MOZ_CRASH("Not implemented!");
|
||||
}
|
||||
|
||||
if (mSameProcessFileImpl) {
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
mSameProcessFileImpl->GetMozFullPathInternal(aFilePath, aRv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mActor) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
|
@ -1976,9 +2037,18 @@ RemoteBlobImpl::CreateSlice(uint64_t aStart,
|
|||
ErrorResult& aRv)
|
||||
{
|
||||
// May be called on any thread.
|
||||
nsRefPtr<RemoteBlobSliceImpl> slice =
|
||||
new RemoteBlobSliceImpl(this, aStart, aLength, aContentType);
|
||||
return slice.forget();
|
||||
if (mSameProcessFileImpl) {
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
return mSameProcessFileImpl->CreateSlice(aStart,
|
||||
aLength,
|
||||
aContentType,
|
||||
aRv);
|
||||
}
|
||||
|
||||
nsRefPtr<RemoteBlobSliceImpl> slice =
|
||||
new RemoteBlobSliceImpl(this, aStart, aLength, aContentType);
|
||||
return slice.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1986,6 +2056,22 @@ BlobChild::
|
|||
RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream)
|
||||
{
|
||||
// May be called on any thread.
|
||||
if (mSameProcessFileImpl) {
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
nsCOMPtr<nsIInputStream> realStream;
|
||||
nsresult rv =
|
||||
mSameProcessFileImpl->GetInternalStream(getter_AddRefs(realStream));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsRefPtr<BlobInputStreamTether> tether =
|
||||
new BlobInputStreamTether(realStream, mSameProcessFileImpl);
|
||||
tether.forget(aStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CreateStreamHelper> helper = new CreateStreamHelper(this);
|
||||
return helper->GetStream(aStream);
|
||||
}
|
||||
|
@ -1998,6 +2084,12 @@ RemoteBlobImpl::GetFileId()
|
|||
MOZ_CRASH("Not implemented!");
|
||||
}
|
||||
|
||||
if (mSameProcessFileImpl) {
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
return mSameProcessFileImpl->GetFileId();
|
||||
}
|
||||
|
||||
int64_t fileId;
|
||||
if (mActor && mActor->SendGetFileId(&fileId)) {
|
||||
return fileId;
|
||||
|
@ -2209,8 +2301,7 @@ RemoteBlobSliceImpl::GetBlobChild()
|
|||
id /* id */,
|
||||
mStart /* begin */,
|
||||
mStart + mLength /* end */,
|
||||
mContentType /* contentType */),
|
||||
void_t() /* optionalInputStream */);
|
||||
mContentType /* contentType */));
|
||||
|
||||
if (nsIContentChild* contentManager = baseActor->GetContentManager()) {
|
||||
mActor = SendSliceConstructor(contentManager, this, params);
|
||||
|
@ -2648,6 +2739,8 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
|
|||
|
||||
AnyBlobConstructorParams::Type paramsType = blobParams.type();
|
||||
MOZ_ASSERT(paramsType != AnyBlobConstructorParams::T__None &&
|
||||
paramsType !=
|
||||
AnyBlobConstructorParams::TSlicedBlobConstructorParams &&
|
||||
paramsType !=
|
||||
AnyBlobConstructorParams::TKnownBlobConstructorParams);
|
||||
|
||||
|
@ -2673,6 +2766,44 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
|
|||
break;
|
||||
}
|
||||
|
||||
case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
|
||||
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
|
||||
|
||||
const SameProcessBlobConstructorParams& params =
|
||||
blobParams.get_SameProcessBlobConstructorParams();
|
||||
MOZ_ASSERT(params.addRefedFileImpl());
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl =
|
||||
dont_AddRef(reinterpret_cast<FileImpl*>(params.addRefedFileImpl()));
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t size = blobImpl->GetSize(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
nsString contentType;
|
||||
blobImpl->GetType(contentType);
|
||||
|
||||
if (blobImpl->IsFile()) {
|
||||
nsString name;
|
||||
blobImpl->GetName(name);
|
||||
|
||||
uint64_t lastModifiedDate = blobImpl->GetLastModified(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
remoteBlob =
|
||||
new RemoteBlobImpl(this,
|
||||
blobImpl,
|
||||
name,
|
||||
contentType,
|
||||
size,
|
||||
lastModifiedDate);
|
||||
} else {
|
||||
remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
|
||||
remoteBlob = new RemoteBlobImpl(this);
|
||||
break;
|
||||
|
@ -2798,7 +2929,7 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
|
|||
}
|
||||
}
|
||||
|
||||
// All blobs shared between processes must be immutable.
|
||||
// All blobs shared between threads or processes must be immutable.
|
||||
if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2808,40 +2939,55 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
|
|||
|
||||
AnyBlobConstructorParams blobParams;
|
||||
|
||||
nsString contentType;
|
||||
aBlobImpl->GetType(contentType);
|
||||
nsCOMPtr<nsIInputStream> snapshotInputStream;
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t length = aBlobImpl->GetSize(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
aBlobImpl->GetInternalStream(getter_AddRefs(stream))));
|
||||
|
||||
if (aBlobImpl->IsFile()) {
|
||||
nsString name;
|
||||
aBlobImpl->GetName(name);
|
||||
|
||||
uint64_t modDate = aBlobImpl->GetLastModified(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
blobParams = FileBlobConstructorParams(name, contentType, length, modDate);
|
||||
} else {
|
||||
blobParams = NormalBlobConstructorParams(contentType, length);
|
||||
if (gProcessType == GeckoProcessType_Default) {
|
||||
nsCOMPtr<PIFileImplSnapshot> snapshot = do_QueryInterface(aBlobImpl);
|
||||
if (snapshot) {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
aBlobImpl->GetInternalStream(getter_AddRefs(snapshotInputStream))));
|
||||
}
|
||||
}
|
||||
|
||||
InputStreamParams inputStreamParams;
|
||||
if (gProcessType == GeckoProcessType_Default && !snapshotInputStream) {
|
||||
nsRefPtr<FileImpl> sameProcessImpl = aBlobImpl;
|
||||
auto addRefedFileImpl =
|
||||
reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
|
||||
|
||||
nsTArray<FileDescriptor> fds;
|
||||
SerializeInputStream(stream, inputStreamParams, fds);
|
||||
blobParams = SameProcessBlobConstructorParams(addRefedFileImpl);
|
||||
} else {
|
||||
BlobData blobData;
|
||||
if (snapshotInputStream) {
|
||||
blobData =
|
||||
reinterpret_cast<intptr_t>(snapshotInputStream.forget().take());
|
||||
} else {
|
||||
BlobDataFromBlobImpl(aBlobImpl, blobData);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(inputStreamParams.type() != InputStreamParams::T__None);
|
||||
MOZ_ASSERT(fds.IsEmpty());
|
||||
nsString contentType;
|
||||
aBlobImpl->GetType(contentType);
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t length = aBlobImpl->GetSize(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
if (aBlobImpl->IsFile()) {
|
||||
nsString name;
|
||||
aBlobImpl->GetName(name);
|
||||
|
||||
uint64_t modDate = aBlobImpl->GetLastModified(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
blobParams =
|
||||
FileBlobConstructorParams(name, contentType, length, modDate, blobData);
|
||||
} else {
|
||||
blobParams = NormalBlobConstructorParams(contentType, length, blobData);
|
||||
}
|
||||
}
|
||||
|
||||
BlobChild* actor = new BlobChild(aManager, aBlobImpl);
|
||||
|
||||
ParentBlobConstructorParams params(blobParams, inputStreamParams);
|
||||
ParentBlobConstructorParams params(blobParams);
|
||||
|
||||
if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
|
||||
BlobChild::Destroy(actor);
|
||||
|
@ -2865,6 +3011,7 @@ BlobChild::CreateFromParams(ChildManagerType* aManager,
|
|||
switch (blobParams.type()) {
|
||||
case AnyBlobConstructorParams::TNormalBlobConstructorParams:
|
||||
case AnyBlobConstructorParams::TFileBlobConstructorParams:
|
||||
case AnyBlobConstructorParams::TSameProcessBlobConstructorParams:
|
||||
case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
|
||||
return new BlobChild(aManager, aParams);
|
||||
}
|
||||
|
@ -2896,8 +3043,6 @@ BlobChild::SendSliceConstructor(ChildManagerType* aManager,
|
|||
MOZ_ASSERT(aRemoteBlobSliceImpl);
|
||||
MOZ_ASSERT(aParams.blobParams().type() ==
|
||||
AnyBlobConstructorParams::TSlicedBlobConstructorParams);
|
||||
MOZ_ASSERT(aParams.optionalInputStreamParams().type() ==
|
||||
OptionalInputStreamParams::Tvoid_t);
|
||||
|
||||
const nsID& id = aParams.blobParams().get_SlicedBlobConstructorParams().id();
|
||||
|
||||
|
@ -2933,8 +3078,7 @@ BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
|
|||
actor = new BlobChild(aManager, actor);
|
||||
|
||||
ParentBlobConstructorParams params(
|
||||
KnownBlobConstructorParams(actor->ParentID()) /* blobParams */,
|
||||
void_t() /* optionalInputStream */);
|
||||
KnownBlobConstructorParams(actor->ParentID()));
|
||||
|
||||
aManager->SendPBlobConstructor(actor, params);
|
||||
|
||||
|
@ -2963,8 +3107,7 @@ BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
|
|||
actor = new BlobChild(aManager, actor);
|
||||
|
||||
ParentBlobConstructorParams params(
|
||||
KnownBlobConstructorParams(actor->ParentID()) /* blobParams */,
|
||||
void_t() /* optionalInputStream */);
|
||||
KnownBlobConstructorParams(actor->ParentID()));
|
||||
|
||||
aManager->SendPBlobConstructor(actor, params);
|
||||
|
||||
|
@ -3019,8 +3162,11 @@ BlobChild::SetMysteryBlobInfo(const nsString& aName,
|
|||
|
||||
mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
|
||||
|
||||
FileBlobConstructorParams params(aName, aContentType, aLength,
|
||||
aLastModifiedDate);
|
||||
FileBlobConstructorParams params(aName,
|
||||
aContentType,
|
||||
aLength,
|
||||
aLastModifiedDate,
|
||||
void_t() /* optionalBlobData */);
|
||||
return SendResolveMystery(params);
|
||||
}
|
||||
|
||||
|
@ -3036,7 +3182,9 @@ BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
|
|||
|
||||
mBlobImpl->SetLazyData(voidString, aContentType, aLength, UINT64_MAX);
|
||||
|
||||
NormalBlobConstructorParams params(aContentType, aLength);
|
||||
NormalBlobConstructorParams params(aContentType,
|
||||
aLength,
|
||||
void_t() /* optionalBlobData */);
|
||||
return SendResolveMystery(params);
|
||||
}
|
||||
|
||||
|
@ -3326,37 +3474,56 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
|
|||
}
|
||||
}
|
||||
|
||||
// All blobs shared between processes must be immutable.
|
||||
// All blobs shared between threads or processes must be immutable.
|
||||
if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bool isSameProcessActor = ActorManagerIsSameProcess(aManager);
|
||||
|
||||
AnyBlobConstructorParams blobParams;
|
||||
|
||||
if (aBlobImpl->IsSizeUnknown() || aBlobImpl->IsDateUnknown()) {
|
||||
// We don't want to call GetSize or GetLastModifiedDate yet since that may
|
||||
// stat a file on the this thread. Instead we'll learn the size lazily from
|
||||
// the other side.
|
||||
blobParams = MysteryBlobConstructorParams();
|
||||
bool isSnapshot;
|
||||
|
||||
if (isSameProcessActor) {
|
||||
nsCOMPtr<PIFileImplSnapshot> snapshot = do_QueryInterface(aBlobImpl);
|
||||
isSnapshot = !!snapshot;
|
||||
} else {
|
||||
nsString contentType;
|
||||
aBlobImpl->GetType(contentType);
|
||||
isSnapshot = false;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t length = aBlobImpl->GetSize(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
if (isSameProcessActor && !isSnapshot) {
|
||||
nsRefPtr<FileImpl> sameProcessImpl = aBlobImpl;
|
||||
auto addRefedFileImpl =
|
||||
reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
|
||||
|
||||
if (aBlobImpl->IsFile()) {
|
||||
nsString name;
|
||||
aBlobImpl->GetName(name);
|
||||
blobParams = SameProcessBlobConstructorParams(addRefedFileImpl);
|
||||
} else {
|
||||
if (aBlobImpl->IsSizeUnknown() || aBlobImpl->IsDateUnknown()) {
|
||||
// We don't want to call GetSize or GetLastModifiedDate yet since that may
|
||||
// stat a file on the this thread. Instead we'll learn the size lazily
|
||||
// from the other side.
|
||||
blobParams = MysteryBlobConstructorParams();
|
||||
} else {
|
||||
nsString contentType;
|
||||
aBlobImpl->GetType(contentType);
|
||||
|
||||
uint64_t modDate = aBlobImpl->GetLastModified(rv);
|
||||
ErrorResult rv;
|
||||
uint64_t length = aBlobImpl->GetSize(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
blobParams =
|
||||
FileBlobConstructorParams(name, contentType, length, modDate);
|
||||
} else {
|
||||
blobParams = NormalBlobConstructorParams(contentType, length);
|
||||
if (aBlobImpl->IsFile()) {
|
||||
nsString name;
|
||||
aBlobImpl->GetName(name);
|
||||
|
||||
uint64_t modDate = aBlobImpl->GetLastModified(rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
blobParams =
|
||||
FileBlobConstructorParams(name, contentType, length, modDate, void_t());
|
||||
} else {
|
||||
blobParams = NormalBlobConstructorParams(contentType, length, void_t());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3397,14 +3564,21 @@ BlobParent::CreateFromParams(ParentManagerType* aManager,
|
|||
|
||||
case AnyBlobConstructorParams::TNormalBlobConstructorParams:
|
||||
case AnyBlobConstructorParams::TFileBlobConstructorParams: {
|
||||
if (aParams.optionalInputStreamParams().type() !=
|
||||
OptionalInputStreamParams::TInputStreamParams) {
|
||||
const OptionalBlobData& optionalBlobData =
|
||||
blobParams.type() ==
|
||||
AnyBlobConstructorParams::TNormalBlobConstructorParams ?
|
||||
blobParams.get_NormalBlobConstructorParams().optionalBlobData() :
|
||||
blobParams.get_FileBlobConstructorParams().optionalBlobData();
|
||||
|
||||
if (NS_WARN_IF(optionalBlobData.type() != OptionalBlobData::TBlobData)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl =
|
||||
CreateBlobImplFromParams(aParams, ActorManagerIsSameProcess(aManager));
|
||||
CreateBlobImpl(aParams,
|
||||
optionalBlobData.get_BlobData(),
|
||||
ActorManagerIsSameProcess(aManager));
|
||||
if (NS_WARN_IF(!blobImpl)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
|
@ -3424,12 +3598,6 @@ BlobParent::CreateFromParams(ParentManagerType* aManager,
|
|||
}
|
||||
|
||||
case AnyBlobConstructorParams::TSlicedBlobConstructorParams: {
|
||||
if (aParams.optionalInputStreamParams().type() !=
|
||||
OptionalInputStreamParams::Tvoid_t) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SlicedBlobConstructorParams& params =
|
||||
blobParams.get_SlicedBlobConstructorParams();
|
||||
|
||||
|
@ -3472,12 +3640,6 @@ BlobParent::CreateFromParams(ParentManagerType* aManager,
|
|||
}
|
||||
|
||||
case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
|
||||
if (aParams.optionalInputStreamParams().type() !=
|
||||
OptionalInputStreamParams::Tvoid_t) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KnownBlobConstructorParams& params =
|
||||
blobParams.get_KnownBlobConstructorParams();
|
||||
|
||||
|
@ -3494,6 +3656,29 @@ BlobParent::CreateFromParams(ParentManagerType* aManager,
|
|||
return new BlobParent(aManager, blobImpl, idTableEntry);
|
||||
}
|
||||
|
||||
case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
|
||||
if (NS_WARN_IF(!ActorManagerIsSameProcess(aManager))) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SameProcessBlobConstructorParams& params =
|
||||
blobParams.get_SameProcessBlobConstructorParams();
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl =
|
||||
dont_AddRef(reinterpret_cast<FileImpl*>(params.addRefedFileImpl()));
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
nsID id;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
|
||||
|
||||
nsRefPtr<IDTableEntry> idTableEntry =
|
||||
IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl);
|
||||
MOZ_ASSERT(idTableEntry);
|
||||
|
||||
return new BlobParent(aManager, blobImpl, idTableEntry);
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unknown params!");
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PBlob;
|
||||
include InputStreamParams;
|
||||
|
||||
using struct mozilla::void_t
|
||||
from "ipc/IPCMessageUtils.h";
|
||||
|
@ -13,6 +12,9 @@ using struct mozilla::void_t
|
|||
using struct mozilla::SerializedStructuredCloneBuffer
|
||||
from "ipc/IPCMessageUtils.h";
|
||||
|
||||
using struct nsID
|
||||
from "nsID.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -28,10 +30,35 @@ struct ClonedMessageData
|
|||
PBlob[] blobs;
|
||||
};
|
||||
|
||||
union BlobData
|
||||
{
|
||||
// For remote blobs.
|
||||
nsID;
|
||||
|
||||
// For memory-backed blobs.
|
||||
uint8_t[];
|
||||
|
||||
// For file snapshots, this is an nsIInputStream.
|
||||
intptr_t;
|
||||
|
||||
// For multiplex blobs.
|
||||
BlobData[];
|
||||
};
|
||||
|
||||
union OptionalBlobData
|
||||
{
|
||||
BlobData;
|
||||
void_t;
|
||||
};
|
||||
|
||||
struct NormalBlobConstructorParams
|
||||
{
|
||||
nsString contentType;
|
||||
uint64_t length;
|
||||
|
||||
// This must be of type BlobData in a child->parent message, and will always
|
||||
// be of type void_t in a parent->child message.
|
||||
OptionalBlobData optionalBlobData;
|
||||
};
|
||||
|
||||
struct FileBlobConstructorParams
|
||||
|
@ -40,6 +67,10 @@ struct FileBlobConstructorParams
|
|||
nsString contentType;
|
||||
uint64_t length;
|
||||
uint64_t modDate;
|
||||
|
||||
// This must be of type BlobData in a child->parent message, and will always
|
||||
// be of type void_t in a parent->child message.
|
||||
OptionalBlobData optionalBlobData;
|
||||
};
|
||||
|
||||
struct SlicedBlobConstructorParams
|
||||
|
@ -61,11 +92,20 @@ struct KnownBlobConstructorParams
|
|||
nsID id;
|
||||
};
|
||||
|
||||
// This may only be used for same-process inter-thread communication!
|
||||
struct SameProcessBlobConstructorParams
|
||||
{
|
||||
// This member should be reinterpret_cast'd to mozilla::dom::FileImpl. It
|
||||
// carries a reference.
|
||||
intptr_t addRefedFileImpl;
|
||||
};
|
||||
|
||||
union AnyBlobConstructorParams
|
||||
{
|
||||
// These types may be sent to/from parent and child.
|
||||
NormalBlobConstructorParams;
|
||||
FileBlobConstructorParams;
|
||||
SameProcessBlobConstructorParams;
|
||||
|
||||
// This type may only be sent from parent to child.
|
||||
MysteryBlobConstructorParams;
|
||||
|
@ -87,14 +127,6 @@ struct ParentBlobConstructorParams
|
|||
{
|
||||
// May not be MysteryBlobConstructorParams.
|
||||
AnyBlobConstructorParams blobParams;
|
||||
|
||||
// This must be of type void_t for:
|
||||
// - SlicedBlobConstructorParams
|
||||
// - KnownBlobConstructorParams
|
||||
// This must be of type InputStreamParams for:
|
||||
// - NormalBlobConstructorParams
|
||||
// - FileBlobConstructorParams
|
||||
OptionalInputStreamParams optionalInputStreamParams;
|
||||
};
|
||||
|
||||
union BlobConstructorParams
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Name: fake
|
||||
Description: Fake GMP Plugin
|
||||
Version: 1.0
|
||||
APIs: encode-video[h264], decode-video[h264]
|
||||
APIs: encode-video[h264], decode-video[h264], eme-decrypt[fake]
|
||||
|
|
|
@ -49,6 +49,10 @@
|
|||
#include "gmp-video-decode.h"
|
||||
#include "gmp-video-frame-i420.h"
|
||||
#include "gmp-video-frame-encoded.h"
|
||||
#include "gmp-decryption.h"
|
||||
|
||||
#include "gmp-test-decryptor.h"
|
||||
#include "gmp-test-storage.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define PUBLIC_FUNC __declspec(dllexport)
|
||||
|
@ -81,7 +85,7 @@ const char* kLogStrings[] = {
|
|||
};
|
||||
|
||||
|
||||
static GMPPlatformAPI* g_platform_api = NULL;
|
||||
GMPPlatformAPI* g_platform_api = NULL;
|
||||
|
||||
class FakeVideoEncoder;
|
||||
class FakeVideoDecoder;
|
||||
|
@ -108,7 +112,7 @@ class FakeEncoderTask : public GMPTask {
|
|||
: encoder_(encoder), frame_(frame), type_(type) {}
|
||||
|
||||
virtual void Run();
|
||||
virtual void Destroy() {}
|
||||
virtual void Destroy() { delete this; }
|
||||
|
||||
FakeVideoEncoder* encoder_;
|
||||
GMPVideoi420Frame* frame_;
|
||||
|
@ -262,7 +266,7 @@ class FakeDecoderTask : public GMPTask {
|
|||
: decoder_(decoder), frame_(frame), time_(time) {}
|
||||
|
||||
virtual void Run();
|
||||
virtual void Destroy() {}
|
||||
virtual void Destroy() { delete this; }
|
||||
|
||||
FakeVideoDecoder* decoder_;
|
||||
GMPVideoEncodedFrame* frame_;
|
||||
|
@ -397,6 +401,9 @@ extern "C" {
|
|||
} else if (!strcmp (aApiName, "encode-video")) {
|
||||
*aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
|
||||
return GMPNoErr;
|
||||
} else if (!strcmp (aApiName, "eme-decrypt")) {
|
||||
*aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
|
||||
return GMPNoErr;
|
||||
}
|
||||
return GMPGenericErr;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gmp-test-decryptor.h"
|
||||
#include "gmp-test-storage.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <assert.h>
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
FakeDecryptor* FakeDecryptor::sInstance = nullptr;
|
||||
|
||||
static bool sFinishedTruncateTest = false;
|
||||
static bool sFinishedReplaceTest = false;
|
||||
static bool sMultiClientTest = false;
|
||||
|
||||
void
|
||||
MaybeFinish()
|
||||
{
|
||||
if (sFinishedTruncateTest && sFinishedReplaceTest && sMultiClientTest) {
|
||||
FakeDecryptor::Message("test-storage complete");
|
||||
}
|
||||
}
|
||||
|
||||
FakeDecryptor::FakeDecryptor(GMPDecryptorHost* aHost)
|
||||
: mHost(aHost)
|
||||
, mCallback(nullptr)
|
||||
{
|
||||
assert(!sInstance);
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
void FakeDecryptor::DecryptingComplete()
|
||||
{
|
||||
sInstance = nullptr;
|
||||
delete this;
|
||||
}
|
||||
|
||||
void
|
||||
FakeDecryptor::Message(const std::string& aMessage)
|
||||
{
|
||||
assert(sInstance);
|
||||
const static std::string sid("fake-session-id");
|
||||
sInstance->mCallback->SessionMessage(sid.c_str(), sid.size(),
|
||||
(const uint8_t*)aMessage.c_str(), aMessage.size(),
|
||||
nullptr, 0);
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
Tokenize(const std::string& aString)
|
||||
{
|
||||
std::stringstream strstr(aString);
|
||||
std::istream_iterator<std::string> it(strstr), end;
|
||||
return std::vector<std::string>(it, end);
|
||||
}
|
||||
|
||||
static const string TruncateRecordId = "truncate-record-id";
|
||||
static const string TruncateRecordData = "I will soon be truncated";
|
||||
|
||||
class ReadThenTask : public GMPTask {
|
||||
public:
|
||||
ReadThenTask(string aId, ReadContinuation* aThen)
|
||||
: mId(aId)
|
||||
, mThen(aThen)
|
||||
{}
|
||||
void Run() MOZ_OVERRIDE {
|
||||
ReadRecord(mId, mThen);
|
||||
}
|
||||
void Destroy() MOZ_OVERRIDE {
|
||||
delete this;
|
||||
}
|
||||
ReadContinuation* mThen;
|
||||
string mId;
|
||||
};
|
||||
|
||||
class TestEmptyContinuation : public ReadContinuation {
|
||||
public:
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
|
||||
if (aData != "") {
|
||||
FakeDecryptor::Message("FAIL TestEmptyContinuation record was not truncated");
|
||||
}
|
||||
sFinishedTruncateTest = true;
|
||||
MaybeFinish();
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
class TruncateContinuation : public ReadContinuation {
|
||||
public:
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
|
||||
if (aData != TruncateRecordData) {
|
||||
FakeDecryptor::Message("FAIL TruncateContinuation read data doesn't match written data");
|
||||
}
|
||||
WriteRecord(TruncateRecordId, nullptr, 0,
|
||||
new ReadThenTask(TruncateRecordId, new TestEmptyContinuation()));
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
class VerifyAndFinishContinuation : public ReadContinuation {
|
||||
public:
|
||||
VerifyAndFinishContinuation(string aValue)
|
||||
: mValue(aValue)
|
||||
{}
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
|
||||
if (aData != mValue) {
|
||||
FakeDecryptor::Message("FAIL VerifyAndFinishContinuation read data doesn't match expected data");
|
||||
}
|
||||
sFinishedReplaceTest = true;
|
||||
MaybeFinish();
|
||||
delete this;
|
||||
}
|
||||
string mValue;
|
||||
};
|
||||
|
||||
class VerifyAndOverwriteContinuation : public ReadContinuation {
|
||||
public:
|
||||
VerifyAndOverwriteContinuation(string aId, string aValue, string aOverwrite)
|
||||
: mId(aId)
|
||||
, mValue(aValue)
|
||||
, mOverwrite(aOverwrite)
|
||||
{}
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
|
||||
if (aData != mValue) {
|
||||
FakeDecryptor::Message("FAIL VerifyAndOverwriteContinuation read data doesn't match expected data");
|
||||
}
|
||||
WriteRecord(mId, mOverwrite, new ReadThenTask(mId, new VerifyAndFinishContinuation(mOverwrite)));
|
||||
delete this;
|
||||
}
|
||||
string mId;
|
||||
string mValue;
|
||||
string mOverwrite;
|
||||
};
|
||||
|
||||
static const string OpenAgainRecordId = "open-again-record-id";
|
||||
|
||||
class OpenedSecondTimeContinuation : public OpenContinuation {
|
||||
public:
|
||||
OpenedSecondTimeContinuation(GMPRecord* aRecord)
|
||||
: mRecord(aRecord)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
|
||||
if (GMP_SUCCEEDED(aStatus)) {
|
||||
FakeDecryptor::Message("FAIL OpenSecondTimeContinuation should not be able to re-open record.");
|
||||
}
|
||||
|
||||
// Succeeded, open should have failed.
|
||||
sMultiClientTest = true;
|
||||
MaybeFinish();
|
||||
|
||||
mRecord->Close();
|
||||
|
||||
delete this;
|
||||
}
|
||||
GMPRecord* mRecord;
|
||||
};
|
||||
|
||||
class OpenedFirstTimeContinuation : public OpenContinuation {
|
||||
public:
|
||||
virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
|
||||
if (GMP_FAILED(aStatus)) {
|
||||
FakeDecryptor::Message("FAIL OpenAgainContinuation to open record initially.");
|
||||
sMultiClientTest = true;
|
||||
MaybeFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
auto err = GMPOpenRecord(OpenAgainRecordId, new OpenedSecondTimeContinuation(aRecord));
|
||||
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
FakeDecryptor::TestStorage()
|
||||
{
|
||||
// Basic I/O tests. We run three cases concurrently. The tests, like
|
||||
// GMPStorage run asynchronously. When they've all passed, we send
|
||||
// a message back to the parent process, or a failure message if not.
|
||||
|
||||
// Test 1: Basic I/O test, and test that writing 0 bytes in a record
|
||||
// deletes record.
|
||||
//
|
||||
// Write data to truncate record, then
|
||||
// read data, verify that we read what we wrote, then
|
||||
// write 0 bytes to truncate record, then
|
||||
// read data, verify that 0 bytes was read
|
||||
// set sFinishedTruncateTest=true and MaybeFinish().
|
||||
WriteRecord(TruncateRecordId,
|
||||
TruncateRecordData,
|
||||
new ReadThenTask(TruncateRecordId, new TruncateContinuation()));
|
||||
|
||||
// Test 2: Test that overwriting a record with a shorter record truncates
|
||||
// the record to the shorter record.
|
||||
//
|
||||
// Write record, then
|
||||
// read and verify record, then
|
||||
// write a shorter record to same record.
|
||||
// read and verify
|
||||
// set sFinishedReplaceTest=true and MaybeFinish().
|
||||
string id = "record1";
|
||||
string record1 = "This is the first write to a record.";
|
||||
string overwrite = "A shorter record";
|
||||
WriteRecord(id,
|
||||
record1,
|
||||
new ReadThenTask(id, new VerifyAndOverwriteContinuation(id, record1, overwrite)));
|
||||
|
||||
// Test 3: Test that opening a record while it's already open fails.
|
||||
//
|
||||
// Open record1, then
|
||||
// open record1, should fail.
|
||||
// close record1,
|
||||
// set sMultiClientTest=true and MaybeFinish().
|
||||
|
||||
GMPOpenRecord(OpenAgainRecordId, new OpenedFirstTimeContinuation());
|
||||
|
||||
// Note: Once all tests finish, dispatch "test-pass" message,
|
||||
// which ends the test for the parent.
|
||||
}
|
||||
|
||||
class ReportWritten : public GMPTask {
|
||||
public:
|
||||
ReportWritten(const string& aRecordId, const string& aValue)
|
||||
: mRecordId(aRecordId)
|
||||
, mValue(aValue)
|
||||
{}
|
||||
void Run() MOZ_OVERRIDE {
|
||||
FakeDecryptor::Message("stored " + mRecordId + " " + mValue);
|
||||
}
|
||||
void Destroy() MOZ_OVERRIDE {
|
||||
delete this;
|
||||
}
|
||||
const string mRecordId;
|
||||
const string mValue;
|
||||
};
|
||||
|
||||
class ReportReadStatusContinuation : public ReadContinuation {
|
||||
public:
|
||||
ReportReadStatusContinuation(const string& aRecordId)
|
||||
: mRecordId(aRecordId)
|
||||
{}
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
|
||||
if (GMP_FAILED(aErr)) {
|
||||
FakeDecryptor::Message("retrieve " + mRecordId + " failed");
|
||||
} else {
|
||||
stringstream ss;
|
||||
ss << aData.size();
|
||||
string len;
|
||||
ss >> len;
|
||||
FakeDecryptor::Message("retrieve " + mRecordId + " succeeded (length " +
|
||||
len + " bytes)");
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
string mRecordId;
|
||||
};
|
||||
|
||||
void
|
||||
FakeDecryptor::UpdateSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const uint8_t* aResponse,
|
||||
uint32_t aResponseSize)
|
||||
{
|
||||
std::string response((const char*)aResponse, (const char*)(aResponse)+aResponseSize);
|
||||
std::vector<std::string> tokens = Tokenize(response);
|
||||
const string& task = tokens[0];
|
||||
if (task == "test-storage") {
|
||||
TestStorage();
|
||||
} else if (task == "store") {
|
||||
// send "stored record" message on complete.
|
||||
const string& id = tokens[1];
|
||||
const string& value = tokens[2];
|
||||
WriteRecord(id,
|
||||
value,
|
||||
new ReportWritten(id, value));
|
||||
} else if (task == "retrieve") {
|
||||
const string& id = tokens[1];
|
||||
ReadRecord(id, new ReportReadStatusContinuation(id));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef FAKE_DECRYPTOR_H__
|
||||
#define FAKE_DECRYPTOR_H__
|
||||
|
||||
#include "gmp-decryption.h"
|
||||
#include "gmp-async-shutdown.h"
|
||||
#include <string>
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class FakeDecryptor : public GMPDecryptor {
|
||||
public:
|
||||
|
||||
FakeDecryptor(GMPDecryptorHost* aHost);
|
||||
|
||||
virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE {
|
||||
mCallback = aCallback;
|
||||
}
|
||||
|
||||
virtual void CreateSession(uint32_t aPromiseId,
|
||||
const char* aInitDataType,
|
||||
uint32_t aInitDataTypeSize,
|
||||
const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize,
|
||||
GMPSessionType aSessionType) MOZ_OVERRIDE
|
||||
{
|
||||
}
|
||||
|
||||
virtual void LoadSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) MOZ_OVERRIDE
|
||||
{
|
||||
}
|
||||
|
||||
virtual void UpdateSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const uint8_t* aResponse,
|
||||
uint32_t aResponseSize) MOZ_OVERRIDE;
|
||||
|
||||
virtual void CloseSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) MOZ_OVERRIDE
|
||||
{
|
||||
}
|
||||
|
||||
virtual void RemoveSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) MOZ_OVERRIDE
|
||||
{
|
||||
}
|
||||
|
||||
virtual void SetServerCertificate(uint32_t aPromiseId,
|
||||
const uint8_t* aServerCert,
|
||||
uint32_t aServerCertSize) MOZ_OVERRIDE
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Decrypt(GMPBuffer* aBuffer,
|
||||
GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE
|
||||
{
|
||||
}
|
||||
|
||||
virtual void DecryptingComplete() MOZ_OVERRIDE;
|
||||
|
||||
static void Message(const std::string& aMessage);
|
||||
|
||||
private:
|
||||
|
||||
static FakeDecryptor* sInstance;
|
||||
|
||||
void TestStorage();
|
||||
|
||||
GMPDecryptorCallback* mCallback;
|
||||
GMPDecryptorHost* mHost;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,194 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gmp-test-storage.h"
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class WriteRecordClient : public GMPRecordClient {
|
||||
public:
|
||||
GMPErr Init(GMPRecord* aRecord,
|
||||
GMPTask* aContinuation,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) {
|
||||
mRecord = aRecord;
|
||||
mContinuation = aContinuation;
|
||||
mData.insert(mData.end(), aData, aData + aDataSize);
|
||||
return mRecord->Open();
|
||||
}
|
||||
|
||||
virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||
mRecord->Write(&mData.front(), mData.size());
|
||||
}
|
||||
|
||||
virtual void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) MOZ_OVERRIDE {}
|
||||
|
||||
virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
mRecord->Close();
|
||||
if (mContinuation) {
|
||||
GMPRunOnMainThread(mContinuation);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
GMPRecord* mRecord;
|
||||
GMPTask* mContinuation;
|
||||
std::vector<uint8_t> mData;
|
||||
};
|
||||
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const uint8_t* aData,
|
||||
uint32_t aNumBytes,
|
||||
GMPTask* aContinuation)
|
||||
{
|
||||
GMPRecord* record;
|
||||
WriteRecordClient* client = new WriteRecordClient();
|
||||
auto err = GMPOpenRecord(aRecordName.c_str(),
|
||||
aRecordName.size(),
|
||||
&record,
|
||||
client);
|
||||
if (GMP_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
return client->Init(record, aContinuation, aData, aNumBytes);
|
||||
}
|
||||
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const std::string& aData,
|
||||
GMPTask* aContinuation)
|
||||
{
|
||||
return WriteRecord(aRecordName,
|
||||
(const uint8_t*)aData.c_str(),
|
||||
aData.size(),
|
||||
aContinuation);
|
||||
}
|
||||
|
||||
class ReadRecordClient : public GMPRecordClient {
|
||||
public:
|
||||
GMPErr Init(GMPRecord* aRecord,
|
||||
ReadContinuation* aContinuation) {
|
||||
mRecord = aRecord;
|
||||
mContinuation = aContinuation;
|
||||
return mRecord->Open();
|
||||
}
|
||||
|
||||
virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||
auto err = mRecord->Read();
|
||||
if (GMP_FAILED(err)) {
|
||||
mContinuation->ReadComplete(err, "");
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) MOZ_OVERRIDE {
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
mRecord->Close();
|
||||
std::string data((const char*)aData, aDataSize);
|
||||
mContinuation->ReadComplete(GMPNoErr, data);
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||
}
|
||||
|
||||
private:
|
||||
GMPRecord* mRecord;
|
||||
ReadContinuation* mContinuation;
|
||||
};
|
||||
|
||||
GMPErr
|
||||
ReadRecord(const std::string& aRecordName,
|
||||
ReadContinuation* aContinuation)
|
||||
{
|
||||
assert(aContinuation);
|
||||
GMPRecord* record;
|
||||
ReadRecordClient* client = new ReadRecordClient();
|
||||
auto err = GMPOpenRecord(aRecordName.c_str(),
|
||||
aRecordName.size(),
|
||||
&record,
|
||||
client);
|
||||
if (GMP_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
return client->Init(record, aContinuation);
|
||||
}
|
||||
|
||||
extern GMPPlatformAPI* g_platform_api; // Defined in gmp-fake.cpp
|
||||
|
||||
GMPErr
|
||||
GMPOpenRecord(const char* aName,
|
||||
uint32_t aNameLength,
|
||||
GMPRecord** aOutRecord,
|
||||
GMPRecordClient* aClient)
|
||||
{
|
||||
assert(g_platform_api);
|
||||
return g_platform_api->createrecord(aName, aNameLength, aOutRecord, aClient);
|
||||
}
|
||||
|
||||
GMPErr
|
||||
GMPRunOnMainThread(GMPTask* aTask)
|
||||
{
|
||||
assert(g_platform_api);
|
||||
return g_platform_api->runonmainthread(aTask);
|
||||
}
|
||||
|
||||
class OpenRecordClient : public GMPRecordClient {
|
||||
public:
|
||||
GMPErr Init(GMPRecord* aRecord,
|
||||
OpenContinuation* aContinuation) {
|
||||
mRecord = aRecord;
|
||||
mContinuation = aContinuation;
|
||||
return mRecord->Open();
|
||||
}
|
||||
|
||||
virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||
mContinuation->OpenComplete(aStatus, mRecord);
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) MOZ_OVERRIDE { }
|
||||
|
||||
virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE { }
|
||||
|
||||
private:
|
||||
GMPRecord* mRecord;
|
||||
OpenContinuation* mContinuation;
|
||||
};
|
||||
|
||||
GMPErr
|
||||
GMPOpenRecord(const std::string& aRecordName,
|
||||
OpenContinuation* aContinuation)
|
||||
{
|
||||
assert(aContinuation);
|
||||
GMPRecord* record;
|
||||
OpenRecordClient* client = new OpenRecordClient();
|
||||
auto err = GMPOpenRecord(aRecordName.c_str(),
|
||||
aRecordName.size(),
|
||||
&record,
|
||||
client);
|
||||
if (GMP_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
return client->Init(record, aContinuation);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TEST_GMP_STORAGE_H__
|
||||
#define TEST_GMP_STORAGE_H__
|
||||
|
||||
#include "gmp-errors.h"
|
||||
#include "gmp-platform.h"
|
||||
#include <string>
|
||||
|
||||
class ReadContinuation {
|
||||
public:
|
||||
virtual void ReadComplete(GMPErr aErr, const std::string& aData) = 0;
|
||||
};
|
||||
|
||||
// Reads a record to storage using GMPRecord.
|
||||
// Calls ReadContinuation with read data.
|
||||
GMPErr
|
||||
ReadRecord(const std::string& aRecordName,
|
||||
ReadContinuation* aContinuation);
|
||||
|
||||
// Writes a record to storage using GMPRecord.
|
||||
// Runs continuation when data is written.
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const std::string& aData,
|
||||
GMPTask* aContinuation);
|
||||
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const uint8_t* aData,
|
||||
uint32_t aNumBytes,
|
||||
GMPTask* aContinuation);
|
||||
|
||||
GMPErr
|
||||
GMPOpenRecord(const char* aName,
|
||||
uint32_t aNameLength,
|
||||
GMPRecord** aOutRecord,
|
||||
GMPRecordClient* aClient);
|
||||
|
||||
GMPErr
|
||||
GMPRunOnMainThread(GMPTask* aTask);
|
||||
|
||||
class OpenContinuation {
|
||||
public:
|
||||
virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) = 0;
|
||||
};
|
||||
|
||||
GMPErr
|
||||
GMPOpenRecord(const std::string& aRecordName,
|
||||
OpenContinuation* aContinuation);
|
||||
|
||||
#endif // TEST_GMP_STORAGE_H__
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
NO_DIST_INSTALL = True
|
||||
SOURCES += [
|
||||
'gmp-fake.cpp'
|
||||
'gmp-fake.cpp',
|
||||
'gmp-test-decryptor.cpp',
|
||||
'gmp-test-storage.cpp',
|
||||
]
|
||||
|
||||
SharedLibrary("fake")
|
||||
|
|
|
@ -1876,7 +1876,10 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
|
|||
|
||||
PluginDestructionGuard guard(npp);
|
||||
|
||||
switch(variable) {
|
||||
// Cast NPNVariable enum to int to avoid warnings about including switch
|
||||
// cases for android_npapi.h's non-standard ANPInterface values.
|
||||
switch (static_cast<int>(variable)) {
|
||||
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
case NPNVxDisplay : {
|
||||
#if defined(MOZ_X11)
|
||||
|
@ -2299,7 +2302,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
|
|||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
case kSystemInterfaceV1_ANPGetValue: {
|
||||
LOG("get system interface v1");
|
||||
ANPSystemInterfaceV1* i = reinterpret_cast<ANPSystemInterfaceV1*>(result);
|
||||
|
@ -2313,7 +2315,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
|
|||
InitSystemInterfaceV2(i);
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// we no longer hand out any XPCOM objects
|
||||
|
@ -2354,7 +2355,9 @@ _setvalue(NPP npp, NPPVariable variable, void *result)
|
|||
|
||||
PluginDestructionGuard guard(inst);
|
||||
|
||||
switch (variable) {
|
||||
// Cast NPNVariable enum to int to avoid warnings about including switch
|
||||
// cases for android_npapi.h's non-standard ANPInterface values.
|
||||
switch (static_cast<int>(variable)) {
|
||||
|
||||
// we should keep backward compatibility with NPAPI where the
|
||||
// actual pointer value is checked rather than its content
|
||||
|
|
|
@ -145,7 +145,8 @@ function main()
|
|||
testTranslate3D,
|
||||
testScale3D,
|
||||
test3D,
|
||||
testParsing
|
||||
testParsing,
|
||||
testStringify
|
||||
];
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
try{
|
||||
|
@ -706,6 +707,19 @@ function testParsing()
|
|||
ok(CompareDOMMatrix(m2, m), "string parsing didn't match");
|
||||
}
|
||||
|
||||
|
||||
function testStringify() {
|
||||
var m = new DOMMatrix();
|
||||
var s = "" + m;
|
||||
ok(s == "matrix" + formatMatrix(m), "stringifier 1 produced wrong result: " + s);
|
||||
m.a = 100;
|
||||
s = "" + m;
|
||||
ok(s == "matrix" + formatMatrix(m), "stringifier 2 produced wrong result: " + s);
|
||||
m.m43 = 200;
|
||||
s = "" + m;
|
||||
ok(s == "matrix3d" + formatMatrix(m), "stringifier 3 produced wrong result:" + s);
|
||||
}
|
||||
|
||||
window.addEventListener("load", main, false);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -76,6 +76,7 @@ interface DOMMatrixReadOnly {
|
|||
DOMPoint transformPoint(optional DOMPointInit point);
|
||||
[Throws] Float32Array toFloat32Array();
|
||||
[Throws] Float64Array toFloat64Array();
|
||||
stringifier;
|
||||
};
|
||||
|
||||
[Pref="layout.css.DOMMatrix.enabled",
|
||||
|
|
|
@ -154,19 +154,32 @@ interface Element : Node {
|
|||
boolean scrollByNoFlush(long dx, long dy);
|
||||
};
|
||||
|
||||
// http://dev.w3.org/csswg/cssom-view/
|
||||
enum ScrollLogicalPosition { "start", "end" };
|
||||
dictionary ScrollIntoViewOptions : ScrollOptions {
|
||||
ScrollLogicalPosition block = "start";
|
||||
};
|
||||
|
||||
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-element-interface
|
||||
partial interface Element {
|
||||
DOMRectList getClientRects();
|
||||
DOMRect getBoundingClientRect();
|
||||
|
||||
// scrolling
|
||||
void scrollIntoView();
|
||||
void scrollIntoView(boolean top, optional ScrollOptions options);
|
||||
void scrollIntoView(boolean top);
|
||||
void scrollIntoView(optional ScrollIntoViewOptions options);
|
||||
// None of the CSSOM attributes are [Pure], because they flush
|
||||
attribute long scrollTop; // scroll on setting
|
||||
attribute long scrollLeft; // scroll on setting
|
||||
readonly attribute long scrollWidth;
|
||||
readonly attribute long scrollHeight;
|
||||
|
||||
void scroll(unrestricted double x, unrestricted double y);
|
||||
void scroll(optional ScrollToOptions options);
|
||||
void scrollTo(unrestricted double x, unrestricted double y);
|
||||
void scrollTo(optional ScrollToOptions options);
|
||||
void scrollBy(unrestricted double x, unrestricted double y);
|
||||
void scrollBy(optional ScrollToOptions options);
|
||||
|
||||
readonly attribute long clientTop;
|
||||
readonly attribute long clientLeft;
|
||||
|
|
|
@ -11,8 +11,9 @@ interface PopupBoxObject : BoxObject
|
|||
*/
|
||||
void showPopup(Element? srcContent, Element popupContent,
|
||||
long xpos, long ypos,
|
||||
DOMString popupType, DOMString anchorAlignment,
|
||||
DOMString popupAlignment);
|
||||
optional DOMString popupType = "",
|
||||
optional DOMString anchorAlignment = "",
|
||||
optional DOMString popupAlignment = "");
|
||||
|
||||
/**
|
||||
* Hide the popup if it is open. The cancel argument is used as a hint that
|
||||
|
@ -105,8 +106,9 @@ interface PopupBoxObject : BoxObject
|
|||
* @param triggerEvent the event that triggered this popup (mouse click for example)
|
||||
*/
|
||||
void openPopup(Element? anchorElement,
|
||||
DOMString position,
|
||||
long x, long y,
|
||||
optional DOMString position = "",
|
||||
long x,
|
||||
long y,
|
||||
boolean isContextMenu,
|
||||
boolean attributesOverride,
|
||||
Event? triggerEvent);
|
||||
|
@ -160,7 +162,7 @@ interface PopupBoxObject : BoxObject
|
|||
* on popups that are not open.
|
||||
*/
|
||||
void moveToAnchor(Element? anchorElement,
|
||||
DOMString position,
|
||||
optional DOMString position = "",
|
||||
long x, long y,
|
||||
boolean attributesOverride);
|
||||
|
||||
|
|
|
@ -152,6 +152,11 @@ dictionary ScrollOptions {
|
|||
ScrollBehavior behavior = "auto";
|
||||
};
|
||||
|
||||
dictionary ScrollToOptions : ScrollOptions {
|
||||
unrestricted double left;
|
||||
unrestricted double top;
|
||||
};
|
||||
|
||||
partial interface Window {
|
||||
//[Throws,NewObject] MediaQueryList matchMedia(DOMString query);
|
||||
[Throws,NewObject] MediaQueryList? matchMedia(DOMString query);
|
||||
|
@ -179,9 +184,12 @@ partial interface Window {
|
|||
//[Throws] readonly attribute double pageXOffset;
|
||||
//[Throws] readonly attribute double scrollY;
|
||||
//[Throws] readonly attribute double pageYOffset;
|
||||
void scroll(unrestricted double x, unrestricted double y, optional ScrollOptions options);
|
||||
void scrollTo(unrestricted double x, unrestricted double y, optional ScrollOptions options);
|
||||
void scrollBy(unrestricted double x, unrestricted double y, optional ScrollOptions options);
|
||||
void scroll(unrestricted double x, unrestricted double y);
|
||||
void scroll(optional ScrollToOptions options);
|
||||
void scrollTo(unrestricted double x, unrestricted double y);
|
||||
void scrollTo(optional ScrollToOptions options);
|
||||
void scrollBy(unrestricted double x, unrestricted double y);
|
||||
void scrollBy(optional ScrollToOptions options);
|
||||
[Replaceable, Throws] readonly attribute long scrollX;
|
||||
[Throws] readonly attribute long pageXOffset;
|
||||
[Replaceable, Throws] readonly attribute long scrollY;
|
||||
|
|
|
@ -2226,7 +2226,15 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(!mFirstChild && !mLastChild);
|
||||
MOZ_ASSERT(!aLayer->GetParent());
|
||||
MOZ_ASSERT(aLayer->Manager() == Manager());
|
||||
if (aLayer->Manager() != Manager()) {
|
||||
// This can happen when e.g. rendering while dragging tabs
|
||||
// between windows - aLayer's manager may be the manager for the
|
||||
// old window's tab. In that case, it will be changed before the
|
||||
// next render (see SetLayerManager). It is simply easier to
|
||||
// ignore the rendering here than it is to pause it.
|
||||
NS_WARNING("ConnectReferentLayer failed - Incorrect LayerManager");
|
||||
return;
|
||||
}
|
||||
|
||||
mFirstChild = mLastChild = aLayer;
|
||||
aLayer->SetParent(this);
|
||||
|
@ -2238,9 +2246,6 @@ public:
|
|||
*/
|
||||
void DetachReferentLayer(Layer* aLayer)
|
||||
{
|
||||
MOZ_ASSERT(aLayer == mFirstChild && mFirstChild == mLastChild);
|
||||
MOZ_ASSERT(aLayer->GetParent() == this);
|
||||
|
||||
mFirstChild = mLastChild = nullptr;
|
||||
aLayer->SetParent(nullptr);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,16 @@ CanvasLayerComposite::GetLayer()
|
|||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
CanvasLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
|
||||
{
|
||||
LayerComposite::SetLayerManager(aManager);
|
||||
mManager = aManager;
|
||||
if (mImageHost) {
|
||||
mImageHost->SetCompositor(mCompositor);
|
||||
}
|
||||
}
|
||||
|
||||
LayerRenderState
|
||||
CanvasLayerComposite::GetRenderState()
|
||||
{
|
||||
|
|
|
@ -49,6 +49,8 @@ public:
|
|||
Destroy();
|
||||
}
|
||||
|
||||
virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE;
|
||||
|
||||
virtual Layer* GetLayer() MOZ_OVERRIDE;
|
||||
virtual void RenderLayer(const nsIntRect& aClipRect) MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -42,6 +42,12 @@ public:
|
|||
// LayerComposite Implementation
|
||||
virtual Layer* GetLayer() MOZ_OVERRIDE { return this; }
|
||||
|
||||
virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE
|
||||
{
|
||||
LayerComposite::SetLayerManager(aManager);
|
||||
mManager = aManager;
|
||||
}
|
||||
|
||||
virtual void Destroy() MOZ_OVERRIDE { mDestroyed = true; }
|
||||
|
||||
virtual void RenderLayer(const nsIntRect& aClipRect) MOZ_OVERRIDE;
|
||||
|
|
|
@ -66,50 +66,6 @@ LayerHasCheckerboardingAPZC(Layer* aLayer, gfxRGBA* aOutColor)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rectangle of content painted opaquely by aLayer. Very consertative;
|
||||
* bails by returning an empty rect in any tricky situations.
|
||||
*/
|
||||
static nsIntRect
|
||||
GetOpaqueRect(Layer* aLayer)
|
||||
{
|
||||
nsIntRect result;
|
||||
gfx::Matrix matrix;
|
||||
bool is2D = aLayer->AsLayerComposite()->GetShadowTransform().Is2D(&matrix);
|
||||
|
||||
// Just bail if there's anything difficult to handle.
|
||||
if (!is2D || aLayer->GetMaskLayer() ||
|
||||
aLayer->GetIsFixedPosition() ||
|
||||
aLayer->GetIsStickyPosition() ||
|
||||
aLayer->GetEffectiveOpacity() != 1.0f ||
|
||||
matrix.HasNonIntegerTranslation()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
|
||||
result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle();
|
||||
} else {
|
||||
// Drill down into RefLayers because that's what we particularly care about;
|
||||
// layer construction for aLayer will not have known about the opaqueness
|
||||
// of any RefLayer subtrees.
|
||||
RefLayer* refLayer = aLayer->AsRefLayer();
|
||||
if (refLayer && refLayer->GetFirstChild()) {
|
||||
result = GetOpaqueRect(refLayer->GetFirstChild());
|
||||
}
|
||||
}
|
||||
|
||||
// Translate our opaque region to cover the child
|
||||
gfx::Point point = matrix.GetTranslation();
|
||||
result.MoveBy(static_cast<int>(point.x), static_cast<int>(point.y));
|
||||
|
||||
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
|
||||
if (clipRect) {
|
||||
result.IntersectRect(result, *clipRect);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
|
||||
LayerManagerComposite* aManager,
|
||||
Layer* aLayer)
|
||||
|
@ -162,12 +118,10 @@ static void PrintUniformityInfo(Layer* aLayer)
|
|||
/* all of the per-layer prepared data we need to maintain */
|
||||
struct PreparedLayer
|
||||
{
|
||||
PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect, bool aRestoreVisibleRegion, nsIntRegion &aVisibleRegion) :
|
||||
mLayer(aLayer), mClipRect(aClipRect), mRestoreVisibleRegion(aRestoreVisibleRegion), mSavedVisibleRegion(aVisibleRegion) {}
|
||||
PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
|
||||
mLayer(aLayer), mClipRect(aClipRect) {}
|
||||
LayerComposite* mLayer;
|
||||
RenderTargetIntRect mClipRect;
|
||||
bool mRestoreVisibleRegion;
|
||||
nsIntRegion mSavedVisibleRegion;
|
||||
};
|
||||
|
||||
/* all of the prepared data that we need in RenderLayer() */
|
||||
|
@ -223,38 +177,8 @@ ContainerPrepare(ContainerT* aContainer,
|
|||
|
||||
CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
|
||||
|
||||
nsIntRegion savedVisibleRegion;
|
||||
bool restoreVisibleRegion = false;
|
||||
gfx::Matrix matrix;
|
||||
bool is2D = layerToRender->GetLayer()->GetBaseTransform().Is2D(&matrix);
|
||||
if (i + 1 < children.Length() &&
|
||||
is2D && !matrix.HasNonIntegerTranslation()) {
|
||||
LayerComposite* nextLayer = static_cast<LayerComposite*>(children.ElementAt(i + 1)->ImplData());
|
||||
CULLING_LOG("Culling against %p\n", nextLayer->GetLayer());
|
||||
nsIntRect nextLayerOpaqueRect;
|
||||
if (nextLayer && nextLayer->GetLayer()) {
|
||||
nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer());
|
||||
gfx::Point point = matrix.GetTranslation();
|
||||
nextLayerOpaqueRect.MoveBy(static_cast<int>(-point.x), static_cast<int>(-point.y));
|
||||
CULLING_LOG(" point %i, %i\n", static_cast<int>(-point.x), static_cast<int>(-point.y));
|
||||
CULLING_LOG(" opaque rect %i, %i, %i, %i\n", nextLayerOpaqueRect.x, nextLayerOpaqueRect.y, nextLayerOpaqueRect.width, nextLayerOpaqueRect.height);
|
||||
}
|
||||
if (!nextLayerOpaqueRect.IsEmpty()) {
|
||||
CULLING_LOG(" draw\n");
|
||||
savedVisibleRegion = layerToRender->GetShadowVisibleRegion();
|
||||
nsIntRegion visibleRegion;
|
||||
visibleRegion.Sub(savedVisibleRegion, nextLayerOpaqueRect);
|
||||
if (visibleRegion.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
layerToRender->SetShadowVisibleRegion(visibleRegion);
|
||||
restoreVisibleRegion = true;
|
||||
} else {
|
||||
CULLING_LOG(" skip\n");
|
||||
}
|
||||
}
|
||||
layerToRender->Prepare(clipRect);
|
||||
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, restoreVisibleRegion, savedVisibleRegion));
|
||||
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
|
||||
}
|
||||
|
||||
CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
|
||||
|
@ -325,11 +249,6 @@ RenderLayers(ContainerT* aContainer,
|
|||
layerToRender->RenderLayer(RenderTargetPixel::ToUntyped(clipRect));
|
||||
}
|
||||
|
||||
if (preparedData.mRestoreVisibleRegion) {
|
||||
// Restore the region in case it's not covered by opaque content next time
|
||||
layerToRender->SetShadowVisibleRegion(preparedData.mSavedVisibleRegion);
|
||||
}
|
||||
|
||||
if (gfxPrefs::UniformityInfo()) {
|
||||
PrintUniformityInfo(layer);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,17 @@ public:
|
|||
// LayerComposite Implementation
|
||||
virtual Layer* GetLayer() MOZ_OVERRIDE { return this; }
|
||||
|
||||
virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE
|
||||
{
|
||||
LayerComposite::SetLayerManager(aManager);
|
||||
mManager = aManager;
|
||||
|
||||
for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
|
||||
LayerComposite* child = l->AsLayerComposite();
|
||||
child->SetLayerManager(aManager);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Destroy() MOZ_OVERRIDE;
|
||||
|
||||
LayerComposite* GetFirstChildComposite();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
|
||||
#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
|
||||
#include "nscore.h" // for nsACString
|
||||
#include "CompositableHost.h" // for CompositableHost
|
||||
|
||||
struct nsIntPoint;
|
||||
struct nsIntRect;
|
||||
|
@ -21,7 +22,6 @@ struct nsIntRect;
|
|||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class CompositableHost;
|
||||
class ImageHost;
|
||||
class Layer;
|
||||
|
||||
|
@ -45,6 +45,15 @@ public:
|
|||
|
||||
virtual Layer* GetLayer() MOZ_OVERRIDE;
|
||||
|
||||
virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE
|
||||
{
|
||||
LayerComposite::SetLayerManager(aManager);
|
||||
mManager = aManager;
|
||||
if (mImageHost) {
|
||||
mImageHost->SetCompositor(mCompositor);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void RenderLayer(const nsIntRect& aClipRect);
|
||||
|
||||
virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) MOZ_OVERRIDE;
|
||||
|
|
|
@ -197,6 +197,55 @@ LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const
|
|||
mTargetBounds = aRect;
|
||||
}
|
||||
|
||||
void
|
||||
LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion)
|
||||
{
|
||||
nsIntRegion localOpaque;
|
||||
Matrix transform2d;
|
||||
bool isTranslation = false;
|
||||
// If aLayer has a simple transform (only an integer translation) then we
|
||||
// can easily convert aOpaqueRegion into pre-transform coordinates and include
|
||||
// that region.
|
||||
if (aLayer->GetLocalTransform().Is2D(&transform2d)) {
|
||||
if (transform2d.IsIntegerTranslation()) {
|
||||
isTranslation = true;
|
||||
localOpaque = aOpaqueRegion;
|
||||
localOpaque.MoveBy(-transform2d._31, -transform2d._32);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract any areas that we know to be opaque from our
|
||||
// visible region.
|
||||
LayerComposite *composite = aLayer->AsLayerComposite();
|
||||
if (!localOpaque.IsEmpty()) {
|
||||
nsIntRegion visible = composite->GetShadowVisibleRegion();
|
||||
visible.Sub(visible, localOpaque);
|
||||
composite->SetShadowVisibleRegion(visible);
|
||||
}
|
||||
|
||||
// Compute occlusions for our descendants (in front-to-back order) and allow them to
|
||||
// contribute to localOpaque.
|
||||
for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
|
||||
ApplyOcclusionCulling(child, localOpaque);
|
||||
}
|
||||
|
||||
// If we have a simple transform, then we can add our opaque area into
|
||||
// aOpaqueRegion.
|
||||
if (isTranslation &&
|
||||
!aLayer->GetMaskLayer() &&
|
||||
aLayer->GetLocalOpacity() == 1.0f) {
|
||||
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
|
||||
localOpaque.Or(localOpaque, composite->GetShadowVisibleRegion());
|
||||
}
|
||||
localOpaque.MoveBy(transform2d._31, transform2d._32);
|
||||
const nsIntRect* clip = aLayer->GetEffectiveClipRect();
|
||||
if (clip) {
|
||||
localOpaque.And(localOpaque, *clip);
|
||||
}
|
||||
aOpaqueRegion.Or(aOpaqueRegion, localOpaque);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
|
||||
{
|
||||
|
@ -257,6 +306,9 @@ LayerManagerComposite::EndTransaction(DrawPaintedLayerCallback aCallback,
|
|||
// so we don't need to pass any global transform here.
|
||||
mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
|
||||
|
||||
nsIntRegion opaque;
|
||||
ApplyOcclusionCulling(mRoot, opaque);
|
||||
|
||||
Render();
|
||||
mGeometryChanged = false;
|
||||
} else {
|
||||
|
@ -1083,6 +1135,13 @@ LayerManagerComposite::NotifyShadowTreeTransaction()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
|
||||
{
|
||||
mCompositeManager = aManager;
|
||||
mCompositor = aManager->GetCompositor();
|
||||
}
|
||||
|
||||
#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
|
||||
|
||||
/*static*/ bool
|
||||
|
|
|
@ -165,6 +165,13 @@ public:
|
|||
|
||||
virtual const char* Name() const MOZ_OVERRIDE { return ""; }
|
||||
|
||||
/**
|
||||
* Restricts the shadow visible region of layers that are covered with
|
||||
* opaque content. aOpaqueRegion is the region already known to be covered
|
||||
* with opaque content, in the post-transform coordinate space of aLayer.
|
||||
*/
|
||||
void ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion);
|
||||
|
||||
/**
|
||||
* RAII helper class to add a mask effect with the compositable from aMaskLayer
|
||||
* to the EffectChain aEffect and notify the compositable when we are done.
|
||||
|
@ -352,6 +359,8 @@ public:
|
|||
|
||||
virtual Layer* GetLayer() = 0;
|
||||
|
||||
virtual void SetLayerManager(LayerManagerComposite* aManager);
|
||||
|
||||
/**
|
||||
* Perform a first pass over the layer tree to render all of the intermediate
|
||||
* surfaces that we can. This allows us to avoid framebuffer switches in the
|
||||
|
|
|
@ -85,6 +85,16 @@ PaintedLayerComposite::GetLayer()
|
|||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
PaintedLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
|
||||
{
|
||||
LayerComposite::SetLayerManager(aManager);
|
||||
mManager = aManager;
|
||||
if (mBuffer) {
|
||||
mBuffer->SetCompositor(mCompositor);
|
||||
}
|
||||
}
|
||||
|
||||
TiledLayerComposer*
|
||||
PaintedLayerComposite::GetTiledLayerComposer()
|
||||
{
|
||||
|
|
|
@ -53,6 +53,8 @@ public:
|
|||
|
||||
virtual Layer* GetLayer() MOZ_OVERRIDE;
|
||||
|
||||
virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE;
|
||||
|
||||
virtual TiledLayerComposer* GetTiledLayerComposer() MOZ_OVERRIDE;
|
||||
|
||||
virtual void RenderLayer(const nsIntRect& aClipRect) MOZ_OVERRIDE;
|
||||
|
|
|
@ -371,6 +371,7 @@ BufferTextureHost::SetCompositor(Compositor* aCompositor)
|
|||
it->SetCompositor(aCompositor);
|
||||
it = it->GetNextSibling();
|
||||
}
|
||||
mFirstSource = nullptr;
|
||||
mCompositor = aCompositor;
|
||||
}
|
||||
|
||||
|
|
|
@ -215,6 +215,15 @@ public:
|
|||
return mLowPrecisionTiledBuffer.GetValidRegion();
|
||||
}
|
||||
|
||||
virtual void SetCompositor(Compositor* aCompositor)
|
||||
{
|
||||
CompositableHost::SetCompositor(aCompositor);
|
||||
mTiledBuffer.SetCompositor(aCompositor);
|
||||
mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
|
||||
mOldTiledBuffer.SetCompositor(aCompositor);
|
||||
mOldLowPrecisionTiledBuffer.SetCompositor(aCompositor);
|
||||
}
|
||||
|
||||
virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
|
||||
const SurfaceDescriptorTiles& aTiledDescriptor) MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -645,6 +645,26 @@ CompositorParent::CompositeCallback()
|
|||
CompositeToTarget(nullptr);
|
||||
}
|
||||
|
||||
// Go down the composite layer tree, setting properties to match their
|
||||
// content-side counterparts.
|
||||
static void
|
||||
SetShadowProperties(Layer* aLayer)
|
||||
{
|
||||
// FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
|
||||
LayerComposite* layerComposite = aLayer->AsLayerComposite();
|
||||
// Set the layerComposite's base transform to the layer's base transform.
|
||||
layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
|
||||
layerComposite->SetShadowTransformSetByAnimation(false);
|
||||
layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
|
||||
layerComposite->SetShadowClipRect(aLayer->GetClipRect());
|
||||
layerComposite->SetShadowOpacity(aLayer->GetOpacity());
|
||||
|
||||
for (Layer* child = aLayer->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
SetShadowProperties(child);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
|
||||
{
|
||||
|
@ -672,6 +692,7 @@ CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
|
|||
}
|
||||
|
||||
AutoResolveRefLayers resolve(mCompositionManager);
|
||||
SetShadowProperties(mLayerManager->GetRoot());
|
||||
|
||||
if (aTarget) {
|
||||
mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
|
||||
|
@ -759,26 +780,6 @@ CompositorParent::CanComposite()
|
|||
!mPaused;
|
||||
}
|
||||
|
||||
// Go down the composite layer tree, setting properties to match their
|
||||
// content-side counterparts.
|
||||
static void
|
||||
SetShadowProperties(Layer* aLayer)
|
||||
{
|
||||
// FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
|
||||
LayerComposite* layerComposite = aLayer->AsLayerComposite();
|
||||
// Set the layerComposite's base transform to the layer's base transform.
|
||||
layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
|
||||
layerComposite->SetShadowTransformSetByAnimation(false);
|
||||
layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
|
||||
layerComposite->SetShadowClipRect(aLayer->GetClipRect());
|
||||
layerComposite->SetShadowOpacity(aLayer->GetOpacity());
|
||||
|
||||
for (Layer* child = aLayer->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
SetShadowProperties(child);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
|
||||
bool aIsFirstPaint)
|
||||
|
@ -826,9 +827,6 @@ CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
|||
MOZ_ASSERT(aTransactionId > mPendingTransaction);
|
||||
mPendingTransaction = aTransactionId;
|
||||
|
||||
if (root) {
|
||||
SetShadowProperties(root);
|
||||
}
|
||||
if (aScheduleComposite) {
|
||||
ScheduleComposition();
|
||||
if (mPaused) {
|
||||
|
@ -842,6 +840,7 @@ CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
|||
// conditions.
|
||||
if (mIsTesting && root && mCurrentCompositeTask) {
|
||||
AutoResolveRefLayers resolve(mCompositionManager);
|
||||
SetShadowProperties(mLayerManager->GetRoot());
|
||||
bool requestNextFrame =
|
||||
mCompositionManager->TransformShadowTree(mTestTime);
|
||||
if (!requestNextFrame) {
|
||||
|
@ -874,6 +873,7 @@ CompositorParent::SetTestSampleTime(LayerTransactionParent* aLayerTree,
|
|||
// Update but only if we were already scheduled to animate
|
||||
if (mCompositionManager && mCurrentCompositeTask) {
|
||||
AutoResolveRefLayers resolve(mCompositionManager);
|
||||
SetShadowProperties(mLayerManager->GetRoot());
|
||||
bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime);
|
||||
if (!requestNextFrame) {
|
||||
CancelCurrentCompositeTask();
|
||||
|
@ -1039,6 +1039,19 @@ CompositorParent::NotifyChildCreated(const uint64_t& aChild)
|
|||
sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorParent::RecvAdoptChild(const uint64_t& child)
|
||||
{
|
||||
NotifyChildCreated(child);
|
||||
if (sIndirectLayerTrees[child].mLayerTree) {
|
||||
sIndirectLayerTrees[child].mLayerTree->mLayerManager = mLayerManager;
|
||||
}
|
||||
if (sIndirectLayerTrees[child].mRoot) {
|
||||
sIndirectLayerTrees[child].mRoot->AsLayerComposite()->SetLayerManager(mLayerManager);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*static*/ uint64_t
|
||||
CompositorParent::AllocateLayerTreeId()
|
||||
{
|
||||
|
@ -1179,6 +1192,7 @@ public:
|
|||
virtual bool RecvPause() MOZ_OVERRIDE { return true; }
|
||||
virtual bool RecvResume() MOZ_OVERRIDE { return true; }
|
||||
virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE;
|
||||
virtual bool RecvAdoptChild(const uint64_t& child) MOZ_OVERRIDE { return false; }
|
||||
virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
|
||||
const nsIntRect& aRect)
|
||||
{ return true; }
|
||||
|
@ -1422,9 +1436,6 @@ CrossProcessCompositorParent::ShadowLayersUpdated(
|
|||
state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
|
||||
|
||||
Layer* shadowRoot = aLayerTree->GetRoot();
|
||||
if (shadowRoot) {
|
||||
SetShadowProperties(shadowRoot);
|
||||
}
|
||||
UpdateIndirectTree(id, shadowRoot, aTargetConfig);
|
||||
|
||||
state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
|
||||
|
|
|
@ -109,6 +109,7 @@ public:
|
|||
virtual bool RecvPause() MOZ_OVERRIDE;
|
||||
virtual bool RecvResume() MOZ_OVERRIDE;
|
||||
virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE;
|
||||
virtual bool RecvAdoptChild(const uint64_t& child) MOZ_OVERRIDE;
|
||||
virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
|
||||
const nsIntRect& aRect) MOZ_OVERRIDE;
|
||||
virtual bool RecvFlushRendering() MOZ_OVERRIDE;
|
||||
|
|
|
@ -73,6 +73,7 @@ parent:
|
|||
sync Resume();
|
||||
|
||||
async NotifyChildCreated(uint64_t id);
|
||||
async AdoptChild(uint64_t id);
|
||||
|
||||
// Make a snapshot of the content that would have been drawn to our
|
||||
// render target at the time this message is received. If the size
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
* 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/. */
|
||||
|
||||
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
include protocol PBlob;
|
||||
include protocol PFileDescriptorSet;
|
||||
|
||||
using struct nsID
|
||||
from "nsID.h";
|
||||
include DOMTypes;
|
||||
|
||||
using struct mozilla::void_t
|
||||
from "ipc/IPCMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#define ScrollbarStyles_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "nsStyleConsts.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -16,13 +18,30 @@ struct ScrollbarStyles
|
|||
// or NS_STYLE_OVERFLOW_AUTO.
|
||||
uint8_t mHorizontal;
|
||||
uint8_t mVertical;
|
||||
ScrollbarStyles(uint8_t h, uint8_t v) : mHorizontal(h), mVertical(v) {}
|
||||
// Always one of NS_STYLE_SCROLL_BEHAVIOR_AUTO,
|
||||
// NS_STYLE_SCROLL_BEHAVIOR_INSTANT, or
|
||||
// NS_STYLE_SCROLL_BEHAVIOR_SMOOTH
|
||||
uint8_t mScrollBehavior;
|
||||
ScrollbarStyles(uint8_t aH, uint8_t aV, uint8_t aB) : mHorizontal(aH),
|
||||
mVertical(aV),
|
||||
mScrollBehavior(aB) {}
|
||||
ScrollbarStyles() {}
|
||||
bool operator==(const ScrollbarStyles& aStyles) const {
|
||||
return aStyles.mHorizontal == mHorizontal && aStyles.mVertical == mVertical;
|
||||
return aStyles.mHorizontal == mHorizontal && aStyles.mVertical == mVertical &&
|
||||
aStyles.mScrollBehavior == mScrollBehavior;
|
||||
}
|
||||
bool operator!=(const ScrollbarStyles& aStyles) const {
|
||||
return aStyles.mHorizontal != mHorizontal || aStyles.mVertical != mVertical;
|
||||
return aStyles.mHorizontal != mHorizontal || aStyles.mVertical != mVertical ||
|
||||
aStyles.mScrollBehavior != mScrollBehavior;
|
||||
}
|
||||
bool IsHiddenInBothDirections() const {
|
||||
return mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
|
||||
mVertical == NS_STYLE_OVERFLOW_HIDDEN;
|
||||
}
|
||||
bool IsSmoothScroll(dom::ScrollBehavior aBehavior) const {
|
||||
return aBehavior == dom::ScrollBehavior::Smooth ||
|
||||
(aBehavior == dom::ScrollBehavior::Auto &&
|
||||
mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2306,15 +2306,22 @@ NeedFrameFor(const nsFrameConstructorState& aState,
|
|||
static bool CheckOverflow(nsPresContext* aPresContext,
|
||||
const nsStyleDisplay* aDisplay)
|
||||
{
|
||||
if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)
|
||||
if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
|
||||
aDisplay->mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_AUTO) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP)
|
||||
aPresContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_HIDDEN,
|
||||
NS_STYLE_OVERFLOW_HIDDEN);
|
||||
else
|
||||
aPresContext->SetViewportOverflowOverride(aDisplay->mOverflowX,
|
||||
aDisplay->mOverflowY);
|
||||
if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP) {
|
||||
aPresContext->SetViewportScrollbarStylesOverride(
|
||||
ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
|
||||
NS_STYLE_OVERFLOW_HIDDEN,
|
||||
aDisplay->mScrollBehavior));
|
||||
} else {
|
||||
aPresContext->SetViewportScrollbarStylesOverride(
|
||||
ScrollbarStyles(aDisplay->mOverflowX,
|
||||
aDisplay->mOverflowY,
|
||||
aDisplay->mScrollBehavior));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2331,8 +2338,10 @@ nsCSSFrameConstructor::PropagateScrollToViewport()
|
|||
{
|
||||
// Set default
|
||||
nsPresContext* presContext = mPresShell->GetPresContext();
|
||||
presContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_AUTO,
|
||||
NS_STYLE_OVERFLOW_AUTO);
|
||||
presContext->SetViewportScrollbarStylesOverride(
|
||||
ScrollbarStyles(NS_STYLE_OVERFLOW_AUTO,
|
||||
NS_STYLE_OVERFLOW_AUTO,
|
||||
NS_STYLE_SCROLL_BEHAVIOR_AUTO));
|
||||
|
||||
// We never mess with the viewport scroll state
|
||||
// when printing or in print preview
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsFrameManager.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "ScrollbarStyles.h"
|
||||
|
||||
struct nsFrameItems;
|
||||
struct nsAbsoluteItems;
|
||||
|
|
|
@ -591,9 +591,11 @@ public:
|
|||
* document so that the anchor with the specified name is displayed at
|
||||
* the top of the window. If |aAnchorName| is empty, then this informs
|
||||
* the pres shell that there is no current target, and |aScroll| must
|
||||
* be false.
|
||||
* be false. If |aAdditionalScrollFlags| is nsIPresShell::SCROLL_SMOOTH_AUTO
|
||||
* and |aScroll| is true, the scrolling may be performed with an animation.
|
||||
*/
|
||||
virtual nsresult GoToAnchor(const nsAString& aAnchorName, bool aScroll) = 0;
|
||||
virtual nsresult GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
uint32_t aAdditionalScrollFlags = 0) = 0;
|
||||
|
||||
/**
|
||||
* Tells the presshell to scroll again to the last anchor scrolled to by
|
||||
|
@ -689,6 +691,12 @@ public:
|
|||
* is enabled, we will scroll smoothly using
|
||||
* nsIScrollableFrame::ScrollMode::SMOOTH_MSD; otherwise,
|
||||
* nsIScrollableFrame::ScrollMode::INSTANT will be used.
|
||||
* If SCROLL_SMOOTH_AUTO is set, the CSSOM-View
|
||||
* scroll-behavior attribute is set to 'smooth' on the
|
||||
* scroll frame, and CSSOM-VIEW scroll-behavior is enabled,
|
||||
* we will scroll smoothly using
|
||||
* nsIScrollableFrame::ScrollMode::SMOOTH_MSD; otherwise,
|
||||
* nsIScrollableFrame::ScrollMode::INSTANT will be used.
|
||||
*/
|
||||
virtual nsresult ScrollContentIntoView(nsIContent* aContent,
|
||||
ScrollAxis aVertical,
|
||||
|
@ -699,7 +707,8 @@ public:
|
|||
SCROLL_FIRST_ANCESTOR_ONLY = 0x01,
|
||||
SCROLL_OVERFLOW_HIDDEN = 0x02,
|
||||
SCROLL_NO_PARENT_FRAMES = 0x04,
|
||||
SCROLL_SMOOTH = 0x08
|
||||
SCROLL_SMOOTH = 0x08,
|
||||
SCROLL_SMOOTH_AUTO = 0x10
|
||||
};
|
||||
/**
|
||||
* Scrolls the view of the document so that the given area of a frame
|
||||
|
|
|
@ -189,7 +189,9 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
|
|||
: mType(aType), mDocument(aDocument), mBaseMinFontSize(0),
|
||||
mTextZoom(1.0), mFullZoom(1.0), mLastFontInflationScreenWidth(-1.0),
|
||||
mPageSize(-1, -1), mPPScale(1.0f),
|
||||
mViewportStyleOverflow(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO),
|
||||
mViewportStyleScrollbar(NS_STYLE_OVERFLOW_AUTO,
|
||||
NS_STYLE_OVERFLOW_AUTO,
|
||||
NS_STYLE_SCROLL_BEHAVIOR_AUTO),
|
||||
mImageAnimationModePref(imgIContainer::kNormalAnimMode),
|
||||
mAllInvalidated(false),
|
||||
mPaintFlashing(false), mPaintFlashingInitialized(false)
|
||||
|
@ -2522,6 +2524,11 @@ nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags)
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PresShell()->IsVisible() && !mFireAfterPaintEvents) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-root prescontexts fire MozAfterPaint to all their descendants
|
||||
// unconditionally, even if no invalidations have been collected. This is
|
||||
// because we don't want to eat the cost of collecting invalidations for
|
||||
|
|
|
@ -138,6 +138,7 @@ class nsRootPresContext;
|
|||
class nsPresContext : public nsIObserver {
|
||||
public:
|
||||
typedef mozilla::FramePropertyTable FramePropertyTable;
|
||||
typedef mozilla::ScrollbarStyles ScrollbarStyles;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
@ -679,14 +680,13 @@ public:
|
|||
nscoord RoundAppUnitsToNearestDevPixels(nscoord aAppUnits) const
|
||||
{ return DevPixelsToAppUnits(AppUnitsToDevPixels(aAppUnits)); }
|
||||
|
||||
void SetViewportOverflowOverride(uint8_t aX, uint8_t aY)
|
||||
void SetViewportScrollbarStylesOverride(const ScrollbarStyles& aScrollbarStyle)
|
||||
{
|
||||
mViewportStyleOverflow.mHorizontal = aX;
|
||||
mViewportStyleOverflow.mVertical = aY;
|
||||
mViewportStyleScrollbar = aScrollbarStyle;
|
||||
}
|
||||
mozilla::ScrollbarStyles GetViewportOverflowOverride()
|
||||
ScrollbarStyles GetViewportScrollbarStylesOverride()
|
||||
{
|
||||
return mViewportStyleOverflow;
|
||||
return mViewportStyleScrollbar;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1280,7 +1280,7 @@ protected:
|
|||
|
||||
nscolor mBodyTextColor;
|
||||
|
||||
mozilla::ScrollbarStyles mViewportStyleOverflow;
|
||||
ScrollbarStyles mViewportStyleScrollbar;
|
||||
uint8_t mFocusRingWidth;
|
||||
|
||||
bool mExistThrottledUpdates;
|
||||
|
|
|
@ -3081,7 +3081,8 @@ PresShell::CreateReferenceRenderingContext()
|
|||
}
|
||||
|
||||
nsresult
|
||||
PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll)
|
||||
PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
uint32_t aAdditionalScrollFlags)
|
||||
{
|
||||
if (!mDocument) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -3188,7 +3189,7 @@ PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll)
|
|||
rv = ScrollContentIntoView(content,
|
||||
ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
|
||||
ScrollAxis(),
|
||||
ANCHOR_SCROLL_FLAGS);
|
||||
ANCHOR_SCROLL_FLAGS | aAdditionalScrollFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
|
||||
|
@ -3529,7 +3530,11 @@ static void ScrollToShowRect(nsIFrame* aFrame,
|
|||
// a current smooth scroll operation.
|
||||
if (needToScroll) {
|
||||
nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
|
||||
if (gfxPrefs::ScrollBehaviorEnabled() && aFlags & nsIPresShell::SCROLL_SMOOTH) {
|
||||
bool autoBehaviorIsSmooth = (aFrameAsScrollable->GetScrollbarStyles().mScrollBehavior
|
||||
== NS_STYLE_SCROLL_BEHAVIOR_SMOOTH);
|
||||
bool smoothScroll = (aFlags & nsIPresShell::SCROLL_SMOOTH) ||
|
||||
((aFlags & nsIPresShell::SCROLL_SMOOTH_AUTO) && autoBehaviorIsSmooth);
|
||||
if (gfxPrefs::ScrollBehaviorEnabled() && smoothScroll) {
|
||||
scrollMode = nsIScrollableFrame::SMOOTH_MSD;
|
||||
}
|
||||
aFrameAsScrollable->ScrollTo(scrollPt, scrollMode, &allowedRange);
|
||||
|
@ -4481,6 +4486,21 @@ PresShell::ContentInserted(nsIDocument* aDocument,
|
|||
VERIFY_STYLE_TREE;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
ReleasePointerCaptureFromRemovedContent(const uint32_t& aKey,
|
||||
nsIPresShell::PointerCaptureInfo* aData,
|
||||
void* aChildLink)
|
||||
{
|
||||
if (aChildLink && aData && aData->mOverrideContent) {
|
||||
if (nsIContent* content = static_cast<nsIContent*>(aChildLink)) {
|
||||
if (nsContentUtils::ContentIsDescendantOf(aData->mOverrideContent, content)) {
|
||||
nsIPresShell::ReleasePointerCapturingContent(aKey, aData->mOverrideContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return PLDHashOperator::PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::ContentRemoved(nsIDocument *aDocument,
|
||||
nsIContent* aContainer,
|
||||
|
@ -4518,6 +4538,10 @@ PresShell::ContentRemoved(nsIDocument *aDocument,
|
|||
RestyleForRemove(aContainer->AsElement(), aChild, oldNextSibling);
|
||||
}
|
||||
|
||||
// We should check that aChild does not contain pointer capturing elements.
|
||||
// If it does we should release the pointer capture for the elements.
|
||||
gPointerCaptureList->EnumerateRead(ReleasePointerCaptureFromRemovedContent, aChild);
|
||||
|
||||
bool didReconstruct;
|
||||
mFrameConstructor->ContentRemoved(aContainer, aChild, oldNextSibling,
|
||||
nsCSSFrameConstructor::REMOVE_CONTENT,
|
||||
|
@ -7915,7 +7939,13 @@ nsIPresShell::DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture,
|
|||
init);
|
||||
if (event) {
|
||||
bool dummy;
|
||||
aCaptureTarget->DispatchEvent(event->InternalDOMEvent(), &dummy);
|
||||
// If the capturing element was removed from the DOM tree,
|
||||
// lostpointercapture event should be fired at the document.
|
||||
if (!aIsGotCapture && !aCaptureTarget->IsInDoc()) {
|
||||
aCaptureTarget->OwnerDoc()->DispatchEvent(event->InternalDOMEvent(), &dummy);
|
||||
} else {
|
||||
aCaptureTarget->DispatchEvent(event->InternalDOMEvent(), &dummy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,8 @@ public:
|
|||
|
||||
virtual void ClearFrameRefs(nsIFrame* aFrame) MOZ_OVERRIDE;
|
||||
virtual already_AddRefed<nsRenderingContext> CreateReferenceRenderingContext();
|
||||
virtual nsresult GoToAnchor(const nsAString& aAnchorName, bool aScroll) MOZ_OVERRIDE;
|
||||
virtual nsresult GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
uint32_t aAdditionalScrollFlags = 0) MOZ_OVERRIDE;
|
||||
virtual nsresult ScrollToAnchor() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult ScrollContentIntoView(nsIContent* aContent,
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1080360
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1080360</title>
|
||||
<meta name="author" content="Maksim Lebedev" />
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
#target, #listener { background: yellow; padding: 10px; }
|
||||
</style>
|
||||
<script type="application/javascript">
|
||||
var target = undefined;
|
||||
var listener = undefined;
|
||||
var test_target = false;
|
||||
var test_listener_got = false;
|
||||
var test_listener_lost = false;
|
||||
var test_document = false;
|
||||
|
||||
function TargetHandler(event) {
|
||||
logger("Target receive event: " + event.type);
|
||||
listener.setPointerCapture(event.pointerId);
|
||||
test_target = true;
|
||||
}
|
||||
function ListenerHandler(event) {
|
||||
logger("Listener receive event: " + event.type);
|
||||
if(event.type == "gotpointercapture") {
|
||||
test_listener_got = true;
|
||||
listener.parentNode.removeChild(listener);
|
||||
}
|
||||
if(event.type == "lostpointercapture")
|
||||
test_listener_lost = true;
|
||||
}
|
||||
function DocumentHandler(event) {
|
||||
logger("Document receive event: " + event.type);
|
||||
if(event.type == "lostpointercapture")
|
||||
test_document = true;
|
||||
}
|
||||
function logger(message) {
|
||||
console.log(message);
|
||||
var log = document.getElementById('log');
|
||||
log.innerHTML = message + "<br>" + log.innerHTML;
|
||||
}
|
||||
|
||||
function prepareTest() {
|
||||
parent.turnOnPointerEvents(executeTest);
|
||||
}
|
||||
function executeTest()
|
||||
{
|
||||
logger("executeTest");
|
||||
target = document.getElementById("target");
|
||||
listener = document.getElementById("listener");
|
||||
target.addEventListener("pointerdown", TargetHandler, false);
|
||||
listener.addEventListener("gotpointercapture", ListenerHandler, false);
|
||||
listener.addEventListener("lostpointercapture", ListenerHandler, false);
|
||||
document.addEventListener("lostpointercapture", DocumentHandler, false);
|
||||
var rect = target.getBoundingClientRect();
|
||||
synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointerdown"});
|
||||
synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointermove"});
|
||||
synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointerup"});
|
||||
finishTest();
|
||||
}
|
||||
function finishTest() {
|
||||
parent.is(test_target, true, "pointerdown event should be received by target");
|
||||
parent.is(test_listener_got, true, "gotpointercapture event should be received by listener");
|
||||
parent.is(test_listener_lost, false, "listener should not receive lostpointercapture event");
|
||||
parent.is(test_document, true, "document should receive lostpointercapture event");
|
||||
logger("finishTest");
|
||||
parent.finishTest();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="prepareTest()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1080360">Mozilla Bug 1080360</a>
|
||||
<div id="target">div id=target</div>
|
||||
<div id="listener">div id=listener</div>
|
||||
<pre id="log"></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -495,6 +495,8 @@ support-files =
|
|||
bug687297_c.html
|
||||
[test_bug990340.html]
|
||||
[test_bug1070851.html]
|
||||
[test_bug1080360.html]
|
||||
support-files = bug1080360_inner.html
|
||||
[test_bug1078327.html]
|
||||
support-files = bug1078327_inner.html
|
||||
[test_bug1080361.html]
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1080360
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1080360</title>
|
||||
<meta name="author" content="Maksim Lebedev" />
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript">
|
||||
var iframe = undefined;
|
||||
function prepareTest() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
iframe = document.getElementById("testFrame");
|
||||
turnOnPointerEvents(startTest);
|
||||
}
|
||||
function turnOnPointerEvents(callback) {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["dom.w3c_pointer_events.enabled", true]
|
||||
]
|
||||
}, callback);
|
||||
}
|
||||
function startTest() {
|
||||
iframe.src = "bug1080360_inner.html";
|
||||
}
|
||||
function finishTest() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="prepareTest()">
|
||||
<iframe id="testFrame" height="700" width="700"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -588,7 +588,8 @@ nsListControlFrame::GetScrollbarStyles() const
|
|||
// and GetScrollbarStyles can be devirtualized
|
||||
int32_t verticalStyle = IsInDropDownMode() ? NS_STYLE_OVERFLOW_AUTO
|
||||
: NS_STYLE_OVERFLOW_SCROLL;
|
||||
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, verticalStyle);
|
||||
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, verticalStyle,
|
||||
NS_STYLE_SCROLL_BEHAVIOR_AUTO);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -7809,11 +7809,11 @@ nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse)
|
|||
// will be enough to make them keyboard scrollable.
|
||||
nsIScrollableFrame *scrollFrame = do_QueryFrame(this);
|
||||
if (scrollFrame &&
|
||||
scrollFrame->GetScrollbarStyles() != ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) &&
|
||||
!scrollFrame->GetScrollbarStyles().IsHiddenInBothDirections() &&
|
||||
!scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
|
||||
// Scroll bars will be used for overflow
|
||||
isFocusable = true;
|
||||
tabIndex = 0;
|
||||
// Scroll bars will be used for overflow
|
||||
isFocusable = true;
|
||||
tabIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -906,8 +906,7 @@ nsHTMLScrollFrame::AccessibleType()
|
|||
// Create an accessible regardless of focusable state because the state can be
|
||||
// changed during frame life cycle without any notifications to accessibility.
|
||||
if (mContent->IsRootOfNativeAnonymousSubtree() ||
|
||||
GetScrollbarStyles() ==
|
||||
ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) ) {
|
||||
GetScrollbarStyles().IsHiddenInBothDirections()) {
|
||||
return a11y::eNoType;
|
||||
}
|
||||
|
||||
|
@ -1787,10 +1786,18 @@ ScrollFrameHelper::AsyncScroll::InitTimingFunction(nsSMILKeySpline& aTimingFunct
|
|||
aTimingFunction.Init(dt, dxy, 1 - kStopDecelerationWeighting, 1);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsSmoothScrollingEnabled()
|
||||
bool
|
||||
ScrollFrameHelper::IsSmoothScrollingEnabled()
|
||||
{
|
||||
return Preferences::GetBool(SMOOTH_SCROLL_PREF_NAME, false);
|
||||
if (!Preferences::GetBool(SMOOTH_SCROLL_PREF_NAME, false)) {
|
||||
return false;
|
||||
}
|
||||
if (gfxPrefs::ScrollBehaviorEnabled()) {
|
||||
ScrollbarStyles styles = GetScrollbarStylesFromFrame();
|
||||
return styles.mScrollBehavior != NS_STYLE_SCROLL_BEHAVIOR_INSTANT;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class ScrollFrameActivityTracker MOZ_FINAL : public nsExpirationTracker<ScrollFrameHelper,4> {
|
||||
|
@ -2996,12 +3003,18 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
// metadata about this scroll box to the compositor process.
|
||||
nsDisplayScrollInfoLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
|
||||
aBuilder, mScrolledFrame, mOuter);
|
||||
nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
|
||||
if (!positionedDescendants->IsEmpty()) {
|
||||
layerItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
|
||||
positionedDescendants->AppendNewToTop(layerItem);
|
||||
if (BuildScrollContainerLayers()) {
|
||||
// We process display items from bottom to top, so if we need to flatten after
|
||||
// the scroll layer items have been processed we need to be on the top.
|
||||
nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
|
||||
if (!positionedDescendants->IsEmpty()) {
|
||||
layerItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
|
||||
positionedDescendants->AppendNewToTop(layerItem);
|
||||
} else {
|
||||
aLists.Outlines()->AppendNewToTop(layerItem);
|
||||
}
|
||||
} else {
|
||||
aLists.Outlines()->AppendNewToTop(layerItem);
|
||||
scrolledContent.BorderBackground()->AppendNewToBottom(layerItem);
|
||||
}
|
||||
}
|
||||
// Now display overlay scrollbars and the resizer, if we have one.
|
||||
|
@ -3068,15 +3081,17 @@ ScrollFrameHelper::GetScrollbarStylesFromFrame() const
|
|||
nsPresContext* presContext = mOuter->PresContext();
|
||||
if (!presContext->IsDynamic() &&
|
||||
!(mIsRoot && presContext->HasPaginatedScrolling())) {
|
||||
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
|
||||
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN,
|
||||
NS_STYLE_SCROLL_BEHAVIOR_AUTO);
|
||||
}
|
||||
|
||||
if (!mIsRoot) {
|
||||
const nsStyleDisplay* disp = mOuter->StyleDisplay();
|
||||
return ScrollbarStyles(disp->mOverflowX, disp->mOverflowY);
|
||||
return ScrollbarStyles(disp->mOverflowX, disp->mOverflowY,
|
||||
disp->mScrollBehavior);
|
||||
}
|
||||
|
||||
ScrollbarStyles result = presContext->GetViewportOverflowOverride();
|
||||
ScrollbarStyles result = presContext->GetViewportScrollbarStylesOverride();
|
||||
nsCOMPtr<nsISupports> container = presContext->GetContainerWeak();
|
||||
nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(container);
|
||||
if (scrollable) {
|
||||
|
|
|
@ -98,6 +98,8 @@ public:
|
|||
void PostScrolledAreaEvent();
|
||||
void FireScrolledAreaEvent();
|
||||
|
||||
bool IsSmoothScrollingEnabled();
|
||||
|
||||
class ScrollEvent : public nsRunnable {
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
ok(window.scrollX == 15 && window.scrollY == 16,
|
||||
"instant scroll-behavior must be synchronous when setting new position");
|
||||
|
||||
window.scrollTo(100, 200, {behavior: 'smooth'});
|
||||
window.scrollTo({left: 100, top: 200, behavior: 'smooth'});
|
||||
ok(window.scrollX == 15 && window.scrollY == 16,
|
||||
"smooth scroll-behavior must be asynchronous");
|
||||
|
||||
|
@ -238,7 +238,7 @@
|
|||
waitForAllPaintsFlushed(function() {
|
||||
|
||||
window.scrollTo(startX, startY);
|
||||
window.scrollTo(endX, endY, {behavior: 'smooth'});
|
||||
window.scrollTo({left: endX, top: endY, behavior: 'smooth'});
|
||||
|
||||
var currentTime = 0; // ms
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ already_AddRefed<LayerManager>
|
|||
GetFrom(nsFrameLoader* aFrameLoader)
|
||||
{
|
||||
nsIDocument* doc = aFrameLoader->GetOwnerDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
return nsContentUtils::LayerManagerForDocument(doc);
|
||||
}
|
||||
|
||||
|
@ -401,6 +404,14 @@ RenderFrameParent::OwnerContentChanged(nsIContent* aContent)
|
|||
{
|
||||
NS_ABORT_IF_FALSE(mFrameLoader->GetOwnerContent() == aContent,
|
||||
"Don't build new map if owner is same!");
|
||||
|
||||
nsRefPtr<LayerManager> lm = GetFrom(mFrameLoader);
|
||||
// Perhaps the document containing this frame currently has no presentation?
|
||||
if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
|
||||
ClientLayerManager *clientManager =
|
||||
static_cast<ClientLayerManager*>(lm.get());
|
||||
clientManager->GetRemoteRenderer()->SendAdoptChild(mLayersId);
|
||||
}
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
|
|
|
@ -9,6 +9,13 @@ random-if(Android&&!browserIsRemote) skip-if(B2G&&browserIsRemote) == iframe-bor
|
|||
random-if(Android) HTTP == image-1.html image-1.html?ref
|
||||
random-if(Android&&!browserIsRemote) HTTP == opacity-mixed-scrolling-1.html opacity-mixed-scrolling-1.html?ref # bug 760269
|
||||
skip-if(B2G) random-if(cocoaWidget) HTTP == opacity-mixed-scrolling-2.html opacity-mixed-scrolling-2.html?ref # see bug 625357
|
||||
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-1.html scroll-behavior-1.html?ref # see bug 1041833
|
||||
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-2.html scroll-behavior-2.html?ref # see bug 1041833
|
||||
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-3.html scroll-behavior-3.html?ref # see bug 1041833
|
||||
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-4.html scroll-behavior-4.html?ref # see bug 1041833
|
||||
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-5.html scroll-behavior-5.html?ref # see bug 1041833
|
||||
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-6.html scroll-behavior-6.html?ref # see bug 1041833
|
||||
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-7.html scroll-behavior-7.html?ref # see bug 1041833
|
||||
skip-if(B2G&&browserIsRemote) HTTP == simple-1.html simple-1.html?ref
|
||||
skip-if(B2G) HTTP == subpixel-1.html#d subpixel-1-ref.html#d
|
||||
fuzzy-if(Android,4,120) HTTP == text-1.html text-1.html?ref
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1010538, smooth scrolling expected</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body {
|
||||
color: black;
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
#a_box {
|
||||
position: relative;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
#another_box {
|
||||
position: relative;
|
||||
left: 2000px;
|
||||
top: 2000px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="a_box"></div>
|
||||
<div id="another_box"></div>
|
||||
<script>
|
||||
function doTest() {
|
||||
if (document.location.search != '?ref') {
|
||||
window.scrollTo({left: 500, top: 500});
|
||||
window.scrollTo({left: window.scrollX, top: window.scrollY});
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
window.scrollTo({left: 0, top: 0, behavior: "instant"});
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,111 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1010538, smooth scrolling expected</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body {
|
||||
color: black;
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.a_box {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.another_box {
|
||||
position: relative;
|
||||
left: 2000px;
|
||||
top: 2000px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
.scroll_box {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#scroll_1, #scroll_2, #scroll_3, #scroll_7 {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
#scroll_4 {
|
||||
scroll-behavior: instant;
|
||||
}
|
||||
|
||||
#scroll_5 {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroll_1" class="scroll_box">
|
||||
<div id="box1a" class="a_box"></div>
|
||||
<div id="box1b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_2" class="scroll_box">
|
||||
<div id="box2a" class="a_box"></div>
|
||||
<div id="box2b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_3" class="scroll_box">
|
||||
<div id="box3a" class="a_box"></div>
|
||||
<div id="box3b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_4" class="scroll_box">
|
||||
<div id="box4a" class="a_box"></div>
|
||||
<div id="box4b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_5" class="scroll_box">
|
||||
<div id="box5a" class="a_box"></div>
|
||||
<div id="box5b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_6" class="scroll_box">
|
||||
<div id="box6a" class="a_box"></div>
|
||||
<div id="box6b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_7" class="scroll_box">
|
||||
<div id="box7a" class="a_box"></div>
|
||||
<div id="box7b" class="another_box"></div>
|
||||
</div>
|
||||
<script>
|
||||
function doTest() {
|
||||
if (document.location.search != '?ref') {
|
||||
document.getElementById("box1b").scrollIntoView({block: "end"});
|
||||
document.getElementById("box2b").scrollIntoView({block: "end", behavior: "auto"});
|
||||
document.getElementById("box3b").scrollIntoView({block: "end", behavior: "smooth"});
|
||||
document.getElementById("box4b").scrollIntoView({block: "end", behavior: "smooth"});
|
||||
document.getElementById("box5b").scrollIntoView({block: "end", behavior: "smooth"});
|
||||
document.getElementById("box6b").scrollIntoView({block: "end", behavior: "smooth"});
|
||||
document.getElementById("box7b").scrollIntoView(false);
|
||||
|
||||
// Interrupt any smooth scrolling
|
||||
for (var i=1; i <= 7; i++) {
|
||||
document.getElementById("scroll_" + i).scrollLeft
|
||||
= document.getElementById("scroll_" + i).scrollLeft;
|
||||
document.getElementById("scroll_" + i).scrollTop
|
||||
= document.getElementById("scroll_" + i).scrollTop;
|
||||
}
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
for (var i=1; i <= 7; i++) {
|
||||
document.getElementById("box" + i + "a")
|
||||
.scrollIntoView({block: "start", behavior: "instant"});
|
||||
}
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,155 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1010538, instant scrolling expected</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body {
|
||||
color: black;
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.a_box {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.another_box {
|
||||
position: relative;
|
||||
left: 2000px;
|
||||
top: 2000px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
.scroll_box {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#scroll_2, #scroll_6, #scroll_9, #scroll_12 {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
#scroll_1, #scroll_5, #scroll_8, #scroll_11 {
|
||||
scroll-behavior: instant;
|
||||
}
|
||||
|
||||
#scroll_4 {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroll_1" class="scroll_box">
|
||||
<div id="box1a" class="a_box"></div>
|
||||
<div id="box1b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_2" class="scroll_box">
|
||||
<div id="box2a" class="a_box"></div>
|
||||
<div id="box2b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_3" class="scroll_box">
|
||||
<div id="box3a" class="a_box"></div>
|
||||
<div id="box3b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_4" class="scroll_box">
|
||||
<div id="box4a" class="a_box"></div>
|
||||
<div id="box4b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_5" class="scroll_box">
|
||||
<div id="box5a" class="a_box"></div>
|
||||
<div id="box5b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_6" class="scroll_box">
|
||||
<div id="box6a" class="a_box"></div>
|
||||
<div id="box6b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_7" class="scroll_box">
|
||||
<div id="box7a" class="a_box"></div>
|
||||
<div id="box7b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_8" class="scroll_box">
|
||||
<div id="box8a" class="a_box"></div>
|
||||
<div id="box8b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_9" class="scroll_box">
|
||||
<div id="box9a" class="a_box"></div>
|
||||
<div id="box9b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_10" class="scroll_box">
|
||||
<div id="box10a" class="a_box"></div>
|
||||
<div id="box10b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_11" class="scroll_box">
|
||||
<div id="box11a" class="a_box"></div>
|
||||
<div id="box11b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_12" class="scroll_box">
|
||||
<div id="box12a" class="a_box"></div>
|
||||
<div id="box12b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_13" class="scroll_box">
|
||||
<div id="box13a" class="a_box"></div>
|
||||
<div id="box13b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_14" class="scroll_box">
|
||||
<div id="box14a" class="a_box"></div>
|
||||
<div id="box14b" class="another_box"></div>
|
||||
</div>
|
||||
<script>
|
||||
function doTest() {
|
||||
if (document.location.search != '?ref') {
|
||||
document.getElementById("box1b").scrollIntoView({block: "end"});
|
||||
document.getElementById("box2b").scrollIntoView({block: "end"});
|
||||
document.getElementById("box3b").scrollIntoView({block: "end"});
|
||||
document.getElementById("box4b").scrollIntoView({block: "end", behavior: "instant"});
|
||||
document.getElementById("box5b").scrollIntoView({block: "end", behavior: "instant"});
|
||||
document.getElementById("box6b").scrollIntoView({block: "end", behavior: "instant"});
|
||||
document.getElementById("box7b").scrollIntoView({block: "end", behavior: "instant"});
|
||||
document.getElementById("box8b").scrollIntoView({block: "end", behavior: "auto"});
|
||||
document.getElementById("box9b").scrollIntoView({block: "end", behavior: "auto"});
|
||||
document.getElementById("box10b").scrollIntoView({block: "end", behavior: "auto"});
|
||||
document.getElementById("box11b").scrollIntoView(false);
|
||||
document.getElementById("box12b").scrollIntoView(false);
|
||||
document.getElementById("box13b").scrollIntoView(false);
|
||||
|
||||
// Scroll_14 is a control, expected to scroll smoothly
|
||||
document.getElementById("box14b").scrollIntoView({block: "end", behavior: "smooth"});
|
||||
|
||||
// Interrupt any smooth scrolling
|
||||
for (var i=1; i <= 14; i++) {
|
||||
document.getElementById("scroll_" + i).scrollLeft
|
||||
= document.getElementById("scroll_" + i).scrollLeft;
|
||||
document.getElementById("scroll_" + i).scrollTop
|
||||
= document.getElementById("scroll_" + i).scrollTop;
|
||||
}
|
||||
} else {
|
||||
// Scroll all boxes except box 14
|
||||
for (var i=1; i <= 13; i++) {
|
||||
document.getElementById("box" + i + "b").scrollIntoView({block: "end", behavior: "instant"});
|
||||
}
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
for (var i=1; i <= 14; i++) {
|
||||
document.getElementById("box" + i + "a")
|
||||
.scrollIntoView({block: "start", behavior: "instant"});
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1010538 - Anchor Link Scrolling</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body {
|
||||
color: black;
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.a_box {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.another_box {
|
||||
position: relative;
|
||||
left: 2000px;
|
||||
top: 2000px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: green;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.scroll_box {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#scroll_1 {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
#scroll_1, #scroll_5, #scroll_8 {
|
||||
scroll-behavior: instant;
|
||||
}
|
||||
|
||||
#scroll_4 {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroll_1" class="scroll_box">
|
||||
<div id="box1a" class="a_box"></div>
|
||||
<a name="test_anchor_1" id="box1b" class="another_box"></a>
|
||||
</div>
|
||||
<div id="scroll_2" class="scroll_box">
|
||||
<div id="box2a" class="a_box"></div>
|
||||
<a name="test_anchor_2" id="box2b" class="another_box"></a>
|
||||
</div>
|
||||
<div id="scroll_3" class="scroll_box">
|
||||
<div id="box3a" class="a_box"></div>
|
||||
<a name="test_anchor_3" id="box3b" class="another_box"></a>
|
||||
</div>
|
||||
<div id="scroll_4" class="scroll_box">
|
||||
<div id="box4a" class="a_box"></div>
|
||||
<a name="test_anchor_4" id="box4b" class="another_box"></a>
|
||||
</div>
|
||||
<script>
|
||||
function doTest() {
|
||||
if (document.location.search != '?ref') {
|
||||
// Scroll_1 to Scroll_3 are expected to scroll instantly
|
||||
window.location.hash = "test_anchor_1";
|
||||
window.location.hash = "test_anchor_2";
|
||||
window.location.hash = "test_anchor_3";
|
||||
|
||||
// Scroll_4 is expected to scroll smoothly
|
||||
document.getElementById("box4b").scrollIntoView({block: "end", behavior: "smooth"});
|
||||
|
||||
// Interrupt any smooth scrolling
|
||||
for (var i=1; i <= 4; i++) {
|
||||
document.getElementById("scroll_" + i).scrollLeft
|
||||
= document.getElementById("scroll_" + i).scrollLeft;
|
||||
document.getElementById("scroll_" + i).scrollTop
|
||||
= document.getElementById("scroll_" + i).scrollTop;
|
||||
}
|
||||
} else {
|
||||
for (var i=1; i <= 3; i++) {
|
||||
document.getElementById("box" + i + "b").scrollIntoView({block: "end", behavior: "instant"});
|
||||
}
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
for (var i=1; i <= 4; i++) {
|
||||
document.getElementById("box" + i + "a")
|
||||
.scrollIntoView({block: "start", behavior: "instant"});
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1010538 - Element.ScrollLeft and Element.ScrollTop scroll-behavior</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body {
|
||||
color: black;
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.a_box {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.another_box {
|
||||
position: relative;
|
||||
left: 2000px;
|
||||
top: 2000px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
.scroll_box {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#scroll_2, #scroll_5 {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
#scroll_3, #scroll_6 {
|
||||
scroll-behavior: instant;
|
||||
}
|
||||
|
||||
#scroll_7, #scroll_8 {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroll_1" class="scroll_box">
|
||||
<div id="box1a" class="a_box"></div>
|
||||
<div id="box1b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_2" class="scroll_box">
|
||||
<div id="box2a" class="a_box"></div>
|
||||
<div id="box2b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_3" class="scroll_box">
|
||||
<div id="box3a" class="a_box"></div>
|
||||
<div id="box3b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_4" class="scroll_box">
|
||||
<div id="box4a" class="a_box"></div>
|
||||
<div id="box4b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_5" class="scroll_box">
|
||||
<div id="box5a" class="a_box"></div>
|
||||
<div id="box5b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_6" class="scroll_box">
|
||||
<div id="box6a" class="a_box"></div>
|
||||
<div id="box6b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_7" class="scroll_box">
|
||||
<div id="box7a" class="a_box"></div>
|
||||
<div id="box7b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_8" class="scroll_box">
|
||||
<div id="box8a" class="a_box"></div>
|
||||
<div id="box8b" class="another_box"></div>
|
||||
</div>
|
||||
<script>
|
||||
function doTest() {
|
||||
if (document.location.search != '?ref') {
|
||||
// Expect instantaneous scroll:
|
||||
document.getElementById("scroll_1").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_2").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_3").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_4").scrollTo(0, 0);
|
||||
document.getElementById("scroll_5").scrollTo(0, 0);
|
||||
document.getElementById("scroll_6").scrollTo(0, 0);
|
||||
|
||||
// Expect smooth scroll:
|
||||
document.getElementById("scroll_7").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_8").scrollTo(0, 0);
|
||||
|
||||
// Interrupt any smooth scrolling
|
||||
for (var i=1; i <= 8; i++) {
|
||||
document.getElementById("scroll_" + i).scrollTo();
|
||||
}
|
||||
} else {
|
||||
// Scroll all boxes except for box7a and box8a
|
||||
for (var i=1; i <= 6; i++) {
|
||||
document.getElementById("box" + i + "a").scrollIntoView({block: "end", behavior: "instant"});
|
||||
}
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
for (var i=1; i <= 8; i++) {
|
||||
document.getElementById("box" + i + "b")
|
||||
.scrollIntoView({block: "start", behavior: "instant"});
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,145 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1010538 - Element.ScrollBy and Element.ScrollTo</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body {
|
||||
color: black;
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.a_box {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.another_box {
|
||||
position: relative;
|
||||
left: 2000px;
|
||||
top: 2000px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
.scroll_box {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroll_1" class="scroll_box">
|
||||
<div id="box1a" class="a_box"></div>
|
||||
<div id="box1b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_2" class="scroll_box">
|
||||
<div id="box2a" class="a_box"></div>
|
||||
<div id="box2b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_3" class="scroll_box">
|
||||
<div id="box3a" class="a_box"></div>
|
||||
<div id="box3b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_4" class="scroll_box">
|
||||
<div id="box4a" class="a_box"></div>
|
||||
<div id="box4b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_5" class="scroll_box">
|
||||
<div id="box5a" class="a_box"></div>
|
||||
<div id="box5b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_6" class="scroll_box">
|
||||
<div id="box6a" class="a_box"></div>
|
||||
<div id="box6b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_7" class="scroll_box">
|
||||
<div id="box7a" class="a_box"></div>
|
||||
<div id="box7b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_8" class="scroll_box">
|
||||
<div id="box8a" class="a_box"></div>
|
||||
<div id="box8b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_9" class="scroll_box">
|
||||
<div id="box9a" class="a_box"></div>
|
||||
<div id="box9b" class="another_box"></div>
|
||||
</div>
|
||||
<div id="scroll_10" class="scroll_box">
|
||||
<div id="box10a" class="a_box"></div>
|
||||
<div id="box10b" class="another_box"></div>
|
||||
</div>
|
||||
<script>
|
||||
function doTest() {
|
||||
if (document.location.search != '?ref') {
|
||||
document.getElementById("scroll_1").scrollTo({left: 5, top: 0});
|
||||
document.getElementById("scroll_2").scrollTo({left: 0, top: 5});
|
||||
document.getElementById("scroll_3").scrollTo({left: 5, top: 5});
|
||||
document.getElementById("scroll_4").scrollTo({left: 5, top: 5});
|
||||
document.getElementById("scroll_5").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_6").scrollTo(0, 5);
|
||||
document.getElementById("scroll_7").scrollTo(5, 0);
|
||||
document.getElementById("scroll_8").scrollTo(0, 5);
|
||||
document.getElementById("scroll_9").scrollTo(5, 5);
|
||||
document.getElementById("scroll_10").scrollTo(0, 0);
|
||||
} else {
|
||||
document.getElementById("scroll_1").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_1").scrollBy({left: 5, top: 0});
|
||||
|
||||
document.getElementById("scroll_2").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_2").scrollBy({left: 0, top: 5});
|
||||
|
||||
document.getElementById("scroll_3").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_3").scrollBy({left: 5, top: 5});
|
||||
|
||||
document.getElementById("scroll_4").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_4").scrollBy({left: 5});
|
||||
document.getElementById("scroll_4").scrollBy({top: 5});
|
||||
|
||||
document.getElementById("scroll_5").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_5").scrollBy({left: 5, top: 5, behavior: "smooth"});
|
||||
// Expected to interrupt smooth scrolling
|
||||
document.getElementById("scroll_5").scrollBy({});
|
||||
|
||||
document.getElementById("scroll_6").scrollTo({left: 0, top: 0});
|
||||
document.getElementById("scroll_6").scrollBy({left: 5, behavior: "smooth"});
|
||||
// Expected to interrupt smooth scrolling and use the current position
|
||||
// As the default for the axis that are not specified in the target
|
||||
document.getElementById("scroll_6").scrollBy({top: 5, behavior: "smooth"});
|
||||
|
||||
document.getElementById("scroll_7").scrollTo(0, 0);
|
||||
document.getElementById("scroll_7").scrollBy(5, 0);
|
||||
|
||||
document.getElementById("scroll_8").scrollTo(0, 0);
|
||||
document.getElementById("scroll_8").scrollBy(0, 5);
|
||||
|
||||
document.getElementById("scroll_9").scrollTo(0, 0);
|
||||
document.getElementById("scroll_9").scrollBy(5, 5);
|
||||
|
||||
document.getElementById("scroll_10").scrollTo(0, 0);
|
||||
document.getElementById("scroll_10").scrollBy({left: 5, top: 5, behavior: "smooth"});
|
||||
// Expected to interrupt smooth scrolling
|
||||
document.getElementById("scroll_10").scrollBy(0, 0);
|
||||
}
|
||||
|
||||
// Allow smooth scrolling to complete before testing result
|
||||
setTimeout(function() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
}, 500);
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1010538, smooth scrolling expected</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body {
|
||||
color: black;
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
#a_box {
|
||||
position: relative;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
#another_box {
|
||||
position: relative;
|
||||
left: 2000px;
|
||||
top: 2000px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="a_box"></div>
|
||||
<div id="another_box"></div>
|
||||
<script>
|
||||
function doTest() {
|
||||
if (document.location.search != '?ref') {
|
||||
window.scrollTo(500, 500);
|
||||
window.scrollTo(window.scrollX, window.scrollY);
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
window.scrollTo({left: 0, top: 0, behavior: "instant"});
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -310,6 +310,7 @@ CSS_KEY(inline-grid, inline_grid)
|
|||
CSS_KEY(inline-table, inline_table)
|
||||
CSS_KEY(inset, inset)
|
||||
CSS_KEY(inside, inside)
|
||||
CSS_KEY(instant, instant)
|
||||
CSS_KEY(interpolatematrix, interpolatematrix)
|
||||
CSS_KEY(isolate, isolate)
|
||||
CSS_KEY(invert, invert)
|
||||
|
@ -486,6 +487,7 @@ CSS_KEY(small, small)
|
|||
CSS_KEY(small-caps, small_caps)
|
||||
CSS_KEY(small-caption, small_caption)
|
||||
CSS_KEY(smaller, smaller)
|
||||
CSS_KEY(smooth, smooth)
|
||||
CSS_KEY(soft, soft)
|
||||
CSS_KEY(soft-light, soft_light)
|
||||
CSS_KEY(solid, solid)
|
||||
|
|
|
@ -2974,6 +2974,16 @@ CSS_PROP_POSITION(
|
|||
nullptr,
|
||||
offsetof(nsStylePosition, mOffset),
|
||||
eStyleAnimType_Sides_Right)
|
||||
CSS_PROP_DISPLAY(
|
||||
scroll-behavior,
|
||||
scroll_behavior,
|
||||
ScrollBehavior,
|
||||
CSS_PROPERTY_PARSE_VALUE,
|
||||
"layout.css.scroll-behavior.property-enabled",
|
||||
VARIANT_HK,
|
||||
kScrollBehaviorKTable,
|
||||
CSS_PROP_NO_OFFSET,
|
||||
eStyleAnimType_None)
|
||||
CSS_PROP_BACKENDONLY(
|
||||
size,
|
||||
size,
|
||||
|
|
|
@ -1590,6 +1590,13 @@ const KTableValue nsCSSProps::kResizeKTable[] = {
|
|||
eCSSKeyword_UNKNOWN,-1
|
||||
};
|
||||
|
||||
const KTableValue nsCSSProps::kScrollBehaviorKTable[] = {
|
||||
eCSSKeyword_auto, NS_STYLE_SCROLL_BEHAVIOR_AUTO,
|
||||
eCSSKeyword_instant, NS_STYLE_SCROLL_BEHAVIOR_INSTANT,
|
||||
eCSSKeyword_smooth, NS_STYLE_SCROLL_BEHAVIOR_SMOOTH,
|
||||
eCSSKeyword_UNKNOWN,-1
|
||||
};
|
||||
|
||||
const KTableValue nsCSSProps::kStackSizingKTable[] = {
|
||||
eCSSKeyword_ignore, NS_STYLE_STACK_SIZING_IGNORE,
|
||||
eCSSKeyword_stretch_to_fit, NS_STYLE_STACK_SIZING_STRETCH_TO_FIT,
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче