toolkit/content/widgets/popup.xml

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 <?xml version="1.0"?>
michael@0 2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 - License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
michael@0 5
michael@0 6
michael@0 7 <bindings id="popupBindings"
michael@0 8 xmlns="http://www.mozilla.org/xbl"
michael@0 9 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
michael@0 10 xmlns:xbl="http://www.mozilla.org/xbl">
michael@0 11
michael@0 12 <binding id="popup-base">
michael@0 13 <resources>
michael@0 14 <stylesheet src="chrome://global/skin/popup.css"/>
michael@0 15 </resources>
michael@0 16
michael@0 17 <implementation implements="nsIDOMXULPopupElement">
michael@0 18 <property name="label" onget="return this.getAttribute('label');"
michael@0 19 onset="this.setAttribute('label', val); return val;"/>
michael@0 20 <property name="position" onget="return this.getAttribute('position');"
michael@0 21 onset="this.setAttribute('position', val); return val;"/>
michael@0 22 <property name="popupBoxObject">
michael@0 23 <getter>
michael@0 24 return this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject);
michael@0 25 </getter>
michael@0 26 </property>
michael@0 27
michael@0 28 <property name="state" readonly="true"
michael@0 29 onget="return this.popupBoxObject.popupState"/>
michael@0 30
michael@0 31 <property name="triggerNode" readonly="true"
michael@0 32 onget="return this.popupBoxObject.triggerNode"/>
michael@0 33
michael@0 34 <property name="anchorNode" readonly="true"
michael@0 35 onget="return this.popupBoxObject.anchorNode"/>
michael@0 36
michael@0 37 <method name="openPopup">
michael@0 38 <parameter name="aAnchorElement"/>
michael@0 39 <parameter name="aPosition"/>
michael@0 40 <parameter name="aX"/>
michael@0 41 <parameter name="aY"/>
michael@0 42 <parameter name="aIsContextMenu"/>
michael@0 43 <parameter name="aAttributesOverride"/>
michael@0 44 <parameter name="aTriggerEvent"/>
michael@0 45 <body>
michael@0 46 <![CDATA[
michael@0 47 try {
michael@0 48 var popupBox = this.popupBoxObject;
michael@0 49 if (popupBox)
michael@0 50 popupBox.openPopup(aAnchorElement, aPosition, aX, aY,
michael@0 51 aIsContextMenu, aAttributesOverride, aTriggerEvent);
michael@0 52 } catch(e) {}
michael@0 53 ]]>
michael@0 54 </body>
michael@0 55 </method>
michael@0 56
michael@0 57 <method name="openPopupAtScreen">
michael@0 58 <parameter name="aX"/>
michael@0 59 <parameter name="aY"/>
michael@0 60 <parameter name="aIsContextMenu"/>
michael@0 61 <parameter name="aTriggerEvent"/>
michael@0 62 <body>
michael@0 63 <![CDATA[
michael@0 64 try {
michael@0 65 var popupBox = this.popupBoxObject;
michael@0 66 if (popupBox)
michael@0 67 popupBox.openPopupAtScreen(aX, aY, aIsContextMenu, aTriggerEvent);
michael@0 68 } catch(e) {}
michael@0 69 ]]>
michael@0 70 </body>
michael@0 71 </method>
michael@0 72
michael@0 73 <method name="showPopup">
michael@0 74 <parameter name="element"/>
michael@0 75 <parameter name="xpos"/>
michael@0 76 <parameter name="ypos"/>
michael@0 77 <parameter name="popuptype"/>
michael@0 78 <parameter name="anchoralignment"/>
michael@0 79 <parameter name="popupalignment"/>
michael@0 80 <body>
michael@0 81 <![CDATA[
michael@0 82 var popupBox = null;
michael@0 83 var menuBox = null;
michael@0 84 try {
michael@0 85 popupBox = this.popupBoxObject;
michael@0 86 } catch(e) {}
michael@0 87 try {
michael@0 88 menuBox = this.parentNode.boxObject;
michael@0 89 } catch(e) {}
michael@0 90 if (menuBox instanceof Components.interfaces.nsIMenuBoxObject)
michael@0 91 menuBox.openMenu(true);
michael@0 92 else if (popupBox)
michael@0 93 popupBox.showPopup(element, this, xpos, ypos, popuptype, anchoralignment, popupalignment);
michael@0 94 ]]>
michael@0 95 </body>
michael@0 96 </method>
michael@0 97
michael@0 98 <method name="hidePopup">
michael@0 99 <body>
michael@0 100 <![CDATA[
michael@0 101 var popupBox = null;
michael@0 102 var menuBox = null;
michael@0 103 try {
michael@0 104 popupBox = this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject);
michael@0 105 } catch(e) {}
michael@0 106 try {
michael@0 107 menuBox = this.parentNode.boxObject;
michael@0 108 } catch(e) {}
michael@0 109 if (menuBox instanceof Components.interfaces.nsIMenuBoxObject)
michael@0 110 menuBox.openMenu(false);
michael@0 111 else if (popupBox)
michael@0 112 popupBox.hidePopup();
michael@0 113 ]]>
michael@0 114 </body>
michael@0 115 </method>
michael@0 116
michael@0 117 <property name="autoPosition">
michael@0 118 <getter>
michael@0 119 <![CDATA[
michael@0 120 return this.popupBoxObject.autoPosition;
michael@0 121 ]]>
michael@0 122 </getter>
michael@0 123 <setter>
michael@0 124 <![CDATA[
michael@0 125 return this.popupBoxObject.autoPosition = val;
michael@0 126 ]]>
michael@0 127 </setter>
michael@0 128 </property>
michael@0 129
michael@0 130 <property name="alignmentPosition" readonly="true">
michael@0 131 <getter>
michael@0 132 <![CDATA[
michael@0 133 return this.popupBoxObject.alignmentPosition;
michael@0 134 ]]>
michael@0 135 </getter>
michael@0 136 </property>
michael@0 137
michael@0 138 <property name="alignmentOffset" readonly="true">
michael@0 139 <getter>
michael@0 140 <![CDATA[
michael@0 141 return this.popupBoxObject.alignmentOffset;
michael@0 142 ]]>
michael@0 143 </getter>
michael@0 144 </property>
michael@0 145
michael@0 146 <method name="enableKeyboardNavigator">
michael@0 147 <parameter name="aEnableKeyboardNavigator"/>
michael@0 148 <body>
michael@0 149 <![CDATA[
michael@0 150 this.popupBoxObject.enableKeyboardNavigator(aEnableKeyboardNavigator);
michael@0 151 ]]>
michael@0 152 </body>
michael@0 153 </method>
michael@0 154
michael@0 155 <method name="enableRollup">
michael@0 156 <parameter name="aEnableRollup"/>
michael@0 157 <body>
michael@0 158 <![CDATA[
michael@0 159 this.popupBoxObject.enableRollup(aEnableRollup);
michael@0 160 ]]>
michael@0 161 </body>
michael@0 162 </method>
michael@0 163
michael@0 164 <method name="sizeTo">
michael@0 165 <parameter name="aWidth"/>
michael@0 166 <parameter name="aHeight"/>
michael@0 167 <body>
michael@0 168 <![CDATA[
michael@0 169 this.popupBoxObject.sizeTo(aWidth, aHeight);
michael@0 170 ]]>
michael@0 171 </body>
michael@0 172 </method>
michael@0 173
michael@0 174 <method name="moveTo">
michael@0 175 <parameter name="aLeft"/>
michael@0 176 <parameter name="aTop"/>
michael@0 177 <body>
michael@0 178 <![CDATA[
michael@0 179 this.popupBoxObject.moveTo(aLeft, aTop);
michael@0 180 ]]>
michael@0 181 </body>
michael@0 182 </method>
michael@0 183
michael@0 184 <method name="moveToAnchor">
michael@0 185 <parameter name="aAnchorElement"/>
michael@0 186 <parameter name="aPosition"/>
michael@0 187 <parameter name="aX"/>
michael@0 188 <parameter name="aY"/>
michael@0 189 <parameter name="aAttributesOverride"/>
michael@0 190 <body>
michael@0 191 <![CDATA[
michael@0 192 this.popupBoxObject.moveToAnchor(aAnchorElement, aPosition, aX, aY, aAttributesOverride);
michael@0 193 ]]>
michael@0 194 </body>
michael@0 195 </method>
michael@0 196
michael@0 197 <method name="getOuterScreenRect">
michael@0 198 <body>
michael@0 199 <![CDATA[
michael@0 200 return this.popupBoxObject.getOuterScreenRect();
michael@0 201 ]]>
michael@0 202 </body>
michael@0 203 </method>
michael@0 204 </implementation>
michael@0 205
michael@0 206 </binding>
michael@0 207
michael@0 208 <binding id="popup" role="xul:menupopup"
michael@0 209 extends="chrome://global/content/bindings/popup.xml#popup-base">
michael@0 210
michael@0 211 <content>
michael@0 212 <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
michael@0 213 smoothscroll="false">
michael@0 214 <children/>
michael@0 215 </xul:arrowscrollbox>
michael@0 216 </content>
michael@0 217
michael@0 218 <handlers>
michael@0 219 <handler event="popupshowing" phase="target">
michael@0 220 <![CDATA[
michael@0 221 var array = [];
michael@0 222 var width = 0;
michael@0 223 for (var menuitem = this.firstChild; menuitem; menuitem = menuitem.nextSibling) {
michael@0 224 if (menuitem.localName == "menuitem" && menuitem.hasAttribute("acceltext")) {
michael@0 225 var accel = document.getAnonymousElementByAttribute(menuitem, "anonid", "accel");
michael@0 226 if (accel && accel.boxObject) {
michael@0 227 array.push(accel);
michael@0 228 if (accel.boxObject.width > width)
michael@0 229 width = accel.boxObject.width;
michael@0 230 }
michael@0 231 }
michael@0 232 }
michael@0 233 for (var i = 0; i < array.length; i++)
michael@0 234 array[i].width = width;
michael@0 235 ]]>
michael@0 236 </handler>
michael@0 237 </handlers>
michael@0 238 </binding>
michael@0 239
michael@0 240 <binding id="panel" role="xul:panel"
michael@0 241 extends="chrome://global/content/bindings/popup.xml#popup-base">
michael@0 242 <implementation implements="nsIDOMXULPopupElement">
michael@0 243 <field name="_prevFocus">0</field>
michael@0 244 <field name="_dragBindingAlive">true</field>
michael@0 245 <constructor>
michael@0 246 <![CDATA[
michael@0 247 if (this.getAttribute("backdrag") == "true" && !this._draggableStarted) {
michael@0 248 this._draggableStarted = true;
michael@0 249 try {
michael@0 250 let tmp = {};
michael@0 251 Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
michael@0 252 let draghandle = new tmp.WindowDraggingElement(this);
michael@0 253 draghandle.mouseDownCheck = function () this._dragBindingAlive;
michael@0 254 } catch (e) {}
michael@0 255 }
michael@0 256 ]]>
michael@0 257 </constructor>
michael@0 258 </implementation>
michael@0 259
michael@0 260 <handlers>
michael@0 261 <handler event="popupshowing"><![CDATA[
michael@0 262 // Capture the previous focus before has a chance to get set inside the panel
michael@0 263 try {
michael@0 264 this._prevFocus = document.commandDispatcher.focusedElement;
michael@0 265 if (!this._prevFocus) // Content window has focus
michael@0 266 this._prevFocus = document.commandDispatcher.focusedWindow;
michael@0 267 } catch (ex) {
michael@0 268 this._prevFocus = document.activeElement;
michael@0 269 }
michael@0 270 ]]></handler>
michael@0 271 <handler event="popupshown"><![CDATA[
michael@0 272 // Fire event for accessibility APIs
michael@0 273 var alertEvent = document.createEvent("Events");
michael@0 274 alertEvent.initEvent("AlertActive", true, true);
michael@0 275 this.dispatchEvent(alertEvent);
michael@0 276 ]]></handler>
michael@0 277 <handler event="popuphiding"><![CDATA[
michael@0 278 try {
michael@0 279 this._currentFocus = document.commandDispatcher.focusedElement;
michael@0 280 } catch (e) {
michael@0 281 this._currentFocus = document.activeElement;
michael@0 282 }
michael@0 283 ]]></handler>
michael@0 284 <handler event="popuphidden"><![CDATA[
michael@0 285 var currentFocus = this._currentFocus;
michael@0 286 var prevFocus = this._prevFocus;
michael@0 287 this._currentFocus = null;
michael@0 288 this._prevFocus = null;
michael@0 289 if (prevFocus && currentFocus && this.getAttribute("norestorefocus") != "true") {
michael@0 290 // Try to restore focus
michael@0 291 try {
michael@0 292 if (document.commandDispatcher.focusedWindow != window)
michael@0 293 return; // Focus has already been set to a window outside of this panel
michael@0 294 } catch(ex) {}
michael@0 295 while (currentFocus) {
michael@0 296 if (currentFocus == this) {
michael@0 297 // Focus was set on an element inside this panel,
michael@0 298 // so we need to move it back to where it was previously
michael@0 299 try {
michael@0 300 let fm = Components.classes["@mozilla.org/focus-manager;1"]
michael@0 301 .getService(Components.interfaces.nsIFocusManager);
michael@0 302 fm.setFocus(prevFocus, fm.FLAG_NOSCROLL);
michael@0 303 } catch(e) {
michael@0 304 prevFocus.focus();
michael@0 305 }
michael@0 306 return;
michael@0 307 }
michael@0 308 currentFocus = currentFocus.parentNode;
michael@0 309 }
michael@0 310 }
michael@0 311 ]]></handler>
michael@0 312 </handlers>
michael@0 313 </binding>
michael@0 314
michael@0 315 <binding id="arrowpanel" extends="chrome://global/content/bindings/popup.xml#panel">
michael@0 316 <content flip="both" side="top" position="bottomcenter topleft" consumeoutsideclicks="false">
michael@0 317 <xul:vbox anonid="container" class="panel-arrowcontainer" flex="1"
michael@0 318 xbl:inherits="side,panelopen">
michael@0 319 <xul:box anonid="arrowbox" class="panel-arrowbox">
michael@0 320 <xul:image anonid="arrow" class="panel-arrow" xbl:inherits="side"/>
michael@0 321 </xul:box>
michael@0 322 <xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
michael@0 323 <children/>
michael@0 324 <xul:box class="panel-inner-arrowcontentfooter" xbl:inherits="footertype" hidden="true"/>
michael@0 325 </xul:box>
michael@0 326 </xul:vbox>
michael@0 327 </content>
michael@0 328 <implementation>
michael@0 329 <field name="_fadeTimer">null</field>
michael@0 330 <method name="sizeTo">
michael@0 331 <parameter name="aWidth"/>
michael@0 332 <parameter name="aHeight"/>
michael@0 333 <body>
michael@0 334 <![CDATA[
michael@0 335 this.popupBoxObject.sizeTo(aWidth, aHeight);
michael@0 336 if (this.state == "open")
michael@0 337 this.adjustArrowPosition();
michael@0 338 ]]>
michael@0 339 </body>
michael@0 340 </method>
michael@0 341 <method name="moveTo">
michael@0 342 <parameter name="aLeft"/>
michael@0 343 <parameter name="aTop"/>
michael@0 344 <body>
michael@0 345 <![CDATA[
michael@0 346 this.popupBoxObject.moveTo(aLeft, aTop);
michael@0 347 if (this.state == "open")
michael@0 348 this.adjustArrowPosition();
michael@0 349 ]]>
michael@0 350 </body>
michael@0 351 </method>
michael@0 352 <method name="moveToAnchor">
michael@0 353 <parameter name="aAnchorElement"/>
michael@0 354 <parameter name="aPosition"/>
michael@0 355 <parameter name="aX"/>
michael@0 356 <parameter name="aY"/>
michael@0 357 <parameter name="aAttributesOverride"/>
michael@0 358 <body>
michael@0 359 <![CDATA[
michael@0 360 this.popupBoxObject.moveToAnchor(aAnchorElement, aPosition, aX, aY, aAttributesOverride);
michael@0 361 if (this.state == "open")
michael@0 362 this.adjustArrowPosition();
michael@0 363 ]]>
michael@0 364 </body>
michael@0 365 </method>
michael@0 366 <method name="adjustArrowPosition">
michael@0 367 <body>
michael@0 368 <![CDATA[
michael@0 369 var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
michael@0 370
michael@0 371 var anchor = this.anchorNode;
michael@0 372 if (!anchor) {
michael@0 373 arrow.hidden = true;
michael@0 374 return;
michael@0 375 }
michael@0 376
michael@0 377 var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
michael@0 378 var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
michael@0 379
michael@0 380 var position = this.alignmentPosition;
michael@0 381 var offset = this.alignmentOffset;
michael@0 382
michael@0 383 // if this panel has a "sliding" arrow, we may have previously set margins...
michael@0 384 arrowbox.style.removeProperty("transform");
michael@0 385 if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
michael@0 386 container.orient = "horizontal";
michael@0 387 arrowbox.orient = "vertical";
michael@0 388 if (position.indexOf("_after") > 0) {
michael@0 389 arrowbox.pack = "end";
michael@0 390 } else {
michael@0 391 arrowbox.pack = "start";
michael@0 392 }
michael@0 393 arrowbox.style.transform = "translate(0, " + -offset + "px)";
michael@0 394
michael@0 395 // The assigned side stays the same regardless of direction.
michael@0 396 var isRTL = (window.getComputedStyle(this).direction == "rtl");
michael@0 397
michael@0 398 if (position.indexOf("start_") == 0) {
michael@0 399 container.dir = "reverse";
michael@0 400 this.setAttribute("side", isRTL ? "left" : "right");
michael@0 401 }
michael@0 402 else {
michael@0 403 container.dir = "";
michael@0 404 this.setAttribute("side", isRTL ? "right" : "left");
michael@0 405 }
michael@0 406 }
michael@0 407 else if (position.indexOf("before_") == 0 || position.indexOf("after_") == 0) {
michael@0 408 container.orient = "";
michael@0 409 arrowbox.orient = "";
michael@0 410 if (position.indexOf("_end") > 0) {
michael@0 411 arrowbox.pack = "end";
michael@0 412 } else {
michael@0 413 arrowbox.pack = "start";
michael@0 414 }
michael@0 415 arrowbox.style.transform = "translate(" + -offset + "px, 0)";
michael@0 416
michael@0 417 if (position.indexOf("before_") == 0) {
michael@0 418 container.dir = "reverse";
michael@0 419 this.setAttribute("side", "bottom");
michael@0 420 }
michael@0 421 else {
michael@0 422 container.dir = "";
michael@0 423 this.setAttribute("side", "top");
michael@0 424 }
michael@0 425 }
michael@0 426
michael@0 427 arrow.hidden = false;
michael@0 428 ]]>
michael@0 429 </body>
michael@0 430 </method>
michael@0 431 </implementation>
michael@0 432 <handlers>
michael@0 433 <handler event="popupshowing" phase="target">
michael@0 434 <![CDATA[
michael@0 435 this.adjustArrowPosition();
michael@0 436
michael@0 437 // set fading
michael@0 438 var fade = this.getAttribute("fade");
michael@0 439 var fadeDelay = (fade == "fast") ? 1 : fade == "slow" ? 4000 : 0;
michael@0 440 if (fadeDelay) {
michael@0 441 this._fadeTimer = setTimeout(function (self) {
michael@0 442 self.style.opacity = 0.2;
michael@0 443 }, fadeDelay, this);
michael@0 444 }
michael@0 445 ]]>
michael@0 446 </handler>
michael@0 447 <handler event="popuphiding" phase="target">
michael@0 448 clearTimeout(this._fadeTimer);
michael@0 449 this.style.removeProperty("opacity");
michael@0 450 </handler>
michael@0 451 <handler event="transitionend" phase="target">
michael@0 452 <![CDATA[
michael@0 453 if (event.propertyName == "opacity" &&
michael@0 454 event.originalTarget == this) {
michael@0 455 this.hidePopup();
michael@0 456 this.style.removeProperty("opacity");
michael@0 457 }
michael@0 458 ]]>
michael@0 459 </handler>
michael@0 460 <handler event="popupshown" phase="target">
michael@0 461 this.setAttribute("panelopen", "true");
michael@0 462 </handler>
michael@0 463 <handler event="popuphidden" phase="target">
michael@0 464 this.removeAttribute("panelopen");
michael@0 465 </handler>
michael@0 466 </handlers>
michael@0 467 </binding>
michael@0 468
michael@0 469 <binding id="tooltip" role="xul:tooltip"
michael@0 470 extends="chrome://global/content/bindings/popup.xml#popup-base">
michael@0 471 <content>
michael@0 472 <children>
michael@0 473 <xul:label class="tooltip-label" xbl:inherits="xbl:text=label" flex="1"/>
michael@0 474 </children>
michael@0 475 </content>
michael@0 476
michael@0 477 <implementation>
michael@0 478 <field name="_mouseOutCount">0</field>
michael@0 479 <field name="_isMouseOver">false</field>
michael@0 480
michael@0 481 <property name="label"
michael@0 482 onget="return this.getAttribute('label');"
michael@0 483 onset="this.setAttribute('label', val); return val;"/>
michael@0 484
michael@0 485 <property name="page" onset="if (val) this.setAttribute('page', 'true');
michael@0 486 else this.removeAttribute('page');
michael@0 487 return val;"
michael@0 488 onget="return this.getAttribute('page') == 'true';"/>
michael@0 489
michael@0 490 <!-- Given the supplied element within a page, set the tooltip's text to the text
michael@0 491 for that element. Returns true if text was assigned, and false if the no text
michael@0 492 is set, which normally would be used to cancel tooltip display.
michael@0 493
michael@0 494 Note that DefaultTooltipTextProvider::GetNodeText() from nsDocShellTreeOwner.cpp
michael@0 495 also performs the same function, but for embedded clients that don't use a XUL/JS
michael@0 496 layer. These two should be kept synchronized.
michael@0 497 -->
michael@0 498 <method name="fillInPageTooltip">
michael@0 499 <parameter name="tipElement"/>
michael@0 500 <body>
michael@0 501 <![CDATA[
michael@0 502 // Don't show the tooltip if the tooltip node is a document or disconnected.
michael@0 503 if (!tipElement || !tipElement.ownerDocument ||
michael@0 504 (tipElement.ownerDocument.compareDocumentPosition(tipElement) & document.DOCUMENT_POSITION_DISCONNECTED)) {
michael@0 505 return false;
michael@0 506 }
michael@0 507
michael@0 508 var defView = tipElement.ownerDocument.defaultView;
michael@0 509 // XXX Work around bug 350679:
michael@0 510 // "Tooltips can be fired in documents with no view".
michael@0 511 if (!defView)
michael@0 512 return false;
michael@0 513
michael@0 514 const XLinkNS = "http://www.w3.org/1999/xlink";
michael@0 515 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 516
michael@0 517 var titleText = null;
michael@0 518 var XLinkTitleText = null;
michael@0 519 var SVGTitleText = null;
michael@0 520 var XULtooltiptextText = null;
michael@0 521 var lookingForSVGTitle = true;
michael@0 522 var direction = tipElement.ownerDocument.dir;
michael@0 523
michael@0 524 // If the element is invalid per HTML5 Forms specifications and has no title,
michael@0 525 // show the constraint validation error message.
michael@0 526 if ((tipElement instanceof HTMLInputElement ||
michael@0 527 tipElement instanceof HTMLTextAreaElement ||
michael@0 528 tipElement instanceof HTMLSelectElement ||
michael@0 529 tipElement instanceof HTMLButtonElement) &&
michael@0 530 !tipElement.hasAttribute('title') &&
michael@0 531 (!tipElement.form || !tipElement.form.noValidate)) {
michael@0 532 // If the element is barred from constraint validation or valid,
michael@0 533 // the validation message will be the empty string.
michael@0 534 titleText = tipElement.validationMessage || null;
michael@0 535 }
michael@0 536
michael@0 537 // If the element is an <input type='file'> without a title, we should show
michael@0 538 // the current file selection.
michael@0 539 if (!titleText &&
michael@0 540 tipElement instanceof HTMLInputElement &&
michael@0 541 tipElement.type == 'file' &&
michael@0 542 !tipElement.hasAttribute('title')) {
michael@0 543 let files = tipElement.files;
michael@0 544
michael@0 545 try {
michael@0 546 var bundle = Components.classes['@mozilla.org/intl/stringbundle;1']
michael@0 547 .getService(Components.interfaces.nsIStringBundleService)
michael@0 548 .createBundle("chrome://global/locale/layout/HtmlForm.properties");
michael@0 549 if (files.length == 0) {
michael@0 550 if (tipElement.multiple) {
michael@0 551 titleText = bundle.GetStringFromName("NoFilesSelected");
michael@0 552 } else {
michael@0 553 titleText = bundle.GetStringFromName("NoFileSelected");
michael@0 554 }
michael@0 555 } else {
michael@0 556 titleText = files[0].name;
michael@0 557 // For UX and performance (jank) reasons we cap the number of
michael@0 558 // files that we list in the tooltip to 20 plus a "and xxx more"
michael@0 559 // line, or to 21 if exactly 21 files were picked.
michael@0 560 const TRUNCATED_FILE_COUNT = 20;
michael@0 561 let count = Math.min(files.length, TRUNCATED_FILE_COUNT);
michael@0 562 for (let i = 1; i < count; ++i) {
michael@0 563 titleText += "\n" + files[i].name;
michael@0 564 }
michael@0 565 if (files.length == TRUNCATED_FILE_COUNT + 1) {
michael@0 566 titleText += "\n" + files[TRUNCATED_FILE_COUNT].name;
michael@0 567 } else if (files.length > TRUNCATED_FILE_COUNT + 1) {
michael@0 568 let xmoreStr = bundle.GetStringFromName("AndNMoreFiles");
michael@0 569 let xmoreNum = files.length - TRUNCATED_FILE_COUNT;
michael@0 570 let tmp = {};
michael@0 571 Components.utils.import("resource://gre/modules/PluralForm.jsm", tmp);
michael@0 572 let andXMoreStr = tmp.PluralForm.get(xmoreNum, xmoreStr).replace("#1", xmoreNum);
michael@0 573 titleText += "\n" + andXMoreStr;
michael@0 574 }
michael@0 575 }
michael@0 576 } catch(e) {}
michael@0 577 }
michael@0 578
michael@0 579 // Check texts against null so that title="" can be used to undefine a
michael@0 580 // title on a child element.
michael@0 581 while (tipElement &&
michael@0 582 (titleText == null) && (XLinkTitleText == null) &&
michael@0 583 (SVGTitleText == null) && (XULtooltiptextText == null)) {
michael@0 584
michael@0 585 if (tipElement.nodeType == Node.ELEMENT_NODE) {
michael@0 586 if (tipElement.namespaceURI == XULNS)
michael@0 587 XULtooltiptextText = tipElement.getAttribute("tooltiptext");
michael@0 588 else
michael@0 589 titleText = tipElement.getAttribute("title");
michael@0 590
michael@0 591 if ((tipElement instanceof HTMLAnchorElement ||
michael@0 592 tipElement instanceof HTMLAreaElement ||
michael@0 593 tipElement instanceof HTMLLinkElement ||
michael@0 594 tipElement instanceof SVGAElement) && tipElement.href) {
michael@0 595 XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
michael@0 596 }
michael@0 597 if (lookingForSVGTitle &&
michael@0 598 (!(tipElement instanceof SVGElement) ||
michael@0 599 tipElement.parentNode.nodeType == Node.DOCUMENT_NODE)) {
michael@0 600 lookingForSVGTitle = false;
michael@0 601 }
michael@0 602 if (lookingForSVGTitle) {
michael@0 603 for (let childNode of tipElement.childNodes) {
michael@0 604 if (childNode instanceof SVGTitleElement) {
michael@0 605 SVGTitleText = childNode.textContent;
michael@0 606 break;
michael@0 607 }
michael@0 608 }
michael@0 609 }
michael@0 610
michael@0 611 direction = defView.getComputedStyle(tipElement, "")
michael@0 612 .getPropertyValue("direction");
michael@0 613 }
michael@0 614
michael@0 615 tipElement = tipElement.parentNode;
michael@0 616 }
michael@0 617
michael@0 618 this.style.direction = direction;
michael@0 619
michael@0 620 return [titleText, XLinkTitleText, SVGTitleText, XULtooltiptextText].some(function (t) {
michael@0 621 if (t && /\S/.test(t)) {
michael@0 622 // Make CRLF and CR render one line break each.
michael@0 623 this.label = t.replace(/\r\n?/g, '\n');
michael@0 624 return true;
michael@0 625 }
michael@0 626
michael@0 627 return false;
michael@0 628 }, this);
michael@0 629
michael@0 630 return false;
michael@0 631 ]]>
michael@0 632 </body>
michael@0 633 </method>
michael@0 634 </implementation>
michael@0 635
michael@0 636 <handlers>
michael@0 637 <handler event="mouseover"><![CDATA[
michael@0 638 var rel = event.relatedTarget;
michael@0 639 //dump("ENTERING " + (rel ? rel.localName : "null") + "\n");
michael@0 640 if (!rel)
michael@0 641 return;
michael@0 642
michael@0 643 // find out if the node we entered from is one of our anonymous children
michael@0 644 while (rel) {
michael@0 645 if (rel == this)
michael@0 646 break;
michael@0 647 rel = rel.parentNode;
michael@0 648 }
michael@0 649
michael@0 650 // if the exited node is not a descendant of ours, we are entering for the first time
michael@0 651 if (rel != this)
michael@0 652 this._isMouseOver = true;
michael@0 653 ]]></handler>
michael@0 654
michael@0 655 <handler event="mouseout"><![CDATA[
michael@0 656 var rel = event.relatedTarget;
michael@0 657 //dump("LEAVING " + (rel ? rel.localName : "null") + "\n");
michael@0 658
michael@0 659 // relatedTarget is null when the titletip is first shown: a mouseout event fires
michael@0 660 // because the mouse is exiting the main window and entering the titletip "window".
michael@0 661 // relatedTarget is also null when the mouse exits the main window completely,
michael@0 662 // so count how many times relatedTarget was null after titletip is first shown
michael@0 663 // and hide popup the 2nd time
michael@0 664 if (!rel) {
michael@0 665 ++this._mouseOutCount;
michael@0 666 if (this._mouseOutCount > 1)
michael@0 667 this.hidePopup();
michael@0 668 return;
michael@0 669 }
michael@0 670
michael@0 671 // find out if the node we are entering is one of our anonymous children
michael@0 672 while (rel) {
michael@0 673 if (rel == this)
michael@0 674 break;
michael@0 675 rel = rel.parentNode;
michael@0 676 }
michael@0 677
michael@0 678 // if the entered node is not a descendant of ours, hide the tooltip
michael@0 679 if (rel != this && this._isMouseOver) {
michael@0 680 this.hidePopup();
michael@0 681 }
michael@0 682 ]]></handler>
michael@0 683
michael@0 684 <handler event="popupshowing"><![CDATA[
michael@0 685 if (this.page && !this.fillInPageTooltip(this.triggerNode)) {
michael@0 686 event.preventDefault();
michael@0 687 }
michael@0 688 ]]></handler>
michael@0 689
michael@0 690 <handler event="popuphiding"><![CDATA[
michael@0 691 this._isMouseOver = false;
michael@0 692 this._mouseOutCount = 0;
michael@0 693 ]]></handler>
michael@0 694 </handlers>
michael@0 695 </binding>
michael@0 696
michael@0 697 <binding id="popup-scrollbars" extends="chrome://global/content/bindings/popup.xml#popup">
michael@0 698 <content>
michael@0 699 <xul:hbox class="popup-internal-box" flex="1" orient="vertical" style="overflow: auto;">
michael@0 700 <children/>
michael@0 701 </xul:hbox>
michael@0 702 </content>
michael@0 703 </binding>
michael@0 704
michael@0 705 </bindings>

mercurial