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.
  • diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index b62ae25365e..558a5c936a7 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -46,208 +46,438 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml"> - + 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; }