Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-10-21 16:02:54 +02:00
commit a2700c231c
155 changed files with 5050 additions and 1412 deletions

View File

@ -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}) => {

View File

@ -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();

View File

@ -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]

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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"

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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)
{

View File

@ -885,7 +885,7 @@ class CSPReportSenderRunnable MOZ_FINAL : public nsRunnable
nsString mScriptSample;
uint32_t mLineNum;
uint64_t mInnerWindowID;
nsCSPContext* mCSPContext;
nsRefPtr<nsCSPContext> mCSPContext;
};
/**

View File

@ -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;

View File

@ -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();

View File

@ -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 {

View File

@ -8,6 +8,7 @@
#include "GMPCallbackBase.h"
#include "gmp-decryption.h"
#include "nsString.h"
namespace mp4_demuxer {
class CryptoSample;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -48,6 +48,8 @@ public:
private:
~GeckoMediaPluginService();
void ClearStorage();
GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags);

View File

@ -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);
}

View File

@ -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'

View File

@ -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 {

View File

@ -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:

View File

@ -195,7 +195,7 @@ private:
ClearConsoleData();
bool
ShouldIncludeStackrace(MethodName aMethodName);
ShouldIncludeStackTrace(MethodName aMethodName);
nsCOMPtr<nsPIDOMWindow> mWindow;
nsCOMPtr<nsITimer> mTimer;

View File

@ -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);
}

View File

@ -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,

View File

@ -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.

View File

@ -3061,7 +3061,6 @@ public:
ClearInputStream()
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(mBlobImpl);
mBlobImpl = nullptr;
}

View File

@ -86,7 +86,7 @@ FileImplSnapshot::AssertSanity()
#endif // DEBUG
NS_IMPL_ISUPPORTS_INHERITED0(FileImplSnapshot, FileImpl)
NS_IMPL_ISUPPORTS_INHERITED(FileImplSnapshot, FileImpl, PIFileImplSnapshot)
void
FileImplSnapshot::Unlink()

View File

@ -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;

View File

@ -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]

View File

@ -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>

View File

@ -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;
}

View File

@ -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);
},
};

View File

@ -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]

View File

@ -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!");
}

View File

@ -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

View File

@ -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]

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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__

View File

@ -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")

View File

@ -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

View File

@ -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>

View File

@ -76,6 +76,7 @@ interface DOMMatrixReadOnly {
DOMPoint transformPoint(optional DOMPointInit point);
[Throws] Float32Array toFloat32Array();
[Throws] Float64Array toFloat64Array();
stringifier;
};
[Pref="layout.css.DOMMatrix.enabled",

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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()
{

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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()
{

View File

@ -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;

View File

@ -371,6 +371,7 @@ BufferTextureHost::SetCompositor(Compositor* aCompositor)
it->SetCompositor(aCompositor);
it = it->GetNextSibling();
}
mFirstSource = nullptr;
mCompositor = aCompositor;
}

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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);
}
};

View File

@ -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

View File

@ -21,6 +21,7 @@
#include "nsIAnonymousContentCreator.h"
#include "nsFrameManager.h"
#include "nsIDocument.h"
#include "ScrollbarStyles.h"
struct nsFrameItems;
struct nsAbsoluteItems;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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,

View File

@ -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>

View File

@ -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]

View File

@ -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>

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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) {

View File

@ -98,6 +98,8 @@ public:
void PostScrolledAreaEvent();
void FireScrolledAreaEvent();
bool IsSmoothScrollingEnabled();
class ScrollEvent : public nsRunnable {
public:
NS_DECL_NSIRUNNABLE

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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,

View File

@ -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,

Some files were not shown because too many files have changed in this diff Show More