LEFT | RIGHT |
1 /* | 1 /* |
2 * This Source Code is subject to the terms of the Mozilla Public License | 2 * This Source Code is subject to the terms of the Mozilla Public License |
3 * version 2.0 (the "License"). You can obtain a copy of the License at | 3 * version 2.0 (the "License"). You can obtain a copy of the License at |
4 * http://mozilla.org/MPL/2.0/. | 4 * http://mozilla.org/MPL/2.0/. |
5 */ | 5 */ |
6 | 6 |
7 /** | 7 /** |
8 * @module crawler | 8 * @module crawler |
9 */ | 9 */ |
10 | 10 |
(...skipping 23 matching lines...) Expand all Loading... |
34 * @constructor | 34 * @constructor |
35 */ | 35 */ |
36 function TabAllocator(browser, maxtabs) | 36 function TabAllocator(browser, maxtabs) |
37 { | 37 { |
38 this._browser = browser; | 38 this._browser = browser; |
39 this._tabs = 0; | 39 this._tabs = 0; |
40 this._maxtabs = maxtabs; | 40 this._maxtabs = maxtabs; |
41 // The queue containing resolve functions of promises waiting for a tab. | 41 // The queue containing resolve functions of promises waiting for a tab. |
42 this._resolvers = []; | 42 this._resolvers = []; |
43 // Keep at least one tab alive to prevent browser from closing itself. | 43 // Keep at least one tab alive to prevent browser from closing itself. |
44 let tabToRemove = this._browser.tabs[0]; | 44 this._tabKeepingWindowAlive = this._browser.tabs[0]; |
45 this._browser.removeAllTabsBut(tabToRemove); | 45 this._browser.removeAllTabsBut(this._tabKeepingWindowAlive); |
46 // this._tab is a keep alive tab | |
47 this._tab = this._createTab().then(tab => | |
48 { | |
49 // Starting from Firefox 48 (nightly) the sequence of calls addTab and | |
50 // removeTab can cause a closing of the browser because a new tab is still | |
51 // not here. Because of that we need to remove the previous tab only after | |
52 // the new tab is ready. | |
53 this._browser.removeTab(tabToRemove); | |
54 return tab; | |
55 }); | |
56 } | 46 } |
57 TabAllocator.prototype = { | 47 TabAllocator.prototype = { |
| 48 _removeTabKeepingWindowAlive: function() |
| 49 { |
| 50 if (!this._tabKeepingWindowAlive) |
| 51 return; |
| 52 this._browser.removeTab(this._tabKeepingWindowAlive); |
| 53 delete this._tabKeepingWindowAlive; |
| 54 }, |
| 55 |
58 /** | 56 /** |
59 * Creates a blank tab in this._browser. | 57 * Creates a blank tab in this._browser. |
60 * | 58 * |
61 * @return {Promise.<tab>} promise which resolves once the tab is fully initia
lized. | 59 * @return {Promise.<tab>} promise which resolves once the tab is fully initia
lized. |
62 */ | 60 */ |
63 _createTab: function() | 61 _createTab: function() |
64 { | 62 { |
65 this._tabs++; | 63 this._tabs++; |
66 let tab = this._browser.addTab("about:blank"); | 64 let tab = this._browser.addTab("about:blank"); |
67 if (tab.linkedBrowser.outerWindowID) | 65 if (tab.linkedBrowser.outerWindowID) |
| 66 { |
| 67 this._removeTabKeepingWindowAlive(); |
68 return Promise.resolve(tab); | 68 return Promise.resolve(tab); |
| 69 } |
69 return new Promise((resolve, reject) => | 70 return new Promise((resolve, reject) => |
70 { | 71 { |
71 let onBrowserInit = (msg) => | 72 let onBrowserInit = (msg) => |
72 { | 73 { |
73 tab.linkedBrowser.messageManager.removeMessageListener("Browser:Init", o
nBrowserInit); | 74 tab.linkedBrowser.messageManager.removeMessageListener("Browser:Init", o
nBrowserInit); |
| 75 this._removeTabKeepingWindowAlive(); |
74 resolve(tab); | 76 resolve(tab); |
75 }; | 77 }; |
76 // "Browser:Init" message is sent once the browser is ready, see | 78 // "Browser:Init" message is sent once the browser is ready, see |
77 // https://bugzil.la/1256602#c1 | 79 // https://bugzil.la/1256602#c1 |
78 tab.linkedBrowser.messageManager.addMessageListener("Browser:Init", onBrow
serInit); | 80 tab.linkedBrowser.messageManager.addMessageListener("Browser:Init", onBrow
serInit); |
79 }); | 81 }); |
80 }, | 82 }, |
81 | 83 |
82 /** | 84 /** |
83 * Returns a promise that will resolve into a tab once a tab is allocated. | 85 * Returns a promise that will resolve into a tab once a tab is allocated. |
84 * The tab cannot be used by other tasks until releaseTab() is called. | 86 * The tab cannot be used by other tasks until releaseTab() is called. |
85 * | 87 * |
86 * @result {Promise.<tab>} | 88 * @result {Promise.<tab>} |
87 */ | 89 */ |
88 getTab: function() | 90 getTab: function() |
89 { | 91 { |
90 if (this._tab) | |
91 { | |
92 let tab = this._tab; | |
93 delete this._tab; | |
94 return tab; | |
95 } | |
96 if (this._tabs < this._maxtabs) | 92 if (this._tabs < this._maxtabs) |
97 return this._createTab(); | 93 return this._createTab(); |
98 return new Promise((resolve, reject) => this._resolvers.push(resolve)); | 94 return new Promise((resolve, reject) => this._resolvers.push(resolve)); |
99 }, | 95 }, |
100 | 96 |
101 /** | 97 /** |
102 * Adds a tab back to the pool so that it can be used by other tasks. | 98 * Adds a tab back to the pool so that it can be used by other tasks. |
103 * | 99 * |
104 * @param {tab} tab | 100 * @param {tab} tab |
105 */ | 101 */ |
106 releaseTab: function(tab) | 102 releaseTab: function(tab) |
107 { | 103 { |
108 // If we are about to close last tab don't close it immediately rather | 104 // If we are about to close last tab don't close it immediately to keep |
109 // allocate a new blank tab and close the current one afterwards. | 105 // the window alive. It will be closed when a new tab is created. |
110 if (this._tabs == 1) | 106 if (this._tabs > 1) |
111 { | 107 this._browser.removeTab(tab); |
112 this._tab = this._createTab().then((resultTab) => | 108 else |
113 { | 109 { |
114 this.releaseTab(tab); | 110 // navigate away from early opened URL |
115 return resultTab; | 111 tab.linkedBrowser.loadURI('about:blank', null, null); |
116 }); | 112 this._tabKeepingWindowAlive = tab; |
117 return; | 113 } |
118 } | 114 |
119 | |
120 this._browser.removeTab(tab); | |
121 this._tabs--; | 115 this._tabs--; |
122 if (this._resolvers.length) | 116 if (this._resolvers.length && this._tabs < this._maxtabs) |
123 { | 117 { |
124 if (this._tab) | 118 this._resolvers.shift()(this._createTab()); |
125 { | |
126 this._resolvers.shift()(this._tab); | |
127 delete this._tab; | |
128 } | |
129 else if (this._tabs < this._maxtabs) | |
130 { | |
131 this._resolvers.shift()(this._createTab()); | |
132 } | |
133 } | 119 } |
134 }, | 120 }, |
135 }; | 121 }; |
136 | 122 |
137 /** | 123 /** |
138 * Observes page loads in a particular tabbed browser. | 124 * Observes page loads in a particular tabbed browser. |
139 * | 125 * |
140 * @param {tabbrowser} browser | 126 * @param {tabbrowser} browser |
141 * The tabbed browser to be observed | 127 * The tabbed browser to be observed |
142 * @param {int} timeout | 128 * @param {int} timeout |
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 | 394 |
409 function reportException(e) | 395 function reportException(e) |
410 { | 396 { |
411 let stack = ""; | 397 let stack = ""; |
412 if (e && typeof e == "object" && "stack" in e) | 398 if (e && typeof e == "object" && "stack" in e) |
413 stack = e.stack + "\n"; | 399 stack = e.stack + "\n"; |
414 | 400 |
415 Cu.reportError(e); | 401 Cu.reportError(e); |
416 dump(e + "\n" + stack + "\n"); | 402 dump(e + "\n" + stack + "\n"); |
417 } | 403 } |
LEFT | RIGHT |