Merge m-c to fx-team

This commit is contained in:
Wes Kocher 2014-02-07 17:32:00 -08:00
commit 376c9c8896
94 changed files with 2613 additions and 1936 deletions

View File

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>

View File

@ -11,7 +11,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/>

View File

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>

View File

@ -1,4 +1,4 @@
{
"revision": "e11cf4c78874de3469c4f338cf275aeae31854ee",
"revision": "aa1e2bec7aab191896988cee73e8bfb332b894cf",
"repo_path": "/integration/gaia-central"
}

View File

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -10,7 +10,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -11,7 +11,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/>

View File

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="260effa7f342db418b8d5a95dc61c6e8dd8d09f4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d15809c5db9f57869d2119aebf7c5bfb1908cf49"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -5,7 +5,7 @@
malformedURI=The URL is not valid and cannot be loaded.
fileNotFound=Firefox can't find the file at %S.
dnsNotFound=Firefox can't find the server at %S.
protocolNotFound=Firefox doesn't know how to open this address, because the protocol (%S) isn't associated with any program.
unknownProtocolFound=Firefox doesn't know how to open this address, because one of the following protocols (%S) isn't associated with any program or is not allowed in this context.
connectionFailure=Firefox can't establish a connection to the server at %S.
netInterrupt=The connection to %S was interrupted while the page was loading.
netTimeout=The server at %S is taking too long to respond.

View File

@ -479,7 +479,7 @@ pref("browser.bookmarks.autoExportHTML", false);
// keep in {PROFILEDIR}/bookmarkbackups. Special values:
// -1: unlimited
// 0: no backups created (and deletes all existing backups)
pref("browser.bookmarks.max_backups", 10);
pref("browser.bookmarks.max_backups", 15);
// Scripts & Windows prefs
pref("dom.disable_open_during_load", true);

View File

@ -82,16 +82,20 @@ XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
"resource:///modules/BrowserUITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
"resource:///modules/AsyncShutdown.jsm");
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
// We try to backup bookmarks at idle times, to avoid doing that at shutdown.
// Number of idle seconds before trying to backup bookmarks. 10 minutes.
const BOOKMARKS_BACKUP_IDLE_TIME = 10 * 60;
// Minimum interval in milliseconds between backups.
const BOOKMARKS_BACKUP_INTERVAL = 86400 * 1000;
// Maximum number of backups to create. Old ones will be purged.
const BOOKMARKS_BACKUP_MAX_BACKUPS = 10;
// Seconds of idle before trying to create a bookmarks backup.
const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 10 * 60;
// Minimum interval between backups. We try to not create more than one backup
// per interval.
const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
// Maximum interval between backups. If the last backup is older than these
// days we will try to create a new one more aggressively.
const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 5;
// Factory object
const BrowserGlueServiceFactory = {
@ -134,7 +138,6 @@ function BrowserGlue() {
BrowserGlue.prototype = {
_saveSession: false,
_isIdleObserver: false,
_isPlacesInitObserver: false,
_isPlacesLockedObserver: false,
_isPlacesShutdownObserver: false,
@ -202,16 +205,7 @@ BrowserGlue.prototype = {
this._onQuitRequest(subject, data);
break;
case "quit-application-granted":
// This pref must be set here because SessionStore will use its value
// on quit-application.
this._setPrefToSaveSession();
try {
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
getService(Ci.nsIAppStartup);
appStartup.trackStartupCrashEnd();
} catch (e) {
Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e);
}
this._onQuitApplicationGranted();
break;
#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
case "browser-lastwindow-close-requested":
@ -262,8 +256,7 @@ BrowserGlue.prototype = {
this._onPlacesShutdown();
break;
case "idle":
if (this._idleService.idleTime > BOOKMARKS_BACKUP_IDLE_TIME * 1000)
this._backupBookmarks();
this._backupBookmarks();
break;
case "distribution-customization-complete":
Services.obs.removeObserver(this, "distribution-customization-complete");
@ -304,7 +297,10 @@ BrowserGlue.prototype = {
}
break;
case "profile-before-change":
this._onProfileShutdown();
// Any component depending on Places should be finalized in
// _onPlacesShutdown. Any component that doesn't need to act after
// the UI has gone should be finalized in _onQuitApplicationGranted.
this._dispose();
break;
#ifdef MOZ_SERVICES_HEALTHREPORT
case "keyword-search":
@ -422,8 +418,10 @@ BrowserGlue.prototype = {
os.removeObserver(this, "weave:engine:clients:display-uri");
#endif
os.removeObserver(this, "session-save");
if (this._isIdleObserver)
this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
if (this._bookmarksBackupIdleTime) {
this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
delete this._bookmarksBackupIdleTime;
}
if (this._isPlacesInitObserver)
os.removeObserver(this, "places-init-complete");
if (this._isPlacesLockedObserver)
@ -634,16 +632,24 @@ BrowserGlue.prototype = {
},
/**
* Profile shutdown handler (contains profile cleanup routines).
* All components depending on Places should be shut down in
* _onPlacesShutdown() and not here.
* Application shutdown handler.
*/
_onProfileShutdown: function BG__onProfileShutdown() {
_onQuitApplicationGranted: function () {
// This pref must be set here because SessionStore will use its value
// on quit-application.
this._setPrefToSaveSession();
try {
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
.getService(Ci.nsIAppStartup);
appStartup.trackStartupCrashEnd();
} catch (e) {
Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e);
}
BrowserNewTabPreloader.uninit();
webappsUI.uninit();
SignInToWebsiteUX.uninit();
webrtcUI.uninit();
this._dispose();
},
// All initial windows have opened.
@ -1060,14 +1066,18 @@ BrowserGlue.prototype = {
}
} catch(ex) {}
// This may be reused later, check for "=== undefined" to see if it has
// been populated already.
let lastBackupFile;
// If the user did not require to restore default bookmarks, or import
// from bookmarks.html, we will try to restore from JSON
if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
// get latest JSON backup
var bookmarksBackupFile = yield PlacesBackups.getMostRecent("json");
if (bookmarksBackupFile) {
lastBackupFile = yield PlacesBackups.getMostRecentBackup("json");
if (lastBackupFile) {
// restore from JSON backup
yield BookmarkJSONUtils.importFromFile(bookmarksBackupFile, true);
yield BookmarkJSONUtils.importFromFile(lastBackupFile, true);
importBookmarks = false;
}
else {
@ -1162,10 +1172,39 @@ BrowserGlue.prototype = {
}
// Initialize bookmark archiving on idle.
// Once a day, either on idle or shutdown, bookmarks are backed up.
if (!this._isIdleObserver) {
this._idleService.addIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
this._isIdleObserver = true;
if (!this._bookmarksBackupIdleTime) {
this._bookmarksBackupIdleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC;
// If there is no backup, or the last bookmarks backup is too old, use
// a more aggressive idle observer.
if (lastBackupFile === undefined)
lastBackupFile = yield PlacesBackups.getMostRecentBackup();
if (!lastBackupFile) {
this._bookmarksBackupIdleTime /= 2;
}
else {
let lastBackupTime = PlacesBackups.getDateForFile(lastBackupFile);
let profileLastUse = Services.appinfo.replacedLockTime || Date.now();
// If there is a backup after the last profile usage date it's fine,
// regardless its age. Otherwise check how old is the last
// available backup compared to that session.
if (profileLastUse > lastBackupTime) {
let backupAge = Math.round((profileLastUse - lastBackupTime) / 86400000);
// Report the age of the last available backup.
try {
Services.telemetry
.getHistogramById("PLACES_BACKUPS_DAYSFROMLAST")
.add(backupAge);
} catch (ex) {
Components.utils.reportError("Unable to report telemetry.");
}
if (backupAge > BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS)
this._bookmarksBackupIdleTime /= 2;
}
}
this._idleService.addIdleObserver(this, this._bookmarksBackupIdleTime);
}
Services.obs.notifyObservers(null, "places-browser-init-complete", "");
@ -1174,63 +1213,34 @@ BrowserGlue.prototype = {
/**
* Places shut-down tasks
* - back up bookmarks if needed.
* - export bookmarks as HTML, if so configured.
* - finalize components depending on Places.
* - export bookmarks as HTML, if so configured.
*/
_onPlacesShutdown: function BG__onPlacesShutdown() {
this._sanitizer.onShutdown();
PageThumbs.uninit();
if (this._isIdleObserver) {
this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
this._isIdleObserver = false;
if (this._bookmarksBackupIdleTime) {
this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
delete this._bookmarksBackupIdleTime;
}
let waitingForBackupToComplete = true;
this._backupBookmarks().then(
function onSuccess() {
waitingForBackupToComplete = false;
},
function onFailure() {
Cu.reportError("Unable to backup bookmarks.");
waitingForBackupToComplete = false;
// Support legacy bookmarks.html format for apps that depend on that format.
try {
if (Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML")) {
// places-shutdown happens at profile-change-teardown, so here we
// can safely add a profile-before-change blocker.
AsyncShutdown.profileBeforeChange.addBlocker(
"Places: bookmarks.html",
() => BookmarkHTMLUtils.exportToFile(Services.dirsvc.get("BMarks", Ci.nsIFile))
.then(null, Cu.reportError)
);
}
);
// Backup bookmarks to bookmarks.html to support apps that depend
// on the legacy format.
let waitingForHTMLExportToComplete = false;
// If this fails to get the preference value, we don't export.
if (Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML")) {
// Exceptionally, since this is a non-default setting and HTML format is
// discouraged in favor of the JSON backups, we spin the event loop on
// shutdown, to wait for the export to finish. We cannot safely spin
// the event loop on shutdown until we include a watchdog to prevent
// potential hangs (bug 518683). The asynchronous shutdown operations
// will then be handled by a shutdown service (bug 435058).
waitingForHTMLExportToComplete = true;
BookmarkHTMLUtils.exportToFile(Services.dirsvc.get("BMarks", Ci.nsIFile)).then(
function onSuccess() {
waitingForHTMLExportToComplete = false;
},
function onFailure() {
Cu.reportError("Unable to auto export html.");
waitingForHTMLExportToComplete = false;
}
);
}
// The events loop should spin at least once because waitingForBackupToComplete
// is true before checking whether backup should be made.
let thread = Services.tm.currentThread;
while (waitingForBackupToComplete || waitingForHTMLExportToComplete) {
thread.processNextEvent(true);
}
} catch (ex) {} // Do not export.
},
/**
* Backup bookmarks.
* If a backup for today doesn't exist, this creates one.
*/
_backupBookmarks: function BG__backupBookmarks() {
return Task.spawn(function() {
@ -1238,14 +1248,9 @@ BrowserGlue.prototype = {
// Should backup bookmarks if there are no backups or the maximum
// interval between backups elapsed.
if (!lastBackupFile ||
new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_INTERVAL) {
let maxBackups = BOOKMARKS_BACKUP_MAX_BACKUPS;
try {
maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups");
}
catch(ex) { /* Use default. */ }
yield PlacesBackups.create(maxBackups); // Don't force creation.
new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000) {
let maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups");
yield PlacesBackups.create(maxBackups);
}
});
},

View File

@ -540,7 +540,8 @@ var PlacesOrganizer = {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
let fpCallback = function fpCallback_done(aResult) {
if (aResult != Ci.nsIFilePicker.returnCancel) {
PlacesBackups.saveBookmarksToJSONFile(fp.file);
// There is no OS.File version of the filepicker yet (Bug 937812).
PlacesBackups.saveBookmarksToJSONFile(fp.file.path);
}
};

View File

@ -4,91 +4,78 @@
* 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/. */
// Get bookmarks service
try {
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
} catch(ex) {
do_throw("Could not get Bookmarks service\n");
}
// Get annotation service
try {
var annosvc = Cc["@mozilla.org/browser/annotation-service;1"].
getService(Ci.nsIAnnotationService);
} catch(ex) {
do_throw("Could not get Annotation service\n");
}
// Get browser glue
try {
var gluesvc = Cc["@mozilla.org/browser/browserglue;1"].
getService(Ci.nsIBrowserGlue).
QueryInterface(Ci.nsIObserver);
// Avoid default bookmarks import.
gluesvc.observe(null, "initial-migration-will-import-default-bookmarks", "");
// gluesvc.observe(null, "initial-migration-did-import-default-bookmarks", "");
} catch(ex) {
do_throw("Could not get BrowserGlue service\n");
}
// Get pref service
try {
var pref = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
} catch(ex) {
do_throw("Could not get Preferences service\n");
}
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
// main
let gluesvc = Cc["@mozilla.org/browser/browserglue;1"].
getService(Ci.nsIBrowserGlue).
QueryInterface(Ci.nsIObserver);
// Avoid default bookmarks import.
gluesvc.observe(null, "initial-migration-will-import-default-bookmarks", "");
function run_test() {
// TEST 1: smart bookmarks disabled
pref.setIntPref("browser.places.smartBookmarksVersion", -1);
gluesvc.ensurePlacesDefaultQueriesInitialized();
var smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, 0);
// check that pref has not been bumped up
do_check_eq(pref.getIntPref("browser.places.smartBookmarksVersion"), -1);
// TEST 2: create smart bookmarks
pref.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_neq(smartBookmarkItemIds.length, 0);
// check that pref has been bumped up
do_check_true(pref.getIntPref("browser.places.smartBookmarksVersion") > 0);
var smartBookmarksCount = smartBookmarkItemIds.length;
// TEST 3: smart bookmarks restore
// remove one smart bookmark and restore
bmsvc.removeItem(smartBookmarkItemIds[0]);
pref.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
// check that pref has been bumped up
do_check_true(pref.getIntPref("browser.places.smartBookmarksVersion") > 0);
// TEST 4: move a smart bookmark, change its title, then restore
// smart bookmark should be restored in place
var parent = bmsvc.getFolderIdForItem(smartBookmarkItemIds[0]);
var oldTitle = bmsvc.getItemTitle(smartBookmarkItemIds[0]);
// create a subfolder and move inside it
var newParent = bmsvc.createFolder(parent, "test", bmsvc.DEFAULT_INDEX);
bmsvc.moveItem(smartBookmarkItemIds[0], newParent, bmsvc.DEFAULT_INDEX);
// change title
bmsvc.setItemTitle(smartBookmarkItemIds[0], "new title");
// restore
pref.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
do_check_eq(bmsvc.getFolderIdForItem(smartBookmarkItemIds[0]), newParent);
do_check_eq(bmsvc.getItemTitle(smartBookmarkItemIds[0]), oldTitle);
// check that pref has been bumped up
do_check_true(pref.getIntPref("browser.places.smartBookmarksVersion") > 0);
run_next_test();
}
add_task(function smart_bookmarks_disabled() {
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
gluesvc.ensurePlacesDefaultQueriesInitialized();
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, 0);
do_log_info("check that pref has not been bumped up");
do_check_eq(Services.prefs.getIntPref("browser.places.smartBookmarksVersion"), -1);
});
add_task(function create_smart_bookmarks() {
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_neq(smartBookmarkItemIds.length, 0);
do_log_info("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});
add_task(function remove_smart_bookmark_and_restore() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
let smartBookmarksCount = smartBookmarkItemIds.length;
do_log_info("remove one smart bookmark and restore");
PlacesUtils.bookmarks.removeItem(smartBookmarkItemIds[0]);
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
do_log_info("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});
add_task(function move_smart_bookmark_rename_and_restore() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
let smartBookmarksCount = smartBookmarkItemIds.length;
do_log_info("smart bookmark should be restored in place");
let parent = PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]);
let oldTitle = PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]);
// create a subfolder and move inside it
let newParent =
PlacesUtils.bookmarks.createFolder(parent, "test",
PlacesUtils.bookmarks.DEFAULT_INDEX);
PlacesUtils.bookmarks.moveItem(smartBookmarkItemIds[0], newParent,
PlacesUtils.bookmarks.DEFAULT_INDEX);
// change title
PlacesUtils.bookmarks.setItemTitle(smartBookmarkItemIds[0], "new title");
// restore
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]), newParent);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]), oldTitle);
do_log_info("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});

View File

@ -1,153 +0,0 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
/**
* Tests that nsBrowserGlue is correctly exporting based on preferences values,
* and creating bookmarks backup if one does not exist for today.
*/
// Initialize nsBrowserGlue after Places.
let bg = Cc["@mozilla.org/browser/browserglue;1"].
getService(Ci.nsIBrowserGlue);
// Initialize Places through Bookmarks Service.
let bs = PlacesUtils.bookmarks;
// Get other services.
let ps = Services.prefs;
let os = Services.obs;
const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
let tests = [];
//------------------------------------------------------------------------------
tests.push({
description: "Export to bookmarks.html if autoExportHTML is true.",
exec: function() {
remove_all_JSON_backups();
// Sanity check: we should have bookmarks on the toolbar.
do_check_true(bs.getIdForItemAt(bs.toolbarFolder, 0) > 0);
// Set preferences.
ps.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
// Force nsBrowserGlue::_shutdownPlaces().
bg.QueryInterface(Ci.nsIObserver).observe(null,
PlacesUtils.TOPIC_SHUTDOWN,
null);
// Check bookmarks.html has been created.
check_bookmarks_html();
// Check JSON backup has been created.
check_JSON_backup(true);
// Check preferences have not been reverted.
do_check_true(ps.getBoolPref(PREF_AUTO_EXPORT_HTML));
// Reset preferences.
ps.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
next_test();
}
});
//------------------------------------------------------------------------------
tests.push({
description: "Export to bookmarks.html if autoExportHTML is true and a bookmarks.html exists.",
exec: function() {
// Sanity check: we should have bookmarks on the toolbar.
do_check_true(bs.getIdForItemAt(bs.toolbarFolder, 0) > 0);
// Set preferences.
ps.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
// Create a bookmarks.html in the profile.
let profileBookmarksHTMLFile = create_bookmarks_html("bookmarks.glue.html");
// set the file's lastModifiedTime to one minute ago and get its size.
let lastMod = Date.now() - 60*1000;
profileBookmarksHTMLFile.lastModifiedTime = lastMod;
let fileSize = profileBookmarksHTMLFile.fileSize;
// Force nsBrowserGlue::_shutdownPlaces().
bg.QueryInterface(Ci.nsIObserver).observe(null,
PlacesUtils.TOPIC_SHUTDOWN,
null);
// Check a new bookmarks.html has been created.
let profileBookmarksHTMLFile = check_bookmarks_html();
do_check_true(profileBookmarksHTMLFile.lastModifiedTime > lastMod);
do_check_neq(profileBookmarksHTMLFile.fileSize, fileSize);
// Check preferences have not been reverted.
do_check_true(ps.getBoolPref(PREF_AUTO_EXPORT_HTML));
// Reset preferences.
ps.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
next_test();
}
});
//------------------------------------------------------------------------------
tests.push({
description: "Backup to JSON should be a no-op if a backup for today already exists.",
exec: function() {
// Sanity check: we should have bookmarks on the toolbar.
do_check_true(bs.getIdForItemAt(bs.toolbarFolder, 0) > 0);
// Create a JSON backup in the profile.
let profileBookmarksJSONFile = create_JSON_backup("bookmarks.glue.json");
// Get file lastModified and size.
let lastMod = profileBookmarksJSONFile.lastModifiedTime;
let fileSize = profileBookmarksJSONFile.fileSize;
// Force nsBrowserGlue::_shutdownPlaces().
bg.QueryInterface(Ci.nsIObserver).observe(null,
PlacesUtils.TOPIC_SHUTDOWN,
null);
// Check a new JSON backup has not been created.
do_check_true(profileBookmarksJSONFile.exists());
do_check_eq(profileBookmarksJSONFile.lastModifiedTime, lastMod);
do_check_eq(profileBookmarksJSONFile.fileSize, fileSize);
do_test_finished();
}
});
//------------------------------------------------------------------------------
var testIndex = 0;
function next_test() {
// Remove bookmarks.html from profile.
remove_bookmarks_html();
// Execute next test.
let test = tests.shift();
dump("\nTEST " + (++testIndex) + ": " + test.description);
test.exec();
}
function run_test() {
do_test_pending();
// Clean up bookmarks.
remove_all_bookmarks();
// Create some bookmarks.
bs.insertBookmark(bs.bookmarksMenuFolder, uri("http://mozilla.org/"),
bs.DEFAULT_INDEX, "bookmark-on-menu");
bs.insertBookmark(bs.toolbarFolder, uri("http://mozilla.org/"),
bs.DEFAULT_INDEX, "bookmark-on-toolbar");
// Kick-off tests.
next_test();
}

View File

@ -19,8 +19,8 @@ const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
let EXPECTED_NOTIFICATIONS = [
"places-shutdown"
, "places-expiration-finished"
, "places-will-close-connection"
, "places-expiration-finished"
, "places-connection-closed"
];

View File

@ -16,7 +16,6 @@ support-files =
[test_browserGlue_migrate.js]
[test_browserGlue_prefs.js]
[test_browserGlue_restore.js]
[test_browserGlue_shutdown.js]
[test_browserGlue_smartBookmarks.js]
[test_clearHistory_shutdown.js]
[test_leftpane_corruption_handling.js]

View File

@ -5,7 +5,7 @@
malformedURI=The URL is not valid and cannot be loaded.
fileNotFound=Firefox can't find the file at %S.
dnsNotFound=Firefox can't find the server at %S.
protocolNotFound=Firefox doesn't know how to open this address, because the protocol (%S) isn't associated with any program.
unknownProtocolFound=Firefox doesn't know how to open this address, because one of the following protocols (%S) isn't associated with any program or is not allowed in this context.
connectionFailure=Firefox can't establish a connection to the server at %S.
netInterrupt=The connection to %S was interrupted while the page was loading.
netTimeout=The server at %S is taking too long to respond.

View File

@ -86,8 +86,8 @@
<!ENTITY netTimeout.title "The connection has timed out">
<!ENTITY netTimeout.longDesc "&sharedLongDesc;">
<!ENTITY protocolNotFound.title "The address wasn't understood">
<!ENTITY protocolNotFound.longDesc "
<!ENTITY unknownProtocolFound.title "The address wasn't understood">
<!ENTITY unknownProtocolFound.longDesc "
<ul>
<li>You might need to install other software to open this address.</li>
</ul>

File diff suppressed because it is too large Load Diff

View File

@ -275,7 +275,7 @@
<h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
<h1 id="et_fileNotFound">&fileNotFound.title;</h1>
<h1 id="et_malformedURI">&malformedURI.title;</h1>
<h1 id="et_protocolNotFound">&protocolNotFound.title;</h1>
<h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
<h1 id="et_connectionFailure">&connectionFailure.title;</h1>
<h1 id="et_netTimeout">&netTimeout.title;</h1>
<h1 id="et_redirectLoop">&redirectLoop.title;</h1>
@ -300,7 +300,7 @@
<div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
<div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
<div id="ed_malformedURI">&malformedURI.longDesc;</div>
<div id="ed_protocolNotFound">&protocolNotFound.longDesc;</div>
<div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
<div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
<div id="ed_netTimeout">&netTimeout.longDesc;</div>
<div id="ed_redirectLoop">&redirectLoop.longDesc;</div>

View File

@ -15,9 +15,6 @@
<tt> aa_block: /* innermost frame denies a */</tt><br/>
<iframe id='aa_block'></iframe><br/>
<tt> aa2_block: /* innermost frame (view-source: URL) denies a */</tt><br/>
<iframe id='aa2_block'></iframe><br/>
<tt> ab_allow: /* innermost frame allows a */</tt><br/>
<iframe id='ab_allow'></iframe><br/>

View File

@ -22,10 +22,6 @@ function setupFrames() {
elt.src = base.a + "?testid=aa_block&internalframe=aa_b&csp=" +
escape("allow 'none'; frame-ancestors 'none'; script-src 'self'");
elt = $('aa2_block');
elt.src = "view-source:" + base.a + "?testid=aa2_block&internalframe=aa_b&csp=" +
escape("allow 'none'; frame-ancestors 'none'; script-src 'self'");
elt = $('ab_allow');
elt.src = base.b + "?testid=ab_allow&internalframe=ab_a&csp=" +
escape("allow 'none'; frame-ancestors " + host.a + "; script-src 'self'");

View File

@ -15,9 +15,6 @@
<tt> aa_block: /* innermost frame denies a */</tt><br/>
<iframe id='aa_block_spec_compliant'></iframe><br/>
<tt> aa2_block: /* innermost frame (view-source: URL) denies a */</tt><br/>
<iframe id='aa2_block_spec_compliant'></iframe><br/>
<tt> ab_allow: /* innermost frame allows a */</tt><br/>
<iframe id='ab_allow_spec_compliant'></iframe><br/>

View File

@ -22,10 +22,6 @@ function setupFrames() {
elt.src = base.a + "?testid=aa_block_spec_compliant&internalframe=aa_b&csp=" +
escape("default-src 'none'; frame-ancestors 'none'; script-src 'self'");
elt = $('aa2_block_spec_compliant');
elt.src = "view-source:" + base.a + "?testid=aa2_block_spec_compliant&internalframe=aa_b&csp=" +
escape("default-src 'none'; frame-ancestors 'none'; script-src 'self'");
elt = $('ab_allow_spec_compliant');
elt.src = base.b + "?testid=ab_allow_spec_compliant&internalframe=ab_a&csp=" +
escape("default-src 'none'; frame-ancestors " + host.a + "; script-src 'self'");

View File

@ -20,7 +20,6 @@ var path = "/tests/content/base/test/csp/";
var framesThatShouldLoad = {
aa_allow: -1, /* innermost frame allows a */
//aa_block: -1, /* innermost frame denies a */
//aa2_block: -1, /* innermost frame denies a */
ab_allow: -1, /* innermost frame allows a */
//ab_block: -1, /* innermost frame denies a */
aba_allow: -1, /* innermost frame allows b,a */
@ -31,7 +30,6 @@ var framesThatShouldLoad = {
//abb2_block: -1, /* innermost frame denies a */
aa_allow_spec_compliant: -1, /* innermost frame allows a *
//aa_block_spec_compliant: -1, /* innermost frame denies a */
//aa2_block_spec_compliant: -1, /* innermost frame denies a */
ab_allow_spec_compliant: -1, /* innermost frame allows a */
//ab_block_spec_compliant: -1, /* innermost frame denies a */
aba_allow_spec_compliant: -1, /* innermost frame allows b,a */
@ -42,7 +40,7 @@ var framesThatShouldLoad = {
//abb2_block_spec_compliant: -1, /* innermost frame denies a */
};
var expectedViolationsLeft = 14;
var expectedViolationsLeft = 12;
// This is used to watch the blocked data bounce off CSP and allowed data
// get sent out to the wire.

View File

@ -4386,12 +4386,25 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
// Turn the error code into a human readable error message.
if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
NS_ENSURE_ARG_POINTER(aURI);
// extract the scheme
// Extract the schemes into a comma delimited list.
nsAutoCString scheme;
aURI->GetScheme(scheme);
CopyASCIItoUTF16(scheme, formatStrs[0]);
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
while (nestedURI) {
nsCOMPtr<nsIURI> tempURI;
nsresult rv2;
rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
if (NS_SUCCEEDED(rv2) && tempURI) {
tempURI->GetScheme(scheme);
formatStrs[0].Append(NS_LITERAL_STRING(", "));
AppendASCIItoUTF16(scheme, formatStrs[0]);
}
nestedURI = do_QueryInterface(tempURI);
}
formatStrCount = 1;
error.AssignLiteral("protocolNotFound");
error.AssignLiteral("unknownProtocolFound");
}
else if (NS_ERROR_FILE_NOT_FOUND == aError) {
NS_ENSURE_ARG_POINTER(aURI);
@ -9682,6 +9695,25 @@ nsDocShell::DoURILoad(nsIURI * aURI,
channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
}
}
// Only allow view-source scheme in top-level docshells. view-source is
// the only scheme to which this applies at the moment due to potential
// timing attacks to read data from cross-origin iframes. If this widens
// we should add a protocol flag for whether the scheme is allowed in
// frames and use something like nsNetUtil::NS_URIChainHasFlags.
nsCOMPtr<nsIURI> tempURI = aURI;
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
while (nestedURI) {
// view-source should always be an nsINestedURI, loop and check the
// scheme on this and all inner URIs that are also nested URIs.
bool isViewSource = false;
rv = tempURI->SchemeIs("view-source", &isViewSource);
if (NS_FAILED(rv) || isViewSource) {
return NS_ERROR_UNKNOWN_PROTOCOL;
}
nestedURI->GetInnerURI(getter_AddRefs(tempURI));
nestedURI = do_QueryInterface(tempURI);
}
}
// open a channel for the url

View File

@ -274,7 +274,7 @@
<h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
<h1 id="et_fileNotFound">&fileNotFound.title;</h1>
<h1 id="et_malformedURI">&malformedURI.title;</h1>
<h1 id="et_protocolNotFound">&protocolNotFound.title;</h1>
<h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
<h1 id="et_connectionFailure">&connectionFailure.title;</h1>
<h1 id="et_netTimeout">&netTimeout.title;</h1>
<h1 id="et_redirectLoop">&redirectLoop.title;</h1>
@ -300,7 +300,7 @@
<div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
<div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
<div id="ed_malformedURI">&malformedURI.longDesc;</div>
<div id="ed_protocolNotFound">&protocolNotFound.longDesc;</div>
<div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
<div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
<div id="ed_netTimeout">&netTimeout.longDesc;</div>
<div id="ed_redirectLoop">&redirectLoop.longDesc;</div>

View File

@ -42,6 +42,7 @@ support-files =
bug909218.js
bug92598_window.xul
docshell_helpers.js
file_viewsource_forbidden_in_iframe.html
generic.html
mozFrameType_window.xul
@ -78,3 +79,4 @@ support-files =
[test_mozFrameType.xul]
[test_principalInherit.xul]
[test_private_hidden_window.html]
[test_viewsource_forbidden_in_iframe.xul]

View File

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test ifranes for view-source forbidden in iframe tests</title>
</head>
<body>
<iframe id="testIframe"></iframe>
<iframe id="refIframe"></iframe>
</body>
</html>

View File

@ -0,0 +1,180 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=624883
-->
<window title="Mozilla Bug 624883"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=624883"
target="_blank">Mozilla Bug 624883</a>
</body>
<!-- test code goes here -->
<iframe type="content" onload="startTest()" src="file_viewsource_forbidden_in_iframe.html"></iframe>
<script type="application/javascript">
<![CDATA[
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
SimpleTest.waitForExplicitFinish();
// We create a promise that will resolve with the error message
// on a network error page load and reject on any other load.
function createNetworkErrorMessagePromise(frame) {
return new Promise(function(resolve, reject) {
// Error pages do not fire "load" events, so use a progressListener.
var originalDocumentURI = frame.contentDocument.documentURI;
var progressListener = {
onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
// Make sure nothing other than an error page is loaded.
if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) {
reject("location change was not to an error page");
}
},
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
// Wait until the documentURI changes (from about:blank) this should
// be the error page URI.
var documentURI = frame.contentDocument.documentURI;
if (documentURI == originalDocumentURI) {
return;
}
aWebProgress.removeProgressListener(progressListener,
Ci.nsIWebProgress.NOTIFY_ALL);
var matchArray = /about:neterror\?.*&d=([^&]*)/.exec(documentURI);
if (!matchArray) {
reject("no network error message found in URI")
return;
}
var errorMsg = matchArray[1];
resolve(decodeURIComponent(errorMsg));
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress)
.addProgressListener(progressListener,
Ci.nsIWebProgress.NOTIFY_LOCATION |
Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
});
}
function startTest() {
// Get a reference message that we know will be an unknown protocol message,
// so we can use it for comparisons in the test cases.
var refIframe = window[0].document.getElementById("refIframe");
var refErrorPromise = createNetworkErrorMessagePromise(refIframe);
refErrorPromise.then(
function(msg) {
window.refErrorMsg = msg;
var testIframe = window[0].document.getElementById("testIframe");
// Run test cases on load of "about:blank", so that the URI always changes
// and we can detect this in our Promise.
testIframe.onload = runNextTestCase;
testIframe.src = "about:blank";
},
function(reason) {
ok(false, "Could not get reference error message", reason);
SimpleTest.finish();
})
.catch(function(e) {
ok(false, "Unexpected exception thrown getting reference error message", exception);
});
refIframe.src = "wibble://example.com";
}
function runTestCase(testCase) {
var testIframe = window[0].document.getElementById("testIframe");
var expectedErrorMsg = window.refErrorMsg.replace("wibble", testCase.expectedProtocolList);
var testErrorPromise = createNetworkErrorMessagePromise(testIframe);
testErrorPromise.then(
function(actualErrorMsg) {
is(actualErrorMsg, expectedErrorMsg, testCase.desc);
testIframe.src = "about:blank";
},
function(reason) {
ok(false, testCase.desc, reason);
testIframe.src = "about:blank";
})
.catch(function(e) {
ok(false, testCase.desc + " - unexpected exception thrown", exception);
});
testIframe.src = testCase.protocols + "://example.com/!/";
}
var testCaseIndex = -1;
testCases = [
{
desc: "Test 1: view-source should not be allowed in an iframe",
protocols: "view-source:http",
expectedProtocolList: "view-source, http"
},
{
desc: "Test 2: feed:view-source should not be allowed in an iframe",
protocols: "feed:view-source:http",
expectedProtocolList: "feed, view-source, http"
},
{
desc: "Test 3: jar:view-source should not be allowed in an iframe",
protocols: "jar:view-source:http",
expectedProtocolList: "jar, view-source, http"
},
{
desc: "Test 4: pcast:view-source should not be allowed in an iframe",
protocols: "pcast:view-source:http",
expectedProtocolList: "pcast, view-source, http"
},
{
desc: "Test 5: pcast:feed:view-source should not be allowed in an iframe",
protocols: "pcast:feed:view-source:http",
expectedProtocolList: "pcast, feed, view-source, http"
},
{
desc: "Test 6: if invalid protocol first should report before view-source",
protocols: "wibble:view-source:http",
// Nothing after the invalid protocol gets set as a proper nested URI,
// so the list stops there.
expectedProtocolList: "wibble"
},
{
desc: "Test 7: if view-source first should report before invalid protocol",
protocols: "view-source:wibble:http",
expectedProtocolList: "view-source, wibble"
}
];
function runNextTestCase() {
++testCaseIndex;
if (testCaseIndex == testCases.length) {
SimpleTest.finish();
return;
}
runTestCase(testCases[testCaseIndex]);
}
]]>
</script>
</window>

View File

@ -1926,11 +1926,6 @@ Navigator::HasDataStoreSupport(JSContext* cx, JSObject* aGlobal)
{
JS::Rooted<JSObject*> global(cx, aGlobal);
// DataStore is enabled by default for chrome code.
if (nsContentUtils::IsCallerChrome()) {
return true;
}
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.datastore.enabled", &enabled);

View File

@ -5,7 +5,7 @@
malformedURI=The URL is not valid and cannot be loaded.
fileNotFound=The file %S cannot be found. Please check the location and try again.
dnsNotFound=%S could not be found. Please check the name and try again.
protocolNotFound=%S is not a registered protocol.
unknownProtocolFound=One of the following %S is not a registered protocol or is not allowed in this context.
connectionFailure=The connection was refused when attempting to contact %S.
netInterrupt=The connection to %S has terminated unexpectedly. Some data may have been transferred.
netTimeout=The operation timed out when attempting to contact %S.

View File

@ -50,8 +50,8 @@
<!ENTITY netTimeout.title "Network Timeout">
<!ENTITY netTimeout.longDesc "<p>The requested site did not respond to a connection request and the browser has stopped waiting for a reply.</p><ul><li>Could the server be experiencing high demand or a temporary outage? Try again later.</li><li>Are you unable to browse other sites? Check the computer's network connection.</li><li>Is your computer or network protected by a firewall or proxy? Incorrect settings can interfere with Web browsing.</li><li>Still having trouble? Consult your network administrator or Internet provider for assistance.</li></ul>">
<!ENTITY protocolNotFound.title "Unknown Protocol">
<!ENTITY protocolNotFound.longDesc "<p>The address specifies a protocol (e.g. <q>wxyz://</q>) the browser does not recognize, so the browser cannot properly connect to the site.</p><ul><li>Are you trying to access multimedia or other non-text services? Check the site for extra requirements.</li><li>Some protocols may require third-party software or plugins before the browser can recognize them.</li></ul>">
<!ENTITY unknownProtocolFound.title "Unknown Protocol">
<!ENTITY unknownProtocolFound.longDesc "<p>The address specifies a protocol (e.g. <q>wxyz://</q>) the browser does not recognize, so the browser cannot properly connect to the site.</p><ul><li>Are you trying to access multimedia or other non-text services? Check the site for extra requirements.</li><li>Some protocols may require third-party software or plugins before the browser can recognize them.</li></ul>">
<!ENTITY proxyConnectFailure.title "Proxy Server Refused Connection">
<!ENTITY proxyConnectFailure.longDesc "<p>The browser is configured to use a proxy server, but the proxy refused a connection.</p><ul><li>Is the browser's proxy configuration correct? Check the settings and try again.</li><li>Does the proxy service allow connections from this network?</li><li>Still having trouble? Consult your network administrator or Internet provider for assistance.</li></ul>">

View File

@ -716,6 +716,21 @@ APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStar
next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffChainIndex);
}
bool
APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
{
if (mOverscrollHandoffChain.length() == 0) {
return false;
}
for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
if (item) {
item->FlushRepaintForOverscrollHandoff();
}
}
return true;
}
bool
APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
{

View File

@ -256,6 +256,8 @@ public:
void DispatchScroll(AsyncPanZoomController* aAPZC, ScreenPoint aStartPoint, ScreenPoint aEndPoint,
uint32_t aOverscrollHandoffChainIndex);
bool FlushRepaintsForOverscrollHandoffChain();
protected:
/**
* Debug-build assertion that can be called to ensure code is running on the

View File

@ -575,16 +575,18 @@ nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent)
nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
nsEventStatus rv = nsEventStatus_eIgnore;
nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
if (listener) {
rv = listener->HandleInputEvent(aEvent);
if (rv == nsEventStatus_eConsumeNoDefault)
return rv;
}
switch (aEvent.mInputType) {
case MULTITOUCH_INPUT: {
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
if (listener) {
rv = listener->HandleInputEvent(multiTouchInput);
if (rv == nsEventStatus_eConsumeNoDefault) {
return rv;
}
}
switch (multiTouchInput.mType) {
case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
@ -757,9 +759,17 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
case PANNING_LOCKED_X:
case PANNING_LOCKED_Y:
{
ReentrantMonitorAutoEnter lock(mMonitor);
RequestContentRepaint();
UpdateSharedCompositorFrameMetrics();
// Make a local copy of the tree manager pointer and check if it's not
// null before calling HandleOverscroll(). This is necessary because
// Destroy(), which nulls out mTreeManager, could be called concurrently.
APZCTreeManager* treeManagerLocal = mTreeManager;
if (treeManagerLocal) {
if (!treeManagerLocal->FlushRepaintsForOverscrollHandoffChain()) {
NS_WARNING("Overscroll handoff chain was empty during panning! This should not be the case.");
// Graceful handling of error condition
FlushRepaintForOverscrollHandoff();
}
}
}
mX.EndTouch();
mY.EndTouch();
@ -1389,6 +1399,12 @@ void AsyncPanZoomController::ScheduleComposite() {
}
}
void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
ReentrantMonitorAutoEnter lock(mMonitor);
RequestContentRepaint();
UpdateSharedCompositorFrameMetrics();
}
void AsyncPanZoomController::RequestContentRepaint() {
RequestContentRepaint(mFrameMetrics);
}

View File

@ -333,6 +333,8 @@ public:
*/
bool HasScrollgrab() const { return mFrameMetrics.mHasScrollgrab; }
void FlushRepaintForOverscrollHandoff();
protected:
/**
* Helper method for touches beginning. Sets everything up for panning and any

View File

@ -48,26 +48,20 @@ GestureEventListener::~GestureEventListener()
{
}
nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
{
if (aEvent.mInputType != MULTITOUCH_INPUT) {
return nsEventStatus_eIgnore;
}
const MultiTouchInput& event = static_cast<const MultiTouchInput&>(aEvent);
// Cache the current event since it may become the single or long tap that we
// send.
mLastTouchInput = event;
mLastTouchInput = aEvent;
switch (event.mType)
switch (aEvent.mType)
{
case MultiTouchInput::MULTITOUCH_START:
case MultiTouchInput::MULTITOUCH_ENTER: {
for (size_t i = 0; i < event.mTouches.Length(); i++) {
for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
bool foundAlreadyExistingTouch = false;
for (size_t j = 0; j < mTouches.Length(); j++) {
if (mTouches[j].mIdentifier == event.mTouches[i].mIdentifier) {
if (mTouches[j].mIdentifier == aEvent.mTouches[i].mIdentifier) {
foundAlreadyExistingTouch = true;
break;
}
@ -75,14 +69,14 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
// If we didn't find a touch in our list that matches this, then add it.
if (!foundAlreadyExistingTouch) {
mTouches.AppendElement(event.mTouches[i]);
mTouches.AppendElement(aEvent.mTouches[i]);
}
}
size_t length = mTouches.Length();
if (length == 1) {
mTapStartTime = event.mTime;
mTouchStartPosition = event.mTouches[0].mScreenPoint;
mTapStartTime = aEvent.mTime;
mTouchStartPosition = aEvent.mTouches[0].mScreenPoint;
if (mState == GESTURE_NONE) {
mState = GESTURE_WAITING_SINGLE_TAP;
@ -95,28 +89,28 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
}
} else if (length == 2) {
// Another finger has been added; it can't be a tap anymore.
HandleTapCancel(event);
HandleTapCancel(aEvent);
}
break;
}
case MultiTouchInput::MULTITOUCH_MOVE: {
// If we move too much, bail out of the tap.
ScreenIntPoint delta = event.mTouches[0].mScreenPoint - mTouchStartPosition;
ScreenIntPoint delta = aEvent.mTouches[0].mScreenPoint - mTouchStartPosition;
if (mTouches.Length() == 1 &&
NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance())
{
HandleTapCancel(event);
HandleTapCancel(aEvent);
}
size_t eventTouchesMatched = 0;
for (size_t i = 0; i < mTouches.Length(); i++) {
bool isTouchRemoved = true;
for (size_t j = 0; j < event.mTouches.Length(); j++) {
if (mTouches[i].mIdentifier == event.mTouches[j].mIdentifier) {
for (size_t j = 0; j < aEvent.mTouches.Length(); j++) {
if (mTouches[i].mIdentifier == aEvent.mTouches[j].mIdentifier) {
eventTouchesMatched++;
isTouchRemoved = false;
mTouches[i] = event.mTouches[j];
mTouches[i] = aEvent.mTouches[j];
}
}
if (isTouchRemoved) {
@ -126,16 +120,16 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
}
}
NS_WARN_IF_FALSE(eventTouchesMatched == event.mTouches.Length(), "Touch moved, but not in list");
NS_WARN_IF_FALSE(eventTouchesMatched == aEvent.mTouches.Length(), "Touch moved, but not in list");
break;
}
case MultiTouchInput::MULTITOUCH_END:
case MultiTouchInput::MULTITOUCH_LEAVE: {
for (size_t i = 0; i < event.mTouches.Length(); i++) {
for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
bool foundAlreadyExistingTouch = false;
for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) {
if (event.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
foundAlreadyExistingTouch = true;
mTouches.RemoveElementAt(j);
}
@ -146,30 +140,30 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
if (mState == GESTURE_WAITING_DOUBLE_TAP) {
CancelDoubleTapTimeoutTask();
if (mTapStartTime - mLastTapEndTime > MAX_TAP_TIME ||
event.mTime - mTapStartTime > MAX_TAP_TIME) {
aEvent.mTime - mTapStartTime > MAX_TAP_TIME) {
// Either the time between taps or the last tap took too long
// confirm previous tap and handle current tap seperately
TimeoutDoubleTap();
mState = GESTURE_WAITING_SINGLE_TAP;
} else {
// We were waiting for a double tap and it has arrived.
HandleDoubleTap(event);
HandleDoubleTap(aEvent);
mState = GESTURE_NONE;
}
}
if (mState == GESTURE_LONG_TAP_UP) {
HandleLongTapUpEvent(event);
HandleLongTapUpEvent(aEvent);
mState = GESTURE_NONE;
} else if (mState == GESTURE_WAITING_SINGLE_TAP &&
event.mTime - mTapStartTime > MAX_TAP_TIME) {
aEvent.mTime - mTapStartTime > MAX_TAP_TIME) {
// Extended taps are immediately dispatched as single taps
CancelLongTapTimeoutTask();
HandleSingleTapConfirmedEvent(event);
HandleSingleTapConfirmedEvent(aEvent);
mState = GESTURE_NONE;
} else if (mState == GESTURE_WAITING_SINGLE_TAP) {
CancelLongTapTimeoutTask();
nsEventStatus tapupEvent = HandleSingleTapUpEvent(event);
nsEventStatus tapupEvent = HandleSingleTapUpEvent(aEvent);
if (tapupEvent == nsEventStatus_eIgnore) {
// We were not waiting for anything but a single tap has happened that
@ -190,7 +184,7 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
}
}
mLastTapEndTime = event.mTime;
mLastTapEndTime = aEvent.mTime;
if (!mTouches.Length()) {
mSpanChange = 0.0f;
@ -199,20 +193,24 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
break;
}
case MultiTouchInput::MULTITOUCH_CANCEL:
// This gets called if there's a touch that has to bail for weird reasons
// like pinching and then moving away from the window that the pinch was
// started in without letting go of the screen.
return HandlePinchGestureEvent(event, true);
// FIXME: we should probably clear a bunch of gesture state here
break;
}
return HandlePinchGestureEvent(event, false);
return HandlePinchGestureEvent(aEvent);
}
nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInput& aEvent, bool aClearTouches)
nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInput& aEvent)
{
nsEventStatus rv = nsEventStatus_eIgnore;
if (mTouches.Length() > 1 && !aClearTouches) {
if (aEvent.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
mTouches.Clear();
mState = GESTURE_NONE;
return rv;
}
if (mTouches.Length() > 1) {
const ScreenIntPoint& firstTouch = mTouches[0].mScreenPoint,
secondTouch = mTouches[1].mScreenPoint;
ScreenPoint focusPoint = ScreenPoint(firstTouch + secondTouch) / 2;
@ -289,10 +287,6 @@ nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInpu
mState = GESTURE_NONE;
}
if (aClearTouches) {
mTouches.Clear();
}
return rv;
}

View File

@ -54,7 +54,7 @@ public:
* of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
* it gets consumed here and never forwarded along.
*/
nsEventStatus HandleInputEvent(const InputData& aEvent);
nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
/**
* Cancels any currently active gesture. May not properly handle situations
@ -94,11 +94,8 @@ protected:
/**
* Attempts to handle the event as a pinch event. If it is not a pinch event,
* then we simply tell the next consumer to consume the event instead.
*
* |aClearTouches| marks whether or not to terminate any pinch currently
* happening.
*/
nsEventStatus HandlePinchGestureEvent(const MultiTouchInput& aEvent, bool aClearTouches);
nsEventStatus HandlePinchGestureEvent(const MultiTouchInput& aEvent);
/**
* Attempts to handle the event as a single tap event, which highlights links

View File

@ -170,10 +170,10 @@ SetScriptSourceFilename(ExclusiveContext *cx, ScriptSource *ss,
{
if (options.hasIntroductionInfo) {
const char *filename = options.filename() ? options.filename() : "<unknown>";
JS_ASSERT(options.introducer != nullptr);
JS_ASSERT(options.introductionType != nullptr);
if (!ss->setIntroducedFilename(cx, filename, options.introductionLineno,
options.introducer, options.introducerFilename()))
options.introductionType, options.introducerFilename()))
return false;
ss->setIntroductionOffset(options.introductionOffset);

View File

@ -0,0 +1,38 @@
// Check that scripts' introduction types are properly marked.
var g = newGlobal();
var dbg = new Debugger(g);
var log;
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.source.introductionType, 'eval');
};
log = '';
g.eval('debugger;');
assertEq(log, 'd');
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.source.introductionType, 'Function');
};
log = '';
g.Function('debugger;')();
assertEq(log, 'd');
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.source.introductionType, 'GeneratorFunction');
};
log = '';
g.eval('(function*() {})').constructor('debugger;')().next();
assertEq(log, 'd');
dbg.onDebuggerStatement = function (frame) {
log += 'd';
assertEq(frame.script.source.introductionType, undefined);
};
log = '';
g.evaluate('debugger;');
assertEq(log, 'd');

View File

@ -0,0 +1,17 @@
if (getBuildConfiguration().parallelJS) {
var map_toSource_called = false;
var mapPar_toSource_called = false;
Array.prototype.mapPar.toSource = function() {
mapPar_toSource_called = true;
};
Array.prototype.map.toSource = function() {
map_toSource_called = true;
};
try { new Array.prototype.mapPar; } catch (e) {}
try { new Array.prototype.map; } catch (e) {}
assertEq(map_toSource_called, mapPar_toSource_called);
}

View File

@ -236,17 +236,25 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
heap = &bufferVal.toObject().as<ArrayBufferObject>();
if (!IsValidAsmJSHeapLength(heap->byteLength())) {
return LinkFail(cx, JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next valid length is 0x%x",
heap->byteLength(),
RoundUpToNextValidAsmJSHeapLength(heap->byteLength())));
ScopedJSFreePtr<char> msg(
JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
"valid length is 0x%x",
heap->byteLength(),
RoundUpToNextValidAsmJSHeapLength(heap->byteLength())));
return LinkFail(cx, msg.get());
}
// This check is sufficient without considering the size of the loaded datum because heap
// loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
if (heap->byteLength() < module.minHeapLength()) {
return LinkFail(cx, JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the largest constant heap access offset rounded up to the next valid heap size).",
heap->byteLength(), module.minHeapLength()));
ScopedJSFreePtr<char> msg(
JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the"
"largest constant heap access offset rounded up to the next valid "
"heap size).",
heap->byteLength(),
module.minHeapLength()));
return LinkFail(cx, msg.get());
}
if (!ArrayBufferObject::prepareForAsmJS(cx, heap))

View File

@ -285,7 +285,7 @@ CanEnterBaselineJIT(JSContext *cx, HandleScript script, bool osr)
if (script->isCallsiteClone()) {
// Ensure the original function is compiled too, so that bailouts from
// Ion code have a BaselineScript to resume into.
RootedScript original(cx, script->originalFunction()->nonLazyScript());
RootedScript original(cx, script->donorFunction()->nonLazyScript());
JS_ASSERT(original != script);
if (!original->canBaselineCompile())

View File

@ -23,10 +23,13 @@ BEGIN_TEST(testGCStoreBufferRemoval)
CHECK(!js::gc::IsInsideNursery(rt, obj.get()));
JS::RootedObject tenuredObject(cx, obj);
// Hide the horrors herein from the static rooting analysis.
typedef JSObject *ObjectPtr;
// Test removal of store buffer entries added by RelocatablePtr<T>.
{
JSObject *badObject = reinterpret_cast<JSObject*>(1);
JSObject *punnedPtr = nullptr;
ObjectPtr badObject = reinterpret_cast<JSObject*>(1);
ObjectPtr punnedPtr = nullptr;
RelocatablePtrObject* relocPtr =
reinterpret_cast<RelocatablePtrObject*>(&punnedPtr);
new (relocPtr) RelocatablePtrObject;
@ -77,8 +80,8 @@ BEGIN_TEST(testGCStoreBufferRemoval)
// Test removal of store buffer entries added by Heap<T>.
{
JSObject *badObject = reinterpret_cast<JSObject*>(1);
JSObject *punnedPtr = nullptr;
ObjectPtr badObject = reinterpret_cast<JSObject*>(1);
ObjectPtr punnedPtr = nullptr;
Heap<JSObject*>* heapPtr =
reinterpret_cast<Heap<JSObject*>*>(&punnedPtr);
new (heapPtr) Heap<JSObject*>;

View File

@ -4312,7 +4312,7 @@ JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions &rhs)
werrorOption = rhs.werrorOption;
asmJSOption = rhs.asmJSOption;
sourcePolicy = rhs.sourcePolicy;
introducer = rhs.introducer;
introductionType = rhs.introductionType;
introductionLineno = rhs.introductionLineno;
introductionOffset = rhs.introductionOffset;
hasIntroductionInfo = rhs.hasIntroductionInfo;

View File

@ -3478,7 +3478,7 @@ class JS_FRIEND_API(ReadOnlyCompileOptions)
asmJSOption(false),
forceAsync(false),
sourcePolicy(SAVE_SOURCE),
introducer(nullptr),
introductionType(nullptr),
introductionLineno(0),
introductionOffset(0),
hasIntroductionInfo(false)
@ -3521,9 +3521,9 @@ class JS_FRIEND_API(ReadOnlyCompileOptions)
SAVE_SOURCE
} sourcePolicy;
// |introducer| is a statically allocated C string:
// |introductionType| is a statically allocated C string:
// one of "eval", "Function", or "GeneratorFunction".
const char *introducer;
const char *introductionType;
unsigned introductionLineno;
uint32_t introductionOffset;
bool hasIntroductionInfo;
@ -3616,7 +3616,7 @@ class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
{
if (!setIntroducerFilename(cx, introducerFn))
return false;
introducer = intro;
introductionType = intro;
introductionLineno = line;
introductionOffset = offset;
hasIntroductionInfo = true;
@ -3692,7 +3692,7 @@ class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) : public ReadOnlyCompileOpti
unsigned line, uint32_t offset)
{
introducerFilename_ = introducerFn;
introducer = intro;
introductionType = intro;
introductionLineno = line;
introductionOffset = offset;
hasIntroductionInfo = true;

View File

@ -118,8 +118,8 @@ fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValu
/* Callsite clones should never escape to script. */
JSObject &maybeClone = iter.calleev().toObject();
if (maybeClone.is<JSFunction>() && maybeClone.as<JSFunction>().nonLazyScript()->isCallsiteClone())
vp.setObject(*maybeClone.as<JSFunction>().nonLazyScript()->originalFunction());
if (maybeClone.is<JSFunction>())
vp.setObject(*maybeClone.as<JSFunction>().originalFunction());
else
vp.set(iter.calleev());
@ -1486,9 +1486,9 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener
CurrentScriptFileLineOrigin(cx, &script, &filename, &lineno, &pcOffset, &originPrincipals);
JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
const char *introducer = "Function";
const char *introductionType = "Function";
if (generatorKind != NotGenerator)
introducer = "GeneratorFunction";
introductionType = "GeneratorFunction";
const char *introducerFilename = filename;
if (script && script->scriptSource()->introducerFilename())
@ -1500,7 +1500,7 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener
.setFileAndLine(filename, 1)
.setNoScriptRval(false)
.setCompileAndGo(true)
.setIntroductionInfo(introducerFilename, introducer, lineno, pcOffset);
.setIntroductionInfo(introducerFilename, introductionType, lineno, pcOffset);
unsigned n = args.length() ? args.length() - 1 : 0;
if (n > 0) {

View File

@ -339,6 +339,16 @@ class JSFunction : public JSObject
return u.i.s.script_;
}
// Returns non-callsited-clone version of this. Use when return
// value can flow to arbitrary JS (see Bug 944975).
JSFunction* originalFunction() {
if (this->hasScript() && this->nonLazyScript()->isCallsiteClone()) {
return this->nonLazyScript()->donorFunction();
} else {
return this;
}
}
js::HeapPtrScript &mutableScript() {
JS_ASSERT(isInterpreted());
return *(js::HeapPtrScript *)&u.i.s.script_;

View File

@ -1599,12 +1599,12 @@ ScriptSource::displayURL()
bool
ScriptSource::setIntroducedFilename(ExclusiveContext *cx,
const char *callerFilename, unsigned callerLineno,
const char *introducer, const char *introducerFilename)
const char *introductionType, const char *introducerFilename)
{
JS_ASSERT(!filename_);
JS_ASSERT(!introducerFilename_);
introducerType_ = introducer;
introductionType_ = introductionType;
if (introducerFilename) {
introducerFilename_ = js_strdup(cx, introducerFilename);
@ -1612,24 +1612,24 @@ ScriptSource::setIntroducedFilename(ExclusiveContext *cx,
return false;
}
// Final format: "{callerFilename} line {callerLineno} > {introducer}"
// Final format: "{callerFilename} line {callerLineno} > {introductionType}"
// Len = strlen(callerFilename) + strlen(" line ") +
// strlen(toStr(callerLineno)) + strlen(" > ") + strlen(introducer);
// strlen(toStr(callerLineno)) + strlen(" > ") + strlen(introductionType);
char linenoBuf[15];
size_t filenameLen = strlen(callerFilename);
size_t linenoLen = JS_snprintf(linenoBuf, 15, "%u", callerLineno);
size_t introducerLen = strlen(introducer);
size_t introductionTypeLen = strlen(introductionType);
size_t len = filenameLen +
6 /* == strlen(" line ") */ +
linenoLen +
3 /* == strlen(" > ") */ +
introducerLen +
introductionTypeLen +
1 /* \0 */;
filename_ = cx->pod_malloc<char>(len);
if (!filename_)
return false;
mozilla::DebugOnly<int> checkLen = JS_snprintf(filename_, len, "%s line %s > %s",
callerFilename, linenoBuf, introducer);
callerFilename, linenoBuf, introductionType);
JS_ASSERT(checkLen == len - 1);
if (!introducerFilename_)

View File

@ -405,7 +405,7 @@ class ScriptSource
// undefined if the implementation doesn't know how the code was introduced.
// This is a constant, statically allocated C string, so does not need
// memory management.
const char *introducerType_;
const char *introductionType_;
// True if we can call JSRuntime::sourceHook to load the source on
// demand. If sourceRetrievable_ and hasSourceData() are false, it is not
@ -426,7 +426,7 @@ class ScriptSource
originPrincipals_(originPrincipals),
introductionOffset_(0),
introducerFilename_(nullptr),
introducerType_(nullptr),
introductionType_(nullptr),
sourceRetrievable_(false),
argumentsNotIncluded_(false),
ready_(true),
@ -471,16 +471,16 @@ class ScriptSource
bool setFilename(ExclusiveContext *cx, const char *filename);
bool setIntroducedFilename(ExclusiveContext *cx,
const char *callerFilename, unsigned callerLineno,
const char *introducer, const char *introducerFilename);
const char *introductionType, const char *introducerFilename);
const char *introducerFilename() const {
return introducerFilename_;
}
bool hasIntroducerType() const {
return introducerType_;
bool hasIntroductionType() const {
return introductionType_;
}
const char *introducerType() const {
JS_ASSERT(hasIntroducerType());
return introducerType_;
const char *introductionType() const {
JS_ASSERT(hasIntroductionType());
return introductionType_;
}
const char *filename() const {
return filename_;
@ -1273,7 +1273,10 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
*/
inline void ensureNonLazyCanonicalFunction(JSContext *cx);
JSFunction *originalFunction() const;
/*
* Donor provided itself to callsite clone; null if this is non-clone.
*/
JSFunction *donorFunction() const;
void setIsCallsiteClone(JSObject *fun);
JSFlatString *sourceData(JSContext *cx);

View File

@ -149,7 +149,7 @@ JSScript::principals()
}
inline JSFunction *
JSScript::originalFunction() const {
JSScript::donorFunction() const {
if (!isCallsiteClone())
return nullptr;
return &enclosingScopeOrOriginalFunction_->as<JSFunction>();

View File

@ -3937,8 +3937,8 @@ DebuggerSource_getIntroductionType(JSContext *cx, unsigned argc, Value *vp)
THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, sourceObject);
ScriptSource *ss = sourceObject->source();
if (ss->hasIntroducerType()) {
JSString *str = js_NewStringCopyZ<CanGC>(cx, ss->introducerType());
if (ss->hasIntroductionType()) {
JSString *str = js_NewStringCopyZ<CanGC>(cx, ss->introductionType());
if (!str)
return false;
args.rval().setString(str);

View File

@ -555,9 +555,10 @@ js::InvokeConstructor(JSContext *cx, CallArgs args)
return ok;
}
if (!fun->isInterpretedConstructor())
return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT);
if (!fun->isInterpretedConstructor()) {
RootedValue orig(cx, ObjectValue(*fun->originalFunction()));
return ReportIsNotFunction(cx, orig, args.length() + 1, CONSTRUCT);
}
if (!Invoke(cx, args, CONSTRUCT))
return false;

View File

@ -203,101 +203,6 @@ nsSVGFilterFrame::AttributeChanged(int32_t aNameSpaceID,
aAttribute, aModType);
}
nsresult
nsSVGFilterFrame::PaintFilteredFrame(nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRect *aDirtyArea,
nsIFrame* aTransformRoot)
{
nsSVGFilterInstance instance(aFilteredFrame, this, aPaintCallback,
aDirtyArea, nullptr, nullptr, nullptr,
aTransformRoot);
if (!instance.IsInitialized()) {
return NS_OK;
}
return instance.Render(aContext->ThebesContext());
}
static nsRect
TransformFilterSpaceToFrameSpace(nsSVGFilterInstance *aInstance,
nsIntRect *aRect)
{
if (aRect->IsEmpty()) {
return nsRect();
}
gfxMatrix m = aInstance->GetFilterSpaceToFrameSpaceInCSSPxTransform();
gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
r = m.TransformBounds(r);
return nsLayoutUtils::RoundGfxRectToAppRect(r, aInstance->AppUnitsPerCSSPixel());
}
nsRect
nsSVGFilterFrame::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRect& aPreFilterDirtyRect)
{
if (aPreFilterDirtyRect.IsEmpty()) {
return nsRect();
}
nsSVGFilterInstance instance(aFilteredFrame, this, nullptr, nullptr,
&aPreFilterDirtyRect);
if (!instance.IsInitialized()) {
return nsRect();
}
// We've passed in the source's dirty area so the instance knows about it.
// Now we can ask the instance to compute the area of the filter output
// that's dirty.
nsIntRect dirtyRect;
nsresult rv = instance.ComputePostFilterDirtyRect(&dirtyRect);
if (NS_SUCCEEDED(rv)) {
return TransformFilterSpaceToFrameSpace(&instance, &dirtyRect);
}
return nsRect();
}
nsRect
nsSVGFilterFrame::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect)
{
nsSVGFilterInstance instance(aFilteredFrame, this, nullptr,
&aPostFilterDirtyRect);
if (!instance.IsInitialized()) {
return nsRect();
}
// Now we can ask the instance to compute the area of the source
// that's needed.
nsIntRect neededRect;
nsresult rv = instance.ComputeSourceNeededRect(&neededRect);
if (NS_SUCCEEDED(rv)) {
return TransformFilterSpaceToFrameSpace(&instance, &neededRect);
}
return nsRect();
}
nsRect
nsSVGFilterFrame::GetPostFilterBounds(nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox,
const nsRect *aPreFilterBounds)
{
MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
!(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
"Non-display SVG do not maintain visual overflow rects");
nsSVGFilterInstance instance(aFilteredFrame, this, nullptr, nullptr,
aPreFilterBounds, aPreFilterBounds,
aOverrideBBox);
if (!instance.IsInitialized()) {
return nsRect();
}
nsIntRect bbox;
nsresult rv = instance.ComputePostFilterExtents(&bbox);
if (NS_SUCCEEDED(rv)) {
return TransformFilterSpaceToFrameSpace(&instance, &bbox);
}
return nsRect();
}
#ifdef DEBUG
void
nsSVGFilterFrame::Init(nsIContent* aContent,

View File

@ -57,48 +57,6 @@ public:
nsIAtom* aAttribute,
int32_t aModType) MOZ_OVERRIDE;
/**
* Paint the given filtered frame.
* @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
* frame space (i.e. relative to its origin, the top-left corner of its
* border box).
*/
nsresult PaintFilteredFrame(nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRect* aDirtyArea,
nsIFrame* aTransformRoot);
/**
* Returns the post-filter area that could be dirtied when the given
* pre-filter area of aFilteredFrame changes.
* @param aPreFilterDirtyRect The pre-filter area of aFilteredFrame that has
* changed, relative to aFilteredFrame, in app units.
*/
nsRect GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRect& aPreFilterDirtyRect);
/**
* Returns the pre-filter area that is needed from aFilteredFrame when the
* given post-filter area needs to be repainted.
* @param aPostFilterDirtyRect The post-filter area that is dirty, relative
* to aFilteredFrame, in app units.
*/
nsRect GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect);
/**
* Returns the post-filter visual overflow rect (paint bounds) of
* aFilteredFrame.
* @param aOverrideBBox A user space rect, in user units, that should be used
* as aFilteredFrame's bbox ('bbox' is a specific SVG term), if non-null.
* @param aPreFilterBounds The pre-filter visual overflow rect of
* aFilteredFrame, if non-null.
*/
nsRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox = nullptr,
const nsRect *aPreFilterBounds = nullptr);
#ifdef DEBUG
virtual void Init(nsIContent* aContent,
nsIFrame* aParent,

View File

@ -23,6 +23,92 @@ using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
nsresult
nsSVGFilterInstance::PaintFilteredFrame(nsSVGFilterFrame* aFilterFrame,
nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRect *aDirtyArea,
nsIFrame* aTransformRoot)
{
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, aPaintCallback,
aDirtyArea, nullptr, nullptr, nullptr,
aTransformRoot);
if (!instance.IsInitialized()) {
return NS_OK;
}
return instance.Render(aContext->ThebesContext());
}
nsRect
nsSVGFilterInstance::GetPostFilterDirtyArea(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const nsRect& aPreFilterDirtyRect)
{
if (aPreFilterDirtyRect.IsEmpty()) {
return nsRect();
}
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr, nullptr,
&aPreFilterDirtyRect);
if (!instance.IsInitialized()) {
return nsRect();
}
// We've passed in the source's dirty area so the instance knows about it.
// Now we can ask the instance to compute the area of the filter output
// that's dirty.
nsRect dirtyRect;
nsresult rv = instance.ComputePostFilterDirtyRect(&dirtyRect);
if (NS_SUCCEEDED(rv)) {
return dirtyRect;
}
return nsRect();
}
nsRect
nsSVGFilterInstance::GetPreFilterNeededArea(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect)
{
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr,
&aPostFilterDirtyRect);
if (!instance.IsInitialized()) {
return nsRect();
}
// Now we can ask the instance to compute the area of the source
// that's needed.
nsRect neededRect;
nsresult rv = instance.ComputeSourceNeededRect(&neededRect);
if (NS_SUCCEEDED(rv)) {
return neededRect;
}
return nsRect();
}
nsRect
nsSVGFilterInstance::GetPostFilterBounds(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox,
const nsRect *aPreFilterBounds)
{
MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
!(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
"Non-display SVG do not maintain visual overflow rects");
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr, nullptr,
aPreFilterBounds, aPreFilterBounds,
aOverrideBBox);
if (!instance.IsInitialized()) {
return nsRect();
}
nsRect bbox;
nsresult rv = instance.ComputePostFilterExtents(&bbox);
if (NS_SUCCEEDED(rv)) {
return bbox;
}
return nsRect();
}
nsSVGFilterInstance::nsSVGFilterInstance(nsIFrame *aTargetFrame,
nsSVGFilterFrame *aFilterFrame,
nsSVGFilterPaintCallback *aPaintCallback,
@ -664,9 +750,9 @@ nsSVGFilterInstance::Render(gfxContext* aContext)
}
nsresult
nsSVGFilterInstance::ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect)
nsSVGFilterInstance::ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect)
{
*aPostFilterDirtyRect = nsIntRect();
*aPostFilterDirtyRect = nsRect();
if (mPreFilterDirtyRect.IsEmpty()) {
return NS_OK;
}
@ -685,14 +771,15 @@ nsSVGFilterInstance::ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect)
nsIntRegion resultChangeRegion =
FilterSupport::ComputeResultChangeRegion(filter,
mPreFilterDirtyRect, nsIntRegion(), nsIntRegion());
*aPostFilterDirtyRect = resultChangeRegion.GetBounds();
*aPostFilterDirtyRect =
FilterSpaceToFrameSpace(resultChangeRegion.GetBounds());
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputePostFilterExtents(nsIntRect* aPostFilterExtents)
nsSVGFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents)
{
*aPostFilterExtents = nsIntRect();
*aPostFilterExtents = nsRect();
nsresult rv = BuildPrimitives();
if (NS_FAILED(rv))
@ -714,12 +801,12 @@ nsSVGFilterInstance::ComputePostFilterExtents(nsIntRect* aPostFilterExtents)
FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
nsIntRegion postFilterExtents =
FilterSupport::ComputePostFilterExtents(filter, sourceBoundsInt);
*aPostFilterExtents = postFilterExtents.GetBounds();
*aPostFilterExtents = FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
nsSVGFilterInstance::ComputeSourceNeededRect(nsRect* aDirty)
{
nsresult rv = BuildPrimitives();
if (NS_FAILED(rv))
@ -731,7 +818,7 @@ nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
}
ComputeNeededBoxes();
*aDirty = mSourceGraphic.mNeededBounds;
*aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
return NS_OK;
}
@ -757,6 +844,17 @@ nsSVGFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
return rect;
}
nsRect
nsSVGFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
{
if (aRect.IsEmpty()) {
return nsRect();
}
gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx);
}
gfxMatrix
nsSVGFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
{

View File

@ -59,6 +59,52 @@ class nsSVGFilterInstance
typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
public:
/**
* Paint the given filtered frame.
* @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
* frame space (i.e. relative to its origin, the top-left corner of its
* border box).
*/
static nsresult PaintFilteredFrame(nsSVGFilterFrame* aFilterFrame,
nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRect* aDirtyArea,
nsIFrame* aTransformRoot = nullptr);
/**
* Returns the post-filter area that could be dirtied when the given
* pre-filter area of aFilteredFrame changes.
* @param aPreFilterDirtyRect The pre-filter area of aFilteredFrame that has
* changed, relative to aFilteredFrame, in app units.
*/
static nsRect GetPostFilterDirtyArea(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const nsRect& aPreFilterDirtyRect);
/**
* Returns the pre-filter area that is needed from aFilteredFrame when the
* given post-filter area needs to be repainted.
* @param aPostFilterDirtyRect The post-filter area that is dirty, relative
* to aFilteredFrame, in app units.
*/
static nsRect GetPreFilterNeededArea(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect);
/**
* Returns the post-filter visual overflow rect (paint bounds) of
* aFilteredFrame.
* @param aOverrideBBox A user space rect, in user units, that should be used
* as aFilteredFrame's bbox ('bbox' is a specific SVG term), if non-null.
* @param aPreFilterBounds The pre-filter visual overflow rect of
* aFilteredFrame, if non-null.
*/
static nsRect GetPostFilterBounds(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox = nullptr,
const nsRect *aPreFilterBounds = nullptr);
/**
* @param aTargetFrame The frame of the filtered element under consideration.
* @param aFilterFrame The frame of the SVG filter element.
@ -118,31 +164,31 @@ public:
nsresult Render(gfxContext* aContext);
/**
* Sets the aPostFilterDirtyRect outparam to the post-filter bounds in filter
* Sets the aPostFilterDirtyRect outparam to the post-filter bounds in frame
* space of the area that would be dirtied by mTargetFrame when a given
* pre-filter area of mTargetFrame is dirtied. The pre-filter area must have
* been specified before calling this method by passing it as the
* aPreFilterDirtyRect argument to the nsSVGFilterInstance constructor.
*/
nsresult ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect);
nsresult ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect);
/**
* Sets the aPostFilterExtents outparam to the post-filter bounds in filter
* Sets the aPostFilterExtents outparam to the post-filter bounds in frame
* space for the whole filter output. This is not necessarily equivalent to
* the area that would be dirtied in the result when the entire pre-filter
* area is dirtied, because some filter primitives can generate output
* without any input.
*/
nsresult ComputePostFilterExtents(nsIntRect* aPostFilterExtents);
nsresult ComputePostFilterExtents(nsRect* aPostFilterExtents);
/**
* Sets the aDirty outparam to the pre-filter bounds in filter space of the
* Sets the aDirty outparam to the pre-filter bounds in frame space of the
* area of mTargetFrame that is needed in order to paint the filtered output
* for a given post-filter dirtied area. The post-filter area must have been
* specified before calling this method by passing it as the aPostFilterDirtyRect
* argument to the nsSVGFilterInstance constructor.
*/
nsresult ComputeSourceNeededRect(nsIntRect* aDirty);
nsresult ComputeSourceNeededRect(nsRect* aDirty);
float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumber2 *aNumber) const
{
@ -269,6 +315,7 @@ private:
* large to be stored in an nsIntRect.
*/
nsIntRect FrameSpaceToFilterSpace(const nsRect* aRect) const;
nsRect FilterSpaceToFrameSpace(const nsIntRect& aRect) const;
/**
* Returns the transform from frame space to the coordinate space that

View File

@ -16,6 +16,7 @@
#include "nsSVGEffects.h"
#include "nsSVGElement.h"
#include "nsSVGFilterFrame.h"
#include "nsSVGFilterInstance.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGMaskFrame.h"
#include "nsSVGPaintServerFrame.h"
@ -276,7 +277,7 @@ nsRect
overrideBBox.RoundOut();
nsRect overflowRect =
filterFrame->GetPostFilterBounds(firstFrame, &overrideBBox);
nsSVGFilterInstance::GetPostFilterBounds(filterFrame, firstFrame, &overrideBBox);
// Return overflowRect relative to aFrame, rather than "user space":
return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToUserSpace);
@ -327,8 +328,8 @@ nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
// Adjust the dirty area for effects, and shift it back to being relative to
// the reference frame.
nsRect result = filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect) -
toUserSpace;
nsRect result = nsSVGFilterInstance::GetPostFilterDirtyArea(filterFrame,
firstFrame, preEffectsRect) - toUserSpace;
// Return the result, in pixels relative to the reference frame.
return result.ToOutsidePixels(appUnitsPerDevPixel);
}
@ -352,8 +353,8 @@ nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
nsRect postEffectsRect = aDirtyRect + toUserSpace;
// Return ther result, relative to aFrame, not in user space:
return filterFrame->GetPreFilterNeededArea(firstFrame, postEffectsRect) -
toUserSpace;
return nsSVGFilterInstance::GetPreFilterNeededArea(filterFrame, firstFrame,
postEffectsRect) - toUserSpace;
}
bool
@ -517,7 +518,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
RegularFramePaintCallback callback(aBuilder, aLayerManager,
offsetWithoutSVGGeomFramePos);
nsRect dirtyRect = aDirtyRect - offset;
filterFrame->PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRect, nullptr);
nsSVGFilterInstance::PaintFilteredFrame(filterFrame, aCtx, aFrame,
&callback, &dirtyRect);
} else {
gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);

View File

@ -36,6 +36,7 @@
#include "nsSVGContainerFrame.h"
#include "nsSVGEffects.h"
#include "nsSVGFilterFrame.h"
#include "nsSVGFilterInstance.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGForeignObjectFrame.h"
#include "gfxSVGGlyphs.h"
@ -159,12 +160,13 @@ nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
"Called on invalid frame type");
nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
if (!filter) {
nsSVGFilterFrame *filterFrame = nsSVGEffects::GetFilterFrame(aFrame);
if (!filterFrame) {
return aPreFilterRect;
}
return filter->GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
return nsSVGFilterInstance::GetPostFilterBounds(filterFrame, aFrame, nullptr,
&aPreFilterRect);
}
bool
@ -621,7 +623,9 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
dirtyRect = &tmpDirtyRect;
}
SVGPaintCallback paintCallback;
filterFrame->PaintFilteredFrame(aContext, aFrame, &paintCallback, dirtyRect, aTransformRoot);
nsSVGFilterInstance::PaintFilteredFrame(filterFrame, aContext, aFrame,
&paintCallback, dirtyRect,
aTransformRoot);
} else {
svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
}

View File

@ -1198,6 +1198,9 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
nsDeviceContext* devContext = presContext->DeviceContext();
nscoord offsetForContextMenu = 0;
bool isNoAutoHide = IsNoAutoHide();
nsPopupLevel popupLevel = PopupLevel(isNoAutoHide);
if (IsAnchored()) {
// if we are anchored, there are certain things we don't want to do when
// repositioning the popup to fit on the screen, such as end up positioned
@ -1238,7 +1241,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
// the window is moved. Popups at the parent level follow the parent
// window as it is moved and remained anchored, so we want to maintain the
// anchoring instead.
if (IsNoAutoHide() && PopupLevel(true) != ePopupLevelParent) {
if (isNoAutoHide && popupLevel != ePopupLevelParent) {
// Account for the margin that will end up being added to the screen coordinate
// the next time SetPopupPosition is called.
mScreenXPos = presContext->AppUnitsToIntCSSPixels(screenPoint.x - margin.left);
@ -1279,7 +1282,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
// If a panel is being moved or has flip="none", don't constrain or flip it. But always do this for
// content shells, so that the popup doesn't extend outside the containing frame.
if (mInContentShell || (mFlip != FlipType_None && (!aIsMove || mPopupType != ePopupTypePanel))) {
nsRect screenRect = GetConstraintRect(anchorRect, rootScreenRect);
nsRect screenRect = GetConstraintRect(anchorRect, rootScreenRect, popupLevel);
// Ensure that anchorRect is on screen.
anchorRect = anchorRect.Intersect(screenRect);
@ -1378,7 +1381,8 @@ nsMenuPopupFrame::GetCurrentMenuItem()
nsRect
nsMenuPopupFrame::GetConstraintRect(const nsRect& aAnchorRect,
const nsRect& aRootScreenRect)
const nsRect& aRootScreenRect,
nsPopupLevel aPopupLevel)
{
nsIntRect screenRectPixels;
nsPresContext* presContext = PresContext();
@ -1401,8 +1405,11 @@ nsMenuPopupFrame::GetConstraintRect(const nsRect& aAnchorRect,
nsPresContext::AppUnitsToIntCSSPixels(rect.y),
width, height, getter_AddRefs(screen));
if (screen) {
// Non-top-level popups (which will always be panels)
// should never overlap the OS bar:
bool dontOverlapOSBar = aPopupLevel != ePopupLevelTop;
// get the total screen area if the popup is allowed to overlap it.
if (mMenuCanOverlapOSBar && !mInContentShell)
if (!dontOverlapOSBar && mMenuCanOverlapOSBar && !mInContentShell)
screen->GetRect(&screenRectPixels.x, &screenRectPixels.y,
&screenRectPixels.width, &screenRectPixels.height);
else

View File

@ -310,7 +310,11 @@ public:
// area of the screen the popup should be displayed on. Content popups,
// however, will also be constrained by the content area, given by
// aRootScreenRect. All coordinates are in app units.
nsRect GetConstraintRect(const nsRect& aAnchorRect, const nsRect& aRootScreenRect);
// For non-toplevel popups (which will always be panels), we will also
// constrain them to the available screen rect, ie they will not fall
// underneath the taskbar, dock or other fixed OS elements.
nsRect GetConstraintRect(const nsRect& aAnchorRect, const nsRect& aRootScreenRect,
nsPopupLevel aPopupLevel);
// Determines whether the given edges of the popup may be moved, where
// aHorizontalSide and aVerticalSide are one of the NS_SIDE_* constants, or

View File

@ -224,7 +224,8 @@ nsResizerFrame::HandleEvent(nsPresContext* aPresContext,
nsIFrame* rootFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame();
nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits();
nsRect screenRect = menuPopupFrame->GetConstraintRect(frameRect, rootScreenRect);
nsPopupLevel popupLevel = menuPopupFrame->PopupLevel();
nsRect screenRect = menuPopupFrame->GetConstraintRect(frameRect, rootScreenRect, popupLevel);
// round using ToInsidePixels as it's better to be a pixel too small
// than be too large. If the popup is too large it could get flipped
// to the opposite side of the anchor point while resizing.

View File

@ -73,8 +73,10 @@ WebrtcAudioConduit::~WebrtcAudioConduit()
{
delete mRecvCodecList[i];
}
delete mCurSendCodecConfig;
if (mPtrVoERTP_RTCP) {
mPtrVoERTP_RTCP->Release();
}
// The first one of a pair to be deleted shuts down media for both
if(mPtrVoEXmedia)
@ -278,19 +280,22 @@ MediaConduitErrorCode WebrtcAudioConduit::Init(WebrtcAudioConduit *other)
CSFLogError(logTag, "%s Unable to initialize VoEProcessing", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
if(!(mPtrVoEXmedia = VoEExternalMedia::GetInterface(mVoiceEngine)))
{
CSFLogError(logTag, "%s Unable to initialize VoEExternalMedia", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
if(!(mPtrVoERTP_RTCP = VoERTP_RTCP::GetInterface(mVoiceEngine)))
{
CSFLogError(logTag, "%s Unable to initialize VoERTP_RTCP", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
if(!(mPtrVoEVideoSync = VoEVideoSync::GetInterface(mVoiceEngine)))
{
CSFLogError(logTag, "%s Unable to initialize VoEVideoSync", __FUNCTION__);
return kMediaConduitSessionNotInited;
}
if (!(mPtrRTP = webrtc::VoERTP_RTCP::GetInterface(mVoiceEngine)))
{
CSFLogError(logTag, "%s Unable to get audio RTP/RTCP interface ",
@ -556,12 +561,24 @@ WebrtcAudioConduit::ConfigureRecvMediaCodecs(
CSFLogError(logTag, "%s Starting playout Failed", __FUNCTION__);
return kMediaConduitPlayoutError;
}
//we should be good here for setting this.
mEngineReceiving = true;
DumpCodecDB();
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcAudioConduit::EnableAudioLevelExtension(bool enabled, uint8_t id)
{
CSFLogDebug(logTag, "%s %d %d ", __FUNCTION__, enabled, id);
if (mPtrVoERTP_RTCP->SetRTPAudioLevelIndicationStatus(mChannel, enabled, id) == -1)
{
CSFLogError(logTag, "%s SetRTPAudioLevelIndicationStatus Failed", __FUNCTION__);
return kMediaConduitUnknownError;
}
return kMediaConduitNoError;
}
MediaConduitErrorCode
WebrtcAudioConduit::SendAudioFrame(const int16_t audio_data[],
@ -570,7 +587,6 @@ WebrtcAudioConduit::SendAudioFrame(const int16_t audio_data[],
int32_t capture_delay)
{
CSFLogDebug(logTag, "%s ", __FUNCTION__);
// Following checks need to be performed
// 1. Non null audio buffer pointer,
// 2. invalid sampling frequency - less than 0 or unsupported ones

View File

@ -23,7 +23,6 @@
#include "webrtc/voice_engine/include/voe_audio_processing.h"
#include "webrtc/voice_engine/include/voe_video_sync.h"
#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
//Some WebRTC types for short notations
using webrtc::VoEBase;
using webrtc::VoENetwork;
@ -31,13 +30,11 @@
using webrtc::VoEExternalMedia;
using webrtc::VoEAudioProcessing;
using webrtc::VoEVideoSync;
using webrtc::VoERTP_RTCP;
/** This file hosts several structures identifying different aspects
* of a RTP Session.
*/
namespace mozilla {
// Helper function
DOMHighResTimeStamp
@ -86,13 +83,17 @@ public:
*/
virtual MediaConduitErrorCode ConfigureRecvMediaCodecs(
const std::vector<AudioCodecConfig* >& codecConfigList);
/**
* Function to enable the audio level extension
* @param enabled: enable extension
*/
virtual MediaConduitErrorCode EnableAudioLevelExtension(bool enabled, uint8_t id);
/**
* Register External Transport to this Conduit. RTP and RTCP frames from the VoiceEngine
* shall be passed to the registered transport for transporting externally.
*/
virtual MediaConduitErrorCode AttachTransport(mozilla::RefPtr<TransportInterface> aTransport);
/**
* Function to deliver externally captured audio sample for encoding and transport
* @param audioData [in]: Pointer to array containing a frame of audio
@ -234,13 +235,12 @@ private:
webrtc::VoEExternalMedia* mPtrVoEXmedia;
webrtc::VoEAudioProcessing* mPtrVoEProcessing;
webrtc::VoEVideoSync* mPtrVoEVideoSync;
webrtc::VoERTP_RTCP* mPtrVoERTP_RTCP;
webrtc::VoERTP_RTCP* mPtrRTP;
//engine states of our interets
bool mEngineTransmitting; // If true => VoiceEngine Send-subsystem is up
bool mEngineReceiving; // If true => VoiceEngine Receive-subsystem is up
// and playout is enabled
// Keep track of each inserted RTP block and the time it was inserted
// so we can estimate the clock time for a specific TimeStamp coming out
// (for when we send data to MediaStreamTracks). Blocks are aged out as needed.

View File

@ -344,16 +344,18 @@ public:
*/
virtual MediaConduitErrorCode ConfigureRecvMediaCodecs(
const std::vector<AudioCodecConfig* >& recvCodecConfigList) = 0;
/**
* Function to enable the audio level extension
* @param enabled: enable extension
* @param id: id to be used for this rtp header extension
* NOTE: See AudioConduit for more information
*/
virtual MediaConduitErrorCode EnableAudioLevelExtension(bool enabled, uint8_t id) = 0;
};
}
#endif

View File

@ -2230,12 +2230,15 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
// and are responsible for cleanly shutting down.
mozilla::RefPtr<mozilla::AudioSessionConduit> conduit =
mozilla::AudioSessionConduit::Create(static_cast<AudioSessionConduit *>(rx_conduit.get()));
if (!conduit || conduit->ConfigureSendMediaCodec(config))
return VCM_ERROR;
CSFLogError(logTag, "Created audio pipeline audio level %d %d",
attrs->audio_level, attrs->audio_level_id);
if (!conduit || conduit->EnableAudioLevelExtension(attrs->audio_level, attrs->audio_level_id))
return VCM_ERROR;
pc.impl()->media()->AddConduit(level, false, conduit);
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
new mozilla::MediaPipelineTransmit(
pc.impl()->GetHandle(),

View File

@ -514,17 +514,16 @@ gsmsdp_init_media (fsmdef_media_t *media)
media->previous_sdp.num_payloads = 0;
media->previous_sdp.tias_bw = SDP_INVALID_VALUE;
media->previous_sdp.profile_level = 0;
media->hold = FSM_HOLD_NONE;
media->flags = 0; /* clear all flags */
media->cap_index = CC_MAX_MEDIA_CAP; /* max is invalid value */
media->video = NULL;
media->candidate_ct = 0;
media->rtcp_mux = FALSE;
media->audio_level = TRUE;
media->audio_level_id = 1;
/* ACTPASS is the value we put in every offer */
media->setup = SDP_SETUP_ACTPASS;
media->local_datachannel_port = 0;
media->remote_datachannel_port = 0;
media->datachannel_streams = WEBRTC_DATACHANNEL_STREAMS_DEFAULT;
@ -1702,7 +1701,6 @@ gsmsdp_set_rtcp_fb_ack_attribute (uint16_t level,
GSM_ERR_MSG("Failed to add attribute");
return;
}
result = sdp_attr_set_rtcp_fb_ack(sdp_p, level, payload_type,
a_instance, ack_type);
if (result != SDP_SUCCESS) {
@ -1710,6 +1708,39 @@ gsmsdp_set_rtcp_fb_ack_attribute (uint16_t level,
}
}
/*
* gsmsdp_set_audio_level_attribute
*
* Description:
*
* Adds an audio level extension attributesto the specified SDP.
*
* Parameters:
*
* level - The media level of the SDP where the media attribute exists.
* sdp_p - Pointer to the SDP to set the attribute against.
*/
void
gsmsdp_set_extmap_attribute (uint16_t level,
void *sdp_p,
u16 id,
const char* uri)
{
uint16_t a_instance = 0;
sdp_result_e result;
result = sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, &a_instance);
if (result != SDP_SUCCESS) {
GSM_ERR_MSG("Failed to add attribute");
return;
}
result = sdp_attr_set_extmap(sdp_p, level, id, uri, a_instance);
if (result != SDP_SUCCESS) {
GSM_ERR_MSG("Failed to set attribute");
}
}
/*
* gsmsdp_set_rtcp_fb_nack_attribute
*
@ -4629,6 +4660,66 @@ gsmsdp_negotiate_rtcp_fb (cc_sdp_t *cc_sdp_p,
}
}
}
return CC_CAUSE_OK;
}
/*
* gsmsdp_negotiate_extmap
*
* Description:
* Negotiates extmaps header extension to local SDP for supported audio codecs
*
* Parameters:
* cc_sdp_p - local and remote SDP
* media - The media structure for the current level to be negotiated
* offer - True if the remote SDP is an offer
*
* returns
* CC_CAUSE_OK - success
* any other code - failure
*/
cc_causes_t
gsmsdp_negotiate_extmap (cc_sdp_t *cc_sdp_p,
fsmdef_media_t *media,
boolean offer)
{
boolean audio_level = FALSE;
u16 audio_level_id = 0xFFFF;
int level = media->level;
int i;
const char* uri;
/*
* Remove any previously negotiated extmap attributes from the
* local SDP
*/
sdp_result_e result = SDP_SUCCESS;
while (result == SDP_SUCCESS) {
result = sdp_delete_attr (cc_sdp_p->src_sdp, level, 0,
SDP_ATTR_EXTMAP, 1);
}
i = 1;
do {
uri = sdp_attr_get_extmap_uri(cc_sdp_p->dest_sdp, level, i);
if (uri != NULL && strcmp(uri, SDP_EXTMAP_AUDIO_LEVEL) == 0) {
audio_level = TRUE;
audio_level_id = sdp_attr_get_extmap_id(cc_sdp_p->dest_sdp, level, i);
}
i++;
} while (uri != NULL);
media->audio_level = audio_level;
media->audio_level_id = audio_level_id;
/*
* Now, in our local SDP, set extmap types that both we and the
* remote party support
*/
if (media->audio_level) {
gsmsdp_set_extmap_attribute (level, cc_sdp_p->src_sdp, audio_level_id, SDP_EXTMAP_AUDIO_LEVEL);
}
return CC_CAUSE_OK;
}
@ -4980,11 +5071,14 @@ gsmsdp_negotiate_media_lines (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean initial
unsupported_line = TRUE;
update_local_ret_value = TRUE;
}
/* Negotiate rtcp feedback mechanisms */
if (media && media_type == SDP_MEDIA_VIDEO) {
gsmsdp_negotiate_rtcp_fb (dcb_p->sdp, media, offer);
}
/* Negotiate redundancy mechanisms */
if (media && media_type == SDP_MEDIA_AUDIO) {
gsmsdp_negotiate_extmap (dcb_p->sdp, media, offer);
}
/*
* Negotiate rtcp-mux
@ -4993,7 +5087,6 @@ gsmsdp_negotiate_media_lines (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean initial
sdp_res = sdp_attr_get_rtcp_mux_attribute(sdp_p->dest_sdp, i,
0, SDP_ATTR_RTCP_MUX,
1, &rtcp_mux);
if (SDP_SUCCESS == sdp_res) {
media->rtcp_mux = TRUE;
}
@ -5597,10 +5690,14 @@ gsmsdp_add_media_line (fsmdef_dcb_t *dcb_p, const cc_media_cap_t *media_cap,
sdp_rtcp_fb_nack_to_bitmap(SDP_RTCP_FB_NACK_PLI) |
sdp_rtcp_fb_ccm_to_bitmap(SDP_RTCP_FB_CCM_FIR));
}
/* Add supported audio level rtp extension */
if (media_cap->type == SDP_MEDIA_AUDIO) {
gsmsdp_set_extmap_attribute(level, dcb_p->sdp->src_sdp, 1,
SDP_EXTMAP_AUDIO_LEVEL);
}
/* Add a=setup attribute */
gsmsdp_set_setup_attribute(level, dcb_p->sdp->src_sdp, media->setup);
/*
* wait until here to set ICE candidates as SDP is now initialized
*/

View File

@ -201,17 +201,21 @@ typedef struct fsmdef_media_t_ {
/* ICE Candidates */
char **candidatesp;
int candidate_ct;
/*
* rtcp-mux indicates media stream is muxed for RTP and RTCP
*/
boolean rtcp_mux;
/*
* Flag to indicate if RTP Header extension for audio level is used
* and the id to be used for it
*/
boolean audio_level;
uint8_t audio_level_id;
/*
* The value of the a=setup line
*/
sdp_setup_type_e setup;
/*
* port number used in m= data channel line
*/

View File

@ -1230,13 +1230,11 @@ lsm_tx_start (lsm_lcb_t *lcb, const char *fname, fsmdef_media_t *media)
continue;
}
}
media->xmit_chan = TRUE;
attrs.mute = FALSE;
attrs.rtcp_mux = media->rtcp_mux;
attrs.audio_level = media->audio_level;
attrs.audio_level_id = (uint8_t)media->audio_level_id;
attrs.is_video = FALSE;
attrs.bundle_level = 0;
attrs.bundle_stream_correlator = 0;

View File

@ -67,15 +67,14 @@
#define SDP_SRTP_CONTEXT_SET_ENCRYPT_AUTHENTICATE(cw) \
((cw) |= (SDP_SRTP_ENCRYPT_MASK | SDP_SRTP_AUTHENTICATE_MASK | \
SDP_SRTCP_ENCRYPT_MASK))
#define SDP_SRTP_CONTEXT_RESET_SSRC(cw) ((cw) &= ~(SDP_SRTCP_SSRC_MASK))
#define SDP_SRTP_CONTEXT_RESET_ROC(cw) ((cw) &= ~(SDP_SRTCP_ROC_MASK))
#define SDP_SRTP_CONTEXT_RESET_KDR(cw) ((cw) &= ~(SDP_SRTCP_KDR_MASK))
#define SDP_CONTEXT_RESET_MASTER_KEY(cw) ((cw) &= ~(SDP_SRTCP_KEY_MASK))
#define SDP_CONTEXT_RESET_MASTER_SALT(cw) ((cw) &= ~(SDP_SRTCP_SALT_MASK))
#define SDP_EXTMAP_AUDIO_LEVEL "urn:ietf:params:rtp-hdrext:ssrc-audio-level"
/* SDP Enum Types */
typedef enum {
SDP_DEBUG_TRACE,
SDP_DEBUG_WARNINGS,
@ -892,6 +891,16 @@ typedef struct sdp_media_profiles {
sdp_payload_ind_e payload_indicator[SDP_MAX_PROFILES][SDP_MAX_PAYLOAD_TYPES];
u16 payload_type[SDP_MAX_PROFILES][SDP_MAX_PAYLOAD_TYPES];
} sdp_media_profiles_t;
/*
* a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
*
*/
typedef struct sdp_extmap {
u16 id;
sdp_direction_e media_direction;
char uri[SDP_MAX_STRING_LEN+1];
char extension_attributes[SDP_MAX_STRING_LEN+1];
} sdp_extmap_t;
/*
@ -899,7 +908,6 @@ typedef struct sdp_media_profiles {
* This type is used to hold cryptographic context information.
*
*/
typedef struct sdp_srtp_crypto_context_t_ {
int32 tag;
unsigned long selection_flags;
@ -982,10 +990,10 @@ typedef struct sdp_attr {
sdp_fmtp_fb_t rtcp_fb;
sdp_setup_type_e setup;
sdp_connection_type_e connection;
sdp_extmap_t extmap;
} attr;
struct sdp_attr *next_p;
} sdp_attr_t;
typedef struct sdp_srtp_crypto_suite_list_ {
sdp_srtp_crypto_suite_t crypto_suite_val;
char * crypto_suite_str;
@ -2099,9 +2107,16 @@ sdp_attr_set_rtcp_fb_nack(void *sdp_ptr, u16 level, u16 payload_type, u16 inst,
sdp_result_e
sdp_attr_set_rtcp_fb_trr_int(void *sdp_ptr, u16 level, u16 payload_type,
u16 inst, u32 interval);
sdp_result_e
sdp_attr_set_rtcp_fb_ccm(void *sdp_ptr, u16 level, u16 payload_type, u16 inst,
sdp_rtcp_fb_ccm_type_e);
const char *
sdp_attr_get_extmap_uri(void *sdp_ptr, u16 level, u16 inst);
u16
sdp_attr_get_extmap_id(void *sdp_ptr, u16 level, u16 inst);
sdp_result_e
sdp_attr_set_extmap(void *sdp_ptr, u16 level, u16 id, const char* uri, u16 inst);
#endif /* _SDP_H_ */

View File

@ -5145,6 +5145,79 @@ sdp_result_e sdp_parse_attr_connection(sdp_t *sdp_p,
return SDP_FAILURE;
break;
}
return SDP_SUCCESS;
}
sdp_result_e sdp_build_attr_extmap(sdp_t *sdp_p,
sdp_attr_t *attr_p,
flex_string *fs)
{
flex_string_sprintf(fs, "a=extmap:%d %s\r\n",
attr_p->attr.extmap.id,
attr_p->attr.extmap.uri);
return SDP_SUCCESS;
}
sdp_result_e sdp_parse_attr_extmap(sdp_t *sdp_p,
sdp_attr_t *attr_p,
const char *ptr)
{
sdp_result_e result;
attr_p->attr.extmap.id = 0;
attr_p->attr.extmap.media_direction = SDP_DIRECTION_SENDRECV;
attr_p->attr.extmap.uri[0] = '\0';
attr_p->attr.extmap.extension_attributes[0] = '\0';
/* Find the payload type number. */
attr_p->attr.extmap.id =
(u16)sdp_getnextnumtok(ptr, &ptr, "/ \t", &result);
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p->peerconnection,
"%s Warning: Invalid extmap id specified for %s attribute.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++;
return (SDP_INVALID_PARAMETER);
}
if (*ptr == '/') {
char direction[SDP_MAX_STRING_LEN+1];
/* Find the encoding name. */
ptr = sdp_getnextstrtok(ptr, direction,
sizeof(direction), " \t", &result);
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p->peerconnection,
"%s Warning: No uri specified in %s attribute.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++;
return (SDP_INVALID_PARAMETER);
}
}
ptr = sdp_getnextstrtok(ptr, attr_p->attr.extmap.uri,
sizeof(attr_p->attr.extmap.uri), " \t", &result);
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p->peerconnection,
"%s Warning: No uri specified in %s attribute.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++;
return (SDP_INVALID_PARAMETER);
}
ptr = sdp_getnextstrtok(ptr, attr_p->attr.extmap.extension_attributes,
sizeof(attr_p->attr.extmap.extension_attributes), "\r\n", &result);
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, id %u, direction %s, "
"uri %s, extension %s", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type),
attr_p->attr.extmap.id,
SDP_DIRECTION_PRINT(attr_p->attr.extmap.media_direction),
attr_p->attr.extmap.uri,
attr_p->attr.extmap.extension_attributes);
}
return (SDP_SUCCESS);
}

View File

@ -12477,10 +12477,112 @@ sdp_attr_set_rtcp_fb_ccm(void *sdp_ptr, u16 level, u16 payload_type, u16 inst,
sdp_p->conf_p->num_invalid_param++;
return (SDP_INVALID_PARAMETER);
}
attr_p->attr.rtcp_fb.payload_num = payload_type;
attr_p->attr.rtcp_fb.feedback_type = SDP_RTCP_FB_CCM;
attr_p->attr.rtcp_fb.param.ccm = type;
attr_p->attr.rtcp_fb.extra[0] = '\0';
return (SDP_SUCCESS);
}
/* Function: sdp_attr_get_extmap_uri
* Description: Returns a pointer to the value of the encoding name
* parameter specified for the given attribute. Value is
* returned as a const ptr and so cannot be modified by the
* application. If the given attribute is not defined, NULL
* will be returned.
* Parameters: sdp_ptr The SDP handle returned by sdp_init_description.
* level The level to check for the attribute.
* inst_num The attribute instance number to check.
* Returns: Codec value or SDP_CODEC_INVALID.
*/
const char *sdp_attr_get_extmap_uri(void *sdp_ptr, u16 level,
u16 inst_num)
{
sdp_t *sdp_p = (sdp_t *)sdp_ptr;
sdp_attr_t *attr_p;
if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
return (NULL);
}
attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, inst_num);
if (attr_p == NULL) {
if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
CSFLogError(logTag, "%s extmap attribute, level %u instance %u "
"not found.", sdp_p->debug_str, level, inst_num);
}
sdp_p->conf_p->num_invalid_param++;
return (NULL);
} else {
return (attr_p->attr.extmap.uri);
}
}
/* Function: sdp_attr_get_extmap_id
* Description: Returns the id of the extmap specified for the given
* attribute. If the given attribute is not defined, 0xFFFF
* will be returned.
* Parameters: sdp_ptr The SDP handle returned by sdp_init_description.
* level The level to check for the attribute.
* inst_num The attribute instance number to check.
* Returns: The id of the extmap attribute.
*/
u16 sdp_attr_get_extmap_id(void *sdp_ptr, u16 level,
u16 inst_num)
{
sdp_t *sdp_p = (sdp_t *)sdp_ptr;
sdp_attr_t *attr_p;
if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
return (NULL);
}
attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, inst_num);
if (attr_p == NULL) {
if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
CSFLogError(logTag, "%s extmap attribute, level %u instance %u "
"not found.", sdp_p->debug_str, level, inst_num);
}
sdp_p->conf_p->num_invalid_param++;
return 0xFFFF;
} else {
return (attr_p->attr.extmap.id);
}
}
/* Function: sdp_attr_set_extmap
* Description: Sets the value of an rtcp-fb:...ccm attribute
* Parameters: sdp_ptr The SDP handle returned by sdp_init_description.
* level The level to set the attribute.
* id The id to set the attribute.
* uri The uri to set the attribute.
* inst The attribute instance number to check.
* Returns: SDP_SUCCESS Attribute param was set successfully.
* SDP_INVALID_PARAMETER Specified attribute is not defined.
*/
sdp_result_e
sdp_attr_set_extmap(void *sdp_ptr, u16 level, u16 id, const char* uri, u16 inst)
{
sdp_t *sdp_p = (sdp_t *)sdp_ptr;
sdp_attr_t *attr_p;
if (!sdp_verify_sdp_ptr(sdp_p)) {
return (SDP_INVALID_SDP_PTR);
}
attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, inst);
if (!attr_p) {
if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
CSFLogError(logTag, "%s extmap attribute, level %u "
"instance %u not found.", sdp_p->debug_str, level,
inst);
}
sdp_p->conf_p->num_invalid_param++;
return (SDP_INVALID_PARAMETER);
}
attr_p->attr.extmap.id = id;
sstrncpy(attr_p->attr.extmap.uri, uri, SDP_MAX_STRING_LEN+1);
return (SDP_SUCCESS);
}

View File

@ -176,8 +176,9 @@ const sdp_attrarray_t sdp_attr[SDP_MAX_ATTR_TYPES] =
sdp_parse_attr_setup, sdp_build_attr_setup},
{"connection", sizeof("connection"),
sdp_parse_attr_connection, sdp_build_attr_connection},
{"extmap", sizeof("extmap"),
sdp_parse_attr_extmap, sdp_build_attr_extmap},
};
/* Note: These *must* be in the same order as the enum types. */
const sdp_namearray_t sdp_media[SDP_MAX_MEDIA_TYPES] =
{

View File

@ -167,11 +167,16 @@ extern sdp_result_e sdp_parse_attr_connection(sdp_t *sdp_p,
extern sdp_result_e sdp_build_attr_connection(sdp_t *sdp_p,
sdp_attr_t *attr_p,
flex_string *fs);
extern sdp_result_e sdp_parse_attr_extmap(sdp_t *sdp_p,
sdp_attr_t *attr_p,
const char *ptr);
extern sdp_result_e sdp_build_attr_extmap(sdp_t *sdp_p,
sdp_attr_t *attr_p,
flex_string *fs);
extern sdp_result_e sdp_parse_attr_mptime(
sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
extern sdp_result_e sdp_build_attr_mptime(
sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
extern sdp_result_e sdp_parse_attr_x_sidin(
sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
extern sdp_result_e sdp_build_attr_x_sidin(

View File

@ -246,10 +246,10 @@ typedef enum {
SDP_ATTR_RTCP_FB, /* RFC 4585 */
SDP_ATTR_SETUP,
SDP_ATTR_CONNECTION,
SDP_ATTR_EXTMAP, /* RFC 5285 */
SDP_MAX_ATTR_TYPES,
SDP_ATTR_INVALID
} sdp_attr_e;
/* This is here so that it can be used in the VcmSIPCCBinding interface */
typedef enum {
SDP_SETUP_NOT_FOUND = -1,

View File

@ -338,6 +338,8 @@ typedef struct vcm_attrs_t_ {
cc_boolean mute;
cc_boolean is_video;
cc_boolean rtcp_mux;
cc_boolean audio_level;
uint8_t audio_level_id;
vcm_audioAttrs_t audio; /**< audio line attribs */
vcm_videoAttrs_t video; /**< Video Atrribs */
uint32_t bundle_level; /**< Where bundle transport info lives, if any */

View File

@ -191,6 +191,14 @@ class SdpTest : public ::testing::Test {
type), SDP_SUCCESS);
return inst_num;
}
u16 AddNewExtMap(int level, const char* uri) {
u16 inst_num = 0;
EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_EXTMAP,
&inst_num), SDP_SUCCESS);
EXPECT_EQ(sdp_attr_set_extmap(sdp_ptr_, level, inst_num,
uri, inst_num), SDP_SUCCESS);
return inst_num;
}
u16 AddNewFmtpMaxFs(int level, u32 max_fs) {
u16 inst_num = 0;
@ -725,6 +733,23 @@ TEST_F(SdpTest, parseRtcpFbAllPayloads) {
SDP_RTCP_FB_ACK_RPSI);
}
}
TEST_F(SdpTest, addExtMap) {
InitLocalSdp();
int level = AddNewMedia(SDP_MEDIA_VIDEO);
AddNewExtMap(level, SDP_EXTMAP_AUDIO_LEVEL);
std::string body = SerializeSdp();
ASSERT_NE(body.find("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"), std::string::npos);
}
TEST_F(SdpTest, parseExtMap) {
ParseSdp(kVideoSdp +
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n");
ASSERT_STREQ(sdp_attr_get_extmap_uri(sdp_ptr_, 1, 1),
SDP_EXTMAP_AUDIO_LEVEL);
ASSERT_EQ(sdp_attr_get_extmap_id(sdp_ptr_, 1, 1),
1);
}
TEST_F(SdpTest, parseFmtpMaxFs) {
u32 val = 0;
@ -732,7 +757,6 @@ TEST_F(SdpTest, parseFmtpMaxFs) {
ASSERT_EQ(sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS);
ASSERT_EQ(val, 300);
}
TEST_F(SdpTest, parseFmtpMaxFr) {
u32 val = 0;
ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n");

View File

@ -275,7 +275,7 @@
<h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
<h1 id="et_fileNotFound">&fileNotFound.title;</h1>
<h1 id="et_malformedURI">&malformedURI.title;</h1>
<h1 id="et_protocolNotFound">&protocolNotFound.title;</h1>
<h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
<h1 id="et_connectionFailure">&connectionFailure.title;</h1>
<h1 id="et_netTimeout">&netTimeout.title;</h1>
<h1 id="et_redirectLoop">&redirectLoop.title;</h1>
@ -304,7 +304,7 @@
<div id="ed_dnsNotFound">&dnsNotFound.longDesc2;</div>
<div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
<div id="ed_malformedURI">&malformedURI.longDesc;</div>
<div id="ed_protocolNotFound">&protocolNotFound.longDesc;</div>
<div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
<div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
<div id="ed_netTimeout">&netTimeout.longDesc;</div>
<div id="ed_redirectLoop">&redirectLoop.longDesc;</div>

View File

@ -5,7 +5,7 @@
malformedURI=The URL is not valid and cannot be loaded.
fileNotFound=Firefox can't find the file at %S.
dnsNotFound=Firefox can't find the server at %S.
protocolNotFound=Firefox doesn't know how to open this address, because the protocol (%S) isn't associated with any program.
unknownProtocolFound=Firefox doesn't know how to open this address, because one of the following protocols (%S) isn't associated with any program or is not allowed in this context.
connectionFailure=Firefox can't establish a connection to the server at %S.
netInterrupt=The connection to %S was interrupted while the page was loading.
netTimeout=The server at %S is taking too long to respond.

View File

@ -83,8 +83,8 @@
<!ENTITY netTimeout.title "The connection has timed out">
<!ENTITY netTimeout.longDesc "&sharedLongDesc2;">
<!ENTITY protocolNotFound.title "The address wasn't understood">
<!ENTITY protocolNotFound.longDesc "
<!ENTITY unknownProtocolFound.title "The address wasn't understood">
<!ENTITY unknownProtocolFound.longDesc "
<ul>
<li>You might need to install other software to open this address.</li>
</ul>

View File

@ -616,8 +616,6 @@ pref("print.use_global_printsettings", true);
// Save the Printings after each print job
pref("print.save_print_settings", true);
pref("print.whileInPrintPreview", true);
// Cache old Presentation when going into Print Preview
pref("print.always_cache_old_pres", false);

View File

@ -6938,7 +6938,7 @@ club
voting
// TOKYO : 2013-11-13 GMO Registry, Inc.
TOKYO
tokyo
// moe : 2013-11-13 Interlink Co., Ltd.
moe

View File

@ -2,7 +2,6 @@
<html>
<head>
<title>Test for view source</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
@ -25,38 +24,26 @@
return xhr.responseText;
}
// Start a "view source" test using the document at "testUrl". Note that
// the test will actually complete asynchronously.
function startViewSourceTest(testUrl) {
// We will "view" the source of the document in an IFRAME.
// If everything is working correctly, the "source" will simply be the
// text content of the IFRAME document's top element.
// We will "view" the source of the document in a new window.
// If everything is working correctly, the "source" will simply be the
// text content of the new window's document's body element.
// We have to use a window as view-source: is only allowed in top level,
// see bug 624883.
// Create the IFRAME.
var iframe = document.createElement('iframe');
iframe.src = "view-source:" + testUrl;
// Open the new window.
var windowWithSource = window.open("about:blank");
// The actual test will be carried out inside the IFRAME's onload handler.
iframe.onload = function () {
// The actual test will be carried out inside the window's onload handler.
windowWithSource.onload = function () {
var apparentSource = this.document.body.textContent;
var actualSource = fetch(location.href);
is(apparentSource, actualSource, "Sources should match");
var apparentSource = this.contentDocument.body.textContent;
var actualSource = fetch(testUrl);
// OK, verify that the apparent source and the actual source agree.
ok(apparentSource == actualSource, "Sources should match");
SimpleTest.finish();
}
// Our test IFRAME is ready to go. However, it will not load until we
// actually append it to the document. Do that now. Note that the test
// itself will run asynchronously some time after this function returns.
document.body.appendChild(iframe);
windowWithSource.close()
SimpleTest.finish();
}
// Start a test using this document as the test document.
startViewSourceTest(document.location.href);
windowWithSource.location.href = "view-source:" + location.href;
</script>
</body>

View File

@ -93,6 +93,24 @@ insertErrorIntoVerifyLog(CERTCertificate* cert, const PRErrorCode err,
}
#endif
SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
PRBool* chainOK)
{
*chainOK = PR_FALSE;
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Inside the Callback \n"));
// On sanity failure we fail closed.
if (!certList) {
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Short circuit, callback, "
"sanity check failed \n"));
PR_SetError(PR_INVALID_STATE_ERROR, 0);
return SECFailure;
}
*chainOK = PR_TRUE;
return SECSuccess;
}
static SECStatus
ClassicVerifyCert(CERTCertificate* cert,
const SECCertificateUsage usage,
@ -300,7 +318,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp + 1;
const bool localOnly = flags & FLAG_LOCAL_ONLY;
CERTValInParam cvin[6];
CERTValInParam cvin[7];
// Parameters for both EV and DV validation
cvin[0].type = cert_pi_useAIACertFetch;
@ -310,6 +328,16 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
cvin[2].type = cert_pi_date;
cvin[2].value.scalar.time = time;
i = 3;
CERTChainVerifyCallback callbackContainer;
if (usage == certificateUsageSSLServer) {
callbackContainer.isChainValid = chainValidationCallback;
callbackContainer.isChainValidArg = nullptr;
cvin[i].type = cert_pi_chainVerifyCallback;
cvin[i].value.pointer.chainVerifyCallback = &callbackContainer;
++i;
}
const size_t evParamLocation = i;
if (evPolicy != SEC_OID_UNKNOWN) {

View File

@ -1,6 +1,7 @@
/* 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/. */
"use strict";
Components.utils.import("resource://gre/modules/Services.jsm");
@ -160,6 +161,48 @@ SpecialPowersObserverAPI.prototype = {
return Services.io.newURI(url, null, null);
},
_readUrlAsString: function(aUrl) {
// Fetch script content as we can't use scriptloader's loadSubScript
// to evaluate http:// urls...
var scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
.getService(Ci.nsIScriptableInputStream);
var channel = Services.io.newChannel(aUrl, null, null);
var input = channel.open();
scriptableStream.init(input);
var str;
var buffer = [];
while ((str = scriptableStream.read(4096))) {
buffer.push(str);
}
var output = buffer.join("");
scriptableStream.close();
input.close();
var status;
try {
channel.QueryInterface(Ci.nsIHttpChannel);
status = channel.responseStatus;
} catch(e) {
/* The channel is not a nsIHttpCHannel, but that's fine */
dump("-*- _readUrlAsString: Got an error while fetching " +
"chrome script '" + aUrl + "': (" + e.name + ") " + e.message + ". " +
"Ignoring.\n");
}
if (status == 404) {
throw new SpecialPowersException(
"Error while executing chrome script '" + aUrl + "':\n" +
"The script doesn't exists. Ensure you have registered it in " +
"'support-files' in your mochitest.ini.");
}
return output;
},
/**
* messageManager callback function
* This will get requests from our API in the window and process them in chrome for it
@ -300,16 +343,7 @@ SpecialPowersObserverAPI.prototype = {
var url = aMessage.json.url;
var id = aMessage.json.id;
// Fetch script content as we can't use scriptloader's loadSubScript
// to evaluate http:// urls...
var scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
.getService(Ci.nsIScriptableInputStream);
var channel = Services.io.newChannel(url, null, null);
var input = channel.open();
scriptableStream.init(input);
var jsScript = scriptableStream.read(input.available());
scriptableStream.close();
input.close();
var jsScript = this._readUrlAsString(url);
// Setup a chrome sandbox that has access to sendAsyncMessage
// and addMessageListener in order to communicate with
@ -332,7 +366,8 @@ SpecialPowersObserverAPI.prototype = {
Components.utils.evalInSandbox(jsScript, sb, "1.8", url, 1);
} catch(e) {
throw new SpecialPowersException("Error while executing chrome " +
"script '" + url + "':\n" + e);
"script '" + url + "':\n" + e + "\n" +
e.fileName + ":" + e.lineNumber);
}
return undefined; // See comment at the beginning of this function.

View File

@ -9,14 +9,24 @@ const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/Sqlite.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
"resource://gre/modules/PlacesBackups.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
"resource://gre/modules/Deprecated.jsm");
XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => new TextDecoder());
XPCOMUtils.defineLazyGetter(this, "gTextEncoder", () => new TextEncoder());
XPCOMUtils.defineLazyGetter(this, "localFileCtor",
() => Components.Constructor("@mozilla.org/file/local;1",
"nsILocalFile", "initWithPath"));
this.BookmarkJSONUtils = Object.freeze({
/**
@ -41,8 +51,8 @@ this.BookmarkJSONUtils = Object.freeze({
* @note any item annotated with "places/excludeFromBackup" won't be removed
* before executing the restore.
*
* @param aFile
* nsIFile of bookmarks in JSON format to be restored.
* @param aFilePath
* OS.File path or nsIFile of bookmarks in JSON format to be restored.
* @param aReplace
* Boolean if true, replace existing bookmarks, else merge.
*
@ -50,24 +60,51 @@ this.BookmarkJSONUtils = Object.freeze({
* @resolves When the new bookmarks have been created.
* @rejects JavaScript exception.
*/
importFromFile: function BJU_importFromFile(aFile, aReplace) {
importFromFile: function BJU_importFromFile(aFilePath, aReplace) {
let importer = new BookmarkImporter();
return importer.importFromFile(aFile, aReplace);
// TODO (bug 967192): convert to pure OS.File
let file = aFilePath instanceof Ci.nsIFile ? aFilePath
: new localFileCtor(aFilePath);
return importer.importFromFile(file, aReplace);
},
/**
* Serializes bookmarks using JSON, and writes to the supplied file.
* Serializes bookmarks using JSON, and writes to the supplied file path.
*
* @param aLocalFile
* nsIFile for the "bookmarks.json" file to be created.
* @param aFilePath
* OS.File path for the "bookmarks.json" file to be created.
*
* @return {Promise}
* @resolves When the file has been created.
* @resolves To the exported bookmarks count when the file has been created.
* @rejects JavaScript exception.
* @deprecated passing an nsIFile is deprecated
*/
exportToFile: function BJU_exportToFile(aLocalFile) {
let exporter = new BookmarkExporter();
return exporter.exportToFile(aLocalFile);
exportToFile: function BJU_exportToFile(aFilePath) {
if (aFilePath instanceof Ci.nsIFile) {
Deprecated.warning("Passing an nsIFile to BookmarksJSONUtils.exportToFile " +
"is deprecated. Please use an OS.File path instead.",
"https://developer.mozilla.org/docs/JavaScript_OS.File");
aFilePath = aFilePath.path;
}
return Task.spawn(function* () {
let [bookmarks, count] = yield PlacesBackups.getBookmarksTree();
let startTime = Date.now();
let jsonString = JSON.stringify(bookmarks);
// Report the time taken to convert the tree to JSON.
try {
Services.telemetry
.getHistogramById("PLACES_BACKUPS_TOJSON_MS")
.add(Date.now() - startTime);
} catch (ex) {
Components.utils.reportError("Unable to report telemetry.");
}
// Write to the temp folder first, to avoid leaving back partial files.
let tmpPath = OS.Path.join(OS.Constants.Path.tmpDir,
OS.Path.basename(aFilePath) + ".tmp");
yield OS.File.writeAtomic(aFilePath, jsonString, { tmpPath: tmpPath });
return count;
});
},
/**
@ -362,7 +399,8 @@ BookmarkImporter.prototype = {
});
return [folderIdMap, searchIds];
}
} else if (aData.livemark && aData.annos) {
} else if (aData.annos &&
aData.annos.some(anno => anno.name == PlacesUtils.LMANNO_FEEDURI)) {
// Node is a livemark
let feedURI = null;
let siteURI = null;
@ -422,7 +460,8 @@ BookmarkImporter.prototype = {
if (aData.keyword)
PlacesUtils.bookmarks.setKeywordForBookmark(id, aData.keyword);
if (aData.tags) {
let tags = aData.tags.split(", ");
// TODO (bug 967196) the tagging service should trim by itself.
let tags = aData.tags.split(",").map(tag => tag.trim());
if (tags.length)
PlacesUtils.tagging.tagURI(NetUtil.newURI(aData.uri), tags);
}
@ -502,306 +541,6 @@ function fixupQuery(aQueryURI, aFolderIdMap) {
return NetUtil.newURI(stringURI);
}
function BookmarkExporter() {}
BookmarkExporter.prototype = {
exportToFile: function BE_exportToFile(aLocalFile) {
return Task.spawn(this._writeToFile(aLocalFile));
},
_converterOut: null,
_writeToFile: function BE__writeToFile(aLocalFile) {
// Create a file that can be accessed by the current user only.
let safeFileOut = Cc["@mozilla.org/network/safe-file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
safeFileOut.init(aLocalFile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
FileUtils.MODE_TRUNCATE, parseInt("0600", 8), 0);
let nodeCount;
try {
// We need a buffered output stream for performance. See bug 202477.
let bufferedOut = Cc["@mozilla.org/network/buffered-output-stream;1"].
createInstance(Ci.nsIBufferedOutputStream);
bufferedOut.init(safeFileOut, 4096);
try {
// Write bookmarks in UTF-8.
this._converterOut = Cc["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Ci.nsIConverterOutputStream);
this._converterOut.init(bufferedOut, "utf-8", 0, 0);
try {
nodeCount = yield this._writeContentToFile();
// Flush the buffer and retain the target file on success only.
bufferedOut.QueryInterface(Ci.nsISafeOutputStream).finish();
} finally {
this._converterOut.close();
this._converterOut = null;
}
} finally {
bufferedOut.close();
}
} finally {
safeFileOut.close();
}
throw new Task.Result(nodeCount);
},
_writeContentToFile: function BE__writeContentToFile() {
return Task.spawn(function() {
// Weep over stream interface variance.
let streamProxy = {
converter: this._converterOut,
write: function(aData, aLen) {
this.converter.writeString(aData);
}
};
// Get list of itemIds that must be excluded from the backup.
let excludeItems = PlacesUtils.annotations.getItemsWithAnnotation(
PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
// Serialize to JSON and write to stream.
let nodeCount = yield BookmarkRow.serializeJSONToOutputStream(streamProxy,
excludeItems);
throw new Task.Result(nodeCount);
}.bind(this));
}
}
let BookmarkRow = {
/**
* Serializes the SQL results as JSON with async SQL call and writes the
* serialization to the given output stream.
*
* @param aStream
* An nsIOutputStream. NOTE: it only uses the write(str, len)
* method of nsIOutputStream. The caller is responsible for
* closing the stream.
* @param aExcludeItems
* An array of item ids that should not be written to the backup.
* @return {Promise}
* @resolves the number of serialized uri nodes.
*/
serializeJSONToOutputStream: function(aStream, aExcludeItems) {
return Task.spawn(function() {
let nodes = [];
let nodeCount = 0;
let dbFilePath = OS.Path.join(OS.Constants.Path.profileDir,
"places.sqlite");
let conn = yield Sqlite.openConnection({ path: dbFilePath,
sharedMemoryCache: false });
try {
let rows = yield conn.execute(
"SELECT b.id, h.url, b.position, b.title, b.parent, " +
"b.type, b.dateAdded, b.lastModified, b.guid, t.parent AS grandParent " +
"FROM moz_bookmarks b " +
"LEFT JOIN moz_bookmarks t ON t.id = b.parent " +
"LEFT JOIN moz_places h ON h.id = b.fk " +
"ORDER BY b.parent, b.position, b.id");
// Create a Map for lookup.
let rowMap = new Map();
for (let row of rows) {
let parent = row.getResultByName("parent");
if (rowMap.has(parent)) {
let data = rowMap.get(parent);
data.children.push(row);
} else {
rowMap.set(parent, { children: [row] });
}
}
let root = rowMap.get(0);
if (!root) {
throw new Error("Root does not exist.");
}
let result = yield BookmarkRow._appendConvertedNode(root.children[0],
rowMap,
nodes,
aExcludeItems);
if (result.appendedNode) {
nodeCount = result.nodeCount;
let json = JSON.stringify(nodes[0]);
aStream.write(json, json.length);
}
} catch(e) {
Cu.reportError("serializeJSONToOutputStream error " + e);
} finally {
yield conn.close();
}
throw new Task.Result(nodeCount);
});
},
_appendConvertedNode: function BR__appendConvertedNode(
aRow, aRowMap, aNodes, aExcludeItems) {
return Task.spawn(function() {
let node = {};
let nodeCount = 0;
this._addGenericProperties(aRow, node);
let parent = aRow.getResultByName("parent");
let grandParent = parent ? aRow.getResultByName("grandParent") : null;
let type = aRow.getResultByName("type");
if (type == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
// Tag root accept only folder nodes
if (parent == PlacesUtils.tagsFolderId)
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
// Check for url validity, since we can't halt while writing a backup.
// This will throw if we try to serialize an invalid url and it does
// not make sense saving a wrong or corrupt uri node.
try {
NetUtil.newURI(aRow.getResultByName("url"));
} catch (ex) {
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
}
yield this._addURIProperties(aRow, node);
nodeCount++;
} else if (type == Ci.nsINavBookmarksService.TYPE_FOLDER) {
// Tag containers accept only uri nodes
if (grandParent && grandParent == PlacesUtils.tagsFolderId) {
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
}
this._addContainerProperties(aRow, node);
} else if (type == Ci.nsINavBookmarksService.TYPE_SEPARATOR) {
// Tag root accept only folder nodes
// Tag containers accept only uri nodes
if ((parent == PlacesUtils.tagsFolderId) ||
(grandParent == PlacesUtils.tagsFolderId)) {
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
}
this._addSeparatorProperties(aRow, node);
}
if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
nodeCount += yield this._appendConvertedComplexNode(node,
aNodes,
aRowMap,
aExcludeItems);
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
}
aNodes.push(node);
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
}.bind(this));
},
_addGenericProperties: function BR__addGenericProperties(aRow, aJSNode) {
let title = aRow.getResultByName("title")
aJSNode.title = title ? title : "";
aJSNode.guid = aRow.getResultByName("guid");
aJSNode.id = aRow.getResultByName("id");
aJSNode.index = aRow.getResultByName("position");
if (aJSNode.id != -1) {
let parent = aRow.getResultByName("parent");
if (parent)
aJSNode.parent = parent;
let dateAdded = aRow.getResultByName("dateAdded");;
if (dateAdded)
aJSNode.dateAdded = dateAdded;
let lastModified = aRow.getResultByName("lastModified");
if (lastModified)
aJSNode.lastModified = lastModified;
// XXX need a hasAnnos api
let annos = [];
try {
annos =
PlacesUtils.getAnnotationsForItem(aJSNode.id).filter(function(anno) {
// XXX should whitelist this instead, w/ a pref for
// backup/restore of non-whitelisted annos
// XXX causes JSON encoding errors, so utf-8 encode
// anno.value = unescape(encodeURIComponent(anno.value));
if (anno.name == PlacesUtils.LMANNO_FEEDURI)
aJSNode.livemark = 1;
return true;
});
} catch(ex) {}
if (annos.length != 0)
aJSNode.annos = annos;
}
// XXXdietrich - store annos for non-bookmark items
},
_addURIProperties: function BR__addURIProperties(aRow, aJSNode) {
return Task.spawn(function() {
aJSNode.type = PlacesUtils.TYPE_X_MOZ_PLACE;
aJSNode.uri = aRow.getResultByName("url");
if (aJSNode.id && aJSNode.id != -1) {
// Harvest bookmark-specific properties
let keyword = PlacesUtils.bookmarks.getKeywordForBookmark(aJSNode.id);
if (keyword)
aJSNode.keyword = keyword;
}
// Last character-set
let uri = NetUtil.newURI(aRow.getResultByName("url"));
let lastCharset = yield PlacesUtils.getCharsetForURI(uri)
if (lastCharset)
aJSNode.charset = lastCharset;
});
},
_addSeparatorProperties: function BR__addSeparatorProperties(aRow, aJSNode) {
aJSNode.type = PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR;
},
_addContainerProperties: function BR__addContainerProperties(aRow, aJSNode) {
// This is a bookmark or a tag container.
// Bookmark folder or a shortcut we should convert to folder.
aJSNode.type = PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER;
// Mark root folders
let itemId = aRow.getResultByName("id");
if (itemId == PlacesUtils.placesRootId)
aJSNode.root = "placesRoot";
else if (itemId == PlacesUtils.bookmarksMenuFolderId)
aJSNode.root = "bookmarksMenuFolder";
else if (itemId == PlacesUtils.tagsFolderId)
aJSNode.root = "tagsFolder";
else if (itemId == PlacesUtils.unfiledBookmarksFolderId)
aJSNode.root = "unfiledBookmarksFolder";
else if (itemId == PlacesUtils.toolbarFolderId)
aJSNode.root = "toolbarFolder";
},
_appendConvertedComplexNode: function BR__appendConvertedComplexNode(
aNode, aNodes, aRowMap, aExcludeItems) {
return Task.spawn(function() {
let repr = {};
let nodeCount = 0;
for (let [name, value] in Iterator(aNode))
repr[name] = value;
repr.children = [];
let data = aRowMap.get(aNode.id);
if (data) {
for (let row of data.children) {
let id = row.getResultByName("id");
// ignore exclude items
if (aExcludeItems && aExcludeItems.indexOf(id) != -1) {
continue;
}
let result = yield this._appendConvertedNode(row,
aRowMap,
repr.children,
aExcludeItems);
nodeCount += result.nodeCount;
}
} else {
Cu.reportError("_appendConvertedComplexNode error: Unable to find node");
}
aNodes.push(repr);
throw new Task.Result(nodeCount);
}.bind(this));
}
}
let BookmarkNode = {
/**
* Serializes the given node (and all its descendents) as JSON
@ -826,7 +565,7 @@ let BookmarkNode = {
serializeAsJSONToOutputStream: function BN_serializeAsJSONToOutputStream(
aNode, aStream, aIsUICommand, aResolveShortcuts, aExcludeItems) {
return Task.spawn(function() {
return Task.spawn(function* () {
// Serialize to stream
let array = [];
let result = yield this._appendConvertedNode(aNode, null, array,
@ -834,18 +573,18 @@ let BookmarkNode = {
aResolveShortcuts,
aExcludeItems);
if (result.appendedNode) {
let json = JSON.stringify(array[0]);
aStream.write(json, json.length);
let jsonString = JSON.stringify(array[0]);
aStream.write(jsonString, jsonString.length);
} else {
throw Cr.NS_ERROR_UNEXPECTED;
}
throw new Task.Result(result.nodeCount);
return result.nodeCount;
}.bind(this));
},
_appendConvertedNode: function BN__appendConvertedNode(
bNode, aIndex, aArray, aIsUICommand, aResolveShortcuts, aExcludeItems) {
return Task.spawn(function() {
return Task.spawn(function* () {
let node = {};
let nodeCount = 0;
@ -863,7 +602,7 @@ let BookmarkNode = {
if (PlacesUtils.nodeIsURI(bNode)) {
// Tag root accept only folder nodes
if (parent && parent.itemId == PlacesUtils.tagsFolderId)
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
return { appendedNode: false, nodeCount: nodeCount };
// Check for url validity, since we can't halt while writing a backup.
// This will throw if we try to serialize an invalid url and it does
@ -871,7 +610,7 @@ let BookmarkNode = {
try {
NetUtil.newURI(bNode.uri);
} catch (ex) {
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
return { appendedNode: false, nodeCount: nodeCount };
}
yield this._addURIProperties(bNode, node, aIsUICommand);
@ -879,7 +618,7 @@ let BookmarkNode = {
} else if (PlacesUtils.nodeIsContainer(bNode)) {
// Tag containers accept only uri nodes
if (grandParent && grandParent.itemId == PlacesUtils.tagsFolderId)
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
return { appendedNode: false, nodeCount: nodeCount };
this._addContainerProperties(bNode, node, aIsUICommand,
aResolveShortcuts);
@ -888,7 +627,7 @@ let BookmarkNode = {
// Tag containers accept only uri nodes
if ((parent && parent.itemId == PlacesUtils.tagsFolderId) ||
(grandParent && grandParent.itemId == PlacesUtils.tagsFolderId))
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
return { appendedNode: false, nodeCount: nodeCount };
this._addSeparatorProperties(bNode, node);
}
@ -900,11 +639,11 @@ let BookmarkNode = {
aIsUICommand,
aResolveShortcuts,
aExcludeItems)
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
return { appendedNode: true, nodeCount: nodeCount };
}
aArray.push(node);
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
return { appendedNode: true, nodeCount: nodeCount };
}.bind(this));
},
@ -932,8 +671,6 @@ let BookmarkNode = {
// backup/restore of non-whitelisted annos
// XXX causes JSON encoding errors, so utf-8 encode
// anno.value = unescape(encodeURIComponent(anno.value));
if (anno.name == PlacesUtils.LMANNO_FEEDURI)
aJSNode.livemark = 1;
if (anno.name == PlacesUtils.READ_ONLY_ANNO && aResolveShortcuts) {
// When copying a read-only node, remove the read-only annotation.
return false;
@ -1014,7 +751,7 @@ let BookmarkNode = {
_appendConvertedComplexNode: function BN__appendConvertedComplexNode(
aNode, aSourceNode, aArray, aIsUICommand, aResolveShortcuts,
aExcludeItems) {
return Task.spawn(function() {
return Task.spawn(function* () {
let repr = {};
let nodeCount = 0;
@ -1023,7 +760,8 @@ let BookmarkNode = {
// Write child nodes
let children = repr.children = [];
if (!aNode.livemark) {
if (!aNode.annos ||
!aNode.annos.some(anno => anno.name == PlacesUtils.LMANNO_FEEDURI)) {
PlacesUtils.asContainer(aSourceNode);
let wasOpen = aSourceNode.containerOpen;
if (!wasOpen)
@ -1043,7 +781,7 @@ let BookmarkNode = {
}
aArray.push(repr);
throw new Task.Result(nodeCount);
return nodeCount;
}.bind(this));
}
}
}

View File

@ -17,11 +17,16 @@ Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Deprecated.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
"resource://gre/modules/Sqlite.jsm");
XPCOMUtils.defineLazyGetter(this, "localFileCtor",
() => Components.Constructor("@mozilla.org/file/local;1",
"nsILocalFile", "initWithPath"));
this.PlacesBackups = {
get _filenamesRegex() {
@ -40,7 +45,14 @@ this.PlacesBackups = {
Deprecated.warning(
"PlacesBackups.folder is deprecated and will be removed in a future version",
"https://bugzilla.mozilla.org/show_bug.cgi?id=859695");
return this._folder;
},
/**
* This exists just to avoid spamming deprecate warnings from internal calls
* needed to support deprecated methods themselves.
*/
get _folder() {
let bookmarksBackupDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
bookmarksBackupDir.append(this.profileRelativeFolderPath);
if (!bookmarksBackupDir.exists()) {
@ -48,8 +60,8 @@ this.PlacesBackups = {
if (!bookmarksBackupDir.exists())
throw("Unable to create bookmarks backup folder");
}
delete this.folder;
return this.folder = bookmarksBackupDir;
delete this._folder;
return this._folder = bookmarksBackupDir;
},
/**
@ -58,21 +70,15 @@ this.PlacesBackups = {
* @resolve the folder (the folder string path).
*/
getBackupFolder: function PB_getBackupFolder() {
return Task.spawn(function() {
if (this._folder) {
throw new Task.Result(this._folder);
return Task.spawn(function* () {
if (this._backupFolder) {
return this._backupFolder;
}
let profileDir = OS.Constants.Path.profileDir;
let backupsDirPath = OS.Path.join(profileDir, this.profileRelativeFolderPath);
yield OS.File.makeDir(backupsDirPath, { ignoreExisting: true }).then(
function onSuccess() {
this._folder = backupsDirPath;
}.bind(this),
function onError() {
throw("Unable to create bookmarks backup folder");
});
throw new Task.Result(this._folder);
}.bind(this));
yield OS.File.makeDir(backupsDirPath, { ignoreExisting: true });
return this._backupFolder = backupsDirPath;
}.bind(this));
},
get profileRelativeFolderPath() "bookmarkbackups",
@ -84,10 +90,17 @@ this.PlacesBackups = {
Deprecated.warning(
"PlacesBackups.entries is deprecated and will be removed in a future version",
"https://bugzilla.mozilla.org/show_bug.cgi?id=859695");
return this._entries;
},
delete this.entries;
this.entries = [];
let files = this.folder.directoryEntries;
/**
* This exists just to avoid spamming deprecate warnings from internal calls
* needed to support deprecated methods themselves.
*/
get _entries() {
delete this._entries;
this._entries = [];
let files = this._folder.directoryEntries;
while (files.hasMoreElements()) {
let entry = files.getNext().QueryInterface(Ci.nsIFile);
// A valid backup is any file that matches either the localized or
@ -99,15 +112,15 @@ this.PlacesBackups = {
entry.remove(false);
continue;
}
this.entries.push(entry);
this._entries.push(entry);
}
}
this.entries.sort((a, b) => {
this._entries.sort((a, b) => {
let aDate = this.getDateForFile(a);
let bDate = this.getDateForFile(b);
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
});
return this.entries;
return this._entries;
},
/**
@ -116,10 +129,10 @@ this.PlacesBackups = {
* @resolve a sorted array of string paths.
*/
getBackupFiles: function PB_getBackupFiles() {
return Task.spawn(function() {
if (this._backupFiles) {
throw new Task.Result(this._backupFiles);
}
return Task.spawn(function* () {
if (this._backupFiles)
return this._backupFiles;
this._backupFiles = [];
let backupFolderPath = yield this.getBackupFolder();
@ -138,13 +151,13 @@ this.PlacesBackups = {
}.bind(this));
iterator.close();
this._backupFiles.sort(function(a, b) {
this._backupFiles.sort((a, b) => {
let aDate = this.getDateForFile(a);
let bDate = this.getDateForFile(b);
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
}.bind(this));
});
throw new Task.Result(this._backupFiles);
return this._backupFiles;
}.bind(this));
},
@ -194,10 +207,10 @@ this.PlacesBackups = {
"https://bugzilla.mozilla.org/show_bug.cgi?id=859695");
let fileExt = aFileExt || "(json|html)";
for (let i = 0; i < this.entries.length; i++) {
for (let i = 0; i < this._entries.length; i++) {
let rx = new RegExp("\." + fileExt + "$");
if (this.entries[i].leafName.match(rx))
return this.entries[i];
if (this._entries[i].leafName.match(rx))
return this._entries[i];
}
return null;
},
@ -212,16 +225,16 @@ this.PlacesBackups = {
* @result the path to the file.
*/
getMostRecentBackup: function PB_getMostRecentBackup(aFileExt) {
return Task.spawn(function() {
return Task.spawn(function* () {
let fileExt = aFileExt || "(json|html)";
let entries = yield this.getBackupFiles();
for (let entry of entries) {
let rx = new RegExp("\." + fileExt + "$");
if (OS.Path.basename(entry).match(rx)) {
throw new Task.Result(entry);
return entry;
}
}
throw new Task.Result(null);
return null;
}.bind(this));
},
@ -230,23 +243,31 @@ this.PlacesBackups = {
* Note: any item that should not be backed up must be annotated with
* "places/excludeFromBackup".
*
* @param aFile
* nsIFile where to save JSON backup.
* @param aFilePath
* OS.File path for the "bookmarks.json" file to be created.
* @return {Promise}
* @resolves the number of serialized uri nodes.
* @deprecated passing an nsIFile is deprecated
*/
saveBookmarksToJSONFile: function PB_saveBookmarksToJSONFile(aFile) {
return Task.spawn(function() {
let nodeCount = yield BookmarkJSONUtils.exportToFile(aFile);
saveBookmarksToJSONFile: function PB_saveBookmarksToJSONFile(aFilePath) {
if (aFilePath instanceof Ci.nsIFile) {
Deprecated.warning("Passing an nsIFile to PlacesBackups.saveBookmarksToJSONFile " +
"is deprecated. Please use an OS.File path instead.",
"https://developer.mozilla.org/docs/JavaScript_OS.File");
aFilePath = aFilePath.path;
}
return Task.spawn(function* () {
let nodeCount = yield BookmarkJSONUtils.exportToFile(aFilePath);
let backupFolderPath = yield this.getBackupFolder();
if (aFile.parent.path == backupFolderPath) {
// Update internal cache.
this.entries.push(aFile);
if (OS.Path.dirname(aFilePath) == backupFolderPath) {
// We are creating a backup in the default backups folder,
// so just update the internal cache.
this._entries.unshift(new localFileCtor(aFilePath));
if (!this._backupFiles) {
yield this.getBackupFiles();
}
this._backupFiles.push(aFile.path);
this._backupFiles.unshift(aFilePath);
} else {
// If we are saving to a folder different than our backups folder, then
// we also want to copy this new backup to it.
@ -257,24 +278,20 @@ this.PlacesBackups = {
{ nodeCount: nodeCount });
let newFilePath = OS.Path.join(backupFolderPath, newFilename);
let backupFile = yield this._getBackupFileForSameDate(name);
if (backupFile) {
yield OS.File.remove(backupFile, { ignoreAbsent: true });
} else {
let file = new FileUtils.File(newFilePath);
if (!backupFile) {
// Update internal cache if we are not replacing an existing
// backup file.
this.entries.push(file);
this._entries.unshift(new localFileCtor(newFilePath));
if (!this._backupFiles) {
yield this.getBackupFiles();
}
this._backupFiles.push(file.path);
this._backupFiles.unshift(newFilePath);
}
yield OS.File.copy(aFile.path, newFilePath);
yield OS.File.copy(aFilePath, newFilePath);
}
throw new Task.Result(nodeCount);
return nodeCount;
}.bind(this));
},
@ -292,7 +309,7 @@ this.PlacesBackups = {
* @return {Promise}
*/
create: function PB_create(aMaxBackups, aForceBackup) {
return Task.spawn(function() {
return Task.spawn(function* () {
// Construct the new leafname.
let newBackupFilename = this.getFilenameForDate();
let mostRecentBackupFile = yield this.getMostRecentBackup();
@ -314,7 +331,7 @@ this.PlacesBackups = {
numberOfBackupsToDelete++;
while (numberOfBackupsToDelete--) {
this.entries.pop();
this._entries.pop();
if (!this._backupFiles) {
yield this.getBackupFiles();
}
@ -341,25 +358,22 @@ this.PlacesBackups = {
}
// Save bookmarks to a backup file.
let backupFolderPath = yield this.getBackupFolder();
let backupFolder = new FileUtils.File(backupFolderPath);
let newBackupFile = backupFolder.clone();
newBackupFile.append(newBackupFilename);
let backupFolder = yield this.getBackupFolder();
let newBackupFile = OS.Path.join(backupFolder, newBackupFilename);
let nodeCount = yield this.saveBookmarksToJSONFile(newBackupFile);
// Rename the filename with metadata.
let newFilenameWithMetaData = this._appendMetaDataToFilename(
newBackupFilename,
{ nodeCount: nodeCount });
newBackupFile.moveTo(backupFolder, newFilenameWithMetaData);
let newBackupFileWithMetadata = OS.Path.join(backupFolder, newFilenameWithMetaData);
yield OS.File.move(newBackupFile, newBackupFileWithMetadata);
// Update internal cache.
let newFileWithMetaData = backupFolder.clone();
newFileWithMetaData.append(newFilenameWithMetaData);
this.entries.pop();
this.entries.push(newFileWithMetaData);
let newFileWithMetaData = new localFileCtor(newBackupFileWithMetadata);
this._entries.pop();
this._entries.unshift(newFileWithMetaData);
this._backupFiles.pop();
this._backupFiles.push(newFileWithMetaData.path);
this._backupFiles.unshift(newBackupFileWithMetadata);
}.bind(this));
},
@ -400,22 +414,216 @@ this.PlacesBackups = {
sourceMatches[4] == targetMatches[4]);
},
_getBackupFileForSameDate:
function PB__getBackupFileForSameDate(aFilename) {
return Task.spawn(function() {
let backupFolderPath = yield this.getBackupFolder();
let iterator = new OS.File.DirectoryIterator(backupFolderPath);
let backupFile;
_getBackupFileForSameDate:
function PB__getBackupFileForSameDate(aFilename) {
return Task.spawn(function* () {
let backupFolderPath = yield this.getBackupFolder();
let iterator = new OS.File.DirectoryIterator(backupFolderPath);
let backupFile;
yield iterator.forEach(function(aEntry) {
if (this._isFilenameWithSameDate(aEntry.name, aFilename)) {
backupFile = aEntry.path;
return iterator.close();
}
}.bind(this));
yield iterator.close();
yield iterator.forEach(function(aEntry) {
if (this._isFilenameWithSameDate(aEntry.name, aFilename)) {
backupFile = aEntry.path;
return iterator.close();
}
}.bind(this));
yield iterator.close();
throw new Task.Result(backupFile);
}.bind(this));
}
return backupFile;
}.bind(this));
},
/**
* Gets a bookmarks tree representation usable to create backups in different
* file formats. The root or the tree is PlacesUtils.placesRootId.
* Items annotated with PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO and all of their
* descendants are excluded.
*
* @return an object representing a tree with the places root as its root.
* Each bookmark is represented by an object having these properties:
* * id: the item id (make this not enumerable after bug 824502)
* * title: the title
* * guid: unique id
* * parent: item id of the parent folder, not enumerable
* * index: the position in the parent
* * dateAdded: microseconds from the epoch
* * lastModified: microseconds from the epoch
* * type: type of the originating node as defined in PlacesUtils
* The following properties exist only for a subset of bookmarks:
* * annos: array of annotations
* * uri: url
* * keyword: associated keyword
* * charset: last known charset
* * tags: csv string of tags
* * root: string describing whether this represents a root
* * children: array of child items in a folder
*/
getBookmarksTree: function () {
return Task.spawn(function* () {
let dbFilePath = OS.Path.join(OS.Constants.Path.profileDir, "places.sqlite");
let conn = yield Sqlite.openConnection({ path: dbFilePath,
sharedMemoryCache: false });
let rows = [];
try {
rows = yield conn.execute(
"SELECT b.id, h.url, IFNULL(b.title, '') AS title, b.parent, " +
"b.position AS [index], b.type, b.dateAdded, b.lastModified, b.guid, " +
"( SELECT GROUP_CONCAT(t.title, ',') " +
"FROM moz_bookmarks b2 " +
"JOIN moz_bookmarks t ON t.id = +b2.parent AND t.parent = :tags_folder " +
"WHERE b2.fk = h.id " +
") AS tags, " +
"EXISTS (SELECT 1 FROM moz_items_annos WHERE item_id = b.id LIMIT 1) AS has_annos, " +
"( SELECT a.content FROM moz_annos a " +
"JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id " +
"WHERE place_id = h.id AND n.name = :charset_anno " +
") AS charset " +
"FROM moz_bookmarks b " +
"LEFT JOIN moz_bookmarks p ON p.id = b.parent " +
"LEFT JOIN moz_places h ON h.id = b.fk " +
"WHERE b.id <> :tags_folder AND b.parent <> :tags_folder AND p.parent <> :tags_folder " +
"ORDER BY b.parent, b.position",
{ tags_folder: PlacesUtils.tagsFolderId,
charset_anno: PlacesUtils.CHARSET_ANNO });
} catch(e) {
Cu.reportError("Unable to query the database " + e);
} finally {
yield conn.close();
}
let startTime = Date.now();
// Create a Map for lookup and recursive building of the tree.
let itemsMap = new Map();
for (let row of rows) {
let id = row.getResultByName("id");
try {
let bookmark = sqliteRowToBookmarkObject(row);
if (itemsMap.has(id)) {
// Since children may be added before parents, we should merge with
// the existing object.
let original = itemsMap.get(id);
for (prop in bookmark) {
original[prop] = bookmark[prop];
}
bookmark = original;
}
else {
itemsMap.set(id, bookmark);
}
// Append bookmark to its parent.
if (!itemsMap.has(bookmark.parent))
itemsMap.set(bookmark.parent, {});
let parent = itemsMap.get(bookmark.parent);
if (!("children" in parent))
parent.children = [];
parent.children.push(bookmark);
} catch (e) {
Cu.reportError("Error while reading node " + id + " " + e);
}
}
// Handle excluded items, by removing entire subtrees pointed by them.
function removeFromMap(id) {
// Could have been removed by a previous call, since we can't
// predict order of items in EXCLUDE_FROM_BACKUP_ANNO.
if (itemsMap.has(id)) {
let excludedItem = itemsMap.get(id);
if (excludedItem.children) {
for (let child of excludedItem.children) {
removeFromMap(child.id);
}
}
// Remove the excluded item from its parent's children...
let parentItem = itemsMap.get(excludedItem.parent);
parentItem.children = parentItem.children.filter(aChild => aChild.id != id);
// ...then remove it from the map.
itemsMap.delete(id);
}
}
for (let id of PlacesUtils.annotations.getItemsWithAnnotation(
PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO)) {
removeFromMap(id);
}
// Report the time taken to build the tree. This doesn't take into
// account the time spent in the query since that's off the main-thread.
try {
Services.telemetry
.getHistogramById("PLACES_BACKUPS_BOOKMARKSTREE_MS")
.add(Date.now() - startTime);
} catch (ex) {
Components.utils.reportError("Unable to report telemetry.");
}
return [itemsMap.get(PlacesUtils.placesRootId), itemsMap.size];
});
}
}
/**
* Helper function to convert a Sqlite.jsm row to a bookmark object
* representation.
*
* @param aRow The Sqlite.jsm result row.
*/
function sqliteRowToBookmarkObject(aRow) {
let bookmark = {};
for (let p of [ "id" ,"guid", "title", "index", "dateAdded", "lastModified" ]) {
bookmark[p] = aRow.getResultByName(p);
}
Object.defineProperty(bookmark, "parent",
{ value: aRow.getResultByName("parent") });
let type = aRow.getResultByName("type");
// Add annotations.
if (aRow.getResultByName("has_annos")) {
try {
bookmark.annos = PlacesUtils.getAnnotationsForItem(bookmark.id);
} catch (e) {
Cu.reportError("Unexpected error while reading annotations " + e);
}
}
switch (type) {
case Ci.nsINavBookmarksService.TYPE_BOOKMARK:
// TODO: What about shortcuts?
bookmark.type = PlacesUtils.TYPE_X_MOZ_PLACE;
// This will throw if we try to serialize an invalid url and the node will
// just be skipped.
bookmark.uri = NetUtil.newURI(aRow.getResultByName("url")).spec;
// Keywords are cached, so this should be decently fast.
let keyword = PlacesUtils.bookmarks.getKeywordForBookmark(bookmark.id);
if (keyword)
bookmark.keyword = keyword;
let charset = aRow.getResultByName("charset");
if (charset)
bookmark.charset = charset;
let tags = aRow.getResultByName("tags");
if (tags)
bookmark.tags = tags;
break;
case Ci.nsINavBookmarksService.TYPE_FOLDER:
bookmark.type = PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER;
// Mark root folders.
if (bookmark.id == PlacesUtils.placesRootId)
bookmark.root = "placesRoot";
else if (bookmark.id == PlacesUtils.bookmarksMenuFolderId)
bookmark.root = "bookmarksMenuFolder";
else if (bookmark.id == PlacesUtils.unfiledBookmarksFolderId)
bookmark.root = "unfiledBookmarksFolder";
else if (bookmark.id == PlacesUtils.toolbarFolderId)
bookmark.root = "toolbarFolder";
break;
case Ci.nsINavBookmarksService.TYPE_SEPARATOR:
bookmark.type = PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR;
break;
default:
Cu.reportError("Unexpected bookmark type");
break;
}
return bookmark;
}

View File

@ -133,52 +133,6 @@
this.mPageTextBox.max = print.printPreviewNumPages;
this.updateToolbar();
// Hide the ``Print...'' button when the underlying gfx code does not
// support multiple devicecontext to be used concurrently
// (e.g. printing and printpreview at the same time; required as
// legacy support for unices.'s PostScript module
// XXX the scaling widgets, and the orientation widgets on unices.
var canPrint = true;
try
{
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
canPrint = prefs.getBoolPref("print.whileInPrintPreview");
if (!canPrint)
{
// Ask the printerfeatures database if this printer device
// supports multiple device instances which can be used
// concurrently
var smdci = prefs.getBoolPref("print.tmp.printerfeatures." +
print.currentPrintSettings.printerName +
".can_use_multiple_devicecontexts_concurrently");
// We can print from print preview when we can use multiple
// devicecontext instances in parallel (regardless what
// "print.whileInPrintPreview" may say here...)
if (smdci)
{
canPrint = true;
}
}
} catch(e) {}
if (!canPrint)
{
// hide print button
this.mPrintButton.setAttribute("hidden", "true");
// hide page setup button
document.getAnonymousNodes(this)[1].setAttribute("hidden", "true");
// hide scale widgets (indices: 8, 9, 10, 11)
// hide orient widgets (indices: 12, 13)
for (var i = 8; i <= 13; ++i)
{
document.getAnonymousNodes(this)[i].setAttribute("hidden", "true");
}
}
]]>
</constructor>

View File

@ -2844,6 +2844,30 @@
"extended_statistics_ok": true,
"description": "PLACES: Number of keywords"
},
"PLACES_BACKUPS_DAYSFROMLAST": {
"expires_in_version": "never",
"kind": "enumerated",
"n_values": 15,
"description": "PLACES: Days from last backup"
},
"PLACES_BACKUPS_BOOKMARKSTREE_MS": {
"expires_in_version": "never",
"kind": "exponential",
"low": 50,
"high": 2000,
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "PLACES: Time to build the bookmarks tree"
},
"PLACES_BACKUPS_TOJSON_MS": {
"expires_in_version": "never",
"kind": "exponential",
"low": 50,
"high": 2000,
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "PLACES: Time to convert and write the backup"
},
"FENNEC_FAVICONS_COUNT": {
"expires_in_version": "never",
"kind": "exponential",

View File

@ -4,7 +4,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<body onload="load();">
<p id="display"></p>
<iframe id="test"></iframe>
<script type="text/javascript">
@ -45,91 +45,99 @@ function make_test(param, expected) {
});
}
var iframe = document.getElementById("test");
var gCallback = null;
function run_test(test, cb) {
iframe.src = "unsafeBidiFileName.sjs?name=" + encodeURIComponent(test.param);
gCallback = cb;
}
var gCounter = -1;
function run_next_test() {
if (++gCounter == gTests.length)
finish_test();
else
run_test(gTests[gCounter], run_next_test);
}
SimpleTest.waitForExplicitFinish();
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
const HELPERAPP_DIALOG_CONTRACT = "@mozilla.org/helperapplauncherdialog;1";
const HELPERAPP_DIALOG_CID = SpecialPowers.wrap(SpecialPowers.Components).ID(SpecialPowers.Cc[HELPERAPP_DIALOG_CONTRACT].number);
const FAKE_CID = SpecialPowers.Cc["@mozilla.org/uuid-generator;1"].
getService(SpecialPowers.Ci.nsIUUIDGenerator).generateUUID();
function HelperAppLauncherDialog() {}
HelperAppLauncherDialog.prototype = {
REASON_CANTHANDLE: 0,
REASON_SERVERREQUEST: 1,
REASON_TYPESNIFFED: 2,
show: function(aLauncher, aWindowContext, aReason) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var test = gTests[gCounter];
is(aLauncher.suggestedFileName, test.expected,
"The filename should be correctly sanitized");
gCallback();
},
promptForSaveToFile: function(aLauncher, aWindowContext, aDefaultFileName, aSuggestedFileExtension, aForcePrompt) {
return null;
},
QueryInterface: function(aIID) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (aIID.equals(SpecialPowers.Ci.nsISupports) ||
aIID.equals(SpecialPowers.Ci.nsIHelperAppLauncherDialog))
return this;
throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
function load() {
info("Started the load handler");
var iframe = document.getElementById("test");
var gCallback = null;
function run_test(test, cb) {
iframe.src = "unsafeBidiFileName.sjs?name=" + encodeURIComponent(test.param);
gCallback = cb;
}
};
var factory = {
createInstance: function(aOuter, aIID) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (aOuter != null)
throw SpecialPowers.Cr.NS_ERROR_NO_AGGREGATION;
return new HelperAppLauncherDialog().QueryInterface(aIID);
var gCounter = -1;
function run_next_test() {
info("run_next_test called, gCounter = " + gCounter);
if (++gCounter == gTests.length)
finish_test();
else
run_test(gTests[gCounter], run_next_test);
}
};
dump("RegisterFactory...\n");
SpecialPowers.wrap(SpecialPowers.Components).manager
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
.registerFactory(FAKE_CID, "",
HELPERAPP_DIALOG_CONTRACT,
factory);
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
function finish_test() {
dump("UnregisterFactory...\n");
const HELPERAPP_DIALOG_CONTRACT = "@mozilla.org/helperapplauncherdialog;1";
const HELPERAPP_DIALOG_CID = SpecialPowers.wrap(SpecialPowers.Components).ID(SpecialPowers.Cc[HELPERAPP_DIALOG_CONTRACT].number);
const FAKE_CID = SpecialPowers.Cc["@mozilla.org/uuid-generator;1"].
getService(SpecialPowers.Ci.nsIUUIDGenerator).generateUUID();
function HelperAppLauncherDialog() {}
HelperAppLauncherDialog.prototype = {
REASON_CANTHANDLE: 0,
REASON_SERVERREQUEST: 1,
REASON_TYPESNIFFED: 2,
show: function(aLauncher, aWindowContext, aReason) {
info("show() called");
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var test = gTests[gCounter];
is(aLauncher.suggestedFileName, test.expected,
"The filename should be correctly sanitized");
gCallback();
},
promptForSaveToFile: function(aLauncher, aWindowContext, aDefaultFileName, aSuggestedFileExtension, aForcePrompt) {
info("promptForSaveToFile called");
return null;
},
QueryInterface: function(aIID) {
info("QueryInterface called");
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (aIID.equals(SpecialPowers.Ci.nsISupports) ||
aIID.equals(SpecialPowers.Ci.nsIHelperAppLauncherDialog))
return this;
throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
}
};
var factory = {
createInstance: function(aOuter, aIID) {
info("createInstance called");
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (aOuter != null)
throw SpecialPowers.Cr.NS_ERROR_NO_AGGREGATION;
return new HelperAppLauncherDialog().QueryInterface(aIID);
}
};
info("RegisterFactory...\n");
SpecialPowers.wrap(SpecialPowers.Components).manager
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
.registerFactory(HELPERAPP_DIALOG_CID, "",
.registerFactory(FAKE_CID, "",
HELPERAPP_DIALOG_CONTRACT,
null);
SimpleTest.finish();
}
factory);
var i,j;
for (i = 0; i < tests.length; ++i) {
for (j in unsafeBidiChars) {
make_test(replace(tests[i], unsafeBidiChars[j]),
sanitize(tests[i]));
function finish_test() {
info("UnregisterFactory...\n");
SpecialPowers.wrap(SpecialPowers.Components).manager
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
.registerFactory(HELPERAPP_DIALOG_CID, "",
HELPERAPP_DIALOG_CONTRACT,
null);
SimpleTest.finish();
}
}
run_next_test();
var i,j;
for (i = 0; i < tests.length; ++i) {
for (j in unsafeBidiChars) {
make_test(replace(tests[i], unsafeBidiChars[j]),
sanitize(tests[i]));
}
}
run_next_test();
}
]]>
</script>

View File

@ -2,11 +2,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/. */
/* Store per-printer features in temp. prefs vars that the
* print dialog can pick them up... */
#define SET_PRINTER_FEATURES_VIA_PREFS 1
#define PRINTERFEATURES_PREF "print.tmp.printerfeatures"
#ifdef MOZ_LOGGING
#define FORCE_PR_LOG 1 /* Allow logging in the release build */
@ -81,265 +76,6 @@ protected:
static nsTArray<nsString>* mGlobalPrinterList;
};
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
/* "Prototype" for the new nsPrinterFeatures service */
class nsPrinterFeatures {
public:
nsPrinterFeatures( const char *printername );
~nsPrinterFeatures() {}
/* Does this printer allow to set/change the paper size ? */
void SetCanChangePaperSize( bool aCanSetPaperSize );
/* Does this Mozilla print module allow set/change the paper size ? */
void SetSupportsPaperSizeChange( bool aSupportsPaperSizeChange );
/* Set number of paper size records and the records itself */
void SetNumPaperSizeRecords( int32_t aCount );
void SetPaperRecord( int32_t aIndex, const char *aName, int32_t aWidthMM, int32_t aHeightMM, bool aIsInch );
/* Does this printer allow to set/change the content orientation ? */
void SetCanChangeOrientation( bool aCanSetOrientation );
/* Does this Mozilla print module allow set/change the content orientation ? */
void SetSupportsOrientationChange( bool aSupportsOrientationChange );
/* Set number of orientation records and the records itself */
void SetNumOrientationRecords( int32_t aCount );
void SetOrientationRecord( int32_t aIndex, const char *aName );
/* Does this printer allow to set/change the plex mode ? */
void SetCanChangePlex( bool aCanSetPlex );
/* Does this Mozilla print module allow set/change the plex mode ? */
void SetSupportsPlexChange( bool aSupportsPlexChange );
/* Set number of plex records and the records itself */
void SetNumPlexRecords( int32_t aCount );
void SetPlexRecord( int32_t aIndex, const char *aName );
/* Does this printer allow to set/change the resolution name ? */
void SetCanChangeResolutionName( bool aCanSetResolutionName );
/* Does this Mozilla print module allow set/change the resolution name ? */
void SetSupportsResolutionNameChange( bool aSupportsResolutionChange );
/* Set number of resolution records and the records itself */
void SetNumResolutionNameRecords( int32_t aCount );
void SetResolutionNameRecord( int32_t aIndex, const char *aName );
/* Does this printer allow to set/change the colorspace ? */
void SetCanChangeColorspace( bool aCanSetColorspace );
/* Does this Mozilla print module allow set/change the colorspace ? */
void SetSupportsColorspaceChange( bool aSupportsColorspace );
/* Set number of colorspace records and the records itself */
void SetNumColorspaceRecords( int32_t aCount );
void SetColorspaceRecord( int32_t aIndex, const char *aName );
/* Does this device allow to set/change the usage of the internal grayscale mode ? */
void SetCanChangePrintInColor( bool aCanSetPrintInColor );
/* Does this printer allow to set/change the usage of the internal grayscale mode ? */
void SetSupportsPrintInColorChange( bool aSupportPrintInColorChange );
/* Does this device allow to set/change the usage of font download to the printer? */
void SetCanChangeDownloadFonts( bool aCanSetDownloadFonts );
/* Does this printer allow to set/change the usage of font download to the printer? */
void SetSupportsDownloadFontsChange( bool aSupportDownloadFontsChange );
/* Does this device allow to set/change the job title ? */
void SetCanChangeJobTitle( bool aCanSetJobTitle );
/* Does this printer allow to set/change the job title ? */
void SetSupportsJobTitleChange( bool aSupportJobTitleChange );
/* Does this device allow to set/change the spooler command ? */
void SetCanChangeSpoolerCommand( bool aCanSetSpoolerCommand );
/* Does this printer allow to set/change the spooler command ? */
void SetSupportsSpoolerCommandChange( bool aSupportSpoolerCommandChange );
/* Does this device allow to set/change number of copies for an document ? */
void SetCanChangeNumCopies( bool aCanSetNumCopies );
private:
/* private helper methods */
void SetBoolValue( const char *tagname, bool value );
void SetIntValue( const char *tagname, int32_t value );
void SetCharValue( const char *tagname, const char *value );
nsXPIDLCString mPrinterName;
};
void nsPrinterFeatures::SetBoolValue( const char *tagname, bool value )
{
nsPrintfCString prefName(PRINTERFEATURES_PREF ".%s.%s",
mPrinterName.get(), tagname);
Preferences::SetBool(prefName.get(), value);
}
void nsPrinterFeatures::SetIntValue( const char *tagname, int32_t value )
{
nsPrintfCString prefName(PRINTERFEATURES_PREF ".%s.%s",
mPrinterName.get(), tagname);
Preferences::SetInt(prefName.get(), value);
}
void nsPrinterFeatures::SetCharValue( const char *tagname, const char *value )
{
nsPrintfCString prefName(PRINTERFEATURES_PREF ".%s.%s",
mPrinterName.get(), tagname);
Preferences::SetCString(prefName.get(), value);
}
nsPrinterFeatures::nsPrinterFeatures( const char *printername )
{
DO_PR_DEBUG_LOG(("nsPrinterFeatures::nsPrinterFeatures('%s')\n", printername));
mPrinterName.Assign(printername);
SetBoolValue("has_special_printerfeatures", true);
}
void nsPrinterFeatures::SetCanChangePaperSize( bool aCanSetPaperSize )
{
SetBoolValue("can_change_paper_size", aCanSetPaperSize);
}
void nsPrinterFeatures::SetSupportsPaperSizeChange( bool aSupportsPaperSizeChange )
{
SetBoolValue("supports_paper_size_change", aSupportsPaperSizeChange);
}
/* Set number of paper size records and the records itself */
void nsPrinterFeatures::SetNumPaperSizeRecords( int32_t aCount )
{
SetIntValue("paper.count", aCount);
}
void nsPrinterFeatures::SetPaperRecord(int32_t aIndex, const char *aPaperName, int32_t aWidthMM, int32_t aHeightMM, bool aIsInch)
{
SetCharValue(nsPrintfCString("paper.%d.name", aIndex).get(), aPaperName);
SetIntValue( nsPrintfCString("paper.%d.width_mm", aIndex).get(), aWidthMM);
SetIntValue( nsPrintfCString("paper.%d.height_mm", aIndex).get(), aHeightMM);
SetBoolValue(nsPrintfCString("paper.%d.is_inch", aIndex).get(), aIsInch);
}
void nsPrinterFeatures::SetCanChangeOrientation( bool aCanSetOrientation )
{
SetBoolValue("can_change_orientation", aCanSetOrientation);
}
void nsPrinterFeatures::SetSupportsOrientationChange( bool aSupportsOrientationChange )
{
SetBoolValue("supports_orientation_change", aSupportsOrientationChange);
}
void nsPrinterFeatures::SetNumOrientationRecords( int32_t aCount )
{
SetIntValue("orientation.count", aCount);
}
void nsPrinterFeatures::SetOrientationRecord( int32_t aIndex, const char *aOrientationName )
{
SetCharValue(nsPrintfCString("orientation.%d.name", aIndex).get(), aOrientationName);
}
void nsPrinterFeatures::SetCanChangePlex( bool aCanSetPlex )
{
SetBoolValue("can_change_plex", aCanSetPlex);
}
void nsPrinterFeatures::SetSupportsPlexChange( bool aSupportsPlexChange )
{
SetBoolValue("supports_plex_change", aSupportsPlexChange);
}
void nsPrinterFeatures::SetNumPlexRecords( int32_t aCount )
{
SetIntValue("plex.count", aCount);
}
void nsPrinterFeatures::SetPlexRecord( int32_t aIndex, const char *aPlexName )
{
SetCharValue(nsPrintfCString("plex.%d.name", aIndex).get(), aPlexName);
}
void nsPrinterFeatures::SetCanChangeResolutionName( bool aCanSetResolutionName )
{
SetBoolValue("can_change_resolution", aCanSetResolutionName);
}
void nsPrinterFeatures::SetSupportsResolutionNameChange( bool aSupportsResolutionNameChange )
{
SetBoolValue("supports_resolution_change", aSupportsResolutionNameChange);
}
void nsPrinterFeatures::SetNumResolutionNameRecords( int32_t aCount )
{
SetIntValue("resolution.count", aCount);
}
void nsPrinterFeatures::SetResolutionNameRecord( int32_t aIndex, const char *aResolutionName )
{
SetCharValue(nsPrintfCString("resolution.%d.name", aIndex).get(), aResolutionName);
}
void nsPrinterFeatures::SetCanChangeColorspace( bool aCanSetColorspace )
{
SetBoolValue("can_change_colorspace", aCanSetColorspace);
}
void nsPrinterFeatures::SetSupportsColorspaceChange( bool aSupportsColorspaceChange )
{
SetBoolValue("supports_colorspace_change", aSupportsColorspaceChange);
}
void nsPrinterFeatures::SetNumColorspaceRecords( int32_t aCount )
{
SetIntValue("colorspace.count", aCount);
}
void nsPrinterFeatures::SetColorspaceRecord( int32_t aIndex, const char *aColorspace )
{
SetCharValue(nsPrintfCString("colorspace.%d.name", aIndex).get(), aColorspace);
}
void nsPrinterFeatures::SetCanChangeDownloadFonts( bool aCanSetDownloadFonts )
{
SetBoolValue("can_change_downloadfonts", aCanSetDownloadFonts);
}
void nsPrinterFeatures::SetSupportsDownloadFontsChange( bool aSupportDownloadFontsChange )
{
SetBoolValue("supports_downloadfonts_change", aSupportDownloadFontsChange);
}
void nsPrinterFeatures::SetCanChangePrintInColor( bool aCanSetPrintInColor )
{
SetBoolValue("can_change_printincolor", aCanSetPrintInColor);
}
void nsPrinterFeatures::SetSupportsPrintInColorChange( bool aSupportPrintInColorChange )
{
SetBoolValue("supports_printincolor_change", aSupportPrintInColorChange);
}
void nsPrinterFeatures::SetCanChangeSpoolerCommand( bool aCanSetSpoolerCommand )
{
SetBoolValue("can_change_spoolercommand", aCanSetSpoolerCommand);
}
void nsPrinterFeatures::SetSupportsSpoolerCommandChange( bool aSupportSpoolerCommandChange )
{
SetBoolValue("supports_spoolercommand_change", aSupportSpoolerCommandChange);
}
void nsPrinterFeatures::SetCanChangeJobTitle( bool aCanSetJobTitle )
{
SetBoolValue("can_change_jobtitle", aCanSetJobTitle);
}
void nsPrinterFeatures::SetSupportsJobTitleChange( bool aSupportsJobTitle )
{
SetBoolValue("supports_jobtitle_change", aSupportsJobTitle);
}
void nsPrinterFeatures::SetCanChangeNumCopies( bool aCanSetNumCopies )
{
SetBoolValue("can_change_num_copies", aCanSetNumCopies);
}
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
//---------------
// static members
GlobalPrinters GlobalPrinters::mGlobalPrinters;
@ -748,15 +484,6 @@ NS_IMETHODIMP nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter(const char16_
if (kNotFound != slash)
printerName.Cut(0, slash + 1);
}
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
/* Defaults to FALSE */
nsPrintfCString prefName(
PRINTERFEATURES_PREF ".%s.has_special_printerfeatures",
fullPrinterName.get());
Preferences::SetBool(prefName.get(), false);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
/* Set filename */
nsAutoCString filename;
@ -778,21 +505,7 @@ NS_IMETHODIMP nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter(const char16_
if (type == pmPostScript) {
DO_PR_DEBUG_LOG(("InitPrintSettingsFromPrinter() for PostScript printer\n"));
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
nsPrinterFeatures printerFeatures(fullPrinterName);
printerFeatures.SetSupportsPaperSizeChange(true);
printerFeatures.SetSupportsOrientationChange(true);
printerFeatures.SetSupportsPlexChange(false);
printerFeatures.SetSupportsResolutionNameChange(false);
printerFeatures.SetSupportsColorspaceChange(false);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetCanChangeOrientation(true);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
nsAutoCString orientation;
if (NS_SUCCEEDED(CopyPrinterCharPref("postscript", printerName,
"orientation", orientation))) {
@ -809,48 +522,18 @@ NS_IMETHODIMP nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter(const char16_
}
}
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetOrientationRecord(0, "portrait");
printerFeatures.SetOrientationRecord(1, "landscape");
printerFeatures.SetNumOrientationRecords(2);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
/* PostScript module does not support changing the plex mode... */
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetCanChangePlex(false);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
DO_PR_DEBUG_LOG(("setting default plex to '%s'\n", "default"));
aPrintSettings->SetPlexName(MOZ_UTF16("default"));
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetPlexRecord(0, "default");
printerFeatures.SetNumPlexRecords(1);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
/* PostScript module does not support changing the resolution mode... */
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetCanChangeResolutionName(false);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
DO_PR_DEBUG_LOG(("setting default resolution to '%s'\n", "default"));
aPrintSettings->SetResolutionName(MOZ_UTF16("default"));
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetResolutionNameRecord(0, "default");
printerFeatures.SetNumResolutionNameRecords(1);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
/* PostScript module does not support changing the colorspace... */
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetCanChangeColorspace(false);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
DO_PR_DEBUG_LOG(("setting default colorspace to '%s'\n", "default"));
aPrintSettings->SetColorspace(MOZ_UTF16("default"));
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetColorspaceRecord(0, "default");
printerFeatures.SetNumColorspaceRecords(1);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetCanChangePaperSize(true);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
nsAutoCString papername;
if (NS_SUCCEEDED(CopyPrinterCharPref("postscript", printerName,
"paper_size", papername))) {
@ -867,36 +550,10 @@ NS_IMETHODIMP nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter(const char16_
else {
DO_PR_DEBUG_LOG(("Unknown paper size '%s' given.\n", papername.get()));
}
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
paper.First();
int count = 0;
while (!paper.AtEnd())
{
printerFeatures.SetPaperRecord(count++, paper.Name(),
(int)paper.Width_mm(), (int)paper.Height_mm(), !paper.IsMetric());
paper.Next();
}
printerFeatures.SetNumPaperSizeRecords(count);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
}
bool hasSpoolerCmd = (nsPSPrinterList::kTypePS ==
nsPSPrinterList::GetPrinterType(fullPrinterName));
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetSupportsSpoolerCommandChange(hasSpoolerCmd);
printerFeatures.SetCanChangeSpoolerCommand(hasSpoolerCmd);
/* Postscript module does not pass the job title to lpr */
printerFeatures.SetSupportsJobTitleChange(false);
printerFeatures.SetCanChangeJobTitle(false);
/* Postscript module has no control over builtin fonts yet */
printerFeatures.SetSupportsDownloadFontsChange(false);
printerFeatures.SetCanChangeDownloadFonts(false);
/* Postscript module does not support multiple colorspaces
* so it has to use the old way */
printerFeatures.SetSupportsPrintInColorChange(true);
printerFeatures.SetCanChangePrintInColor(true);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
if (hasSpoolerCmd) {
nsAutoCString command;
@ -908,10 +565,6 @@ NS_IMETHODIMP nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter(const char16_
}
}
#ifdef SET_PRINTER_FEATURES_VIA_PREFS
printerFeatures.SetCanChangeNumCopies(true);
#endif /* SET_PRINTER_FEATURES_VIA_PREFS */
return NS_OK;
}