From c920372ff2588cf21bc405e8f5d25d426e6afc70 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 16 Mar 2014 22:33:39 +0100 Subject: [PATCH 1/3] Rewrite 'Preferences' to make it async --- web/firefoxcom.js | 12 +-- web/preferences.js | 200 +++++++++++++++++++++++++++++---------------- web/viewer.js | 53 ++++++++---- 3 files changed, 173 insertions(+), 92 deletions(-) diff --git a/web/firefoxcom.js b/web/firefoxcom.js index ccc3581d9..3e1b9b2cc 100644 --- a/web/firefoxcom.js +++ b/web/firefoxcom.js @@ -104,15 +104,17 @@ var DownloadManager = (function DownloadManagerClosure() { return DownloadManager; })(); -Preferences.prototype.writeToStorage = function(prefObj) { - FirefoxCom.requestSync('setPreferences', prefObj); +Preferences._writeToStorage = function (prefObj) { + return new Promise(function (resolve) { + FirefoxCom.requestSync('setPreferences', prefObj); + resolve(); + }); }; -Preferences.prototype.readFromStorage = function(prefObj) { - var readFromStoragePromise = new Promise(function (resolve) { +Preferences._readFromStorage = function (prefObj) { + return new Promise(function (resolve) { var readPrefs = JSON.parse(FirefoxCom.requestSync('getPreferences', prefObj)); resolve(readPrefs); }); - return readFromStoragePromise; }; diff --git a/web/preferences.js b/web/preferences.js index a43c5bfe6..7c7469a00 100644 --- a/web/preferences.js +++ b/web/preferences.js @@ -14,51 +14,99 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals DEFAULT_PREFERENCES, PDFJS, isLocalStorageEnabled, Promise */ +/* globals DEFAULT_PREFERENCES, isLocalStorageEnabled, Promise */ 'use strict'; //#include default_preferences.js -var Preferences = (function PreferencesClosure() { - function Preferences() { - this.prefs = {}; - this.isInitializedPromiseResolved = false; - this.initializedPromise = this.readFromStorage(DEFAULT_PREFERENCES).then( - function(prefObj) { - this.isInitializedPromiseResolved = true; +/** + * Preferences - Utility for storing persistent settings. + * Used for settings that should be applied to all opened documents, + * or every time the viewer is loaded. + */ +var Preferences = { + prefs: Object.create(DEFAULT_PREFERENCES), + isInitializedPromiseResolved: false, + initializedPromise: null, + + /** + * Initialize and fetch the current preference values from storage. + * @return {Promise} A promise that is resolved when the preferences + * have been initialized. + */ + initialize: function preferencesInitialize() { + return this.initializedPromise = + this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) { + this.isInitializedPromiseResolved = true; + if (prefObj) { + this.prefs = prefObj; + } + }.bind(this)); + }, + + /** + * Stub function for writing preferences to storage. + * NOTE: This should be overridden by a build-specific function defined below. + * @param {Object} prefObj The preferences that should be written to storage. + * @return {Promise} A promise that is resolved when the preference values + * have been written. + */ + _writeToStorage: function preferences_writeToStorage(prefObj) { + return Promise.resolve(); + }, + + /** + * Stub function for reading preferences from storage. + * NOTE: This should be overridden by a build-specific function defined below. + * @param {Object} prefObj The preferences that should be read from storage. + * @return {Promise} A promise that is resolved with an {Object} containing + * the preferences that have been read. + */ + _readFromStorage: function preferences_readFromStorage(prefObj) { + return Promise.resolve(); + }, + + /** + * Reset the preferences to their default values and update storage. + * @return {Promise} A promise that is resolved when the preference values + * have been reset. + */ + reset: function preferencesReset() { + return this.initializedPromise.then(function() { + this.prefs = Object.create(DEFAULT_PREFERENCES); + return this._writeToStorage(DEFAULT_PREFERENCES); + }.bind(this)); + }, + + /** + * Replace the current preference values with the ones from storage. + * @return {Promise} A promise that is resolved when the preference values + * have been updated. + */ + reload: function preferencesReload() { + return this.initializedPromise.then(function () { + this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) { if (prefObj) { this.prefs = prefObj; } }.bind(this)); - } - - Preferences.prototype = { - writeToStorage: function Preferences_writeToStorage(prefObj) { - return; - }, + }.bind(this)); + }, - readFromStorage: function Preferences_readFromStorage(prefObj) { - var readFromStoragePromise = Promise.resolve(); - return readFromStoragePromise; - }, - - reset: function Preferences_reset() { - if (this.isInitializedPromiseResolved) { - this.prefs = {}; - this.writeToStorage(DEFAULT_PREFERENCES); - } - }, - - set: function Preferences_set(name, value) { - if (!this.isInitializedPromiseResolved) { - return; - } else if (DEFAULT_PREFERENCES[name] === undefined) { - console.error('Preferences_set: \'' + name + '\' is undefined.'); - return; + /** + * Set the value of a preference. + * @param {string} name The name of the preference that should be changed. + * @param {boolean|number|string} value The new value of the preference. + * @return {Promise} A promise that is resolved when the value has been set, + * provided that the preference exists and the types match. + */ + set: function preferencesSet(name, value) { + return this.initializedPromise.then(function () { + if (DEFAULT_PREFERENCES[name] === undefined) { + throw new Error('preferencesSet: \'' + name + '\' is undefined.'); } else if (value === undefined) { - console.error('Preferences_set: no value is specified.'); - return; + throw new Error('preferencesSet: no value is specified.'); } var valueType = typeof value; var defaultType = typeof DEFAULT_PREFERENCES[name]; @@ -67,71 +115,79 @@ var Preferences = (function PreferencesClosure() { if (valueType === 'number' && defaultType === 'string') { value = value.toString(); } else { - console.error('Preferences_set: \'' + value + '\' is a \"' + - valueType + '\", expected a \"' + defaultType + '\".'); - return; + throw new Error('Preferences_set: \'' + value + '\' is a \"' + + valueType + '\", expected \"' + defaultType + '\".'); } } else { if (valueType === 'number' && (value | 0) !== value) { - console.error('Preferences_set: \'' + value + - '\' must be an \"integer\".'); - return; + throw new Error('Preferences_set: \'' + value + + '\' must be an \"integer\".'); } } this.prefs[name] = value; - this.writeToStorage(this.prefs); - }, + return this._writeToStorage(this.prefs); + }.bind(this)); + }, - get: function Preferences_get(name) { - var defaultPref = DEFAULT_PREFERENCES[name]; + /** + * Get the value of a preference. + * @param {string} name The name of the preference whose value is requested. + * @return {Promise} A promise that is resolved with a {boolean|number|string} + * containing the value of the preference. + */ + get: function preferencesGet(name) { + return this.initializedPromise.then(function () { + var defaultValue = DEFAULT_PREFERENCES[name]; - if (defaultPref === undefined) { - console.error('Preferences_get: \'' + name + '\' is undefined.'); - return; - } else if (this.isInitializedPromiseResolved) { - var pref = this.prefs[name]; + if (defaultValue === undefined) { + throw new Error('preferencesGet: \'' + name + '\' is undefined.'); + } else { + var prefValue = this.prefs[name]; - if (pref !== undefined) { - return pref; + if (prefValue !== undefined) { + return prefValue; } } - return defaultPref; - } - }; - - return Preferences; -})(); + return defaultValue; + }.bind(this)); + } +}; //#if B2G -//Preferences.prototype.writeToStorage = function(prefObj) { -// asyncStorage.setItem('pdfjs.preferences', JSON.stringify(prefObj)); +//Preferences._writeToStorage = function (prefObj) { +// return new Promise(function (resolve) { +// asyncStorage.setItem('pdfjs.preferences', JSON.stringify(prefObj), +// resolve); +// }); //}; // -//Preferences.prototype.readFromStorage = function(prefObj) { -// var readFromStoragePromise = new Promise(function (resolve) { -// asyncStorage.getItem('pdfjs.preferences', function(prefStr) { +//Preferences._readFromStorage = function (prefObj) { +// return new Promise(function (resolve) { +// asyncStorage.getItem('pdfjs.preferences', function (prefStr) { // var readPrefs = JSON.parse(prefStr); // resolve(readPrefs); // }); // }); -// return readFromStoragePromise; //}; //#endif //#if !(FIREFOX || MOZCENTRAL || B2G) -Preferences.prototype.writeToStorage = function(prefObj) { - if (isLocalStorageEnabled) { - localStorage.setItem('pdfjs.preferences', JSON.stringify(prefObj)); - } +Preferences._writeToStorage = function (prefObj) { + return new Promise(function (resolve) { + if (isLocalStorageEnabled) { + localStorage.setItem('pdfjs.preferences', JSON.stringify(prefObj)); + } + resolve(); + }); }; -Preferences.prototype.readFromStorage = function(prefObj) { - var readFromStoragePromise = new Promise(function (resolve) { +Preferences._readFromStorage = function (prefObj) { + return new Promise(function (resolve) { + var readPrefs; if (isLocalStorageEnabled) { - var readPrefs = JSON.parse(localStorage.getItem('pdfjs.preferences')); - resolve(readPrefs); + readPrefs = JSON.parse(localStorage.getItem('pdfjs.preferences')); } + resolve(readPrefs); }); - return readFromStoragePromise; }; //#endif diff --git a/web/viewer.js b/web/viewer.js index 6239bd9af..3ec7665a0 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -140,6 +140,8 @@ var PDFView = { this.watchScroll(thumbnailContainer, this.thumbnailViewScroll, this.renderHighestPriority.bind(this)); + Preferences.initialize(); + PDFFindBar.initialize({ bar: document.getElementById('findbar'), toggleButton: document.getElementById('viewFind'), @@ -583,6 +585,9 @@ var PDFView = { pdfDataRangeTransport, args) { if (this.pdfDocument) { this.close(); + + // Reload the preferences if a document was previously opened. + Preferences.reload(); } var parameters = {password: password}; @@ -904,7 +909,6 @@ var PDFView = { mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}'); document.getElementById('pageNumber').max = pagesCount; - var prefs = PDFView.prefs = new Preferences(); PDFView.documentFingerprint = id; var store = PDFView.store = new ViewHistory(id); @@ -973,15 +977,26 @@ var PDFView = { PDFView.loadingBar.setWidth(container); PDFFindController.resolveFirstPage(); + + // Initialize the browsing history. + PDFHistory.initialize(self.documentFingerprint); }); - var prefsPromise = prefs.initializedPromise; - var storePromise = store.initializedPromise; - Promise.all([firstPagePromise, prefsPromise, storePromise]). - then(function() { - var showPreviousViewOnLoad = prefs.get('showPreviousViewOnLoad'); - var defaultZoomValue = prefs.get('defaultZoomValue'); + // Fetch the necessary preference values. + var showPreviousViewOnLoad; + var showPreviousViewOnLoadPromise = + Preferences.get('showPreviousViewOnLoad').then(function (prefValue) { + showPreviousViewOnLoad = prefValue; + }); + var defaultZoomValue; + var defaultZoomValuePromise = + Preferences.get('defaultZoomValue').then(function (prefValue) { + defaultZoomValue = prefValue; + }); + var storePromise = store.initializedPromise; + Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise, + defaultZoomValuePromise]).then(function resolved() { var storedHash = null; if (showPreviousViewOnLoad && store.get('exists', false)) { var pageNum = store.get('page', '1'); @@ -994,9 +1009,6 @@ var PDFView = { } else if (defaultZoomValue) { storedHash = 'page=1&zoom=' + defaultZoomValue; } - // Initialize the browsing history. - PDFHistory.initialize(self.documentFingerprint); - self.setInitialView(storedHash, scale); // Make all navigation keys work on document load, @@ -1007,6 +1019,12 @@ var PDFView = { // self.container.blur(); //#endif } + }, function rejected(errorMsg) { + console.error(errorMsg); + + firstPagePromise.then(function () { + self.setInitialView(null, scale); + }); }); pagesPromise.then(function() { @@ -1045,11 +1063,16 @@ var PDFView = { self.outline = new DocumentOutlineView(outline); document.getElementById('viewOutline').disabled = !outline; - if (outline && prefs.get('ifAvailableShowOutlineOnLoad')) { - if (!self.sidebarOpen) { - document.getElementById('sidebarToggle').click(); - } - self.switchSidebarView('outline'); + if (outline) { + Preferences.get('ifAvailableShowOutlineOnLoad').then( + function (prefValue) { + if (prefValue) { + if (!self.sidebarOpen) { + document.getElementById('sidebarToggle').click(); + } + self.switchSidebarView('outline'); + } + }); } }); }); From 4c9c43f82be35b1f8932eb42f8a91807f18034b1 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 26 Mar 2014 19:38:56 +0100 Subject: [PATCH 2/3] Modify {get, set}Preferences in PdfStreamConverter.jsm to support async --- extensions/firefox/content/PdfStreamConverter.jsm | 13 ++++++++++--- web/firefoxcom.js | 10 +++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/extensions/firefox/content/PdfStreamConverter.jsm b/extensions/firefox/content/PdfStreamConverter.jsm index 9e455a7f6..28ec7e156 100644 --- a/extensions/firefox/content/PdfStreamConverter.jsm +++ b/extensions/firefox/content/PdfStreamConverter.jsm @@ -452,7 +452,7 @@ ChromeActions.prototype = { getChromeWindow(this.domWindow).gFindBar .updateControlState(result, findPrevious); }, - setPreferences: function(prefs) { + setPreferences: function(prefs, sendResponse) { var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); var numberOfPrefs = 0; var prefValue, prefName; @@ -483,8 +483,11 @@ ChromeActions.prototype = { break; } } + if (sendResponse) { + sendResponse(true); + } }, - getPreferences: function(prefs) { + getPreferences: function(prefs, sendResponse) { var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); var currentPrefs = {}, numberOfPrefs = 0; var prefValue, prefName; @@ -510,7 +513,11 @@ ChromeActions.prototype = { break; } } - return JSON.stringify(currentPrefs); + if (sendResponse) { + sendResponse(JSON.stringify(currentPrefs)); + } else { + return JSON.stringify(currentPrefs); + } } }; diff --git a/web/firefoxcom.js b/web/firefoxcom.js index 3e1b9b2cc..8ccb87570 100644 --- a/web/firefoxcom.js +++ b/web/firefoxcom.js @@ -106,15 +106,15 @@ var DownloadManager = (function DownloadManagerClosure() { Preferences._writeToStorage = function (prefObj) { return new Promise(function (resolve) { - FirefoxCom.requestSync('setPreferences', prefObj); - resolve(); + FirefoxCom.request('setPreferences', prefObj, resolve); }); }; Preferences._readFromStorage = function (prefObj) { return new Promise(function (resolve) { - var readPrefs = JSON.parse(FirefoxCom.requestSync('getPreferences', - prefObj)); - resolve(readPrefs); + FirefoxCom.request('getPreferences', prefObj, function (prefStr) { + var readPrefs = JSON.parse(prefStr); + resolve(readPrefs); + }); }); }; From 42f9541081c3e9757d8540e9e9f2020c7c4561cf Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Mon, 17 Mar 2014 02:11:41 +0100 Subject: [PATCH 3/3] Add preference to automatically enable the Hand Tool when the viewer loads --- web/default_preferences.js | 3 ++- web/hand_tool.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/web/default_preferences.js b/web/default_preferences.js index e6417e540..8ef7ad90c 100644 --- a/web/default_preferences.js +++ b/web/default_preferences.js @@ -21,5 +21,6 @@ var DEFAULT_PREFERENCES = { showPreviousViewOnLoad: true, defaultZoomValue: '', - ifAvailableShowOutlineOnLoad: false + ifAvailableShowOutlineOnLoad: false, + enableHandToolOnLoad: false }; diff --git a/web/hand_tool.js b/web/hand_tool.js index 7b8f3eb2f..0405a68a6 100644 --- a/web/hand_tool.js +++ b/web/hand_tool.js @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals mozL10n, GrabToPan, PDFView, SecondaryToolbar */ +/* globals mozL10n, GrabToPan, PDFView, Preferences, SecondaryToolbar */ 'use strict'; @@ -43,8 +43,15 @@ var HandTool = { }); if (toggleHandTool) { toggleHandTool.addEventListener('click', this.toggle.bind(this), false); + + window.addEventListener('localized', function (evt) { + Preferences.get('enableHandToolOnLoad').then(function (prefValue) { + if (prefValue) { + this.handTool.activate(); + } + }.bind(this)); + }.bind(this)); } - // TODO: Read global prefs and call this.handTool.activate() if needed. }, toggle: function handToolToggle() {