mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 09:54:42 +00:00
merge mozilla-inbound to mozilla-central. r=merge a=merge
MozReview-Commit-ID: DZowmCXo7Q
This commit is contained in:
commit
4bd7a206de
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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() })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -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 }) {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
||||
callDelayed(function () {
|
||||
expect(spy.callCount).to.be(1);
|
||||
done();
|
||||
}, ASYNC_TIMEOUT * 3);
|
||||
spy.waitForNotification(function() {
|
||||
callDelayed(function () {
|
||||
expect(spy.callCount).to.be(1);
|
||||
done();
|
||||
}, 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,20 +992,23 @@ 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);
|
||||
done();
|
||||
}, ASYNC_TIMEOUT);
|
||||
});
|
||||
|
||||
callDelayed(function() {
|
||||
expect(lastestRecords.length).to.be(1);
|
||||
expect(lastestRecords[0].intersectionRatio).to.be(1);
|
||||
done();
|
||||
}, ASYNC_TIMEOUT);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('unobserve', function() {
|
||||
|
||||
it('removes targets from the internal store', function(done) {
|
||||
@ -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();
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,89 +472,151 @@ 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());
|
||||
{
|
||||
// In a worker thread request.
|
||||
MutexAutoLock lock(mProxy->Lock());
|
||||
|
||||
if (NS_WARN_IF(mProxy->CleanedUp())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
if (NS_WARN_IF(mProxy->CleanedUp())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<FinishWorkerRunnable> runnable = new FinishWorkerRunnable(this);
|
||||
if (NS_WARN_IF(!runnable->Dispatch())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<FinishWorkerRunnable> runnable = new FinishWorkerRunnable(this);
|
||||
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
|
||||
|
@ -39,6 +39,12 @@ public:
|
||||
}
|
||||
|
||||
// WebIDL
|
||||
already_AddRefed<Promise>
|
||||
Persisted(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Persist(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Estimate(ErrorResult& aRv);
|
||||
|
||||
|
@ -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'
|
||||
]
|
||||
|
10
dom/quota/test/browser.ini
Normal file
10
dom/quota/test/browser.ini
Normal 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]
|
49
dom/quota/test/browserHelpers.js
Normal file
49
dom/quota/test/browserHelpers.js
Normal 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);
|
||||
}
|
35
dom/quota/test/browser_permissionsPrompt.html
Normal file
35
dom/quota/test/browser_permissionsPrompt.html
Normal 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>
|
65
dom/quota/test/browser_permissionsPromptAllow.js
Normal file
65
dom/quota/test/browser_permissionsPromptAllow.js
Normal 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");
|
||||
});
|
95
dom/quota/test/browser_permissionsPromptDeny.js
Normal file
95
dom/quota/test/browser_permissionsPromptDeny.js
Normal 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");
|
||||
});
|
40
dom/quota/test/browser_permissionsPromptUnknown.js
Normal file
40
dom/quota/test/browser_permissionsPromptUnknown.js
Normal 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
121
dom/quota/test/head.js
Normal 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
293
dom/quota/test/helpers.js
Normal 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" });
|
||||
}
|
17
dom/quota/test/mochitest.ini
Normal file
17
dom/quota/test/mochitest.ini
Normal 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
|
19
dom/quota/test/test_storage_manager_persist_allow.html
Normal file
19
dom/quota/test/test_storage_manager_persist_allow.html
Normal 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>
|
19
dom/quota/test/test_storage_manager_persist_deny.html
Normal file
19
dom/quota/test/test_storage_manager_persist_deny.html
Normal 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>
|
19
dom/quota/test/test_storage_manager_persisted.html
Normal file
19
dom/quota/test/test_storage_manager_persisted.html
Normal 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>
|
24
dom/quota/test/unit/test_storage_manager_persist_allow.js
Normal file
24
dom/quota/test/unit/test_storage_manager_persist_allow.js
Normal 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();
|
||||
}
|
24
dom/quota/test/unit/test_storage_manager_persist_deny.js
Normal file
24
dom/quota/test/unit/test_storage_manager_persist_deny.js
Normal 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();
|
||||
}
|
11
dom/quota/test/unit/test_storage_manager_persisted.js
Normal file
11
dom/quota/test/unit/test_storage_manager_persisted.js
Normal 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();
|
||||
}
|
@ -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();
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -1091,6 +1091,9 @@ ShadowLayerForwarder::ReleaseCompositable(const CompositableHandle& aHandle)
|
||||
{
|
||||
AssertInForwarderThread();
|
||||
if (!DestroyInTransaction(aHandle)) {
|
||||
if (!IPCOpen()) {
|
||||
return;
|
||||
}
|
||||
mShadowManager->SendReleaseCompositable(aHandle);
|
||||
}
|
||||
mCompositables.Remove(aHandle.Value());
|
||||
|
@ -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,
|
||||
|
@ -285,7 +285,7 @@ public:
|
||||
SHAPED_WORD_TIMEOUT_SECONDS = 60
|
||||
};
|
||||
|
||||
gfxFontCache();
|
||||
explicit gfxFontCache(nsIEventTarget* aEventTarget);
|
||||
~gfxFontCache();
|
||||
|
||||
/*
|
||||
|
@ -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();
|
||||
|
@ -24,8 +24,7 @@ class SVGImageContext
|
||||
{
|
||||
public:
|
||||
SVGImageContext()
|
||||
: mGlobalOpacity(1.0)
|
||||
, mIsPaintingSVGImageElement(false)
|
||||
: mIsPaintingSVGImageElement(false)
|
||||
{ }
|
||||
|
||||
/**
|
||||
@ -48,12 +47,10 @@ public:
|
||||
* the SVG image in question is being painted for an SVG <image> element.
|
||||
*/
|
||||
explicit SVGImageContext(const Maybe<CSSIntSize>& aViewportSize,
|
||||
const Maybe<SVGPreserveAspectRatio>& aPreserveAspectRatio = Nothing(),
|
||||
gfxFloat aOpacity = 1.0,
|
||||
const Maybe<SVGPreserveAspectRatio>& aPreserveAspectRatio = Nothing(),
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -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*.
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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>
|
@ -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>
|
14
testing/web-platform/tests/storage/persisted.https.html
Normal file
14
testing/web-platform/tests/storage/persisted.https.html
Normal 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>
|
18
testing/web-platform/tests/storage/storage-persisted.js
Normal file
18
testing/web-platform/tests/storage/storage-persisted.js
Normal 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();
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -93,7 +93,6 @@ XPCOMUtils.defineLazyGetter(this, "IconDetails", () => {
|
||||
return ExtensionUtils.IconDetails;
|
||||
});
|
||||
|
||||
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
|
||||
const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
|
||||
|
@ -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/
|
||||
|
Loading…
Reference in New Issue
Block a user