diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index 9d0693ee4b1..8dc74618f29 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index 322347c7d6a..6cdd2179f11 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index 9d0693ee4b1..8dc74618f29 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index ece56e02512..75c1620daa3 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
{
- "revision": "e11cf4c78874de3469c4f338cf275aeae31854ee",
+ "revision": "aa1e2bec7aab191896988cee73e8bfb332b894cf",
"repo_path": "/integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index a750b559c0c..0d1d185198f 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index 68c2cdc7534..3b6b3d784c0 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml
index a087d7e0bb8..eff9666412a 100644
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml
index a85f257f2f6..dd8938a804e 100644
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml
index a94dbc9c8e8..20dd781a5f3 100644
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index 9ea693de925..d6c9e6e15b6 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/locales/en-US/chrome/overrides/appstrings.properties b/b2g/locales/en-US/chrome/overrides/appstrings.properties
index 1de0093306c..7028131b354 100644
--- a/b2g/locales/en-US/chrome/overrides/appstrings.properties
+++ b/b2g/locales/en-US/chrome/overrides/appstrings.properties
@@ -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.
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index c2dc69353a0..de418dcd0d5 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -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);
diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
index 43c88bfce51..a644e9745c7 100644
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -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);
}
});
},
diff --git a/browser/components/places/content/places.js b/browser/components/places/content/places.js
index 45fd52edcde..86fe0e44d4d 100644
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -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);
}
};
diff --git a/browser/components/places/tests/unit/test_421483.js b/browser/components/places/tests/unit/test_421483.js
index 46eb5dc559d..5aa997e3144 100644
--- a/browser/components/places/tests/unit/test_421483.js
+++ b/browser/components/places/tests/unit/test_421483.js
@@ -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);
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_shutdown.js b/browser/components/places/tests/unit/test_browserGlue_shutdown.js
deleted file mode 100644
index a1281b59e0f..00000000000
--- a/browser/components/places/tests/unit/test_browserGlue_shutdown.js
+++ /dev/null
@@ -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();
-}
diff --git a/browser/components/places/tests/unit/test_clearHistory_shutdown.js b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
index c083fa37b91..f25dc1b248b 100644
--- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -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"
];
diff --git a/browser/components/places/tests/unit/xpcshell.ini b/browser/components/places/tests/unit/xpcshell.ini
index 41bd7123360..214a5d170c1 100644
--- a/browser/components/places/tests/unit/xpcshell.ini
+++ b/browser/components/places/tests/unit/xpcshell.ini
@@ -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]
diff --git a/browser/locales/en-US/chrome/overrides/appstrings.properties b/browser/locales/en-US/chrome/overrides/appstrings.properties
index 1de0093306c..7028131b354 100644
--- a/browser/locales/en-US/chrome/overrides/appstrings.properties
+++ b/browser/locales/en-US/chrome/overrides/appstrings.properties
@@ -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.
diff --git a/browser/locales/en-US/chrome/overrides/netError.dtd b/browser/locales/en-US/chrome/overrides/netError.dtd
index 9f6cf045f64..2ebcfc481f0 100644
--- a/browser/locales/en-US/chrome/overrides/netError.dtd
+++ b/browser/locales/en-US/chrome/overrides/netError.dtd
@@ -86,8 +86,8 @@
-
-
+
You might need to install other software to open this address.
- aa2_block: /* innermost frame (view-source: URL) denies a */
-
-
ab_allow: /* innermost frame allows a */
diff --git a/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.js b/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.js
index 710f0cf38fa..750ef333dd2 100644
--- a/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.js
+++ b/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.js
@@ -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'");
diff --git a/content/base/test/csp/test_CSP_frameancestors.html b/content/base/test/csp/test_CSP_frameancestors.html
index 991a394815c..b294ac153fb 100644
--- a/content/base/test/csp/test_CSP_frameancestors.html
+++ b/content/base/test/csp/test_CSP_frameancestors.html
@@ -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.
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 305b3c2e7eb..42102496dfe 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -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 nestedURI = do_QueryInterface(aURI);
+ while (nestedURI) {
+ nsCOMPtr 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 tempURI = aURI;
+ nsCOMPtr 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
diff --git a/docshell/resources/content/netError.xhtml b/docshell/resources/content/netError.xhtml
index 731a8374478..517f36cf413 100644
--- a/docshell/resources/content/netError.xhtml
+++ b/docshell/resources/content/netError.xhtml
@@ -274,7 +274,7 @@
&dnsNotFound.title;
&fileNotFound.title;
&malformedURI.title;
-
&protocolNotFound.title;
+
&unknownProtocolFound.title;
&connectionFailure.title;
&netTimeout.title;
&redirectLoop.title;
@@ -300,7 +300,7 @@
&dnsNotFound.longDesc;
&fileNotFound.longDesc;
&malformedURI.longDesc;
-
&protocolNotFound.longDesc;
+
&unknownProtocolFound.longDesc;
&connectionFailure.longDesc;
&netTimeout.longDesc;
&redirectLoop.longDesc;
diff --git a/docshell/test/chrome/chrome.ini b/docshell/test/chrome/chrome.ini
index 900b54e3133..82818aa805f 100644
--- a/docshell/test/chrome/chrome.ini
+++ b/docshell/test/chrome/chrome.ini
@@ -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]
diff --git a/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html b/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html
new file mode 100644
index 00000000000..fdecbbdfe1f
--- /dev/null
+++ b/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html
@@ -0,0 +1,11 @@
+
+
+
+
+Test ifranes for view-source forbidden in iframe tests
+
+
+
+
+
+
diff --git a/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
new file mode 100644
index 00000000000..536ca4d037b
--- /dev/null
+++ b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+ Mozilla Bug 624883
+
+
+
+
+
+
+
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index cf299189e14..9979848fe9e 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1926,11 +1926,6 @@ Navigator::HasDataStoreSupport(JSContext* cx, JSObject* aGlobal)
{
JS::Rooted 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);
diff --git a/dom/locales/en-US/chrome/appstrings.properties b/dom/locales/en-US/chrome/appstrings.properties
index f6a00c07bbe..4244b2dca99 100644
--- a/dom/locales/en-US/chrome/appstrings.properties
+++ b/dom/locales/en-US/chrome/appstrings.properties
@@ -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.
diff --git a/dom/locales/en-US/chrome/netError.dtd b/dom/locales/en-US/chrome/netError.dtd
index 5ef66cad92b..3135f125e76 100644
--- a/dom/locales/en-US/chrome/netError.dtd
+++ b/dom/locales/en-US/chrome/netError.dtd
@@ -50,8 +50,8 @@
The requested site did not respond to a connection request and the browser has stopped waiting for a reply.
Could the server be experiencing high demand or a temporary outage? Try again later.
Are you unable to browse other sites? Check the computer's network connection.
Is your computer or network protected by a firewall or proxy? Incorrect settings can interfere with Web browsing.
Still having trouble? Consult your network administrator or Internet provider for assistance.
">
-
-The address specifies a protocol (e.g. wxyz://) the browser does not recognize, so the browser cannot properly connect to the site.
Are you trying to access multimedia or other non-text services? Check the site for extra requirements.
Some protocols may require third-party software or plugins before the browser can recognize them.
">
+
+The address specifies a protocol (e.g. wxyz://) the browser does not recognize, so the browser cannot properly connect to the site.
Are you trying to access multimedia or other non-text services? Check the site for extra requirements.
Some protocols may require third-party software or plugins before the browser can recognize them.
">
The browser is configured to use a proxy server, but the proxy refused a connection.
Is the browser's proxy configuration correct? Check the settings and try again.
Does the proxy service allow connections from this network?
Still having trouble? Consult your network administrator or Internet provider for assistance.
">
diff --git a/gfx/layers/composite/APZCTreeManager.cpp b/gfx/layers/composite/APZCTreeManager.cpp
index dfeebbfdb3a..e0cf9209d16 100644
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -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 item = mOverscrollHandoffChain[i];
+ if (item) {
+ item->FlushRepaintForOverscrollHandoff();
+ }
+ }
+ return true;
+}
+
bool
APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
{
diff --git a/gfx/layers/composite/APZCTreeManager.h b/gfx/layers/composite/APZCTreeManager.h
index 81bef9d4269..7df110d007c 100644
--- a/gfx/layers/composite/APZCTreeManager.h
+++ b/gfx/layers/composite/APZCTreeManager.h
@@ -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
diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp
index 5a8fd64b4cc..72dc5ffcefa 100644
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -575,16 +575,18 @@ nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent)
nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
nsEventStatus rv = nsEventStatus_eIgnore;
- nsRefPtr 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 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);
}
diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h
index bc9f418df30..b8fa02cb784 100644
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -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
diff --git a/gfx/layers/ipc/GestureEventListener.cpp b/gfx/layers/ipc/GestureEventListener.cpp
index 3d8a8c5dd53..36d13ee2227 100644
--- a/gfx/layers/ipc/GestureEventListener.cpp
+++ b/gfx/layers/ipc/GestureEventListener.cpp
@@ -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(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;
}
diff --git a/gfx/layers/ipc/GestureEventListener.h b/gfx/layers/ipc/GestureEventListener.h
index f655b438285..e8b21477258 100644
--- a/gfx/layers/ipc/GestureEventListener.h
+++ b/gfx/layers/ipc/GestureEventListener.h
@@ -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
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
index 68579681793..d5a6f96e846 100644
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -170,10 +170,10 @@ SetScriptSourceFilename(ExclusiveContext *cx, ScriptSource *ss,
{
if (options.hasIntroductionInfo) {
const char *filename = options.filename() ? options.filename() : "";
- 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);
diff --git a/js/src/jit-test/tests/debug/Source-introductionType.js b/js/src/jit-test/tests/debug/Source-introductionType.js
new file mode 100644
index 00000000000..939c3dd0c2f
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Source-introductionType.js
@@ -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');
+
diff --git a/js/src/jit-test/tests/parallel/bug944975.js b/js/src/jit-test/tests/parallel/bug944975.js
new file mode 100644
index 00000000000..0b4b2ca81e9
--- /dev/null
+++ b/js/src/jit-test/tests/parallel/bug944975.js
@@ -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);
+}
diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp
index 6d3db2af534..350f13ec571 100644
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -236,17 +236,25 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
heap = &bufferVal.toObject().as();
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 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 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))
diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp
index 51200fd0d50..a59481647d6 100644
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -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())
diff --git a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp
index 1c9476b268c..1f131dd1a4c 100644
--- a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp
+++ b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp
@@ -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.
{
- JSObject *badObject = reinterpret_cast(1);
- JSObject *punnedPtr = nullptr;
+ ObjectPtr badObject = reinterpret_cast(1);
+ ObjectPtr punnedPtr = nullptr;
RelocatablePtrObject* relocPtr =
reinterpret_cast(&punnedPtr);
new (relocPtr) RelocatablePtrObject;
@@ -77,8 +80,8 @@ BEGIN_TEST(testGCStoreBufferRemoval)
// Test removal of store buffer entries added by Heap.
{
- JSObject *badObject = reinterpret_cast(1);
- JSObject *punnedPtr = nullptr;
+ ObjectPtr badObject = reinterpret_cast(1);
+ ObjectPtr punnedPtr = nullptr;
Heap* heapPtr =
reinterpret_cast*>(&punnedPtr);
new (heapPtr) Heap;
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index a4f65d8d8d5..2b92e7d1fd4 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -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;
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 9df323ef3b8..a216a43522f 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -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;
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index 97d874db834..bde4c0cd306 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -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() && maybeClone.as().nonLazyScript()->isCallsiteClone())
- vp.setObject(*maybeClone.as().nonLazyScript()->originalFunction());
+ if (maybeClone.is())
+ vp.setObject(*maybeClone.as().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) {
diff --git a/js/src/jsfun.h b/js/src/jsfun.h
index 502843c2a9e..56d89c319cf 100644
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -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_;
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
index ed007e29a20..3b63ab3fed6 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -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(len);
if (!filename_)
return false;
mozilla::DebugOnly checkLen = JS_snprintf(filename_, len, "%s line %s > %s",
- callerFilename, linenoBuf, introducer);
+ callerFilename, linenoBuf, introductionType);
JS_ASSERT(checkLen == len - 1);
if (!introducerFilename_)
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index ad5bb72201b..2ffa3db20ed 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -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
*/
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);
diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h
index 740ecf5688e..4f3e275d842 100644
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -149,7 +149,7 @@ JSScript::principals()
}
inline JSFunction *
-JSScript::originalFunction() const {
+JSScript::donorFunction() const {
if (!isCallsiteClone())
return nullptr;
return &enclosingScopeOrOriginalFunction_->as();
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index 5d3804aa451..dcb522614df 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -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(cx, ss->introducerType());
+ if (ss->hasIntroductionType()) {
+ JSString *str = js_NewStringCopyZ(cx, ss->introductionType());
if (!str)
return false;
args.rval().setString(str);
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index cfc5b03411f..ab9038255df 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -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;
diff --git a/layout/svg/nsSVGFilterFrame.cpp b/layout/svg/nsSVGFilterFrame.cpp
index b15253d2ab2..c7311b0bd74 100644
--- a/layout/svg/nsSVGFilterFrame.cpp
+++ b/layout/svg/nsSVGFilterFrame.cpp
@@ -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,
diff --git a/layout/svg/nsSVGFilterFrame.h b/layout/svg/nsSVGFilterFrame.h
index c5d7188f67d..f1a734cae60 100644
--- a/layout/svg/nsSVGFilterFrame.h
+++ b/layout/svg/nsSVGFilterFrame.h
@@ -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,
diff --git a/layout/svg/nsSVGFilterInstance.cpp b/layout/svg/nsSVGFilterInstance.cpp
index 7d674f0864c..bc14aa8f03c 100644
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -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
{
diff --git a/layout/svg/nsSVGFilterInstance.h b/layout/svg/nsSVGFilterInstance.h
index bee8d21ac9d..6947e630d76 100644
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -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
diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp
index 4383622f62d..8a185a1f300 100644
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -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);
diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp
index 4028c27516d..2276d95ca9a 100644
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -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);
}
diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp
index e544723ac5e..68dc85be0fd 100644
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -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
diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h
index cb4d4b702f4..80f5b5f747b 100644
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -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
diff --git a/layout/xul/nsResizerFrame.cpp b/layout/xul/nsResizerFrame.cpp
index 3858e6df46c..56edf320892 100644
--- a/layout/xul/nsResizerFrame.cpp
+++ b/layout/xul/nsResizerFrame.cpp
@@ -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.
diff --git a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
index b8ceb3bc913..40f60be4c02 100644
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -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
diff --git a/media/webrtc/signaling/src/media-conduit/AudioConduit.h b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
index eab3fb17d0b..9e87cfec03f 100755
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -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& 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 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.
diff --git a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
index 41bc9aac7a4..48ccaea5fa4 100755
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -344,16 +344,18 @@ public:
*/
virtual MediaConduitErrorCode ConfigureRecvMediaCodecs(
const std::vector& 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
-
-
diff --git a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
index 4bb9611a7e7..6aa201ee46a 100644
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -2230,12 +2230,15 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
// and are responsible for cleanly shutting down.
mozilla::RefPtr conduit =
mozilla::AudioSessionConduit::Create(static_cast(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 pipeline =
new mozilla::MediaPipelineTransmit(
pc.impl()->GetHandle(),
diff --git a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
index f09200e7756..0b46a410330 100644
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -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
*/
diff --git a/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h b/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
index 25300b7ee83..ba8e1ffaf1f 100755
--- a/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
@@ -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
*/
diff --git a/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c b/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
index 8a62210e95a..7bff8a4c516 100755
--- a/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
@@ -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;
diff --git a/media/webrtc/signaling/src/sipcc/core/sdp/sdp.h b/media/webrtc/signaling/src/sipcc/core/sdp/sdp.h
index 9c98529be9f..4327ab60652 100644
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp.h
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp.h
@@ -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:["/"]
+ *
+ */
+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_ */
diff --git a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
index e414e819882..fa5ca2eab44 100644
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
@@ -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);
+}
+
diff --git a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr_access.c b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr_access.c
index 906e4b73c7d..9192e3f7efe 100644
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr_access.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr_access.c
@@ -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);
+}
+
diff --git a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
index f855a096ab6..4d993548453 100644
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
@@ -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] =
{
diff --git a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_private.h b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_private.h
index 476ff4ace66..959305383d2 100644
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_private.h
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_private.h
@@ -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(
diff --git a/media/webrtc/signaling/src/sipcc/include/ccsdp.h b/media/webrtc/signaling/src/sipcc/include/ccsdp.h
index c056c8044ed..cb2a6f8b683 100644
--- a/media/webrtc/signaling/src/sipcc/include/ccsdp.h
+++ b/media/webrtc/signaling/src/sipcc/include/ccsdp.h
@@ -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,
diff --git a/media/webrtc/signaling/src/sipcc/include/vcm.h b/media/webrtc/signaling/src/sipcc/include/vcm.h
index c19c48c0c7e..81899c86c4e 100755
--- a/media/webrtc/signaling/src/sipcc/include/vcm.h
+++ b/media/webrtc/signaling/src/sipcc/include/vcm.h
@@ -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 */
diff --git a/media/webrtc/signaling/test/sdp_unittests.cpp b/media/webrtc/signaling/test/sdp_unittests.cpp
index e26b844c82e..51df09b3ad6 100644
--- a/media/webrtc/signaling/test/sdp_unittests.cpp
+++ b/media/webrtc/signaling/test/sdp_unittests.cpp
@@ -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");
diff --git a/mobile/android/chrome/content/netError.xhtml b/mobile/android/chrome/content/netError.xhtml
index b8ef34ede6a..faeecd63636 100644
--- a/mobile/android/chrome/content/netError.xhtml
+++ b/mobile/android/chrome/content/netError.xhtml
@@ -275,7 +275,7 @@
&dnsNotFound.title;
&fileNotFound.title;
&malformedURI.title;
-
&protocolNotFound.title;
+
&unknownProtocolFound.title;
&connectionFailure.title;
&netTimeout.title;
&redirectLoop.title;
@@ -304,7 +304,7 @@
&dnsNotFound.longDesc2;
&fileNotFound.longDesc;
&malformedURI.longDesc;
-
&protocolNotFound.longDesc;
+
&unknownProtocolFound.longDesc;
&connectionFailure.longDesc;
&netTimeout.longDesc;
&redirectLoop.longDesc;
diff --git a/mobile/locales/en-US/overrides/appstrings.properties b/mobile/locales/en-US/overrides/appstrings.properties
index 1de0093306c..7028131b354 100644
--- a/mobile/locales/en-US/overrides/appstrings.properties
+++ b/mobile/locales/en-US/overrides/appstrings.properties
@@ -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.
diff --git a/mobile/locales/en-US/overrides/netError.dtd b/mobile/locales/en-US/overrides/netError.dtd
index d43c0e4c245..00afc09d213 100644
--- a/mobile/locales/en-US/overrides/netError.dtd
+++ b/mobile/locales/en-US/overrides/netError.dtd
@@ -83,8 +83,8 @@
-
-
+
You might need to install other software to open this address.
diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js
index 34ff8a060b8..b6b4a0823fd 100644
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -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);
diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat
index 220472822e6..3c8d7e4dd96 100644
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -6938,7 +6938,7 @@ club
voting
// TOKYO : 2013-11-13 GMO Registry, Inc.
-TOKYO
+tokyo
// moe : 2013-11-13 Interlink Co., Ltd.
moe
diff --git a/parser/htmlparser/tests/mochitest/test_viewsource.html b/parser/htmlparser/tests/mochitest/test_viewsource.html
index c9f74f9af6d..42721954a46 100644
--- a/parser/htmlparser/tests/mochitest/test_viewsource.html
+++ b/parser/htmlparser/tests/mochitest/test_viewsource.html
@@ -2,7 +2,6 @@
Test for view source
-
@@ -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;
diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp
index 1ff7214cf38..40e3ca0bf26 100644
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -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) {
diff --git a/testing/specialpowers/content/SpecialPowersObserverAPI.js b/testing/specialpowers/content/SpecialPowersObserverAPI.js
index 0403b46b7c8..cbe5a990415 100644
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -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.
diff --git a/toolkit/components/places/BookmarkJSONUtils.jsm b/toolkit/components/places/BookmarkJSONUtils.jsm
index 7d274f1f745..7414d9b32f8 100644
--- a/toolkit/components/places/BookmarkJSONUtils.jsm
+++ b/toolkit/components/places/BookmarkJSONUtils.jsm
@@ -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));
}
-}
\ No newline at end of file
+}
diff --git a/toolkit/components/places/PlacesBackups.jsm b/toolkit/components/places/PlacesBackups.jsm
index dbfbec7cd4a..8de3a93ea4f 100644
--- a/toolkit/components/places/PlacesBackups.jsm
+++ b/toolkit/components/places/PlacesBackups.jsm
@@ -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;
}
diff --git a/toolkit/components/printing/content/printPreviewBindings.xml b/toolkit/components/printing/content/printPreviewBindings.xml
index 4be1121712d..dbc6139a462 100644
--- a/toolkit/components/printing/content/printPreviewBindings.xml
+++ b/toolkit/components/printing/content/printPreviewBindings.xml
@@ -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");
- }
- }
]]>
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index a8a875c787f..a4015d04c14 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -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",
diff --git a/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml b/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
index 772fff80733..2be4e573dfd 100644
--- a/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
+++ b/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
@@ -4,7 +4,7 @@
-
+
diff --git a/widget/gtk/nsDeviceContextSpecG.cpp b/widget/gtk/nsDeviceContextSpecG.cpp
index 66762a2ac88..8e06757394d 100644
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -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* 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;
}