зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to graphics
MozReview-Commit-ID: AxmKwI9dnre
This commit is contained in:
Коммит
7fc02188d8
|
@ -25,21 +25,4 @@ UnbindCacheEntriesFromDocument(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache and shutdown the accessibles.
|
||||
*/
|
||||
template <class T>
|
||||
static void
|
||||
ClearCache(nsRefPtrHashtable<nsPtrHashKey<const void>, T>& aCache)
|
||||
{
|
||||
for (auto iter = aCache.Iter(); !iter.Done(); iter.Next()) {
|
||||
T* accessible = iter.Data();
|
||||
MOZ_ASSERT(accessible);
|
||||
if (accessible && !accessible->IsDefunct()) {
|
||||
accessible->Shutdown();
|
||||
}
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -483,7 +483,17 @@ DocAccessible::Shutdown()
|
|||
|
||||
mDependentIDsHash.Clear();
|
||||
mNodeToAccessibleMap.Clear();
|
||||
ClearCache(mAccessibleCache);
|
||||
|
||||
for (auto iter = mAccessibleCache.Iter(); !iter.Done(); iter.Next()) {
|
||||
Accessible* accessible = iter.Data();
|
||||
MOZ_ASSERT(accessible);
|
||||
if (accessible && !accessible->IsDefunct()) {
|
||||
// Unlink parent to avoid its cleaning overhead in shutdown.
|
||||
accessible->mParent = nullptr;
|
||||
accessible->Shutdown();
|
||||
}
|
||||
iter.Remove();
|
||||
}
|
||||
|
||||
HyperTextAccessibleWrap::Shutdown();
|
||||
|
||||
|
|
|
@ -29,10 +29,6 @@ LOCAL_INCLUDES += [
|
|||
'/xpcom/build',
|
||||
]
|
||||
|
||||
USE_LIBS += [
|
||||
'mozglue',
|
||||
]
|
||||
|
||||
if CONFIG['LIBFUZZER']:
|
||||
USE_LIBS += [ 'fuzzer' ]
|
||||
LOCAL_INCLUDES += [
|
||||
|
|
|
@ -3,11 +3,136 @@
|
|||
* 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/. */
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
|
||||
"resource://gre/modules/DeferredTask.jsm");
|
||||
|
||||
const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
|
||||
const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
|
||||
const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
|
||||
|
||||
const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
|
||||
|
||||
const PREF_SELECTED_APP = "browser.feeds.handlers.application";
|
||||
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
|
||||
const PREF_SELECTED_ACTION = "browser.feeds.handler";
|
||||
const PREF_SELECTED_READER = "browser.feeds.handler.default";
|
||||
|
||||
const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
|
||||
const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
|
||||
const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
|
||||
const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
|
||||
|
||||
const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
|
||||
const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
|
||||
const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
|
||||
const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
|
||||
|
||||
const PREF_UPDATE_DELAY = 2000;
|
||||
|
||||
const SETTABLE_PREFS = new Set([
|
||||
PREF_VIDEO_SELECTED_ACTION,
|
||||
PREF_AUDIO_SELECTED_ACTION,
|
||||
PREF_SELECTED_ACTION,
|
||||
PREF_VIDEO_SELECTED_READER,
|
||||
PREF_AUDIO_SELECTED_READER,
|
||||
PREF_SELECTED_READER,
|
||||
PREF_VIDEO_SELECTED_WEB,
|
||||
PREF_AUDIO_SELECTED_WEB,
|
||||
PREF_SELECTED_WEB
|
||||
]);
|
||||
|
||||
const EXECUTABLE_PREFS = new Set([
|
||||
PREF_SELECTED_APP,
|
||||
PREF_VIDEO_SELECTED_APP,
|
||||
PREF_AUDIO_SELECTED_APP
|
||||
]);
|
||||
|
||||
const VALID_ACTIONS = new Set(["ask", "reader", "bookmarks"]);
|
||||
const VALID_READERS = new Set(["web", "client", "default", "bookmarks"]);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "SHOULD_LOG",
|
||||
"feeds.log", false);
|
||||
|
||||
function LOG(str) {
|
||||
if (SHOULD_LOG)
|
||||
dump("*** Feeds: " + str + "\n");
|
||||
}
|
||||
|
||||
function getPrefActionForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_ACTION;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_ACTION;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
function getPrefReaderForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_READER;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_READER;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_READER;
|
||||
}
|
||||
}
|
||||
|
||||
function getPrefWebForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_WEB;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_WEB;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_WEB;
|
||||
}
|
||||
}
|
||||
|
||||
function getPrefAppForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_APP;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_APP;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_APP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a feed type to a maybe-feed mimetype.
|
||||
*/
|
||||
function getMimeTypeForFeedType(aFeedType) {
|
||||
switch (aFeedType) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return TYPE_MAYBE_VIDEO_FEED;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return TYPE_MAYBE_AUDIO_FEED;
|
||||
|
||||
default:
|
||||
return TYPE_MAYBE_FEED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
|
||||
* and shows UI when they are discovered.
|
||||
*/
|
||||
var FeedHandler = {
|
||||
_prefChangeCallback: null,
|
||||
|
||||
/** Called when the user clicks on the Subscribe to This Page... menu item,
|
||||
* or when the user clicks the feed button when the page contains multiple
|
||||
* feeds.
|
||||
|
@ -195,7 +320,8 @@ var FeedHandler = {
|
|||
return file.leafName;
|
||||
},
|
||||
|
||||
chooseClientApp(aTitle, aPrefName, aBrowser) {
|
||||
_chooseClientApp(aTitle, aTypeName, aBrowser) {
|
||||
const prefName = getPrefAppForType(aTypeName);
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
|
||||
fp.init(window, aTitle, Ci.nsIFilePicker.modeOpen);
|
||||
|
@ -222,7 +348,7 @@ var FeedHandler = {
|
|||
}
|
||||
|
||||
if (fp.file.leafName != appName) {
|
||||
Services.prefs.setComplexValue(aPrefName, Ci.nsILocalFile, selectedApp);
|
||||
Services.prefs.setComplexValue(prefName, Ci.nsILocalFile, selectedApp);
|
||||
aBrowser.messageManager.sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
|
||||
{ name: this._getFileDisplayName(selectedApp),
|
||||
type: "SelectedAppMenuItem" });
|
||||
|
@ -277,70 +403,240 @@ var FeedHandler = {
|
|||
}
|
||||
},
|
||||
|
||||
// nsISupports
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
|
||||
init() {
|
||||
window.messageManager.addMessageListener("FeedWriter:ChooseClientApp", this);
|
||||
window.messageManager.addMessageListener("FeedWriter:RequestClientAppName", this);
|
||||
window.messageManager.addMessageListener("FeedWriter:SetFeedCharPref", this);
|
||||
window.messageManager.addMessageListener("FeedWriter:SetFeedComplexString", this);
|
||||
window.messageManager.addMessageListener("FeedWriter:GetSubscriptionUI", this);
|
||||
window.messageManager.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribe", this);
|
||||
window.messageManager.addMessageListener("FeedWriter:ShownFirstRun", this);
|
||||
|
||||
Services.ppmm.addMessageListener("FeedConverter:ExecuteClientApp", this);
|
||||
|
||||
const prefs = Services.prefs;
|
||||
prefs.addObserver(PREF_SELECTED_ACTION, this, true);
|
||||
prefs.addObserver(PREF_SELECTED_READER, this, true);
|
||||
prefs.addObserver(PREF_SELECTED_WEB, this, true);
|
||||
prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, true);
|
||||
prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, true);
|
||||
prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, true);
|
||||
prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, true);
|
||||
prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, true);
|
||||
prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, true);
|
||||
},
|
||||
|
||||
uninit() {
|
||||
Services.ppmm.removeMessageListener("FeedConverter:ExecuteClientApp", this);
|
||||
|
||||
this._prefChangeCallback = null;
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
observe(subject, topic, data) {
|
||||
if (topic == "nsPref:changed") {
|
||||
LOG(`Pref changed ${data}`)
|
||||
if (this._prefChangeCallback) {
|
||||
this._prefChangeCallback.disarm();
|
||||
}
|
||||
// Multiple prefs are set at the same time, debounce to reduce noise
|
||||
// This can happen in one feed and we want to message all feed pages
|
||||
this._prefChangeCallback = new DeferredTask(() => {
|
||||
this._prefChanged(data);
|
||||
}, PREF_UPDATE_DELAY);
|
||||
this._prefChangeCallback.arm();
|
||||
}
|
||||
},
|
||||
|
||||
_prefChanged(prefName) {
|
||||
// Don't observe for PREF_*SELECTED_APP as user likely just picked one
|
||||
// That is also handled by SetApplicationLauncherMenuItem call
|
||||
// Rather than the others which happen on subscription
|
||||
switch (prefName) {
|
||||
case PREF_SELECTED_READER:
|
||||
case PREF_SELECTED_WEB:
|
||||
case PREF_VIDEO_SELECTED_READER:
|
||||
case PREF_VIDEO_SELECTED_WEB:
|
||||
case PREF_AUDIO_SELECTED_READER:
|
||||
case PREF_AUDIO_SELECTED_WEB:
|
||||
case PREF_SELECTED_ACTION:
|
||||
case PREF_VIDEO_SELECTED_ACTION:
|
||||
case PREF_AUDIO_SELECTED_ACTION:
|
||||
const response = {
|
||||
default: this._getReaderForType(Ci.nsIFeed.TYPE_FEED),
|
||||
[Ci.nsIFeed.TYPE_AUDIO]: this._getReaderForType(Ci.nsIFeed.TYPE_AUDIO),
|
||||
[Ci.nsIFeed.TYPE_VIDEO]: this._getReaderForType(Ci.nsIFeed.TYPE_VIDEO)
|
||||
};
|
||||
Services.mm.broadcastAsyncMessage("FeedWriter:PreferenceUpdated",
|
||||
response);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_initSubscriptionUIResponse(feedType) {
|
||||
const wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
const handlersRaw = wccr.getContentHandlers(getMimeTypeForFeedType(feedType));
|
||||
const handlers = [];
|
||||
for (let handler of handlersRaw) {
|
||||
LOG(`Handler found: ${handler}`);
|
||||
handlers.push({
|
||||
name: handler.name,
|
||||
uri: handler.uri
|
||||
});
|
||||
}
|
||||
let showFirstRunUI = true;
|
||||
// eslint-disable-next-line mozilla/use-default-preference-values
|
||||
try {
|
||||
showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
|
||||
} catch (ex) { }
|
||||
const response = { handlers, showFirstRunUI };
|
||||
let selectedClientApp;
|
||||
const feedTypePref = getPrefAppForType(feedType);
|
||||
try {
|
||||
selectedClientApp = Services.prefs.getComplexValue(feedTypePref, Ci.nsILocalFile);
|
||||
} catch (ex) {
|
||||
// Just do nothing, then we won't bother populating
|
||||
}
|
||||
|
||||
let defaultClientApp = null;
|
||||
try {
|
||||
// This can sometimes not exist
|
||||
defaultClientApp = Cc["@mozilla.org/browser/shell-service;1"]
|
||||
.getService(Ci.nsIShellService)
|
||||
.defaultFeedReader;
|
||||
} catch (ex) {
|
||||
// Just do nothing, then we don't bother populating
|
||||
}
|
||||
|
||||
if (selectedClientApp && selectedClientApp.exists()) {
|
||||
if (defaultClientApp && selectedClientApp.path != defaultClientApp.path) {
|
||||
// Only set the default menu item if it differs from the selected one
|
||||
response.defaultMenuItem = this._getFileDisplayName(defaultClientApp);
|
||||
}
|
||||
response.selectedMenuItem = this._getFileDisplayName(selectedClientApp);
|
||||
}
|
||||
response.reader = this._getReaderForType(feedType);
|
||||
return response;
|
||||
},
|
||||
|
||||
_setPref(aPrefName, aPrefValue, aIsComplex = false) {
|
||||
LOG(`FeedWriter._setPref ${aPrefName}`);
|
||||
// Ensure we have a pref that is settable
|
||||
if (aPrefName && SETTABLE_PREFS.has(aPrefName)) {
|
||||
if (aIsComplex) {
|
||||
Services.prefs.setStringPref(aPrefName, aPrefValue);
|
||||
} else {
|
||||
Services.prefs.setCharPref(aPrefName, aPrefValue);
|
||||
}
|
||||
} else {
|
||||
LOG(`FeedWriter._setPref ${aPrefName} not allowed`);
|
||||
}
|
||||
},
|
||||
|
||||
_getReaderForType(feedType) {
|
||||
let prefs = Services.prefs;
|
||||
let handler = "bookmarks";
|
||||
let url;
|
||||
// eslint-disable-next-line mozilla/use-default-preference-values
|
||||
try {
|
||||
handler = prefs.getCharPref(getPrefReaderForType(feedType));
|
||||
} catch (ex) { }
|
||||
|
||||
if (handler === "web") {
|
||||
try {
|
||||
url = prefs.getStringPref(getPrefWebForType(feedType));
|
||||
} catch (ex) {
|
||||
LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs");
|
||||
url = null;
|
||||
}
|
||||
}
|
||||
const alwaysUse = this._getAlwaysUseState(feedType);
|
||||
const action = prefs.getCharPref(getPrefActionForType(feedType));
|
||||
return { handler, url, alwaysUse, action };
|
||||
},
|
||||
|
||||
_getAlwaysUseState(feedType) {
|
||||
try {
|
||||
return Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask";
|
||||
} catch (ex) { }
|
||||
return false;
|
||||
},
|
||||
|
||||
receiveMessage(msg) {
|
||||
let handler;
|
||||
switch (msg.name) {
|
||||
case "FeedWriter:ChooseClientApp":
|
||||
this.chooseClientApp(msg.data.title, msg.data.prefName, msg.target);
|
||||
case "FeedWriter:GetSubscriptionUI":
|
||||
const response = this._initSubscriptionUIResponse(msg.data.feedType);
|
||||
msg.target.messageManager
|
||||
.sendAsyncMessage("FeedWriter:GetSubscriptionUIResponse",
|
||||
response);
|
||||
break;
|
||||
case "FeedWriter:RequestClientAppName":
|
||||
let selectedClientApp;
|
||||
try {
|
||||
selectedClientApp = Services.prefs.getComplexValue(msg.data.feedTypePref, Ci.nsILocalFile);
|
||||
} catch (ex) {
|
||||
// Just do nothing, then we won't bother populating
|
||||
}
|
||||
|
||||
let defaultClientApp = null;
|
||||
try {
|
||||
// This can sometimes not exist
|
||||
defaultClientApp = Cc["@mozilla.org/browser/shell-service;1"]
|
||||
.getService(Ci.nsIShellService)
|
||||
.defaultFeedReader;
|
||||
} catch (ex) {
|
||||
// Just do nothing, then we don't bother populating
|
||||
}
|
||||
|
||||
if (selectedClientApp && selectedClientApp.exists()) {
|
||||
if (defaultClientApp && selectedClientApp.path != defaultClientApp.path) {
|
||||
// Only set the default menu item if it differs from the selected one
|
||||
msg.target.messageManager
|
||||
.sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
|
||||
{ name: this._getFileDisplayName(defaultClientApp),
|
||||
type: "DefaultAppMenuItem" });
|
||||
}
|
||||
msg.target.messageManager
|
||||
.sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
|
||||
{ name: this._getFileDisplayName(selectedClientApp),
|
||||
type: "SelectedAppMenuItem" });
|
||||
}
|
||||
case "FeedWriter:ChooseClientApp":
|
||||
this._chooseClientApp(msg.data.title, msg.data.feedType, msg.target);
|
||||
break;
|
||||
case "FeedWriter:ShownFirstRun":
|
||||
Services.prefs.setBoolPref("browser.feeds.showFirstRunUI", false);
|
||||
Services.prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
|
||||
break;
|
||||
case "FeedWriter:SetFeedCharPref":
|
||||
Services.prefs.setCharPref(msg.data.pref, msg.data.value);
|
||||
break;
|
||||
case "FeedWriter:SetFeedComplexString": {
|
||||
Services.prefs.setStringPref(msg.data.pref, msg.data.value);
|
||||
break;
|
||||
}
|
||||
case "FeedWriter:SetFeedPrefsAndSubscribe":
|
||||
const settings = msg.data;
|
||||
if (!settings.action || !VALID_ACTIONS.has(settings.action)) {
|
||||
LOG(`Invalid action ${settings.action}`);
|
||||
return;
|
||||
}
|
||||
if (!settings.reader || !VALID_READERS.has(settings.reader)) {
|
||||
LOG(`Invalid reader ${settings.reader}`);
|
||||
return;
|
||||
}
|
||||
const actionPref = getPrefActionForType(settings.feedType);
|
||||
this._setPref(actionPref, settings.action);
|
||||
const readerPref = getPrefReaderForType(settings.feedType);
|
||||
this._setPref(readerPref, settings.reader);
|
||||
handler = null;
|
||||
|
||||
switch (settings.reader) {
|
||||
case "web":
|
||||
// This is a web set URI by content using window.registerContentHandler()
|
||||
// Lets make sure we know about it before setting it
|
||||
const webPref = getPrefWebForType(settings.feedType);
|
||||
let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
// If the user provided an invalid web URL this function won't give us a reference
|
||||
handler = wccr.getWebContentHandlerByURI(getMimeTypeForFeedType(settings.feedType), settings.uri);
|
||||
if (handler) {
|
||||
this._setPref(webPref, settings.uri, true);
|
||||
if (settings.useAsDefault) {
|
||||
wccr.setAutoHandler(getMimeTypeForFeedType(settings.feedType), handler);
|
||||
}
|
||||
msg.target.messageManager
|
||||
.sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribeResponse",
|
||||
{ redirect: handler.getHandlerURI(settings.feedLocation) });
|
||||
} else {
|
||||
LOG(`No handler found for web ${settings.feedType} ${settings.uri}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
const feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
|
||||
getService(Ci.nsIFeedResultService);
|
||||
|
||||
feedService.addToClientReader(settings.feedLocation,
|
||||
settings.feedTitle,
|
||||
settings.feedSubtitle,
|
||||
settings.feedType,
|
||||
settings.reader);
|
||||
}
|
||||
break;
|
||||
case "FeedConverter:ExecuteClientApp":
|
||||
this.executeClientApp(msg.data.spec, msg.data.title,
|
||||
msg.data.subtitle, msg.data.feedHandler);
|
||||
// Always check feedHandler is from a set array of executable prefs
|
||||
if (EXECUTABLE_PREFS.has(msg.data.feedHandler)) {
|
||||
this.executeClientApp(msg.data.spec, msg.data.title,
|
||||
msg.data.subtitle, msg.data.feedHandler);
|
||||
} else {
|
||||
LOG(`FeedConverter:ExecuteClientApp - Will not exec ${msg.data.feedHandler}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -473,7 +473,7 @@ XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
|
|||
// XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
|
||||
// but for now just make it work
|
||||
return Services.strings.createBundle(
|
||||
"chrome://weave/locale/services/sync.properties");
|
||||
"chrome://weave/locale/sync.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
|
||||
|
|
|
@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
|
|||
|
||||
// lazy module getters
|
||||
|
||||
/* global AboutHome:false, AddonWatcher:false, AppConstants: false,
|
||||
/* global AboutHome:false, AddonWatcher:false,
|
||||
BrowserUITelemetry:false, BrowserUsageTelemetry:false, BrowserUtils:false,
|
||||
CastingApps:false, CharsetMenu:false, Color:false, ContentSearch:false,
|
||||
Deprecated:false, E10SUtils:false, FormValidationHandler:false,
|
||||
|
@ -38,7 +38,6 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
|
|||
[
|
||||
["AboutHome", "resource:///modules/AboutHome.jsm"],
|
||||
["AddonWatcher", "resource://gre/modules/AddonWatcher.jsm"],
|
||||
["AppConstants", "resource://gre/modules/AppConstants.jsm"],
|
||||
["BrowserUITelemetry", "resource:///modules/BrowserUITelemetry.jsm"],
|
||||
["BrowserUsageTelemetry", "resource:///modules/BrowserUsageTelemetry.jsm"],
|
||||
["BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"],
|
||||
|
@ -2572,8 +2571,13 @@ function URLBarSetURI(aURI) {
|
|||
valid = !isBlankPageURL(uri.spec);
|
||||
}
|
||||
|
||||
let isDifferentValidValue = valid && value != gURLBar.value;
|
||||
gURLBar.value = value;
|
||||
gURLBar.valueIsTyped = !valid;
|
||||
if (isDifferentValidValue) {
|
||||
gURLBar.selectionStart = gURLBar.selectionEnd = 0;
|
||||
}
|
||||
|
||||
SetPageProxyState(valid ? "valid" : "invalid");
|
||||
}
|
||||
|
||||
|
@ -6680,6 +6684,35 @@ function BrowserOpenSyncTabs() {
|
|||
gSyncUI.openSyncedTabsPanel();
|
||||
}
|
||||
|
||||
function ReportFalseDeceptiveSite() {
|
||||
let docURI = gBrowser.selectedBrowser.documentURI;
|
||||
let isPhishingPage =
|
||||
docURI && docURI.spec.startsWith("about:blocked?e=deceptiveBlocked");
|
||||
|
||||
if (isPhishingPage) {
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let onMessage = (message) => {
|
||||
mm.removeMessageListener("DeceptiveBlockedDetails:Result", onMessage);
|
||||
let reportUrl = gSafeBrowsing.getReportURL("PhishMistake", message.data.blockedInfo);
|
||||
if (reportUrl) {
|
||||
openUILinkIn(reportUrl, "tab");
|
||||
} else {
|
||||
let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
getService(Ci.nsIPromptService);
|
||||
let bundle =
|
||||
Services.strings.createBundle("chrome://browser/locale/safebrowsing/safebrowsing.properties");
|
||||
promptService.alert(window,
|
||||
bundle.GetStringFromName("errorReportFalseDeceptiveTitle"),
|
||||
bundle.formatStringFromName("errorReportFalseDeceptiveMessage",
|
||||
[message.data.blockedInfo.provider], 1));
|
||||
}
|
||||
}
|
||||
mm.addMessageListener("DeceptiveBlockedDetails:Result", onMessage);
|
||||
|
||||
mm.sendAsyncMessage("DeceptiveBlockedDetails");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a URL
|
||||
* eg:
|
||||
|
|
|
@ -262,6 +262,35 @@ function getSerializedSecurityInfo(docShell) {
|
|||
return serhelper.serializeToString(securityInfo);
|
||||
}
|
||||
|
||||
function getSiteBlockedErrorDetails(docShell) {
|
||||
let blockedInfo = {};
|
||||
if (docShell.failedChannel) {
|
||||
let classifiedChannel = docShell.failedChannel.
|
||||
QueryInterface(Ci.nsIClassifiedChannel);
|
||||
if (classifiedChannel) {
|
||||
let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
let reportUri = httpChannel.URI.clone();
|
||||
|
||||
// Remove the query to avoid leaking sensitive data
|
||||
if (reportUri instanceof Ci.nsIURL) {
|
||||
reportUri.query = "";
|
||||
}
|
||||
|
||||
blockedInfo = { list: classifiedChannel.matchedList,
|
||||
provider: classifiedChannel.matchedProvider,
|
||||
uri: reportUri.asciiSpec };
|
||||
}
|
||||
}
|
||||
return blockedInfo;
|
||||
}
|
||||
|
||||
addMessageListener("DeceptiveBlockedDetails", (message) => {
|
||||
sendAsyncMessage("DeceptiveBlockedDetails:Result", {
|
||||
blockedInfo: getSiteBlockedErrorDetails(docShell),
|
||||
});
|
||||
});
|
||||
|
||||
var AboutNetAndCertErrorListener = {
|
||||
init(chromeGlobal) {
|
||||
addMessageListener("CertErrorDetails", this);
|
||||
|
@ -583,32 +612,13 @@ var ClickEventHandler = {
|
|||
let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
let blockedInfo = {};
|
||||
if (docShell.failedChannel) {
|
||||
let classifiedChannel = docShell.failedChannel.
|
||||
QueryInterface(Ci.nsIClassifiedChannel);
|
||||
if (classifiedChannel) {
|
||||
let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
let reportUri = httpChannel.URI.clone();
|
||||
|
||||
// Remove the query to avoid leaking sensitive data
|
||||
if (reportUri instanceof Ci.nsIURL) {
|
||||
reportUri.query = "";
|
||||
}
|
||||
|
||||
blockedInfo = { list: classifiedChannel.matchedList,
|
||||
provider: classifiedChannel.matchedProvider,
|
||||
uri: reportUri.asciiSpec };
|
||||
}
|
||||
}
|
||||
|
||||
sendAsyncMessage("Browser:SiteBlockedError", {
|
||||
location: ownerDoc.location.href,
|
||||
reason,
|
||||
elementId: targetElement.getAttribute("id"),
|
||||
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
|
||||
blockedInfo
|
||||
blockedInfo: getSiteBlockedErrorDetails(docShell),
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
accesskey="&safeb.palm.notdeceptive.accesskey;"
|
||||
insertbefore="aboutSeparator"
|
||||
observes="reportPhishingErrorBroadcaster"
|
||||
oncommand="openUILinkIn(gSafeBrowsing.getReportURL('PhishMistake'), 'tab');"
|
||||
oncommand="ReportFalseDeceptiveSite();"
|
||||
onclick="checkForMiddleClick(this, event);"/>
|
||||
</menupopup>
|
||||
</overlay>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
// Bug 1318389 - This test does a lot of window and tab manipulation,
|
||||
// causing it to take a long time on debug.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(setupPrefsAndRecentWindowBehavior);
|
||||
|
||||
// Each of the test cases below is run twice: once for login-success and once
|
||||
|
|
|
@ -6,7 +6,7 @@ var {Weave} = Cu.import("resource://services-sync/main.js", {});
|
|||
|
||||
var stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Ci.nsIStringBundleService)
|
||||
.createBundle("chrome://weave/locale/services/sync.properties");
|
||||
.createBundle("chrome://weave/locale/sync.properties");
|
||||
|
||||
// ensure test output sees log messages.
|
||||
Log.repository.getLogger("browserwindow.syncui").addAppender(new Log.DumpAppender());
|
||||
|
|
|
@ -4,39 +4,31 @@
|
|||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
var testPage = "data:text/html,<body><style>:-moz-window-inactive { background-color: red; }</style><div id='area'></div></body>";
|
||||
var testPage = "data:text/html;charset=utf-8,<body><style>:-moz-window-inactive { background-color: red; }</style><div id='area'></div></body>";
|
||||
|
||||
var colorChangeNotifications = 0;
|
||||
var otherWindow;
|
||||
|
||||
var browser1, browser2;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
waitForFocus(reallyRunTests);
|
||||
}
|
||||
add_task(function* reallyRunTests() {
|
||||
|
||||
function reallyRunTests() {
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
|
||||
browser1 = tab1.linkedBrowser;
|
||||
|
||||
let tab1 = gBrowser.addTab();
|
||||
let tab2 = gBrowser.addTab();
|
||||
browser1 = gBrowser.getBrowserForTab(tab1);
|
||||
browser2 = gBrowser.getBrowserForTab(tab2);
|
||||
// This can't use openNewForegroundTab because if we focus tab2 now, we
|
||||
// won't send a focus event during test 6, further down in this file.
|
||||
let tab2 = gBrowser.addTab(testPage);
|
||||
browser2 = tab2.linkedBrowser;
|
||||
yield BrowserTestUtils.browserLoaded(browser2);
|
||||
|
||||
browser1.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
|
||||
browser2.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
|
||||
|
||||
gURLBar.focus();
|
||||
|
||||
var loadCount = 0;
|
||||
function check() {
|
||||
// wait for both tabs to load
|
||||
if (++loadCount != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
browser1.removeEventListener("load", check, true);
|
||||
browser2.removeEventListener("load", check, true);
|
||||
|
||||
sendGetBackgroundRequest(true);
|
||||
}
|
||||
let testFinished = {};
|
||||
testFinished.promise = new Promise(resolve => testFinished.resolve = resolve);
|
||||
|
||||
// The test performs four checks, using -moz-window-inactive on two child tabs.
|
||||
// First, the initial state should be transparent. The second check is done
|
||||
|
@ -74,7 +66,7 @@ function reallyRunTests() {
|
|||
break;
|
||||
case 8:
|
||||
is(message.data.color, "rgba(0, 0, 0, 0)", "second window after tab switch");
|
||||
finishTest();
|
||||
testFinished.resolve();
|
||||
break;
|
||||
case 9:
|
||||
ok(false, "too many color change notifications");
|
||||
|
@ -97,16 +89,17 @@ function reallyRunTests() {
|
|||
ok(message.data.ok, "Test:DeactivateEvent");
|
||||
});
|
||||
|
||||
browser1.addEventListener("load", check, true);
|
||||
browser2.addEventListener("load", check, true);
|
||||
browser1.contentWindow.location = testPage;
|
||||
browser2.contentWindow.location = testPage;
|
||||
|
||||
browser1.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
|
||||
browser2.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
}
|
||||
|
||||
// Start the test.
|
||||
sendGetBackgroundRequest(true);
|
||||
|
||||
yield testFinished.promise;
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
yield BrowserTestUtils.removeTab(tab2);
|
||||
otherWindow = null;
|
||||
});
|
||||
|
||||
function sendGetBackgroundRequest(ifChanged) {
|
||||
browser1.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged });
|
||||
|
@ -114,19 +107,12 @@ function sendGetBackgroundRequest(ifChanged) {
|
|||
}
|
||||
|
||||
function runOtherWindowTests() {
|
||||
otherWindow = window.open("data:text/html,<body>Hi</body>", "", "chrome");
|
||||
otherWindow = window.open("data:text/html;charset=utf-8,<body>Hi</body>", "", "chrome");
|
||||
waitForFocus(function() {
|
||||
sendGetBackgroundRequest(true);
|
||||
}, otherWindow);
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
gBrowser.removeCurrentTab();
|
||||
gBrowser.removeCurrentTab();
|
||||
otherWindow = null;
|
||||
finish();
|
||||
}
|
||||
|
||||
function childFunction() {
|
||||
let oldColor = null;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ skip-if = (os == "linux" && (debug || asan))
|
|||
[browser_popupNotification_4.js]
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
[browser_popupNotification_5.js]
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
skip-if = true # bug 1332646
|
||||
[browser_popupNotification_checkbox.js]
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
[browser_popupNotification_keyboard.js]
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
support-files =
|
||||
head.js
|
||||
|
||||
[browser_all_files_referenced.js]
|
||||
skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
|
||||
[browser_misused_characters_in_strings.js]
|
||||
support-files =
|
||||
bug1262648_string_with_newlines.dtd
|
||||
|
|
|
@ -0,0 +1,498 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var isDevtools = SimpleTest.harnessParameters.subsuite == "devtools";
|
||||
|
||||
var gExceptionPaths = ["chrome://browser/content/defaultthemes/",
|
||||
"chrome://browser/locale/searchplugins/"];
|
||||
|
||||
var whitelist = new Set([
|
||||
// browser/extensions/pdfjs/content/PdfStreamConverter.jsm
|
||||
{file: "chrome://pdf.js/locale/chrome.properties"},
|
||||
{file: "chrome://pdf.js/locale/viewer.properties"},
|
||||
|
||||
// security/manager/pki/resources/content/device_manager.js
|
||||
{file: "chrome://pippki/content/load_device.xul"},
|
||||
|
||||
// browser/modules/ReaderParent.jsm
|
||||
{file: "chrome://browser/skin/reader-tour.png"},
|
||||
{file: "chrome://browser/skin/reader-tour@2x.png"},
|
||||
|
||||
// Used by setting this url as a pref in about:config
|
||||
{file: "chrome://browser/content/newtab/alternativeDefaultSites.json"},
|
||||
|
||||
// Add-on compat
|
||||
{file: "chrome://browser/skin/devtools/common.css"},
|
||||
{file: "chrome://global/content/XPCNativeWrapper.js"},
|
||||
{file: "chrome://global/locale/brand.dtd"},
|
||||
|
||||
// The l10n build system can't package string files only for some platforms.
|
||||
// See bug 1339424 for why this is hard to fix.
|
||||
{file: "chrome://global/locale/fallbackMenubar.properties",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/locale/printPageSetup.dtd", platforms: ["macosx"]},
|
||||
{file: "chrome://global/locale/printPreviewProgress.dtd",
|
||||
platforms: ["macosx"]},
|
||||
{file: "chrome://global/locale/printProgress.dtd", platforms: ["macosx"]},
|
||||
{file: "chrome://global/locale/printdialog.dtd",
|
||||
platforms: ["macosx", "win"]},
|
||||
{file: "chrome://global/locale/printjoboptions.dtd",
|
||||
platforms: ["macosx", "win"]},
|
||||
|
||||
// services/cloudsync/CloudSyncLocal.jsm
|
||||
{file: "chrome://weave/locale/errors.properties"},
|
||||
|
||||
// devtools/client/inspector/bin/dev-server.js
|
||||
{file: "chrome://devtools/content/inspector/markup/markup.xhtml",
|
||||
isFromDevTools: true},
|
||||
|
||||
// Starting from here, files in the whitelist are bugs that need fixing.
|
||||
// Bug 1339420
|
||||
{file: "chrome://branding/content/icon128.png"},
|
||||
// Bug 1339424 (wontfix?)
|
||||
{file: "chrome://browser/locale/taskbar.properties",
|
||||
platforms: ["linux", "macosx"]},
|
||||
// Bug 1320156
|
||||
{file: "chrome://browser/skin/Privacy-16.png", platforms: ["linux"]},
|
||||
// Bug 1343584
|
||||
{file: "chrome://browser/skin/click-to-play-warning-stripes.png"},
|
||||
// Bug 1343824
|
||||
{file: "chrome://browser/skin/customizableui/customize-illustration-rtl@2x.png",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://browser/skin/customizableui/customize-illustration@2x.png",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://browser/skin/customizableui/panelarrow-customizeTip@2x.png",
|
||||
platforms: ["linux", "win"]},
|
||||
// Bug 1320058
|
||||
{file: "chrome://browser/skin/preferences/saveFile.png", platforms: ["win"]},
|
||||
// Bug 1348369
|
||||
{file: "chrome://formautofill/content/editProfile.xhtml"},
|
||||
// Bug 1316187
|
||||
{file: "chrome://global/content/customizeToolbar.xul"},
|
||||
// Bug 1343837
|
||||
{file: "chrome://global/content/findUtils.js"},
|
||||
// Bug 1343843
|
||||
{file: "chrome://global/content/url-classifier/unittests.xul"},
|
||||
// Bug 1343839
|
||||
{file: "chrome://global/locale/headsUpDisplay.properties"},
|
||||
// Bug 1348358
|
||||
{file: "chrome://global/skin/arrow.css"},
|
||||
{file: "chrome://global/skin/arrow/arrow-dn-sharp.gif",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/arrow/arrow-down.png",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/arrow/arrow-lft-sharp-end.gif"},
|
||||
{file: "chrome://global/skin/arrow/arrow-lft-sharp.gif",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/arrow/arrow-rit-sharp-end.gif"},
|
||||
{file: "chrome://global/skin/arrow/arrow-rit-sharp.gif",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/arrow/arrow-up-sharp.gif",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/arrow/panelarrow-horizontal.svg",
|
||||
platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/arrow/panelarrow-vertical.svg",
|
||||
platforms: ["linux"]},
|
||||
// Bug 1348529
|
||||
{file: "chrome://global/skin/checkbox/cbox-check-dis.gif",
|
||||
platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/checkbox/cbox-check.gif", platforms: ["linux"]},
|
||||
// Bug 1348359
|
||||
{file: "chrome://global/skin/dirListing/folder.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/dirListing/local.png", platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/dirListing/remote.png"},
|
||||
{file: "chrome://global/skin/dirListing/up.png", platforms: ["linux"]},
|
||||
// Bug 1348362
|
||||
{file: "chrome://global/skin/icons/Close.gif", platforms: ["win"]},
|
||||
{file: "chrome://global/skin/icons/Error.png", platforms: ["linux", "macosx"]},
|
||||
{file: "chrome://global/skin/icons/Landscape.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/Minimize.gif", platforms: ["win"]},
|
||||
{file: "chrome://global/skin/icons/Portrait.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/Print-preview.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/Question.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/Restore.gif", platforms: ["win"]},
|
||||
{file: "chrome://global/skin/icons/Search-close.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/Search-glass.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/Warning.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/checkbox.png", platforms: ["macosx"]},
|
||||
{file: "chrome://global/skin/icons/checkbox@2x.png", platforms: ["macosx"]},
|
||||
{file: "chrome://global/skin/icons/close-inverted.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/close-inverted@2x.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/close.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/close@2x.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/collapse.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/error-64.png", platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/icons/error-large.png", platforms: ["macosx"]},
|
||||
{file: "chrome://global/skin/icons/expand.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/folder-item.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/question-large.png", platforms: ["macosx"]},
|
||||
{file: "chrome://global/skin/icons/warning-32.png", platforms: ["macosx"]},
|
||||
{file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux", "win"]},
|
||||
{file: "chrome://global/skin/icons/warning-large.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/icons/windowControls.png", platforms: ["linux"]},
|
||||
// Bug 1348521
|
||||
{file: "chrome://global/skin/linkTree.css"},
|
||||
// Bug 1348522
|
||||
{file: "chrome://global/skin/media/clicktoplay-bgtexture.png"},
|
||||
{file: "chrome://global/skin/media/videoClickToPlayButton.svg"},
|
||||
// Bug 1348524
|
||||
{file: "chrome://global/skin/notification/close.png", platforms: ["macosx"]},
|
||||
// Bug 1348525
|
||||
{file: "chrome://global/skin/splitter/grip-bottom.gif", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/splitter/grip-left.gif", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/splitter/grip-right.gif", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/splitter/grip-top.gif", platforms: ["linux"]},
|
||||
// Bug 1348526
|
||||
{file: "chrome://global/skin/tree/sort-asc-classic.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/tree/sort-asc.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/tree/sort-dsc-classic.png", platforms: ["linux"]},
|
||||
{file: "chrome://global/skin/tree/sort-dsc.png", platforms: ["linux"]},
|
||||
// Bug 1344267
|
||||
{file: "chrome://marionette/content/test_anonymous_content.xul"},
|
||||
{file: "chrome://marionette/content/test_dialog.properties"},
|
||||
{file: "chrome://marionette/content/test_dialog.xul"},
|
||||
// Bug 1348532
|
||||
{file: "chrome://mozapps/content/extensions/list.xul"},
|
||||
// Bug 1348533
|
||||
{file: "chrome://mozapps/skin/downloads/buttons.png", platforms: ["macosx"]},
|
||||
{file: "chrome://mozapps/skin/downloads/downloadButtons.png", platforms: ["linux", "win"]},
|
||||
// Bug 1348555
|
||||
{file: "chrome://mozapps/skin/extensions/dictionaryGeneric-16.png"},
|
||||
{file: "chrome://mozapps/skin/extensions/search.png", platforms: ["macosx"]},
|
||||
{file: "chrome://mozapps/skin/extensions/themeGeneric-16.png"},
|
||||
// Bug 1348556
|
||||
{file: "chrome://mozapps/skin/plugins/pluginBlocked.png"},
|
||||
// Bug 1348558
|
||||
{file: "chrome://mozapps/skin/update/downloadButtons.png",
|
||||
platforms: ["linux"]},
|
||||
// Bug 1348559
|
||||
{file: "chrome://pippki/content/resetpassword.xul"},
|
||||
// Bug 1348562
|
||||
{file: "chrome://devtools/skin/images/debugging-devices.svg",
|
||||
isFromDevTools: true},
|
||||
{file: "chrome://devtools/skin/images/fast-forward.svg",
|
||||
isFromDevTools: true},
|
||||
{file: "chrome://devtools/skin/images/firebug/spinner.png",
|
||||
isFromDevTools: true},
|
||||
{file: "chrome://devtools/skin/images/noise.png",
|
||||
isFromDevTools: true},
|
||||
|
||||
].filter(item =>
|
||||
("isFromDevTools" in item) == isDevtools &&
|
||||
(!item.platforms || item.platforms.includes(AppConstants.platform))
|
||||
).map(item => item.file));
|
||||
|
||||
const ignorableWhitelist = new Set([
|
||||
// chrome://xslt-qa/ isn't referenced, but isn't included in packaged builds,
|
||||
// so it's fine to just ignore it and ignore if the exceptions are unused.
|
||||
"chrome://xslt-qa/content/buster/result-view.xul",
|
||||
"chrome://xslt-qa/content/xslt-qa-overlay.xul",
|
||||
// The communicator.css file is kept for add-on backward compat, but it is
|
||||
// referenced by something in xslt-qa, so the exception won't be used when
|
||||
// running the test on a local non-packaged build.
|
||||
"chrome://communicator/skin/communicator.css",
|
||||
|
||||
// These 2 files are unreferenced only when building without the crash
|
||||
// reporter (eg. Linux x64 asan builds on treeherder)
|
||||
"chrome://global/locale/crashes.dtd",
|
||||
"chrome://global/locale/crashes.properties",
|
||||
]);
|
||||
for (let entry of ignorableWhitelist)
|
||||
whitelist.add(entry);
|
||||
|
||||
const gInterestingCategories = new Set([
|
||||
"agent-style-sheets", "webextension-scripts",
|
||||
"webextension-schemas", "webextension-scripts-addon",
|
||||
"webextension-scripts-content", "webextension-scripts-devtools"
|
||||
]);
|
||||
|
||||
var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
||||
.getService(Ci.nsIChromeRegistry);
|
||||
var gChromeMap = new Map();
|
||||
var gOverrideMap = new Map();
|
||||
var gReferencesFromCode = new Set();
|
||||
|
||||
function getBaseUriForChromeUri(chromeUri) {
|
||||
let chromeFile = chromeUri + "gobbledygooknonexistentfile.reallynothere";
|
||||
let uri = Services.io.newURI(chromeFile);
|
||||
let fileUri = gChromeReg.convertChromeURL(uri);
|
||||
return fileUri.resolve(".");
|
||||
}
|
||||
|
||||
function parseManifest(manifestUri) {
|
||||
return fetchFile(manifestUri.spec).then(data => {
|
||||
for (let line of data.split("\n")) {
|
||||
let [type, ...argv] = line.split(/\s+/);
|
||||
if (type == "content" || type == "skin" || type == "locale") {
|
||||
let chromeUri = `chrome://${argv[0]}/${type}/`;
|
||||
gChromeMap.set(getBaseUriForChromeUri(chromeUri), chromeUri);
|
||||
} else if (type == "override" || type == "overlay") {
|
||||
// Overlays aren't really overrides, but behave the same in
|
||||
// that the overlay is only referenced if the original xul
|
||||
// file is referenced somewhere.
|
||||
let os = "os=" + Services.appinfo.OS;
|
||||
if (!argv.some(s => s.startsWith("os=") && s != os)) {
|
||||
gOverrideMap.set(Services.io.newURI(argv[1]).specIgnoringRef,
|
||||
Services.io.newURI(argv[0]).specIgnoringRef);
|
||||
}
|
||||
} else if (type == "category" && gInterestingCategories.has(argv[0])) {
|
||||
gReferencesFromCode.add(argv[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function parseCSSFile(fileUri) {
|
||||
return fetchFile(fileUri.spec).then(data => {
|
||||
for (let line of data.split("\n")) {
|
||||
let urls = line.match(/url\([^()]+\)/g);
|
||||
if (!urls) {
|
||||
// @import rules can take a string instead of a url.
|
||||
let importMatch = line.match(/@import ['"]?([^'"]*)['"]?/);
|
||||
if (importMatch && importMatch[1]) {
|
||||
let url = Services.io.newURI(importMatch[1], null, fileUri).spec;
|
||||
gReferencesFromCode.add(convertToChromeUri(url));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let url of urls) {
|
||||
// Remove the url(" prefix and the ") suffix.
|
||||
url = url.replace(/url\(([^)]*)\)/, "$1")
|
||||
.replace(/^"(.*)"$/, "$1")
|
||||
.replace(/^'(.*)'$/, "$1");
|
||||
if (url.startsWith("data:"))
|
||||
continue;
|
||||
|
||||
try {
|
||||
url = Services.io.newURI(url, null, fileUri).specIgnoringRef;
|
||||
gReferencesFromCode.add(convertToChromeUri(url));
|
||||
} catch (e) {
|
||||
ok(false, "unexpected error while resolving this URI: " + url);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function parseCodeFile(fileUri) {
|
||||
return fetchFile(fileUri.spec).then(data => {
|
||||
for (let line of data.split("\n")) {
|
||||
let urls =
|
||||
line.match(/["']chrome:\/\/[a-zA-Z0-9 -]+\/(content|skin|locale)\/[^"' ]*["']/g);
|
||||
if (!urls) {
|
||||
// If there's no absolute chrome URL, look for relative ones in
|
||||
// src and href attributes.
|
||||
let match = line.match("(?:src|href)=[\"']([^$&\"']+)");
|
||||
if (match && match[1]) {
|
||||
let url = Services.io.newURI(match[1], null, fileUri).spec;
|
||||
gReferencesFromCode.add(convertToChromeUri(url));
|
||||
}
|
||||
|
||||
if (isDevtools) {
|
||||
// Handle usage of devtools' LocalizationHelper object
|
||||
match = line.match('"devtools/client/locales/([^/.]+).properties"');
|
||||
if (match && match[1]) {
|
||||
gReferencesFromCode.add("chrome://devtools/locale/" +
|
||||
match[1] + ".properties");
|
||||
}
|
||||
|
||||
match = line.match('"devtools/shared/locales/([^/.]+).properties"');
|
||||
if (match && match[1]) {
|
||||
gReferencesFromCode.add("chrome://devtools-shared/locale/" +
|
||||
match[1] + ".properties");
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let url of urls) {
|
||||
// Remove quotes.
|
||||
url = url.slice(1, -1);
|
||||
// Remove ? or \ trailing characters.
|
||||
if (url.endsWith("?") || url.endsWith("\\"))
|
||||
url = url.slice(0, -1);
|
||||
|
||||
// Make urls like chrome://browser/skin/ point to an actual file,
|
||||
// and remove the ref if any.
|
||||
url = Services.io.newURI(url).specIgnoringRef;
|
||||
|
||||
gReferencesFromCode.add(url);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function convertToChromeUri(fileUri) {
|
||||
let baseUri = fileUri;
|
||||
let path = "";
|
||||
while (true) {
|
||||
let slashPos = baseUri.lastIndexOf("/", baseUri.length - 2);
|
||||
if (slashPos <= 0) {
|
||||
// File not accessible from chrome protocol,
|
||||
// TODO: bug 1349005 handle resource:// urls.
|
||||
return fileUri;
|
||||
}
|
||||
path = baseUri.slice(slashPos + 1) + path;
|
||||
baseUri = baseUri.slice(0, slashPos + 1);
|
||||
if (gChromeMap.has(baseUri)) {
|
||||
let chromeBaseUri = gChromeMap.get(baseUri);
|
||||
return `${chromeBaseUri}${path}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function chromeFileExists(aURI) {
|
||||
let available = 0;
|
||||
try {
|
||||
let channel = NetUtil.newChannel({uri: aURI, loadUsingSystemPrincipal: true});
|
||||
let stream = channel.open();
|
||||
let sstream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
sstream.init(stream);
|
||||
available = sstream.available();
|
||||
sstream.close();
|
||||
} catch (e) {
|
||||
if (e.result != Components.results.NS_ERROR_FILE_NOT_FOUND) {
|
||||
dump("Checking " + aURI + ": " + e + "\n");
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
return available > 0;
|
||||
}
|
||||
|
||||
function findChromeUrlsFromArray(array) {
|
||||
const prefix = "chrome://";
|
||||
// Find the 'c' character...
|
||||
for (let index = 0;
|
||||
(index = array.indexOf(prefix.charCodeAt(0), index)) != -1;
|
||||
++index) {
|
||||
// Then ensure we actually have the whole chrome:// prefix.
|
||||
let found = true;
|
||||
for (let i = 1; i < prefix.length; ++i) {
|
||||
if (array[index + i] != prefix.charCodeAt(i)) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
continue;
|
||||
|
||||
// C strings are null terminated, but " also terminates urls
|
||||
// (nsIndexedToHTML.cpp contains an HTML fragment with several chrome urls)
|
||||
// Let's also terminate the string on the # character to skip references.
|
||||
let end = Math.min(array.indexOf(0, index),
|
||||
array.indexOf('"'.charCodeAt(0), index),
|
||||
array.indexOf("#".charCodeAt(0), index));
|
||||
let string = "";
|
||||
for ( ; index < end; ++index)
|
||||
string += String.fromCharCode(array[index]);
|
||||
|
||||
// Only keep strings that look like real chrome urls.
|
||||
if (/chrome:\/\/[a-zA-Z09 -]+\/(content|skin|locale)\//.test(string))
|
||||
gReferencesFromCode.add(string);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(function* checkAllTheFiles() {
|
||||
let libxulPath = OS.Constants.Path.libxul;
|
||||
if (AppConstants.platform != "macosx")
|
||||
libxulPath = OS.Constants.Path.libDir + "/" + libxulPath;
|
||||
let libxul = yield OS.File.read(libxulPath);
|
||||
findChromeUrlsFromArray(libxul);
|
||||
// Handle NS_LITERAL_STRING.
|
||||
findChromeUrlsFromArray(new Uint16Array(libxul.buffer));
|
||||
|
||||
const kCodeExtensions = [".xul", ".xml", ".xsl", ".js", ".jsm", ".html", ".xhtml"];
|
||||
|
||||
let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
|
||||
// This asynchronously produces a list of URLs (sadly, mostly sync on our
|
||||
// test infrastructure because it runs against jarfiles there, and
|
||||
// our zipreader APIs are all sync)
|
||||
let uris = yield generateURIsFromDirTree(appDir, [".css", ".manifest", ".json", ".jpg", ".png", ".gif", ".svg", ".dtd", ".properties"].concat(kCodeExtensions));
|
||||
|
||||
// Parse and remove all manifests from the list.
|
||||
// NOTE that this must be done before filtering out devtools paths
|
||||
// so that all chrome paths can be recorded.
|
||||
let manifestPromises = [];
|
||||
uris = uris.filter(uri => {
|
||||
let path = uri.path;
|
||||
if (path.endsWith(".manifest")) {
|
||||
manifestPromises.push(parseManifest(uri));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// Wait for all manifest to be parsed
|
||||
yield Promise.all(manifestPromises);
|
||||
|
||||
// We build a list of promises that get resolved when their respective
|
||||
// files have loaded and produced no errors.
|
||||
let allPromises = [];
|
||||
|
||||
for (let uri of uris) {
|
||||
let path = uri.path;
|
||||
if (path.endsWith(".css"))
|
||||
allPromises.push(parseCSSFile(uri));
|
||||
else if (kCodeExtensions.some(ext => path.endsWith(ext)))
|
||||
allPromises.push(parseCodeFile(uri));
|
||||
}
|
||||
|
||||
// Wait for all the files to have actually loaded:
|
||||
yield Promise.all(allPromises);
|
||||
|
||||
// Keep only chrome:// files, and filter out either the devtools paths or
|
||||
// the non-devtools paths:
|
||||
let devtoolsPrefixes = ["chrome://webide/", "chrome://devtools"];
|
||||
let chromeFiles =
|
||||
uris.map(uri => convertToChromeUri(uri.spec))
|
||||
.filter(u => u.startsWith("chrome://"))
|
||||
.filter(u => isDevtools == devtoolsPrefixes.some(prefix => u.startsWith(prefix)));
|
||||
|
||||
let isUnreferenced =
|
||||
file => !gReferencesFromCode.has(file) &&
|
||||
!gExceptionPaths.some(e => file.startsWith(e)) &&
|
||||
(!gOverrideMap.has(file) || isUnreferenced(gOverrideMap.get(file)));
|
||||
|
||||
let notWhitelisted = file => {
|
||||
if (!whitelist.has(file))
|
||||
return true;
|
||||
whitelist.delete(file);
|
||||
return false;
|
||||
};
|
||||
|
||||
let unreferencedFiles =
|
||||
chromeFiles.filter(isUnreferenced).filter(notWhitelisted).sort();
|
||||
|
||||
is(unreferencedFiles.length, 0, "there should be no unreferenced files");
|
||||
for (let file of unreferencedFiles)
|
||||
ok(false, "unreferenced chrome file: " + file);
|
||||
|
||||
for (let file of whitelist) {
|
||||
if (ignorableWhitelist.has(file))
|
||||
info("ignored unused whitelist entry: " + file);
|
||||
else
|
||||
ok(false, "unused whitelist entry: " + file);
|
||||
}
|
||||
|
||||
for (let file of gReferencesFromCode) {
|
||||
if (isDevtools != devtoolsPrefixes.some(prefix => file.startsWith(prefix)))
|
||||
continue;
|
||||
|
||||
if (file.startsWith("chrome://") && !chromeFileExists(file)) {
|
||||
// Ignore chrome prefixes that have been automatically expanded.
|
||||
let pathParts =
|
||||
file.match("chrome://([^/]+)/content/([^/.]+)\.xul") ||
|
||||
file.match("chrome://([^/]+)/skin/([^/.]+)\.css");
|
||||
if (!pathParts || pathParts[1] != pathParts[2]) {
|
||||
// TODO: bug 1349010 - add a whitelist and make this reliable enough
|
||||
// that we could make the test fail when this catches something new.
|
||||
info("missing file with code reference: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -129,30 +129,6 @@ function ignoredError(filepath, key, type) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function fetchFile(uri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "text";
|
||||
xhr.open("GET", uri, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState != this.DONE) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resolve(this.responseText);
|
||||
} catch (ex) {
|
||||
ok(false, `Script error reading ${uri}: ${ex}`);
|
||||
resolve("");
|
||||
}
|
||||
};
|
||||
xhr.onerror = error => {
|
||||
ok(false, `XHR error reading ${uri}: ${error}`);
|
||||
resolve("");
|
||||
};
|
||||
xhr.send(null);
|
||||
});
|
||||
}
|
||||
|
||||
function testForError(filepath, key, str, pattern, type, helpText) {
|
||||
if (str.match(pattern) &&
|
||||
!ignoredError(filepath, key, type)) {
|
||||
|
|
|
@ -107,30 +107,6 @@ function once(target, name) {
|
|||
});
|
||||
}
|
||||
|
||||
function fetchFile(uri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "text";
|
||||
xhr.open("GET", uri, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState != this.DONE) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resolve(this.responseText);
|
||||
} catch (ex) {
|
||||
ok(false, `Script error reading ${uri}: ${ex}`);
|
||||
resolve("");
|
||||
}
|
||||
};
|
||||
xhr.onerror = error => {
|
||||
ok(false, `XHR error reading ${uri}: ${error}`);
|
||||
resolve("");
|
||||
};
|
||||
xhr.send(null);
|
||||
});
|
||||
}
|
||||
|
||||
var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
||||
.getService(Ci.nsIChromeRegistry);
|
||||
var gChromeMap = new Map();
|
||||
|
|
|
@ -125,3 +125,27 @@ function* generateEntriesFromJarFile(jarFile, extension) {
|
|||
}
|
||||
zr.close();
|
||||
}
|
||||
|
||||
function fetchFile(uri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "text";
|
||||
xhr.open("GET", uri, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState != this.DONE) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resolve(this.responseText);
|
||||
} catch (ex) {
|
||||
ok(false, `Script error reading ${uri}: ${ex}`);
|
||||
resolve("");
|
||||
}
|
||||
};
|
||||
xhr.onerror = error => {
|
||||
ok(false, `XHR error reading ${uri}: ${error}`);
|
||||
resolve("");
|
||||
};
|
||||
xhr.send(null);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -48,6 +48,9 @@ add_task(function* () {
|
|||
resolve();
|
||||
}, {once: true});
|
||||
gURLBar.focus();
|
||||
if (gURLBar.selectionStart == gURLBar.selectionEnd) {
|
||||
gURLBar.selectionStart = gURLBar.selectionEnd = gURLBar.textValue.length;
|
||||
}
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ add_task(function*() {
|
|||
let promiseLoad = waitForDocLoadAndStopIt(expectedURL);
|
||||
gURLBar.focus();
|
||||
if (Object.keys(options).length > 0) {
|
||||
gURLBar.selectionStart = gURLBar.selectionEnd =
|
||||
gURLBar.inputField.value.length;
|
||||
gURLBar.inputField.value = inputValue.slice(0, -1);
|
||||
EventUtils.synthesizeKey(inputValue.slice(-1), {});
|
||||
} else {
|
||||
|
|
|
@ -129,6 +129,7 @@ support-files =
|
|||
[browser_ext_webNavigation_frameId0.js]
|
||||
[browser_ext_webNavigation_getFrames.js]
|
||||
[browser_ext_webNavigation_onCreatedNavigationTarget.js]
|
||||
[browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js]
|
||||
[browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js]
|
||||
[browser_ext_webNavigation_urlbar_transitions.js]
|
||||
[browser_ext_windows.js]
|
||||
|
|
|
@ -52,14 +52,6 @@ async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
|
|||
is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
|
||||
}
|
||||
|
||||
async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
|
||||
const contentAreaContextMenu = await openContextMenu(pageElementSelector);
|
||||
const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
|
||||
is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
|
||||
item[0].click();
|
||||
await closeContextMenu();
|
||||
}
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_mouse_click() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
|
@ -127,59 +119,6 @@ add_task(function* test_on_created_navigation_target_from_mouse_click() {
|
|||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_context_menu() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open link in a new tab from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: "#test-create-new-tab-from-context-menu",
|
||||
contextMenuItemLabel: "Open Link in New Tab",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-tab-from-context-menu`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open link in a new window from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: "#test-create-new-window-from-context-menu",
|
||||
contextMenuItemLabel: "Open Link in New Window",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-window-from-context-menu`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_mouse_click_subframe() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
|
@ -256,65 +195,3 @@ add_task(function* test_on_created_navigation_target_from_mouse_click_subframe()
|
|||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_context_menu_subframe() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open a subframe link in a new tab from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: function() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.document.querySelector("#test-create-new-tab-from-context-menu-subframe");
|
||||
},
|
||||
contextMenuItemLabel: "Open Link in New Tab",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open a subframe link in a new window from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: function() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.document.querySelector("#test-create-new-window-from-context-menu-subframe");
|
||||
},
|
||||
contextMenuItemLabel: "Open Link in New Window",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-window-from-context-menu-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
|
||||
const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
|
||||
const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
|
||||
|
||||
async function background() {
|
||||
const tabs = await browser.tabs.query({active: true, currentWindow: true});
|
||||
const sourceTabId = tabs[0].id;
|
||||
|
||||
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
|
||||
|
||||
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
|
||||
browser.test.sendMessage("webNavOnCreated", msg);
|
||||
});
|
||||
|
||||
browser.webNavigation.onCompleted.addListener(async (msg) => {
|
||||
// NOTE: checking the url is currently necessary because of Bug 1252129
|
||||
// ( Filter out webNavigation events related to new window initialization phase).
|
||||
if (msg.tabId !== sourceTabId && msg.url !== "about:blank") {
|
||||
await browser.tabs.remove(msg.tabId);
|
||||
browser.test.sendMessage("webNavOnCompleted", msg);
|
||||
}
|
||||
});
|
||||
|
||||
browser.tabs.onCreated.addListener((tab) => {
|
||||
browser.test.sendMessage("tabsOnCreated", tab.id);
|
||||
});
|
||||
|
||||
browser.test.sendMessage("expectedSourceTab", {
|
||||
sourceTabId, sourceTabFrames,
|
||||
});
|
||||
}
|
||||
|
||||
async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
|
||||
await openNavTarget();
|
||||
|
||||
const webNavMsg = await extension.awaitMessage("webNavOnCreated");
|
||||
const createdTabId = await extension.awaitMessage("tabsOnCreated");
|
||||
const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
|
||||
|
||||
let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
|
||||
|
||||
is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
|
||||
is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
|
||||
is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
|
||||
is(webNavMsg.url, url, "Got the expected url property");
|
||||
|
||||
is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
|
||||
is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
|
||||
}
|
||||
|
||||
async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
|
||||
const contentAreaContextMenu = await openContextMenu(pageElementSelector);
|
||||
const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
|
||||
is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
|
||||
item[0].click();
|
||||
await closeContextMenu();
|
||||
}
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_context_menu() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open link in a new tab from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: "#test-create-new-tab-from-context-menu",
|
||||
contextMenuItemLabel: "Open Link in New Tab",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-tab-from-context-menu`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open link in a new window from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: "#test-create-new-window-from-context-menu",
|
||||
contextMenuItemLabel: "Open Link in New Window",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-window-from-context-menu`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_context_menu_subframe() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open a subframe link in a new tab from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: function() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.document.querySelector("#test-create-new-tab-from-context-menu-subframe");
|
||||
},
|
||||
contextMenuItemLabel: "Open Link in New Tab",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open a subframe link in a new window from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: function() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.document.querySelector("#test-create-new-window-from-context-menu-subframe");
|
||||
},
|
||||
contextMenuItemLabel: "Open Link in New Window",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-window-from-context-menu-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
|
@ -223,8 +223,7 @@ FeedConverter.prototype = {
|
|||
try {
|
||||
let title = feed.title ? feed.title.plainText() : "";
|
||||
let desc = feed.subtitle ? feed.subtitle.plainText() : "";
|
||||
let feedReader = safeGetCharPref(getPrefActionForType(feed.type), "bookmarks");
|
||||
feedService.addToClientReader(result.uri.spec, title, desc, feed.type, feedReader);
|
||||
feedService.addToClientReader(result.uri.spec, title, desc, feed.type, handler);
|
||||
return;
|
||||
} catch (ex) { /* fallback to preview mode */ }
|
||||
}
|
||||
|
|
|
@ -44,83 +44,11 @@ function makeURI(aURLSpec, aCharset) {
|
|||
const XML_NS = "http://www.w3.org/XML/1998/namespace";
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
|
||||
const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
|
||||
const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
|
||||
const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties";
|
||||
|
||||
const PREF_SELECTED_APP = "browser.feeds.handlers.application";
|
||||
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
|
||||
const PREF_SELECTED_ACTION = "browser.feeds.handler";
|
||||
const PREF_SELECTED_READER = "browser.feeds.handler.default";
|
||||
|
||||
const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
|
||||
const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
|
||||
const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
|
||||
const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
|
||||
|
||||
const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
|
||||
const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
|
||||
const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
|
||||
const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
|
||||
|
||||
const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
|
||||
|
||||
const TITLE_ID = "feedTitleText";
|
||||
const SUBTITLE_ID = "feedSubtitleText";
|
||||
|
||||
function getPrefAppForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_APP;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_APP;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_APP;
|
||||
}
|
||||
}
|
||||
|
||||
function getPrefWebForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_WEB;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_WEB;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_WEB;
|
||||
}
|
||||
}
|
||||
|
||||
function getPrefActionForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_ACTION;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_ACTION;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
function getPrefReaderForType(t) {
|
||||
switch (t) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return PREF_VIDEO_SELECTED_READER;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return PREF_AUDIO_SELECTED_READER;
|
||||
|
||||
default:
|
||||
return PREF_SELECTED_READER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number of bytes to the appropriate unit that results in a
|
||||
* number that needs fewer than 4 digits
|
||||
|
@ -153,9 +81,6 @@ function FeedWriter() {
|
|||
}
|
||||
|
||||
FeedWriter.prototype = {
|
||||
_mimeSvc : Cc["@mozilla.org/mime;1"].
|
||||
getService(Ci.nsIMIMEService),
|
||||
|
||||
_getPropertyAsBag(container, property) {
|
||||
return container.fields.getProperty(property).
|
||||
QueryInterface(Ci.nsIPropertyBag2);
|
||||
|
@ -226,18 +151,21 @@ FeedWriter.prototype = {
|
|||
return this._bundle.GetStringFromName(key);
|
||||
},
|
||||
|
||||
_setCheckboxCheckedState(aCheckbox, aValue) {
|
||||
// see checkbox.xml, xbl bindings are not applied within the sandbox! TODO
|
||||
let change = (aValue != (aCheckbox.getAttribute("checked") == "true"));
|
||||
if (aValue)
|
||||
aCheckbox.setAttribute("checked", "true");
|
||||
else
|
||||
aCheckbox.removeAttribute("checked");
|
||||
_setCheckboxCheckedState(aValue) {
|
||||
let checkbox = this._document.getElementById("alwaysUse");
|
||||
if (checkbox) {
|
||||
// see checkbox.xml, xbl bindings are not applied within the sandbox! TODO
|
||||
let change = (aValue != (checkbox.getAttribute("checked") == "true"));
|
||||
if (aValue)
|
||||
checkbox.setAttribute("checked", "true");
|
||||
else
|
||||
checkbox.removeAttribute("checked");
|
||||
|
||||
if (change) {
|
||||
let event = this._document.createEvent("Events");
|
||||
event.initEvent("CheckboxStateChange", true, true);
|
||||
aCheckbox.dispatchEvent(event);
|
||||
if (change) {
|
||||
let event = this._document.createEvent("Events");
|
||||
event.initEvent("CheckboxStateChange", true, true);
|
||||
checkbox.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -290,22 +218,6 @@ FeedWriter.prototype = {
|
|||
return Ci.nsIFeed.TYPE_FEED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maps a feed type to a maybe-feed mimetype.
|
||||
*/
|
||||
_getMimeTypeForFeedType() {
|
||||
switch (this._getFeedType()) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
return TYPE_MAYBE_VIDEO_FEED;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
return TYPE_MAYBE_AUDIO_FEED;
|
||||
|
||||
default:
|
||||
return TYPE_MAYBE_FEED;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes the feed title into the preview document.
|
||||
* @param container
|
||||
|
@ -491,17 +403,11 @@ FeedWriter.prototype = {
|
|||
|
||||
if (enc.hasKey("type")) {
|
||||
type_text = enc.get("type");
|
||||
try {
|
||||
let handlerInfoWrapper = this._mimeSvc.getFromTypeAndExtension(enc.get("type"), null);
|
||||
|
||||
if (handlerInfoWrapper)
|
||||
type_text = handlerInfoWrapper.description;
|
||||
|
||||
if (type_text && type_text.length > 0)
|
||||
mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
|
||||
|
||||
} catch (ex) { }
|
||||
if (enc.hasKey("typeDesc"))
|
||||
type_text = enc.get("typeDesc");
|
||||
|
||||
if (type_text && type_text.length > 0)
|
||||
mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
|
||||
}
|
||||
|
||||
if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) {
|
||||
|
@ -596,19 +502,7 @@ FeedWriter.prototype = {
|
|||
this._subscribeCallback = aCallback;
|
||||
this._mm.sendAsyncMessage("FeedWriter:ChooseClientApp",
|
||||
{ title: this._getString("chooseApplicationDialogTitle"),
|
||||
prefName: getPrefAppForType(this._getFeedType()) });
|
||||
},
|
||||
|
||||
_setAlwaysUseCheckedState(feedType) {
|
||||
let checkbox = this._document.getElementById("alwaysUse");
|
||||
if (checkbox) {
|
||||
let alwaysUse = false;
|
||||
try {
|
||||
if (Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask")
|
||||
alwaysUse = true;
|
||||
} catch (ex) { }
|
||||
this._setCheckboxCheckedState(checkbox, alwaysUse);
|
||||
}
|
||||
feedType: this._getFeedType() });
|
||||
},
|
||||
|
||||
_setSubscribeUsingLabel() {
|
||||
|
@ -671,13 +565,13 @@ FeedWriter.prototype = {
|
|||
}
|
||||
break;
|
||||
case "change":
|
||||
LOG("Change fired");
|
||||
if (event.target.selectedOptions[0].id == "chooseApplicationMenuItem") {
|
||||
this._chooseClientApp((aResult) => {
|
||||
if (!aResult) {
|
||||
// Select the (per-prefs) selected handler if no application
|
||||
// was selected
|
||||
this._setSelectedHandler(this._getFeedType());
|
||||
}
|
||||
this._chooseClientApp(() => {
|
||||
// Select the (per-prefs) selected handler if no application
|
||||
// was selected
|
||||
LOG("Selected handler after callback");
|
||||
this._setAlwaysUseLabel();
|
||||
});
|
||||
} else {
|
||||
this._setAlwaysUseLabel();
|
||||
|
@ -687,28 +581,18 @@ FeedWriter.prototype = {
|
|||
},
|
||||
|
||||
_getWebHandlerElementsForURL(aURL) {
|
||||
let menu = this._document.getElementById("handlersMenuList");
|
||||
return menu.querySelectorAll('[webhandlerurl="' + aURL + '"]');
|
||||
return this._handlersList.querySelectorAll('[webhandlerurl="' + aURL + '"]');
|
||||
},
|
||||
|
||||
_setSelectedHandler(feedType) {
|
||||
let prefs = Services.prefs;
|
||||
let handler = prefs.getCharPref(getPrefReaderForType(feedType), "bookmarks");
|
||||
|
||||
_setSelectedHandlerResponse(handler, url) {
|
||||
LOG(`Selecting handler response ${handler} ${url}`);
|
||||
switch (handler) {
|
||||
case "web": {
|
||||
if (this._handlersList) {
|
||||
let url;
|
||||
try {
|
||||
url = prefs.getStringPref(getPrefWebForType(feedType));
|
||||
} catch (ex) {
|
||||
LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs");
|
||||
return;
|
||||
}
|
||||
let handlers =
|
||||
this._getWebHandlerElementsForURL(url);
|
||||
if (handlers.length == 0) {
|
||||
LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist")
|
||||
LOG(`Selected web handler isn't in the menulist ${url}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -729,10 +613,10 @@ FeedWriter.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_initSubscriptionUI() {
|
||||
let handlersList = this._document.getElementById("handlersMenuList");
|
||||
if (!handlersList)
|
||||
_initSubscriptionUI(setupMessage) {
|
||||
if (!this._handlersList)
|
||||
return;
|
||||
LOG("UI init");
|
||||
|
||||
let feedType = this._getFeedType();
|
||||
|
||||
|
@ -763,7 +647,7 @@ FeedWriter.prototype = {
|
|||
menuItem.style.display = "none";
|
||||
this._selectedAppMenuItem = menuItem;
|
||||
|
||||
handlersList.appendChild(this._selectedAppMenuItem);
|
||||
this._handlersList.appendChild(this._selectedAppMenuItem);
|
||||
|
||||
// Create the menuitem for the default reader, but don't show/populate it until
|
||||
// we get confirmation of what it is from the parent
|
||||
|
@ -774,10 +658,7 @@ FeedWriter.prototype = {
|
|||
menuItem.style.display = "none";
|
||||
|
||||
this._defaultHandlerMenuItem = menuItem;
|
||||
handlersList.appendChild(this._defaultHandlerMenuItem);
|
||||
|
||||
this._mm.sendAsyncMessage("FeedWriter:RequestClientAppName",
|
||||
{ feedTypePref: getPrefAppForType(feedType) });
|
||||
this._handlersList.appendChild(this._defaultHandlerMenuItem);
|
||||
|
||||
// "Choose Application..." menuitem
|
||||
menuItem = liveBookmarksMenuItem.cloneNode(false);
|
||||
|
@ -785,18 +666,14 @@ FeedWriter.prototype = {
|
|||
menuItem.setAttribute("id", "chooseApplicationMenuItem");
|
||||
menuItem.textContent = this._getString("chooseApplicationMenuItem");
|
||||
|
||||
handlersList.appendChild(menuItem);
|
||||
this._handlersList.appendChild(menuItem);
|
||||
|
||||
// separator
|
||||
let chooseAppSep = liveBookmarksMenuItem.nextElementSibling.cloneNode(false);
|
||||
chooseAppSep.textContent = liveBookmarksMenuItem.nextElementSibling.textContent;
|
||||
handlersList.appendChild(chooseAppSep);
|
||||
this._handlersList.appendChild(chooseAppSep);
|
||||
|
||||
// List of web handlers
|
||||
let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
let handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType));
|
||||
for (let handler of handlers) {
|
||||
for (let handler of setupMessage.handlers) {
|
||||
if (!handler.uri) {
|
||||
LOG("Handler with name " + handler.name + " has no URI!? Skipping...");
|
||||
continue;
|
||||
|
@ -807,29 +684,37 @@ FeedWriter.prototype = {
|
|||
menuItem.textContent = handler.name;
|
||||
menuItem.setAttribute("handlerType", "web");
|
||||
menuItem.setAttribute("webhandlerurl", handler.uri);
|
||||
handlersList.appendChild(menuItem);
|
||||
this._handlersList.appendChild(menuItem);
|
||||
}
|
||||
|
||||
this._setSelectedHandler(feedType);
|
||||
this._setSelectedHandlerResponse(setupMessage.reader.handler, setupMessage.reader.url);
|
||||
|
||||
if (setupMessage.defaultMenuItem) {
|
||||
LOG(`Setting default menu item ${setupMessage.defaultMenuItem}`);
|
||||
this._setApplicationLauncherMenuItem(this._defaultHandlerMenuItem, setupMessage.defaultMenuItem);
|
||||
}
|
||||
if (setupMessage.selectedMenuItem) {
|
||||
LOG(`Setting selected menu item ${setupMessage.selectedMenuItem}`);
|
||||
this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, setupMessage.selectedMenuItem);
|
||||
}
|
||||
|
||||
// "Subscribe using..."
|
||||
this._setSubscribeUsingLabel();
|
||||
|
||||
// "Always use..." checkbox initial state
|
||||
this._setAlwaysUseCheckedState(feedType);
|
||||
this._setCheckboxCheckedState(setupMessage.reader.alwaysUse);
|
||||
this._setAlwaysUseLabel();
|
||||
|
||||
// We update the "Always use.." checkbox label whenever the selected item
|
||||
// in the list is changed
|
||||
handlersList.addEventListener("change", this);
|
||||
this._handlersList.addEventListener("change", this);
|
||||
|
||||
// Set up the "Subscribe Now" button
|
||||
this._document.getElementById("subscribeButton")
|
||||
.addEventListener("click", this);
|
||||
|
||||
// first-run ui
|
||||
let showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI, true);
|
||||
if (showFirstRunUI) {
|
||||
if (setupMessage.showFirstRunUI) {
|
||||
let textfeedinfo1, textfeedinfo2;
|
||||
switch (feedType) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
|
@ -915,53 +800,74 @@ FeedWriter.prototype = {
|
|||
|
||||
LOG("Subscribe Preview: feed uri = " + this._window.location.href);
|
||||
|
||||
// Set up the subscription UI
|
||||
this._initSubscriptionUI();
|
||||
let prefs = Services.prefs;
|
||||
prefs.addObserver(PREF_SELECTED_ACTION, this, false);
|
||||
prefs.addObserver(PREF_SELECTED_READER, this, false);
|
||||
prefs.addObserver(PREF_SELECTED_WEB, this, false);
|
||||
prefs.addObserver(PREF_SELECTED_APP, this, false);
|
||||
prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, false);
|
||||
prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, false);
|
||||
prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, false);
|
||||
prefs.addObserver(PREF_VIDEO_SELECTED_APP, this, false);
|
||||
|
||||
prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, false);
|
||||
prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, false);
|
||||
prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, false);
|
||||
prefs.addObserver(PREF_AUDIO_SELECTED_APP, this, false);
|
||||
|
||||
this._mm.addMessageListener("FeedWriter:PreferenceUpdated", this);
|
||||
this._mm.addMessageListener("FeedWriter:SetApplicationLauncherMenuItem", this);
|
||||
this._mm.addMessageListener("FeedWriter:GetSubscriptionUIResponse", this);
|
||||
this._mm.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribeResponse", this);
|
||||
|
||||
const feedType = this._getFeedType();
|
||||
this._mm.sendAsyncMessage("FeedWriter:GetSubscriptionUI",
|
||||
{ feedType });
|
||||
},
|
||||
|
||||
receiveMessage(msg) {
|
||||
if (!this._window) {
|
||||
// this._window is null unless this.init was called with a trusted
|
||||
// window object.
|
||||
return;
|
||||
}
|
||||
LOG(`received message from parent ${msg.name}`);
|
||||
switch (msg.name) {
|
||||
case "FeedWriter:SetApplicationLauncherMenuItem":
|
||||
let menuItem = null;
|
||||
|
||||
if (msg.data.type == "DefaultAppMenuItem") {
|
||||
menuItem = this._defaultHandlerMenuItem;
|
||||
} else {
|
||||
// Most likely SelectedAppMenuItem
|
||||
menuItem = this._selectedAppMenuItem;
|
||||
case "FeedWriter:PreferenceUpdated":
|
||||
// This is called when browser-feeds.js spots a pref change
|
||||
// This will happen when
|
||||
// - about:preferences#applications changes
|
||||
// - another feed reader page changes the preference
|
||||
// - when this page itself changes the select and there isn't a redirect
|
||||
// bookmarks and launching an external app means the page stays open after subscribe
|
||||
const feedType = this._getFeedType();
|
||||
LOG(`Got prefChange! ${JSON.stringify(msg.data)} current type: ${feedType}`);
|
||||
let feedTypePref = msg.data.default;
|
||||
if (feedType in msg.data) {
|
||||
feedTypePref = msg.data[feedType];
|
||||
}
|
||||
|
||||
menuItem.textContent = msg.data.name;
|
||||
menuItem.style.display = "";
|
||||
menuItem.selected = true;
|
||||
|
||||
LOG(`Got pref ${JSON.stringify(feedTypePref)}`);
|
||||
this._setCheckboxCheckedState(feedTypePref.alwaysUse);
|
||||
this._setSelectedHandlerResponse(feedTypePref.handler, feedTypePref.url);
|
||||
this._setAlwaysUseLabel();
|
||||
break;
|
||||
case "FeedWriter:SetFeedPrefsAndSubscribeResponse":
|
||||
LOG(`FeedWriter:SetFeedPrefsAndSubscribeResponse - Redirecting ${msg.data.redirect}`);
|
||||
this._window.location.href = msg.data.redirect;
|
||||
break;
|
||||
case "FeedWriter:GetSubscriptionUIResponse":
|
||||
// Set up the subscription UI
|
||||
this._initSubscriptionUI(msg.data);
|
||||
break;
|
||||
case "FeedWriter:SetApplicationLauncherMenuItem":
|
||||
LOG(`FeedWriter:SetApplicationLauncherMenuItem - picked ${msg.data.name}`);
|
||||
this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, msg.data.name);
|
||||
// Potentially a bit racy, but I don't think we can get into a state where this callback is set and
|
||||
// we're not coming back from ChooseClientApp in browser-feeds.js
|
||||
if (this._subscribeCallback) {
|
||||
this._subscribeCallback();
|
||||
this._subscribeCallback = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_setApplicationLauncherMenuItem(menuItem, aName) {
|
||||
/* unselect all handlers */
|
||||
[...this._handlersList.children].forEach((option) => {
|
||||
option.removeAttribute("selected");
|
||||
});
|
||||
menuItem.textContent = aName;
|
||||
menuItem.style.display = "";
|
||||
menuItem.selected = true;
|
||||
},
|
||||
|
||||
writeContent() {
|
||||
if (!this._window)
|
||||
return;
|
||||
|
@ -983,24 +889,11 @@ FeedWriter.prototype = {
|
|||
close() {
|
||||
this._document.getElementById("subscribeButton")
|
||||
.removeEventListener("click", this);
|
||||
this._document.getElementById("handlersMenuList")
|
||||
this._handlersList
|
||||
.removeEventListener("change", this);
|
||||
this._document = null;
|
||||
this._window = null;
|
||||
let prefs = Services.prefs;
|
||||
prefs.removeObserver(PREF_SELECTED_ACTION, this);
|
||||
prefs.removeObserver(PREF_SELECTED_READER, this);
|
||||
prefs.removeObserver(PREF_SELECTED_WEB, this);
|
||||
prefs.removeObserver(PREF_SELECTED_APP, this);
|
||||
prefs.removeObserver(PREF_VIDEO_SELECTED_ACTION, this);
|
||||
prefs.removeObserver(PREF_VIDEO_SELECTED_READER, this);
|
||||
prefs.removeObserver(PREF_VIDEO_SELECTED_WEB, this);
|
||||
prefs.removeObserver(PREF_VIDEO_SELECTED_APP, this);
|
||||
|
||||
prefs.removeObserver(PREF_AUDIO_SELECTED_ACTION, this);
|
||||
prefs.removeObserver(PREF_AUDIO_SELECTED_READER, this);
|
||||
prefs.removeObserver(PREF_AUDIO_SELECTED_WEB, this);
|
||||
prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this);
|
||||
this._handlersList = null;
|
||||
|
||||
this._removeFeedFromCache();
|
||||
this.__bundle = null;
|
||||
|
@ -1020,20 +913,6 @@ FeedWriter.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
setFeedCharPref(aPrefName, aPrefValue) {
|
||||
this._mm.sendAsyncMessage("FeedWriter:SetFeedCharPref",
|
||||
{ pref: aPrefName,
|
||||
value: aPrefValue });
|
||||
},
|
||||
|
||||
setFeedComplexString(aPrefName, aPrefValue) {
|
||||
// This sends the string data across to the parent, which will use it in an nsISupportsString
|
||||
// for a complex value pref.
|
||||
this._mm.sendAsyncMessage("FeedWriter:SetFeedComplexString",
|
||||
{ pref: aPrefName,
|
||||
value: aPrefValue });
|
||||
},
|
||||
|
||||
subscribe() {
|
||||
let feedType = this._getFeedType();
|
||||
|
||||
|
@ -1041,26 +920,21 @@ FeedWriter.prototype = {
|
|||
let defaultHandler = "reader";
|
||||
let useAsDefault = this._document.getElementById("alwaysUse").getAttribute("checked");
|
||||
|
||||
let menuList = this._document.getElementById("handlersMenuList");
|
||||
let selectedItem = menuList.selectedOptions[0];
|
||||
let selectedItem = this._handlersList.selectedOptions[0];
|
||||
let subscribeCallback = () => {
|
||||
let feedReader = null;
|
||||
let settings = {
|
||||
feedType,
|
||||
useAsDefault,
|
||||
// Pull the title and subtitle out of the document
|
||||
feedTitle: this._document.getElementById(TITLE_ID).textContent,
|
||||
feedSubtitle: this._document.getElementById(SUBTITLE_ID).textContent,
|
||||
feedLocation: this._window.location.href
|
||||
};
|
||||
if (selectedItem.hasAttribute("webhandlerurl")) {
|
||||
let webURI = selectedItem.getAttribute("webhandlerurl");
|
||||
this.setFeedCharPref(getPrefReaderForType(feedType), "web");
|
||||
this.setFeedComplexString(getPrefWebForType(feedType), webURI);
|
||||
|
||||
let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
let handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
|
||||
if (handler) {
|
||||
if (useAsDefault) {
|
||||
wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
|
||||
}
|
||||
|
||||
this._window.location.href = handler.getHandlerURI(this._window.location.href);
|
||||
}
|
||||
feedReader = "web";
|
||||
settings.uri = selectedItem.getAttribute("webhandlerurl");
|
||||
} else {
|
||||
let feedReader = null;
|
||||
switch (selectedItem.id) {
|
||||
case "selectedAppMenuItem":
|
||||
feedReader = "client";
|
||||
|
@ -1073,27 +947,20 @@ FeedWriter.prototype = {
|
|||
feedReader = "bookmarks";
|
||||
break;
|
||||
}
|
||||
|
||||
this.setFeedCharPref(getPrefReaderForType(feedType), feedReader);
|
||||
|
||||
let feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
|
||||
getService(Ci.nsIFeedResultService);
|
||||
|
||||
// Pull the title and subtitle out of the document
|
||||
let feedTitle = this._document.getElementById(TITLE_ID).textContent;
|
||||
let feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
|
||||
feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType, feedReader);
|
||||
}
|
||||
settings.reader = feedReader;
|
||||
|
||||
// If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
|
||||
// to either "reader" (If a web reader or if an application is selected),
|
||||
// or to "bookmarks" (if the live bookmarks option is selected).
|
||||
// Otherwise, we should set it to "ask"
|
||||
if (useAsDefault) {
|
||||
this.setFeedCharPref(getPrefActionForType(feedType), defaultHandler);
|
||||
} else {
|
||||
this.setFeedCharPref(getPrefActionForType(feedType), "ask");
|
||||
if (!useAsDefault) {
|
||||
defaultHandler = "ask";
|
||||
}
|
||||
settings.action = defaultHandler;
|
||||
LOG(`FeedWriter:SetFeedPrefsAndSubscribe - ${JSON.stringify(settings)}`);
|
||||
this._mm.sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribe",
|
||||
settings);
|
||||
}
|
||||
|
||||
// Show the file picker before subscribing if the
|
||||
|
@ -1111,37 +978,6 @@ FeedWriter.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
observe(subject, topic, data) {
|
||||
if (!this._window) {
|
||||
// this._window is null unless this.init was called with a trusted
|
||||
// window object.
|
||||
return;
|
||||
}
|
||||
|
||||
let feedType = this._getFeedType();
|
||||
|
||||
if (topic == "nsPref:changed") {
|
||||
switch (data) {
|
||||
case PREF_SELECTED_READER:
|
||||
case PREF_SELECTED_WEB:
|
||||
case PREF_SELECTED_APP:
|
||||
case PREF_VIDEO_SELECTED_READER:
|
||||
case PREF_VIDEO_SELECTED_WEB:
|
||||
case PREF_VIDEO_SELECTED_APP:
|
||||
case PREF_AUDIO_SELECTED_READER:
|
||||
case PREF_AUDIO_SELECTED_WEB:
|
||||
case PREF_AUDIO_SELECTED_APP:
|
||||
this._setSelectedHandler(feedType);
|
||||
break;
|
||||
case PREF_SELECTED_ACTION:
|
||||
case PREF_VIDEO_SELECTED_ACTION:
|
||||
case PREF_AUDIO_SELECTED_ACTION:
|
||||
this._setAlwaysUseCheckedState(feedType);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get _mm() {
|
||||
let mm = this._window.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDocShell).
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<title>&welcomeback2.tabtitle;</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="chrome://browser/skin/aboutWelcomeBack.css" type="text/css" media="all"/>
|
||||
<link rel="icon" type="image/png" href="chrome://global/skin/icons/information-16.png"/>
|
||||
<link rel="icon" type="image/png" href="chrome://browser/skin/info.svg"/>
|
||||
|
||||
<script type="application/javascript" src="chrome://browser/content/aboutSessionRestore.js"/>
|
||||
</head>
|
||||
|
|
|
@ -19,7 +19,7 @@ const TEST_THRESHOLD = {
|
|||
|
||||
const ADDON_ROLLOUT_POLICY = {
|
||||
"beta" : "51alladdons", // Any WebExtension or addon except with mpc = false
|
||||
"release" : "51set1",
|
||||
"release" : "50allmpc",
|
||||
"esr" : "esrA", // WebExtensions and Addons with mpc=true
|
||||
};
|
||||
|
||||
|
@ -73,7 +73,10 @@ function defineCohort() {
|
|||
|
||||
Preferences.set(PREF_E10S_ADDON_BLOCKLIST,
|
||||
// bug 1185672 - Tab Mix Plus
|
||||
"{dc572301-7619-498c-a57d-39143191b318};");
|
||||
"{dc572301-7619-498c-a57d-39143191b318};"
|
||||
// bug 1344345 - Mega
|
||||
+ "firefox@mega.co.nz"
|
||||
);
|
||||
} else {
|
||||
Preferences.reset(PREF_E10S_ADDON_POLICY);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>e10srollout@mozilla.org</em:id>
|
||||
<em:version>1.11</em:version>
|
||||
<em:version>1.12</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# 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/.
|
||||
|
||||
errorReportFalseDeceptiveTitle=This isn’t a deceptive site
|
||||
errorReportFalseDeceptiveMessage=It’s not possible to report this error at this time.
|
|
@ -54,6 +54,7 @@
|
|||
locale/browser/places/moveBookmarks.dtd (%chrome/browser/places/moveBookmarks.dtd)
|
||||
locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd)
|
||||
locale/browser/safebrowsing/report-phishing.dtd (%chrome/browser/safebrowsing/report-phishing.dtd)
|
||||
locale/browser/safebrowsing/safebrowsing.properties (%chrome/browser/safebrowsing/safebrowsing.properties)
|
||||
locale/browser/feeds/subscribe.dtd (%chrome/browser/feeds/subscribe.dtd)
|
||||
locale/browser/feeds/subscribe.properties (%chrome/browser/feeds/subscribe.properties)
|
||||
locale/browser/migration/migration.dtd (%chrome/browser/migration/migration.dtd)
|
||||
|
|
|
@ -7,7 +7,7 @@ body {
|
|||
|
||||
#message-box {
|
||||
margin-top: 2em;
|
||||
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
|
||||
background: url(chrome://browser/skin/info.svg) no-repeat left 8px;
|
||||
padding-inline-start: 30px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
The certificate authority and server certificates here are generated by $topsrcdir/build/pgo/genpgocert.py.
|
||||
|
||||
You can generate a new CA cert by running:
|
||||
$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-ca
|
||||
./mach python build/pgo/genpgocert.py --gen-ca
|
||||
|
||||
You can generate new server certificates by running:
|
||||
$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-server
|
||||
./mach python build/pgo/genpgocert.py --gen-server
|
||||
|
||||
These will place the new files in this directory where you can commit them.
|
||||
These commands will modify cert8.db and key3.db. The changes to these should be committed.
|
||||
|
|
|
@ -442,6 +442,21 @@ BasePrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
|
||||
{
|
||||
// Never destroy an existing CSP on the principal.
|
||||
// This method should only be called in rare cases.
|
||||
|
||||
MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
|
||||
if (mCSP) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
mCSP = aCsp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::EnsureCSP(nsIDOMDocument* aDocument,
|
||||
nsIContentSecurityPolicy** aCSP)
|
||||
|
|
|
@ -222,6 +222,7 @@ public:
|
|||
NS_IMETHOD SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* other, bool* _retval) final;
|
||||
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) final;
|
||||
NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
|
||||
NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
|
||||
NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
|
||||
NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
|
||||
NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
|
||||
|
|
|
@ -151,10 +151,11 @@ interface nsIPrincipal : nsISerializable
|
|||
|
||||
/**
|
||||
* A Content Security Policy associated with this principal.
|
||||
*
|
||||
* Use this function to query the associated CSP with this principal.
|
||||
* Please *only* use this function to *set* a CSP when you know exactly what you are doing.
|
||||
* Most likely you want to call ensureCSP instead of setCSP.
|
||||
*/
|
||||
[noscript] readonly attribute nsIContentSecurityPolicy csp;
|
||||
[noscript] attribute nsIContentSecurityPolicy csp;
|
||||
|
||||
/*
|
||||
* Use this function to query a CSP associated with this principal.
|
||||
|
|
|
@ -135,7 +135,10 @@ ReadSuffixAndSpec(JSStructuredCloneReader* aReader,
|
|||
}
|
||||
|
||||
nsAutoCString suffix;
|
||||
suffix.SetLength(suffixLength);
|
||||
if (!suffix.SetLength(suffixLength, fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -144,7 +147,10 @@ ReadSuffixAndSpec(JSStructuredCloneReader* aReader,
|
|||
return false;
|
||||
}
|
||||
|
||||
aSpec.SetLength(specLength);
|
||||
if (!aSpec.SetLength(specLength, fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -195,7 +201,7 @@ ReadPrincipalInfo(JSStructuredCloneReader* aReader,
|
|||
return false;
|
||||
}
|
||||
|
||||
aInfo = ContentPrincipalInfo(attrs, spec);
|
||||
aInfo = ContentPrincipalInfo(attrs, void_t(), spec);
|
||||
} else {
|
||||
MOZ_CRASH("unexpected principal structured clone tag");
|
||||
}
|
||||
|
|
|
@ -108,6 +108,20 @@ nsNullPrincipal::GetHashValue(uint32_t *aResult)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp) {
|
||||
// Never destroy an existing CSP on the principal.
|
||||
// This method should only be called in rare cases.
|
||||
|
||||
MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
|
||||
if (mCSP) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
mCSP = aCsp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::GetURI(nsIURI** aURI)
|
||||
{
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
|
||||
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
|
||||
NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
|
||||
NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
|
||||
NS_IMETHOD GetURI(nsIURI** aURI) override;
|
||||
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
|
||||
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
|
||||
|
|
|
@ -269,10 +269,68 @@ nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
|
|||
nsIPrincipal** aPrincipal,
|
||||
bool aIgnoreSandboxing)
|
||||
{
|
||||
NS_PRECONDITION(aChannel, "Must have channel!");
|
||||
// Check whether we have an nsILoadInfo that says what we should do.
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
|
||||
if (loadInfo && loadInfo->GetForceInheritPrincipalOverruleOwner()) {
|
||||
NS_PRECONDITION(aChannel, "Must have channel!");
|
||||
// Check whether we have an nsILoadInfo that says what we should do.
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
|
||||
if (loadInfo && loadInfo->GetForceInheritPrincipalOverruleOwner()) {
|
||||
nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
|
||||
if (!principalToInherit) {
|
||||
principalToInherit = loadInfo->TriggeringPrincipal();
|
||||
}
|
||||
principalToInherit.forget(aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> owner;
|
||||
aChannel->GetOwner(getter_AddRefs(owner));
|
||||
if (owner) {
|
||||
CallQueryInterface(owner, aPrincipal);
|
||||
if (*aPrincipal) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadInfo) {
|
||||
if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSandboxedLoadingPrincipal(aPrincipal)));
|
||||
MOZ_ASSERT(*aPrincipal);
|
||||
// if the new NullPrincipal (above) loads an iframe[srcdoc], we
|
||||
// need to inherit an existing CSP to avoid bypasses (bug 1073952).
|
||||
// We continue inheriting for nested frames with e.g., data: URLs.
|
||||
if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_SUBDOCUMENT) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aChannel->GetURI(getter_AddRefs(uri));
|
||||
nsAutoCString URISpec;
|
||||
uri->GetSpec(URISpec);
|
||||
bool isData = (NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData);
|
||||
if (URISpec.EqualsLiteral("about:srcdoc") || isData) {
|
||||
nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
|
||||
if (!principalToInherit) {
|
||||
principalToInherit = loadInfo->TriggeringPrincipal();
|
||||
}
|
||||
nsCOMPtr<nsIContentSecurityPolicy> originalCsp;
|
||||
principalToInherit->GetCsp(getter_AddRefs(originalCsp));
|
||||
// if the principalToInherit had a CSP,
|
||||
// add it to the newly created NullPrincipal.
|
||||
if (originalCsp) {
|
||||
nsresult rv = (*aPrincipal)->SetCsp(originalCsp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool forceInherit = loadInfo->GetForceInheritPrincipal();
|
||||
if (aIgnoreSandboxing && !forceInherit) {
|
||||
// Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
|
||||
// sandboxing:
|
||||
if (loadInfo->GetLoadingSandboxed() &&
|
||||
loadInfo->GetForceInheritPrincipalDropped()) {
|
||||
forceInherit = true;
|
||||
}
|
||||
}
|
||||
if (forceInherit) {
|
||||
nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
|
||||
if (!principalToInherit) {
|
||||
principalToInherit = loadInfo->TriggeringPrincipal();
|
||||
|
@ -281,67 +339,33 @@ nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> owner;
|
||||
aChannel->GetOwner(getter_AddRefs(owner));
|
||||
if (owner) {
|
||||
CallQueryInterface(owner, aPrincipal);
|
||||
if (*aPrincipal) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsSecurityFlags securityFlags = loadInfo->GetSecurityMode();
|
||||
// The data: inheritance flags should only apply to the initial load,
|
||||
// not to loads that it might have redirected to.
|
||||
if (loadInfo->RedirectChain().IsEmpty() &&
|
||||
(securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
|
||||
securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
|
||||
securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
|
||||
if (!principalToInherit) {
|
||||
principalToInherit = loadInfo->TriggeringPrincipal();
|
||||
}
|
||||
bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
|
||||
|
||||
if (nsContentUtils::ChannelShouldInheritPrincipal(principalToInherit,
|
||||
uri,
|
||||
inheritForAboutBlank,
|
||||
false)) {
|
||||
principalToInherit.forget(aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadInfo) {
|
||||
if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSandboxedLoadingPrincipal(aPrincipal)));
|
||||
MOZ_ASSERT(*aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool forceInherit = loadInfo->GetForceInheritPrincipal();
|
||||
if (aIgnoreSandboxing && !forceInherit) {
|
||||
// Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
|
||||
// sandboxing:
|
||||
if (loadInfo->GetLoadingSandboxed() &&
|
||||
loadInfo->GetForceInheritPrincipalDropped()) {
|
||||
forceInherit = true;
|
||||
}
|
||||
}
|
||||
if (forceInherit) {
|
||||
nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
|
||||
if (!principalToInherit) {
|
||||
principalToInherit = loadInfo->TriggeringPrincipal();
|
||||
}
|
||||
principalToInherit.forget(aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsSecurityFlags securityFlags = loadInfo->GetSecurityMode();
|
||||
// The data: inheritance flags should only apply to the initial load,
|
||||
// not to loads that it might have redirected to.
|
||||
if (loadInfo->RedirectChain().IsEmpty() &&
|
||||
(securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
|
||||
securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
|
||||
securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
|
||||
if (!principalToInherit) {
|
||||
principalToInherit = loadInfo->TriggeringPrincipal();
|
||||
}
|
||||
bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
|
||||
|
||||
if (nsContentUtils::ChannelShouldInheritPrincipal(principalToInherit,
|
||||
uri,
|
||||
inheritForAboutBlank,
|
||||
false)) {
|
||||
principalToInherit.forget(aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return GetChannelURIPrincipal(aChannel, aPrincipal);
|
||||
}
|
||||
return GetChannelURIPrincipal(aChannel, aPrincipal);
|
||||
}
|
||||
|
||||
/* The principal of the URI that this channel is loading. This is never
|
||||
|
|
|
@ -57,7 +57,7 @@ nsSystemPrincipal::GetHashValue(uint32_t *result)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_IMETHODIMP
|
||||
nsSystemPrincipal::GetURI(nsIURI** aURI)
|
||||
{
|
||||
*aURI = nullptr;
|
||||
|
@ -78,6 +78,15 @@ nsSystemPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSystemPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
|
||||
{
|
||||
// Never destroy an existing CSP on the principal.
|
||||
// This method should only be called in rare cases.
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSystemPrincipal::EnsureCSP(nsIDOMDocument* aDocument,
|
||||
nsIContentSecurityPolicy** aCSP)
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
|
||||
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
|
||||
NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
|
||||
NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
|
||||
NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
|
||||
NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
|
||||
NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
|
||||
|
|
|
@ -34,6 +34,7 @@ ostream
|
|||
set
|
||||
stack
|
||||
string
|
||||
thread
|
||||
type_traits
|
||||
utility
|
||||
vector
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
|
||||
const TAB_URL = "data:text/html,<title>foo</title>";
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [["dom.ipc.processCount", 1]]
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
let { tab, document } = yield openAboutDebugging("tabs");
|
||||
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [["dom.ipc.processCount", 1]]
|
||||
});
|
||||
});
|
||||
|
||||
// Test that the spacebar key press toggles the toggleAll button state
|
||||
// when a node with no animation is selected.
|
||||
// This test doesn't need to test if animations actually pause/resume
|
||||
|
|
|
@ -94,5 +94,8 @@ skip-if = os == "mac" && os_version == "10.8" || os == "win" && os_version == "5
|
|||
[browser_toolbox_window_title_frame_select.js]
|
||||
[browser_toolbox_zoom.js]
|
||||
[browser_two_tabs.js]
|
||||
# We want this test to run for mochitest-dt as well, so we include it here:
|
||||
# We want these tests to run for mochitest-dt as well, so we include them here:
|
||||
[../../../../browser/base/content/test/static/browser_parsable_css.js]
|
||||
skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
|
||||
[../../../../browser/base/content/test/static/browser_all_files_referenced.js]
|
||||
skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
|
||||
|
|
|
@ -30,12 +30,6 @@ let checkToolbox = Task.async(function* (tab, location) {
|
|||
ok(!!gDevTools.getToolbox(target), `Toolbox exists ${location}`);
|
||||
});
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [["dom.ipc.processCount", 1]]
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
let tab = yield addTab(TEST_URL);
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
|
||||
.notificationbox .messageImage {
|
||||
display: inline-block;
|
||||
background-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 6px;
|
||||
|
@ -59,7 +60,7 @@
|
|||
/* Default icons for notifications */
|
||||
|
||||
.notificationbox .messageImage[data-type="info"] {
|
||||
background-image: url("chrome://global/skin/icons/information-16.png");
|
||||
background-image: url("chrome://global/skin/icons/info.svg");
|
||||
}
|
||||
|
||||
.notificationbox .messageImage[data-type="warning"] {
|
||||
|
|
|
@ -181,7 +181,6 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
|
|||
[browser_console_native_getters.js]
|
||||
[browser_console_navigation_marker.js]
|
||||
[browser_console_netlogging.js]
|
||||
skip-if = true # Bug 1298364
|
||||
[browser_console_nsiconsolemessage.js]
|
||||
[browser_console_optimized_out_vars.js]
|
||||
[browser_console_private_browsing.js]
|
||||
|
@ -338,6 +337,7 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
|||
[browser_webconsole_message_node_id.js]
|
||||
[browser_webconsole_multiline_input.js]
|
||||
[browser_webconsole_netlogging.js]
|
||||
skip-if = true # Bug 1298364
|
||||
[browser_webconsole_netlogging_basic.js]
|
||||
[browser_webconsole_netlogging_panel.js]
|
||||
[browser_webconsole_netlogging_reset_filter.js]
|
||||
|
|
|
@ -13,12 +13,6 @@ const TEST_DOC = "https://example.com/browser/devtools/client/webconsole/" +
|
|||
"test/test_bug1092055_shouldwarn.html";
|
||||
const SAMPLE_MSG = "specified a header that could not be parsed successfully.";
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [["dom.ipc.processCount", 1]]
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
let { browser } = yield loadTab(TEST_URI);
|
||||
|
||||
|
|
|
@ -9,10 +9,6 @@ const TEST_URI = "data:text/html;charset=utf-8,Web Console test for splitting";
|
|||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, runTest);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// Test is slow on Linux EC2 instances - Bug 962931
|
||||
requestLongerTimeout(2);
|
||||
|
||||
|
|
|
@ -125,11 +125,16 @@ var LayoutActor = ActorClassWithSpec(layoutSpec, {
|
|||
* @return {Array} An array of GridActor objects.
|
||||
*/
|
||||
getAllGrids: function (rootNode, traverseFrames) {
|
||||
let grids = [];
|
||||
|
||||
if (!rootNode) {
|
||||
return grids;
|
||||
}
|
||||
|
||||
if (!traverseFrames) {
|
||||
return this.getGrids(rootNode.rawNode);
|
||||
}
|
||||
|
||||
let grids = [];
|
||||
for (let {document} of this.tabActor.windows) {
|
||||
grids = [...grids, ...this.getGrids(document.documentElement)];
|
||||
}
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [["dom.ipc.processCount", 1]]
|
||||
});
|
||||
});
|
||||
|
||||
// Check that the duration, iterationCount and delay are retrieved correctly for
|
||||
// multiple animations.
|
||||
|
||||
|
|
|
@ -2865,6 +2865,7 @@ exports.CSS_PROPERTIES = {
|
|||
"column-rule-color",
|
||||
"column-rule-style",
|
||||
"column-rule-width",
|
||||
"column-span",
|
||||
"column-width",
|
||||
"contain",
|
||||
"content",
|
||||
|
@ -9346,6 +9347,10 @@ exports.PREFERENCES = [
|
|||
"color-adjust",
|
||||
"layout.css.color-adjust.enabled"
|
||||
],
|
||||
[
|
||||
"column-span",
|
||||
"layout.css.column-span.enabled"
|
||||
],
|
||||
[
|
||||
"contain",
|
||||
"layout.css.contain.enabled"
|
||||
|
|
|
@ -36,6 +36,8 @@ using namespace mozilla;
|
|||
using namespace mozilla::dom;
|
||||
using namespace mozilla::hal;
|
||||
|
||||
static mozilla::LazyLogModule gAudioChannelLog("AudioChannel");
|
||||
|
||||
namespace {
|
||||
|
||||
// If true, any new AudioChannelAgent will be muted when created.
|
||||
|
@ -226,13 +228,9 @@ AudioChannelService::Get()
|
|||
return service.forget();
|
||||
}
|
||||
|
||||
/* static */ PRLogModuleInfo*
|
||||
/* static */ LogModule*
|
||||
AudioChannelService::GetAudioChannelLog()
|
||||
{
|
||||
static PRLogModuleInfo *gAudioChannelLog;
|
||||
if (!gAudioChannelLog) {
|
||||
gAudioChannelLog = PR_NewLogModule("AudioChannel");
|
||||
}
|
||||
return gAudioChannelLog;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "AudioChannelAgent.h"
|
||||
#include "nsAttrValue.h"
|
||||
#include "mozilla/dom/AudioChannelBinding.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
@ -102,7 +103,7 @@ public:
|
|||
|
||||
static bool IsAudioChannelMutedByDefault();
|
||||
|
||||
static PRLogModuleInfo* GetAudioChannelLog();
|
||||
static LogModule* GetAudioChannelLog();
|
||||
|
||||
static bool IsEnableAudioCompeting();
|
||||
|
||||
|
|
|
@ -4406,7 +4406,8 @@ nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType,
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Loading the sheet sync.
|
||||
RefPtr<css::Loader> loader = new css::Loader(GetStyleBackendType());
|
||||
RefPtr<css::Loader> loader =
|
||||
new css::Loader(GetStyleBackendType(), GetDocGroup());
|
||||
|
||||
css::SheetParsingMode parsingMode;
|
||||
switch (aType) {
|
||||
|
@ -10564,14 +10565,12 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
|
||||
|
||||
explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise)
|
||||
: mDocument(aDocument)
|
||||
, mPromise(aPromise)
|
||||
: mPromise(aPromise)
|
||||
{
|
||||
nsCOMPtr<nsIParser> parser = mDocument->CreatorParserOrNull();
|
||||
nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
|
||||
if (parser) {
|
||||
parser->BlockParser();
|
||||
} else {
|
||||
mDocument = nullptr;
|
||||
mParser = do_GetWeakReference(parser);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10599,21 +10598,19 @@ protected:
|
|||
|
||||
private:
|
||||
void MaybeUnblockParser() {
|
||||
if (mDocument) {
|
||||
nsCOMPtr<nsIParser> parser = mDocument->CreatorParserOrNull();
|
||||
if (parser) {
|
||||
parser->UnblockParser();
|
||||
parser->ContinueInterruptedParsingAsync();
|
||||
}
|
||||
mDocument = nullptr;
|
||||
nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
|
||||
if (parser) {
|
||||
parser->UnblockParser();
|
||||
parser->ContinueInterruptedParsingAsync();
|
||||
mParser = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<nsIDocument> mDocument;
|
||||
nsWeakPtr mParser;
|
||||
RefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mPromise)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
|
|
|
@ -1975,15 +1975,15 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
|
|||
|
||||
aSavedResponseOut->mValue.principalInfo() = void_t();
|
||||
if (!serializedInfo.IsEmpty()) {
|
||||
nsAutoCString originNoSuffix;
|
||||
nsAutoCString specNoSuffix;
|
||||
OriginAttributes attrs;
|
||||
if (!attrs.PopulateFromOrigin(serializedInfo, originNoSuffix)) {
|
||||
if (!attrs.PopulateFromOrigin(serializedInfo, specNoSuffix)) {
|
||||
NS_WARNING("Something went wrong parsing a serialized principal!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aSavedResponseOut->mValue.principalInfo() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, originNoSuffix);
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), specNoSuffix);
|
||||
}
|
||||
|
||||
rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 12
|
||||
#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 19
|
||||
|
||||
/**
|
||||
* EventStates is the class used to represent the event states of nsIContent
|
||||
|
@ -223,6 +223,21 @@ private:
|
|||
#define NS_EVENT_STATE_INVALID NS_DEFINE_EVENT_STATE_MACRO(11)
|
||||
// UI friendly version of :valid pseudo-class.
|
||||
#define NS_EVENT_STATE_MOZ_UI_VALID NS_DEFINE_EVENT_STATE_MACRO(12)
|
||||
// Content could not be rendered (image/object/etc).
|
||||
#define NS_EVENT_STATE_BROKEN NS_DEFINE_EVENT_STATE_MACRO(13)
|
||||
// Content disabled by the user (images turned off, say).
|
||||
#define NS_EVENT_STATE_USERDISABLED NS_DEFINE_EVENT_STATE_MACRO(14)
|
||||
// Content suppressed by the user (ad blocking, etc).
|
||||
#define NS_EVENT_STATE_SUPPRESSED NS_DEFINE_EVENT_STATE_MACRO(15)
|
||||
// Content is still loading such that there is nothing to show the
|
||||
// user (eg an image which hasn't started coming in yet).
|
||||
#define NS_EVENT_STATE_LOADING NS_DEFINE_EVENT_STATE_MACRO(16)
|
||||
// Handler for the content has been blocked.
|
||||
#define NS_EVENT_STATE_HANDLER_BLOCKED NS_DEFINE_EVENT_STATE_MACRO(17)
|
||||
// Handler for the content has been disabled.
|
||||
#define NS_EVENT_STATE_HANDLER_DISABLED NS_DEFINE_EVENT_STATE_MACRO(18)
|
||||
// Handler for the content has crashed
|
||||
#define NS_EVENT_STATE_HANDLER_CRASHED NS_DEFINE_EVENT_STATE_MACRO(19)
|
||||
|
||||
/*
|
||||
* Bits below here do not have Servo-related ordering constraints.
|
||||
|
@ -232,42 +247,27 @@ private:
|
|||
*/
|
||||
|
||||
// Drag is hovering over content.
|
||||
#define NS_EVENT_STATE_DRAGOVER NS_DEFINE_EVENT_STATE_MACRO(13)
|
||||
#define NS_EVENT_STATE_DRAGOVER NS_DEFINE_EVENT_STATE_MACRO(20)
|
||||
// Content is required.
|
||||
#define NS_EVENT_STATE_REQUIRED NS_DEFINE_EVENT_STATE_MACRO(14)
|
||||
#define NS_EVENT_STATE_REQUIRED NS_DEFINE_EVENT_STATE_MACRO(21)
|
||||
// Content is optional (and can be required).
|
||||
#define NS_EVENT_STATE_OPTIONAL NS_DEFINE_EVENT_STATE_MACRO(15)
|
||||
#define NS_EVENT_STATE_OPTIONAL NS_DEFINE_EVENT_STATE_MACRO(22)
|
||||
// Link has been visited.
|
||||
#define NS_EVENT_STATE_VISITED NS_DEFINE_EVENT_STATE_MACRO(16)
|
||||
#define NS_EVENT_STATE_VISITED NS_DEFINE_EVENT_STATE_MACRO(23)
|
||||
// Link hasn't been visited.
|
||||
#define NS_EVENT_STATE_UNVISITED NS_DEFINE_EVENT_STATE_MACRO(17)
|
||||
#define NS_EVENT_STATE_UNVISITED NS_DEFINE_EVENT_STATE_MACRO(24)
|
||||
// Content value is in-range (and can be out-of-range).
|
||||
#define NS_EVENT_STATE_INRANGE NS_DEFINE_EVENT_STATE_MACRO(18)
|
||||
#define NS_EVENT_STATE_INRANGE NS_DEFINE_EVENT_STATE_MACRO(25)
|
||||
// Content value is out-of-range.
|
||||
#define NS_EVENT_STATE_OUTOFRANGE NS_DEFINE_EVENT_STATE_MACRO(19)
|
||||
#define NS_EVENT_STATE_OUTOFRANGE NS_DEFINE_EVENT_STATE_MACRO(26)
|
||||
// These two are temporary (see bug 302188)
|
||||
// Content is read-only.
|
||||
#define NS_EVENT_STATE_MOZ_READONLY NS_DEFINE_EVENT_STATE_MACRO(20)
|
||||
#define NS_EVENT_STATE_MOZ_READONLY NS_DEFINE_EVENT_STATE_MACRO(27)
|
||||
// Content is editable.
|
||||
#define NS_EVENT_STATE_MOZ_READWRITE NS_DEFINE_EVENT_STATE_MACRO(21)
|
||||
#define NS_EVENT_STATE_MOZ_READWRITE NS_DEFINE_EVENT_STATE_MACRO(28)
|
||||
// Content is the default one (meaning depends of the context).
|
||||
#define NS_EVENT_STATE_DEFAULT NS_DEFINE_EVENT_STATE_MACRO(22)
|
||||
// Content could not be rendered (image/object/etc).
|
||||
#define NS_EVENT_STATE_BROKEN NS_DEFINE_EVENT_STATE_MACRO(23)
|
||||
// Content disabled by the user (images turned off, say).
|
||||
#define NS_EVENT_STATE_USERDISABLED NS_DEFINE_EVENT_STATE_MACRO(24)
|
||||
// Content suppressed by the user (ad blocking, etc).
|
||||
#define NS_EVENT_STATE_SUPPRESSED NS_DEFINE_EVENT_STATE_MACRO(25)
|
||||
// Content is still loading such that there is nothing to show the
|
||||
// user (eg an image which hasn't started coming in yet).
|
||||
#define NS_EVENT_STATE_LOADING NS_DEFINE_EVENT_STATE_MACRO(26)
|
||||
#define NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL NS_DEFINE_EVENT_STATE_MACRO(27)
|
||||
// Handler for the content has been blocked.
|
||||
#define NS_EVENT_STATE_HANDLER_BLOCKED NS_DEFINE_EVENT_STATE_MACRO(28)
|
||||
// Handler for the content has been disabled.
|
||||
#define NS_EVENT_STATE_HANDLER_DISABLED NS_DEFINE_EVENT_STATE_MACRO(29)
|
||||
// Handler for the content has crashed
|
||||
#define NS_EVENT_STATE_HANDLER_CRASHED NS_DEFINE_EVENT_STATE_MACRO(30)
|
||||
#define NS_EVENT_STATE_DEFAULT NS_DEFINE_EVENT_STATE_MACRO(29)
|
||||
#define NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL NS_DEFINE_EVENT_STATE_MACRO(30)
|
||||
// Content has focus and should show a ring.
|
||||
#define NS_EVENT_STATE_FOCUSRING NS_DEFINE_EVENT_STATE_MACRO(31)
|
||||
// Content is a submit control and the form isn't valid.
|
||||
|
|
|
@ -6,6 +6,16 @@ support-files =
|
|||
pointerevent_styles.css
|
||||
pointerevent_support.js
|
||||
|
||||
[test_bug1285128.html]
|
||||
[test_bug1293174_implicit_pointer_capture_for_touch_1.html]
|
||||
support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
|
||||
[test_bug1293174_implicit_pointer_capture_for_touch_2.html]
|
||||
support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
|
||||
[test_bug1303704.html]
|
||||
[test_bug1315862.html]
|
||||
[test_bug1323158.html]
|
||||
[test_empty_file.html]
|
||||
disabled = disabled # Bug 1150091 - Issue with support-files
|
||||
[test_pointerevent_attributes_hoverable_pointers-manual.html]
|
||||
support-files =
|
||||
pointerevent_attributes_hoverable_pointers-manual.html
|
||||
|
@ -118,14 +128,5 @@ support-files =
|
|||
pointerevent_touch-action-pan-left-css_touch-manual.html
|
||||
pointerevent_touch-action-pan-right-css_touch-manual.html
|
||||
pointerevent_touch-action-pan-up-css_touch-manual.html
|
||||
[test_bug1285128.html]
|
||||
[test_bug1293174_implicit_pointer_capture_for_touch_1.html]
|
||||
support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
|
||||
[test_bug1293174_implicit_pointer_capture_for_touch_2.html]
|
||||
support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
|
||||
[test_bug1303704.html]
|
||||
[test_bug1323158.html]
|
||||
[test_trigger_fullscreen_by_pointer_events.html]
|
||||
[test_trigger_popup_by_pointer_events.html]
|
||||
[test_empty_file.html]
|
||||
disabled = disabled # Bug 1150091 - Issue with support-files
|
||||
[test_bug1315862.html]
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for triggering Fullscreen by pointer events</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target" style="width: 50px; height: 50px; background: green"></div>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var target = document.getElementById("target");
|
||||
target.addEventListener("pointerdown", () => {
|
||||
target.requestFullscreen();
|
||||
target.addEventListener("pointerdown", () => {
|
||||
document.exitFullscreen();
|
||||
}, {once: true});
|
||||
}, {once: true});
|
||||
|
||||
document.addEventListener("fullscreenchange", () => {
|
||||
if (document.fullscreenElement) {
|
||||
ok(document.fullscreenElement, target, "fullscreenElement should be the div element");
|
||||
// synthesize mouse events to generate pointer events and leave full screen.
|
||||
synthesizeMouseAtCenter(target, { type: "mousedown" });
|
||||
synthesizeMouseAtCenter(target, { type: "mouseup" });
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
|
||||
function startTest() {
|
||||
// synthesize mouse events to generate pointer events and enter full screen.
|
||||
synthesizeMouseAtCenter(target, { type: "mousedown" });
|
||||
synthesizeMouseAtCenter(target, { type: "mouseup" });
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus(() => {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["full-screen-api.unprefix.enabled", true],
|
||||
["full-screen-api.allow-trusted-requests-only", false],
|
||||
["dom.w3c_pointer_events.enabled", true]
|
||||
]
|
||||
}, startTest);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -993,7 +993,8 @@ RemoteInputStream::BlockAndWaitForStream()
|
|||
nsTArray<FileDescriptor> fds;
|
||||
OptionalFileDescriptorSetToFDs(optionalFDs, fds);
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(params, fds);
|
||||
nsCOMPtr<nsIInputStream> stream =
|
||||
InputStreamHelper::DeserializeInputStream(params, fds);
|
||||
MOZ_ASSERT(stream);
|
||||
|
||||
SetStream(stream);
|
||||
|
@ -4450,7 +4451,7 @@ BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor,
|
|||
if (mBlobImpl->IsMemoryFile()) {
|
||||
InputStreamParams params;
|
||||
nsTArray<FileDescriptor> fds;
|
||||
SerializeInputStream(stream, params, fds);
|
||||
InputStreamHelper::SerializeInputStream(stream, params, fds);
|
||||
|
||||
MOZ_ASSERT(params.type() != InputStreamParams::T__None);
|
||||
MOZ_ASSERT(fds.IsEmpty());
|
||||
|
@ -4788,7 +4789,8 @@ InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
|
|||
const_cast<OptionalFileDescriptorSet&>(aOptionalSet),
|
||||
fds);
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
|
||||
nsCOMPtr<nsIInputStream> stream =
|
||||
InputStreamHelper::DeserializeInputStream(aParams, fds);
|
||||
MOZ_ASSERT(stream);
|
||||
|
||||
mRemoteStream->SetStream(stream);
|
||||
|
|
|
@ -18796,10 +18796,12 @@ Maintenance::DirectoryWork()
|
|||
MOZ_ASSERT(origin.IsEmpty());
|
||||
|
||||
int64_t dummyTimeStamp;
|
||||
bool dummyPersisted;
|
||||
nsCString dummySuffix;
|
||||
if (NS_WARN_IF(NS_FAILED(
|
||||
quotaManager->GetDirectoryMetadata2(originDir,
|
||||
&dummyTimeStamp,
|
||||
&dummyPersisted,
|
||||
dummySuffix,
|
||||
group,
|
||||
origin)))) {
|
||||
|
|
|
@ -4009,7 +4009,8 @@ MediaStreamGraph::ApplyAudioContextOperation(MediaStream* aDestinationStream,
|
|||
}
|
||||
void RunDuringShutdown() override
|
||||
{
|
||||
MOZ_ASSERT(false, "We should be reviving the graph?");
|
||||
MOZ_ASSERT(mAudioContextOperation == AudioContextOperation::Close,
|
||||
"We should be reviving the graph?");
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -107,7 +107,7 @@ class OriginKeyStore : public nsISupports
|
|||
private:
|
||||
void
|
||||
PrincipalInfoToString(const ipc::PrincipalInfo& aPrincipalInfo,
|
||||
nsAutoCString aString)
|
||||
nsACString& aString)
|
||||
{
|
||||
switch (aPrincipalInfo.type()) {
|
||||
case ipc::PrincipalInfo::TSystemPrincipalInfo:
|
||||
|
@ -124,11 +124,11 @@ class OriginKeyStore : public nsISupports
|
|||
case ipc::PrincipalInfo::TContentPrincipalInfo: {
|
||||
const ipc::ContentPrincipalInfo& info =
|
||||
aPrincipalInfo.get_ContentPrincipalInfo();
|
||||
aString.Assign(info.spec());
|
||||
aString.Assign(info.originNoSuffix());
|
||||
|
||||
nsAutoCString suffix;
|
||||
info.attrs().CreateSuffix(suffix);
|
||||
suffix.Append(suffix);
|
||||
aString.Append(suffix);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1096,7 +1096,7 @@ tags = webvtt
|
|||
[test_unseekable.html]
|
||||
skip-if = toolkit == 'android' # android(bug 1232305)
|
||||
[test_video_to_canvas.html]
|
||||
skip-if = android_version == '15' || android_version == '17' || (android_version == '19' && debug) || android_version == '22' # bug 1320418, android(bug 1232305)
|
||||
skip-if = toolkit == 'android' # android(bug 1232305), bugs 1320418,1347953,1347954,1348140,1348386
|
||||
[test_video_in_audio_element.html]
|
||||
skip-if = android_version == '15' || android_version == '17' # bug 1320417, 1326326, android(bug 1232323, bug 1232305)
|
||||
[test_videoDocumentTitle.html]
|
||||
|
|
|
@ -117,8 +117,7 @@ runTest(async () => {
|
|||
"Same origin deviceId for " + device.label + " must match");
|
||||
}
|
||||
for (let device of differentOriginDevices) {
|
||||
// TODO: s/todo/ok/ once bug 1340163 is fixed.
|
||||
todo(!devices.find(d => d.deviceId == device.deviceId),
|
||||
ok(!devices.find(d => d.deviceId == device.deviceId),
|
||||
"Different origin deviceId for " + device.label + " must be different");
|
||||
}
|
||||
|
||||
|
|
|
@ -404,7 +404,7 @@ Cu.import('resource://gre/modules/Services.jsm');
|
|||
// Otherwise just ignore the end tag.
|
||||
continue;
|
||||
}
|
||||
var ts = collectTimeStamp(t.substr(1, t.length - 2));
|
||||
var ts = collectTimeStamp(t.substr(1, t.length - 1));
|
||||
var node;
|
||||
if (ts) {
|
||||
// Timestamps are lead nodes as well.
|
||||
|
|
|
@ -272,6 +272,7 @@ QuotaRequestChild::Recv__delete__(const RequestResponse& aResponse)
|
|||
case RequestResponse::TClearDataResponse:
|
||||
case RequestResponse::TClearAllResponse:
|
||||
case RequestResponse::TResetAllResponse:
|
||||
case RequestResponse::TPersistResponse:
|
||||
HandleResponse();
|
||||
break;
|
||||
|
||||
|
@ -279,6 +280,10 @@ QuotaRequestChild::Recv__delete__(const RequestResponse& aResponse)
|
|||
HandleResponse(aResponse.get_InitOriginResponse().created());
|
||||
break;
|
||||
|
||||
case RequestResponse::TPersistedResponse:
|
||||
HandleResponse(aResponse.get_PersistedResponse().persisted());
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unknown response type!");
|
||||
}
|
||||
|
|
|
@ -1255,6 +1255,63 @@ private:
|
|||
GetResponse(RequestResponse& aResponse) override;
|
||||
};
|
||||
|
||||
class PersistRequestBase
|
||||
: public QuotaRequestBase
|
||||
{
|
||||
const PrincipalInfo mPrincipalInfo;
|
||||
|
||||
protected:
|
||||
nsCString mSuffix;
|
||||
nsCString mGroup;
|
||||
|
||||
public:
|
||||
bool
|
||||
Init(Quota* aQuota) override;
|
||||
|
||||
protected:
|
||||
explicit PersistRequestBase(const PrincipalInfo& aPrincipalInfo);
|
||||
|
||||
private:
|
||||
nsresult
|
||||
DoInitOnMainThread() override;
|
||||
};
|
||||
|
||||
class PersistedOp final
|
||||
: public PersistRequestBase
|
||||
{
|
||||
bool mPersisted;
|
||||
|
||||
public:
|
||||
explicit PersistedOp(const RequestParams& aParams);
|
||||
|
||||
private:
|
||||
~PersistedOp()
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
DoDirectoryWork(QuotaManager* aQuotaManager) override;
|
||||
|
||||
void
|
||||
GetResponse(RequestResponse& aResponse) override;
|
||||
};
|
||||
|
||||
class PersistOp final
|
||||
: public PersistRequestBase
|
||||
{
|
||||
public:
|
||||
explicit PersistOp(const RequestParams& aParams);
|
||||
|
||||
private:
|
||||
~PersistOp()
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
DoDirectoryWork(QuotaManager* aQuotaManager) override;
|
||||
|
||||
void
|
||||
GetResponse(RequestResponse& aResponse) override;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Helper Functions
|
||||
******************************************************************************/
|
||||
|
@ -1852,6 +1909,52 @@ EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EnsureOriginDirectory(nsIFile* aDirectory, bool* aCreated)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
nsresult rv;
|
||||
|
||||
#ifndef RELEASE_OR_BETA
|
||||
bool exists;
|
||||
rv = aDirectory->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
nsString leafName;
|
||||
nsresult rv = aDirectory->GetLeafName(leafName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!leafName.EqualsLiteral(kChromeOrigin)) {
|
||||
nsCString spec;
|
||||
OriginAttributes attrs;
|
||||
OriginParser::ResultType result =
|
||||
OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
|
||||
spec,
|
||||
&attrs);
|
||||
if (NS_WARN_IF(result != OriginParser::ValidOrigin)) {
|
||||
QM_WARNING("Preventing creation of a new origin directory which is not "
|
||||
"supported by our origin parser or is obsolete!");
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
rv = EnsureDirectory(aDirectory, aCreated);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
enum FileFlag {
|
||||
kTruncateFileFlag,
|
||||
kUpdateFileFlag,
|
||||
|
@ -2077,6 +2180,7 @@ CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
|
|||
nsresult
|
||||
CreateDirectoryMetadata2(nsIFile* aDirectory,
|
||||
int64_t aTimestamp,
|
||||
bool aPersisted,
|
||||
const nsACString& aSuffix,
|
||||
const nsACString& aGroup,
|
||||
const nsACString& aOrigin)
|
||||
|
@ -2108,8 +2212,7 @@ CreateDirectoryMetadata2(nsIFile* aDirectory,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// Reserved for navigator.persist()
|
||||
rv = stream->WriteBoolean(false);
|
||||
rv = stream->WriteBoolean(aPersisted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -2168,6 +2271,43 @@ CreateDirectoryMetadata2(nsIFile* aDirectory,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateDirectoryMetadataFiles(nsIFile* aDirectory,
|
||||
bool aPersisted,
|
||||
const nsACString& aSuffix,
|
||||
const nsACString& aGroup,
|
||||
const nsACString& aOrigin,
|
||||
int64_t* aTimestamp)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
int64_t timestamp = PR_Now();
|
||||
|
||||
nsresult rv = CreateDirectoryMetadata(aDirectory,
|
||||
timestamp,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = CreateDirectoryMetadata2(aDirectory,
|
||||
timestamp,
|
||||
aPersisted,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aTimestamp) {
|
||||
*aTimestamp = timestamp;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetBinaryInputStream(nsIFile* aDirectory,
|
||||
const nsAString& aFilename,
|
||||
|
@ -3512,6 +3652,8 @@ QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
|
|||
void
|
||||
QuotaManager::RemoveQuota()
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
MutexAutoLock lock(mQuotaMutex);
|
||||
|
||||
for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
|
||||
|
@ -3637,6 +3779,40 @@ QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
|
|||
return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
|
||||
}
|
||||
|
||||
Nullable<bool>
|
||||
QuotaManager::OriginPersisted(const nsACString& aGroup,
|
||||
const nsACString& aOrigin)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
MutexAutoLock lock(mQuotaMutex);
|
||||
|
||||
RefPtr<OriginInfo> originInfo = LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
if (originInfo) {
|
||||
return Nullable<bool>(originInfo->LockedPersisted());
|
||||
}
|
||||
|
||||
return Nullable<bool>();
|
||||
}
|
||||
|
||||
void
|
||||
QuotaManager::PersistOrigin(const nsACString& aGroup,
|
||||
const nsACString& aOrigin)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
MutexAutoLock lock(mQuotaMutex);
|
||||
|
||||
RefPtr<OriginInfo> originInfo = LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
if (originInfo && !originInfo->LockedPersisted()) {
|
||||
originInfo->LockedPersist();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId)
|
||||
{
|
||||
|
@ -3691,6 +3867,7 @@ QuotaManager::RestoreDirectoryMetadata2(nsIFile* aDirectory, bool aPersistent)
|
|||
nsresult
|
||||
QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted,
|
||||
nsACString& aSuffix,
|
||||
nsACString& aGroup,
|
||||
nsACString& aOrigin)
|
||||
|
@ -3698,6 +3875,7 @@ QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
|
|||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aDirectory);
|
||||
MOZ_ASSERT(aTimestamp);
|
||||
MOZ_ASSERT(aPersisted);
|
||||
MOZ_ASSERT(mStorageInitialized);
|
||||
|
||||
nsCOMPtr<nsIBinaryInputStream> binaryStream;
|
||||
|
@ -3750,6 +3928,7 @@ QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
|
|||
}
|
||||
|
||||
*aTimestamp = timestamp;
|
||||
*aPersisted = persisted;
|
||||
aSuffix = suffix;
|
||||
aGroup = group;
|
||||
aOrigin = origin;
|
||||
|
@ -3760,12 +3939,14 @@ nsresult
|
|||
QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
|
||||
bool aPersistent,
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted,
|
||||
nsACString& aSuffix,
|
||||
nsACString& aGroup,
|
||||
nsACString& aOrigin)
|
||||
{
|
||||
nsresult rv = GetDirectoryMetadata2(aDirectory,
|
||||
aTimestamp,
|
||||
aPersisted,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
|
@ -3777,6 +3958,7 @@ QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
|
|||
|
||||
rv = GetDirectoryMetadata2(aDirectory,
|
||||
aTimestamp,
|
||||
aPersisted,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
|
@ -3789,11 +3971,13 @@ QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
|
|||
}
|
||||
|
||||
nsresult
|
||||
QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp)
|
||||
QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
MOZ_ASSERT(aDirectory);
|
||||
MOZ_ASSERT(aTimestamp);
|
||||
MOZ_ASSERT(aTimestamp || aPersisted);
|
||||
MOZ_ASSERT(mStorageInitialized);
|
||||
|
||||
nsCOMPtr<nsIBinaryInputStream> binaryStream;
|
||||
|
@ -3810,23 +3994,37 @@ QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp)
|
|||
return rv;
|
||||
}
|
||||
|
||||
*aTimestamp = timestamp;
|
||||
bool persisted;
|
||||
if (aPersisted) {
|
||||
rv = binaryStream->ReadBoolean(&persisted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (aTimestamp) {
|
||||
*aTimestamp = timestamp;
|
||||
}
|
||||
if (aPersisted) {
|
||||
*aPersisted = persisted;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
|
||||
bool aPersistent,
|
||||
int64_t* aTimestamp)
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted)
|
||||
{
|
||||
nsresult rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
|
||||
nsresult rv = GetDirectoryMetadata2(aDirectory, aTimestamp, aPersisted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
|
||||
rv = GetDirectoryMetadata2(aDirectory, aTimestamp, aPersisted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -3899,12 +4097,14 @@ QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
|
|||
}
|
||||
|
||||
int64_t timestamp;
|
||||
bool persisted;
|
||||
nsCString suffix;
|
||||
nsCString group;
|
||||
nsCString origin;
|
||||
rv = GetDirectoryMetadata2WithRestore(childDirectory,
|
||||
/* aPersistent */ false,
|
||||
×tamp,
|
||||
&persisted,
|
||||
suffix,
|
||||
group,
|
||||
origin);
|
||||
|
@ -3912,8 +4112,8 @@ QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = InitializeOrigin(aPersistenceType, group, origin, timestamp,
|
||||
/* aPersisted */ false, childDirectory);
|
||||
rv = InitializeOrigin(aPersistenceType, group, origin, timestamp, persisted,
|
||||
childDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -4820,66 +5020,29 @@ QuotaManager::EnsureOriginIsInitializedInternal(
|
|||
CheckTemporaryStorageLimits();
|
||||
}
|
||||
|
||||
int64_t timestamp;
|
||||
|
||||
#ifndef RELEASE_OR_BETA
|
||||
bool exists;
|
||||
rv = directory->Exists(&exists);
|
||||
bool created;
|
||||
rv = EnsureOriginDirectory(directory, &created);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
nsString leafName;
|
||||
nsresult rv = directory->GetLeafName(leafName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!leafName.EqualsLiteral(kChromeOrigin)) {
|
||||
nsCString spec;
|
||||
OriginAttributes attrs;
|
||||
OriginParser::ResultType result =
|
||||
OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
|
||||
spec,
|
||||
&attrs);
|
||||
if (NS_WARN_IF(result != OriginParser::ValidOrigin)) {
|
||||
QM_WARNING("Preventing creation of a new origin directory which is not "
|
||||
"supported by our origin parser or is obsolete!");
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool created;
|
||||
rv = EnsureDirectory(directory, &created);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int64_t timestamp;
|
||||
if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
|
||||
if (created) {
|
||||
timestamp = PR_Now();
|
||||
|
||||
rv = CreateDirectoryMetadata(directory,
|
||||
timestamp,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
rv = CreateDirectoryMetadataFiles(directory,
|
||||
/* aPersisted */ true,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin,
|
||||
×tamp);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = CreateDirectoryMetadata2(directory,
|
||||
timestamp,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
rv = GetDirectoryMetadata2WithRestore(directory,
|
||||
/* aPersistent */ true,
|
||||
×tamp);
|
||||
×tamp,
|
||||
/* aPersisted */ nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -4893,24 +5056,16 @@ QuotaManager::EnsureOriginIsInitializedInternal(
|
|||
|
||||
mInitializedOrigins.AppendElement(aOrigin);
|
||||
} else if (created) {
|
||||
timestamp = PR_Now();
|
||||
|
||||
rv = CreateDirectoryMetadata(directory,
|
||||
timestamp,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
rv = CreateDirectoryMetadataFiles(directory,
|
||||
/* aPersisted */ false,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin,
|
||||
×tamp);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = CreateDirectoryMetadata2(directory,
|
||||
timestamp,
|
||||
aSuffix,
|
||||
aGroup,
|
||||
aOrigin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, timestamp,
|
||||
/* aPersisted */ false, directory);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -5228,6 +5383,25 @@ QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<OriginInfo>
|
||||
QuotaManager::LockedGetOriginInfo(PersistenceType aPersistenceType,
|
||||
const nsACString& aGroup,
|
||||
const nsACString& aOrigin)
|
||||
{
|
||||
mQuotaMutex.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
|
||||
|
||||
GroupInfoPair* pair;
|
||||
if (mGroupInfoPairs.Get(aGroup, &pair)) {
|
||||
RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
|
||||
if (groupInfo) {
|
||||
return groupInfo->LockedGetOriginInfo(aOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
QuotaManager::CheckTemporaryStorageLimits()
|
||||
{
|
||||
|
@ -6135,6 +6309,14 @@ Quota::AllocPQuotaRequestParent(const RequestParams& aParams)
|
|||
actor = new ResetOrClearOp(/* aClear */ false);
|
||||
break;
|
||||
|
||||
case RequestParams::TPersistedParams:
|
||||
actor = new PersistedOp(aParams);
|
||||
break;
|
||||
|
||||
case RequestParams::TPersistParams:
|
||||
actor = new PersistOp(aParams);
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
@ -6817,9 +6999,11 @@ ClearRequestBase::DeleteFiles(QuotaManager* aQuotaManager,
|
|||
nsCString suffix;
|
||||
nsCString group;
|
||||
nsCString origin;
|
||||
bool persisted;
|
||||
rv = aQuotaManager->GetDirectoryMetadata2WithRestore(file,
|
||||
persistent,
|
||||
×tamp,
|
||||
&persisted,
|
||||
suffix,
|
||||
group,
|
||||
origin);
|
||||
|
@ -6981,6 +7165,235 @@ ClearDataOp::GetResponse(RequestResponse& aResponse)
|
|||
aResponse = ClearDataResponse();
|
||||
}
|
||||
|
||||
PersistRequestBase::PersistRequestBase(const PrincipalInfo& aPrincipalInfo)
|
||||
: QuotaRequestBase(/* aExclusive */ false)
|
||||
, mPrincipalInfo(aPrincipalInfo)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
}
|
||||
|
||||
bool
|
||||
PersistRequestBase::Init(Quota* aQuota)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aQuota);
|
||||
|
||||
if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mPersistenceType.SetValue(PERSISTENCE_TYPE_DEFAULT);
|
||||
|
||||
mNeedsMainThreadInit = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PersistRequestBase::DoInitOnMainThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(GetState() == State_Initializing);
|
||||
MOZ_ASSERT(mNeedsMainThreadInit);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPrincipal> principal =
|
||||
PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Figure out which origin we're dealing with.
|
||||
nsCString origin;
|
||||
rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
|
||||
&origin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mOriginScope.SetFromOrigin(origin);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PersistedOp::PersistedOp(const RequestParams& aParams)
|
||||
: PersistRequestBase(aParams.get_PersistedParams().principalInfo())
|
||||
, mPersisted(false)
|
||||
{
|
||||
MOZ_ASSERT(aParams.type() == RequestParams::TPersistedParams);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PersistedOp::DoDirectoryWork(QuotaManager* aQuotaManager)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
MOZ_ASSERT(!mPersistenceType.IsNull());
|
||||
MOZ_ASSERT(mPersistenceType.Value() == PERSISTENCE_TYPE_DEFAULT);
|
||||
MOZ_ASSERT(mOriginScope.IsOrigin());
|
||||
|
||||
PROFILER_LABEL("Quota", "PersistedOp::DoDirectoryWork",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
|
||||
Nullable<bool> persisted =
|
||||
aQuotaManager->OriginPersisted(mGroup, mOriginScope.GetOrigin());
|
||||
|
||||
if (!persisted.IsNull()) {
|
||||
mPersisted = persisted.Value();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we get here, it means the origin hasn't been initialized yet.
|
||||
// Try to get the persisted flag from directory metadata on disk.
|
||||
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
nsresult rv = aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
|
||||
mOriginScope.GetOrigin(),
|
||||
getter_AddRefs(directory));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool exists;
|
||||
rv = directory->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
// Get the persisted flag.
|
||||
bool persisted;
|
||||
rv =
|
||||
aQuotaManager->GetDirectoryMetadata2WithRestore(directory,
|
||||
/* aPersistent */ false,
|
||||
/* aTimestamp */ nullptr,
|
||||
&persisted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mPersisted = persisted;
|
||||
} else {
|
||||
// The directory has not been created yet.
|
||||
mPersisted = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PersistedOp::GetResponse(RequestResponse& aResponse)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
PersistedResponse persistedResponse;
|
||||
persistedResponse.persisted() = mPersisted;
|
||||
|
||||
aResponse = persistedResponse;
|
||||
}
|
||||
|
||||
PersistOp::PersistOp(const RequestParams& aParams)
|
||||
: PersistRequestBase(aParams.get_PersistParams().principalInfo())
|
||||
{
|
||||
MOZ_ASSERT(aParams.type() == RequestParams::TPersistParams);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PersistOp::DoDirectoryWork(QuotaManager* aQuotaManager)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
MOZ_ASSERT(!mPersistenceType.IsNull());
|
||||
MOZ_ASSERT(mPersistenceType.Value() == PERSISTENCE_TYPE_DEFAULT);
|
||||
MOZ_ASSERT(mOriginScope.IsOrigin());
|
||||
|
||||
PROFILER_LABEL("Quota", "PersistOp::DoDirectoryWork",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
|
||||
// Update directory metadata on disk first.
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
nsresult rv = aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
|
||||
mOriginScope.GetOrigin(),
|
||||
getter_AddRefs(directory));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool created;
|
||||
rv = EnsureOriginDirectory(directory, &created);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (created) {
|
||||
rv = CreateDirectoryMetadataFiles(directory,
|
||||
/* aPersisted */ true,
|
||||
mSuffix,
|
||||
mGroup,
|
||||
mOriginScope.GetOrigin(),
|
||||
/* aTimestamp */ nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
// Get the persisted flag (restore the metadata file if necessary).
|
||||
bool persisted;
|
||||
rv =
|
||||
aQuotaManager->GetDirectoryMetadata2WithRestore(directory,
|
||||
/* aPersistent */ false,
|
||||
/* aTimestamp */ nullptr,
|
||||
&persisted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!persisted) {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = directory->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = file->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBinaryOutputStream> stream;
|
||||
rv = GetBinaryOutputStream(file, kUpdateFileFlag, getter_AddRefs(stream));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(stream);
|
||||
|
||||
// Update origin access time while we are here.
|
||||
rv = stream->Write64(PR_Now());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Set the persisted flag to true.
|
||||
rv = stream->WriteBoolean(true);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Directory metadata has been successfully created/updated, try to update
|
||||
// OriginInfo too (it's ok if OriginInfo doesn't exist yet).
|
||||
aQuotaManager->PersistOrigin(mGroup, mOriginScope.GetOrigin());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PersistOp::GetResponse(RequestResponse& aResponse)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
aResponse = PersistResponse();
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageDirectoryHelper::GetDirectoryMetadata(nsIFile* aDirectory,
|
||||
int64_t& aTimestamp,
|
||||
|
@ -8125,6 +8538,7 @@ UpgradeStorageFrom0_0To1_0Helper::ProcessOriginDirectory(
|
|||
|
||||
rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
|
||||
aOriginProps.mTimestamp,
|
||||
/* aPersisted */ false,
|
||||
aOriginProps.mSuffix,
|
||||
aOriginProps.mGroup,
|
||||
aOriginProps.mOrigin);
|
||||
|
@ -8407,6 +8821,7 @@ UpgradeStorageFrom1_0To2_0Helper::MaybeStripObsoleteOriginAttributes(
|
|||
|
||||
rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
|
||||
aOriginProps.mTimestamp,
|
||||
/* aPersisted */ false,
|
||||
aOriginProps.mSuffix,
|
||||
aOriginProps.mGroup,
|
||||
aOriginProps.mOrigin);
|
||||
|
@ -8478,6 +8893,7 @@ UpgradeStorageFrom1_0To2_0Helper::ProcessOriginDirectory(
|
|||
if (aOriginProps.mNeedsRestore2) {
|
||||
rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
|
||||
aOriginProps.mTimestamp,
|
||||
/* aPersisted */ false,
|
||||
aOriginProps.mSuffix,
|
||||
aOriginProps.mGroup,
|
||||
aOriginProps.mOrigin);
|
||||
|
@ -8520,8 +8936,10 @@ RestoreDirectoryMetadata2Helper::ProcessOriginDirectory(
|
|||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
// We don't have any approach to restore aPersisted, so reset it to false.
|
||||
nsresult rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
|
||||
aOriginProps.mTimestamp,
|
||||
/* aPersisted */ false,
|
||||
aOriginProps.mSuffix,
|
||||
aOriginProps.mGroup,
|
||||
aOriginProps.mOrigin);
|
||||
|
|
|
@ -59,6 +59,16 @@ struct ResetAllParams
|
|||
{
|
||||
};
|
||||
|
||||
struct PersistedParams
|
||||
{
|
||||
PrincipalInfo principalInfo;
|
||||
};
|
||||
|
||||
struct PersistParams
|
||||
{
|
||||
PrincipalInfo principalInfo;
|
||||
};
|
||||
|
||||
union RequestParams
|
||||
{
|
||||
InitParams;
|
||||
|
@ -67,6 +77,8 @@ union RequestParams
|
|||
ClearDataParams;
|
||||
ClearAllParams;
|
||||
ResetAllParams;
|
||||
PersistedParams;
|
||||
PersistParams;
|
||||
};
|
||||
|
||||
protocol PQuota
|
||||
|
|
|
@ -33,6 +33,15 @@ struct ResetAllResponse
|
|||
{
|
||||
};
|
||||
|
||||
struct PersistedResponse
|
||||
{
|
||||
bool persisted;
|
||||
};
|
||||
|
||||
struct PersistResponse
|
||||
{
|
||||
};
|
||||
|
||||
union RequestResponse
|
||||
{
|
||||
nsresult;
|
||||
|
@ -42,6 +51,8 @@ union RequestResponse
|
|||
ClearDataResponse;
|
||||
ClearAllResponse;
|
||||
ResetAllResponse;
|
||||
PersistedResponse;
|
||||
PersistResponse;
|
||||
};
|
||||
|
||||
protocol PQuotaRequest
|
||||
|
|
|
@ -185,6 +185,14 @@ public:
|
|||
const nsACString& aOrigin,
|
||||
const nsAString& aPath);
|
||||
|
||||
Nullable<bool>
|
||||
OriginPersisted(const nsACString& aGroup,
|
||||
const nsACString& aOrigin);
|
||||
|
||||
void
|
||||
PersistOrigin(const nsACString& aGroup,
|
||||
const nsACString& aOrigin);
|
||||
|
||||
// Called when a process is being shot down. Aborts any running operations
|
||||
// for the given process.
|
||||
void
|
||||
|
@ -201,6 +209,7 @@ public:
|
|||
nsresult
|
||||
GetDirectoryMetadata2(nsIFile* aDirectory,
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted,
|
||||
nsACString& aSuffix,
|
||||
nsACString& aGroup,
|
||||
nsACString& aOrigin);
|
||||
|
@ -209,17 +218,21 @@ public:
|
|||
GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
|
||||
bool aPersistent,
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted,
|
||||
nsACString& aSuffix,
|
||||
nsACString& aGroup,
|
||||
nsACString& aOrigin);
|
||||
|
||||
nsresult
|
||||
GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp);
|
||||
GetDirectoryMetadata2(nsIFile* aDirectory,
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted);
|
||||
|
||||
nsresult
|
||||
GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
|
||||
bool aPersistent,
|
||||
int64_t* aTimestamp);
|
||||
int64_t* aTimestamp,
|
||||
bool* aPersisted);
|
||||
|
||||
// This is the main entry point into the QuotaManager API.
|
||||
// Any storage API implementation (quota client) that participates in
|
||||
|
@ -438,6 +451,11 @@ private:
|
|||
const nsACString& aGroup,
|
||||
const nsACString& aOrigin);
|
||||
|
||||
already_AddRefed<OriginInfo>
|
||||
LockedGetOriginInfo(PersistenceType aPersistenceType,
|
||||
const nsACString& aGroup,
|
||||
const nsACString& aOrigin);
|
||||
|
||||
nsresult
|
||||
MaybeUpgradeIndexedDBDirectory();
|
||||
|
||||
|
|
|
@ -60,6 +60,25 @@ TestingPrefChangedCallback(const char* aPrefName,
|
|||
gTestingMode = Preferences::GetBool(aPrefName);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
|
||||
PrincipalInfo& aPrincipalInfo)
|
||||
{
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
|
||||
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
|
||||
aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class AbortOperationsRunnable final
|
||||
: public Runnable
|
||||
{
|
||||
|
@ -540,18 +559,12 @@ QuotaManagerService::InitStoragesForPrincipal(
|
|||
|
||||
InitOriginParams params;
|
||||
|
||||
PrincipalInfo& principalInfo = params.principalInfo();
|
||||
|
||||
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
|
||||
nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
|
||||
params.principalInfo());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
|
||||
principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
Nullable<PersistenceType> persistenceType;
|
||||
rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || persistenceType.IsNull()) {
|
||||
|
@ -585,17 +598,12 @@ QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
|
|||
|
||||
UsageParams params;
|
||||
|
||||
PrincipalInfo& principalInfo = params.principalInfo();
|
||||
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
|
||||
nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
|
||||
params.principalInfo());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
|
||||
principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
params.getGroupUsage() = aGetGroupUsage;
|
||||
|
||||
nsAutoPtr<PendingRequestInfo> info(new UsageRequestInfo(request, params));
|
||||
|
@ -655,18 +663,12 @@ QuotaManagerService::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal,
|
|||
|
||||
ClearOriginParams params;
|
||||
|
||||
PrincipalInfo& principalInfo = params.principalInfo();
|
||||
|
||||
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
|
||||
nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
|
||||
params.principalInfo());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
|
||||
principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
Nullable<PersistenceType> persistenceType;
|
||||
rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -717,6 +719,64 @@ QuotaManagerService::Reset(nsIQuotaRequest** _retval)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
QuotaManagerService::Persisted(nsIPrincipal* aPrincipal,
|
||||
nsIQuotaRequest** _retval)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(_retval);
|
||||
|
||||
RefPtr<Request> request = new Request(aPrincipal);
|
||||
|
||||
PersistedParams params;
|
||||
|
||||
nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
|
||||
params.principalInfo());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
|
||||
|
||||
rv = InitiateRequest(info);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
request.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
QuotaManagerService::Persist(nsIPrincipal* aPrincipal,
|
||||
nsIQuotaRequest** _retval)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(_retval);
|
||||
|
||||
RefPtr<Request> request = new Request(aPrincipal);
|
||||
|
||||
PersistParams params;
|
||||
|
||||
nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
|
||||
params.principalInfo());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
|
||||
|
||||
rv = InitiateRequest(info);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
request.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
QuotaManagerService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
|
|
|
@ -100,4 +100,22 @@ interface nsIQuotaManagerService : nsISupports
|
|||
*/
|
||||
[must_use] nsIQuotaRequest
|
||||
reset();
|
||||
|
||||
/**
|
||||
* Check if given origin is persisted.
|
||||
*
|
||||
* @param aPrincipal
|
||||
* A principal for the origin which we want to check.
|
||||
*/
|
||||
[must_use] nsIQuotaRequest
|
||||
persisted(in nsIPrincipal aPrincipal);
|
||||
|
||||
/**
|
||||
* Persist given origin.
|
||||
*
|
||||
* @param aPrincipal
|
||||
* A principal for the origin which we want to persist.
|
||||
*/
|
||||
[must_use] nsIQuotaRequest
|
||||
persist(in nsIPrincipal aPrincipal);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
const NS_OK = Cr.NS_OK;
|
||||
const NS_ERROR_FAILURE = Cr.NS_ERROR_FAILURE;
|
||||
const NS_ERROR_UNEXPECTED = Cr.NS_ERROR_UNEXPECTED;
|
||||
|
||||
function is(a, b, msg)
|
||||
|
@ -146,6 +147,20 @@ function reset(callback)
|
|||
return request;
|
||||
}
|
||||
|
||||
function persist(principal, callback) {
|
||||
let request = SpecialPowers._getQuotaManager().persist(principal);
|
||||
request.callback = callback;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
function persisted(principal, callback) {
|
||||
let request = SpecialPowers._getQuotaManager().persisted(principal);
|
||||
request.callback = callback;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
function installPackage(packageName)
|
||||
{
|
||||
let directoryService = Cc["@mozilla.org/file/directory_service;1"]
|
||||
|
@ -237,6 +252,15 @@ function compareBuffers(buffer1, buffer2)
|
|||
return true;
|
||||
}
|
||||
|
||||
function getPersistedFromMetadata(readBuffer)
|
||||
{
|
||||
const persistedPosition = 8; // Persisted state is stored in the 9th byte
|
||||
let view =
|
||||
readBuffer instanceof Uint8Array ? readBuffer : new Uint8Array(readBuffer);
|
||||
|
||||
return !!view[persistedPosition];
|
||||
}
|
||||
|
||||
function grabUsageAndContinueHandler(request)
|
||||
{
|
||||
testGenerator.next(request.usage);
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var testGenerator = testSteps();
|
||||
|
||||
function* testSteps()
|
||||
{
|
||||
const origins = [
|
||||
{
|
||||
url: "http://default.test.persist",
|
||||
path: "storage/default/http+++default.test.persist",
|
||||
persistence: "default"
|
||||
},
|
||||
|
||||
{
|
||||
url: "ftp://ftp.invalid.origin",
|
||||
path: "storage/default/ftp+++ftp.invalid.origin",
|
||||
persistence: "default"
|
||||
},
|
||||
];
|
||||
|
||||
const metadataFileName = ".metadata-v2";
|
||||
|
||||
let principal = getPrincipal(origins[0].url);
|
||||
|
||||
info("Persisting an uninitialized origin");
|
||||
|
||||
// Origin directory doesn't exist yet, so only check the result for
|
||||
// persisted().
|
||||
let request = persisted(principal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_OK, "Persisted() succeeded");
|
||||
ok(!request.result, "The origin is not persisted");
|
||||
|
||||
info("Verifying persist() does update the metadata");
|
||||
|
||||
request = persist(principal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_OK, "Persist() succeeded");
|
||||
|
||||
let originDir = getRelativeFile(origins[0].path);
|
||||
let exists = originDir.exists();
|
||||
ok(exists, "Origin directory does exist");
|
||||
|
||||
info("Reading out contents of metadata file");
|
||||
|
||||
let metadataFile = originDir.clone();
|
||||
metadataFile.append(metadataFileName);
|
||||
|
||||
File.createFromNsIFile(metadataFile).then(grabArgAndContinueHandler);
|
||||
let file = yield undefined;
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = continueToNextStepSync;
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
yield undefined;
|
||||
|
||||
let originPersisted = getPersistedFromMetadata(fileReader.result);
|
||||
ok(originPersisted, "The origin is persisted");
|
||||
|
||||
info("Verifying persisted()");
|
||||
|
||||
request = persisted(principal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_OK, "Persisted() succeeded");
|
||||
ok(request.result === originPersisted, "Persisted() concurs with metadata");
|
||||
|
||||
info("Clearing the origin");
|
||||
|
||||
// Clear the origin since we'll test the same directory again under different
|
||||
// circumstances.
|
||||
clearOrigin(principal, origins[0].persistence, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
info("Persisting an already initialized origin");
|
||||
|
||||
initOrigin(principal, origins[0].persistence, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
info("Reading out contents of metadata file");
|
||||
|
||||
fileReader = new FileReader();
|
||||
fileReader.onload = continueToNextStepSync;
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
yield undefined;
|
||||
|
||||
originPersisted = getPersistedFromMetadata(fileReader.result);
|
||||
ok(!originPersisted, "The origin isn't persisted after clearing");
|
||||
|
||||
info("Verifying persisted()");
|
||||
|
||||
request = persisted(principal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_OK, "Persisted() succeeded");
|
||||
ok(request.result === originPersisted, "Persisted() concurs with metadata");
|
||||
|
||||
info("Verifying persist() does update the metadata");
|
||||
|
||||
request = persist(principal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_OK, "Persist() succeeded");
|
||||
|
||||
info("Reading out contents of metadata file");
|
||||
|
||||
fileReader = new FileReader();
|
||||
fileReader.onload = continueToNextStepSync;
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
yield undefined;
|
||||
|
||||
originPersisted = getPersistedFromMetadata(fileReader.result);
|
||||
ok(originPersisted, "The origin is persisted");
|
||||
|
||||
info("Verifying persisted()");
|
||||
|
||||
request = persisted(principal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_OK, "Persisted() succeeded");
|
||||
ok(request.result === originPersisted, "Persisted() concurs with metadata");
|
||||
|
||||
info("Persisting an invalid origin");
|
||||
|
||||
let invalidPrincipal = getPrincipal(origins[1].url);
|
||||
|
||||
request = persist(invalidPrincipal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_ERROR_FAILURE,
|
||||
"Persist() failed because of the invalid origin");
|
||||
|
||||
originDir = getRelativeFile(origins[1].path);
|
||||
exists = originDir.exists();
|
||||
ok(!exists, "Directory for invalid origin doesn't exist");
|
||||
|
||||
request = persisted(invalidPrincipal, continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
ok(request.resultCode === NS_OK, "Persisted() succeeded");
|
||||
ok(!request.result,
|
||||
"The origin isn't persisted since the operation failed");
|
||||
|
||||
finishTest();
|
||||
}
|
|
@ -22,6 +22,7 @@ support-files =
|
|||
[test_morgueCleanup.js]
|
||||
[test_obsoleteOriginAttributesUpgrade.js]
|
||||
[test_originAttributesUpgrade.js]
|
||||
[test_persist.js]
|
||||
[test_removeAppsUpgrade.js]
|
||||
[test_storagePersistentUpgrade.js]
|
||||
[test_tempMetadataCleanup.js]
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe srcdoc="<img src=x onerror='parent.postMessage({result: `unexpected-csp-violation`}, `*`);'>"
|
||||
sandbox="allow-scripts"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
content-security-policy: default-src *;
|
|
@ -0,0 +1,79 @@
|
|||
// Custom *.sjs file specifically for the needs of
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1073952
|
||||
|
||||
"use strict";
|
||||
Components.utils.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
const SCRIPT = `
|
||||
<script>
|
||||
parent.parent.postMessage({result: "allowed"}, "*");
|
||||
</script>`;
|
||||
|
||||
const SIMPLE_IFRAME_SRCDOC = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"></head>
|
||||
<body>
|
||||
<iframe sandbox="allow-scripts" srcdoc="` + SCRIPT + `"></iframe>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const INNER_SRCDOC_IFRAME = `
|
||||
<iframe sandbox='allow-scripts' srcdoc='<script>
|
||||
parent.parent.parent.postMessage({result: "allowed"}, "*");
|
||||
</script>'>
|
||||
</iframe>`;
|
||||
|
||||
const NESTED_IFRAME_SRCDOC = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"></head>
|
||||
<body>
|
||||
<iframe sandbox="allow-scripts" srcdoc="` + INNER_SRCDOC_IFRAME + `"></iframe>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
|
||||
const INNER_DATAURI_IFRAME = `
|
||||
<iframe sandbox='allow-scripts' src='data:text/html,<script>
|
||||
parent.parent.parent.postMessage({result: "allowed"}, "*");
|
||||
</script>'>
|
||||
</iframe>`;
|
||||
|
||||
const NESTED_IFRAME_SRCDOC_DATAURI = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"></head>
|
||||
<body>
|
||||
<iframe sandbox="allow-scripts" srcdoc="` + INNER_DATAURI_IFRAME + `"></iframe>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
function handleRequest(request, response) {
|
||||
const query = new URLSearchParams(request.queryString);
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
if (typeof query.get("csp") === "string") {
|
||||
response.setHeader("Content-Security-Policy", query.get("csp"), false);
|
||||
}
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
|
||||
if (query.get("action") === "simple_iframe_srcdoc") {
|
||||
response.write(SIMPLE_IFRAME_SRCDOC);
|
||||
return;
|
||||
}
|
||||
|
||||
if (query.get("action") === "nested_iframe_srcdoc") {
|
||||
response.write(NESTED_IFRAME_SRCDOC);
|
||||
return;
|
||||
}
|
||||
|
||||
if (query.get("action") === "nested_iframe_srcdoc_datauri") {
|
||||
response.write(NESTED_IFRAME_SRCDOC_DATAURI);
|
||||
return;
|
||||
}
|
||||
|
||||
// we should never get here, but just in case
|
||||
// return something unexpected
|
||||
response.write("do'h");
|
||||
}
|
|
@ -205,6 +205,9 @@ support-files =
|
|||
file_upgrade_insecure_navigation.sjs
|
||||
file_punycode_host_src.sjs
|
||||
file_punycode_host_src.js
|
||||
file_iframe_srcdoc.sjs
|
||||
file_iframe_sandbox_srcdoc.html
|
||||
file_iframe_sandbox_srcdoc.html^headers^
|
||||
|
||||
[test_base-uri.html]
|
||||
[test_blob_data_schemes.html]
|
||||
|
@ -293,3 +296,5 @@ tags = mcb
|
|||
[test_strict_dynamic_default_src.html]
|
||||
[test_upgrade_insecure_navigation.html]
|
||||
[test_punycode_host_src.html]
|
||||
[test_iframe_sandbox_srcdoc.html]
|
||||
[test_iframe_srcdoc.html]
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display">Bug 1073952</p>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
}
|
||||
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
var violationString = SpecialPowers.getPrivilegedProps(SpecialPowers.
|
||||
do_QueryInterface(subject, "nsISupportsCString"), "data");
|
||||
// the violation subject for inline script violations is unfortunately vague,
|
||||
// all we can do is match the string.
|
||||
if (!violationString.includes("Inline Script")) {
|
||||
return
|
||||
}
|
||||
ok(true, "CSP inherited into sandboxed srcdoc iframe, script blocked.");
|
||||
window.testFinished();
|
||||
}
|
||||
},
|
||||
|
||||
// must eventually call this to remove the listener,
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
}
|
||||
}
|
||||
|
||||
window.examiner = new examiner();
|
||||
|
||||
function testFinished() {
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
addEventListener("message", function(e) {
|
||||
ok(false, "We should not execute JS in srcdoc iframe.");
|
||||
window.testFinished();
|
||||
})
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
document.getElementById('cspframe').src = 'file_iframe_sandbox_srcdoc.html';
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,140 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1073952 - Test CSP enforcement within iframe srcdoc</title>
|
||||
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<iframe style="width:100%;" id="testframe"></iframe>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/*
|
||||
* Description of the test:
|
||||
* (1) We serve a site which makes use of script-allowed sandboxed iframe srcdoc
|
||||
* and make sure that CSP applies to the nested browsing context
|
||||
* within the iframe.
|
||||
* [PAGE WITH CSP [IFRAME SANDBOX SRCDOC [SCRIPT]]]
|
||||
*
|
||||
* (2) We serve a site which nests script within an script-allowed sandboxed
|
||||
* iframe srcdoc within another script-allowed sandboxed iframe srcdoc and
|
||||
* make sure that CSP applies to the nested browsing context
|
||||
* within the iframe*s*.
|
||||
* [PAGE WITH CSP [IFRAME SANDBOX SRCDOC [IFRAME SANDBOX SRCDOC [SCRIPT]]]]
|
||||
*
|
||||
* Please note that the test relies on the "csp-on-violate-policy" observer.
|
||||
* Whenever the script within the iframe is blocked observers are notified.
|
||||
* In turn, this renders the 'result' within tests[] unused. In case the script
|
||||
* would execute however, the postMessageHandler would bubble up 'allowed' and
|
||||
* the test would fail.
|
||||
*/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var tests = [
|
||||
// [PAGE *WITHOUT* CSP [IFRAME SRCDOC [SCRIPT]]]
|
||||
{ csp: "",
|
||||
result: "allowed",
|
||||
query: "simple_iframe_srcdoc",
|
||||
desc: "No CSP should run script within script-allowed sandboxed iframe srcdoc"
|
||||
},
|
||||
{ csp: "script-src https://test1.com",
|
||||
result: "blocked",
|
||||
query: "simple_iframe_srcdoc",
|
||||
desc: "CSP should block script within script-allowed sandboxediframe srcdoc"
|
||||
},
|
||||
// [PAGE *WITHOUT* CSP [IFRAME SRCDOC [IFRAME SRCDOC [SCRIPT]]]]
|
||||
{ csp: "",
|
||||
result: "allowed",
|
||||
query: "nested_iframe_srcdoc",
|
||||
desc: "No CSP should run script within script-allowed sandboxed iframe srcdoc nested within another script-allowed sandboxed iframe srcdoc"
|
||||
},
|
||||
// [PAGE WITH CSP [IFRAME SRCDOC ]]
|
||||
{ csp: "script-src https://test2.com",
|
||||
result: "blocked",
|
||||
query: "nested_iframe_srcdoc",
|
||||
desc: "CSP should block script within script-allowed sandboxed iframe srcdoc nested within another script-allowed sandboxed iframe srcdoc"
|
||||
},
|
||||
{ csp: "",
|
||||
result: "allowed",
|
||||
query: "nested_iframe_srcdoc_datauri",
|
||||
desc: "No CSP, should run script within script-allowed sandboxed iframe src with data URL nested within another script-allowed sandboxed iframe srcdoc"
|
||||
},
|
||||
{ csp: "script-src https://test3.com",
|
||||
result: "blocked",
|
||||
query: "nested_iframe_srcdoc_datauri",
|
||||
desc: "CSP should block script within script-allowed sandboxed iframe src with data URL nested within another script-allowed sandboxed iframe srcdoc"
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
// initializing to -1 so we start at index 0 when we start the test
|
||||
var counter = -1;
|
||||
|
||||
function finishTest() {
|
||||
window.removeEventListener("message", receiveMessage, false);
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage, false);
|
||||
function receiveMessage(event) {
|
||||
var result = event.data.result;
|
||||
testComplete(result, tests[counter].result, tests[counter].desc);
|
||||
}
|
||||
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
}
|
||||
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic === "csp-on-violate-policy") {
|
||||
var violationString = SpecialPowers.getPrivilegedProps(SpecialPowers.
|
||||
do_QueryInterface(subject, "nsISupportsCString"), "data");
|
||||
// the violation subject for inline script violations is unfortunately vague,
|
||||
// all we can do is match the string.
|
||||
if (!violationString.includes("Inline Script")) {
|
||||
return
|
||||
}
|
||||
testComplete("blocked", tests[counter].result, tests[counter].desc);
|
||||
}
|
||||
},
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
}
|
||||
}
|
||||
|
||||
function testComplete(result, expected, desc) {
|
||||
is(result, expected, desc);
|
||||
// ignore cases when we get csp violations and postMessage from the same frame.
|
||||
var frameURL = new URL(document.getElementById("testframe").src);
|
||||
var params = new URLSearchParams(frameURL.search);
|
||||
var counterInFrame = params.get("counter");
|
||||
if (counterInFrame == counter) {
|
||||
loadNextTest();
|
||||
}
|
||||
}
|
||||
|
||||
function loadNextTest() {
|
||||
counter++;
|
||||
if (counter == tests.length) {
|
||||
finishTest();
|
||||
return;
|
||||
}
|
||||
var src = "file_iframe_srcdoc.sjs";
|
||||
src += "?csp=" + escape(tests[counter].csp);
|
||||
src += "&action=" + escape(tests[counter].query);
|
||||
src += "&counter=" + counter;
|
||||
document.getElementById("testframe").src = src;
|
||||
}
|
||||
|
||||
// start running the tests
|
||||
window.examiner = new examiner();
|
||||
loadNextTest();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -385,6 +385,8 @@ nsSMILAnimationController::DoSample(bool aSkipUnchangedContainers)
|
|||
// Create the compositor table
|
||||
nsAutoPtr<nsSMILCompositorTable>
|
||||
currentCompositorTable(new nsSMILCompositorTable(0));
|
||||
nsTArray<RefPtr<SVGAnimationElement>>
|
||||
animElems(mAnimationElementTable.Count());
|
||||
|
||||
for (auto iter = mAnimationElementTable.Iter(); !iter.Done(); iter.Next()) {
|
||||
SVGAnimationElement* animElem = iter.Get()->GetKey();
|
||||
|
@ -392,6 +394,7 @@ nsSMILAnimationController::DoSample(bool aSkipUnchangedContainers)
|
|||
AddAnimationToCompositorTable(animElem,
|
||||
currentCompositorTable,
|
||||
isStyleFlushNeeded);
|
||||
animElems.AppendElement(animElem);
|
||||
}
|
||||
activeContainers.Clear();
|
||||
|
||||
|
|
|
@ -658,7 +658,7 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
|||
|
||||
nsCOMPtr<nsIOutputStream> responseBody;
|
||||
rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !responseBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1580,7 +1580,8 @@ private:
|
|||
nsresult rv2 =
|
||||
DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
|
||||
event, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
|
||||
if ((NS_WARN_IF(NS_FAILED(rv2)) && rv2 != NS_ERROR_XPC_JS_THREW_EXCEPTION) ||
|
||||
!event->WaitToRespond()) {
|
||||
nsCOMPtr<nsIRunnable> runnable;
|
||||
MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
|
||||
"We don't support system-principal serviceworkers");
|
||||
|
|
|
@ -354,7 +354,7 @@ ServiceWorkerRegistrar::ReadData()
|
|||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
GET_LINE(entry->currentWorkerURL());
|
||||
|
||||
|
@ -395,7 +395,7 @@ ServiceWorkerRegistrar::ReadData()
|
|||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
GET_LINE(entry->currentWorkerURL());
|
||||
|
||||
|
@ -428,7 +428,7 @@ ServiceWorkerRegistrar::ReadData()
|
|||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
GET_LINE(entry->currentWorkerURL());
|
||||
|
||||
|
@ -458,7 +458,7 @@ ServiceWorkerRegistrar::ReadData()
|
|||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
GET_LINE(entry->currentWorkerURL());
|
||||
|
||||
|
@ -488,7 +488,7 @@ ServiceWorkerRegistrar::ReadData()
|
|||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
// scriptSpec is no more used in latest version.
|
||||
GET_LINE(unused);
|
||||
|
|
|
@ -243,7 +243,8 @@ TEST(ServiceWorkerRegistrar, TestWriteData)
|
|||
nsAutoCString spec;
|
||||
spec.AppendPrintf("spec write %d", i);
|
||||
reg.principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(i, i % 2), spec);
|
||||
mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(i, i % 2),
|
||||
mozilla::void_t(), spec);
|
||||
|
||||
swr->TestRegisterServiceWorker(reg);
|
||||
}
|
||||
|
@ -597,7 +598,8 @@ TEST(ServiceWorkerRegistrar, TestDedupeWrite)
|
|||
nsAutoCString spec;
|
||||
spec.AppendPrintf("spec write dedupe/%d", i);
|
||||
reg.principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(0, false), spec);
|
||||
mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(0, false),
|
||||
mozilla::void_t(), spec);
|
||||
|
||||
swr->TestRegisterServiceWorker(reg);
|
||||
}
|
||||
|
|
|
@ -416,8 +416,10 @@ public:
|
|||
~AutoStyleNewChildren()
|
||||
{
|
||||
nsIPresShell* presShell = mElement->OwnerDoc()->GetShell();
|
||||
ServoStyleSet* servoSet = presShell ? presShell->StyleSet()->GetAsServo() : nullptr;
|
||||
if (servoSet) {
|
||||
if (!presShell || !presShell->DidInitialize()) {
|
||||
return;
|
||||
}
|
||||
if (ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo()) {
|
||||
// In general the element is always styled by the time we're applying XBL
|
||||
// bindings, because we need to style the element to know what the binding
|
||||
// URI is. However, programmatic consumers of the XBL service (like the
|
||||
|
|
|
@ -2338,7 +2338,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
|||
|
||||
else
|
||||
{
|
||||
yy_size_t num_to_read =
|
||||
int num_to_read =
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
|
||||
|
||||
while ( num_to_read <= 0 )
|
||||
|
|
|
@ -1273,6 +1273,9 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
|||
flingVelocity.Length().value, gfxPrefs::APZFlingMinVelocityThreshold());
|
||||
|
||||
if (flingVelocity.Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
|
||||
// Relieve overscroll now if needed, since we will not transition to a fling
|
||||
// animation and then an overscroll animation, and relieve it then.
|
||||
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
|
|
|
@ -314,6 +314,23 @@ TEST_F(APZCBasicTester, OverScroll_Bug1152051b) {
|
|||
SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
|
||||
}
|
||||
|
||||
// Tests that the page doesn't get stuck in an
|
||||
// overscroll animation after a low-velocity pan.
|
||||
TEST_F(APZCBasicTester, OverScrollAfterLowVelocityPan_Bug1343775) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
// Pan into overscroll with a velocity less than the
|
||||
// apz.fling_min_velocity_threshold preference.
|
||||
Pan(apzc, 10, 30);
|
||||
|
||||
EXPECT_TRUE(apzc->IsOverscrolled());
|
||||
|
||||
apzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Check that we recovered from overscroll.
|
||||
EXPECT_FALSE(apzc->IsOverscrolled());
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, OverScrollAbort) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Dragging the scrollbar on a page with a fixed-positioned element just past the right edge of the content</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<style>
|
||||
body {
|
||||
height: 2000px;
|
||||
}
|
||||
#fixed {
|
||||
width: 240px;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
right: -240px;
|
||||
z-index: 1000;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
#fixed-content {
|
||||
height: 2000px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var root;
|
||||
var scrollPos;
|
||||
var haveScrolled = false;
|
||||
var generatedAll = false;
|
||||
|
||||
// Be careful not to call subtestDone() until we've scrolled AND generated
|
||||
// all of the events.
|
||||
function maybeDone() {
|
||||
if (haveScrolled && generatedAll) {
|
||||
subtestDone();
|
||||
}
|
||||
}
|
||||
|
||||
function scrolled(e) {
|
||||
// Test that we have scrolled
|
||||
ok(root.scrollTop > scrollPos, "document scrolled after dragging scrollbar");
|
||||
haveScrolled = true;
|
||||
maybeDone();
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
root = document.scrollingElement;
|
||||
scrollPos = root.scrollTop;
|
||||
document.addEventListener('scroll', scrolled);
|
||||
|
||||
var scrollbarX = (window.innerWidth + root.clientWidth) / 2;
|
||||
// Move the mouse to the scrollbar
|
||||
yield synthesizeNativeMouseEvent(root, scrollbarX, 100, nativeMouseMoveEventMsg(), testDriver);
|
||||
// mouse down
|
||||
yield synthesizeNativeMouseEvent(root, scrollbarX, 100, nativeMouseDownEventMsg(), testDriver);
|
||||
// drag vertically
|
||||
yield synthesizeNativeMouseEvent(root, scrollbarX, 150, nativeMouseMoveEventMsg(), testDriver);
|
||||
// and release
|
||||
yield synthesizeNativeMouseEvent(root, scrollbarX, 150, nativeMouseUpEventMsg(), testDriver);
|
||||
|
||||
generatedAll = true;
|
||||
maybeDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test));
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="fixed">
|
||||
<p id="fixed-content"></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -10,6 +10,7 @@
|
|||
helper_bug1280013.html
|
||||
helper_bug1285070.html
|
||||
helper_bug1299195.html
|
||||
helper_bug1346632.html
|
||||
helper_click.html
|
||||
helper_div_pan.html
|
||||
helper_drag_click.html
|
||||
|
|
|
@ -17,7 +17,9 @@ var subtests = [
|
|||
// Sanity test for click but with some mouse movement between the down and up
|
||||
{'file': 'helper_drag_click.html'},
|
||||
// Test for dragging on a fake-scrollbar element that scrolls the page
|
||||
{'file': 'helper_drag_scroll.html'}
|
||||
{'file': 'helper_drag_scroll.html'},
|
||||
// Test for dragging the scrollbar with a fixed-pos element overlaying it
|
||||
{'file': 'helper_bug1346632.html'}
|
||||
];
|
||||
|
||||
if (isApzEnabled()) {
|
||||
|
|
|
@ -628,8 +628,15 @@ PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
|
|||
ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
|
||||
nsPoint point =
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
|
||||
uint32_t flags = 0;
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// On Android, we need IGNORE_ROOT_SCROLL_FRAME for correct hit testing
|
||||
// when zoomed out. On desktop, don't use it because it interferes with
|
||||
// hit testing for some purposes such as scrollbar dragging.
|
||||
flags = nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME;
|
||||
#endif
|
||||
nsIFrame* target =
|
||||
nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
|
||||
nsLayoutUtils::GetFrameForPoint(aRootFrame, point, flags);
|
||||
nsIScrollableFrame* scrollAncestor = target
|
||||
? nsLayoutUtils::GetAsyncScrollableAncestorFrame(target)
|
||||
: aRootFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
|
||||
|
|
|
@ -74,6 +74,7 @@ APZThreadUtils::IsControllerThread()
|
|||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(GenericTimerCallbackBase, nsITimerCallback)
|
||||
NS_IMPL_ISUPPORTS(GenericNamedTimerCallbackBase, nsITimerCallback, nsINamed)
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define mozilla_layers_APZThreadUtils_h
|
||||
|
||||
#include "base/message_loop.h"
|
||||
#include "nsINamed.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -98,6 +99,68 @@ GenericTimerCallback<Function>* NewTimerCallback(const Function& aFunction)
|
|||
return new GenericTimerCallback<Function>(aFunction);
|
||||
}
|
||||
|
||||
// A base class for GenericNamedTimerCallback<Function>.
|
||||
// This is necessary because NS_IMPL_ISUPPORTS doesn't work for a class
|
||||
// template.
|
||||
class GenericNamedTimerCallbackBase : public nsITimerCallback,
|
||||
public nsINamed
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
protected:
|
||||
virtual ~GenericNamedTimerCallbackBase() {}
|
||||
};
|
||||
|
||||
// An nsITimerCallback implementation with nsINamed that can be used with any
|
||||
// function object that's callable with no arguments.
|
||||
template <typename Function>
|
||||
class GenericNamedTimerCallback final : public GenericNamedTimerCallbackBase
|
||||
{
|
||||
public:
|
||||
explicit GenericNamedTimerCallback(const Function& aFunction,
|
||||
const char* aName)
|
||||
: mFunction(aFunction)
|
||||
, mName(aName)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Notify(nsITimer*) override
|
||||
{
|
||||
mFunction();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetName(nsACString& aName) override
|
||||
{
|
||||
aName = mName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD SetName(const char * aName) override
|
||||
{
|
||||
mName.Assign(aName);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
Function mFunction;
|
||||
nsCString mName;
|
||||
};
|
||||
|
||||
// Convenience function for constructing a GenericNamedTimerCallback.
|
||||
// Returns a raw pointer, suitable for passing directly as an argument to
|
||||
// nsITimer::InitWithCallback(). The intention is to enable the following
|
||||
// terse inline usage:
|
||||
// timer->InitWithCallback(NewNamedTimerCallback([](){ ... }, name), delay);
|
||||
template <typename Function>
|
||||
GenericNamedTimerCallback<Function>*
|
||||
NewNamedTimerCallback(const Function& aFunction,
|
||||
const char* aName)
|
||||
{
|
||||
return new GenericNamedTimerCallback<Function>(aFunction, aName);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ static pfn_ovr_GetFloatArray ovr_GetFloatArray = nullptr;
|
|||
static pfn_ovr_SetFloatArray ovr_SetFloatArray = nullptr;
|
||||
static pfn_ovr_GetString ovr_GetString = nullptr;
|
||||
static pfn_ovr_SetString ovr_SetString = nullptr;
|
||||
static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions = nullptr;
|
||||
|
||||
#ifdef XP_WIN
|
||||
static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
|
||||
|
@ -286,6 +287,7 @@ InitializeOculusCAPI()
|
|||
REQUIRE_FUNCTION(ovr_SetFloatArray);
|
||||
REQUIRE_FUNCTION(ovr_GetString);
|
||||
REQUIRE_FUNCTION(ovr_SetString);
|
||||
REQUIRE_FUNCTION(ovr_GetBoundaryDimensions);
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
|
@ -356,6 +358,7 @@ VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
|
|||
, mVertexBuffer(nullptr)
|
||||
, mInputLayout(nullptr)
|
||||
, mIsPresenting(false)
|
||||
, mEyeHeight(OVR_DEFAULT_EYE_HEIGHT)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
|
||||
|
||||
|
@ -373,6 +376,7 @@ VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
|
|||
if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
|
||||
}
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
|
||||
|
@ -401,6 +405,8 @@ VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
|
|||
// take the max of both for eye resolution
|
||||
mDisplayInfo.mEyeResolution.width = std::max(texSize[VRDisplayInfo::Eye_Left].w, texSize[VRDisplayInfo::Eye_Right].w);
|
||||
mDisplayInfo.mEyeResolution.height = std::max(texSize[VRDisplayInfo::Eye_Left].h, texSize[VRDisplayInfo::Eye_Right].h);
|
||||
|
||||
UpdateStageParameters();
|
||||
}
|
||||
|
||||
VRDisplayOculus::~VRDisplayOculus() {
|
||||
|
@ -418,10 +424,49 @@ VRDisplayOculus::Destroy()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayOculus::UpdateStageParameters()
|
||||
{
|
||||
ovrVector3f playArea;
|
||||
ovrResult res = ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
|
||||
if (res == ovrSuccess) {
|
||||
mDisplayInfo.mStageSize.width = playArea.x;
|
||||
mDisplayInfo.mStageSize.height = playArea.z;
|
||||
} else {
|
||||
// If we fail, fall back to reasonable defaults.
|
||||
// 1m x 1m space
|
||||
mDisplayInfo.mStageSize.width = 1.0f;
|
||||
mDisplayInfo.mStageSize.height = 1.0f;
|
||||
}
|
||||
|
||||
mEyeHeight = ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._42 = mEyeHeight;
|
||||
mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayOculus::ZeroSensor()
|
||||
{
|
||||
ovr_RecenterTrackingOrigin(mSession);
|
||||
UpdateStageParameters();
|
||||
}
|
||||
|
||||
VRHMDSensorState
|
||||
|
@ -440,6 +485,7 @@ VRDisplayOculus::GetSensorState()
|
|||
result = GetSensorState(frameDelta);
|
||||
result.inputFrameID = mInputFrameID;
|
||||
mLastSensorState[result.inputFrameID % kMaxLatencyFrames] = result;
|
||||
result.position[1] -= mEyeHeight;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -988,6 +1034,11 @@ VRSystemManagerOculus::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
|
|||
ovrResult orv = ovr_Create(&session, &luid);
|
||||
if (orv == ovrSuccess) {
|
||||
mSession = session;
|
||||
orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
|
||||
if (orv != ovrSuccess) {
|
||||
NS_WARNING("ovr_SetTrackingOriginType failed.\n");
|
||||
}
|
||||
|
||||
mHMDInfo = new VRDisplayOculus(session);
|
||||
}
|
||||
}
|
||||
|
@ -1099,6 +1150,9 @@ VRSystemManagerOculus::HandleInput()
|
|||
poseState.linearAcceleration[0] = pose.LinearAcceleration.x;
|
||||
poseState.linearAcceleration[1] = pose.LinearAcceleration.y;
|
||||
poseState.linearAcceleration[2] = pose.LinearAcceleration.z;
|
||||
|
||||
float eyeHeight = ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
|
||||
poseState.position[1] -= eyeHeight;
|
||||
}
|
||||
HandlePoseTracking(i, poseState, controller);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ protected:
|
|||
const VRHMDSensorState& aSensorState,
|
||||
const gfx::Rect& aLeftEyeRect,
|
||||
const gfx::Rect& aRightEyeRect) override;
|
||||
void UpdateStageParameters();
|
||||
|
||||
public:
|
||||
explicit VRDisplayOculus(ovrSession aSession);
|
||||
|
@ -82,7 +83,8 @@ protected:
|
|||
RefPtr<ID3D11InputLayout> mInputLayout;
|
||||
|
||||
bool mIsPresenting;
|
||||
|
||||
float mEyeHeight;
|
||||
|
||||
bool UpdateConstantBuffers();
|
||||
|
||||
struct Vertex
|
||||
|
|
|
@ -45,10 +45,6 @@ parent:
|
|||
// asynchronously to children via UpdateDisplayInfo.
|
||||
async RefreshDisplays();
|
||||
|
||||
// GetDisplays synchronously returns the VR displays that have already been
|
||||
// enumerated by RefreshDisplays() but does not enumerate new ones.
|
||||
sync GetDisplays() returns(VRDisplayInfo[] aDisplayInfo);
|
||||
|
||||
// Reset the sensor of the display identified by aDisplayID so that the current
|
||||
// sensor state is the "Zero" position.
|
||||
async ResetSensor(uint32_t aDisplayID);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче