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 |