|
1 #ifdef 0 |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 #endif |
|
6 |
|
7 /** |
|
8 * This singleton represents the whole 'New Tab Page' and takes care of |
|
9 * initializing all its components. |
|
10 */ |
|
11 let gPage = { |
|
12 /** |
|
13 * Initializes the page. |
|
14 */ |
|
15 init: function Page_init() { |
|
16 // Add ourselves to the list of pages to receive notifications. |
|
17 gAllPages.register(this); |
|
18 |
|
19 // Listen for 'unload' to unregister this page. |
|
20 addEventListener("unload", this, false); |
|
21 |
|
22 // XXX bug 991111 - Not all click events are correctly triggered when |
|
23 // listening from xhtml nodes -- in particular middle clicks on sites, so |
|
24 // listen from the xul window and filter then delegate |
|
25 addEventListener("click", this, false); |
|
26 |
|
27 // Initialize sponsored panel |
|
28 this._sponsoredPanel = document.getElementById("sponsored-panel"); |
|
29 let link = this._sponsoredPanel.querySelector(".text-link"); |
|
30 link.addEventListener("click", () => this._sponsoredPanel.hidePopup()); |
|
31 if (UpdateChannel.get().startsWith("release")) { |
|
32 document.getElementById("sponsored-panel-trial-descr").style.display = "none"; |
|
33 } |
|
34 else { |
|
35 document.getElementById("sponsored-panel-release-descr").style.display = "none"; |
|
36 } |
|
37 |
|
38 // Check if the new tab feature is enabled. |
|
39 let enabled = gAllPages.enabled; |
|
40 if (enabled) |
|
41 this._init(); |
|
42 |
|
43 this._updateAttributes(enabled); |
|
44 }, |
|
45 |
|
46 /** |
|
47 * True if the page is allowed to capture thumbnails using the background |
|
48 * thumbnail service. |
|
49 */ |
|
50 get allowBackgroundCaptures() { |
|
51 // The preloader is bypassed altogether for private browsing windows, and |
|
52 // therefore allow-background-captures will not be set. In that case, the |
|
53 // page is not preloaded and so it's visible, so allow background captures. |
|
54 return inPrivateBrowsingMode() || |
|
55 document.documentElement.getAttribute("allow-background-captures") == |
|
56 "true"; |
|
57 }, |
|
58 |
|
59 /** |
|
60 * Listens for notifications specific to this page. |
|
61 */ |
|
62 observe: function Page_observe(aSubject, aTopic, aData) { |
|
63 if (aTopic == "nsPref:changed") { |
|
64 let enabled = gAllPages.enabled; |
|
65 this._updateAttributes(enabled); |
|
66 |
|
67 // Initialize the whole page if we haven't done that, yet. |
|
68 if (enabled) { |
|
69 this._init(); |
|
70 } else { |
|
71 gUndoDialog.hide(); |
|
72 } |
|
73 } else if (aTopic == "page-thumbnail:create" && gGrid.ready) { |
|
74 for (let site of gGrid.sites) { |
|
75 if (site && site.url === aData) { |
|
76 site.refreshThumbnail(); |
|
77 } |
|
78 } |
|
79 } |
|
80 }, |
|
81 |
|
82 /** |
|
83 * Updates the whole page and the grid when the storage has changed. |
|
84 * @param aOnlyIfHidden If true, the page is updated only if it's hidden in |
|
85 * the preloader. |
|
86 */ |
|
87 update: function Page_update(aOnlyIfHidden=false) { |
|
88 let skipUpdate = aOnlyIfHidden && this.allowBackgroundCaptures; |
|
89 // The grid might not be ready yet as we initialize it asynchronously. |
|
90 if (gGrid.ready && !skipUpdate) { |
|
91 gGrid.refresh(); |
|
92 } |
|
93 }, |
|
94 |
|
95 /** |
|
96 * Shows sponsored panel |
|
97 */ |
|
98 showSponsoredPanel: function Page_showSponsoredPanel(aTarget) { |
|
99 if (this._sponsoredPanel.state == "closed") { |
|
100 let self = this; |
|
101 this._sponsoredPanel.addEventListener("popuphidden", function onPopupHidden(aEvent) { |
|
102 self._sponsoredPanel.removeEventListener("popuphidden", onPopupHidden, false); |
|
103 aTarget.removeAttribute("panelShown"); |
|
104 }); |
|
105 } |
|
106 aTarget.setAttribute("panelShown", "true"); |
|
107 this._sponsoredPanel.openPopup(aTarget); |
|
108 }, |
|
109 |
|
110 /** |
|
111 * Internally initializes the page. This runs only when/if the feature |
|
112 * is/gets enabled. |
|
113 */ |
|
114 _init: function Page_init() { |
|
115 if (this._initialized) |
|
116 return; |
|
117 |
|
118 this._initialized = true; |
|
119 |
|
120 gSearch.init(); |
|
121 |
|
122 this._mutationObserver = new MutationObserver(() => { |
|
123 if (this.allowBackgroundCaptures) { |
|
124 Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true); |
|
125 |
|
126 // Initialize type counting with the types we want to count |
|
127 let directoryCount = {}; |
|
128 for (let type of DirectoryLinksProvider.linkTypes) { |
|
129 directoryCount[type] = 0; |
|
130 } |
|
131 |
|
132 for (let site of gGrid.sites) { |
|
133 if (site) { |
|
134 site.captureIfMissing(); |
|
135 let {type} = site.link; |
|
136 if (type in directoryCount) { |
|
137 directoryCount[type]++; |
|
138 } |
|
139 } |
|
140 } |
|
141 |
|
142 // Record how many directory sites were shown, but place counts over the |
|
143 // default 9 in the same bucket |
|
144 for (let [type, count] of Iterator(directoryCount)) { |
|
145 let shownId = "NEWTAB_PAGE_DIRECTORY_" + type.toUpperCase() + "_SHOWN"; |
|
146 let shownCount = Math.min(10, count); |
|
147 Services.telemetry.getHistogramById(shownId).add(shownCount); |
|
148 } |
|
149 |
|
150 // content.js isn't loaded for the page while it's in the preloader, |
|
151 // which is why this is necessary. |
|
152 gSearch.setUpInitialState(); |
|
153 } |
|
154 }); |
|
155 this._mutationObserver.observe(document.documentElement, { |
|
156 attributes: true, |
|
157 attributeFilter: ["allow-background-captures"], |
|
158 }); |
|
159 |
|
160 gLinks.populateCache(function () { |
|
161 // Initialize and render the grid. |
|
162 gGrid.init(); |
|
163 |
|
164 // Initialize the drop target shim. |
|
165 gDropTargetShim.init(); |
|
166 |
|
167 #ifdef XP_MACOSX |
|
168 // Workaround to prevent a delay on MacOSX due to a slow drop animation. |
|
169 document.addEventListener("dragover", this, false); |
|
170 document.addEventListener("drop", this, false); |
|
171 #endif |
|
172 }.bind(this)); |
|
173 }, |
|
174 |
|
175 /** |
|
176 * Updates the 'page-disabled' attributes of the respective DOM nodes. |
|
177 * @param aValue Whether the New Tab Page is enabled or not. |
|
178 */ |
|
179 _updateAttributes: function Page_updateAttributes(aValue) { |
|
180 // Set the nodes' states. |
|
181 let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid, #newtab-search-container"; |
|
182 for (let node of document.querySelectorAll(nodeSelector)) { |
|
183 if (aValue) |
|
184 node.removeAttribute("page-disabled"); |
|
185 else |
|
186 node.setAttribute("page-disabled", "true"); |
|
187 } |
|
188 |
|
189 // Enables/disables the control and link elements. |
|
190 let inputSelector = ".newtab-control, .newtab-link"; |
|
191 for (let input of document.querySelectorAll(inputSelector)) { |
|
192 if (aValue) |
|
193 input.removeAttribute("tabindex"); |
|
194 else |
|
195 input.setAttribute("tabindex", "-1"); |
|
196 } |
|
197 |
|
198 // Update the toggle button's title. |
|
199 let toggle = document.getElementById("newtab-toggle"); |
|
200 toggle.setAttribute("title", newTabString(aValue ? "hide" : "show")); |
|
201 }, |
|
202 |
|
203 /** |
|
204 * Handles all page events. |
|
205 */ |
|
206 handleEvent: function Page_handleEvent(aEvent) { |
|
207 switch (aEvent.type) { |
|
208 case "unload": |
|
209 if (this._mutationObserver) |
|
210 this._mutationObserver.disconnect(); |
|
211 gAllPages.unregister(this); |
|
212 break; |
|
213 case "click": |
|
214 let {button, target} = aEvent; |
|
215 if (target.id == "newtab-toggle") { |
|
216 if (button == 0) { |
|
217 gAllPages.enabled = !gAllPages.enabled; |
|
218 } |
|
219 break; |
|
220 } |
|
221 |
|
222 // Go up ancestors until we find a Site or not |
|
223 while (target) { |
|
224 if (target.hasOwnProperty("_newtabSite")) { |
|
225 target._newtabSite.onClick(aEvent); |
|
226 break; |
|
227 } |
|
228 target = target.parentNode; |
|
229 } |
|
230 break; |
|
231 case "dragover": |
|
232 if (gDrag.isValid(aEvent) && gDrag.draggedSite) |
|
233 aEvent.preventDefault(); |
|
234 break; |
|
235 case "drop": |
|
236 if (gDrag.isValid(aEvent) && gDrag.draggedSite) { |
|
237 aEvent.preventDefault(); |
|
238 aEvent.stopPropagation(); |
|
239 } |
|
240 break; |
|
241 } |
|
242 } |
|
243 }; |