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 3// found in the LICENSE file. 4 5'use strict'; 6 7/** 8 * @param {Object.<string, string>} stringData String data. 9 * @param {VolumeManager} volumeManager Volume manager. 10 */ 11function BackgroundComponents(stringData, volumeManager) { 12 /** 13 * String data. 14 * @type {Object.<string, string>} 15 */ 16 this.stringData = stringData; 17 18 /** 19 * Volume manager. 20 * @type {VolumeManager} 21 */ 22 this.volumeManager = volumeManager; 23 24 Object.freeze(this); 25} 26 27/** 28 * Loads background component. 29 * @return {Promise} Promise fulfilled with BackgroundComponents. 30 */ 31BackgroundComponents.load = function() { 32 var stringDataPromise = new Promise(function(fulfill) { 33 chrome.fileBrowserPrivate.getStrings(function(stringData) { 34 loadTimeData.data = stringData; 35 fulfill(stringData); 36 }); 37 }); 38 39 // VolumeManager should be obtained after stringData initialized. 40 var volumeManagerPromise = stringDataPromise.then(function() { 41 return new Promise(function(fulfill) { 42 VolumeManager.getInstance(fulfill); 43 }); 44 }); 45 46 return Promise.all([stringDataPromise, volumeManagerPromise]).then( 47 function(args) { 48 return new BackgroundComponents(args[0], args[1]); 49 }); 50}; 51 52/** 53 * Promise to be fulfilled with singleton instance of background components. 54 * @type {Promise} 55 */ 56var backgroundComponentsPromise = BackgroundComponents.load(); 57 58/** 59 * Resolves file system names and obtains entries. 60 * @param {Array.<FileEntry>} entries Names of isolated file system. 61 * @return {Promise} Promise to be fulfilled with an entry array. 62 */ 63function resolveEntries(entries) { 64 return new Promise(function(fulfill, reject) { 65 chrome.fileBrowserPrivate.resolveIsolatedEntries(entries, 66 function(externalEntries) { 67 if (!chrome.runtime.lastError) 68 fulfill(externalEntries); 69 else 70 reject(chrome.runtime.lastError); 71 }); 72 }); 73} 74 75/** 76 * Obtains child entries. 77 * @param {DirectoryEntry} entry Directory entry. 78 * @return {Promise} Promise to be fulfilled with child entries. 79 */ 80function getChildren(entry) { 81 var reader = entry.createReader(); 82 var readEntries = function() { 83 return new Promise(reader.readEntries.bind(reader)).then(function(entries) { 84 if (entries.length === 0) 85 return []; 86 return readEntries().then(function(nextEntries) { 87 return entries.concat(nextEntries); 88 }); 89 }); 90 }; 91 return readEntries().then(function(entries) { 92 return entries.sort(function(a, b) { 93 return a.name.localeCompare(b.name); 94 }); 95 }); 96} 97 98/** 99 * Promise to be fulfilled with single application window. 100 * @type {Promise} 101 */ 102var appWindowPromise = Promise.resolve(null); 103 104/** 105 * Promise to be fulfilled with the current window is closed. 106 * @type {Promise} 107 */ 108var closingPromise = Promise.resolve(null); 109 110/** 111 * Launches the application with entries. 112 * 113 * @param {Promise} selectedEntriesPromise Promise to be fulfilled with the 114 * entries that are stored in the exteranl file system (not in the isolated 115 * file system). 116 */ 117function launch(selectedEntriesPromise) { 118 // If there is the previous window, close the window. 119 appWindowPromise = appWindowPromise.then(function(appWindow) { 120 if (appWindow) { 121 appWindow.close(); 122 return closingPromise; 123 } 124 }); 125 126 // Create a new window. 127 appWindowPromise = appWindowPromise.then(function() { 128 return new Promise(function(fulfill) { 129 chrome.app.window.create( 130 'gallery.html', 131 { 132 id: 'gallery', 133 innerBounds: { 134 minWidth: 800, 135 minHeight: 300 136 }, 137 frame: 'none' 138 }, 139 function(appWindow) { 140 appWindow.contentWindow.addEventListener( 141 'load', fulfill.bind(null, appWindow)); 142 closingPromise = new Promise(function(fulfill) { 143 appWindow.onClosed.addListener(fulfill); 144 }); 145 }); 146 }); 147 }); 148 149 // Initialize the window document. 150 appWindowPromise = Promise.all([ 151 appWindowPromise, 152 backgroundComponentsPromise, 153 ]).then(function(args) { 154 args[0].contentWindow.initialize(args[1]); 155 return args[0]; 156 }); 157 158 159 // If only 1 entry is selected, retrieve entries in the same directory. 160 // Otherwise, just use the selectedEntries as an entry set. 161 var allEntriesPromise = selectedEntriesPromise.then(function(entries) { 162 if (entries.length === 1) { 163 var parentPromise = new Promise(entries[0].getParent.bind(entries[0])); 164 return parentPromise.then(getChildren).then(function(entries) { 165 return entries.filter(FileType.isImage); 166 }); 167 } else { 168 return entries; 169 } 170 }); 171 172 // Open entries. 173 return Promise.all([ 174 appWindowPromise, 175 allEntriesPromise, 176 selectedEntriesPromise 177 ]).then(function(args) { 178 args[0].contentWindow.loadEntries(args[1], args[2]); 179 }); 180} 181 182chrome.app.runtime.onLaunched.addListener(function(launchData) { 183 // Skip if files are not selected. 184 if (!launchData || !launchData.items || launchData.items.length === 0) 185 return; 186 187 // Obtains entries in non-isolated file systems. 188 // The entries in launchData are stored in the isolated file system. 189 // We need to map the isolated entries to the normal entries to retrieve their 190 // parent directory. 191 var isolatedEntries = launchData.items.map(function(item) { 192 return item.entry; 193 }); 194 var selectedEntriesPromise = backgroundComponentsPromise.then(function() { 195 return resolveEntries(isolatedEntries); 196 }); 197 198 launch(selectedEntriesPromise).catch(function(error) { 199 console.error(error.stack || error); 200 }); 201}); 202 203// If is is run in the browser test, wait for the test resources are installed 204// as a component extension, and then load the test resources. 205if (chrome.test) { 206 chrome.runtime.onMessageExternal.addListener(function(message) { 207 if (message.name !== 'testResourceLoaded') 208 return; 209 var script = document.createElement('script'); 210 script.src = 211 'chrome-extension://ejhcmmdhhpdhhgmifplfmjobgegbibkn' + 212 '/gallery/test_loader.js'; 213 document.documentElement.appendChild(script); 214 }); 215} 216