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="placesTreeBindings"
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-tree" extends="chrome://global/content/bindings/tree.xml#tree">
14 <implementation>
15 <constructor><![CDATA[
16 // Force an initial build.
17 if (this.place)
18 this.place = this.place;
19 ]]></constructor>
21 <destructor><![CDATA[
22 // Break the treeviewer->result->treeviewer cycle.
23 // Note: unsetting the result's viewer also unsets
24 // the viewer's reference to our treeBoxObject.
25 var result = this.result;
26 if (result) {
27 result.root.containerOpen = false;
28 }
30 // Unregister the controllber before unlinking the view, otherwise it
31 // may still try to update commands on a view with a null result.
32 if (this._controller) {
33 this._controller.terminate();
34 this.controllers.removeController(this._controller);
35 }
37 this.view = null;
38 ]]></destructor>
40 <property name="controller"
41 readonly="true"
42 onget="return this._controller"/>
44 <!-- overriding -->
45 <property name="view">
46 <getter><![CDATA[
47 try {
48 return this.treeBoxObject.view.wrappedJSObject || null;
49 }
50 catch(e) {
51 return null;
52 }
53 ]]></getter>
54 <setter><![CDATA[
55 return this.treeBoxObject.view = val;
56 ]]></setter>
57 </property>
59 <property name="associatedElement"
60 readonly="true"
61 onget="return this"/>
63 <method name="applyFilter">
64 <parameter name="filterString"/>
65 <parameter name="folderRestrict"/>
66 <parameter name="includeHidden"/>
67 <body><![CDATA[
68 // preserve grouping
69 var queryNode = PlacesUtils.asQuery(this.result.root);
70 var options = queryNode.queryOptions.clone();
72 // Make sure we're getting uri results.
73 // We do not yet support searching into grouped queries or into
74 // tag containers, so we must fall to the default case.
75 if (PlacesUtils.nodeIsHistoryContainer(queryNode) ||
76 options.resultType == options.RESULTS_AS_TAG_QUERY ||
77 options.resultType == options.RESULTS_AS_TAG_CONTENTS)
78 options.resultType = options.RESULTS_AS_URI;
80 var query = PlacesUtils.history.getNewQuery();
81 query.searchTerms = filterString;
83 if (folderRestrict) {
84 query.setFolders(folderRestrict, folderRestrict.length);
85 options.queryType = options.QUERY_TYPE_BOOKMARKS;
86 }
88 options.includeHidden = !!includeHidden;
90 this.load([query], options);
91 ]]></body>
92 </method>
94 <method name="load">
95 <parameter name="queries"/>
96 <parameter name="options"/>
97 <body><![CDATA[
98 let result = PlacesUtils.history
99 .executeQueries(queries, queries.length,
100 options);
101 let callback;
102 if (this.flatList) {
103 let onOpenFlatContainer = this.onOpenFlatContainer;
104 if (onOpenFlatContainer)
105 callback = new Function("aContainer", onOpenFlatContainer);
106 }
108 if (!this._controller) {
109 this._controller = new PlacesController(this);
110 this.controllers.appendController(this._controller);
111 }
113 let treeView = new PlacesTreeView(this.flatList, callback, this._controller);
115 // Observer removal is done within the view itself. When the tree
116 // goes away, treeboxobject calls view.setTree(null), which then
117 // calls removeObserver.
118 result.addObserver(treeView, false);
119 this.view = treeView;
121 if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) {
122 treeView.selection.select(0);
123 }
125 this._cachedInsertionPoint = undefined;
126 ]]></body>
127 </method>
129 <property name="flatList">
130 <getter><![CDATA[
131 return this.getAttribute("flatList") == "true";
132 ]]></getter>
133 <setter><![CDATA[
134 if (this.flatList != val) {
135 this.setAttribute("flatList", val);
136 // reload with the last place set
137 if (this.place)
138 this.place = this.place;
139 }
140 return val;
141 ]]></setter>
142 </property>
144 <property name="onOpenFlatContainer">
145 <getter><![CDATA[
146 return this.getAttribute("onopenflatcontainer");
147 ]]></getter>
148 <setter><![CDATA[
149 if (this.onOpenFlatContainer != val) {
150 this.setAttribute("onopenflatcontainer", val);
151 // reload with the last place set
152 if (this.place)
153 this.place = this.place;
154 }
155 return val;
156 ]]></setter>
157 </property>
159 <!--
160 Causes a particular node represented by the specified placeURI to be
161 selected in the tree. All containers above the node in the hierarchy
162 will be opened, so that the node is visible.
163 -->
164 <method name="selectPlaceURI">
165 <parameter name="placeURI"/>
166 <body><![CDATA[
167 // Do nothing if a node matching the given uri is already selected
168 if (this.hasSelection && this.selectedNode.uri == placeURI)
169 return;
171 function findNode(container, placeURI, nodesURIChecked) {
172 var containerURI = container.uri;
173 if (containerURI == placeURI)
174 return container;
175 if (nodesURIChecked.indexOf(containerURI) != -1)
176 return null;
178 // never check the contents of the same query
179 nodesURIChecked.push(containerURI);
181 var wasOpen = container.containerOpen;
182 if (!wasOpen)
183 container.containerOpen = true;
184 for (var i = 0; i < container.childCount; ++i) {
185 var child = container.getChild(i);
186 var childURI = child.uri;
187 if (childURI == placeURI)
188 return child;
189 else if (PlacesUtils.nodeIsContainer(child)) {
190 var nested = findNode(PlacesUtils.asContainer(child), placeURI, nodesURIChecked);
191 if (nested)
192 return nested;
193 }
194 }
196 if (!wasOpen)
197 container.containerOpen = false;
199 return null;
200 }
202 var container = this.result.root;
203 NS_ASSERT(container, "No result, cannot select place URI!");
204 if (!container)
205 return;
207 var child = findNode(container, placeURI, []);
208 if (child)
209 this.selectNode(child);
210 else {
211 // If the specified child could not be located, clear the selection
212 var selection = this.view.selection;
213 selection.clearSelection();
214 }
215 ]]></body>
216 </method>
218 <!--
219 Causes a particular node to be selected in the tree, resulting in all
220 containers above the node in the hierarchy to be opened, so that the
221 node is visible.
222 -->
223 <method name="selectNode">
224 <parameter name="node"/>
225 <body><![CDATA[
226 var view = this.view;
228 var parent = node.parent;
229 if (parent && !parent.containerOpen) {
230 // Build a list of all of the nodes that are the parent of this one
231 // in the result.
232 var parents = [];
233 var root = this.result.root;
234 while (parent && parent != root) {
235 parents.push(parent);
236 parent = parent.parent;
237 }
239 // Walk the list backwards (opening from the root of the hierarchy)
240 // opening each folder as we go.
241 for (var i = parents.length - 1; i >= 0; --i) {
242 var index = view.treeIndexForNode(parents[i]);
243 if (index != Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE &&
244 view.isContainer(index) && !view.isContainerOpen(index))
245 view.toggleOpenState(index);
246 }
247 // Select the specified node...
248 }
250 var index = view.treeIndexForNode(node);
251 if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
252 return;
254 view.selection.select(index);
255 // ... and ensure it's visible, not scrolled off somewhere.
256 this.treeBoxObject.ensureRowIsVisible(index);
257 ]]></body>
258 </method>
260 <!-- nsIPlacesView -->
261 <property name="result">
262 <getter><![CDATA[
263 try {
264 return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result;
265 }
266 catch (e) {
267 return null;
268 }
269 ]]></getter>
270 </property>
272 <!-- nsIPlacesView -->
273 <property name="place">
274 <getter><![CDATA[
275 return this.getAttribute("place");
276 ]]></getter>
277 <setter><![CDATA[
278 this.setAttribute("place", val);
280 var queriesRef = { };
281 var queryCountRef = { };
282 var optionsRef = { };
283 PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
284 if (queryCountRef.value == 0)
285 queriesRef.value = [PlacesUtils.history.getNewQuery()];
286 if (!optionsRef.value)
287 optionsRef.value = PlacesUtils.history.getNewQueryOptions();
289 this.load(queriesRef.value, optionsRef.value);
291 return val;
292 ]]></setter>
293 </property>
295 <!-- nsIPlacesView -->
296 <property name="hasSelection">
297 <getter><![CDATA[
298 return this.view && this.view.selection.count >= 1;
299 ]]></getter>
300 </property>
302 <!-- nsIPlacesView -->
303 <property name="selectedNodes">
304 <getter><![CDATA[
305 let nodes = [];
306 if (!this.hasSelection)
307 return nodes;
309 let selection = this.view.selection;
310 let rc = selection.getRangeCount();
311 let resultview = this.view;
312 for (let i = 0; i < rc; ++i) {
313 let min = { }, max = { };
314 selection.getRangeAt(i, min, max);
315 for (let j = min.value; j <= max.value; ++j) {
316 nodes.push(resultview.nodeForTreeIndex(j));
317 }
318 }
319 return nodes;
320 ]]></getter>
321 </property>
323 <method name="toggleCutNode">
324 <parameter name="aNode"/>
325 <parameter name="aValue"/>
326 <body><![CDATA[
327 this.view.toggleCutNode(aNode, aValue);
328 ]]></body>
329 </method>
331 <!-- nsIPlacesView -->
332 <property name="removableSelectionRanges">
333 <getter><![CDATA[
334 // This property exists in addition to selectedNodes because it
335 // encodes selection ranges (which only occur in list views) into
336 // the return value. For each removed range, the index at which items
337 // will be re-inserted upon the remove transaction being performed is
338 // the first index of the range, so that the view updates correctly.
339 //
340 // For example, if we remove rows 2,3,4 and 7,8 from a list, when we
341 // undo that operation, if we insert what was at row 3 at row 3 again,
342 // it will show up _after_ the item that was at row 5. So we need to
343 // insert all items at row 2, and the tree view will update correctly.
344 //
345 // Also, this function collapses the selection to remove redundant
346 // data, e.g. when deleting this selection:
347 //
348 // http://www.foo.com/
349 // (-) Some Folder
350 // http://www.bar.com/
351 //
352 // ... returning http://www.bar.com/ as part of the selection is
353 // redundant because it is implied by removing "Some Folder". We
354 // filter out all such redundancies since some partial amount of
355 // the folder's children may be selected.
356 //
357 let nodes = [];
358 if (!this.hasSelection)
359 return nodes;
361 var selection = this.view.selection;
362 var rc = selection.getRangeCount();
363 var resultview = this.view;
364 // This list is kept independently of the range selected (i.e. OUTSIDE
365 // the for loop) since the row index of a container is unique for the
366 // entire view, and we could have some really wacky selection and we
367 // don't want to blow up.
368 var containers = { };
369 for (var i = 0; i < rc; ++i) {
370 var range = [];
371 var min = { }, max = { };
372 selection.getRangeAt(i, min, max);
374 for (var j = min.value; j <= max.value; ++j) {
375 if (this.view.isContainer(j))
376 containers[j] = true;
377 if (!(this.view.getParentIndex(j) in containers))
378 range.push(resultview.nodeForTreeIndex(j));
379 }
380 nodes.push(range);
381 }
382 return nodes;
383 ]]></getter>
384 </property>
386 <!-- nsIPlacesView -->
387 <property name="draggableSelection"
388 onget="return this.selectedNodes"/>
390 <!-- nsIPlacesView -->
391 <property name="selectedNode">
392 <getter><![CDATA[
393 var view = this.view;
394 if (!view || view.selection.count != 1)
395 return null;
397 var selection = view.selection;
398 var min = { }, max = { };
399 selection.getRangeAt(0, min, max);
401 return this.view.nodeForTreeIndex(min.value);
402 ]]></getter>
403 </property>
405 <!-- nsIPlacesView -->
406 <property name="insertionPoint">
407 <getter><![CDATA[
408 // invalidated on selection and focus changes
409 if (this._cachedInsertionPoint !== undefined)
410 return this._cachedInsertionPoint;
412 // there is no insertion point for history queries
413 // so bail out now and save a lot of work when updating commands
414 var resultNode = this.result.root;
415 if (PlacesUtils.nodeIsQuery(resultNode) &&
416 PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
417 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
418 return this._cachedInsertionPoint = null;
420 var orientation = Ci.nsITreeView.DROP_BEFORE;
421 // If there is no selection, insert at the end of the container.
422 if (!this.hasSelection) {
423 var index = this.view.rowCount - 1;
424 this._cachedInsertionPoint =
425 this._getInsertionPoint(index, orientation);
426 return this._cachedInsertionPoint;
427 }
429 // This is a two-part process. The first part is determining the drop
430 // orientation.
431 // * The default orientation is to drop _before_ the selected item.
432 // * If the selected item is a container, the default orientation
433 // is to drop _into_ that container.
434 //
435 // Warning: It may be tempting to use tree indexes in this code, but
436 // you must not, since the tree is nested and as your tree
437 // index may change when folders before you are opened and
438 // closed. You must convert your tree index to a node, and
439 // then use getChildIndex to find your absolute index in
440 // the parent container instead.
441 //
442 var resultView = this.view;
443 var selection = resultView.selection;
444 var rc = selection.getRangeCount();
445 var min = { }, max = { };
446 selection.getRangeAt(rc - 1, min, max);
448 // If the sole selection is a container, and we are not in
449 // a flatlist, insert into it.
450 // Note that this only applies to _single_ selections,
451 // if the last element within a multi-selection is a
452 // container, insert _adjacent_ to the selection.
453 //
454 // If the sole selection is the bookmarks toolbar folder, we insert
455 // into it even if it is not opened
456 if (selection.count == 1 && resultView.isContainer(max.value) &&
457 !this.flatList)
458 orientation = Ci.nsITreeView.DROP_ON;
460 this._cachedInsertionPoint =
461 this._getInsertionPoint(max.value, orientation);
462 return this._cachedInsertionPoint;
463 ]]></getter>
464 </property>
466 <method name="_getInsertionPoint">
467 <parameter name="index"/>
468 <parameter name="orientation"/>
469 <body><![CDATA[
470 var result = this.result;
471 var resultview = this.view;
472 var container = result.root;
473 var dropNearItemId = -1;
474 NS_ASSERT(container, "null container");
475 // When there's no selection, assume the container is the container
476 // the view is populated from (i.e. the result's itemId).
477 if (index != -1) {
478 var lastSelected = resultview.nodeForTreeIndex(index);
479 if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
480 // If the last selected item is an open container, append _into_
481 // it, rather than insert adjacent to it.
482 container = lastSelected;
483 index = -1;
484 }
485 else if (lastSelected.containerOpen &&
486 orientation == Ci.nsITreeView.DROP_AFTER &&
487 lastSelected.hasChildren) {
488 // If the last selected item is an open container and the user is
489 // trying to drag into it as a first item, really insert into it.
490 container = lastSelected;
491 orientation = Ci.nsITreeView.DROP_ON;
492 index = 0;
493 }
494 else {
495 // Use the last-selected node's container.
496 container = lastSelected.parent;
498 // See comment in the treeView.js's copy of this method
499 if (!container || !container.containerOpen)
500 return null;
502 // Avoid the potentially expensive call to getChildIndex
503 // if we know this container doesn't allow insertion
504 if (PlacesControllerDragHelper.disallowInsertion(container))
505 return null;
507 var queryOptions = PlacesUtils.asQuery(result.root).queryOptions;
508 if (queryOptions.sortingMode !=
509 Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
510 // If we are within a sorted view, insert at the end
511 index = -1;
512 }
513 else if (queryOptions.excludeItems ||
514 queryOptions.excludeQueries ||
515 queryOptions.excludeReadOnlyFolders) {
516 // Some item may be invisible, insert near last selected one.
517 // We don't replace index here to avoid requests to the db,
518 // instead it will be calculated later by the controller.
519 index = -1;
520 dropNearItemId = lastSelected.itemId;
521 }
522 else {
523 var lsi = container.getChildIndex(lastSelected);
524 index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
525 }
526 }
527 }
529 if (PlacesControllerDragHelper.disallowInsertion(container))
530 return null;
532 return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
533 index, orientation,
534 PlacesUtils.nodeIsTagQuery(container),
535 dropNearItemId);
536 ]]></body>
537 </method>
539 <!-- nsIPlacesView -->
540 <method name="selectAll">
541 <body><![CDATA[
542 this.view.selection.selectAll();
543 ]]></body>
544 </method>
546 <!-- This method will select the first node in the tree that matches
547 each given item id. It will open any folder nodes that it needs
548 to in order to show the selected items.
549 -->
550 <method name="selectItems">
551 <parameter name="aIDs"/>
552 <parameter name="aOpenContainers"/>
553 <body><![CDATA[
554 // Never open containers in flat lists.
555 if (this.flatList)
556 aOpenContainers = false;
557 // By default, we do search and select within containers which were
558 // closed (note that containers in which nodes were not found are
559 // closed).
560 if (aOpenContainers === undefined)
561 aOpenContainers = true;
563 var ids = aIDs; // don't manipulate the caller's array
565 // Array of nodes found by findNodes which are to be selected
566 var nodes = [];
568 // Array of nodes found by findNodes which should be opened
569 var nodesToOpen = [];
571 // A set of URIs of container-nodes that were previously searched,
572 // and thus shouldn't be searched again. This is empty at the initial
573 // start of the recursion and gets filled in as the recursion
574 // progresses.
575 var nodesURIChecked = [];
577 /**
578 * Recursively search through a node's children for items
579 * with the given IDs. When a matching item is found, remove its ID
580 * from the IDs array, and add the found node to the nodes dictionary.
581 *
582 * NOTE: This method will leave open any node that had matching items
583 * in its subtree.
584 */
585 function findNodes(node) {
586 var foundOne = false;
587 // See if node matches an ID we wanted; add to results.
588 // For simple folder queries, check both itemId and the concrete
589 // item id.
590 var index = ids.indexOf(node.itemId);
591 if (index == -1 &&
592 node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT)
593 index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId);
595 if (index != -1) {
596 nodes.push(node);
597 foundOne = true;
598 ids.splice(index, 1);
599 }
601 if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
602 nodesURIChecked.indexOf(node.uri) != -1)
603 return foundOne;
605 // Don't try to open a query or a shurtcut, since it may return
606 // any duplicate data and be infinitely nested. Though, if it has
607 // been explicitly opened by the caller, search into it.
608 let shouldOpen = aOpenContainers &&
609 node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER;
610 PlacesUtils.asContainer(node);
611 if (!node.containerOpen && !shouldOpen)
612 return foundOne;
614 nodesURIChecked.push(node.uri);
616 // Remember the beginning state so that we can re-close
617 // this node if we don't find any additional results here.
618 var previousOpenness = node.containerOpen;
619 node.containerOpen = true;
620 for (var child = 0; child < node.childCount && ids.length > 0;
621 child++) {
622 var childNode = node.getChild(child);
623 var found = findNodes(childNode);
624 if (!foundOne)
625 foundOne = found;
626 }
628 // If we didn't find any additional matches in this node's
629 // subtree, revert the node to its previous openness.
630 if (foundOne)
631 nodesToOpen.unshift(node);
632 node.containerOpen = previousOpenness;
633 return foundOne;
634 }
636 // Disable notifications while looking for nodes.
637 let result = this.result;
638 let didSuppressNotifications = result.suppressNotifications;
639 if (!didSuppressNotifications)
640 result.suppressNotifications = true
641 try {
642 findNodes(this.result.root);
643 }
644 finally {
645 if (!didSuppressNotifications)
646 result.suppressNotifications = false;
647 }
649 // For all the nodes we've found, highlight the corresponding
650 // index in the tree.
651 var resultview = this.view;
652 var selection = this.view.selection;
653 selection.selectEventsSuppressed = true;
654 selection.clearSelection();
655 // Open nodes containing found items
656 for (var i = 0; i < nodesToOpen.length; i++) {
657 nodesToOpen[i].containerOpen = true;
658 }
659 for (var i = 0; i < nodes.length; i++) {
660 var index = resultview.treeIndexForNode(nodes[i]);
661 if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
662 continue;
663 selection.rangedSelect(index, index, true);
664 }
665 selection.selectEventsSuppressed = false;
666 ]]></body>
667 </method>
669 <field name="_contextMenuShown">false</field>
671 <method name="buildContextMenu">
672 <parameter name="aPopup"/>
673 <body><![CDATA[
674 this._contextMenuShown = true;
675 return this.controller.buildContextMenu(aPopup);
676 ]]></body>
677 </method>
679 <method name="destroyContextMenu">
680 <parameter name="aPopup"/>
681 this._contextMenuShown = false;
682 <body/>
683 </method>
685 <property name="ownerWindow"
686 readonly="true"
687 onget="return window;"/>
689 <field name="_active">true</field>
690 <property name="active"
691 onget="return this._active"
692 onset="return this._active = val"/>
694 </implementation>
695 <handlers>
696 <handler event="focus"><![CDATA[
697 this._cachedInsertionPoint = undefined;
699 // See select handler. We need the sidebar's places commandset to be
700 // updated as well
701 document.commandDispatcher.updateCommands("focus");
702 ]]></handler>
703 <handler event="select"><![CDATA[
704 this._cachedInsertionPoint = undefined;
706 // This additional complexity is here for the sidebars
707 var win = window;
708 while (true) {
709 win.document.commandDispatcher.updateCommands("focus");
710 if (win == window.top)
711 break;
713 win = win.parent;
714 }
715 ]]></handler>
717 <handler event="dragstart"><![CDATA[
718 if (event.target.localName != "treechildren")
719 return;
721 let nodes = this.selectedNodes;
722 for (let i = 0; i < nodes.length; i++) {
723 let node = nodes[i];
725 // Disallow dragging the root node of a tree.
726 if (!node.parent) {
727 event.preventDefault();
728 event.stopPropagation();
729 return;
730 }
732 // If this node is child of a readonly container (e.g. a livemark)
733 // or cannot be moved, we must force a copy.
734 if (!PlacesControllerDragHelper.canMoveNode(node)) {
735 event.dataTransfer.effectAllowed = "copyLink";
736 break;
737 }
738 }
740 this._controller.setDataTransfer(event);
741 event.stopPropagation();
742 ]]></handler>
744 <handler event="dragover"><![CDATA[
745 if (event.target.localName != "treechildren")
746 return;
748 let row = { }, col = { }, child = { };
749 this.treeBoxObject.getCellAt(event.clientX, event.clientY,
750 row, col, child);
751 let node = row.value != -1 ?
752 this.view.nodeForTreeIndex(row.value) :
753 this.result.root;
754 // cache the dropTarget for the view
755 PlacesControllerDragHelper.currentDropTarget = node;
757 // We have to calculate the orientation since view.canDrop will use
758 // it and we want to be consistent with the dropfeedback.
759 let tbo = this.treeBoxObject;
760 let rowHeight = tbo.rowHeight;
761 let eventY = event.clientY - tbo.treeBody.boxObject.y -
762 rowHeight * (row.value - tbo.getFirstVisibleRow());
764 let orientation = Ci.nsITreeView.DROP_BEFORE;
766 if (row.value == -1) {
767 // If the row is not valid we try to insert inside the resultNode.
768 orientation = Ci.nsITreeView.DROP_ON;
769 }
770 else if (PlacesUtils.nodeIsContainer(node) &&
771 eventY > rowHeight * 0.75) {
772 // If we are below the 75% of a container the treeview we try
773 // to drop after the node.
774 orientation = Ci.nsITreeView.DROP_AFTER;
775 }
776 else if (PlacesUtils.nodeIsContainer(node) &&
777 eventY > rowHeight * 0.25) {
778 // If we are below the 25% of a container the treeview we try
779 // to drop inside the node.
780 orientation = Ci.nsITreeView.DROP_ON;
781 }
783 if (!this.view.canDrop(row.value, orientation, event.dataTransfer))
784 return;
786 event.preventDefault();
787 event.stopPropagation();
788 ]]></handler>
790 <handler event="dragend"><![CDATA[
791 PlacesControllerDragHelper.currentDropTarget = null;
792 ]]></handler>
794 </handlers>
795 </binding>
797 </bindings>