Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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
7 /**
8 * This class represents a site that is contained in a cell and can be pinned,
9 * moved around or deleted.
10 */
11 function Site(aNode, aLink) {
12 this._node = aNode;
13 this._node._newtabSite = this;
15 this._link = aLink;
17 this._render();
18 this._addEventHandlers();
19 }
21 Site.prototype = {
22 /**
23 * The site's DOM node.
24 */
25 get node() this._node,
27 /**
28 * The site's link.
29 */
30 get link() this._link,
32 /**
33 * The url of the site's link.
34 */
35 get url() this.link.url,
37 /**
38 * The title of the site's link.
39 */
40 get title() this.link.title,
42 /**
43 * The site's parent cell.
44 */
45 get cell() {
46 let parentNode = this.node.parentNode;
47 return parentNode && parentNode._newtabCell;
48 },
50 /**
51 * Pins the site on its current or a given index.
52 * @param aIndex The pinned index (optional).
53 */
54 pin: function Site_pin(aIndex) {
55 if (typeof aIndex == "undefined")
56 aIndex = this.cell.index;
58 this._updateAttributes(true);
59 gPinnedLinks.pin(this._link, aIndex);
60 },
62 /**
63 * Unpins the site and calls the given callback when done.
64 */
65 unpin: function Site_unpin() {
66 if (this.isPinned()) {
67 this._updateAttributes(false);
68 gPinnedLinks.unpin(this._link);
69 gUpdater.updateGrid();
70 }
71 },
73 /**
74 * Checks whether this site is pinned.
75 * @return Whether this site is pinned.
76 */
77 isPinned: function Site_isPinned() {
78 return gPinnedLinks.isPinned(this._link);
79 },
81 /**
82 * Blocks the site (removes it from the grid) and calls the given callback
83 * when done.
84 */
85 block: function Site_block() {
86 if (!gBlockedLinks.isBlocked(this._link)) {
87 gUndoDialog.show(this);
88 gBlockedLinks.block(this._link);
89 gUpdater.updateGrid();
90 }
91 },
93 /**
94 * Gets the DOM node specified by the given query selector.
95 * @param aSelector The query selector.
96 * @return The DOM node we found.
97 */
98 _querySelector: function Site_querySelector(aSelector) {
99 return this.node.querySelector(aSelector);
100 },
102 /**
103 * Updates attributes for all nodes which status depends on this site being
104 * pinned or unpinned.
105 * @param aPinned Whether this site is now pinned or unpinned.
106 */
107 _updateAttributes: function (aPinned) {
108 let control = this._querySelector(".newtab-control-pin");
110 if (aPinned) {
111 control.setAttribute("pinned", true);
112 control.setAttribute("title", newTabString("unpin"));
113 } else {
114 control.removeAttribute("pinned");
115 control.setAttribute("title", newTabString("pin"));
116 }
117 },
119 /**
120 * Renders the site's data (fills the HTML fragment).
121 */
122 _render: function Site_render() {
123 let url = this.url;
124 let title = this.title || url;
125 let tooltip = (title == url ? title : title + "\n" + url);
127 let link = this._querySelector(".newtab-link");
128 link.setAttribute("title", tooltip);
129 link.setAttribute("href", url);
130 this._querySelector(".newtab-title").textContent = title;
131 this.node.setAttribute("type", this.link.type);
133 if (this.isPinned())
134 this._updateAttributes(true);
135 // Capture the page if the thumbnail is missing, which will cause page.js
136 // to be notified and call our refreshThumbnail() method.
137 this.captureIfMissing();
138 // but still display whatever thumbnail might be available now.
139 this.refreshThumbnail();
140 },
142 /**
143 * Captures the site's thumbnail in the background, but only if there's no
144 * existing thumbnail and the page allows background captures.
145 */
146 captureIfMissing: function Site_captureIfMissing() {
147 if (gPage.allowBackgroundCaptures && !this.link.imageURI) {
148 BackgroundPageThumbs.captureIfMissing(this.url);
149 }
150 },
152 /**
153 * Refreshes the thumbnail for the site.
154 */
155 refreshThumbnail: function Site_refreshThumbnail() {
156 let thumbnail = this._querySelector(".newtab-thumbnail");
157 if (this.link.bgColor) {
158 thumbnail.style.backgroundColor = this.link.bgColor;
159 }
160 let uri = this.link.imageURI || PageThumbs.getThumbnailURL(this.url);
161 thumbnail.style.backgroundImage = "url(" + uri + ")";
162 },
164 /**
165 * Adds event handlers for the site and its buttons.
166 */
167 _addEventHandlers: function Site_addEventHandlers() {
168 // Register drag-and-drop event handlers.
169 this._node.addEventListener("dragstart", this, false);
170 this._node.addEventListener("dragend", this, false);
171 this._node.addEventListener("mouseover", this, false);
173 // Specially treat the sponsored icon to prevent regular hover effects
174 let sponsored = this._querySelector(".newtab-control-sponsored");
175 sponsored.addEventListener("mouseover", () => {
176 this.cell.node.setAttribute("ignorehover", "true");
177 });
178 sponsored.addEventListener("mouseout", () => {
179 this.cell.node.removeAttribute("ignorehover");
180 });
181 },
183 /**
184 * Speculatively opens a connection to the current site.
185 */
186 _speculativeConnect: function Site_speculativeConnect() {
187 let sc = Services.io.QueryInterface(Ci.nsISpeculativeConnect);
188 let uri = Services.io.newURI(this.url, null, null);
189 sc.speculativeConnect(uri, null);
190 },
192 /**
193 * Record interaction with site using telemetry.
194 */
195 _recordSiteClicked: function Site_recordSiteClicked(aIndex) {
196 if (Services.prefs.prefHasUserValue("browser.newtabpage.rows") ||
197 Services.prefs.prefHasUserValue("browser.newtabpage.columns") ||
198 aIndex > 8) {
199 // We only want to get indices for the default configuration, everything
200 // else goes in the same bucket.
201 aIndex = 9;
202 }
203 Services.telemetry.getHistogramById("NEWTAB_PAGE_SITE_CLICKED")
204 .add(aIndex);
206 // Specially count clicks on directory tiles
207 let typeIndex = DirectoryLinksProvider.linkTypes.indexOf(this.link.type);
208 if (typeIndex != -1) {
209 Services.telemetry.getHistogramById("NEWTAB_PAGE_DIRECTORY_TYPE_CLICKED")
210 .add(typeIndex);
211 }
212 },
214 /**
215 * Handles site click events.
216 */
217 onClick: function Site_onClick(aEvent) {
218 let {button, target} = aEvent;
219 if (target.classList.contains("newtab-link") ||
220 target.parentElement.classList.contains("newtab-link")) {
221 // Record for primary and middle clicks
222 if (button == 0 || button == 1) {
223 this._recordSiteClicked(this.cell.index);
224 }
225 return;
226 }
228 // Only handle primary clicks for the remaining targets
229 if (button != 0) {
230 return;
231 }
233 aEvent.preventDefault();
234 if (aEvent.target.classList.contains("newtab-control-block"))
235 this.block();
236 else if (target.classList.contains("newtab-control-sponsored"))
237 gPage.showSponsoredPanel(target);
238 else if (this.isPinned())
239 this.unpin();
240 else
241 this.pin();
242 },
244 /**
245 * Handles all site events.
246 */
247 handleEvent: function Site_handleEvent(aEvent) {
248 switch (aEvent.type) {
249 case "mouseover":
250 this._node.removeEventListener("mouseover", this, false);
251 this._speculativeConnect();
252 break;
253 case "dragstart":
254 gDrag.start(this, aEvent);
255 break;
256 case "dragend":
257 gDrag.end(this, aEvent);
258 break;
259 }
260 }
261 };