Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 <?xml version="1.0"?>
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 <bindings id="placesMenuBindings"
8 xmlns="http://www.mozilla.org/xbl"
9 xmlns:xbl="http://www.mozilla.org/xbl"
10 xmlns:html="http://www.w3.org/1999/xhtml"
11 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
13 <binding id="places-popup-base"
14 extends="chrome://global/content/bindings/popup.xml#popup">
15 <content>
16 <xul:hbox flex="1">
17 <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
18 <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
19 </xul:vbox>
20 <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
21 smoothscroll="false">
22 <children/>
23 </xul:arrowscrollbox>
24 </xul:hbox>
25 </content>
27 <implementation>
29 <field name="_indicatorBar">
30 document.getAnonymousElementByAttribute(this, "class",
31 "menupopup-drop-indicator-bar");
32 </field>
34 <field name="_scrollBox">
35 document.getAnonymousElementByAttribute(this, "class",
36 "popup-internal-box");
37 </field>
39 <!-- This is the view that manage the popup -->
40 <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
42 <!-- Check if we should hide the drop indicator for the target -->
43 <method name="_hideDropIndicator">
44 <parameter name="aEvent"/>
45 <body><![CDATA[
46 let target = aEvent.target;
48 // Don't draw the drop indicator outside of markers.
49 // The markers are hidden, since otherwise sometimes popups acquire
50 // scrollboxes on OS X, so we can't use them directly.
51 let firstChildTop = this._startMarker.nextSibling.boxObject.y;
52 let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
53 this._endMarker.previousSibling.boxObject.height;
54 let betweenMarkers = target.boxObject.y >= firstChildTop ||
55 target.boxObject.y <= lastChildBottom;
57 // Hide the dropmarker if current node is not a Places node.
58 return !(target && target._placesNode && betweenMarkers);
59 ]]></body>
60 </method>
62 <!-- This function returns information about where to drop when
63 dragging over this popup insertion point -->
64 <method name="_getDropPoint">
65 <parameter name="aEvent"/>
66 <body><![CDATA[
67 // Can't drop if the menu isn't a folder
68 let resultNode = this._placesNode;
70 if (!PlacesUtils.nodeIsFolder(resultNode) ||
71 PlacesControllerDragHelper.disallowInsertion(resultNode)) {
72 return null;
73 }
75 var dropPoint = { ip: null, folderElt: null };
77 // The element we are dragging over
78 let elt = aEvent.target;
79 if (elt.localName == "menupopup")
80 elt = elt.parentNode;
82 // Calculate positions taking care of arrowscrollbox
83 let scrollbox = this._scrollBox;
84 let eventY = aEvent.layerY + (scrollbox.boxObject.y - this.boxObject.y);
85 let scrollboxOffset = scrollbox.scrollBoxObject.y -
86 (scrollbox.boxObject.y - this.boxObject.y);
87 let eltY = elt.boxObject.y - scrollboxOffset;
88 let eltHeight = elt.boxObject.height;
90 if (!elt._placesNode) {
91 // If we are dragging over a non places node drop at the end.
92 dropPoint.ip = new InsertionPoint(
93 PlacesUtils.getConcreteItemId(resultNode),
94 -1,
95 Ci.nsITreeView.DROP_ON);
96 // We can set folderElt if we are dropping over a static menu that
97 // has an internal placespopup.
98 let isMenu = elt.localName == "menu" ||
99 (elt.localName == "toolbarbutton" &&
100 elt.getAttribute("type") == "menu");
101 if (isMenu && elt.lastChild &&
102 elt.lastChild.hasAttribute("placespopup"))
103 dropPoint.folderElt = elt;
104 return dropPoint;
105 }
106 else if ((PlacesUtils.nodeIsFolder(elt._placesNode) ||
107 PlacesUtils.nodeIsTagQuery(elt._placesNode)) &&
108 !PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
109 // This is a folder or a tag container.
110 if (eventY - eltY < eltHeight * 0.20) {
111 // If mouse is in the top part of the element, drop above folder.
112 dropPoint.ip = new InsertionPoint(
113 PlacesUtils.getConcreteItemId(resultNode),
114 -1,
115 Ci.nsITreeView.DROP_BEFORE,
116 PlacesUtils.nodeIsTagQuery(elt._placesNode),
117 elt._placesNode.itemId);
118 return dropPoint;
119 }
120 else if (eventY - eltY < eltHeight * 0.80) {
121 // If mouse is in the middle of the element, drop inside folder.
122 dropPoint.ip = new InsertionPoint(
123 PlacesUtils.getConcreteItemId(elt._placesNode),
124 -1,
125 Ci.nsITreeView.DROP_ON,
126 PlacesUtils.nodeIsTagQuery(elt._placesNode));
127 dropPoint.folderElt = elt;
128 return dropPoint;
129 }
130 }
131 else if (eventY - eltY <= eltHeight / 2) {
132 // This is a non-folder node or a readonly folder.
133 // If the mouse is above the middle, drop above this item.
134 dropPoint.ip = new InsertionPoint(
135 PlacesUtils.getConcreteItemId(resultNode),
136 -1,
137 Ci.nsITreeView.DROP_BEFORE,
138 PlacesUtils.nodeIsTagQuery(elt._placesNode),
139 elt._placesNode.itemId);
140 return dropPoint;
141 }
143 // Drop below the item.
144 dropPoint.ip = new InsertionPoint(
145 PlacesUtils.getConcreteItemId(resultNode),
146 -1,
147 Ci.nsITreeView.DROP_AFTER,
148 PlacesUtils.nodeIsTagQuery(elt._placesNode),
149 elt._placesNode.itemId);
150 return dropPoint;
151 ]]></body>
152 </method>
154 <!-- Sub-menus should be opened when the mouse drags over them, and closed
155 when the mouse drags off. The overFolder object manages opening and
156 closing of folders when the mouse hovers. -->
157 <field name="_overFolder"><![CDATA[({
158 _self: this,
159 _folder: {elt: null,
160 openTimer: null,
161 hoverTime: 350,
162 closeTimer: null},
163 _closeMenuTimer: null,
165 get elt() {
166 return this._folder.elt;
167 },
168 set elt(val) {
169 return this._folder.elt = val;
170 },
172 get openTimer() {
173 return this._folder.openTimer;
174 },
175 set openTimer(val) {
176 return this._folder.openTimer = val;
177 },
179 get hoverTime() {
180 return this._folder.hoverTime;
181 },
182 set hoverTime(val) {
183 return this._folder.hoverTime = val;
184 },
186 get closeTimer() {
187 return this._folder.closeTimer;
188 },
189 set closeTimer(val) {
190 return this._folder.closeTimer = val;
191 },
193 get closeMenuTimer() {
194 return this._closeMenuTimer;
195 },
196 set closeMenuTimer(val) {
197 return this._closeMenuTimer = val;
198 },
200 setTimer: function OF__setTimer(aTime) {
201 var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
202 timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
203 return timer;
204 },
206 notify: function OF__notify(aTimer) {
207 // Function to process all timer notifications.
209 if (aTimer == this._folder.openTimer) {
210 // Timer to open a submenu that's being dragged over.
211 this._folder.elt.lastChild.setAttribute("autoopened", "true");
212 this._folder.elt.lastChild.showPopup(this._folder.elt);
213 this._folder.openTimer = null;
214 }
216 else if (aTimer == this._folder.closeTimer) {
217 // Timer to close a submenu that's been dragged off of.
218 // Only close the submenu if the mouse isn't being dragged over any
219 // of its child menus.
220 var draggingOverChild = PlacesControllerDragHelper
221 .draggingOverChildNode(this._folder.elt);
222 if (draggingOverChild)
223 this._folder.elt = null;
224 this.clear();
226 // Close any parent folders which aren't being dragged over.
227 // (This is necessary because of the above code that keeps a folder
228 // open while its children are being dragged over.)
229 if (!draggingOverChild)
230 this.closeParentMenus();
231 }
233 else if (aTimer == this.closeMenuTimer) {
234 // Timer to close this menu after the drag exit.
235 var popup = this._self;
236 // if we are no more dragging we can leave the menu open to allow
237 // for better D&D bookmark organization
238 if (PlacesControllerDragHelper.getSession() &&
239 !PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) {
240 popup.hidePopup();
241 // Close any parent menus that aren't being dragged over;
242 // otherwise they'll stay open because they couldn't close
243 // while this menu was being dragged over.
244 this.closeParentMenus();
245 }
246 this._closeMenuTimer = null;
247 }
248 },
250 // Helper function to close all parent menus of this menu,
251 // as long as none of the parent's children are currently being
252 // dragged over.
253 closeParentMenus: function OF__closeParentMenus() {
254 var popup = this._self;
255 var parent = popup.parentNode;
256 while (parent) {
257 if (parent.localName == "menupopup" && parent._placesNode) {
258 if (PlacesControllerDragHelper.draggingOverChildNode(parent.parentNode))
259 break;
260 parent.hidePopup();
261 }
262 parent = parent.parentNode;
263 }
264 },
266 // The mouse is no longer dragging over the stored menubutton.
267 // Close the menubutton, clear out drag styles, and clear all
268 // timers for opening/closing it.
269 clear: function OF__clear() {
270 if (this._folder.elt && this._folder.elt.lastChild) {
271 if (!this._folder.elt.lastChild.hasAttribute("dragover"))
272 this._folder.elt.lastChild.hidePopup();
273 // remove menuactive style
274 this._folder.elt.removeAttribute("_moz-menuactive");
275 this._folder.elt = null;
276 }
277 if (this._folder.openTimer) {
278 this._folder.openTimer.cancel();
279 this._folder.openTimer = null;
280 }
281 if (this._folder.closeTimer) {
282 this._folder.closeTimer.cancel();
283 this._folder.closeTimer = null;
284 }
285 }
286 })]]></field>
288 <method name="_cleanupDragDetails">
289 <body><![CDATA[
290 // Called on dragend and drop.
291 PlacesControllerDragHelper.currentDropTarget = null;
292 this._rootView._draggedElt = null;
293 this.removeAttribute("dragover");
294 this.removeAttribute("dragstart");
295 this._indicatorBar.hidden = true;
296 ]]></body>
297 </method>
299 </implementation>
301 <handlers>
302 <handler event="DOMMenuItemActive"><![CDATA[
303 let elt = event.target;
304 if (elt.parentNode != this)
305 return;
307 #ifdef XP_MACOSX
308 // XXX: The following check is a temporary hack until bug 420033 is
309 // resolved.
310 let parentElt = elt.parent;
311 while (parentElt) {
312 if (parentElt.id == "bookmarksMenuPopup" ||
313 parentElt.id == "goPopup")
314 return;
316 parentElt = parentElt.parentNode;
317 }
318 #endif
320 if (window.XULBrowserWindow) {
321 let elt = event.target;
322 let placesNode = elt._placesNode;
324 var linkURI;
325 if (placesNode && PlacesUtils.nodeIsURI(placesNode))
326 linkURI = placesNode.uri;
327 else if (elt.hasAttribute("targetURI"))
328 linkURI = elt.getAttribute("targetURI");
330 if (linkURI)
331 window.XULBrowserWindow.setOverLink(linkURI, null);
332 }
333 ]]></handler>
335 <handler event="DOMMenuItemInactive"><![CDATA[
336 let elt = event.target;
337 if (elt.parentNode != this)
338 return;
340 if (window.XULBrowserWindow)
341 window.XULBrowserWindow.setOverLink("", null);
342 ]]></handler>
344 <handler event="dragstart"><![CDATA[
345 if (!event.target._placesNode)
346 return;
348 let draggedElt = event.target._placesNode;
350 // Force a copy action if parent node is a query or we are dragging a
351 // not-removable node.
352 if (!PlacesControllerDragHelper.canMoveNode(draggedElt))
353 event.dataTransfer.effectAllowed = "copyLink";
355 // Activate the view and cache the dragged element.
356 this._rootView._draggedElt = draggedElt;
357 this._rootView.controller.setDataTransfer(event);
358 this.setAttribute("dragstart", "true");
359 event.stopPropagation();
360 ]]></handler>
362 <handler event="drop"><![CDATA[
363 PlacesControllerDragHelper.currentDropTarget = event.target;
365 let dropPoint = this._getDropPoint(event);
366 if (dropPoint && dropPoint.ip) {
367 PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer);
368 event.preventDefault();
369 }
371 this._cleanupDragDetails();
372 event.stopPropagation();
373 ]]></handler>
375 <handler event="dragover"><![CDATA[
376 PlacesControllerDragHelper.currentDropTarget = event.target;
377 let dt = event.dataTransfer;
379 let dropPoint = this._getDropPoint(event);
380 if (!dropPoint || !dropPoint.ip ||
381 !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
382 this._indicatorBar.hidden = true;
383 event.stopPropagation();
384 return;
385 }
387 // Mark this popup as being dragged over.
388 this.setAttribute("dragover", "true");
390 if (dropPoint.folderElt) {
391 // We are dragging over a folder.
392 // _overFolder should take the care of opening it on a timer.
393 if (this._overFolder.elt &&
394 this._overFolder.elt != dropPoint.folderElt) {
395 // We are dragging over a new folder, let's clear old values
396 this._overFolder.clear();
397 }
398 if (!this._overFolder.elt) {
399 this._overFolder.elt = dropPoint.folderElt;
400 // Create the timer to open this folder.
401 this._overFolder.openTimer = this._overFolder
402 .setTimer(this._overFolder.hoverTime);
403 }
404 // Since we are dropping into a folder set the corresponding style.
405 dropPoint.folderElt.setAttribute("_moz-menuactive", true);
406 }
407 else {
408 // We are not dragging over a folder.
409 // Clear out old _overFolder information.
410 this._overFolder.clear();
411 }
413 // Autoscroll the popup strip if we drag over the scroll buttons.
414 let anonid = event.originalTarget.getAttribute('anonid');
415 let scrollDir = anonid == "scrollbutton-up" ? -1 :
416 anonid == "scrollbutton-down" ? 1 : 0;
417 if (scrollDir != 0) {
418 this._scrollBox.scrollByIndex(scrollDir, false);
419 }
421 // Check if we should hide the drop indicator for this target.
422 if (dropPoint.folderElt || this._hideDropIndicator(event)) {
423 this._indicatorBar.hidden = true;
424 event.preventDefault();
425 event.stopPropagation();
426 return;
427 }
429 // We should display the drop indicator relative to the arrowscrollbox.
430 let sbo = this._scrollBox.scrollBoxObject;
431 let newMarginTop = 0;
432 if (scrollDir == 0) {
433 let elt = this.firstChild;
434 while (elt && event.screenY > elt.boxObject.screenY +
435 elt.boxObject.height / 2)
436 elt = elt.nextSibling;
437 newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY :
438 sbo.height;
439 }
440 else if (scrollDir == 1)
441 newMarginTop = sbo.height;
443 // Set the new marginTop based on arrowscrollbox.
444 newMarginTop += sbo.y - this._scrollBox.boxObject.y;
445 this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
446 this._indicatorBar.hidden = false;
448 event.preventDefault();
449 event.stopPropagation();
450 ]]></handler>
452 <handler event="dragexit"><![CDATA[
453 PlacesControllerDragHelper.currentDropTarget = null;
454 this.removeAttribute("dragover");
456 // If we have not moved to a valid new target clear the drop indicator
457 // this happens when moving out of the popup.
458 let target = event.relatedTarget;
459 if (!target || !this.contains(target))
460 this._indicatorBar.hidden = true;
462 // Close any folder being hovered over
463 if (this._overFolder.elt) {
464 this._overFolder.closeTimer = this._overFolder
465 .setTimer(this._overFolder.hoverTime);
466 }
468 // The autoopened attribute is set when this folder was automatically
469 // opened after the user dragged over it. If this attribute is set,
470 // auto-close the folder on drag exit.
471 // We should also try to close this popup if the drag has started
472 // from here, the timer will check if we are dragging over a child.
473 if (this.hasAttribute("autoopened") ||
474 this.hasAttribute("dragstart")) {
475 this._overFolder.closeMenuTimer = this._overFolder
476 .setTimer(this._overFolder.hoverTime);
477 }
479 event.stopPropagation();
480 ]]></handler>
482 <handler event="dragend"><![CDATA[
483 this._cleanupDragDetails();
484 ]]></handler>
486 </handlers>
487 </binding>
489 <!-- Most of this is copied from the arrowpanel binding in popup.xml -->
490 <binding id="places-popup-arrow"
491 extends="chrome://browser/content/places/menu.xml#places-popup-base">
492 <content flip="both" side="top" position="bottomcenter topright">
493 <xul:vbox anonid="container" class="panel-arrowcontainer" flex="1"
494 xbl:inherits="side,panelopen">
495 <xul:box anonid="arrowbox" class="panel-arrowbox">
496 <xul:image anonid="arrow" class="panel-arrow" xbl:inherits="side"/>
497 </xul:box>
498 <xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
499 <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
500 <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
501 </xul:vbox>
502 <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
503 smoothscroll="false">
504 <children/>
505 </xul:arrowscrollbox>
506 </xul:box>
507 </xul:vbox>
508 </content>
510 <implementation>
511 <constructor><![CDATA[
512 this.style.pointerEvents = 'none';
513 ]]></constructor>
514 <method name="adjustArrowPosition">
515 <body><![CDATA[
516 var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
518 var anchor = this.anchorNode;
519 if (!anchor) {
520 arrow.hidden = true;
521 return;
522 }
524 var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
525 var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
527 var position = this.alignmentPosition;
528 var offset = this.alignmentOffset;
530 // if this panel has a "sliding" arrow, we may have previously set margins...
531 arrowbox.style.removeProperty("transform");
532 if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
533 container.orient = "horizontal";
534 arrowbox.orient = "vertical";
535 if (position.indexOf("_after") > 0) {
536 arrowbox.pack = "end";
537 } else {
538 arrowbox.pack = "start";
539 }
540 arrowbox.style.transform = "translate(0, " + -offset + "px)";
542 // The assigned side stays the same regardless of direction.
543 var isRTL = (window.getComputedStyle(this).direction == "rtl");
545 if (position.indexOf("start_") == 0) {
546 container.dir = "reverse";
547 this.setAttribute("side", isRTL ? "left" : "right");
548 }
549 else {
550 container.dir = "";
551 this.setAttribute("side", isRTL ? "right" : "left");
552 }
553 }
554 else if (position.indexOf("before_") == 0 || position.indexOf("after_") == 0) {
555 container.orient = "";
556 arrowbox.orient = "";
557 if (position.indexOf("_end") > 0) {
558 arrowbox.pack = "end";
559 } else {
560 arrowbox.pack = "start";
561 }
562 arrowbox.style.transform = "translate(" + -offset + "px, 0)";
564 if (position.indexOf("before_") == 0) {
565 container.dir = "reverse";
566 this.setAttribute("side", "bottom");
567 }
568 else {
569 container.dir = "";
570 this.setAttribute("side", "top");
571 }
572 }
574 arrow.hidden = false;
575 ]]></body>
576 </method>
577 </implementation>
579 <handlers>
580 <handler event="popupshowing" phase="target"><![CDATA[
581 this.adjustArrowPosition();
582 ]]></handler>
583 <handler event="popupshown" phase="target"><![CDATA[
584 this.setAttribute("panelopen", "true");
585 let disablePointerEvents;
586 if (!this.hasAttribute("disablepointereventsfortransition")) {
587 let container = document.getAnonymousElementByAttribute(this, "anonid", "container");
588 let cs = getComputedStyle(container);
589 let transitionProp = cs.transitionProperty;
590 let transitionTime = parseFloat(cs.transitionDuration);
591 disablePointerEvents = (transitionProp.contains("transform") ||
592 transitionProp == "all") &&
593 transitionTime > 0;
594 this.setAttribute("disablepointereventsfortransition", disablePointerEvents);
595 } else {
596 disablePointerEvents = this.getAttribute("disablepointereventsfortransition") == "true";
597 }
598 if (!disablePointerEvents) {
599 this.style.removeProperty("pointer-events");
600 }
601 ]]></handler>
602 <handler event="transitionend"><![CDATA[
603 if (event.originalTarget.getAttribute("anonid") == "container" &&
604 event.propertyName == "transform") {
605 this.style.removeProperty("pointer-events");
606 }
607 ]]></handler>
608 <handler event="popuphidden" phase="target"><![CDATA[
609 this.removeAttribute("panelopen");
610 if (this.getAttribute("disablepointereventsfortransition") == "true") {
611 this.style.pointerEvents = 'none';
612 }
613 ]]></handler>
614 </handlers>
615 </binding>
616 </bindings>