| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @param {!Object.<string, string>} stringData String data. | 6 * Configuration of the Gallery window. |
| 7 * @param {!VolumeManager} volumeManager Volume manager. | 7 * @const |
| 8 * @constructor | 8 * @type {Object} |
| 9 * @struct | |
| 10 */ | 9 */ |
| 11 function BackgroundComponents(stringData, volumeManager) { | 10 var windowCreateOptions = { |
| 12 /** | 11 id: 'gallery', |
| 13 * String data. | 12 innerBounds: { |
| 14 * @type {!Object.<string, string>} | 13 minWidth: 820, |
| 15 */ | 14 minHeight: 554 |
| 16 this.stringData = stringData; | 15 }, |
| 17 | 16 frame: 'none' |
| 18 /** | |
| 19 * Volume manager. | |
| 20 * @type {!VolumeManager} | |
| 21 */ | |
| 22 this.volumeManager = volumeManager; | |
| 23 } | |
| 24 | |
| 25 /** | |
| 26 * Loads background component. | |
| 27 * @return {!Promise} Promise fulfilled with BackgroundComponents. | |
| 28 */ | |
| 29 BackgroundComponents.load = function() { | |
| 30 var stringDataPromise = new Promise(function(fulfill) { | |
| 31 chrome.fileManagerPrivate.getStrings(function(stringData) { | |
| 32 loadTimeData.data = stringData; | |
| 33 fulfill(stringData); | |
| 34 }); | |
| 35 }); | |
| 36 | |
| 37 // VolumeManager should be obtained after stringData initialized. | |
| 38 var volumeManagerPromise = stringDataPromise.then(function() { | |
| 39 return new Promise(function(fulfill) { | |
| 40 VolumeManager.getInstance(fulfill); | |
| 41 }); | |
| 42 }); | |
| 43 | |
| 44 return Promise.all([stringDataPromise, volumeManagerPromise]).then( | |
| 45 function(args) { | |
| 46 return new BackgroundComponents(args[0], args[1]); | |
| 47 }); | |
| 48 }; | 17 }; |
| 49 | 18 |
| 50 /** | 19 /** |
| 51 * Resolves file system names and obtains entries. | 20 * Backgound object. This is necessary for AppWindowWrapper. |
| 52 * @param {!Array.<!FileEntry>} entries Names of isolated file system. | 21 * @type {!BackgroundBase} |
| 53 * @return {!Promise} Promise to be fulfilled with an entry array. | |
| 54 */ | 22 */ |
| 55 function resolveEntries(entries) { | 23 var background = new BackgroundBase(); |
| 56 return new Promise(function(fulfill, reject) { | 24 |
| 25 |
| 26 // Initializes the strings. This needs for the volume manager. |
| 27 var loadTimeDataPromise = new Promise(function(fulfill, reject) { |
| 28 chrome.fileManagerPrivate.getStrings(function(stringData) { |
| 29 loadTimeData.data = stringData; |
| 30 fulfill(true); |
| 31 }); |
| 32 }); |
| 33 |
| 34 // Initializes the volume manager. This needs for isolated entries. |
| 35 var volumeManagerPromise = new Promise(function(fulfill, reject) { |
| 36 VolumeManager.getInstance(fulfill); |
| 37 }); |
| 38 |
| 39 /** |
| 40 * Queue to serialize initialization. |
| 41 * @type {!Promise} |
| 42 */ |
| 43 window.initializePromise = Promise.all([loadTimeDataPromise, |
| 44 volumeManagerPromise]); |
| 45 |
| 46 // Registers the handlers. |
| 47 chrome.app.runtime.onLaunched.addListener(onLaunched); |
| 48 |
| 49 /** |
| 50 * Called when an app is launched. |
| 51 * |
| 52 * @param {!Object} launchData Launch data. See the manual of chrome.app.runtime |
| 53 * .onLaunched for detail. |
| 54 */ |
| 55 function onLaunched(launchData) { |
| 56 // Skip if files are not selected. |
| 57 if (!launchData || !launchData.items || launchData.items.length == 0) |
| 58 return; |
| 59 |
| 60 window.initializePromise.then(function() { |
| 61 var isolatedEntries = launchData.items.map(function(item) { |
| 62 return item.entry; |
| 63 }); |
| 64 |
| 65 // Obtains entries in non-isolated file systems. |
| 66 // The entries in launchData are stored in the isolated file system. |
| 67 // We need to map the isolated entries to the normal entries to retrieve |
| 68 // their parent directory. |
| 57 chrome.fileManagerPrivate.resolveIsolatedEntries( | 69 chrome.fileManagerPrivate.resolveIsolatedEntries( |
| 58 entries, function(externalEntries) { | 70 isolatedEntries, |
| 59 if (!chrome.runtime.lastError) | 71 function(externalEntries) { |
| 60 fulfill(externalEntries); | 72 var urls = util.entriesToURLs(externalEntries); |
| 61 else | 73 openGalleryWindow(urls, false); |
| 62 reject(chrome.runtime.lastError); | |
| 63 }); | 74 }); |
| 64 }); | 75 }); |
| 65 } | 76 } |
| 66 | 77 |
| 67 /** | 78 /** |
| 68 * Promise to be fulfilled with singleton instance of background components. | 79 * Returns a function to generate an ID for window. |
| 69 * @type {Promise} | 80 * @type {function():string} Function which returns an unique id. |
| 70 */ | 81 */ |
| 71 var backgroundComponentsPromise = null; | 82 var generateWindowId = (function() { |
| 83 var seq = 0; |
| 84 return function() { |
| 85 return 'GALLERY_' + seq++; |
| 86 }; |
| 87 })(); |
| 72 | 88 |
| 73 /** | 89 /** |
| 74 * Promise to be fulfilled with single application window. | 90 * Opens gallery window. |
| 75 * This can be null when the window is not opened. | 91 * @param {!Array.<string>} urls List of URL to show. |
| 76 * @type {Promise} | 92 * @param {boolean} reopen True if reopen, false otherwise. |
| 93 * @return {!Promise} Promise to be fulfilled on success, or rejected on error. |
| 77 */ | 94 */ |
| 78 var appWindowPromise = null; | 95 function openGalleryWindow(urls, reopen) { |
| 96 return new Promise(function(fulfill, reject) { |
| 97 util.URLsToEntries(urls).then(function(result) { |
| 98 fulfill(util.entriesToURLs(result.entries)); |
| 99 }).catch(reject); |
| 100 }).then(function(urls) { |
| 101 if (urls.length === 0) |
| 102 return Promise.reject('No file to open.'); |
| 79 | 103 |
| 80 /** | 104 var windowId = generateWindowId(); |
| 81 * Promise to be fulfilled with entries that are used for reopening the | |
| 82 * application window. | |
| 83 * @type {Promise} | |
| 84 */ | |
| 85 var reopenEntriesPromise = null; | |
| 86 | 105 |
| 87 /** | 106 // Opens a window. |
| 88 * Launches the application with entries. | 107 return new Promise(function(fulfill, reject) { |
| 89 * | 108 var gallery = new AppWindowWrapper('gallery.html', |
| 90 * @param {!Promise} selectedEntriesPromise Promise to be fulfilled with the | 109 windowId, |
| 91 * entries that are stored in the external file system (not in the isolated | 110 windowCreateOptions); |
| 92 * file system). | 111 |
| 93 * @return {!Promise} Promise to be fulfilled after the application is launched. | 112 gallery.launch( |
| 94 */ | 113 {urls: urls}, |
| 95 function launch(selectedEntriesPromise) { | 114 reopen, |
| 96 // If there is the previous window, close the window. | 115 fulfill.bind(null, gallery)); |
| 97 if (appWindowPromise) { | 116 }).then(function(gallery) { |
| 98 reopenEntriesPromise = selectedEntriesPromise; | 117 var galleryDocument = gallery.rawAppWindow.contentWindow.document; |
| 99 appWindowPromise.then(function(appWindow) { | 118 if (galleryDocument.readyState == 'complete') |
| 100 appWindow.close(); | 119 return gallery; |
| 120 |
| 121 return new Promise(function(fulfill, reject) { |
| 122 galleryDocument.addEventListener( |
| 123 'DOMContentLoaded', fulfill.bind(null, gallery)); |
| 124 }); |
| 101 }); | 125 }); |
| 102 return Promise.reject('The window has already opened.'); | 126 }).then(function(gallery) { |
| 103 } | 127 gallery.rawAppWindow.focus(); |
| 104 reopenEntriesPromise = null; | 128 return gallery.rawAppWindow; |
| 105 | 129 }).catch(function(error) { |
| 106 // Create a new window. | 130 console.error('Launch failed' + error.stack || error); |
| 107 appWindowPromise = new Promise(function(fulfill) { | 131 return Promise.reject(error); |
| 108 chrome.app.window.create( | |
| 109 'gallery.html', | |
| 110 { | |
| 111 id: 'gallery', | |
| 112 innerBounds: { | |
| 113 minWidth: 820, | |
| 114 minHeight: 544 | |
| 115 }, | |
| 116 frame: 'none' | |
| 117 }, | |
| 118 function(appWindow) { | |
| 119 appWindow.contentWindow.addEventListener( | |
| 120 'load', fulfill.bind(null, appWindow)); | |
| 121 appWindow.onClosed.addListener(function() { | |
| 122 appWindowPromise = null; | |
| 123 if (reopenEntriesPromise) { | |
| 124 // TODO(hirono): This is workaround for crbug.com/442217. Remove | |
| 125 // this after fixing it. | |
| 126 setTimeout(function() { | |
| 127 if (reopenEntriesPromise) | |
| 128 launch(reopenEntriesPromise); | |
| 129 }, 500); | |
| 130 } | |
| 131 }); | |
| 132 }); | |
| 133 }); | |
| 134 | |
| 135 // Initialize the window document. | |
| 136 return Promise.all([ | |
| 137 appWindowPromise, | |
| 138 backgroundComponentsPromise | |
| 139 ]).then(function(args) { | |
| 140 var appWindow = /** @type {!chrome.app.window.AppWindow} */ (args[0]); | |
| 141 var galleryWindow = /** @type {!GalleryWindow} */ (appWindow.contentWindow); | |
| 142 galleryWindow.initialize(args[1]); | |
| 143 return selectedEntriesPromise.then(function(entries) { | |
| 144 galleryWindow.loadEntries(entries); | |
| 145 }); | |
| 146 }); | 132 }); |
| 147 } | 133 } |
| 148 | 134 |
| 149 // If the script is loaded from unit test, chrome.app.runtime is not defined. | |
| 150 // In this case, does not run the initialization code for the application. | |
| 151 if (chrome.app.runtime) { | |
| 152 backgroundComponentsPromise = BackgroundComponents.load(); | |
| 153 chrome.app.runtime.onLaunched.addListener(function(launchData) { | |
| 154 // Skip if files are not selected. | |
| 155 if (!launchData || !launchData.items || launchData.items.length === 0) | |
| 156 return; | |
| 157 | |
| 158 // Obtains entries in non-isolated file systems. | |
| 159 // The entries in launchData are stored in the isolated file system. | |
| 160 // We need to map the isolated entries to the normal entries to retrieve | |
| 161 // their parent directory. | |
| 162 var isolatedEntries = launchData.items.map(function(item) { | |
| 163 return item.entry; | |
| 164 }); | |
| 165 var selectedEntriesPromise = backgroundComponentsPromise.then(function() { | |
| 166 return resolveEntries(isolatedEntries); | |
| 167 }); | |
| 168 | |
| 169 launch(selectedEntriesPromise).catch(function(error) { | |
| 170 console.error(error.stack || error); | |
| 171 }); | |
| 172 }); | |
| 173 } | |
| 174 | |
| 175 // If is is run in the browser test, wait for the test resources are installed | 135 // If is is run in the browser test, wait for the test resources are installed |
| 176 // as a component extension, and then load the test resources. | 136 // as a component extension, and then load the test resources. |
| 177 if (chrome.test) { | 137 if (chrome.test) { |
| 178 // Sets a global flag that we are in tests, so other components are aware of | 138 // Sets a global flag that we are in tests, so other components are aware of |
| 179 // it. | 139 // it. |
| 180 window.IN_TEST = true; | 140 window.IN_TEST = true; |
| 181 | 141 |
| 182 /** @type {string} */ | 142 /** @type {string} */ |
| 183 window.testExtensionId = 'ejhcmmdhhpdhhgmifplfmjobgegbibkn'; | 143 window.testExtensionId = 'ejhcmmdhhpdhhgmifplfmjobgegbibkn'; |
| 184 chrome.runtime.onMessageExternal.addListener(function(message) { | 144 chrome.runtime.onMessageExternal.addListener(function(message) { |
| 185 if (message.name !== 'testResourceLoaded') | 145 if (message.name !== 'testResourceLoaded') |
| 186 return; | 146 return; |
| 187 var script = document.createElement('script'); | 147 var script = document.createElement('script'); |
| 188 script.src = | 148 script.src = |
| 189 'chrome-extension://' + window.testExtensionId + | 149 'chrome-extension://' + window.testExtensionId + |
| 190 '/gallery/test_loader.js'; | 150 '/gallery/test_loader.js'; |
| 191 document.documentElement.appendChild(script); | 151 document.documentElement.appendChild(script); |
| 192 }); | 152 }); |
| 193 } | 153 } |
| OLD | NEW |