// Copyright (C) 2016 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. (function() { 'use strict'; // Date cutoff is one day: const DRAFT_MAX_AGE = 24 * 60 * 60 * 1000; // Clean up old entries no more frequently than one day. const CLEANUP_THROTTLE_INTERVAL = 24 * 60 * 60 * 1000; Polymer({ is: 'gr-storage', properties: { _lastCleanup: Number, /** @type {?Storage} */ _storage: { type: Object, value() { return window.localStorage; }, }, _exceededQuota: { type: Boolean, value: false, }, }, getDraftComment(location) { this._cleanupDrafts(); return this._getObject(this._getDraftKey(location)); }, setDraftComment(location, message) { const key = this._getDraftKey(location); this._setObject(key, {message, updated: Date.now()}); }, eraseDraftComment(location) { const key = this._getDraftKey(location); this._storage.removeItem(key); }, getPreferences() { return this._getObject('localPrefs'); }, savePreferences(localPrefs) { this._setObject('localPrefs', localPrefs || null); }, _getDraftKey(location) { const range = location.range ? `${location.range.start_line}-${location.range.start_character}` + `-${location.range.end_character}-${location.range.end_line}` : null; let key = ['draft', location.changeNum, location.patchNum, location.path, location.line || ''].join(':'); if (range) { key = key + ':' + range; } return key; }, _cleanupDrafts() { // Throttle cleanup to the throttle interval. if (this._lastCleanup && Date.now() - this._lastCleanup < CLEANUP_THROTTLE_INTERVAL) { return; } this._lastCleanup = Date.now(); let draft; for (const key in this._storage) { if (key.startsWith('draft:')) { draft = this._getObject(key); if (Date.now() - draft.updated > DRAFT_MAX_AGE) { this._storage.removeItem(key); } } } }, _getObject(key) { const serial = this._storage.getItem(key); if (!serial) { return null; } return JSON.parse(serial); }, _setObject(key, obj) { if (this._exceededQuota) { return; } try { this._storage.setItem(key, JSON.stringify(obj)); } catch (exc) { // Catch for QuotaExceededError and disable writes on local storage the // first time that it occurs. if (exc.code === 22) { this._exceededQuota = true; console.warn('Local storage quota exceeded: disabling'); return; } else { throw exc; } } }, }); })();