|
1 <?xml version="1.0"?> |
|
2 |
|
3 # -*- Mode: HTML -*- |
|
4 # This Source Code Form is subject to the terms of the Mozilla Public |
|
5 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
7 |
|
8 <!DOCTYPE bindings [ |
|
9 <!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd"> |
|
10 %notificationDTD; |
|
11 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> |
|
12 %browserDTD; |
|
13 ]> |
|
14 |
|
15 <bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl" |
|
16 xmlns:html="http://www.w3.org/1999/xhtml" |
|
17 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
|
18 xmlns:xbl="http://www.mozilla.org/xbl"> |
|
19 |
|
20 <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> |
|
21 |
|
22 <content sizetopopup="pref"> |
|
23 <xul:hbox anonid="textbox-container" |
|
24 class="autocomplete-textbox-container urlbar-textbox-container" |
|
25 flex="1" xbl:inherits="focused"> |
|
26 <children includes="image|deck|stack|box"> |
|
27 <xul:image class="autocomplete-icon" allowevents="true"/> |
|
28 </children> |
|
29 <xul:hbox anonid="textbox-input-box" |
|
30 class="textbox-input-box urlbar-input-box" |
|
31 flex="1" xbl:inherits="tooltiptext=inputtooltiptext"> |
|
32 <children/> |
|
33 <html:input anonid="input" |
|
34 class="autocomplete-textbox urlbar-input textbox-input uri-element-right-align" |
|
35 allowevents="true" |
|
36 xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/> |
|
37 </xul:hbox> |
|
38 <children includes="hbox"/> |
|
39 </xul:hbox> |
|
40 <xul:dropmarker anonid="historydropmarker" |
|
41 class="autocomplete-history-dropmarker urlbar-history-dropmarker" |
|
42 allowevents="true" |
|
43 xbl:inherits="open,enablehistory,parentfocused=focused"/> |
|
44 <xul:popupset anonid="popupset" |
|
45 class="autocomplete-result-popupset"/> |
|
46 <children includes="toolbarbutton"/> |
|
47 </content> |
|
48 |
|
49 <implementation implements="nsIObserver, nsIDOMEventListener"> |
|
50 <constructor><![CDATA[ |
|
51 this._prefs = Components.classes["@mozilla.org/preferences-service;1"] |
|
52 .getService(Components.interfaces.nsIPrefService) |
|
53 .getBranch("browser.urlbar."); |
|
54 |
|
55 this._prefs.addObserver("", this, false); |
|
56 this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll"); |
|
57 this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll"); |
|
58 this.completeDefaultIndex = this._prefs.getBoolPref("autoFill"); |
|
59 this.timeout = this._prefs.getIntPref("delay"); |
|
60 this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled"); |
|
61 this._mayTrimURLs = this._prefs.getBoolPref("trimURLs"); |
|
62 this._ignoreNextSelect = false; |
|
63 |
|
64 this.inputField.controllers.insertControllerAt(0, this._copyCutController); |
|
65 this.inputField.addEventListener("mousedown", this, false); |
|
66 this.inputField.addEventListener("mousemove", this, false); |
|
67 this.inputField.addEventListener("mouseout", this, false); |
|
68 this.inputField.addEventListener("overflow", this, false); |
|
69 this.inputField.addEventListener("underflow", this, false); |
|
70 |
|
71 try { |
|
72 if (this._prefs.getBoolPref("unifiedcomplete")) { |
|
73 this.setAttribute("autocompletesearch", "unifiedcomplete"); |
|
74 } |
|
75 } catch (ex) {} |
|
76 |
|
77 const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
78 var textBox = document.getAnonymousElementByAttribute(this, |
|
79 "anonid", "textbox-input-box"); |
|
80 var cxmenu = document.getAnonymousElementByAttribute(textBox, |
|
81 "anonid", "input-box-contextmenu"); |
|
82 var pasteAndGo; |
|
83 cxmenu.addEventListener("popupshowing", function() { |
|
84 if (!pasteAndGo) |
|
85 return; |
|
86 var controller = document.commandDispatcher.getControllerForCommand("cmd_paste"); |
|
87 var enabled = controller.isCommandEnabled("cmd_paste"); |
|
88 if (enabled) |
|
89 pasteAndGo.removeAttribute("disabled"); |
|
90 else |
|
91 pasteAndGo.setAttribute("disabled", "true"); |
|
92 }, false); |
|
93 |
|
94 var insertLocation = cxmenu.firstChild; |
|
95 while (insertLocation.nextSibling && |
|
96 insertLocation.getAttribute("cmd") != "cmd_paste") |
|
97 insertLocation = insertLocation.nextSibling; |
|
98 if (insertLocation) { |
|
99 pasteAndGo = document.createElement("menuitem"); |
|
100 let label = Services.strings.createBundle("chrome://browser/locale/browser.properties"). |
|
101 GetStringFromName("pasteAndGo.label"); |
|
102 pasteAndGo.setAttribute("label", label); |
|
103 pasteAndGo.setAttribute("anonid", "paste-and-go"); |
|
104 pasteAndGo.setAttribute("oncommand", |
|
105 "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();"); |
|
106 cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling); |
|
107 } |
|
108 ]]></constructor> |
|
109 |
|
110 <destructor><![CDATA[ |
|
111 this._prefs.removeObserver("", this); |
|
112 this._prefs = null; |
|
113 this.inputField.controllers.removeController(this._copyCutController); |
|
114 this.inputField.removeEventListener("mousedown", this, false); |
|
115 this.inputField.removeEventListener("mousemove", this, false); |
|
116 this.inputField.removeEventListener("mouseout", this, false); |
|
117 this.inputField.removeEventListener("overflow", this, false); |
|
118 this.inputField.removeEventListener("underflow", this, false); |
|
119 ]]></destructor> |
|
120 |
|
121 <field name="_value"></field> |
|
122 |
|
123 <!-- |
|
124 onBeforeValueGet is called by the base-binding's .value getter. |
|
125 It can return an object with a "value" property, to override the |
|
126 return value of the getter. |
|
127 --> |
|
128 <method name="onBeforeValueGet"> |
|
129 <body><![CDATA[ |
|
130 if (this.hasAttribute("actiontype")) |
|
131 return {value: this._value}; |
|
132 return null; |
|
133 ]]></body> |
|
134 </method> |
|
135 |
|
136 <!-- |
|
137 onBeforeValueSet is called by the base-binding's .value setter. |
|
138 It should return the value that the setter should use. |
|
139 --> |
|
140 <method name="onBeforeValueSet"> |
|
141 <parameter name="aValue"/> |
|
142 <body><![CDATA[ |
|
143 this._value = aValue; |
|
144 var returnValue = aValue; |
|
145 var action = this._parseActionUrl(aValue); |
|
146 // Don't put back the action if we are invoked while override actions |
|
147 // is active. |
|
148 if (action && this._numNoActionsKeys <= 0) { |
|
149 returnValue = action.param; |
|
150 this.setAttribute("actiontype", action.type); |
|
151 } else { |
|
152 this.removeAttribute("actiontype"); |
|
153 } |
|
154 return returnValue; |
|
155 ]]></body> |
|
156 </method> |
|
157 |
|
158 <field name="_mayTrimURLs">true</field> |
|
159 <method name="trimValue"> |
|
160 <parameter name="aURL"/> |
|
161 <body><![CDATA[ |
|
162 // This method must not modify the given URL such that calling |
|
163 // nsIURIFixup::createFixupURI with the result will produce a different URI. |
|
164 return this._mayTrimURLs ? trimURL(aURL) : aURL; |
|
165 ]]></body> |
|
166 </method> |
|
167 |
|
168 <field name="_formattingEnabled">true</field> |
|
169 <method name="formatValue"> |
|
170 <body><![CDATA[ |
|
171 if (!this._formattingEnabled || this.focused) |
|
172 return; |
|
173 |
|
174 let controller = this.editor.selectionController; |
|
175 let selection = controller.getSelection(controller.SELECTION_URLSECONDARY); |
|
176 selection.removeAllRanges(); |
|
177 |
|
178 let textNode = this.editor.rootElement.firstChild; |
|
179 let value = textNode.textContent; |
|
180 |
|
181 let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/); |
|
182 if (protocol && |
|
183 ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1) |
|
184 return; |
|
185 let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/); |
|
186 if (!matchedURL) |
|
187 return; |
|
188 |
|
189 let [, preDomain, domain] = matchedURL; |
|
190 let baseDomain = domain; |
|
191 let subDomain = ""; |
|
192 // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159) |
|
193 if (domain[0] != "[") { |
|
194 try { |
|
195 baseDomain = Services.eTLD.getBaseDomainFromHost(domain); |
|
196 if (!domain.endsWith(baseDomain)) { |
|
197 // getBaseDomainFromHost converts its resultant to ACE. |
|
198 let IDNService = Cc["@mozilla.org/network/idn-service;1"] |
|
199 .getService(Ci.nsIIDNService); |
|
200 baseDomain = IDNService.convertACEtoUTF8(baseDomain); |
|
201 } |
|
202 } catch (e) {} |
|
203 } |
|
204 if (baseDomain != domain) { |
|
205 subDomain = domain.slice(0, -baseDomain.length); |
|
206 } |
|
207 |
|
208 let rangeLength = preDomain.length + subDomain.length; |
|
209 if (rangeLength) { |
|
210 let range = document.createRange(); |
|
211 range.setStart(textNode, 0); |
|
212 range.setEnd(textNode, rangeLength); |
|
213 selection.addRange(range); |
|
214 } |
|
215 |
|
216 let startRest = preDomain.length + domain.length; |
|
217 if (startRest < value.length) { |
|
218 let range = document.createRange(); |
|
219 range.setStart(textNode, startRest); |
|
220 range.setEnd(textNode, value.length); |
|
221 selection.addRange(range); |
|
222 } |
|
223 ]]></body> |
|
224 </method> |
|
225 |
|
226 <method name="_clearFormatting"> |
|
227 <body><![CDATA[ |
|
228 if (!this._formattingEnabled) |
|
229 return; |
|
230 |
|
231 let controller = this.editor.selectionController; |
|
232 let selection = controller.getSelection(controller.SELECTION_URLSECONDARY); |
|
233 selection.removeAllRanges(); |
|
234 ]]></body> |
|
235 </method> |
|
236 |
|
237 <method name="handleRevert"> |
|
238 <body><![CDATA[ |
|
239 var isScrolling = this.popupOpen; |
|
240 |
|
241 gBrowser.userTypedValue = null; |
|
242 |
|
243 // don't revert to last valid url unless page is NOT loading |
|
244 // and user is NOT key-scrolling through autocomplete list |
|
245 if (!XULBrowserWindow.isBusy && !isScrolling) { |
|
246 URLBarSetURI(); |
|
247 |
|
248 // If the value isn't empty and the urlbar has focus, select the value. |
|
249 if (this.value && this.hasAttribute("focused")) |
|
250 this.select(); |
|
251 } |
|
252 |
|
253 // tell widget to revert to last typed text only if the user |
|
254 // was scrolling when they hit escape |
|
255 return !isScrolling; |
|
256 ]]></body> |
|
257 </method> |
|
258 |
|
259 <method name="handleCommand"> |
|
260 <parameter name="aTriggeringEvent"/> |
|
261 <body><![CDATA[ |
|
262 if (aTriggeringEvent instanceof MouseEvent && aTriggeringEvent.button == 2) |
|
263 return; // Do nothing for right clicks |
|
264 |
|
265 var url = this.value; |
|
266 var mayInheritPrincipal = false; |
|
267 var postData = null; |
|
268 |
|
269 var action = this._parseActionUrl(url); |
|
270 let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange; |
|
271 |
|
272 let matchLastLocationChange = true; |
|
273 if (action) { |
|
274 url = action.param; |
|
275 if (this.hasAttribute("actiontype")) { |
|
276 if (action.type == "switchtab") { |
|
277 this.handleRevert(); |
|
278 let prevTab = gBrowser.selectedTab; |
|
279 if (switchToTabHavingURI(url) && |
|
280 isTabEmpty(prevTab)) |
|
281 gBrowser.removeTab(prevTab); |
|
282 } |
|
283 return; |
|
284 } |
|
285 continueOperation.call(this); |
|
286 } |
|
287 else { |
|
288 this._canonizeURL(aTriggeringEvent, response => { |
|
289 [url, postData, mayInheritPrincipal] = response; |
|
290 if (url) { |
|
291 matchLastLocationChange = (lastLocationChange == |
|
292 gBrowser.selectedBrowser.lastLocationChange); |
|
293 continueOperation.call(this); |
|
294 } |
|
295 }); |
|
296 } |
|
297 |
|
298 function continueOperation() |
|
299 { |
|
300 this.value = url; |
|
301 gBrowser.userTypedValue = url; |
|
302 try { |
|
303 addToUrlbarHistory(url); |
|
304 } catch (ex) { |
|
305 // Things may go wrong when adding url to session history, |
|
306 // but don't let that interfere with the loading of the url. |
|
307 Cu.reportError(ex); |
|
308 } |
|
309 |
|
310 function loadCurrent() { |
|
311 let webnav = Ci.nsIWebNavigation; |
|
312 let flags = webnav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | |
|
313 webnav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; |
|
314 // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from |
|
315 // inheriting the currently loaded document's principal, unless this |
|
316 // URL is marked as safe to inherit (e.g. came from a bookmark |
|
317 // keyword). |
|
318 if (!mayInheritPrincipal) |
|
319 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER; |
|
320 gBrowser.loadURIWithFlags(url, flags, null, null, postData); |
|
321 } |
|
322 |
|
323 // Focus the content area before triggering loads, since if the load |
|
324 // occurs in a new tab, we want focus to be restored to the content |
|
325 // area when the current tab is re-selected. |
|
326 gBrowser.selectedBrowser.focus(); |
|
327 |
|
328 let isMouseEvent = aTriggeringEvent instanceof MouseEvent; |
|
329 let altEnter = !isMouseEvent && aTriggeringEvent && aTriggeringEvent.altKey; |
|
330 |
|
331 if (altEnter) { |
|
332 // XXX This was added a long time ago, and I'm not sure why it is |
|
333 // necessary. Alt+Enter's default action might cause a system beep, |
|
334 // or something like that? |
|
335 aTriggeringEvent.preventDefault(); |
|
336 aTriggeringEvent.stopPropagation(); |
|
337 } |
|
338 |
|
339 // If the current tab is empty, ignore Alt+Enter (just reuse this tab) |
|
340 altEnter = altEnter && !isTabEmpty(gBrowser.selectedTab); |
|
341 |
|
342 if (isMouseEvent || altEnter) { |
|
343 // Use the standard UI link behaviors for clicks or Alt+Enter |
|
344 let where = "tab"; |
|
345 if (isMouseEvent) |
|
346 where = whereToOpenLink(aTriggeringEvent, false, false); |
|
347 |
|
348 if (where == "current") { |
|
349 if (matchLastLocationChange) { |
|
350 loadCurrent(); |
|
351 } |
|
352 } else { |
|
353 this.handleRevert(); |
|
354 let params = { allowThirdPartyFixup: true, |
|
355 postData: postData, |
|
356 initiatingDoc: document }; |
|
357 openUILinkIn(url, where, params); |
|
358 } |
|
359 } else { |
|
360 if (matchLastLocationChange) { |
|
361 loadCurrent(); |
|
362 } |
|
363 } |
|
364 } |
|
365 ]]></body> |
|
366 </method> |
|
367 |
|
368 <method name="_canonizeURL"> |
|
369 <parameter name="aTriggeringEvent"/> |
|
370 <parameter name="aCallback"/> |
|
371 <body><![CDATA[ |
|
372 var url = this.value; |
|
373 if (!url) { |
|
374 aCallback(["", null, false]); |
|
375 return; |
|
376 } |
|
377 |
|
378 // Only add the suffix when the URL bar value isn't already "URL-like", |
|
379 // and only if we get a keyboard event, to match user expectations. |
|
380 if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(url) && |
|
381 (aTriggeringEvent instanceof KeyEvent)) { |
|
382 #ifdef XP_MACOSX |
|
383 let accel = aTriggeringEvent.metaKey; |
|
384 #else |
|
385 let accel = aTriggeringEvent.ctrlKey; |
|
386 #endif |
|
387 let shift = aTriggeringEvent.shiftKey; |
|
388 |
|
389 let suffix = ""; |
|
390 |
|
391 switch (true) { |
|
392 case (accel && shift): |
|
393 suffix = ".org/"; |
|
394 break; |
|
395 case (shift): |
|
396 suffix = ".net/"; |
|
397 break; |
|
398 case (accel): |
|
399 try { |
|
400 suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix"); |
|
401 if (suffix.charAt(suffix.length - 1) != "/") |
|
402 suffix += "/"; |
|
403 } catch(e) { |
|
404 suffix = ".com/"; |
|
405 } |
|
406 break; |
|
407 } |
|
408 |
|
409 if (suffix) { |
|
410 // trim leading/trailing spaces (bug 233205) |
|
411 url = url.trim(); |
|
412 |
|
413 // Tack www. and suffix on. If user has appended directories, insert |
|
414 // suffix before them (bug 279035). Be careful not to get two slashes. |
|
415 |
|
416 let firstSlash = url.indexOf("/"); |
|
417 |
|
418 if (firstSlash >= 0) { |
|
419 url = url.substring(0, firstSlash) + suffix + |
|
420 url.substring(firstSlash + 1); |
|
421 } else { |
|
422 url = url + suffix; |
|
423 } |
|
424 |
|
425 url = "http://www." + url; |
|
426 } |
|
427 } |
|
428 |
|
429 getShortcutOrURIAndPostData(url, data => { |
|
430 aCallback([data.url, data.postData, data.mayInheritPrincipal]); |
|
431 }); |
|
432 ]]></body> |
|
433 </method> |
|
434 |
|
435 <field name="_contentIsCropped">false</field> |
|
436 |
|
437 <method name="_initURLTooltip"> |
|
438 <body><![CDATA[ |
|
439 if (this.focused || !this._contentIsCropped) |
|
440 return; |
|
441 this.inputField.setAttribute("tooltiptext", this.value); |
|
442 ]]></body> |
|
443 </method> |
|
444 |
|
445 <method name="_hideURLTooltip"> |
|
446 <body><![CDATA[ |
|
447 this.inputField.removeAttribute("tooltiptext"); |
|
448 ]]></body> |
|
449 </method> |
|
450 |
|
451 <method name="onDragOver"> |
|
452 <parameter name="aEvent"/> |
|
453 <body> |
|
454 var types = aEvent.dataTransfer.types; |
|
455 if (types.contains("application/x-moz-file") || |
|
456 types.contains("text/x-moz-url") || |
|
457 types.contains("text/uri-list") || |
|
458 types.contains("text/unicode")) |
|
459 aEvent.preventDefault(); |
|
460 </body> |
|
461 </method> |
|
462 |
|
463 <method name="onDrop"> |
|
464 <parameter name="aEvent"/> |
|
465 <body><![CDATA[ |
|
466 let url = browserDragAndDrop.drop(aEvent, { }) |
|
467 |
|
468 // The URL bar automatically handles inputs with newline characters, |
|
469 // so we can get away with treating text/x-moz-url flavours as text/plain. |
|
470 if (url) { |
|
471 aEvent.preventDefault(); |
|
472 this.value = url; |
|
473 SetPageProxyState("invalid"); |
|
474 this.focus(); |
|
475 try { |
|
476 urlSecurityCheck(url, |
|
477 gBrowser.contentPrincipal, |
|
478 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); |
|
479 } catch (ex) { |
|
480 return; |
|
481 } |
|
482 this.handleCommand(); |
|
483 } |
|
484 ]]></body> |
|
485 </method> |
|
486 |
|
487 <method name="_getSelectedValueForClipboard"> |
|
488 <body><![CDATA[ |
|
489 // Grab the actual input field's value, not our value, which could include moz-action: |
|
490 var inputVal = this.inputField.value; |
|
491 var selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd); |
|
492 |
|
493 // If the selection doesn't start at the beginning or doesn't span the full domain or |
|
494 // the URL bar is modified, nothing else to do here. |
|
495 if (this.selectionStart > 0 || this.valueIsTyped) |
|
496 return selectedVal; |
|
497 // The selection doesn't span the full domain if it doesn't contain a slash and is |
|
498 // followed by some character other than a slash. |
|
499 if (!selectedVal.contains("/")) { |
|
500 let remainder = inputVal.replace(selectedVal, ""); |
|
501 if (remainder != "" && remainder[0] != "/") |
|
502 return selectedVal; |
|
503 } |
|
504 |
|
505 let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup); |
|
506 |
|
507 let uri; |
|
508 try { |
|
509 uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE); |
|
510 } catch (e) {} |
|
511 if (!uri) |
|
512 return selectedVal; |
|
513 |
|
514 // Only copy exposable URIs |
|
515 try { |
|
516 uri = uriFixup.createExposableURI(uri); |
|
517 } catch (ex) {} |
|
518 |
|
519 // If the entire URL is selected, just use the actual loaded URI. |
|
520 if (inputVal == selectedVal) { |
|
521 // ... but only if isn't a javascript: or data: URI, since those |
|
522 // are hard to read when encoded |
|
523 if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) { |
|
524 // Parentheses are known to confuse third-party applications (bug 458565). |
|
525 selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c)); |
|
526 } |
|
527 |
|
528 return selectedVal; |
|
529 } |
|
530 |
|
531 // Just the beginning of the URL is selected, check for a trimmed |
|
532 // value |
|
533 let spec = uri.spec; |
|
534 let trimmedSpec = this.trimValue(spec); |
|
535 if (spec != trimmedSpec) { |
|
536 // Prepend the portion that trimValue removed from the beginning. |
|
537 // This assumes trimValue will only truncate the URL at |
|
538 // the beginning or end (or both). |
|
539 let trimmedSegments = spec.split(trimmedSpec); |
|
540 selectedVal = trimmedSegments[0] + selectedVal; |
|
541 } |
|
542 |
|
543 return selectedVal; |
|
544 ]]></body> |
|
545 </method> |
|
546 |
|
547 <field name="_copyCutController"><![CDATA[ |
|
548 ({ |
|
549 urlbar: this, |
|
550 doCommand: function(aCommand) { |
|
551 var urlbar = this.urlbar; |
|
552 var val = urlbar._getSelectedValueForClipboard(); |
|
553 if (!val) |
|
554 return; |
|
555 |
|
556 if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) { |
|
557 let start = urlbar.selectionStart; |
|
558 let end = urlbar.selectionEnd; |
|
559 urlbar.inputField.value = urlbar.inputField.value.substring(0, start) + |
|
560 urlbar.inputField.value.substring(end); |
|
561 urlbar.selectionStart = urlbar.selectionEnd = start; |
|
562 urlbar.removeAttribute("actiontype"); |
|
563 SetPageProxyState("invalid"); |
|
564 } |
|
565 |
|
566 Cc["@mozilla.org/widget/clipboardhelper;1"] |
|
567 .getService(Ci.nsIClipboardHelper) |
|
568 .copyString(val, document); |
|
569 }, |
|
570 supportsCommand: function(aCommand) { |
|
571 switch (aCommand) { |
|
572 case "cmd_copy": |
|
573 case "cmd_cut": |
|
574 return true; |
|
575 } |
|
576 return false; |
|
577 }, |
|
578 isCommandEnabled: function(aCommand) { |
|
579 return this.supportsCommand(aCommand) && |
|
580 (aCommand != "cmd_cut" || !this.urlbar.readOnly) && |
|
581 this.urlbar.selectionStart < this.urlbar.selectionEnd; |
|
582 }, |
|
583 onEvent: function(aEventName) {} |
|
584 }) |
|
585 ]]></field> |
|
586 |
|
587 <method name="observe"> |
|
588 <parameter name="aSubject"/> |
|
589 <parameter name="aTopic"/> |
|
590 <parameter name="aData"/> |
|
591 <body><![CDATA[ |
|
592 if (aTopic == "nsPref:changed") { |
|
593 switch (aData) { |
|
594 case "clickSelectsAll": |
|
595 case "doubleClickSelectsAll": |
|
596 this[aData] = this._prefs.getBoolPref(aData); |
|
597 break; |
|
598 case "autoFill": |
|
599 this.completeDefaultIndex = this._prefs.getBoolPref(aData); |
|
600 break; |
|
601 case "delay": |
|
602 this.timeout = this._prefs.getIntPref(aData); |
|
603 break; |
|
604 case "formatting.enabled": |
|
605 this._formattingEnabled = this._prefs.getBoolPref(aData); |
|
606 break; |
|
607 case "trimURLs": |
|
608 this._mayTrimURLs = this._prefs.getBoolPref(aData); |
|
609 break; |
|
610 case "unifiedcomplete": |
|
611 let useUnifiedComplete = false; |
|
612 try { |
|
613 useUnifiedComplete = this._prefs.getBoolPref(aData); |
|
614 } catch (ex) {} |
|
615 this.setAttribute("autocompletesearch", |
|
616 useUnifiedComplete ? "unifiedcomplete" |
|
617 : "urlinline history"); |
|
618 } |
|
619 } |
|
620 ]]></body> |
|
621 </method> |
|
622 |
|
623 <method name="handleEvent"> |
|
624 <parameter name="aEvent"/> |
|
625 <body><![CDATA[ |
|
626 switch (aEvent.type) { |
|
627 case "mousedown": |
|
628 if (this.doubleClickSelectsAll && |
|
629 aEvent.button == 0 && aEvent.detail == 2) { |
|
630 this.editor.selectAll(); |
|
631 aEvent.preventDefault(); |
|
632 } |
|
633 break; |
|
634 case "mousemove": |
|
635 this._initURLTooltip(); |
|
636 break; |
|
637 case "mouseout": |
|
638 this._hideURLTooltip(); |
|
639 break; |
|
640 case "overflow": |
|
641 this._contentIsCropped = true; |
|
642 break; |
|
643 case "underflow": |
|
644 this._contentIsCropped = false; |
|
645 this._hideURLTooltip(); |
|
646 break; |
|
647 } |
|
648 ]]></body> |
|
649 </method> |
|
650 |
|
651 <property name="textValue" |
|
652 onget="return this.value;"> |
|
653 <setter> |
|
654 <![CDATA[ |
|
655 try { |
|
656 val = losslessDecodeURI(makeURI(val)); |
|
657 } catch (ex) { } |
|
658 |
|
659 // Trim popup selected values, but never trim results coming from |
|
660 // autofill. |
|
661 if (this.popup.selectedIndex == -1) |
|
662 this._disableTrim = true; |
|
663 this.value = val; |
|
664 this._disableTrim = false; |
|
665 |
|
666 // Completing a result should simulate the user typing the result, so |
|
667 // fire an input event. |
|
668 let evt = document.createEvent("UIEvents"); |
|
669 evt.initUIEvent("input", true, false, window, 0); |
|
670 this.mIgnoreInput = true; |
|
671 this.dispatchEvent(evt); |
|
672 this.mIgnoreInput = false; |
|
673 |
|
674 return this.value; |
|
675 ]]> |
|
676 </setter> |
|
677 </property> |
|
678 |
|
679 <method name="_parseActionUrl"> |
|
680 <parameter name="aUrl"/> |
|
681 <body><![CDATA[ |
|
682 if (!aUrl.startsWith("moz-action:")) |
|
683 return null; |
|
684 |
|
685 // url is in the format moz-action:ACTION,PARAM |
|
686 let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/); |
|
687 return {type: action, param: param}; |
|
688 ]]></body> |
|
689 </method> |
|
690 |
|
691 <field name="_numNoActionsKeys"><![CDATA[ |
|
692 0 |
|
693 ]]></field> |
|
694 |
|
695 <method name="_clearNoActions"> |
|
696 <parameter name="aURL"/> |
|
697 <body><![CDATA[ |
|
698 this._numNoActionsKeys = 0; |
|
699 this.popup.removeAttribute("noactions"); |
|
700 let action = this._parseActionUrl(this._value); |
|
701 if (action) |
|
702 this.setAttribute("actiontype", action.type); |
|
703 ]]></body> |
|
704 </method> |
|
705 |
|
706 <method name="selectTextRange"> |
|
707 <parameter name="aStartIndex"/> |
|
708 <parameter name="aEndIndex"/> |
|
709 <body><![CDATA[ |
|
710 this._ignoreNextSelect = true; |
|
711 this.inputField.setSelectionRange(aStartIndex, aEndIndex); |
|
712 ]]></body> |
|
713 </method> |
|
714 </implementation> |
|
715 |
|
716 <handlers> |
|
717 <handler event="keydown"><![CDATA[ |
|
718 if ((event.keyCode === KeyEvent.DOM_VK_ALT || |
|
719 event.keyCode === KeyEvent.DOM_VK_SHIFT) && |
|
720 this.popup.selectedIndex >= 0) { |
|
721 this._numNoActionsKeys++; |
|
722 this.popup.setAttribute("noactions", "true"); |
|
723 this.removeAttribute("actiontype"); |
|
724 } |
|
725 ]]></handler> |
|
726 |
|
727 <handler event="keyup"><![CDATA[ |
|
728 if ((event.keyCode === KeyEvent.DOM_VK_ALT || |
|
729 event.keyCode === KeyEvent.DOM_VK_SHIFT) && |
|
730 this._numNoActionsKeys > 0) { |
|
731 this._numNoActionsKeys--; |
|
732 if (this._numNoActionsKeys == 0) |
|
733 this._clearNoActions(); |
|
734 } |
|
735 ]]></handler> |
|
736 |
|
737 <handler event="blur"><![CDATA[ |
|
738 this._clearNoActions(); |
|
739 this.formatValue(); |
|
740 ]]></handler> |
|
741 |
|
742 <handler event="dragstart" phase="capturing"><![CDATA[ |
|
743 // Drag only if the gesture starts from the input field. |
|
744 if (event.originalTarget != this.inputField) |
|
745 return; |
|
746 |
|
747 // Drag only if the entire value is selected and it's a valid URI. |
|
748 var isFullSelection = this.selectionStart == 0 && |
|
749 this.selectionEnd == this.textLength; |
|
750 if (!isFullSelection || |
|
751 this.getAttribute("pageproxystate") != "valid") |
|
752 return; |
|
753 |
|
754 var urlString = content.location.href; |
|
755 var title = content.document.title || urlString; |
|
756 var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>"; |
|
757 |
|
758 var dt = event.dataTransfer; |
|
759 dt.setData("text/x-moz-url", urlString + "\n" + title); |
|
760 dt.setData("text/unicode", urlString); |
|
761 dt.setData("text/html", htmlString); |
|
762 |
|
763 dt.effectAllowed = "copyLink"; |
|
764 event.stopPropagation(); |
|
765 ]]></handler> |
|
766 |
|
767 <handler event="focus" phase="capturing"><![CDATA[ |
|
768 this._hideURLTooltip(); |
|
769 this._clearFormatting(); |
|
770 ]]></handler> |
|
771 |
|
772 <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/> |
|
773 <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/> |
|
774 <handler event="select"><![CDATA[ |
|
775 if (this._ignoreNextSelect) { |
|
776 // If this select event is coming from autocomplete's selectTextRange, |
|
777 // then we don't need to adjust what's on the selection keyboard here, |
|
778 // but make sure to reset the flag since this should be a one-time |
|
779 // suppression. |
|
780 this._ignoreNextSelect = false; |
|
781 return; |
|
782 } |
|
783 |
|
784 if (!Cc["@mozilla.org/widget/clipboard;1"] |
|
785 .getService(Ci.nsIClipboard) |
|
786 .supportsSelectionClipboard()) |
|
787 return; |
|
788 |
|
789 var val = this._getSelectedValueForClipboard(); |
|
790 if (!val) |
|
791 return; |
|
792 |
|
793 Cc["@mozilla.org/widget/clipboardhelper;1"] |
|
794 .getService(Ci.nsIClipboardHelper) |
|
795 .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard, document); |
|
796 ]]></handler> |
|
797 </handlers> |
|
798 |
|
799 </binding> |
|
800 |
|
801 <!-- Note: this binding is applied to the autocomplete popup used in the Search bar and in web page content --> |
|
802 <binding id="browser-autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-result-popup"> |
|
803 <implementation> |
|
804 <method name="openAutocompletePopup"> |
|
805 <parameter name="aInput"/> |
|
806 <parameter name="aElement"/> |
|
807 <body> |
|
808 <![CDATA[ |
|
809 // initially the panel is hidden |
|
810 // to avoid impacting startup / new window performance |
|
811 aInput.popup.hidden = false; |
|
812 |
|
813 // this method is defined on the base binding |
|
814 this._openAutocompletePopup(aInput, aElement); |
|
815 ]]></body> |
|
816 </method> |
|
817 |
|
818 <method name="onPopupClick"> |
|
819 <parameter name="aEvent"/> |
|
820 <body><![CDATA[ |
|
821 // Ignore all right-clicks |
|
822 if (aEvent.button == 2) |
|
823 return; |
|
824 |
|
825 var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController); |
|
826 |
|
827 // Check for unmodified left-click, and use default behavior |
|
828 if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey && |
|
829 !aEvent.altKey && !aEvent.metaKey) { |
|
830 controller.handleEnter(true); |
|
831 return; |
|
832 } |
|
833 |
|
834 // Check for middle-click or modified clicks on the search bar |
|
835 var searchBar = BrowserSearch.searchBar; |
|
836 if (searchBar && searchBar.textbox == this.mInput) { |
|
837 // Handle search bar popup clicks |
|
838 var search = controller.getValueAt(this.selectedIndex); |
|
839 |
|
840 // close the autocomplete popup and revert the entered search term |
|
841 this.closePopup(); |
|
842 controller.handleEscape(); |
|
843 |
|
844 // Fill in the search bar's value |
|
845 searchBar.value = search; |
|
846 |
|
847 // open the search results according to the clicking subtlety |
|
848 var where = whereToOpenLink(aEvent, false, true); |
|
849 searchBar.doSearch(search, where); |
|
850 } |
|
851 ]]></body> |
|
852 </method> |
|
853 </implementation> |
|
854 </binding> |
|
855 |
|
856 <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup"> |
|
857 <implementation> |
|
858 <field name="_maxResults">0</field> |
|
859 |
|
860 <field name="_bundle" readonly="true"> |
|
861 Cc["@mozilla.org/intl/stringbundle;1"]. |
|
862 getService(Ci.nsIStringBundleService). |
|
863 createBundle("chrome://browser/locale/places/places.properties"); |
|
864 </field> |
|
865 |
|
866 <property name="maxResults" readonly="true"> |
|
867 <getter> |
|
868 <![CDATA[ |
|
869 if (!this._maxResults) { |
|
870 var prefService = |
|
871 Components.classes["@mozilla.org/preferences-service;1"] |
|
872 .getService(Components.interfaces.nsIPrefBranch); |
|
873 this._maxResults = prefService.getIntPref("browser.urlbar.maxRichResults"); |
|
874 } |
|
875 return this._maxResults; |
|
876 ]]> |
|
877 </getter> |
|
878 </property> |
|
879 |
|
880 <method name="openAutocompletePopup"> |
|
881 <parameter name="aInput"/> |
|
882 <parameter name="aElement"/> |
|
883 <body> |
|
884 <![CDATA[ |
|
885 // initially the panel is hidden |
|
886 // to avoid impacting startup / new window performance |
|
887 aInput.popup.hidden = false; |
|
888 |
|
889 // this method is defined on the base binding |
|
890 this._openAutocompletePopup(aInput, aElement); |
|
891 ]]></body> |
|
892 </method> |
|
893 |
|
894 <method name="onPopupClick"> |
|
895 <parameter name="aEvent"/> |
|
896 <body> |
|
897 <![CDATA[ |
|
898 // Ignore right-clicks |
|
899 if (aEvent.button == 2) |
|
900 return; |
|
901 |
|
902 var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController); |
|
903 |
|
904 // Check for unmodified left-click, and use default behavior |
|
905 if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey && |
|
906 !aEvent.altKey && !aEvent.metaKey) { |
|
907 controller.handleEnter(true); |
|
908 return; |
|
909 } |
|
910 |
|
911 // Check for middle-click or modified clicks on the URL bar |
|
912 if (gURLBar && this.mInput == gURLBar) { |
|
913 var url = controller.getValueAt(this.selectedIndex); |
|
914 |
|
915 // close the autocomplete popup and revert the entered address |
|
916 this.closePopup(); |
|
917 controller.handleEscape(); |
|
918 |
|
919 // Check if this is meant to be an action |
|
920 let action = this.mInput._parseActionUrl(url); |
|
921 if (action) { |
|
922 if (action.type == "switchtab") |
|
923 url = action.param; |
|
924 else |
|
925 return; |
|
926 } |
|
927 |
|
928 // respect the usual clicking subtleties |
|
929 openUILink(url, aEvent); |
|
930 } |
|
931 ]]> |
|
932 </body> |
|
933 </method> |
|
934 |
|
935 <method name="createResultLabel"> |
|
936 <parameter name="aTitle"/> |
|
937 <parameter name="aUrl"/> |
|
938 <parameter name="aType"/> |
|
939 <body> |
|
940 <![CDATA[ |
|
941 var label = aTitle + " " + aUrl; |
|
942 // convert aType (ex: "ac-result-type-<aType>") to text to be spoke aloud |
|
943 // by screen readers. convert "tag" and "bookmark" to the localized versions, |
|
944 // but don't do anything for "favicon" (the default) |
|
945 if (aType != "favicon") { |
|
946 label += " " + this._bundle.GetStringFromName(aType + "ResultLabel"); |
|
947 } |
|
948 return label; |
|
949 ]]> |
|
950 </body> |
|
951 </method> |
|
952 |
|
953 </implementation> |
|
954 </binding> |
|
955 |
|
956 <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> |
|
957 <content align="start"> |
|
958 <xul:image class="popup-notification-icon" |
|
959 xbl:inherits="popupid,src=icon"/> |
|
960 <xul:vbox flex="1"> |
|
961 <xul:description class="popup-notification-description addon-progress-description" |
|
962 xbl:inherits="xbl:text=label"/> |
|
963 <xul:spacer flex="1"/> |
|
964 <xul:hbox align="center"> |
|
965 <xul:progressmeter anonid="progressmeter" flex="1" mode="undetermined" class="popup-progress-meter"/> |
|
966 <xul:button anonid="cancel" class="popup-progress-cancel" oncommand="document.getBindingParent(this).cancel()"/> |
|
967 </xul:hbox> |
|
968 <xul:label anonid="progresstext" class="popup-progress-label"/> |
|
969 <xul:hbox class="popup-notification-button-container" |
|
970 pack="end" align="center"> |
|
971 <xul:button anonid="button" |
|
972 class="popup-notification-menubutton" |
|
973 type="menu-button" |
|
974 xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey"> |
|
975 <xul:menupopup anonid="menupopup" |
|
976 xbl:inherits="oncommand=menucommand"> |
|
977 <children/> |
|
978 <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon" |
|
979 label="&closeNotificationItem.label;" |
|
980 xbl:inherits="oncommand=closeitemcommand"/> |
|
981 </xul:menupopup> |
|
982 </xul:button> |
|
983 </xul:hbox> |
|
984 </xul:vbox> |
|
985 <xul:vbox pack="start"> |
|
986 <xul:toolbarbutton anonid="closebutton" |
|
987 class="messageCloseButton close-icon popup-notification-closebutton tabbable" |
|
988 xbl:inherits="oncommand=closebuttoncommand" |
|
989 tooltiptext="&closeNotification.tooltip;"/> |
|
990 </xul:vbox> |
|
991 </content> |
|
992 <implementation> |
|
993 <constructor><![CDATA[ |
|
994 this.cancelbtn.setAttribute("tooltiptext", gNavigatorBundle.getString("addonDownloadCancelTooltip")); |
|
995 |
|
996 this.notification.options.installs.forEach(function(aInstall) { |
|
997 aInstall.addListener(this); |
|
998 }, this); |
|
999 |
|
1000 // Calling updateProgress can sometimes cause this notification to be |
|
1001 // removed in the middle of refreshing the notification panel which |
|
1002 // makes the panel get refreshed again. Just initialise to the |
|
1003 // undetermined state and then schedule a proper check at the next |
|
1004 // opportunity |
|
1005 this.setProgress(0, -1); |
|
1006 this._updateProgressTimeout = setTimeout(this.updateProgress.bind(this), 0); |
|
1007 ]]></constructor> |
|
1008 |
|
1009 <destructor><![CDATA[ |
|
1010 this.destroy(); |
|
1011 ]]></destructor> |
|
1012 |
|
1013 <field name="progressmeter" readonly="true"> |
|
1014 document.getAnonymousElementByAttribute(this, "anonid", "progressmeter"); |
|
1015 </field> |
|
1016 <field name="progresstext" readonly="true"> |
|
1017 document.getAnonymousElementByAttribute(this, "anonid", "progresstext"); |
|
1018 </field> |
|
1019 <field name="cancelbtn" readonly="true"> |
|
1020 document.getAnonymousElementByAttribute(this, "anonid", "cancel"); |
|
1021 </field> |
|
1022 <field name="DownloadUtils" readonly="true"> |
|
1023 let utils = {}; |
|
1024 Components.utils.import("resource://gre/modules/DownloadUtils.jsm", utils); |
|
1025 utils.DownloadUtils; |
|
1026 </field> |
|
1027 |
|
1028 <method name="destroy"> |
|
1029 <body><![CDATA[ |
|
1030 this.notification.options.installs.forEach(function(aInstall) { |
|
1031 aInstall.removeListener(this); |
|
1032 }, this); |
|
1033 clearTimeout(this._updateProgressTimeout); |
|
1034 ]]></body> |
|
1035 </method> |
|
1036 |
|
1037 <method name="setProgress"> |
|
1038 <parameter name="aProgress"/> |
|
1039 <parameter name="aMaxProgress"/> |
|
1040 <body><![CDATA[ |
|
1041 if (aMaxProgress == -1) { |
|
1042 this.progressmeter.mode = "undetermined"; |
|
1043 } |
|
1044 else { |
|
1045 this.progressmeter.mode = "determined"; |
|
1046 this.progressmeter.value = (aProgress * 100) / aMaxProgress; |
|
1047 } |
|
1048 |
|
1049 let now = Date.now(); |
|
1050 |
|
1051 if (!this.notification.lastUpdate) { |
|
1052 this.notification.lastUpdate = now; |
|
1053 this.notification.lastProgress = aProgress; |
|
1054 return; |
|
1055 } |
|
1056 |
|
1057 let delta = now - this.notification.lastUpdate; |
|
1058 if ((delta < 400) && (aProgress < aMaxProgress)) |
|
1059 return; |
|
1060 |
|
1061 delta /= 1000; |
|
1062 |
|
1063 // This code is taken from nsDownloadManager.cpp |
|
1064 let speed = (aProgress - this.notification.lastProgress) / delta; |
|
1065 if (this.notification.speed) |
|
1066 speed = speed * 0.9 + this.notification.speed * 0.1; |
|
1067 |
|
1068 this.notification.lastUpdate = now; |
|
1069 this.notification.lastProgress = aProgress; |
|
1070 this.notification.speed = speed; |
|
1071 |
|
1072 let status = null; |
|
1073 [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last); |
|
1074 this.progresstext.value = status; |
|
1075 ]]></body> |
|
1076 </method> |
|
1077 |
|
1078 <method name="cancel"> |
|
1079 <body><![CDATA[ |
|
1080 // Cache these as cancelling the installs will remove this |
|
1081 // notification which will drop these references |
|
1082 let browser = this.notification.browser; |
|
1083 let contentWindow = this.notification.options.contentWindow; |
|
1084 let sourceURI = this.notification.options.sourceURI; |
|
1085 |
|
1086 let installs = this.notification.options.installs; |
|
1087 installs.forEach(function(aInstall) { |
|
1088 try { |
|
1089 aInstall.cancel(); |
|
1090 } |
|
1091 catch (e) { |
|
1092 // Cancel will throw if the download has already failed |
|
1093 } |
|
1094 }, this); |
|
1095 |
|
1096 let anchorID = "addons-notification-icon"; |
|
1097 let notificationID = "addon-install-cancelled"; |
|
1098 let messageString = gNavigatorBundle.getString("addonDownloadCancelled"); |
|
1099 messageString = PluralForm.get(installs.length, messageString); |
|
1100 let buttonText = gNavigatorBundle.getString("addonDownloadRestart"); |
|
1101 buttonText = PluralForm.get(installs.length, buttonText); |
|
1102 |
|
1103 let action = { |
|
1104 label: buttonText, |
|
1105 accessKey: gNavigatorBundle.getString("addonDownloadRestart.accessKey"), |
|
1106 callback: function() { |
|
1107 let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"]. |
|
1108 getService(Ci.amIWebInstallListener); |
|
1109 if (weblistener.onWebInstallRequested(contentWindow, sourceURI, |
|
1110 installs, installs.length)) { |
|
1111 installs.forEach(function(aInstall) { |
|
1112 aInstall.install(); |
|
1113 }); |
|
1114 } |
|
1115 } |
|
1116 }; |
|
1117 |
|
1118 PopupNotifications.show(browser, notificationID, messageString, |
|
1119 anchorID, action); |
|
1120 ]]></body> |
|
1121 </method> |
|
1122 |
|
1123 <method name="updateProgress"> |
|
1124 <body><![CDATA[ |
|
1125 let downloadingCount = 0; |
|
1126 let progress = 0; |
|
1127 let maxProgress = 0; |
|
1128 |
|
1129 this.notification.options.installs.forEach(function(aInstall) { |
|
1130 if (aInstall.maxProgress == -1) |
|
1131 maxProgress = -1; |
|
1132 progress += aInstall.progress; |
|
1133 if (maxProgress >= 0) |
|
1134 maxProgress += aInstall.maxProgress; |
|
1135 if (aInstall.state < AddonManager.STATE_DOWNLOADED) |
|
1136 downloadingCount++; |
|
1137 }); |
|
1138 |
|
1139 if (downloadingCount == 0) { |
|
1140 this.destroy(); |
|
1141 PopupNotifications.remove(this.notification); |
|
1142 } |
|
1143 else { |
|
1144 this.setProgress(progress, maxProgress); |
|
1145 } |
|
1146 ]]></body> |
|
1147 </method> |
|
1148 |
|
1149 <method name="onDownloadProgress"> |
|
1150 <body><![CDATA[ |
|
1151 this.updateProgress(); |
|
1152 ]]></body> |
|
1153 </method> |
|
1154 |
|
1155 <method name="onDownloadFailed"> |
|
1156 <body><![CDATA[ |
|
1157 this.updateProgress(); |
|
1158 ]]></body> |
|
1159 </method> |
|
1160 |
|
1161 <method name="onDownloadCancelled"> |
|
1162 <body><![CDATA[ |
|
1163 this.updateProgress(); |
|
1164 ]]></body> |
|
1165 </method> |
|
1166 |
|
1167 <method name="onDownloadEnded"> |
|
1168 <body><![CDATA[ |
|
1169 this.updateProgress(); |
|
1170 ]]></body> |
|
1171 </method> |
|
1172 </implementation> |
|
1173 </binding> |
|
1174 |
|
1175 <binding id="identity-request-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> |
|
1176 <content align="start"> |
|
1177 |
|
1178 <xul:image class="popup-notification-icon" |
|
1179 xbl:inherits="popupid,src=icon"/> |
|
1180 |
|
1181 <xul:vbox flex="1"> |
|
1182 <xul:vbox anonid="identity-deck"> |
|
1183 <xul:vbox flex="1" pack="center"> <!-- 1: add an email --> |
|
1184 <html:input type="email" anonid="email" required="required" size="30"/> |
|
1185 <xul:description anonid="newidentitydesc"/> |
|
1186 <xul:spacer flex="1"/> |
|
1187 <xul:label class="text-link custom-link small-margin" anonid="chooseemail" hidden="true"/> |
|
1188 </xul:vbox> |
|
1189 <xul:vbox flex="1" hidden="true"> <!-- 2: choose an email --> |
|
1190 <xul:description anonid="chooseidentitydesc"/> |
|
1191 <xul:radiogroup anonid="identities"> |
|
1192 </xul:radiogroup> |
|
1193 <xul:label class="text-link custom-link" anonid="newemail"/> |
|
1194 </xul:vbox> |
|
1195 </xul:vbox> |
|
1196 <xul:hbox class="popup-notification-button-container" |
|
1197 pack="end" align="center"> |
|
1198 <xul:label anonid="tos" class="text-link" hidden="true"/> |
|
1199 <xul:label anonid="privacypolicy" class="text-link" hidden="true"/> |
|
1200 <xul:spacer flex="1"/> |
|
1201 <xul:image anonid="throbber" src="chrome://browser/skin/tabbrowser/loading.png" |
|
1202 style="visibility:hidden" width="16" height="16"/> |
|
1203 <xul:button anonid="button" |
|
1204 type="menu-button" |
|
1205 class="popup-notification-menubutton" |
|
1206 xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey"> |
|
1207 <xul:menupopup anonid="menupopup" |
|
1208 xbl:inherits="oncommand=menucommand"> |
|
1209 <children/> |
|
1210 <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon" |
|
1211 label="&closeNotificationItem.label;" |
|
1212 xbl:inherits="oncommand=closeitemcommand"/> |
|
1213 </xul:menupopup> |
|
1214 </xul:button> |
|
1215 </xul:hbox> |
|
1216 </xul:vbox> |
|
1217 <xul:vbox pack="start"> |
|
1218 <xul:toolbarbutton anonid="closebutton" |
|
1219 class="messageCloseButton close-icon popup-notification-closebutton tabbable" |
|
1220 xbl:inherits="oncommand=closebuttoncommand" |
|
1221 tooltiptext="&closeNotification.tooltip;"/> |
|
1222 </xul:vbox> |
|
1223 </content> |
|
1224 <implementation> |
|
1225 <constructor><![CDATA[ |
|
1226 // this.notification.options.identity is used to pass identity-specific info to the binding |
|
1227 let origin = this.identity.origin |
|
1228 |
|
1229 // Populate text |
|
1230 this.emailField.placeholder = gNavigatorBundle. |
|
1231 getString("identity.newIdentity.email.placeholder"); |
|
1232 this.newIdentityDesc.textContent = gNavigatorBundle.getFormattedString( |
|
1233 "identity.newIdentity.description", [origin]); |
|
1234 this.chooseIdentityDesc.textContent = gNavigatorBundle.getFormattedString( |
|
1235 "identity.chooseIdentity.description", [origin]); |
|
1236 |
|
1237 // Show optional terms of service and privacy policy links |
|
1238 this._populateLink(this.identity.termsOfService, "tos", "identity.termsOfService"); |
|
1239 this._populateLink(this.identity.privacyPolicy, "privacypolicy", "identity.privacyPolicy"); |
|
1240 |
|
1241 // Populate the list of identities to choose from. The origin is used to provide |
|
1242 // better suggestions. |
|
1243 let identities = this.SignInToWebsiteUX.getIdentitiesForSite(origin); |
|
1244 |
|
1245 this._populateIdentityList(identities); |
|
1246 |
|
1247 if (typeof this.step == "undefined") { |
|
1248 // First opening of this notification |
|
1249 // Show the add email pane (0) if there are no existing identities otherwise show the list |
|
1250 this.step = "result" in identities && identities.result.length ? 1 : 0; |
|
1251 } else { |
|
1252 // Already opened so restore previous state |
|
1253 if (this.identity.typedEmail) { |
|
1254 this.emailField.value = this.identity.typedEmail; |
|
1255 } |
|
1256 if (this.identity.selected) { |
|
1257 // If the user already chose an identity then update the UI to reflect that |
|
1258 this.onIdentitySelected(); |
|
1259 } |
|
1260 // Update the view for the step |
|
1261 this.step = this.step; |
|
1262 } |
|
1263 |
|
1264 // Fire notification with the chosen identity when main button is clicked |
|
1265 this.button.addEventListener("command", this._onButtonCommand.bind(this), true); |
|
1266 |
|
1267 // Do the same if enter is pressed in the email field |
|
1268 this.emailField.addEventListener("keypress", function emailFieldKeypress(aEvent) { |
|
1269 if (aEvent.keyCode != aEvent.DOM_VK_RETURN) |
|
1270 return; |
|
1271 this._onButtonCommand(aEvent); |
|
1272 }.bind(this)); |
|
1273 |
|
1274 this.addEmailLink.value = gNavigatorBundle.getString("identity.newIdentity.label"); |
|
1275 this.addEmailLink.accessKey = gNavigatorBundle.getString("identity.newIdentity.accessKey"); |
|
1276 this.addEmailLink.addEventListener("click", function addEmailClick(evt) { |
|
1277 this.step = 0; |
|
1278 }.bind(this)); |
|
1279 |
|
1280 this.chooseEmailLink.value = gNavigatorBundle.getString("identity.chooseIdentity.label"); |
|
1281 this.chooseEmailLink.hidden = !("result" in identities && identities.result.length); |
|
1282 this.chooseEmailLink.addEventListener("click", function chooseEmailClick(evt) { |
|
1283 this.step = 1; |
|
1284 }.bind(this)); |
|
1285 |
|
1286 this.emailField.addEventListener("blur", function onEmailBlur() { |
|
1287 this.identity.typedEmail = this.emailField.value; |
|
1288 }.bind(this)); |
|
1289 ]]></constructor> |
|
1290 |
|
1291 <field name="SignInToWebsiteUX" readonly="true"> |
|
1292 let sitw = {}; |
|
1293 Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); |
|
1294 sitw.SignInToWebsiteUX; |
|
1295 </field> |
|
1296 |
|
1297 <field name="newIdentityDesc" readonly="true"> |
|
1298 document.getAnonymousElementByAttribute(this, "anonid", "newidentitydesc"); |
|
1299 </field> |
|
1300 |
|
1301 <field name="chooseIdentityDesc" readonly="true"> |
|
1302 document.getAnonymousElementByAttribute(this, "anonid", "chooseidentitydesc"); |
|
1303 </field> |
|
1304 |
|
1305 <field name="identityList" readonly="true"> |
|
1306 document.getAnonymousElementByAttribute(this, "anonid", "identities"); |
|
1307 </field> |
|
1308 |
|
1309 <field name="emailField" readonly="true"> |
|
1310 document.getAnonymousElementByAttribute(this, "anonid", "email"); |
|
1311 </field> |
|
1312 |
|
1313 <field name="addEmailLink" readonly="true"> |
|
1314 document.getAnonymousElementByAttribute(this, "anonid", "newemail"); |
|
1315 </field> |
|
1316 |
|
1317 <field name="chooseEmailLink" readonly="true"> |
|
1318 document.getAnonymousElementByAttribute(this, "anonid", "chooseemail"); |
|
1319 </field> |
|
1320 |
|
1321 <field name="throbber" readonly="true"> |
|
1322 document.getAnonymousElementByAttribute(this, "anonid", "throbber"); |
|
1323 </field> |
|
1324 |
|
1325 <field name="identity" readonly="true"> |
|
1326 this.notification.options.identity; |
|
1327 </field> |
|
1328 |
|
1329 <!-- persist the state on the identity object so we can re-create the |
|
1330 notification state upon re-opening --> |
|
1331 <property name="step"> |
|
1332 <getter> |
|
1333 return this.identity.step; |
|
1334 </getter> |
|
1335 <setter><![CDATA[ |
|
1336 let deck = document.getAnonymousElementByAttribute(this, "anonid", "identity-deck"); |
|
1337 for (let i = 0; i < deck.children.length; i++) { |
|
1338 deck.children[i].hidden = (val != i); |
|
1339 } |
|
1340 this.identity.step = val; |
|
1341 switch (val) { |
|
1342 case 0: |
|
1343 this.emailField.focus(); |
|
1344 break; |
|
1345 }]]> |
|
1346 </setter> |
|
1347 </property> |
|
1348 |
|
1349 <method name="onIdentitySelected"> |
|
1350 <body><![CDATA[ |
|
1351 this.throbber.style.visibility = "visible"; |
|
1352 this.button.disabled = true; |
|
1353 this.emailField.value = this.identity.selected |
|
1354 this.emailField.disabled = true; |
|
1355 this.identityList.disabled = true; |
|
1356 ]]></body> |
|
1357 </method> |
|
1358 |
|
1359 <method name="_populateLink"> |
|
1360 <parameter name="aURL"/> |
|
1361 <parameter name="aLinkId"/> |
|
1362 <parameter name="aStringId"/> |
|
1363 <body><![CDATA[ |
|
1364 if (aURL) { |
|
1365 // Show optional link to aURL |
|
1366 let link = document.getAnonymousElementByAttribute(this, "anonid", aLinkId); |
|
1367 link.value = gNavigatorBundle.getString(aStringId); |
|
1368 link.href = aURL; |
|
1369 link.hidden = false; |
|
1370 } |
|
1371 ]]></body> |
|
1372 </method> |
|
1373 |
|
1374 <method name="_populateIdentityList"> |
|
1375 <parameter name="aIdentities"/> |
|
1376 <body><![CDATA[ |
|
1377 let foundLastUsed = false; |
|
1378 let lastUsed = this.identity.selected || aIdentities.lastUsed; |
|
1379 for (let id in aIdentities.result) { |
|
1380 let label = aIdentities.result[id]; |
|
1381 let opt = this.identityList.appendItem(label); |
|
1382 if (label == lastUsed) { |
|
1383 this.identityList.selectedItem = opt; |
|
1384 foundLastUsed = true; |
|
1385 } |
|
1386 } |
|
1387 if (!foundLastUsed) { |
|
1388 this.identityList.selectedIndex = -1; |
|
1389 } |
|
1390 ]]></body> |
|
1391 </method> |
|
1392 |
|
1393 <method name="_onButtonCommand"> |
|
1394 <parameter name="aEvent"/> |
|
1395 <body><![CDATA[ |
|
1396 if (aEvent.target != aEvent.currentTarget) |
|
1397 return; |
|
1398 let chosenId; |
|
1399 switch (this.step) { |
|
1400 case 0: |
|
1401 aEvent.stopPropagation(); |
|
1402 if (!this.emailField.validity.valid) { |
|
1403 this.emailField.focus(); |
|
1404 return; |
|
1405 } |
|
1406 chosenId = this.emailField.value; |
|
1407 break; |
|
1408 case 1: |
|
1409 aEvent.stopPropagation(); |
|
1410 let selectedItem = this.identityList.selectedItem |
|
1411 chosenId = selectedItem ? selectedItem.label : null; |
|
1412 if (!chosenId) |
|
1413 return; |
|
1414 break; |
|
1415 default: |
|
1416 throw new Error("Unknown case"); |
|
1417 return; |
|
1418 } |
|
1419 // Actually select the identity |
|
1420 this.SignInToWebsiteUX.selectIdentity(this.identity.rpId, chosenId); |
|
1421 this.identity.selected = chosenId; |
|
1422 this.onIdentitySelected(); |
|
1423 ]]></body> |
|
1424 </method> |
|
1425 |
|
1426 </implementation> |
|
1427 </binding> |
|
1428 |
|
1429 <binding id="plugin-popupnotification-center-item"> |
|
1430 <content align="center"> |
|
1431 <xul:vbox pack="center" anonid="itemBox" class="itemBox"> |
|
1432 <xul:description anonid="center-item-label" class="center-item-label" /> |
|
1433 <xul:hbox flex="1" pack="start" align="center" anonid="center-item-warning"> |
|
1434 <xul:image anonid="center-item-warning-icon" class="center-item-warning-icon"/> |
|
1435 <xul:label anonid="center-item-warning-label"/> |
|
1436 <xul:label anonid="center-item-link" value="&checkForUpdates;" class="text-link"/> |
|
1437 </xul:hbox> |
|
1438 </xul:vbox> |
|
1439 <xul:vbox pack="center"> |
|
1440 <xul:menulist class="center-item-menulist" |
|
1441 anonid="center-item-menulist"> |
|
1442 <xul:menupopup> |
|
1443 <xul:menuitem anonid="allownow" value="allownow" |
|
1444 label="&pluginActivateNow.label;" /> |
|
1445 <xul:menuitem anonid="allowalways" value="allowalways" |
|
1446 label="&pluginActivateAlways.label;" /> |
|
1447 <xul:menuitem anonid="block" value="block" |
|
1448 label="&pluginBlockNow.label;" /> |
|
1449 </xul:menupopup> |
|
1450 </xul:menulist> |
|
1451 </xul:vbox> |
|
1452 </content> |
|
1453 <resources> |
|
1454 <stylesheet src="chrome://global/skin/notification.css"/> |
|
1455 </resources> |
|
1456 <implementation> |
|
1457 <constructor><![CDATA[ |
|
1458 document.getAnonymousElementByAttribute(this, "anonid", "center-item-label").value = this.action.pluginName; |
|
1459 |
|
1460 let curState = "block"; |
|
1461 if (this.action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { |
|
1462 if (this.action.pluginPermissionType == Ci.nsIPermissionManager.EXPIRE_SESSION) { |
|
1463 curState = "allownow"; |
|
1464 } |
|
1465 else { |
|
1466 curState = "allowalways"; |
|
1467 } |
|
1468 } |
|
1469 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").value = curState; |
|
1470 |
|
1471 let warningString = ""; |
|
1472 let linkString = ""; |
|
1473 |
|
1474 let link = document.getAnonymousElementByAttribute(this, "anonid", "center-item-link"); |
|
1475 |
|
1476 let url; |
|
1477 let linkHandler; |
|
1478 |
|
1479 if (this.action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) { |
|
1480 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true; |
|
1481 warningString = gNavigatorBundle.getString("pluginActivateDisabled.label"); |
|
1482 linkString = gNavigatorBundle.getString("pluginActivateDisabled.manage"); |
|
1483 linkHandler = function(event) { |
|
1484 event.preventDefault(); |
|
1485 gPluginHandler.managePlugins(); |
|
1486 }; |
|
1487 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-icon").hidden = true; |
|
1488 } |
|
1489 else { |
|
1490 url = this.action.detailsLink; |
|
1491 |
|
1492 switch (this.action.blocklistState) { |
|
1493 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: |
|
1494 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning").hidden = true; |
|
1495 break; |
|
1496 case Ci.nsIBlocklistService.STATE_BLOCKED: |
|
1497 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true; |
|
1498 warningString = gNavigatorBundle.getString("pluginActivateBlocked.label"); |
|
1499 linkString = gNavigatorBundle.getString("pluginActivate.learnMore"); |
|
1500 break; |
|
1501 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: |
|
1502 warningString = gNavigatorBundle.getString("pluginActivateOutdated.label"); |
|
1503 linkString = gNavigatorBundle.getString("pluginActivate.updateLabel"); |
|
1504 break; |
|
1505 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: |
|
1506 warningString = gNavigatorBundle.getString("pluginActivateVulnerable.label"); |
|
1507 linkString = gNavigatorBundle.getString("pluginActivate.riskLabel"); |
|
1508 break; |
|
1509 } |
|
1510 } |
|
1511 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-label").value = warningString; |
|
1512 |
|
1513 if (url || linkHandler) { |
|
1514 link.value = linkString; |
|
1515 if (url) { |
|
1516 link.href = url; |
|
1517 } |
|
1518 if (linkHandler) { |
|
1519 link.addEventListener("click", linkHandler, false); |
|
1520 } |
|
1521 } |
|
1522 else { |
|
1523 link.hidden = true; |
|
1524 } |
|
1525 ]]></constructor> |
|
1526 <property name="value"> |
|
1527 <getter> |
|
1528 return document.getAnonymousElementByAttribute(this, "anonid", |
|
1529 "center-item-menulist").value; |
|
1530 </getter> |
|
1531 <setter><!-- This should be used only in automated tests --> |
|
1532 document.getAnonymousElementByAttribute(this, "anonid", |
|
1533 "center-item-menulist").value = val; |
|
1534 </setter> |
|
1535 </property> |
|
1536 </implementation> |
|
1537 </binding> |
|
1538 |
|
1539 <binding id="click-to-play-plugins-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> |
|
1540 <content align="start" style="width: &pluginNotification.width;;"> |
|
1541 <xul:vbox flex="1" align="stretch" class="popup-notification-main-box" |
|
1542 xbl:inherits="popupid"> |
|
1543 <xul:hbox class="click-to-play-plugins-notification-description-box" flex="1" align="start"> |
|
1544 <xul:description class="click-to-play-plugins-outer-description" flex="1"> |
|
1545 <html:span anonid="click-to-play-plugins-notification-description" /> |
|
1546 <xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" /> |
|
1547 </xul:description> |
|
1548 <xul:toolbarbutton anonid="closebutton" |
|
1549 class="messageCloseButton popup-notification-closebutton tabbable close-icon" |
|
1550 xbl:inherits="oncommand=closebuttoncommand" |
|
1551 tooltiptext="&closeNotification.tooltip;"/> |
|
1552 </xul:hbox> |
|
1553 <xul:grid anonid="click-to-play-plugins-notification-center-box" |
|
1554 class="click-to-play-plugins-notification-center-box"> |
|
1555 <xul:columns> |
|
1556 <xul:column flex="1"/> |
|
1557 <xul:column/> |
|
1558 </xul:columns> |
|
1559 <xul:rows> |
|
1560 <children includes="row"/> |
|
1561 <xul:hbox pack="start" anonid="plugin-notification-showbox"> |
|
1562 <xul:button label="&pluginNotification.showAll.label;" |
|
1563 accesskey="&pluginNotification.showAll.accesskey;" |
|
1564 class="plugin-notification-showbutton" |
|
1565 oncommand="document.getBindingParent(this)._setState(2)"/> |
|
1566 </xul:hbox> |
|
1567 </xul:rows> |
|
1568 </xul:grid> |
|
1569 <xul:hbox anonid="button-container" |
|
1570 class="click-to-play-plugins-notification-button-container" |
|
1571 pack="center" align="center"> |
|
1572 <xul:button anonid="primarybutton" |
|
1573 class="click-to-play-popup-button" |
|
1574 oncommand="document.getBindingParent(this)._onButton(this)" |
|
1575 flex="1"/> |
|
1576 <xul:button anonid="secondarybutton" |
|
1577 class="click-to-play-popup-button" |
|
1578 oncommand="document.getBindingParent(this)._onButton(this);" |
|
1579 flex="1"/> |
|
1580 </xul:hbox> |
|
1581 <xul:box hidden="true"> |
|
1582 <children/> |
|
1583 </xul:box> |
|
1584 </xul:vbox> |
|
1585 </content> |
|
1586 <resources> |
|
1587 <stylesheet src="chrome://global/skin/notification.css"/> |
|
1588 </resources> |
|
1589 <implementation> |
|
1590 <field name="_states"> |
|
1591 ({SINGLE: 0, MULTI_COLLAPSED: 1, MULTI_EXPANDED: 2}) |
|
1592 </field> |
|
1593 <field name="_primaryButton"> |
|
1594 document.getAnonymousElementByAttribute(this, "anonid", "primarybutton"); |
|
1595 </field> |
|
1596 <field name="_secondaryButton"> |
|
1597 document.getAnonymousElementByAttribute(this, "anonid", "secondarybutton") |
|
1598 </field> |
|
1599 <field name="_buttonContainer"> |
|
1600 document.getAnonymousElementByAttribute(this, "anonid", "button-container") |
|
1601 </field> |
|
1602 <field name="_brandShortName"> |
|
1603 document.getElementById("bundle_brand").getString("brandShortName") |
|
1604 </field> |
|
1605 <field name="_items">[]</field> |
|
1606 <constructor><![CDATA[ |
|
1607 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
1608 let sortedActions = []; |
|
1609 for (let action of this.notification.options.pluginData.values()) { |
|
1610 sortedActions.push(action); |
|
1611 } |
|
1612 sortedActions.sort((a, b) => a.pluginName.localeCompare(b.pluginName)); |
|
1613 |
|
1614 for (let action of sortedActions) { |
|
1615 let item = document.createElementNS(XUL_NS, "row"); |
|
1616 item.setAttribute("class", "plugin-popupnotification-centeritem"); |
|
1617 item.action = action; |
|
1618 this.appendChild(item); |
|
1619 this._items.push(item); |
|
1620 } |
|
1621 switch (this._items.length) { |
|
1622 case 0: |
|
1623 PopupNotifications._dismiss(); |
|
1624 break; |
|
1625 case 1: |
|
1626 this._setState(this._states.SINGLE); |
|
1627 break; |
|
1628 default: |
|
1629 if (this.notification.options.primaryPlugin) { |
|
1630 this._setState(this._states.MULTI_COLLAPSED); |
|
1631 } else { |
|
1632 this._setState(this._states.MULTI_EXPANDED); |
|
1633 } |
|
1634 } |
|
1635 ]]></constructor> |
|
1636 <method name="_setState"> |
|
1637 <parameter name="state" /> |
|
1638 <body><![CDATA[ |
|
1639 var grid = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-center-box"); |
|
1640 |
|
1641 if (this._states.SINGLE == state) { |
|
1642 grid.hidden = true; |
|
1643 this._setupSingleState(); |
|
1644 return; |
|
1645 } |
|
1646 |
|
1647 let host = gPluginHandler._getHostFromPrincipal(this.notification.browser.contentWindow.document.nodePrincipal); |
|
1648 this._setupDescription("pluginActivateMultiple.message", null, host); |
|
1649 |
|
1650 var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox"); |
|
1651 |
|
1652 var dialogStrings = Services.strings.createBundle("chrome://global/locale/dialog.properties"); |
|
1653 this._primaryButton.label = dialogStrings.GetStringFromName("button-accept"); |
|
1654 this._primaryButton.setAttribute("default", "true"); |
|
1655 |
|
1656 this._secondaryButton.label = dialogStrings.GetStringFromName("button-cancel"); |
|
1657 this._primaryButton.setAttribute("action", "_multiAccept"); |
|
1658 this._secondaryButton.setAttribute("action", "_cancel"); |
|
1659 |
|
1660 grid.hidden = false; |
|
1661 |
|
1662 if (this._states.MULTI_COLLAPSED == state) { |
|
1663 for (let child of this.childNodes) { |
|
1664 if (child.tagName != "row") { |
|
1665 continue; |
|
1666 } |
|
1667 child.hidden = this.notification.options.primaryPlugin != |
|
1668 child.action.permissionString; |
|
1669 } |
|
1670 showBox.hidden = false; |
|
1671 } |
|
1672 else { |
|
1673 for (let child of this.childNodes) { |
|
1674 if (child.tagName != "row") { |
|
1675 continue; |
|
1676 } |
|
1677 child.hidden = false; |
|
1678 } |
|
1679 showBox.hidden = true; |
|
1680 } |
|
1681 this._setupLink(null); |
|
1682 ]]></body> |
|
1683 </method> |
|
1684 <method name="_setupSingleState"> |
|
1685 <body><![CDATA[ |
|
1686 var action = this._items[0].action; |
|
1687 var host = action.pluginPermissionHost; |
|
1688 |
|
1689 let label, linkLabel, linkUrl, button1, button2; |
|
1690 |
|
1691 if (action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { |
|
1692 button1 = { |
|
1693 label: "pluginBlockNow.label", |
|
1694 accesskey: "pluginBlockNow.accesskey", |
|
1695 action: "_singleBlock" |
|
1696 }; |
|
1697 button2 = { |
|
1698 label: "pluginContinue.label", |
|
1699 accesskey: "pluginContinue.accesskey", |
|
1700 action: "_singleContinue", |
|
1701 default: true |
|
1702 }; |
|
1703 switch (action.blocklistState) { |
|
1704 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: |
|
1705 label = "pluginEnabled.message"; |
|
1706 linkLabel = "pluginActivate.learnMore"; |
|
1707 break; |
|
1708 |
|
1709 case Ci.nsIBlocklistService.STATE_BLOCKED: |
|
1710 Cu.reportError(Error("Cannot happen!")); |
|
1711 break; |
|
1712 |
|
1713 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: |
|
1714 label = "pluginEnabledOutdated.message"; |
|
1715 linkLabel = "pluginActivate.updateLabel"; |
|
1716 break; |
|
1717 |
|
1718 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: |
|
1719 label = "pluginEnabledVulnerable.message"; |
|
1720 linkLabel = "pluginActivate.riskLabel" |
|
1721 break; |
|
1722 |
|
1723 default: |
|
1724 Cu.reportError(Error("Unexpected blocklist state")); |
|
1725 } |
|
1726 } |
|
1727 else if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) { |
|
1728 let linkElement = |
|
1729 document.getAnonymousElementByAttribute( |
|
1730 this, "anonid", "click-to-play-plugins-notification-link"); |
|
1731 linkElement.textContent = gNavigatorBundle.getString("pluginActivateDisabled.manage"); |
|
1732 linkElement.setAttribute("onclick", "gPluginHandler.managePlugins()"); |
|
1733 |
|
1734 let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); |
|
1735 descElement.textContent = gNavigatorBundle.getFormattedString( |
|
1736 "pluginActivateDisabled.message", [action.pluginName, this._brandShortName]) + " "; |
|
1737 this._buttonContainer.hidden = true; |
|
1738 return; |
|
1739 } |
|
1740 else if (action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) { |
|
1741 let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); |
|
1742 descElement.textContent = gNavigatorBundle.getFormattedString( |
|
1743 "pluginActivateBlocked.message", [action.pluginName, this._brandShortName]) + " "; |
|
1744 this._setupLink("pluginActivate.learnMore", action.detailsLink); |
|
1745 this._buttonContainer.hidden = true; |
|
1746 return; |
|
1747 } |
|
1748 else { |
|
1749 button1 = { |
|
1750 label: "pluginActivateNow.label", |
|
1751 accesskey: "pluginActivateNow.accesskey", |
|
1752 action: "_singleActivateNow" |
|
1753 }; |
|
1754 button2 = { |
|
1755 label: "pluginActivateAlways.label", |
|
1756 accesskey: "pluginActivateAlways.accesskey", |
|
1757 action: "_singleActivateAlways" |
|
1758 }; |
|
1759 switch (action.blocklistState) { |
|
1760 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: |
|
1761 label = "pluginActivateNew.message"; |
|
1762 linkLabel = "pluginActivate.learnMore"; |
|
1763 button2.default = true; |
|
1764 break; |
|
1765 |
|
1766 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: |
|
1767 label = "pluginActivateOutdated.message"; |
|
1768 linkLabel = "pluginActivate.updateLabel"; |
|
1769 button1.default = true; |
|
1770 break; |
|
1771 |
|
1772 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: |
|
1773 label = "pluginActivateVulnerable.message"; |
|
1774 linkLabel = "pluginActivate.riskLabel" |
|
1775 button1.default = true; |
|
1776 break; |
|
1777 |
|
1778 default: |
|
1779 Cu.reportError(Error("Unexpected blocklist state")); |
|
1780 } |
|
1781 } |
|
1782 this._setupDescription(label, action.pluginName, host); |
|
1783 this._setupLink(linkLabel, action.detailsLink); |
|
1784 |
|
1785 this._primaryButton.label = gNavigatorBundle.getString(button1.label); |
|
1786 this._primaryButton.accesskey = gNavigatorBundle.getString(button1.accesskey); |
|
1787 this._primaryButton.setAttribute("action", button1.action); |
|
1788 |
|
1789 this._secondaryButton.label = gNavigatorBundle.getString(button2.label); |
|
1790 this._secondaryButton.accesskey = gNavigatorBundle.getString(button2.accesskey); |
|
1791 this._secondaryButton.setAttribute("action", button2.action); |
|
1792 if (button1.default) { |
|
1793 this._primaryButton.setAttribute("default", "true"); |
|
1794 } |
|
1795 else if (button2.default) { |
|
1796 this._secondaryButton.setAttribute("default", "true"); |
|
1797 } |
|
1798 ]]></body> |
|
1799 </method> |
|
1800 <method name="_setupDescription"> |
|
1801 <parameter name="baseString" /> |
|
1802 <parameter name="pluginName" /> <!-- null for the multiple-plugin case --> |
|
1803 <parameter name="host" /> |
|
1804 <body><![CDATA[ |
|
1805 var bsn = this._brandShortName; |
|
1806 var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); |
|
1807 while (span.lastChild) { |
|
1808 span.removeChild(span.lastChild); |
|
1809 } |
|
1810 |
|
1811 var args = ["__host__", this._brandShortName]; |
|
1812 if (pluginName) { |
|
1813 args.unshift(pluginName); |
|
1814 } |
|
1815 var bases = gNavigatorBundle.getFormattedString(baseString, args). |
|
1816 split("__host__", 2); |
|
1817 |
|
1818 span.appendChild(document.createTextNode(bases[0])); |
|
1819 var hostSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "em"); |
|
1820 hostSpan.appendChild(document.createTextNode(host)); |
|
1821 span.appendChild(hostSpan); |
|
1822 span.appendChild(document.createTextNode(bases[1] + " ")); |
|
1823 ]]></body> |
|
1824 </method> |
|
1825 <method name="_setupLink"> |
|
1826 <parameter name="linkString"/> |
|
1827 <parameter name="linkUrl" /> |
|
1828 <body><![CDATA[ |
|
1829 var link = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-link"); |
|
1830 if (!linkString || !linkUrl) { |
|
1831 link.hidden = true; |
|
1832 return; |
|
1833 } |
|
1834 |
|
1835 link.hidden = false; |
|
1836 link.textContent = gNavigatorBundle.getString(linkString); |
|
1837 link.href = linkUrl; |
|
1838 ]]></body> |
|
1839 </method> |
|
1840 <method name="_onButton"> |
|
1841 <parameter name="aButton" /> |
|
1842 <body><![CDATA[ |
|
1843 let methodName = aButton.getAttribute("action"); |
|
1844 this[methodName](); |
|
1845 ]]></body> |
|
1846 </method> |
|
1847 <method name="_singleActivateNow"> |
|
1848 <body><![CDATA[ |
|
1849 gPluginHandler._updatePluginPermission(this.notification, |
|
1850 this._items[0].action, |
|
1851 "allownow"); |
|
1852 this._cancel(); |
|
1853 ]]></body> |
|
1854 </method> |
|
1855 <method name="_singleBlock"> |
|
1856 <body><![CDATA[ |
|
1857 gPluginHandler._updatePluginPermission(this.notification, |
|
1858 this._items[0].action, |
|
1859 "block"); |
|
1860 this._cancel(); |
|
1861 ]]></body> |
|
1862 </method> |
|
1863 <method name="_singleActivateAlways"> |
|
1864 <body><![CDATA[ |
|
1865 gPluginHandler._updatePluginPermission(this.notification, |
|
1866 this._items[0].action, |
|
1867 "allowalways"); |
|
1868 this._cancel(); |
|
1869 ]]></body> |
|
1870 </method> |
|
1871 <method name="_singleContinue"> |
|
1872 <body><![CDATA[ |
|
1873 gPluginHandler._updatePluginPermission(this.notification, |
|
1874 this._items[0].action, |
|
1875 "continue"); |
|
1876 this._cancel(); |
|
1877 ]]></body> |
|
1878 </method> |
|
1879 <method name="_multiAccept"> |
|
1880 <body><![CDATA[ |
|
1881 for (let item of this._items) { |
|
1882 let action = item.action; |
|
1883 if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED || |
|
1884 action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) { |
|
1885 continue; |
|
1886 } |
|
1887 gPluginHandler._updatePluginPermission(this.notification, |
|
1888 item.action, item.value); |
|
1889 } |
|
1890 this._cancel(); |
|
1891 ]]></body> |
|
1892 </method> |
|
1893 <method name="_cancel"> |
|
1894 <body><![CDATA[ |
|
1895 PopupNotifications._dismiss(); |
|
1896 ]]></body> |
|
1897 </method> |
|
1898 <method name="_accept"> |
|
1899 <parameter name="aEvent" /> |
|
1900 <body><![CDATA[ |
|
1901 if (aEvent.defaultPrevented) |
|
1902 return; |
|
1903 aEvent.preventDefault(); |
|
1904 if (this._primaryButton.getAttribute("default") == "true") { |
|
1905 this._primaryButton.click(); |
|
1906 } |
|
1907 else if (this._secondaryButton.getAttribute("default") == "true") { |
|
1908 this._secondaryButton.click(); |
|
1909 } |
|
1910 ]]></body> |
|
1911 </method> |
|
1912 </implementation> |
|
1913 <handlers> |
|
1914 <!-- The _accept method checks for .defaultPrevented so that if focus is in a button, |
|
1915 enter activates the button and not this default action --> |
|
1916 <handler event="keypress" keycode="VK_RETURN" group="system" action="this._accept(event);"/> |
|
1917 </handlers> |
|
1918 </binding> |
|
1919 |
|
1920 <binding id="splitmenu"> |
|
1921 <content> |
|
1922 <xul:hbox anonid="menuitem" flex="1" |
|
1923 class="splitmenu-menuitem" |
|
1924 xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/> |
|
1925 <xul:menu anonid="menu" class="splitmenu-menu" |
|
1926 xbl:inherits="disabled,_moz-menuactive=active" |
|
1927 oncommand="event.stopPropagation();"> |
|
1928 <children includes="menupopup"/> |
|
1929 </xul:menu> |
|
1930 </content> |
|
1931 |
|
1932 <implementation implements="nsIDOMEventListener"> |
|
1933 <constructor><![CDATA[ |
|
1934 this._parentMenupopup.addEventListener("DOMMenuItemActive", this, false); |
|
1935 this._parentMenupopup.addEventListener("popuphidden", this, false); |
|
1936 ]]></constructor> |
|
1937 |
|
1938 <destructor><![CDATA[ |
|
1939 this._parentMenupopup.removeEventListener("DOMMenuItemActive", this, false); |
|
1940 this._parentMenupopup.removeEventListener("popuphidden", this, false); |
|
1941 ]]></destructor> |
|
1942 |
|
1943 <field name="menuitem" readonly="true"> |
|
1944 document.getAnonymousElementByAttribute(this, "anonid", "menuitem"); |
|
1945 </field> |
|
1946 <field name="menu" readonly="true"> |
|
1947 document.getAnonymousElementByAttribute(this, "anonid", "menu"); |
|
1948 </field> |
|
1949 |
|
1950 <field name="_menuDelay">600</field> |
|
1951 |
|
1952 <field name="_parentMenupopup"><![CDATA[ |
|
1953 this._getParentMenupopup(this); |
|
1954 ]]></field> |
|
1955 |
|
1956 <method name="_getParentMenupopup"> |
|
1957 <parameter name="aNode"/> |
|
1958 <body><![CDATA[ |
|
1959 let node = aNode.parentNode; |
|
1960 while (node) { |
|
1961 if (node.localName == "menupopup") |
|
1962 break; |
|
1963 node = node.parentNode; |
|
1964 } |
|
1965 return node; |
|
1966 ]]></body> |
|
1967 </method> |
|
1968 |
|
1969 <method name="handleEvent"> |
|
1970 <parameter name="event"/> |
|
1971 <body><![CDATA[ |
|
1972 switch (event.type) { |
|
1973 case "DOMMenuItemActive": |
|
1974 if (this.getAttribute("active") == "true" && |
|
1975 event.target != this && |
|
1976 this._getParentMenupopup(event.target) == this._parentMenupopup) |
|
1977 this.removeAttribute("active"); |
|
1978 break; |
|
1979 case "popuphidden": |
|
1980 if (event.target == this._parentMenupopup) |
|
1981 this.removeAttribute("active"); |
|
1982 break; |
|
1983 } |
|
1984 ]]></body> |
|
1985 </method> |
|
1986 </implementation> |
|
1987 |
|
1988 <handlers> |
|
1989 <handler event="mouseover"><![CDATA[ |
|
1990 if (this.getAttribute("active") != "true") { |
|
1991 this.setAttribute("active", "true"); |
|
1992 |
|
1993 let event = document.createEvent("Events"); |
|
1994 event.initEvent("DOMMenuItemActive", true, false); |
|
1995 this.dispatchEvent(event); |
|
1996 |
|
1997 if (this.getAttribute("disabled") != "true") { |
|
1998 let self = this; |
|
1999 setTimeout(function () { |
|
2000 if (self.getAttribute("active") == "true") |
|
2001 self.menu.open = true; |
|
2002 }, this._menuDelay); |
|
2003 } |
|
2004 } |
|
2005 ]]></handler> |
|
2006 |
|
2007 <handler event="popupshowing"><![CDATA[ |
|
2008 if (event.target == this.firstChild && |
|
2009 this._parentMenupopup._currentPopup) |
|
2010 this._parentMenupopup._currentPopup.hidePopup(); |
|
2011 ]]></handler> |
|
2012 |
|
2013 <handler event="click" phase="capturing"><![CDATA[ |
|
2014 if (this.getAttribute("disabled") == "true") { |
|
2015 // Prevent the command from being carried out |
|
2016 event.stopPropagation(); |
|
2017 return; |
|
2018 } |
|
2019 |
|
2020 let node = event.originalTarget; |
|
2021 while (true) { |
|
2022 if (node == this.menuitem) |
|
2023 break; |
|
2024 if (node == this) |
|
2025 return; |
|
2026 node = node.parentNode; |
|
2027 } |
|
2028 |
|
2029 this._parentMenupopup.hidePopup(); |
|
2030 ]]></handler> |
|
2031 </handlers> |
|
2032 </binding> |
|
2033 |
|
2034 <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem"> |
|
2035 <implementation> |
|
2036 <constructor><![CDATA[ |
|
2037 this.setAttribute("tooltiptext", this.getAttribute("acceltext")); |
|
2038 // TODO: Simplify this to this.setAttribute("acceltext", "") once bug |
|
2039 // 592424 is fixed |
|
2040 document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", ""); |
|
2041 ]]></constructor> |
|
2042 </implementation> |
|
2043 </binding> |
|
2044 |
|
2045 <binding id="menuitem-iconic-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem-iconic"> |
|
2046 <implementation> |
|
2047 <constructor><![CDATA[ |
|
2048 this.setAttribute("tooltiptext", this.getAttribute("acceltext")); |
|
2049 // TODO: Simplify this to this.setAttribute("acceltext", "") once bug |
|
2050 // 592424 is fixed |
|
2051 document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", ""); |
|
2052 ]]></constructor> |
|
2053 </implementation> |
|
2054 </binding> |
|
2055 |
|
2056 <binding id="promobox"> |
|
2057 <content> |
|
2058 <xul:hbox class="panel-promo-box" align="start" flex="1"> |
|
2059 <xul:hbox align="center" flex="1"> |
|
2060 <xul:image class="panel-promo-icon"/> |
|
2061 <xul:description anonid="promo-message" class="panel-promo-message" flex="1"> |
|
2062 <xul:description anonid="promo-link" |
|
2063 class="plain text-link inline-link" |
|
2064 onclick="document.getBindingParent(this).onLinkClick();"/> |
|
2065 </xul:description> |
|
2066 </xul:hbox> |
|
2067 <xul:toolbarbutton class="panel-promo-closebutton close-icon" |
|
2068 oncommand="document.getBindingParent(this).onCloseButtonCommand();" |
|
2069 tooltiptext="&closeNotification.tooltip;"/> |
|
2070 </xul:hbox> |
|
2071 </content> |
|
2072 |
|
2073 <implementation implements="nsIDOMEventListener"> |
|
2074 <constructor><![CDATA[ |
|
2075 this._panel.addEventListener("popupshowing", this, false); |
|
2076 ]]></constructor> |
|
2077 |
|
2078 <destructor><![CDATA[ |
|
2079 this._panel.removeEventListener("popupshowing", this, false); |
|
2080 ]]></destructor> |
|
2081 |
|
2082 <field name="_panel" readonly="true"><![CDATA[ |
|
2083 let node = this.parentNode; |
|
2084 while(node && node.localName != "panel") { |
|
2085 node = node.parentNode; |
|
2086 } |
|
2087 node; |
|
2088 ]]></field> |
|
2089 <field name="_promomessage" readonly="true"> |
|
2090 document.getAnonymousElementByAttribute(this, "anonid", "promo-message"); |
|
2091 </field> |
|
2092 <field name="_promolink" readonly="true"> |
|
2093 document.getAnonymousElementByAttribute(this, "anonid", "promo-link"); |
|
2094 </field> |
|
2095 <field name="_brandBundle" readonly="true"> |
|
2096 Services.strings.createBundle("chrome://branding/locale/brand.properties"); |
|
2097 </field> |
|
2098 <property name="_viewsLeftMap"> |
|
2099 <getter><![CDATA[ |
|
2100 try { |
|
2101 return JSON.parse(Services.prefs.getCharPref("browser.syncPromoViewsLeftMap")); |
|
2102 } catch (ex) {} |
|
2103 return {}; |
|
2104 ]]></getter> |
|
2105 </property> |
|
2106 <property name="_viewsLeft"> |
|
2107 <getter><![CDATA[ |
|
2108 let views = 5; |
|
2109 let map = this._viewsLeftMap; |
|
2110 if (this._notificationType in map) { |
|
2111 views = map[this._notificationType]; |
|
2112 } |
|
2113 return views; |
|
2114 ]]></getter> |
|
2115 <setter><![CDATA[ |
|
2116 let map = this._viewsLeftMap; |
|
2117 map[this._notificationType] = val; |
|
2118 Services.prefs.setCharPref("browser.syncPromoViewsLeftMap", |
|
2119 JSON.stringify(map)); |
|
2120 return val; |
|
2121 ]]></setter> |
|
2122 </property> |
|
2123 <property name="_notificationType"> |
|
2124 <getter><![CDATA[ |
|
2125 // Use the popupid attribute to identify the notification type, |
|
2126 // otherwise just rely on the panel id for common arrowpanels. |
|
2127 let type = this._panel.firstChild.getAttribute("popupid") || |
|
2128 this._panel.id; |
|
2129 if (type.startsWith("password-")) |
|
2130 return "passwords"; |
|
2131 if (type == "editBookmarkPanel") |
|
2132 return "bookmarks"; |
|
2133 if (type == "addon-install-complete") { |
|
2134 if (!Services.prefs.prefHasUserValue("services.sync.username")) |
|
2135 return "addons"; |
|
2136 if (!Services.prefs.getBoolPref("services.sync.engine.addons")) |
|
2137 return "addons-sync-disabled"; |
|
2138 } |
|
2139 return null; |
|
2140 ]]></getter> |
|
2141 </property> |
|
2142 <property name="_notificationMessage"> |
|
2143 <getter><![CDATA[ |
|
2144 return gNavigatorBundle.getFormattedString( |
|
2145 "syncPromoNotification." + this._notificationType + ".description", |
|
2146 [this._brandBundle.GetStringFromName("syncBrandShortName")] |
|
2147 ); |
|
2148 ]]></getter> |
|
2149 </property> |
|
2150 <property name="_notificationLink"> |
|
2151 <getter><![CDATA[ |
|
2152 if (this._notificationType == "addons-sync-disabled") { |
|
2153 return "https://support.mozilla.org/kb/how-do-i-enable-add-sync"; |
|
2154 } |
|
2155 return "https://services.mozilla.com/sync/"; |
|
2156 ]]></getter> |
|
2157 </property> |
|
2158 <method name="onCloseButtonCommand"> |
|
2159 <body><![CDATA[ |
|
2160 this._viewsLeft = 0; |
|
2161 this.hidden = true; |
|
2162 ]]></body> |
|
2163 </method> |
|
2164 <method name="onLinkClick"> |
|
2165 <body><![CDATA[ |
|
2166 // Open a new selected tab and close the current panel. |
|
2167 openUILinkIn(this._promolink.getAttribute("href"), "tab"); |
|
2168 this._panel.hidePopup(); |
|
2169 ]]></body> |
|
2170 </method> |
|
2171 <method name="handleEvent"> |
|
2172 <parameter name="event"/> |
|
2173 <body><![CDATA[ |
|
2174 if (event.type != "popupshowing" || event.target != this._panel) |
|
2175 return; |
|
2176 |
|
2177 // A previous notification may have unhidden this. |
|
2178 this.hidden = true; |
|
2179 |
|
2180 // Only handle supported notification panels. |
|
2181 if (!this._notificationType) { |
|
2182 return; |
|
2183 } |
|
2184 |
|
2185 let viewsLeft = this._viewsLeft; |
|
2186 if (viewsLeft) { |
|
2187 if (Services.prefs.prefHasUserValue("services.sync.username") && |
|
2188 this._notificationType != "addons-sync-disabled") { |
|
2189 // If the user has already setup Sync, don't show the notification. |
|
2190 this._viewsLeft = 0; |
|
2191 // Be sure to hide the panel, in case it was visible and the user |
|
2192 // decided to setup Sync after noticing it. |
|
2193 viewsLeft = 0; |
|
2194 // The panel is still hidden, just bail out. |
|
2195 return; |
|
2196 } |
|
2197 else { |
|
2198 this._viewsLeft = viewsLeft - 1; |
|
2199 } |
|
2200 |
|
2201 this._promolink.setAttribute("href", this._notificationLink); |
|
2202 this._promolink.value = gNavigatorBundle.getString("syncPromoNotification.learnMoreLinkText"); |
|
2203 |
|
2204 this.hidden = false; |
|
2205 |
|
2206 // HACK: The description element doesn't wrap correctly in panels, |
|
2207 // thus set a width on it, based on the available space, before |
|
2208 // setting its textContent. Then set its height as well, to |
|
2209 // fix wrong height calculation on Linux (bug 659578). |
|
2210 this._panel.addEventListener("popupshown", function panelShown() { |
|
2211 this._panel.removeEventListener("popupshown", panelShown, true); |
|
2212 // Previous popupShown events may close the panel or change |
|
2213 // its contents, so ensure this is still valid. |
|
2214 if (this._panel.state != "open" || !this._notificationType) |
|
2215 return; |
|
2216 this._promomessage.width = this._promomessage.getBoundingClientRect().width; |
|
2217 this._promomessage.firstChild.textContent = this._notificationMessage; |
|
2218 this._promomessage.height = this._promomessage.getBoundingClientRect().height; |
|
2219 }.bind(this), true); |
|
2220 } |
|
2221 ]]></body> |
|
2222 </method> |
|
2223 </implementation> |
|
2224 </binding> |
|
2225 |
|
2226 <binding id="toolbarbutton-badged" display="xul:button" |
|
2227 extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton"> |
|
2228 <content> |
|
2229 <children includes="observes|template|menupopup|panel|tooltip"/> |
|
2230 <xul:hbox class="toolbarbutton-badge-container" align="start" pack="end"> |
|
2231 <xul:hbox class="toolbarbutton-badge" xbl:inherits="badge"/> |
|
2232 <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/> |
|
2233 </xul:hbox> |
|
2234 <xul:label class="toolbarbutton-text" crop="right" flex="1" |
|
2235 xbl:inherits="value=label,accesskey,crop,wrap"/> |
|
2236 <xul:label class="toolbarbutton-multiline-text" flex="1" |
|
2237 xbl:inherits="xbl:text=label,accesskey,wrap"/> |
|
2238 </content> |
|
2239 </binding> |
|
2240 |
|
2241 </bindings> |