merge mozilla-inbound to mozilla-central. r=merge a=merge

MozReview-Commit-ID: DZowmCXo7Q
This commit is contained in:
Sebastian Hengst 2017-04-17 16:21:05 +02:00
commit 4bd7a206de
52 changed files with 1812 additions and 266 deletions

View File

@ -30,4 +30,4 @@ pref("app.update.badgeWaitTime", 0);
// Number of usages of the web console or scratchpad.
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
pref("devtools.selfxss.count", 0);
pref("devtools.selfxss.count", 5);

View File

@ -103,6 +103,17 @@ body,
text-overflow: ellipsis;
}
.learn-more-link {
color: var(--theme-highlight-blue);
cursor: pointer;
margin: 0 5px;
white-space: nowrap;
}
.learn-more-link:hover {
text-decoration: underline;
}
/* Status bar */
.status-bar-label {
@ -152,6 +163,8 @@ body,
.notice-perf-message {
margin-top: 2px;
display: flex;
align-items: center;
}
.requests-list-perf-notice-button {
@ -868,18 +881,6 @@ body,
color: var(--theme-selection-color);
}
.learn-more-link {
color: var(--theme-highlight-blue);
cursor: pointer;
margin: 0 5px;
white-space: nowrap;
flex-grow: 1;
}
.learn-more-link:hover {
text-decoration: underline;
}
/* Headers tabpanel */
.headers-overview {
@ -930,6 +931,10 @@ body,
width: auto!important;
}
.headers-summary .learn-more-link {
flex-grow: 1;
}
/* Response tabpanel */
.response-error-header {
@ -1163,6 +1168,14 @@ body,
height: 100%;
}
.statistics-panel .learn-more-link {
font-weight: 400;
}
.statistics-panel .table-chart-title {
display: flex;
}
.pie-table-chart-container {
display: flex;
justify-content: center;

View File

@ -6,6 +6,7 @@
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
@ -14,6 +15,10 @@ const Actions = require("../actions/index");
const { ACTIVITY_TYPE } = require("../constants");
const { NetMonitorController } = require("../netmonitor-controller");
const { L10N } = require("../utils/l10n");
const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
// Components
const MDNLink = createFactory(require("./mdn-link"));
const { button, div, span } = DOM;
@ -54,7 +59,8 @@ const RequestListEmptyNotice = createClass({
"data-standalone": true,
onClick: this.props.onPerfClick,
}),
span(null, L10N.getStr("netmonitor.perfNotice2"))
span(null, L10N.getStr("netmonitor.perfNotice2")),
MDNLink({ url: getPerformanceAnalysisURL() })
)
);
}

View File

@ -4,8 +4,10 @@
"use strict";
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
@ -19,6 +21,10 @@ const {
getTimeWithDecimals
} = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
// Components
const MDNLink = createFactory(require("./mdn-link"));
const { button, div } = DOM;
const MediaQueryList = window.matchMedia("(min-width: 700px)");
@ -48,6 +54,10 @@ const StatisticsPanel = createClass({
};
},
componentWillMount() {
this.mdnLinkContainerNodes = new Map();
},
componentDidUpdate(prevProps) {
MediaQueryList.addListener(this.onLayoutChange);
@ -68,10 +78,35 @@ const StatisticsPanel = createClass({
title: CHARTS_CACHE_DISABLED,
data: ready ? this.sanitizeChartDataSource(requests, true) : null,
});
this.createMDNLink("primedCacheChart", getPerformanceAnalysisURL());
this.createMDNLink("emptyCacheChart", getPerformanceAnalysisURL());
},
componentWillUnmount() {
MediaQueryList.removeListener(this.onLayoutChange);
this.unmountMDNLinkContainers();
},
createMDNLink(chartId, url) {
if (this.mdnLinkContainerNodes.has(chartId)) {
ReactDOM.unmountComponentAtNode(this.mdnLinkContainerNodes.get(chartId));
}
// MDNLink is a React component but Chart isn't. To get the link
// into the chart we mount a new ReactDOM at the appropriate
// location after the chart has been created.
let title = this.refs[chartId].querySelector(".table-chart-title");
let containerNode = document.createElement("span");
title.appendChild(containerNode);
ReactDOM.render(MDNLink({ url }), containerNode);
this.mdnLinkContainerNodes.set(chartId, containerNode);
},
unmountMDNLinkContainers() {
for (let [, node] of this.mdnLinkContainerNodes) {
ReactDOM.unmountComponentAtNode(node);
}
},
createChart({ id, title, data }) {

View File

@ -140,12 +140,10 @@ const SUPPORTED_HTTP_CODES = [
"511"
];
const MDN_URL = "https://developer.mozilla.org/docs/";
const GA_PARAMS =
"?utm_source=mozilla&utm_medium=devtools-netmonitor&utm_campaign=default";
const NETWORK_MONITOR_TIMINGS_MDN_URL =
"https://developer.mozilla.org/docs/Tools/Network_Monitor#Timings";
/**
* Get the MDN URL for the specified header.
*
@ -158,7 +156,7 @@ function getHeadersURL(header) {
let idx = SUPPORTED_HEADERS.findIndex(item =>
item.toLowerCase() === lowerCaseHeader);
return idx > -1 ?
`https://developer.mozilla.org/docs/Web/HTTP/Headers/${SUPPORTED_HEADERS[idx] + GA_PARAMS}` : null;
`${MDN_URL}Web/HTTP/Headers/${SUPPORTED_HEADERS[idx] + GA_PARAMS}` : null;
}
/**
@ -170,7 +168,8 @@ function getHeadersURL(header) {
*/
function getHTTPStatusCodeURL(statusCode) {
let idx = SUPPORTED_HTTP_CODES.indexOf(statusCode);
return idx > -1 ? `https://developer.mozilla.org/docs/Web/HTTP/Status/${SUPPORTED_HTTP_CODES[idx] + GA_PARAMS}` : null;
return idx > -1 ?
`${MDN_URL}Web/HTTP/Status/${SUPPORTED_HTTP_CODES[idx] + GA_PARAMS}` : null;
}
/**
@ -179,11 +178,21 @@ function getHTTPStatusCodeURL(statusCode) {
* @return {string} the MDN URL of the Timings tag for Network Monitor.
*/
function getNetMonitorTimingsURL() {
return NETWORK_MONITOR_TIMINGS_MDN_URL;
return `${MDN_URL}Tools/Network_Monitor${GA_PARAMS}#Timings`;
}
/**
* Get the MDN URL for Performance Analysis
*
* @return {string} The MDN URL for the documentation of Performance Analysis.
*/
function getPerformanceAnalysisURL() {
return `${MDN_URL}Tools/Network_Monitor${GA_PARAMS}#Performance_analysis`;
}
module.exports = {
getHeadersURL,
getHTTPStatusCodeURL,
getNetMonitorTimingsURL,
getPerformanceAnalysisURL,
};

View File

@ -226,6 +226,7 @@ support-files =
!/image/test/mochitest/blue.png
script_bug1238440.js
intersectionobserver_iframe.html
intersectionobserver_cross_domain_iframe.html
intersectionobserver_window.html
[test_anchor_area_referrer.html]

View File

@ -369,7 +369,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.display = 'none';
@ -379,7 +379,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(0);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.display = 'block';
@ -389,7 +389,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
rootEl.style.display = 'none';
@ -399,7 +399,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(0);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
rootEl.style.display = 'block';
@ -409,7 +409,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
},
], done);
});
@ -430,7 +430,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.left = '-40px';
@ -440,7 +440,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(0);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
parentEl.style.overflow = 'visible';
@ -450,7 +450,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
}
], done);
});
@ -471,7 +471,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be.greaterThan(0.5);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.left = '-15px';
@ -481,7 +481,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be.lessThan(0.5);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.left = '-25px';
@ -498,7 +498,7 @@ limitations under the License.
expect(records.length).to.be(1);
expect(records[0].intersectionRatio).to.be(0.5);
done();
}, ASYNC_TIMEOUT);
});
}
], done);
@ -534,7 +534,7 @@ limitations under the License.
expect(records[1].target).to.be(targetEl2);
expect(records[1].intersectionRatio).to.be(0.75);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.top = '0px';
@ -554,7 +554,7 @@ limitations under the License.
expect(records[2].target).to.be(targetEl3);
expect(records[2].intersectionRatio).to.be(0.25);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.top = '0px';
@ -574,7 +574,7 @@ limitations under the License.
expect(records[2].target).to.be(targetEl3);
expect(records[2].intersectionRatio).to.be(0.75);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.top = '0px';
@ -590,7 +590,7 @@ limitations under the License.
expect(records[0].target).to.be(targetEl3);
expect(records[0].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
}
], done);
});
@ -709,7 +709,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(0);
expect(records[0].target).to.be(targetEl2);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.top = '0px';
@ -726,7 +726,7 @@ limitations under the License.
expect(records[1].intersectionRatio).to.be(0);
expect(records[1].target).to.be(targetEl2);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.top = '-20px';
@ -740,7 +740,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(0);
expect(records[0].target).to.be(targetEl2);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl3.style.top = '20px';
@ -759,7 +759,7 @@ limitations under the License.
expect(records[1].intersectionRatio).to.be(0);
expect(records[1].target).to.be(targetEl4);
done();
}, ASYNC_TIMEOUT);
});
}
], done);
@ -785,7 +785,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(0);
expect(records[0].isIntersecting).to.be.ok();
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
targetEl1.style.top = '-1px';
@ -795,7 +795,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(0);
expect(records[0].isIntersecting).to.be(false);
done();
}, ASYNC_TIMEOUT);
});
}
], done);
});
@ -830,7 +830,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(1);
expect(records[0].target).to.be(targetEl1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
grandParentEl.remove();
@ -841,7 +841,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(0);
expect(records[0].target).to.be(targetEl1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
rootEl.appendChild(targetEl1);
@ -852,7 +852,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(1);
expect(records[0].target).to.be(targetEl1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
rootEl.remove();
@ -863,7 +863,7 @@ limitations under the License.
expect(records[0].intersectionRatio).to.be(0);
expect(records[0].target).to.be(targetEl1);
done();
}, ASYNC_TIMEOUT);
});
}
], done);
});
@ -918,10 +918,12 @@ limitations under the License.
io.observe(targetEl1);
io.observe(targetEl1);
spy.waitForNotification(function() {
callDelayed(function () {
expect(spy.callCount).to.be(1);
done();
}, ASYNC_TIMEOUT * 3);
}, ASYNC_TIMEOUT);
});
});
});
@ -967,7 +969,7 @@ limitations under the License.
targetEl4.src = "intersectionobserver_iframe.html";
});
it('rootBounds should is set to null for cross-origin observations', function(done) {
it('rootBounds is set to null for cross-origin observations', function(done) {
window.onmessage = function (e) {
expect(e.data).to.be(true);
@ -982,8 +984,7 @@ limitations under the License.
describe('takeRecords', function() {
it('supports getting records before the callback is invoked',
function(done) {
it('supports getting records before the callback is invoked', function(done) {
var lastestRecords = [];
io = new IntersectionObserver(function(records) {
@ -991,10 +992,12 @@ limitations under the License.
}, {root: rootEl});
io.observe(targetEl1);
window.requestAnimationFrame && requestAnimationFrame(function() {
window.requestAnimationFrame && requestAnimationFrame(function wait() {
lastestRecords = lastestRecords.concat(io.takeRecords());
});
if (!lastestRecords.length) {
requestAnimationFrame(wait);
return;
}
callDelayed(function() {
expect(lastestRecords.length).to.be(1);
expect(lastestRecords[0].intersectionRatio).to.be(1);
@ -1004,6 +1007,7 @@ limitations under the License.
});
});
describe('unobserve', function() {
@ -1027,7 +1031,7 @@ limitations under the License.
expect(records[1].target).to.be(targetEl2);
expect(records[1].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
io.unobserve(targetEl1);
@ -1040,7 +1044,7 @@ limitations under the License.
expect(records[0].target).to.be(targetEl2);
expect(records[0].intersectionRatio).to.be(0);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
io.unobserve(targetEl2);
@ -1079,7 +1083,7 @@ limitations under the License.
expect(records[1].target).to.be(targetEl2);
expect(records[1].intersectionRatio).to.be(1);
done();
}, ASYNC_TIMEOUT);
});
},
function(done) {
io.disconnect();

View File

@ -5463,12 +5463,12 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas(
uint32_t modifiedFlags = aImage.mDrawingFlags | imgIContainer::FLAG_CLAMP;
CSSIntSize sz(scaledImageSize.width, scaledImageSize.height); // XXX hmm is scaledImageSize really in CSS pixels?
SVGImageContext svgContext(Some(sz), Nothing(), CurrentState().globalAlpha);
SVGImageContext svgContext(Some(sz));
auto result = aImage.mImgContainer->
Draw(context, scaledImageSize,
ImageRegion::Create(gfxRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height)),
aImage.mWhichFrame, SamplingFilter::GOOD, Some(svgContext), modifiedFlags, 1.0);
aImage.mWhichFrame, SamplingFilter::GOOD, Some(svgContext), modifiedFlags, CurrentState().globalAlpha);
if (result != DrawResult::SUCCESS) {
NS_WARNING("imgIContainer::Draw failed");

View File

@ -2813,6 +2813,18 @@ ShutdownObserver::Observe(nsISupports* aSubject,
MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID));
MOZ_ASSERT(gInstance);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (NS_WARN_IF(!observerService)) {
return NS_ERROR_FAILURE;
}
// Unregister ourselves from the observer service first to make sure the
// nested event loop below will not cause re-entrancy issues.
Unused <<
observerService->RemoveObserver(this,
PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID);
QuotaManagerService* qms = QuotaManagerService::Get();
MOZ_ASSERT(qms);

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/StorageManagerBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ErrorResult.h"
#include "nsContentPermissionHelper.h"
#include "nsIQuotaCallbacks.h"
#include "nsIQuotaRequests.h"
#include "nsPIDOMWindow.h"
@ -22,10 +23,21 @@ namespace dom {
namespace {
// This class is used to get quota usage callback.
class EstimateResolver final
: public nsIQuotaUsageCallback
// This class is used to get quota usage, request persist and check persisted
// status callbacks.
class RequestResolver final
: public nsIQuotaCallback
, public nsIQuotaUsageCallback
{
public:
enum Type
{
Estimate,
Persist,
Persisted
};
private:
class FinishWorkerRunnable;
// If this resolver was created for a window then mPromise must be non-null.
@ -35,44 +47,69 @@ class EstimateResolver final
nsresult mResultCode;
StorageEstimate mStorageEstimate;
const Type mType;
bool mPersisted;
public:
explicit EstimateResolver(Promise* aPromise)
RequestResolver(Type aType, Promise* aPromise)
: mPromise(aPromise)
, mResultCode(NS_OK)
, mType(aType)
, mPersisted(false)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPromise);
}
explicit EstimateResolver(PromiseWorkerProxy* aProxy)
RequestResolver(Type aType, PromiseWorkerProxy* aProxy)
: mProxy(aProxy)
, mResultCode(NS_OK)
, mType(aType)
, mPersisted(false)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aProxy);
}
Type
GetType() const
{
return mType;
}
void
ResolveOrReject(Promise* aPromise);
ResolveOrReject();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIQUOTACALLBACK
NS_DECL_NSIQUOTAUSAGECALLBACK
private:
~EstimateResolver()
~RequestResolver()
{ }
nsresult
GetStorageEstimate(nsIVariant* aResult);
nsresult
GetPersisted(nsIVariant* aResult);
template <typename T>
nsresult
OnCompleteOrUsageResult(T* aRequest);
nsresult
Finish();
};
// This class is used to return promise on worker thread.
class EstimateResolver::FinishWorkerRunnable final
class RequestResolver::FinishWorkerRunnable final
: public WorkerRunnable
{
RefPtr<EstimateResolver> mResolver;
RefPtr<RequestResolver> mResolver;
public:
explicit FinishWorkerRunnable(EstimateResolver* aResolver)
explicit FinishWorkerRunnable(RequestResolver* aResolver)
: WorkerRunnable(aResolver->mProxy->GetWorkerPrivate())
, mResolver(aResolver)
{
@ -80,11 +117,11 @@ public:
MOZ_ASSERT(aResolver);
}
virtual bool
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
};
class EstimateWorkerMainThreadRunnable
class EstimateWorkerMainThreadRunnable final
: public WorkerMainThreadRunnable
{
RefPtr<PromiseWorkerProxy> mProxy;
@ -101,10 +138,69 @@ public:
MOZ_ASSERT(aProxy);
}
virtual bool
bool
MainThreadRun() override;
};
class PersistedWorkerMainThreadRunnable final
: public WorkerMainThreadRunnable
{
RefPtr<PromiseWorkerProxy> mProxy;
public:
PersistedWorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aProxy)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("StorageManager :: Persisted"))
, mProxy(aProxy)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aProxy);
}
bool
MainThreadRun() override;
};
/*******************************************************************************
* PersistentStoragePermissionRequest
******************************************************************************/
class PersistentStoragePermissionRequest final
: public nsIContentPermissionRequest
{
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<Promise> mPromise;
nsCOMPtr<nsIContentPermissionRequester> mRequester;
public:
PersistentStoragePermissionRequest(nsIPrincipal* aPrincipal,
nsPIDOMWindowInner* aWindow,
Promise* aPromise)
: mPrincipal(aPrincipal)
, mWindow(aWindow)
, mPromise(aPromise)
{
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aPromise);
mRequester = new nsContentPermissionRequester(mWindow);
}
nsresult
Start();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICONTENTPERMISSIONREQUEST
private:
~PersistentStoragePermissionRequest()
{ }
};
nsresult
GetUsageForPrincipal(nsIPrincipal* aPrincipal,
nsIQuotaUsageCallback* aCallback,
@ -119,7 +215,10 @@ GetUsageForPrincipal(nsIPrincipal* aPrincipal,
return NS_ERROR_FAILURE;
}
nsresult rv = qms->GetUsageForPrincipal(aPrincipal, aCallback, true, aRequest);
nsresult rv = qms->GetUsageForPrincipal(aPrincipal,
aCallback,
true,
aRequest);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -128,20 +227,240 @@ GetUsageForPrincipal(nsIPrincipal* aPrincipal,
};
nsresult
GetStorageEstimate(nsIQuotaUsageRequest* aRequest,
StorageEstimate& aStorageEstimate)
Persisted(nsIPrincipal* aPrincipal,
nsIQuotaCallback* aCallback,
nsIQuotaRequest** aRequest)
{
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aCallback);
MOZ_ASSERT(aRequest);
nsCOMPtr<nsIVariant> result;
nsresult rv = aRequest->GetResult(getter_AddRefs(result));
nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate();
if (NS_WARN_IF(!qms)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIQuotaRequest> request;
nsresult rv = qms->Persisted(aPrincipal, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// All the methods in nsIQuotaManagerService shouldn't synchronously fire
// any callbacks when they are being executed. Even when a result is ready,
// a new runnable should be dispatched to current thread to fire the callback
// asynchronously. It's safe to set the callback after we call Persisted().
MOZ_ALWAYS_SUCCEEDS(request->SetCallback(aCallback));
request.forget(aRequest);
return NS_OK;
};
already_AddRefed<Promise>
ExecuteOpOnMainOrWorkerThread(nsIGlobalObject* aGlobal,
RequestResolver::Type aType,
ErrorResult& aRv)
{
MOZ_ASSERT(aGlobal);
MOZ_ASSERT_IF(aType == RequestResolver::Type::Persist,
NS_IsMainThread());
RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
if (NS_WARN_IF(!promise)) {
return nullptr;
}
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
if (NS_WARN_IF(!window)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
MOZ_ASSERT(principal);
// Storage Standard 7. API
// If origin is an opaque origin, then reject promise with a TypeError.
if (principal->GetIsNullPrincipal()) {
promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
return promise.forget();
}
switch (aType) {
case RequestResolver::Type::Persisted: {
RefPtr<RequestResolver> resolver =
new RequestResolver(RequestResolver::Type::Persisted, promise);
RefPtr<nsIQuotaRequest> request;
aRv = Persisted(principal, resolver, getter_AddRefs(request));
break;
}
case RequestResolver::Type::Persist: {
RefPtr<PersistentStoragePermissionRequest> request =
new PersistentStoragePermissionRequest(principal, window, promise);
// In private browsing mode, no permission prompt.
if (nsContentUtils::IsInPrivateBrowsing(doc)) {
aRv = request->Cancel();
} else {
aRv = request->Start();
}
break;
}
case RequestResolver::Type::Estimate: {
RefPtr<RequestResolver> resolver =
new RequestResolver(RequestResolver::Type::Estimate, promise);
RefPtr<nsIQuotaUsageRequest> request;
aRv = GetUsageForPrincipal(principal,
resolver,
getter_AddRefs(request));
break;
}
default:
MOZ_CRASH("Invalid aRequest type!");
}
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return promise.forget();
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (NS_WARN_IF(!promiseProxy)) {
return nullptr;
}
switch (aType) {
case RequestResolver::Type::Estimate: {
RefPtr<EstimateWorkerMainThreadRunnable> runnnable =
new EstimateWorkerMainThreadRunnable(promiseProxy->GetWorkerPrivate(),
promiseProxy);
runnnable->Dispatch(Terminating, aRv);
break;
}
case RequestResolver::Type::Persisted: {
RefPtr<PersistedWorkerMainThreadRunnable> runnnable =
new PersistedWorkerMainThreadRunnable(promiseProxy->GetWorkerPrivate(),
promiseProxy);
runnnable->Dispatch(Terminating, aRv);
break;
}
default:
MOZ_CRASH("Invalid aRequest type");
}
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return promise.forget();
};
} // namespace
/*******************************************************************************
* Local class implementations
******************************************************************************/
void
RequestResolver::ResolveOrReject()
{
class MOZ_STACK_CLASS AutoCleanup final
{
RefPtr<PromiseWorkerProxy> mProxy;
public:
explicit AutoCleanup(PromiseWorkerProxy* aProxy)
: mProxy(aProxy)
{
MOZ_ASSERT(aProxy);
}
~AutoCleanup()
{
MOZ_ASSERT(mProxy);
mProxy->CleanUp();
}
};
RefPtr<Promise> promise;
Maybe<AutoCleanup> autoCleanup;
if (mPromise) {
promise = mPromise;
} else {
MOZ_ASSERT(mProxy);
promise = mProxy->WorkerPromise();
// Only clean up for worker case.
autoCleanup.emplace(mProxy);
}
MOZ_ASSERT(promise);
if (mType == Type::Estimate) {
if (NS_SUCCEEDED(mResultCode)) {
promise->MaybeResolve(mStorageEstimate);
} else {
promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
}
return;
}
MOZ_ASSERT(mType == Type::Persist || mType == Type::Persisted);
if (NS_SUCCEEDED(mResultCode)) {
promise->MaybeResolve(mPersisted);
} else {
promise->MaybeResolve(false);
}
}
NS_IMPL_ISUPPORTS(RequestResolver, nsIQuotaUsageCallback, nsIQuotaCallback)
nsresult
RequestResolver::GetStorageEstimate(nsIVariant* aResult)
{
MOZ_ASSERT(aResult);
MOZ_ASSERT(mType == Type::Estimate);
#ifdef DEBUG
uint16_t dataType;
MOZ_ALWAYS_SUCCEEDS(aResult->GetDataType(&dataType));
MOZ_ASSERT(dataType == nsIDataType::VTYPE_INTERFACE_IS);
#endif
nsID* iid;
nsCOMPtr<nsISupports> supports;
rv = result->GetAsInterface(&iid, getter_AddRefs(supports));
nsresult rv = aResult->GetAsInterface(&iid, getter_AddRefs(supports));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -153,58 +472,93 @@ GetStorageEstimate(nsIQuotaUsageRequest* aRequest,
MOZ_ASSERT(originUsageResult);
MOZ_ALWAYS_SUCCEEDS(
originUsageResult->GetUsage(&aStorageEstimate.mUsage.Construct()));
originUsageResult->GetUsage(&mStorageEstimate.mUsage.Construct()));
MOZ_ALWAYS_SUCCEEDS(
originUsageResult->GetLimit(&aStorageEstimate.mQuota.Construct()));
originUsageResult->GetLimit(&mStorageEstimate.mQuota.Construct()));
return NS_OK;
}
} // namespace
/*******************************************************************************
* Local class implementations
******************************************************************************/
void
EstimateResolver::ResolveOrReject(Promise* aPromise)
nsresult
RequestResolver::GetPersisted(nsIVariant* aResult)
{
MOZ_ASSERT(aPromise);
MOZ_ASSERT(aResult);
MOZ_ASSERT(mType == Type::Persist || mType == Type::Persisted);
if (NS_SUCCEEDED(mResultCode)) {
aPromise->MaybeResolve(mStorageEstimate);
} else {
aPromise->MaybeReject(mResultCode);
#ifdef DEBUG
uint16_t dataType;
MOZ_ALWAYS_SUCCEEDS(aResult->GetDataType(&dataType));
#endif
if (mType == Type::Persist) {
MOZ_ASSERT(dataType == nsIDataType::VTYPE_VOID);
mPersisted = true;
return NS_OK;
}
MOZ_ASSERT(dataType == nsIDataType::VTYPE_BOOL);
bool persisted;
nsresult rv = aResult->GetAsBool(&persisted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mPersisted = persisted;
return NS_OK;
}
NS_IMPL_ISUPPORTS(EstimateResolver, nsIQuotaUsageCallback)
NS_IMETHODIMP
EstimateResolver::OnUsageResult(nsIQuotaUsageRequest *aRequest)
template <typename T>
nsresult
RequestResolver::OnCompleteOrUsageResult(T* aRequest)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequest);
nsresult rv = aRequest->GetResultCode(&mResultCode);
nsresult resultCode;
nsresult rv = aRequest->GetResultCode(&resultCode);
if (NS_WARN_IF(NS_FAILED(rv))) {
mResultCode = rv;
} else if (NS_SUCCEEDED(mResultCode)) {
rv = GetStorageEstimate(aRequest, mStorageEstimate);
if (NS_WARN_IF(NS_FAILED(rv))) {
mResultCode = rv;
}
return rv;
}
if (NS_FAILED(resultCode)) {
return resultCode;
}
nsCOMPtr<nsIVariant> result;
rv = aRequest->GetResult(getter_AddRefs(result));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mType == Type::Estimate) {
rv = GetStorageEstimate(result);
} else {
MOZ_ASSERT(mType == Type::Persist || mType == Type::Persisted);
rv = GetPersisted(result);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
RequestResolver::Finish()
{
// In a main thread request.
if (!mProxy) {
MOZ_ASSERT(mPromise);
ResolveOrReject(mPromise);
ResolveOrReject();
return NS_OK;
}
{
// In a worker thread request.
MutexAutoLock lock(mProxy->Lock());
@ -216,26 +570,53 @@ EstimateResolver::OnUsageResult(nsIQuotaUsageRequest *aRequest)
if (NS_WARN_IF(!runnable->Dispatch())) {
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
NS_IMETHODIMP
RequestResolver::OnComplete(nsIQuotaRequest *aRequest)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequest);
mResultCode = OnCompleteOrUsageResult(aRequest);
nsresult rv = Finish();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
RequestResolver::OnUsageResult(nsIQuotaUsageRequest *aRequest)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequest);
mResultCode = OnCompleteOrUsageResult(aRequest);
nsresult rv = Finish();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
bool
EstimateResolver::
RequestResolver::
FinishWorkerRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<PromiseWorkerProxy> proxy = mResolver->mProxy;
MOZ_ASSERT(proxy);
RefPtr<Promise> promise = proxy->WorkerPromise();
MOZ_ASSERT(promise);
mResolver->ResolveOrReject(promise);
proxy->CleanUp();
MOZ_ASSERT(mResolver);
mResolver->ResolveOrReject();
return true;
}
@ -257,7 +638,8 @@ EstimateWorkerMainThreadRunnable::MainThreadRun()
MOZ_ASSERT(principal);
RefPtr<EstimateResolver> resolver = new EstimateResolver(mProxy);
RefPtr<RequestResolver> resolver =
new RequestResolver(RequestResolver::Type::Estimate, mProxy);
RefPtr<nsIQuotaUsageRequest> request;
nsresult rv =
@ -269,6 +651,156 @@ EstimateWorkerMainThreadRunnable::MainThreadRun()
return true;
}
bool
PersistedWorkerMainThreadRunnable::MainThreadRun()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPrincipal> principal;
{
MutexAutoLock lock(mProxy->Lock());
if (mProxy->CleanedUp()) {
return true;
}
principal = mProxy->GetWorkerPrivate()->GetPrincipal();
}
MOZ_ASSERT(principal);
RefPtr<RequestResolver> resolver =
new RequestResolver(RequestResolver::Type::Persisted, mProxy);
RefPtr<nsIQuotaRequest> request;
nsresult rv = Persisted(principal, resolver, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return true;
}
nsresult
PersistentStoragePermissionRequest::Start()
{
MOZ_ASSERT(NS_IsMainThread());
// Grant permission if pref'ed on.
if (Preferences::GetBool("dom.storageManager.prompt.testing", false)) {
if (Preferences::GetBool("dom.storageManager.prompt.testing.allow",
false)) {
return Allow(JS::UndefinedHandleValue);
}
return Cancel();
}
return nsContentPermissionUtils::AskPermission(this, mWindow);
}
NS_IMPL_ISUPPORTS(PersistentStoragePermissionRequest,
nsIContentPermissionRequest)
NS_IMETHODIMP
PersistentStoragePermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(mPrincipal);
NS_ADDREF(*aPrincipal = mPrincipal);
return NS_OK;
}
NS_IMETHODIMP
PersistentStoragePermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequestingWindow);
MOZ_ASSERT(mWindow);
NS_ADDREF(*aRequestingWindow = mWindow);
return NS_OK;
}
NS_IMETHODIMP
PersistentStoragePermissionRequest::GetElement(nsIDOMElement** aElement)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aElement);
*aElement = nullptr;
return NS_OK;
}
NS_IMETHODIMP
PersistentStoragePermissionRequest::Cancel()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPromise);
RefPtr<RequestResolver> resolver =
new RequestResolver(RequestResolver::Type::Persisted, mPromise);
RefPtr<nsIQuotaRequest> request;
return Persisted(mPrincipal, resolver, getter_AddRefs(request));
}
NS_IMETHODIMP
PersistentStoragePermissionRequest::Allow(JS::HandleValue aChoices)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<RequestResolver> resolver =
new RequestResolver(RequestResolver::Type::Persist, mPromise);
nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate();
if (NS_WARN_IF(!qms)) {
return NS_ERROR_FAILURE;
}
RefPtr<nsIQuotaRequest> request;
nsresult rv = qms->Persist(mPrincipal, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ALWAYS_SUCCEEDS(request->SetCallback(resolver));
return NS_OK;
}
NS_IMETHODIMP
PersistentStoragePermissionRequest::GetRequester(
nsIContentPermissionRequester** aRequester)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequester);
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
requester.forget(aRequester);
return NS_OK;
}
NS_IMETHODIMP
PersistentStoragePermissionRequest::GetTypes(nsIArray** aTypes)
{
MOZ_ASSERT(aTypes);
nsTArray<nsString> emptyOptions;
return nsContentPermissionUtils::CreatePermissionArray(
NS_LITERAL_CSTRING("persistent-storage"),
NS_LITERAL_CSTRING("unused"),
emptyOptions,
aTypes);
}
/*******************************************************************************
* StorageManager
******************************************************************************/
@ -283,64 +815,34 @@ StorageManager::~StorageManager()
{
}
already_AddRefed<Promise>
StorageManager::Persisted(ErrorResult& aRv)
{
MOZ_ASSERT(mOwner);
return ExecuteOpOnMainOrWorkerThread(mOwner,
RequestResolver::Type::Persisted,
aRv);
}
already_AddRefed<Promise>
StorageManager::Persist(ErrorResult& aRv)
{
MOZ_ASSERT(mOwner);
return ExecuteOpOnMainOrWorkerThread(mOwner,
RequestResolver::Type::Persist,
aRv);
}
already_AddRefed<Promise>
StorageManager::Estimate(ErrorResult& aRv)
{
MOZ_ASSERT(mOwner);
RefPtr<Promise> promise = Promise::Create(mOwner, aRv);
if (NS_WARN_IF(!promise)) {
return nullptr;
}
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mOwner);
if (NS_WARN_IF(!window)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
MOZ_ASSERT(principal);
RefPtr<EstimateResolver> resolver = new EstimateResolver(promise);
RefPtr<nsIQuotaUsageRequest> request;
nsresult rv =
GetUsageForPrincipal(principal, resolver, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
return promise.forget();
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (NS_WARN_IF(!promiseProxy)) {
return nullptr;
}
RefPtr<EstimateWorkerMainThreadRunnable> runnnable =
new EstimateWorkerMainThreadRunnable(promiseProxy->GetWorkerPrivate(),
promiseProxy);
runnnable->Dispatch(Terminating, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return promise.forget();
return ExecuteOpOnMainOrWorkerThread(mOwner,
RequestResolver::Type::Estimate,
aRv);
}
// static

View File

@ -39,6 +39,12 @@ public:
}
// WebIDL
already_AddRefed<Promise>
Persisted(ErrorResult& aRv);
already_AddRefed<Promise>
Persist(ErrorResult& aRv);
already_AddRefed<Promise>
Estimate(ErrorResult& aRv);

View File

@ -7,6 +7,10 @@
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Quota Manager")
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
XPCSHELL_TESTS_MANIFESTS += [
'test/unit/xpcshell.ini'
]

View File

@ -0,0 +1,10 @@
[DEFAULT]
skip-if = (buildapp != "browser")
support-files =
head.js
browserHelpers.js
browser_permissionsPrompt.html
[browser_permissionsPromptAllow.js]
[browser_permissionsPromptDeny.js]
[browser_permissionsPromptUnknown.js]

View File

@ -0,0 +1,49 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
var testResult;
function clearAllDatabases(callback)
{
let qms = SpecialPowers.Services.qms;
let principal = SpecialPowers.wrap(document).nodePrincipal;
let request = qms.clearStoragesForPrincipal(principal);
let cb = SpecialPowers.wrapCallback(callback);
request.callback = cb;
}
function runTest()
{
clearAllDatabases(() => {
testGenerator.next();
});
}
function finishTestNow()
{
if (testGenerator) {
testGenerator.return();
testGenerator = undefined;
}
}
function finishTest()
{
clearAllDatabases(() => {
setTimeout(finishTestNow, 0);
setTimeout(() => {
window.parent.postMessage(testResult, "*");
}, 0);
});
}
function continueToNextStep()
{
setTimeout(() => {
testGenerator.next();
}, 0);
}

View File

@ -0,0 +1,35 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<meta charset=UTF-8>
<title>Persistent-Storage Permission Prompt Test</title>
<script type="text/javascript">
function* testSteps()
{
SpecialPowers.pushPrefEnv({
"set": [["dom.storageManager.enabled", true],
["dom.storageManager.prompt.testing", false],
["dom.storageManager.prompt.testing.allow", false]]
}, continueToNextStep);
yield undefined;
navigator.storage.persist().then(result => {
testGenerator.next(result);
});
testResult = yield undefined;
finishTest();
}
</script>
<script type="text/javascript" src="browserHelpers.js"></script>
</head>
<body onload="runTest();" onunload="finishTestNow();"></body>
</html>

View File

@ -0,0 +1,65 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const testPageURL =
"https://example.com/browser/dom/quota/test/browser_permissionsPrompt.html";
add_task(function* testPermissionAllow() {
removePermission(testPageURL, "persistent-storage");
info("Creating tab");
gBrowser.selectedTab = gBrowser.addTab();
info("Loading test page: " + testPageURL);
gBrowser.selectedBrowser.loadURI(testPageURL);
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
registerPopupEventHandler("popupshowing", function () {
ok(true, "prompt showing");
});
registerPopupEventHandler("popupshown", function () {
ok(true, "prompt shown");
triggerMainCommand(this);
});
registerPopupEventHandler("popuphidden", function () {
ok(true, "prompt hidden");
});
yield promiseMessage(true, gBrowser);
is(getPermission(testPageURL, "persistent-storage"),
Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
"Correct permission set");
gBrowser.removeCurrentTab();
unregisterAllPopupEventHandlers();
// Keep persistent-storage permission for the next test.
});
add_task(function* testNoPermissionPrompt() {
info("Creating tab");
gBrowser.selectedTab = gBrowser.addTab();
info("Loading test page: " + testPageURL);
gBrowser.selectedBrowser.loadURI(testPageURL);
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
registerPopupEventHandler("popupshowing", function () {
ok(false, "Shouldn't show a popup this time");
});
registerPopupEventHandler("popupshown", function () {
ok(false, "Shouldn't show a popup this time");
});
registerPopupEventHandler("popuphidden", function () {
ok(false, "Shouldn't show a popup this time");
});
yield promiseMessage(true, gBrowser);
is(getPermission(testPageURL, "persistent-storage"),
Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
"Correct permission set");
gBrowser.removeCurrentTab();
unregisterAllPopupEventHandlers();
removePermission(testPageURL, "persistent-storage");
});

View File

@ -0,0 +1,95 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const testPageURL =
"https://example.com/browser/dom/quota/test/browser_permissionsPrompt.html";
add_task(function* testPermissionDenied() {
removePermission(testPageURL, "persistent-storage");
info("Creating tab");
gBrowser.selectedTab = gBrowser.addTab();
info("Loading test page: " + testPageURL);
gBrowser.selectedBrowser.loadURI(testPageURL);
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
registerPopupEventHandler("popupshowing", function () {
ok(true, "prompt showing");
});
registerPopupEventHandler("popupshown", function () {
ok(true, "prompt shown");
triggerSecondaryCommand(this);
});
registerPopupEventHandler("popuphidden", function () {
ok(true, "prompt hidden");
});
yield promiseMessage(false, gBrowser);
is(getPermission(testPageURL, "persistent-storage"),
Components.interfaces.nsIPermissionManager.DENY_ACTION,
"Correct permission set");
unregisterAllPopupEventHandlers();
gBrowser.removeCurrentTab();
// Keep persistent-storage permission for the next test.
});
add_task(function* testNoPermissionPrompt() {
info("Creating tab");
gBrowser.selectedTab = gBrowser.addTab();
info("Loading test page: " + testPageURL);
gBrowser.selectedBrowser.loadURI(testPageURL);
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
registerPopupEventHandler("popupshowing", function () {
ok(false, "Shouldn't show a popup this time");
});
registerPopupEventHandler("popupshown", function () {
ok(false, "Shouldn't show a popup this time");
});
registerPopupEventHandler("popuphidden", function () {
ok(false, "Shouldn't show a popup this time");
});
yield promiseMessage(false, gBrowser);
is(getPermission(testPageURL, "persistent-storage"),
Components.interfaces.nsIPermissionManager.DENY_ACTION,
"Correct permission set");
unregisterAllPopupEventHandlers();
gBrowser.removeCurrentTab();
removePermission(testPageURL, "persistent-storage");
});
add_task(function* testPermissionDeniedDismiss() {
info("Creating tab");
gBrowser.selectedTab = gBrowser.addTab();
info("Loading test page: " + testPageURL);
gBrowser.selectedBrowser.loadURI(testPageURL);
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
registerPopupEventHandler("popupshowing", function () {
ok(true, "prompt showing");
});
registerPopupEventHandler("popupshown", function () {
ok(true, "prompt shown");
// Dismiss permission prompt.
dismissNotification(this);
});
registerPopupEventHandler("popuphidden", function () {
ok(true, "prompt hidden");
});
yield promiseMessage(false, gBrowser);
is(getPermission(testPageURL, "persistent-storage"),
Components.interfaces.nsIPermissionManager.DENY_ACTION,
"Correct permission set");
unregisterAllPopupEventHandlers();
gBrowser.removeCurrentTab();
removePermission(testPageURL, "persistent-storage");
});

View File

@ -0,0 +1,40 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const testPageURL =
"https://example.com/browser/dom/quota/test/browser_permissionsPrompt.html";
add_task(function* testPermissionUnknownInPrivateWindow() {
removePermission(testPageURL, "persistent-storage");
info("Creating private window");
let win = yield BrowserTestUtils.openNewBrowserWindow({ private : true });
info("Creating private tab");
win.gBrowser.selectedTab = win.gBrowser.addTab();
info("Loading test page: " + testPageURL);
win.gBrowser.selectedBrowser.loadURI(testPageURL);
yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
registerPopupEventHandler("popupshowing", function () {
ok(false, "Shouldn't show a popup this time");
}, win);
registerPopupEventHandler("popupshown", function () {
ok(false, "Shouldn't show a popup this time");
}, win);
registerPopupEventHandler("popuphidden", function () {
ok(false, "Shouldn't show a popup this time");
}, win);
yield promiseMessage(false, win.gBrowser);
is(getPermission(testPageURL, "persistent-storage"),
Components.interfaces.nsIPermissionManager.UNKNOWN_ACTION,
"Correct permission set");
unregisterAllPopupEventHandlers(win);
win.gBrowser.removeCurrentTab();
win.close();
removePermission(testPageURL, "persistent-storage");
});

121
dom/quota/test/head.js Normal file
View File

@ -0,0 +1,121 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var gActiveListeners = {};
// These event (un)registration handlers only work for one window, DONOT use
// them with multiple windows.
function registerPopupEventHandler(eventName, callback, win)
{
if (!win) {
win = window;
}
gActiveListeners[eventName] = function (event) {
if (event.target != win.PopupNotifications.panel)
return;
win.PopupNotifications.panel.removeEventListener(
eventName,
gActiveListeners[eventName]);
delete gActiveListeners[eventName];
callback.call(win.PopupNotifications.panel);
}
win.PopupNotifications.panel.addEventListener(eventName,
gActiveListeners[eventName]);
}
function unregisterAllPopupEventHandlers(win)
{
if (!win) {
win = window;
}
for (let eventName in gActiveListeners) {
win.PopupNotifications.panel.removeEventListener(
eventName,
gActiveListeners[eventName]);
}
gActiveListeners = {};
}
function triggerMainCommand(popup, win)
{
if (!win) {
win = window;
}
info("triggering main command");
let notifications = popup.childNodes;
ok(notifications.length > 0, "at least one notification displayed");
let notification = notifications[0];
info("triggering command: " + notification.getAttribute("buttonlabel"));
EventUtils.synthesizeMouseAtCenter(notification.button, {}, win);
}
function triggerSecondaryCommand(popup, win)
{
if (!win) {
win = window;
}
info("triggering secondary command");
let notifications = popup.childNodes;
ok(notifications.length > 0, "at least one notification displayed");
let notification = notifications[0];
EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {}, win);
}
function dismissNotification(popup, win)
{
if (!win) {
win = window;
}
info("dismissing notification");
executeSoon(function () {
EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
});
}
function promiseMessage(aMessage, browser)
{
return ContentTask.spawn(browser.selectedBrowser, aMessage, function* (aMessage) {
yield new Promise((resolve, reject) => {
content.addEventListener("message", function(event) {
is(event.data, aMessage, "received " + aMessage);
if (event.data == aMessage)
resolve();
else
reject();
}, {once: true});
});
});
}
function removePermission(url, permission)
{
let uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url);
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let principal = ssm.createCodebasePrincipal(uri, {});
Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager)
.removeFromPrincipal(principal, permission);
}
function getPermission(url, permission)
{
let uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url);
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let principal = ssm.createCodebasePrincipal(uri, {});
return Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager)
.testPermissionFromPrincipal(principal, permission);
}

293
dom/quota/test/helpers.js Normal file
View File

@ -0,0 +1,293 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function clearAllDatabases(callback)
{
let qms = SpecialPowers.Services.qms;
let principal = SpecialPowers.wrap(document).nodePrincipal;
let request = qms.clearStoragesForPrincipal(principal);
let cb = SpecialPowers.wrapCallback(callback);
request.callback = cb;
}
var testHarnessGenerator = testHarnessSteps();
testHarnessGenerator.next();
function* testHarnessSteps()
{
function nextTestHarnessStep(val)
{
testHarnessGenerator.next(val);
}
let testScriptPath;
let testScriptFilename;
let scripts = document.getElementsByTagName("script");
for (let i = 0; i < scripts.length; i++) {
let src = scripts[i].src;
let match = src.match(/quota\/test\/unit\/(test_[^\/]+\.js)$/);
if (match && match.length == 2) {
testScriptPath = src;
testScriptFilename = match[1];
break;
}
}
yield undefined;
info("Clearing old databases");
clearAllDatabases(nextTestHarnessStep);
yield undefined;
info("Running" +
(testScriptFilename ? " '" + testScriptFilename + "'" : ""));
if (testScriptFilename && !window.disableWorkerTest) {
info("Running test in a worker");
let workerScriptBlob =
new Blob([ "(" + workerScript.toString() + ")();" ],
{ type: "text/javascript" });
let workerScriptURL = URL.createObjectURL(workerScriptBlob);
let worker = new Worker(workerScriptURL);
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.message);
worker.terminate();
nextTestHarnessStep();
};
worker.onmessage = function(event) {
let message = event.data;
switch (message.op) {
case "ok":
ok(message.condition, message.name, message.diag);
break;
case "todo":
todo(message.condition, message.name, message.diag);
break;
case "info":
info(message.msg);
break;
case "ready":
worker.postMessage({ op: "load", files: [ testScriptPath ] });
break;
case "loaded":
worker.postMessage({ op: "start" });
break;
case "done":
ok(true, "Worker finished");
nextTestHarnessStep();
break;
case "clearAllDatabases":
clearAllDatabases(function(){
worker.postMessage({ op: "clearAllDatabasesDone" });
});
break;
default:
ok(false,
"Received a bad message from worker: " + JSON.stringify(message));
nextTestHarnessStep();
}
};
URL.revokeObjectURL(workerScriptURL);
yield undefined;
worker.terminate();
worker = null;
clearAllDatabases(nextTestHarnessStep);
yield undefined;
} else if (testScriptFilename) {
todo(false,
"Skipping test in a worker because it is explicitly disabled: " +
disableWorkerTest);
} else {
todo(false,
"Skipping test in a worker because it's not structured properly");
}
info("Running test in main thread");
// Now run the test script in the main thread.
testGenerator.next();
yield undefined;
}
if (!window.runTest) {
window.runTest = function()
{
SimpleTest.waitForExplicitFinish();
testHarnessGenerator.next();
}
}
function finishTest()
{
SimpleTest.executeSoon(function() {
clearAllDatabases(function() { SimpleTest.finish(); });
});
}
function grabArgAndContinueHandler(arg)
{
testGenerator.next(arg);
}
function continueToNextStep()
{
SimpleTest.executeSoon(function() {
testGenerator.next();
});
}
function continueToNextStepSync()
{
testGenerator.next();
}
function workerScript()
{
"use strict";
self.repr = function(_thing_) {
if (typeof(_thing_) == "undefined") {
return "undefined";
}
let str;
try {
str = _thing_ + "";
} catch (e) {
return "[" + typeof(_thing_) + "]";
}
if (typeof(_thing_) == "function") {
str = str.replace(/^\s+/, "");
let idx = str.indexOf("{");
if (idx != -1) {
str = str.substr(0, idx) + "{...}";
}
}
return str;
};
self.ok = function(_condition_, _name_, _diag_) {
self.postMessage({ op: "ok",
condition: !!_condition_,
name: _name_,
diag: _diag_ });
};
self.is = function(_a_, _b_, _name_) {
let pass = (_a_ == _b_);
let diag = pass ? "" : "got " + repr(_a_) + ", expected " + repr(_b_);
ok(pass, _name_, diag);
};
self.isnot = function(_a_, _b_, _name_) {
let pass = (_a_ != _b_);
let diag = pass ? "" : "didn't expect " + repr(_a_) + ", but got it";
ok(pass, _name_, diag);
};
self.todo = function(_condition_, _name_, _diag_) {
self.postMessage({ op: "todo",
condition: !!_condition_,
name: _name_,
diag: _diag_ });
};
self.info = function(_msg_) {
self.postMessage({ op: "info", msg: _msg_ });
};
self.executeSoon = function(_fun_) {
var channel = new MessageChannel();
channel.port1.postMessage("");
channel.port2.onmessage = function(event) { _fun_(); };
};
self.finishTest = function() {
self.postMessage({ op: "done" });
};
self.grabArgAndContinueHandler = function(_arg_) {
testGenerator.next(_arg_);
};
self.continueToNextStep = function() {
executeSoon(function() {
testGenerator.next();
});
};
self.continueToNextStepSync = function() {
testGenerator.next();
};
self._clearAllDatabasesCallback = undefined;
self.clearAllDatabases = function(_callback_) {
self._clearAllDatabasesCallback = _callback_;
self.postMessage({ op: "clearAllDatabases" });
}
self.onerror = function(_message_, _file_, _line_) {
ok(false,
"Worker: uncaught exception [" + _file_ + ":" + _line_ + "]: '" +
_message_ + "'");
self.finishTest();
self.close();
return true;
};
self.onmessage = function(_event_) {
let message = _event_.data;
switch (message.op) {
case "load":
info("Worker: loading " + JSON.stringify(message.files));
self.importScripts(message.files);
self.postMessage({ op: "loaded" });
break;
case "start":
executeSoon(function() {
info("Worker: starting tests");
testGenerator.next();
});
break;
case "clearAllDatabasesDone":
info("Worker: all databases are cleared");
if (self._clearAllDatabasesCallback) {
self._clearAllDatabasesCallback();
}
break;
default:
throw new Error("Received a bad message from parent: " +
JSON.stringify(message));
}
};
self.postMessage({ op: "ready" });
}

View File

@ -0,0 +1,17 @@
# 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/.
[DEFAULT]
support-files =
helpers.js
unit/test_storage_manager_persist_allow.js
unit/test_storage_manager_persist_deny.js
unit/test_storage_manager_persisted.js
[test_storage_manager_persist_allow.html]
scheme=https
[test_storage_manager_persist_deny.html]
scheme=https
[test_storage_manager_persisted.html]
scheme=https

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Allow Persist Prompt for StorageManager</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" src="unit/test_storage_manager_persist_allow.js"></script>
<script type="text/javascript" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Deny Persist Prompt for StorageManager</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" src="unit/test_storage_manager_persist_deny.js"></script>
<script type="text/javascript" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Storage Manager Persisted 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" src="unit/test_storage_manager_persisted.js"></script>
<script type="text/javascript" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,24 @@
var disableWorkerTest = "Persist doesn't work in workers";
var testGenerator = testSteps();
function* testSteps()
{
SpecialPowers.pushPrefEnv({
"set": [["dom.storageManager.enabled", true],
["dom.storageManager.prompt.testing", true],
["dom.storageManager.prompt.testing.allow", true]]
}, continueToNextStep);
yield undefined;
navigator.storage.persist().then(grabArgAndContinueHandler);
let persistResult = yield undefined;
is(persistResult, true, "Persist succeeded");
navigator.storage.persisted().then(grabArgAndContinueHandler);
let persistedResult = yield undefined;
is(persistResult, persistedResult, "Persist/persisted results are consistent");
finishTest();
}

View File

@ -0,0 +1,24 @@
var disableWorkerTest = "Persist doesn't work in workers";
var testGenerator = testSteps();
function* testSteps()
{
SpecialPowers.pushPrefEnv({
"set": [["dom.storageManager.enabled", true],
["dom.storageManager.prompt.testing", true],
["dom.storageManager.prompt.testing.allow", false]]
}, continueToNextStep);
yield undefined;
navigator.storage.persist().then(grabArgAndContinueHandler);
let persistResult = yield undefined;
is(persistResult, false, "Cancel the persist prompt and resolve a promise with false");
navigator.storage.persisted().then(grabArgAndContinueHandler);
let persistedResult = yield undefined;
is(persistResult, persistedResult, "Persist/persisted results are consistent");
finishTest();
}

View File

@ -0,0 +1,11 @@
var testGenerator = testSteps();
function* testSteps()
{
navigator.storage.persisted().then(grabArgAndContinueHandler);
let persistedResult = yield undefined;
is(persistedResult, false, "Persisted returns false");
finishTest();
}

View File

@ -12,10 +12,12 @@
Exposed=(Window,Worker),
Func="mozilla::dom::StorageManager::PrefEnabled"]
interface StorageManager {
// [Throws]
// Promise<boolean> persisted();
// [Throws]
// [Exposed=Window] Promise<boolean> persist();
[Throws]
Promise<boolean> persisted();
[Exposed=Window, Throws]
Promise<boolean> persist();
[Throws]
Promise<StorageEstimate> estimate();
};

View File

@ -8077,16 +8077,20 @@ HTMLEditRules::ConfirmSelectionInBody()
{
// get the body
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mHTMLEditor->GetRoot());
NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
RefPtr<Element> rootElement = mHTMLEditor->GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_UNEXPECTED;
}
// get the selection
NS_ENSURE_STATE(mHTMLEditor);
RefPtr<Selection> selection = mHTMLEditor->GetSelection();
NS_ENSURE_STATE(selection);
if (NS_WARN_IF(!selection)) {
return NS_ERROR_UNEXPECTED;
}
// get the selection start location
nsCOMPtr<nsIDOMNode> selNode, temp, parent;
nsCOMPtr<nsINode> selNode;
int32_t selOffset;
nsresult rv =
EditorBase::GetStartNodeAndOffset(selection,
@ -8095,12 +8099,11 @@ HTMLEditRules::ConfirmSelectionInBody()
return rv;
}
temp = selNode;
nsINode* temp = selNode;
// check that selNode is inside body
while (temp && !TextEditUtils::IsBody(temp)) {
temp->GetParentNode(getter_AddRefs(parent));
temp = parent;
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
temp = temp->GetParentNode();
}
// if we aren't in the body, force the issue
@ -8108,6 +8111,7 @@ HTMLEditRules::ConfirmSelectionInBody()
// uncomment this to see when we get bad selections
// NS_NOTREACHED("selection not in body");
selection->Collapse(rootElement, 0);
return NS_OK;
}
// get the selection end location
@ -8117,9 +8121,8 @@ HTMLEditRules::ConfirmSelectionInBody()
temp = selNode;
// check that selNode is inside body
while (temp && !TextEditUtils::IsBody(temp)) {
rv = temp->GetParentNode(getter_AddRefs(parent));
temp = parent;
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
temp = temp->GetParentNode();
}
// if we aren't in the body, force the issue
@ -8129,9 +8132,7 @@ HTMLEditRules::ConfirmSelectionInBody()
selection->Collapse(rootElement, 0);
}
// XXX This is the result of the last call of GetParentNode(), it doesn't
// make sense...
return rv;
return NS_OK;
}
nsresult

View File

@ -1091,6 +1091,9 @@ ShadowLayerForwarder::ReleaseCompositable(const CompositableHandle& aHandle)
{
AssertInForwarderThread();
if (!DestroyInTransaction(aHandle)) {
if (!IPCOpen()) {
return;
}
mShadowManager->SendReleaseCompositable(aHandle);
}
mCompositables.Remove(aHandle.Value());

View File

@ -145,7 +145,7 @@ nsresult
gfxFontCache::Init()
{
NS_ASSERTION(!gGlobalCache, "Where did this come from?");
gGlobalCache = new gfxFontCache();
gGlobalCache = new gfxFontCache(SystemGroup::EventTargetFor(TaskCategory::Other));
if (!gGlobalCache) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -172,9 +172,9 @@ gfxFontCache::Shutdown()
#endif
}
gfxFontCache::gfxFontCache()
gfxFontCache::gfxFontCache(nsIEventTarget* aEventTarget)
: nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000,
"gfxFontCache")
"gfxFontCache", aEventTarget)
{
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (obs) {
@ -186,6 +186,9 @@ gfxFontCache::gfxFontCache()
// during expiration; see bug 717175 & 894798.
mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mWordCacheExpirationTimer) {
if (XRE_IsContentProcess() && NS_IsMainThread()) {
mWordCacheExpirationTimer->SetTarget(aEventTarget);
}
mWordCacheExpirationTimer->
InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
SHAPED_WORD_TIMEOUT_SECONDS * 1000,

View File

@ -285,7 +285,7 @@ public:
SHAPED_WORD_TIMEOUT_SECONDS = 60
};
gfxFontCache();
explicit gfxFontCache(nsIEventTarget* aEventTarget);
~gfxFontCache();
/*

View File

@ -793,7 +793,7 @@ struct SVGDrawingParameters
, viewportSize(aSize)
, animationTime(aAnimationTime)
, flags(aFlags)
, opacity(aSVGContext ? aSVGContext->GetGlobalOpacity() : aOpacity)
, opacity(aOpacity)
{
if (aSVGContext) {
auto sz = aSVGContext->GetViewportSize();

View File

@ -24,8 +24,7 @@ class SVGImageContext
{
public:
SVGImageContext()
: mGlobalOpacity(1.0)
, mIsPaintingSVGImageElement(false)
: mIsPaintingSVGImageElement(false)
{ }
/**
@ -49,11 +48,9 @@ public:
*/
explicit SVGImageContext(const Maybe<CSSIntSize>& aViewportSize,
const Maybe<SVGPreserveAspectRatio>& aPreserveAspectRatio = Nothing(),
gfxFloat aOpacity = 1.0,
bool aIsPaintingSVGImageElement = false)
: mViewportSize(aViewportSize)
, mPreserveAspectRatio(aPreserveAspectRatio)
, mGlobalOpacity(aOpacity)
, mIsPaintingSVGImageElement(aIsPaintingSVGImageElement)
{ }
@ -77,10 +74,6 @@ public:
mPreserveAspectRatio = aPAR;
}
gfxFloat GetGlobalOpacity() const {
return mGlobalOpacity;
}
const SVGEmbeddingContextPaint* GetContextPaint() const {
return mContextPaint.get();
}
@ -100,7 +93,6 @@ public:
return contextPaintIsEqual &&
mViewportSize == aOther.mViewportSize &&
mPreserveAspectRatio == aOther.mPreserveAspectRatio &&
mGlobalOpacity == aOther.mGlobalOpacity &&
mIsPaintingSVGImageElement == aOther.mIsPaintingSVGImageElement;
}
@ -116,7 +108,6 @@ public:
return HashGeneric(hash,
mViewportSize.map(HashSize).valueOr(0),
mPreserveAspectRatio.map(HashPAR).valueOr(0),
HashBytes(&mGlobalOpacity, sizeof(mGlobalOpacity)),
mIsPaintingSVGImageElement);
}
@ -132,7 +123,6 @@ private:
RefPtr<SVGEmbeddingContextPaint> mContextPaint;
Maybe<CSSIntSize> mViewportSize;
Maybe<SVGPreserveAspectRatio> mPreserveAspectRatio;
gfxFloat mGlobalOpacity;
bool mIsPaintingSVGImageElement;
};

View File

@ -407,7 +407,7 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
const Maybe<SVGImageContext> context(
Some(SVGImageContext(Some(CSSIntSize::Truncate(width, height)),
Some(imgElem->mPreserveAspectRatio.GetAnimValue()),
1.0, /* aIsPaintingSVGImageElement */ true)));
/* aIsPaintingSVGImageElement */ true)));
// For the actual draw operation to draw crisply (and at the right size),
// our destination rect needs to be |width|x|height|, *in dev pixels*.

View File

@ -5655,6 +5655,9 @@ pref("dom.storageManager.enabled", true);
pref("dom.storageManager.enabled", false);
#endif
pref("dom.storageManager.prompt.testing", false);
pref("dom.storageManager.prompt.testing.allow", false);
// Enable the Storage management in about:preferences and persistent-storage permission request
// To enable the DOM implementation, turn on "dom.storageManager.enabled"
#ifdef NIGHTLY_BUILD

View File

@ -109,6 +109,7 @@ class nsCookie : public nsICookie2
inline bool IsDomain() const { return *mHost == '.'; }
inline bool IsSecure() const { return mIsSecure; }
inline bool IsHttpOnly() const { return mIsHttpOnly; }
inline const OriginAttributes& OriginAttributesRef() const { return mOriginAttributes; }
// setters
inline void SetExpiry(int64_t aExpiry) { mExpiry = aExpiry; }

View File

@ -273,6 +273,11 @@ LogCookie(nsCookie *aCookie)
MOZ_LOG(gCookieLog, LogLevel::Debug,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false"));
MOZ_LOG(gCookieLog, LogLevel::Debug,("is httpOnly: %s\n", aCookie->IsHttpOnly() ? "true" : "false"));
nsAutoCString suffix;
aCookie->OriginAttributesRef().CreateSuffix(suffix);
MOZ_LOG(gCookieLog, LogLevel::Debug,("origin attributes: %s\n",
suffix.IsEmpty() ? "{empty}" : suffix.get()));
}
}

View File

@ -2575,6 +2575,12 @@
{}
]
],
"storage/persist-permission-manual.https.html": [
[
"/storage/persist-permission-manual.https.html",
{}
]
],
"svg/import/animate-dom-01-f-manual.svg": [
[
"/svg/import/animate-dom-01-f-manual.svg",
@ -61436,6 +61442,11 @@
{}
]
],
"storage/storage-persisted.js": [
[
{}
]
],
"streams/OWNERS": [
[
{}
@ -120890,6 +120901,18 @@
{}
]
],
"storage/persisted-worker.https.html": [
[
"/storage/persisted-worker.https.html",
{}
]
],
"storage/persisted.https.html": [
[
"/storage/persisted.https.html",
{}
]
],
"streams/byte-length-queuing-strategy.dedicatedworker.html": [
[
"/streams/byte-length-queuing-strategy.dedicatedworker.html",
@ -203625,10 +203648,26 @@
"6ce5a9b14d80030f0adfa1808857294e8c923cb2",
"testharness"
],
"storage/persist-permission-manual.https.html": [
"6b7c0b9d5c8cee3922f6797dace85b441e5ea45c",
"manual"
],
"storage/persisted-worker.https.html": [
"87d7bf4c615b07b3fa701239fc1823826a054e80",
"testharness"
],
"storage/persisted.https.html": [
"98be04abdc48c76b30f90af007f214f9759083dd",
"testharness"
],
"storage/storage-estimate-indexeddb.js": [
"660d3d068314c34d215df19c0b849ec711f57854",
"support"
],
"storage/storage-persisted.js": [
"dbf6e5bed3dec6ca59926c439ec9d6aca89d78b9",
"support"
],
"streams/OWNERS": [
"5ed27d1c21178be00e972816933945e094a0e170",
"support"

View File

@ -1,15 +0,0 @@
[interfaces.https.html]
type: testharness
prefs: [dom.storageManager.enabled:true]
[StorageManager interface: operation persisted()]
expected: FAIL
[StorageManager interface: operation persist()]
expected: FAIL
[StorageManager interface: navigator.storage must inherit property "persisted" with the proper type (0)]
expected: FAIL
[StorageManager interface: navigator.storage must inherit property "persist" with the proper type (1)]
expected: FAIL

View File

@ -1,18 +1,6 @@
[opaque-origin.https.html]
type: testharness
prefs: [dom.storageManager.enabled:true]
[navigator.storage.persist() in non-sandboxed iframe should not reject]
expected: FAIL
[navigator.storage.persist() in sandboxed iframe should reject with TypeError]
expected: FAIL
[navigator.storage.persisted() in non-sandboxed iframe should not reject]
expected: FAIL
[navigator.storage.persisted() in sandboxed iframe should reject with TypeError]
expected: FAIL
[navigator.storage.estimate() in sandboxed iframe should reject with TypeError]
expected: FAIL
prefs: [dom.storageManager.enabled:true,
dom.storageManager.prompt.testing:true,
dom.storageManager.prompt.testing.allow:true]

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>StorageManager: permission state is granted</title>
<p>Clear all persistent storage permissions before running this test.</p>
<p>Test passes if there is a permission prompt and click allow store persistent data</p>
<meta name="help" href="https://storage.spec.whatwg.org/#dom-storagemanager-persist">
<meta name="author" title="Mozilla" href="https://www.mozilla.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
promise_test(function(t) {
return navigator.storage.persist()
.then(function(result) {
assert_true(result);
return navigator.storage.persisted();
})
.then(function(result) {
assert_true(result);
})
}, 'Expect permission state is granted after calling persist()');
</script>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>StorageManager: persisted() from worker</title>
<meta name="help" href="https://storage.spec.whatwg.org/#dom-storagemanager-persisted">
<meta name="author" title="Mozilla" href="https://www.mozilla.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
fetch_tests_from_worker(new Worker("storage-persisted.js"));
</script>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>StorageManager: persisted()</title>
<meta name="help" href="https://storage.spec.whatwg.org/#dom-storagemanager-persisted">
<meta name="author" title="Mozilla" href="https://www.mozilla.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script src="storage-persisted.js"></script>
</body>
</html>

View File

@ -0,0 +1,18 @@
if (this.document === undefined) {
importScripts("/resources/testharness.js");
}
test(function(t) {
assert_true('persisted' in navigator.storage);
assert_equals(typeof navigator.storage.persisted, 'function');
assert_true(navigator.storage.persisted() instanceof Promise);
}, 'persisted() method exists and returns a Promise');
promise_test(function(t) {
return navigator.storage.persisted().then(function(result) {
assert_equals(typeof result, 'boolean');
assert_equals(result, false);
});
}, 'persisted() returns a promise and resolves as boolean with false');
done();

View File

@ -667,8 +667,6 @@ this.ExtensionData = class {
}
};
let _browserUpdated = false;
const PROXIED_EVENTS = new Set(["test-harness-message", "add-permissions", "remove-permissions"]);
// We create one instance of this class per extension. |addonData|
@ -745,14 +743,6 @@ this.Extension = class extends ExtensionData {
/* eslint-enable mozilla/balanced-listeners */
}
static set browserUpdated(updated) {
_browserUpdated = updated;
}
static get browserUpdated() {
return _browserUpdated;
}
static generateXPI(data) {
return ExtensionTestCommon.generateXPI(data);
}

View File

@ -28,9 +28,6 @@ XPCOMUtils.defineLazyGetter(this, "UUIDMap", () => {
const {appinfo} = Services;
const isParentProcess = appinfo.processType === appinfo.PROCESS_TYPE_DEFAULT;
if (isParentProcess) {
Services.ppmm.loadProcessScript("chrome://extensions/content/extension-process-script.js", true);
}
var ExtensionManagement;

View File

@ -2,6 +2,8 @@
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
@ -34,7 +36,7 @@ this.runtime = class extends ExtensionAPI {
let listener = () => {
switch (extension.startupReason) {
case "APP_STARTUP":
if (Extension.browserUpdated) {
if (AddonManagerPrivate.browserUpdated) {
fire.sync({reason: "browser_update"});
}
break;

View File

@ -38,7 +38,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionUtils.getConsole());
XPCOMUtils.defineLazyGetter(this, "getInnerWindowID", () => ExtensionUtils.getInnerWindowID);
const isContentProcess = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
// We need to avoid touching Services.appinfo here in order to prevent
// the wrong version from being cached during xpcshell test startup.
const appinfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
const isContentProcess = appinfo.processType == appinfo.PROCESS_TYPE_CONTENT;
class ScriptMatcher {

View File

@ -105,6 +105,8 @@ XPCOMUtils.defineLazyGetter(this, "CertUtils", function() {
XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
PREF_WEBEXT_PERM_PROMPTS, false);
Services.ppmm.loadProcessScript("chrome://extensions/content/extension-process-script.js", true);
const INTEGER = /^[1-9]\d*$/;
this.EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
@ -643,6 +645,7 @@ var gShutdownBarrier = null;
var gRepoShutdownState = "";
var gShutdownInProgress = false;
var gPluginPageListener = null;
var gBrowserUpdated = null;
/**
* This is the real manager, kept here rather than in AddonManager to keep its
@ -815,7 +818,7 @@ var AddonManagerInternal = {
appChanged = Services.appinfo.version != oldAppVersion;
} catch (e) { }
Extension.browserUpdated = appChanged;
gBrowserUpdated = appChanged;
let oldPlatformVersion = Services.prefs.getCharPref(PREF_EM_LAST_PLATFORM_VERSION, "");
@ -3098,6 +3101,10 @@ this.AddonManagerPrivate = {
AddonManagerInternal.startup();
},
get browserUpdated() {
return gBrowserUpdated;
},
registerProvider(aProvider, aTypes) {
AddonManagerInternal.registerProvider(aProvider, aTypes);
},

View File

@ -93,7 +93,6 @@ XPCOMUtils.defineLazyGetter(this, "IconDetails", () => {
return ExtensionUtils.IconDetails;
});
Cu.importGlobalProperties(["URL"]);
const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",

View File

@ -63,8 +63,28 @@ netwerk/srtp/src/
nsprpub/
other-licenses/
parser/expat/
python/altgraph/
python/blessings/
python/configobj/
python/futures/
python/jsmin/
python/mock-*/
python/psutil/
python/py/
python/pyasn1/
python/pyasn1-modules/
python/PyECC/
python/pytest/
python/pyyaml/
python/pytoml/
python/redo/
python/requests/
python/rsa/
python/which/
security/nss/
security/sandbox/chromium/
testing/gtest/gmock/
testing/gtest/gtest/
testing/talos/talos/tests/dromaeo/
toolkit/components/protobuf/
toolkit/crashreporter/google-breakpad/