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.
michael@0 | 1 | <?xml version="1.0"?> |
michael@0 | 2 | # -*- Mode: HTML -*- |
michael@0 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | |
michael@0 | 7 | <!DOCTYPE bindings [ |
michael@0 | 8 | <!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd" > |
michael@0 | 9 | %searchBarDTD; |
michael@0 | 10 | <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> |
michael@0 | 11 | %browserDTD; |
michael@0 | 12 | ]> |
michael@0 | 13 | |
michael@0 | 14 | <bindings id="SearchBindings" |
michael@0 | 15 | xmlns="http://www.mozilla.org/xbl" |
michael@0 | 16 | xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
michael@0 | 17 | xmlns:xbl="http://www.mozilla.org/xbl"> |
michael@0 | 18 | |
michael@0 | 19 | <binding id="searchbar"> |
michael@0 | 20 | <resources> |
michael@0 | 21 | <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/> |
michael@0 | 22 | <stylesheet src="chrome://browser/skin/searchbar.css"/> |
michael@0 | 23 | </resources> |
michael@0 | 24 | <content> |
michael@0 | 25 | <xul:stringbundle src="chrome://browser/locale/search.properties" |
michael@0 | 26 | anonid="searchbar-stringbundle"/> |
michael@0 | 27 | <!-- |
michael@0 | 28 | There is a dependency between "maxrows" attribute and |
michael@0 | 29 | "SuggestAutoComplete._historyLimit" (nsSearchSuggestions.js). Changing |
michael@0 | 30 | one of them requires changing the other one. |
michael@0 | 31 | --> |
michael@0 | 32 | <xul:textbox class="searchbar-textbox" |
michael@0 | 33 | anonid="searchbar-textbox" |
michael@0 | 34 | type="autocomplete" |
michael@0 | 35 | flex="1" |
michael@0 | 36 | autocompletepopup="PopupAutoComplete" |
michael@0 | 37 | autocompletesearch="search-autocomplete" |
michael@0 | 38 | autocompletesearchparam="searchbar-history" |
michael@0 | 39 | timeout="250" |
michael@0 | 40 | maxrows="10" |
michael@0 | 41 | completeselectedindex="true" |
michael@0 | 42 | showcommentcolumn="true" |
michael@0 | 43 | tabscrolling="true" |
michael@0 | 44 | xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines"> |
michael@0 | 45 | <!-- |
michael@0 | 46 | Empty <box> to properly position the icon within the autocomplete |
michael@0 | 47 | binding's anonymous children (the autocomplete binding positions <box> |
michael@0 | 48 | children differently) |
michael@0 | 49 | --> |
michael@0 | 50 | <xul:box> |
michael@0 | 51 | <xul:button class="searchbar-engine-button" |
michael@0 | 52 | type="menu" |
michael@0 | 53 | anonid="searchbar-engine-button"> |
michael@0 | 54 | <xul:image class="searchbar-engine-image" xbl:inherits="src"/> |
michael@0 | 55 | <xul:image class="searchbar-dropmarker-image"/> |
michael@0 | 56 | <xul:menupopup class="searchbar-popup" |
michael@0 | 57 | anonid="searchbar-popup"> |
michael@0 | 58 | <xul:menuseparator/> |
michael@0 | 59 | <xul:menuitem class="open-engine-manager" |
michael@0 | 60 | anonid="open-engine-manager" |
michael@0 | 61 | label="&cmd_engineManager.label;" |
michael@0 | 62 | oncommand="openManager(event);"/> |
michael@0 | 63 | </xul:menupopup> |
michael@0 | 64 | </xul:button> |
michael@0 | 65 | </xul:box> |
michael@0 | 66 | <xul:hbox class="search-go-container"> |
michael@0 | 67 | <xul:image class="search-go-button" |
michael@0 | 68 | anonid="search-go-button" |
michael@0 | 69 | onclick="handleSearchCommand(event);" |
michael@0 | 70 | tooltiptext="&searchEndCap.label;"/> |
michael@0 | 71 | </xul:hbox> |
michael@0 | 72 | </xul:textbox> |
michael@0 | 73 | </content> |
michael@0 | 74 | |
michael@0 | 75 | <implementation implements="nsIObserver"> |
michael@0 | 76 | <constructor><![CDATA[ |
michael@0 | 77 | if (this.parentNode.parentNode.localName == "toolbarpaletteitem") |
michael@0 | 78 | return; |
michael@0 | 79 | // Make sure we rebuild the popup in onpopupshowing |
michael@0 | 80 | this._needToBuildPopup = true; |
michael@0 | 81 | |
michael@0 | 82 | var os = |
michael@0 | 83 | Components.classes["@mozilla.org/observer-service;1"] |
michael@0 | 84 | .getService(Components.interfaces.nsIObserverService); |
michael@0 | 85 | os.addObserver(this, "browser-search-engine-modified", false); |
michael@0 | 86 | |
michael@0 | 87 | this._initialized = true; |
michael@0 | 88 | |
michael@0 | 89 | this.searchService.init((function search_init_cb(aStatus) { |
michael@0 | 90 | // Bail out if the binding's been destroyed |
michael@0 | 91 | if (!this._initialized) |
michael@0 | 92 | return; |
michael@0 | 93 | |
michael@0 | 94 | if (Components.isSuccessCode(aStatus)) { |
michael@0 | 95 | // Refresh the display (updating icon, etc) |
michael@0 | 96 | this.updateDisplay(); |
michael@0 | 97 | } else { |
michael@0 | 98 | Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus); |
michael@0 | 99 | } |
michael@0 | 100 | }).bind(this)); |
michael@0 | 101 | ]]></constructor> |
michael@0 | 102 | |
michael@0 | 103 | <destructor><![CDATA[ |
michael@0 | 104 | if (this._initialized) { |
michael@0 | 105 | this._initialized = false; |
michael@0 | 106 | |
michael@0 | 107 | var os = Components.classes["@mozilla.org/observer-service;1"] |
michael@0 | 108 | .getService(Components.interfaces.nsIObserverService); |
michael@0 | 109 | os.removeObserver(this, "browser-search-engine-modified"); |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | // Make sure to break the cycle from _textbox to us. Otherwise we leak |
michael@0 | 113 | // the world. But make sure it's actually pointing to us. |
michael@0 | 114 | if (this._textbox.mController.input == this) |
michael@0 | 115 | this._textbox.mController.input = null; |
michael@0 | 116 | ]]></destructor> |
michael@0 | 117 | |
michael@0 | 118 | <field name="_stringBundle">document.getAnonymousElementByAttribute(this, |
michael@0 | 119 | "anonid", "searchbar-stringbundle");</field> |
michael@0 | 120 | <field name="_textbox">document.getAnonymousElementByAttribute(this, |
michael@0 | 121 | "anonid", "searchbar-textbox");</field> |
michael@0 | 122 | <field name="_popup">document.getAnonymousElementByAttribute(this, |
michael@0 | 123 | "anonid", "searchbar-popup");</field> |
michael@0 | 124 | <field name="_ss">null</field> |
michael@0 | 125 | <field name="_engines">null</field> |
michael@0 | 126 | <field name="FormHistory" readonly="true"> |
michael@0 | 127 | (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory; |
michael@0 | 128 | </field> |
michael@0 | 129 | |
michael@0 | 130 | <property name="engines" readonly="true"> |
michael@0 | 131 | <getter><![CDATA[ |
michael@0 | 132 | if (!this._engines) |
michael@0 | 133 | this._engines = this.searchService.getVisibleEngines(); |
michael@0 | 134 | return this._engines; |
michael@0 | 135 | ]]></getter> |
michael@0 | 136 | </property> |
michael@0 | 137 | |
michael@0 | 138 | <field name="searchButton">document.getAnonymousElementByAttribute(this, |
michael@0 | 139 | "anonid", "searchbar-engine-button");</field> |
michael@0 | 140 | |
michael@0 | 141 | <property name="currentEngine"> |
michael@0 | 142 | <setter><![CDATA[ |
michael@0 | 143 | let ss = this.searchService; |
michael@0 | 144 | ss.defaultEngine = ss.currentEngine = val; |
michael@0 | 145 | return val; |
michael@0 | 146 | ]]></setter> |
michael@0 | 147 | <getter><![CDATA[ |
michael@0 | 148 | var currentEngine = this.searchService.currentEngine; |
michael@0 | 149 | // Return a dummy engine if there is no currentEngine |
michael@0 | 150 | return currentEngine || {name: "", uri: null}; |
michael@0 | 151 | ]]></getter> |
michael@0 | 152 | </property> |
michael@0 | 153 | |
michael@0 | 154 | <!-- textbox is used by sanitize.js to clear the undo history when |
michael@0 | 155 | clearing form information. --> |
michael@0 | 156 | <property name="textbox" readonly="true" |
michael@0 | 157 | onget="return this._textbox;"/> |
michael@0 | 158 | |
michael@0 | 159 | <property name="searchService" readonly="true"> |
michael@0 | 160 | <getter><![CDATA[ |
michael@0 | 161 | if (!this._ss) { |
michael@0 | 162 | const nsIBSS = Components.interfaces.nsIBrowserSearchService; |
michael@0 | 163 | this._ss = |
michael@0 | 164 | Components.classes["@mozilla.org/browser/search-service;1"] |
michael@0 | 165 | .getService(nsIBSS); |
michael@0 | 166 | } |
michael@0 | 167 | return this._ss; |
michael@0 | 168 | ]]></getter> |
michael@0 | 169 | </property> |
michael@0 | 170 | |
michael@0 | 171 | <property name="value" onget="return this._textbox.value;" |
michael@0 | 172 | onset="return this._textbox.value = val;"/> |
michael@0 | 173 | |
michael@0 | 174 | <method name="focus"> |
michael@0 | 175 | <body><![CDATA[ |
michael@0 | 176 | this._textbox.focus(); |
michael@0 | 177 | ]]></body> |
michael@0 | 178 | </method> |
michael@0 | 179 | |
michael@0 | 180 | <method name="select"> |
michael@0 | 181 | <body><![CDATA[ |
michael@0 | 182 | this._textbox.select(); |
michael@0 | 183 | ]]></body> |
michael@0 | 184 | </method> |
michael@0 | 185 | |
michael@0 | 186 | <method name="observe"> |
michael@0 | 187 | <parameter name="aEngine"/> |
michael@0 | 188 | <parameter name="aTopic"/> |
michael@0 | 189 | <parameter name="aVerb"/> |
michael@0 | 190 | <body><![CDATA[ |
michael@0 | 191 | if (aTopic == "browser-search-engine-modified") { |
michael@0 | 192 | switch (aVerb) { |
michael@0 | 193 | case "engine-removed": |
michael@0 | 194 | this.offerNewEngine(aEngine); |
michael@0 | 195 | break; |
michael@0 | 196 | case "engine-added": |
michael@0 | 197 | this.hideNewEngine(aEngine); |
michael@0 | 198 | break; |
michael@0 | 199 | case "engine-current": |
michael@0 | 200 | // The current engine was changed. Rebuilding the menu appears to |
michael@0 | 201 | // confuse its idea of whether it should be open when it's just |
michael@0 | 202 | // been clicked, so we force it to close now. |
michael@0 | 203 | this._popup.hidePopup(); |
michael@0 | 204 | break; |
michael@0 | 205 | case "engine-changed": |
michael@0 | 206 | // An engine was removed (or hidden) or added, or an icon was |
michael@0 | 207 | // changed. Do nothing special. |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | // Make sure the engine list is refetched next time it's needed |
michael@0 | 211 | this._engines = null; |
michael@0 | 212 | |
michael@0 | 213 | // Rebuild the popup and update the display after any modification. |
michael@0 | 214 | this.rebuildPopup(); |
michael@0 | 215 | this.updateDisplay(); |
michael@0 | 216 | } |
michael@0 | 217 | ]]></body> |
michael@0 | 218 | </method> |
michael@0 | 219 | |
michael@0 | 220 | <!-- There are two seaprate lists of search engines, whose uses intersect |
michael@0 | 221 | in this file. The search service (nsIBrowserSearchService and |
michael@0 | 222 | nsSearchService.js) maintains a list of Engine objects which is used to |
michael@0 | 223 | populate the searchbox list of available engines and to perform queries. |
michael@0 | 224 | That list is accessed here via this.SearchService, and it's that sort of |
michael@0 | 225 | Engine that is passed to this binding's observer as aEngine. |
michael@0 | 226 | |
michael@0 | 227 | In addition, browser.js fills two lists of autodetected search engines |
michael@0 | 228 | (browser.engines and browser.hiddenEngines) as properties of |
michael@0 | 229 | mCurrentBrowser. Those lists contain unnamed JS objects of the form |
michael@0 | 230 | { uri:, title:, icon: }, and that's what the searchbar uses to determine |
michael@0 | 231 | whether to show any "Add <EngineName>" menu items in the drop-down. |
michael@0 | 232 | |
michael@0 | 233 | The two types of engines are currently related by their identifying |
michael@0 | 234 | titles (the Engine object's 'name'), although that may change; see bug |
michael@0 | 235 | 335102. --> |
michael@0 | 236 | |
michael@0 | 237 | <!-- If the engine that was just removed from the searchbox list was |
michael@0 | 238 | autodetected on this page, move it to each browser's active list so it |
michael@0 | 239 | will be offered to be added again. --> |
michael@0 | 240 | <method name="offerNewEngine"> |
michael@0 | 241 | <parameter name="aEngine"/> |
michael@0 | 242 | <body><![CDATA[ |
michael@0 | 243 | var allbrowsers = getBrowser().mPanelContainer.childNodes; |
michael@0 | 244 | for (var tab = 0; tab < allbrowsers.length; tab++) { |
michael@0 | 245 | var browser = getBrowser().getBrowserAtIndex(tab); |
michael@0 | 246 | if (browser.hiddenEngines) { |
michael@0 | 247 | // XXX This will need to be changed when engines are identified by |
michael@0 | 248 | // URL rather than title; see bug 335102. |
michael@0 | 249 | var removeTitle = aEngine.wrappedJSObject.name; |
michael@0 | 250 | for (var i = 0; i < browser.hiddenEngines.length; i++) { |
michael@0 | 251 | if (browser.hiddenEngines[i].title == removeTitle) { |
michael@0 | 252 | if (!browser.engines) |
michael@0 | 253 | browser.engines = []; |
michael@0 | 254 | browser.engines.push(browser.hiddenEngines[i]); |
michael@0 | 255 | browser.hiddenEngines.splice(i, 1); |
michael@0 | 256 | break; |
michael@0 | 257 | } |
michael@0 | 258 | } |
michael@0 | 259 | } |
michael@0 | 260 | } |
michael@0 | 261 | ]]></body> |
michael@0 | 262 | </method> |
michael@0 | 263 | |
michael@0 | 264 | <!-- If the engine that was just added to the searchbox list was |
michael@0 | 265 | autodetected on this page, move it to each browser's hidden list so it is |
michael@0 | 266 | no longer offered to be added. --> |
michael@0 | 267 | <method name="hideNewEngine"> |
michael@0 | 268 | <parameter name="aEngine"/> |
michael@0 | 269 | <body><![CDATA[ |
michael@0 | 270 | var allbrowsers = getBrowser().mPanelContainer.childNodes; |
michael@0 | 271 | for (var tab = 0; tab < allbrowsers.length; tab++) { |
michael@0 | 272 | var browser = getBrowser().getBrowserAtIndex(tab); |
michael@0 | 273 | if (browser.engines) { |
michael@0 | 274 | // XXX This will need to be changed when engines are identified by |
michael@0 | 275 | // URL rather than title; see bug 335102. |
michael@0 | 276 | var removeTitle = aEngine.wrappedJSObject.name; |
michael@0 | 277 | for (var i = 0; i < browser.engines.length; i++) { |
michael@0 | 278 | if (browser.engines[i].title == removeTitle) { |
michael@0 | 279 | if (!browser.hiddenEngines) |
michael@0 | 280 | browser.hiddenEngines = []; |
michael@0 | 281 | browser.hiddenEngines.push(browser.engines[i]); |
michael@0 | 282 | browser.engines.splice(i, 1); |
michael@0 | 283 | break; |
michael@0 | 284 | } |
michael@0 | 285 | } |
michael@0 | 286 | } |
michael@0 | 287 | } |
michael@0 | 288 | ]]></body> |
michael@0 | 289 | </method> |
michael@0 | 290 | |
michael@0 | 291 | <method name="setIcon"> |
michael@0 | 292 | <parameter name="element"/> |
michael@0 | 293 | <parameter name="uri"/> |
michael@0 | 294 | <body><![CDATA[ |
michael@0 | 295 | if (uri) { |
michael@0 | 296 | let size = Math.round(16 * window.devicePixelRatio); |
michael@0 | 297 | if (!uri.contains("#")) |
michael@0 | 298 | uri += "#-moz-resolution=" + size + "," + size; |
michael@0 | 299 | } |
michael@0 | 300 | element.setAttribute("src", uri); |
michael@0 | 301 | ]]></body> |
michael@0 | 302 | </method> |
michael@0 | 303 | |
michael@0 | 304 | <method name="updateDisplay"> |
michael@0 | 305 | <body><![CDATA[ |
michael@0 | 306 | var uri = this.currentEngine.iconURI; |
michael@0 | 307 | this.setIcon(this, uri ? uri.spec : ""); |
michael@0 | 308 | |
michael@0 | 309 | var name = this.currentEngine.name; |
michael@0 | 310 | var text = this._stringBundle.getFormattedString("searchtip", [name]); |
michael@0 | 311 | this._textbox.placeholder = name; |
michael@0 | 312 | this._textbox.label = text; |
michael@0 | 313 | this._textbox.tooltipText = text; |
michael@0 | 314 | ]]></body> |
michael@0 | 315 | </method> |
michael@0 | 316 | |
michael@0 | 317 | <!-- Rebuilds the dynamic portion of the popup menu (i.e., the menu items |
michael@0 | 318 | for new search engines that can be added to the available list). This |
michael@0 | 319 | is called each time the popup is shown. |
michael@0 | 320 | --> |
michael@0 | 321 | <method name="rebuildPopupDynamic"> |
michael@0 | 322 | <body><![CDATA[ |
michael@0 | 323 | // We might not have added the main popup items yet, do that first |
michael@0 | 324 | // if needed. |
michael@0 | 325 | if (this._needToBuildPopup) |
michael@0 | 326 | this.rebuildPopup(); |
michael@0 | 327 | |
michael@0 | 328 | var popup = this._popup; |
michael@0 | 329 | // Clear any addengine menuitems, including addengine-item entries and |
michael@0 | 330 | // the addengine-separator. Work backward to avoid invalidating the |
michael@0 | 331 | // indexes as items are removed. |
michael@0 | 332 | var items = popup.childNodes; |
michael@0 | 333 | for (var i = items.length - 1; i >= 0; i--) { |
michael@0 | 334 | if (items[i].classList.contains("addengine-item") || |
michael@0 | 335 | items[i].classList.contains("addengine-separator")) |
michael@0 | 336 | popup.removeChild(items[i]); |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | var addengines = getBrowser().mCurrentBrowser.engines; |
michael@0 | 340 | if (addengines && addengines.length > 0) { |
michael@0 | 341 | const kXULNS = |
michael@0 | 342 | "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
michael@0 | 343 | |
michael@0 | 344 | // Find the (first) separator in the remaining menu, or the first item |
michael@0 | 345 | // if no separators are present. |
michael@0 | 346 | var insertLocation = popup.firstChild; |
michael@0 | 347 | while (insertLocation.nextSibling && |
michael@0 | 348 | insertLocation.localName != "menuseparator") { |
michael@0 | 349 | insertLocation = insertLocation.nextSibling; |
michael@0 | 350 | } |
michael@0 | 351 | if (insertLocation.localName != "menuseparator") |
michael@0 | 352 | insertLocation = popup.firstChild; |
michael@0 | 353 | |
michael@0 | 354 | var separator = document.createElementNS(kXULNS, "menuseparator"); |
michael@0 | 355 | separator.setAttribute("class", "addengine-separator"); |
michael@0 | 356 | popup.insertBefore(separator, insertLocation); |
michael@0 | 357 | |
michael@0 | 358 | // Insert the "add this engine" items. |
michael@0 | 359 | for (var i = 0; i < addengines.length; i++) { |
michael@0 | 360 | var menuitem = document.createElement("menuitem"); |
michael@0 | 361 | var engineInfo = addengines[i]; |
michael@0 | 362 | var labelStr = |
michael@0 | 363 | this._stringBundle.getFormattedString("cmd_addFoundEngine", |
michael@0 | 364 | [engineInfo.title]); |
michael@0 | 365 | menuitem = document.createElementNS(kXULNS, "menuitem"); |
michael@0 | 366 | menuitem.setAttribute("class", "menuitem-iconic addengine-item"); |
michael@0 | 367 | menuitem.setAttribute("label", labelStr); |
michael@0 | 368 | menuitem.setAttribute("tooltiptext", engineInfo.uri); |
michael@0 | 369 | menuitem.setAttribute("uri", engineInfo.uri); |
michael@0 | 370 | if (engineInfo.icon) |
michael@0 | 371 | this.setIcon(menuitem, engineInfo.icon); |
michael@0 | 372 | menuitem.setAttribute("title", engineInfo.title); |
michael@0 | 373 | popup.insertBefore(menuitem, insertLocation); |
michael@0 | 374 | } |
michael@0 | 375 | } |
michael@0 | 376 | ]]></body> |
michael@0 | 377 | </method> |
michael@0 | 378 | |
michael@0 | 379 | <!-- Rebuilds the list of visible search engines in the menu. Does not remove |
michael@0 | 380 | or update any dynamic entries (i.e., "Add this engine" items) nor the |
michael@0 | 381 | Manage Engines item. This is called by the observer when the list of |
michael@0 | 382 | visible engines, or the currently selected engine, has changed. |
michael@0 | 383 | --> |
michael@0 | 384 | <method name="rebuildPopup"> |
michael@0 | 385 | <body><![CDATA[ |
michael@0 | 386 | var popup = this._popup; |
michael@0 | 387 | |
michael@0 | 388 | // Clear the popup, down to the first separator |
michael@0 | 389 | while (popup.firstChild && popup.firstChild.localName != "menuseparator") |
michael@0 | 390 | popup.removeChild(popup.firstChild); |
michael@0 | 391 | |
michael@0 | 392 | const kXULNS = |
michael@0 | 393 | "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
michael@0 | 394 | |
michael@0 | 395 | var engines = this.engines; |
michael@0 | 396 | for (var i = engines.length - 1; i >= 0; --i) { |
michael@0 | 397 | var menuitem = document.createElementNS(kXULNS, "menuitem"); |
michael@0 | 398 | var name = engines[i].name; |
michael@0 | 399 | menuitem.setAttribute("label", name); |
michael@0 | 400 | menuitem.setAttribute("id", name); |
michael@0 | 401 | menuitem.setAttribute("class", "menuitem-iconic searchbar-engine-menuitem menuitem-with-favicon"); |
michael@0 | 402 | // Since this menu is rebuilt by the observer method whenever a new |
michael@0 | 403 | // engine is selected, the "selected" attribute does not need to be |
michael@0 | 404 | // explicitly cleared anywhere. |
michael@0 | 405 | if (engines[i] == this.currentEngine) |
michael@0 | 406 | menuitem.setAttribute("selected", "true"); |
michael@0 | 407 | var tooltip = this._stringBundle.getFormattedString("searchtip", [name]); |
michael@0 | 408 | menuitem.setAttribute("tooltiptext", tooltip); |
michael@0 | 409 | if (engines[i].iconURI) |
michael@0 | 410 | this.setIcon(menuitem, engines[i].iconURI.spec); |
michael@0 | 411 | popup.insertBefore(menuitem, popup.firstChild); |
michael@0 | 412 | menuitem.engine = engines[i]; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | this._needToBuildPopup = false; |
michael@0 | 416 | ]]></body> |
michael@0 | 417 | </method> |
michael@0 | 418 | |
michael@0 | 419 | <method name="openManager"> |
michael@0 | 420 | <parameter name="aEvent"/> |
michael@0 | 421 | <body><![CDATA[ |
michael@0 | 422 | var wm = |
michael@0 | 423 | Components.classes["@mozilla.org/appshell/window-mediator;1"] |
michael@0 | 424 | .getService(Components.interfaces.nsIWindowMediator); |
michael@0 | 425 | |
michael@0 | 426 | var window = wm.getMostRecentWindow("Browser:SearchManager"); |
michael@0 | 427 | if (window) |
michael@0 | 428 | window.focus() |
michael@0 | 429 | else { |
michael@0 | 430 | setTimeout(function () { |
michael@0 | 431 | openDialog("chrome://browser/content/search/engineManager.xul", |
michael@0 | 432 | "_blank", "chrome,dialog,modal,centerscreen,resizable"); |
michael@0 | 433 | }, 0); |
michael@0 | 434 | } |
michael@0 | 435 | ]]></body> |
michael@0 | 436 | </method> |
michael@0 | 437 | |
michael@0 | 438 | <method name="selectEngine"> |
michael@0 | 439 | <parameter name="aEvent"/> |
michael@0 | 440 | <parameter name="isNextEngine"/> |
michael@0 | 441 | <body><![CDATA[ |
michael@0 | 442 | // Find the new index |
michael@0 | 443 | var newIndex = this.engines.indexOf(this.currentEngine); |
michael@0 | 444 | newIndex += isNextEngine ? 1 : -1; |
michael@0 | 445 | |
michael@0 | 446 | if (newIndex >= 0 && newIndex < this.engines.length) { |
michael@0 | 447 | this.currentEngine = this.engines[newIndex]; |
michael@0 | 448 | } |
michael@0 | 449 | |
michael@0 | 450 | aEvent.preventDefault(); |
michael@0 | 451 | aEvent.stopPropagation(); |
michael@0 | 452 | ]]></body> |
michael@0 | 453 | </method> |
michael@0 | 454 | |
michael@0 | 455 | <method name="handleSearchCommand"> |
michael@0 | 456 | <parameter name="aEvent"/> |
michael@0 | 457 | <body><![CDATA[ |
michael@0 | 458 | var textBox = this._textbox; |
michael@0 | 459 | var textValue = textBox.value; |
michael@0 | 460 | |
michael@0 | 461 | var where = "current"; |
michael@0 | 462 | if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") { |
michael@0 | 463 | if (aEvent.button == 2) |
michael@0 | 464 | return; |
michael@0 | 465 | where = whereToOpenLink(aEvent, false, true); |
michael@0 | 466 | } |
michael@0 | 467 | else { |
michael@0 | 468 | var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab"); |
michael@0 | 469 | if ((aEvent && aEvent.altKey) ^ newTabPref) |
michael@0 | 470 | where = "tab"; |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | this.doSearch(textValue, where); |
michael@0 | 474 | ]]></body> |
michael@0 | 475 | </method> |
michael@0 | 476 | |
michael@0 | 477 | <method name="doSearch"> |
michael@0 | 478 | <parameter name="aData"/> |
michael@0 | 479 | <parameter name="aWhere"/> |
michael@0 | 480 | <body><![CDATA[ |
michael@0 | 481 | var textBox = this._textbox; |
michael@0 | 482 | |
michael@0 | 483 | // Save the current value in the form history |
michael@0 | 484 | if (aData && !PrivateBrowsingUtils.isWindowPrivate(window)) { |
michael@0 | 485 | this.FormHistory.update( |
michael@0 | 486 | { op : "bump", |
michael@0 | 487 | fieldname : textBox.getAttribute("autocompletesearchparam"), |
michael@0 | 488 | value : aData }, |
michael@0 | 489 | { handleError : function(aError) { |
michael@0 | 490 | Components.utils.reportError("Saving search to form history failed: " + aError.message); |
michael@0 | 491 | }}); |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | // null parameter below specifies HTML response for search |
michael@0 | 495 | var submission = this.currentEngine.getSubmission(aData, null, "searchbar"); |
michael@0 | 496 | BrowserSearch.recordSearchInHealthReport(this.currentEngine, "searchbar"); |
michael@0 | 497 | openUILinkIn(submission.uri.spec, aWhere, null, submission.postData); |
michael@0 | 498 | ]]></body> |
michael@0 | 499 | </method> |
michael@0 | 500 | </implementation> |
michael@0 | 501 | |
michael@0 | 502 | <handlers> |
michael@0 | 503 | <handler event="command"><![CDATA[ |
michael@0 | 504 | const target = event.originalTarget; |
michael@0 | 505 | if (target.engine) { |
michael@0 | 506 | this.currentEngine = target.engine; |
michael@0 | 507 | } else if (target.classList.contains("addengine-item")) { |
michael@0 | 508 | var searchService = |
michael@0 | 509 | Components.classes["@mozilla.org/browser/search-service;1"] |
michael@0 | 510 | .getService(Components.interfaces.nsIBrowserSearchService); |
michael@0 | 511 | // We only detect OpenSearch files |
michael@0 | 512 | var type = Components.interfaces.nsISearchEngine.DATA_XML; |
michael@0 | 513 | // Select the installed engine if the installation succeeds |
michael@0 | 514 | var installCallback = { |
michael@0 | 515 | onSuccess: engine => this.currentEngine = engine |
michael@0 | 516 | } |
michael@0 | 517 | searchService.addEngine(target.getAttribute("uri"), type, |
michael@0 | 518 | target.getAttribute("src"), false, |
michael@0 | 519 | installCallback); |
michael@0 | 520 | } |
michael@0 | 521 | else |
michael@0 | 522 | return; |
michael@0 | 523 | |
michael@0 | 524 | this.focus(); |
michael@0 | 525 | this.select(); |
michael@0 | 526 | ]]></handler> |
michael@0 | 527 | |
michael@0 | 528 | <handler event="popupshowing" action="this.rebuildPopupDynamic();"/> |
michael@0 | 529 | |
michael@0 | 530 | <handler event="DOMMouseScroll" |
michael@0 | 531 | phase="capturing" |
michael@0 | 532 | modifiers="accel" |
michael@0 | 533 | action="this.selectEngine(event, (event.detail > 0));"/> |
michael@0 | 534 | |
michael@0 | 535 | <handler event="focus"> |
michael@0 | 536 | <![CDATA[ |
michael@0 | 537 | // Speculatively connect to the current engine's search URI (and |
michael@0 | 538 | // suggest URI, if different) to reduce request latency |
michael@0 | 539 | |
michael@0 | 540 | const SUGGEST_TYPE = "application/x-suggestions+json"; |
michael@0 | 541 | var engine = this.currentEngine; |
michael@0 | 542 | var connector = |
michael@0 | 543 | Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect); |
michael@0 | 544 | var searchURI = engine.getSubmission("dummy", null, "searchbar").uri; |
michael@0 | 545 | let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) |
michael@0 | 546 | .getInterface(Components.interfaces.nsIWebNavigation) |
michael@0 | 547 | .QueryInterface(Components.interfaces.nsILoadContext); |
michael@0 | 548 | connector.speculativeConnect(searchURI, callbacks); |
michael@0 | 549 | |
michael@0 | 550 | if (engine.supportsResponseType(SUGGEST_TYPE)) { |
michael@0 | 551 | var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE, "searchbar").uri; |
michael@0 | 552 | if (suggestURI.prePath != searchURI.prePath) |
michael@0 | 553 | connector.speculativeConnect(suggestURI, callbacks); |
michael@0 | 554 | } |
michael@0 | 555 | ]]></handler> |
michael@0 | 556 | </handlers> |
michael@0 | 557 | </binding> |
michael@0 | 558 | |
michael@0 | 559 | <binding id="searchbar-textbox" |
michael@0 | 560 | extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> |
michael@0 | 561 | <implementation implements="nsIObserver"> |
michael@0 | 562 | <constructor><![CDATA[ |
michael@0 | 563 | const kXULNS = |
michael@0 | 564 | "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
michael@0 | 565 | |
michael@0 | 566 | if (document.getBindingParent(this).parentNode.parentNode.localName == |
michael@0 | 567 | "toolbarpaletteitem") |
michael@0 | 568 | return; |
michael@0 | 569 | |
michael@0 | 570 | // Initialize fields |
michael@0 | 571 | this._stringBundle = document.getBindingParent(this)._stringBundle; |
michael@0 | 572 | this._prefBranch = |
michael@0 | 573 | Components.classes["@mozilla.org/preferences-service;1"] |
michael@0 | 574 | .getService(Components.interfaces.nsIPrefBranch); |
michael@0 | 575 | this._suggestEnabled = |
michael@0 | 576 | this._prefBranch.getBoolPref("browser.search.suggest.enabled"); |
michael@0 | 577 | |
michael@0 | 578 | if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll")) |
michael@0 | 579 | this.setAttribute("clickSelectsAll", true); |
michael@0 | 580 | |
michael@0 | 581 | // Add items to context menu and attach controller to handle them |
michael@0 | 582 | var textBox = document.getAnonymousElementByAttribute(this, |
michael@0 | 583 | "anonid", "textbox-input-box"); |
michael@0 | 584 | var cxmenu = document.getAnonymousElementByAttribute(textBox, |
michael@0 | 585 | "anonid", "input-box-contextmenu"); |
michael@0 | 586 | var pasteAndSearch; |
michael@0 | 587 | cxmenu.addEventListener("popupshowing", function() { |
michael@0 | 588 | if (!pasteAndSearch) |
michael@0 | 589 | return; |
michael@0 | 590 | var controller = document.commandDispatcher.getControllerForCommand("cmd_paste"); |
michael@0 | 591 | var enabled = controller.isCommandEnabled("cmd_paste"); |
michael@0 | 592 | if (enabled) |
michael@0 | 593 | pasteAndSearch.removeAttribute("disabled"); |
michael@0 | 594 | else |
michael@0 | 595 | pasteAndSearch.setAttribute("disabled", "true"); |
michael@0 | 596 | }, false); |
michael@0 | 597 | |
michael@0 | 598 | var element, label, akey; |
michael@0 | 599 | |
michael@0 | 600 | element = document.createElementNS(kXULNS, "menuseparator"); |
michael@0 | 601 | cxmenu.appendChild(element); |
michael@0 | 602 | |
michael@0 | 603 | var insertLocation = cxmenu.firstChild; |
michael@0 | 604 | while (insertLocation.nextSibling && |
michael@0 | 605 | insertLocation.getAttribute("cmd") != "cmd_paste") |
michael@0 | 606 | insertLocation = insertLocation.nextSibling; |
michael@0 | 607 | if (insertLocation) { |
michael@0 | 608 | element = document.createElementNS(kXULNS, "menuitem"); |
michael@0 | 609 | label = this._stringBundle.getString("cmd_pasteAndSearch"); |
michael@0 | 610 | element.setAttribute("label", label); |
michael@0 | 611 | element.setAttribute("anonid", "paste-and-search"); |
michael@0 | 612 | element.setAttribute("oncommand", |
michael@0 | 613 | "BrowserSearch.searchBar.select(); goDoCommand('cmd_paste'); BrowserSearch.searchBar.handleSearchCommand();"); |
michael@0 | 614 | cxmenu.insertBefore(element, insertLocation.nextSibling); |
michael@0 | 615 | pasteAndSearch = element; |
michael@0 | 616 | } |
michael@0 | 617 | |
michael@0 | 618 | element = document.createElementNS(kXULNS, "menuitem"); |
michael@0 | 619 | label = this._stringBundle.getString("cmd_clearHistory"); |
michael@0 | 620 | akey = this._stringBundle.getString("cmd_clearHistory_accesskey"); |
michael@0 | 621 | element.setAttribute("label", label); |
michael@0 | 622 | element.setAttribute("accesskey", akey); |
michael@0 | 623 | element.setAttribute("cmd", "cmd_clearhistory"); |
michael@0 | 624 | cxmenu.appendChild(element); |
michael@0 | 625 | |
michael@0 | 626 | element = document.createElementNS(kXULNS, "menuitem"); |
michael@0 | 627 | label = this._stringBundle.getString("cmd_showSuggestions"); |
michael@0 | 628 | akey = this._stringBundle.getString("cmd_showSuggestions_accesskey"); |
michael@0 | 629 | element.setAttribute("anonid", "toggle-suggest-item"); |
michael@0 | 630 | element.setAttribute("label", label); |
michael@0 | 631 | element.setAttribute("accesskey", akey); |
michael@0 | 632 | element.setAttribute("cmd", "cmd_togglesuggest"); |
michael@0 | 633 | element.setAttribute("type", "checkbox"); |
michael@0 | 634 | element.setAttribute("checked", this._suggestEnabled); |
michael@0 | 635 | element.setAttribute("autocheck", "false"); |
michael@0 | 636 | this._suggestMenuItem = element; |
michael@0 | 637 | cxmenu.appendChild(element); |
michael@0 | 638 | |
michael@0 | 639 | this.controllers.appendController(this.searchbarController); |
michael@0 | 640 | |
michael@0 | 641 | // Add observer for suggest preference |
michael@0 | 642 | var prefs = Components.classes["@mozilla.org/preferences-service;1"] |
michael@0 | 643 | .getService(Components.interfaces.nsIPrefBranch); |
michael@0 | 644 | prefs.addObserver("browser.search.suggest.enabled", this, false); |
michael@0 | 645 | ]]></constructor> |
michael@0 | 646 | |
michael@0 | 647 | <destructor><![CDATA[ |
michael@0 | 648 | var prefs = Components.classes["@mozilla.org/preferences-service;1"] |
michael@0 | 649 | .getService(Components.interfaces.nsIPrefBranch); |
michael@0 | 650 | prefs.removeObserver("browser.search.suggest.enabled", this); |
michael@0 | 651 | |
michael@0 | 652 | // Because XBL and the customize toolbar code interacts poorly, |
michael@0 | 653 | // there may not be anything to remove here |
michael@0 | 654 | try { |
michael@0 | 655 | this.controllers.removeController(this.searchbarController); |
michael@0 | 656 | } catch (ex) { } |
michael@0 | 657 | ]]></destructor> |
michael@0 | 658 | |
michael@0 | 659 | <field name="_stringBundle"/> |
michael@0 | 660 | <field name="_prefBranch"/> |
michael@0 | 661 | <field name="_suggestMenuItem"/> |
michael@0 | 662 | <field name="_suggestEnabled"/> |
michael@0 | 663 | |
michael@0 | 664 | <!-- |
michael@0 | 665 | This overrides the searchParam property in autocomplete.xml. We're |
michael@0 | 666 | hijacking this property as a vehicle for delivering the privacy |
michael@0 | 667 | information about the window into the guts of nsSearchSuggestions. |
michael@0 | 668 | |
michael@0 | 669 | Note that the setter is the same as the parent. We were not sure whether |
michael@0 | 670 | we can override just the getter. If that proves to be the case, the setter |
michael@0 | 671 | can be removed. |
michael@0 | 672 | --> |
michael@0 | 673 | <property name="searchParam" |
michael@0 | 674 | onget="return this.getAttribute('autocompletesearchparam') + |
michael@0 | 675 | (PrivateBrowsingUtils.isWindowPrivate(window) ? '|private' : '');" |
michael@0 | 676 | onset="this.setAttribute('autocompletesearchparam', val); return val;"/> |
michael@0 | 677 | |
michael@0 | 678 | <!-- |
michael@0 | 679 | This method overrides the autocomplete binding's openPopup (essentially |
michael@0 | 680 | duplicating the logic from the autocomplete popup binding's |
michael@0 | 681 | openAutocompletePopup method), modifying it so that the popup is aligned with |
michael@0 | 682 | the inner textbox, but sized to not extend beyond the search bar border. |
michael@0 | 683 | --> |
michael@0 | 684 | <method name="openPopup"> |
michael@0 | 685 | <body><![CDATA[ |
michael@0 | 686 | var popup = this.popup; |
michael@0 | 687 | if (!popup.mPopupOpen) { |
michael@0 | 688 | // Initially the panel used for the searchbar (PopupAutoComplete |
michael@0 | 689 | // in browser.xul) is hidden to avoid impacting startup / new |
michael@0 | 690 | // window performance. The base binding's openPopup would normally |
michael@0 | 691 | // call the overriden openAutocompletePopup in urlbarBindings.xml's |
michael@0 | 692 | // browser-autocomplete-result-popup binding to unhide the popup, |
michael@0 | 693 | // but since we're overriding openPopup we need to unhide the panel |
michael@0 | 694 | // ourselves. |
michael@0 | 695 | popup.hidden = false; |
michael@0 | 696 | |
michael@0 | 697 | popup.mInput = this; |
michael@0 | 698 | popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView); |
michael@0 | 699 | popup.invalidate(); |
michael@0 | 700 | |
michael@0 | 701 | popup.showCommentColumn = this.showCommentColumn; |
michael@0 | 702 | popup.showImageColumn = this.showImageColumn; |
michael@0 | 703 | |
michael@0 | 704 | document.popupNode = null; |
michael@0 | 705 | |
michael@0 | 706 | const isRTL = getComputedStyle(this, "").direction == "rtl"; |
michael@0 | 707 | |
michael@0 | 708 | var outerRect = this.getBoundingClientRect(); |
michael@0 | 709 | var innerRect = this.inputField.getBoundingClientRect(); |
michael@0 | 710 | if (isRTL) { |
michael@0 | 711 | var width = innerRect.right - outerRect.left; |
michael@0 | 712 | } else { |
michael@0 | 713 | var width = outerRect.right - innerRect.left; |
michael@0 | 714 | } |
michael@0 | 715 | popup.setAttribute("width", width > 100 ? width : 100); |
michael@0 | 716 | |
michael@0 | 717 | var yOffset = outerRect.bottom - innerRect.bottom; |
michael@0 | 718 | popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false); |
michael@0 | 719 | } |
michael@0 | 720 | ]]></body> |
michael@0 | 721 | </method> |
michael@0 | 722 | |
michael@0 | 723 | <method name="observe"> |
michael@0 | 724 | <parameter name="aSubject"/> |
michael@0 | 725 | <parameter name="aTopic"/> |
michael@0 | 726 | <parameter name="aData"/> |
michael@0 | 727 | <body><![CDATA[ |
michael@0 | 728 | if (aTopic == "nsPref:changed") { |
michael@0 | 729 | this._suggestEnabled = |
michael@0 | 730 | this._prefBranch.getBoolPref("browser.search.suggest.enabled"); |
michael@0 | 731 | this._suggestMenuItem.setAttribute("checked", this._suggestEnabled); |
michael@0 | 732 | } |
michael@0 | 733 | ]]></body> |
michael@0 | 734 | </method> |
michael@0 | 735 | |
michael@0 | 736 | <method name="openSearch"> |
michael@0 | 737 | <body> |
michael@0 | 738 | <![CDATA[ |
michael@0 | 739 | // Don't open search popup if history popup is open |
michael@0 | 740 | if (!this.popupOpen) { |
michael@0 | 741 | document.getBindingParent(this).searchButton.open = true; |
michael@0 | 742 | return false; |
michael@0 | 743 | } |
michael@0 | 744 | return true; |
michael@0 | 745 | ]]> |
michael@0 | 746 | </body> |
michael@0 | 747 | </method> |
michael@0 | 748 | |
michael@0 | 749 | <!-- override |onTextEntered| in autocomplete.xml --> |
michael@0 | 750 | <method name="onTextEntered"> |
michael@0 | 751 | <parameter name="aEvent"/> |
michael@0 | 752 | <body><![CDATA[ |
michael@0 | 753 | var evt = aEvent || this.mEnterEvent; |
michael@0 | 754 | document.getBindingParent(this).handleSearchCommand(evt); |
michael@0 | 755 | this.mEnterEvent = null; |
michael@0 | 756 | ]]></body> |
michael@0 | 757 | </method> |
michael@0 | 758 | |
michael@0 | 759 | <!-- nsIController --> |
michael@0 | 760 | <field name="searchbarController" readonly="true"><![CDATA[({ |
michael@0 | 761 | _self: this, |
michael@0 | 762 | supportsCommand: function(aCommand) { |
michael@0 | 763 | return aCommand == "cmd_clearhistory" || |
michael@0 | 764 | aCommand == "cmd_togglesuggest"; |
michael@0 | 765 | }, |
michael@0 | 766 | |
michael@0 | 767 | isCommandEnabled: function(aCommand) { |
michael@0 | 768 | return true; |
michael@0 | 769 | }, |
michael@0 | 770 | |
michael@0 | 771 | doCommand: function (aCommand) { |
michael@0 | 772 | switch (aCommand) { |
michael@0 | 773 | case "cmd_clearhistory": |
michael@0 | 774 | var param = this._self.getAttribute("autocompletesearchparam"); |
michael@0 | 775 | |
michael@0 | 776 | let searchBar = this._self.parentNode; |
michael@0 | 777 | |
michael@0 | 778 | BrowserSearch.searchBar.FormHistory.update({ op : "remove", fieldname : param }, null); |
michael@0 | 779 | this._self.value = ""; |
michael@0 | 780 | break; |
michael@0 | 781 | case "cmd_togglesuggest": |
michael@0 | 782 | // The pref observer will update _suggestEnabled and the menu |
michael@0 | 783 | // checkmark. |
michael@0 | 784 | this._self._prefBranch.setBoolPref("browser.search.suggest.enabled", |
michael@0 | 785 | !this._self._suggestEnabled); |
michael@0 | 786 | break; |
michael@0 | 787 | default: |
michael@0 | 788 | // do nothing with unrecognized command |
michael@0 | 789 | } |
michael@0 | 790 | } |
michael@0 | 791 | })]]></field> |
michael@0 | 792 | </implementation> |
michael@0 | 793 | |
michael@0 | 794 | <handlers> |
michael@0 | 795 | <handler event="keypress" keycode="VK_UP" modifiers="accel" |
michael@0 | 796 | phase="capturing" |
michael@0 | 797 | action="document.getBindingParent(this).selectEngine(event, false);"/> |
michael@0 | 798 | |
michael@0 | 799 | <handler event="keypress" keycode="VK_DOWN" modifiers="accel" |
michael@0 | 800 | phase="capturing" |
michael@0 | 801 | action="document.getBindingParent(this).selectEngine(event, true);"/> |
michael@0 | 802 | |
michael@0 | 803 | <handler event="keypress" keycode="VK_DOWN" modifiers="alt" |
michael@0 | 804 | phase="capturing" |
michael@0 | 805 | action="return this.openSearch();"/> |
michael@0 | 806 | |
michael@0 | 807 | <handler event="keypress" keycode="VK_UP" modifiers="alt" |
michael@0 | 808 | phase="capturing" |
michael@0 | 809 | action="return this.openSearch();"/> |
michael@0 | 810 | |
michael@0 | 811 | #ifndef XP_MACOSX |
michael@0 | 812 | <handler event="keypress" keycode="VK_F4" |
michael@0 | 813 | phase="capturing" |
michael@0 | 814 | action="return this.openSearch();"/> |
michael@0 | 815 | #endif |
michael@0 | 816 | |
michael@0 | 817 | <handler event="dragover"> |
michael@0 | 818 | <![CDATA[ |
michael@0 | 819 | var types = event.dataTransfer.types; |
michael@0 | 820 | if (types.contains("text/plain") || types.contains("text/x-moz-text-internal")) |
michael@0 | 821 | event.preventDefault(); |
michael@0 | 822 | ]]> |
michael@0 | 823 | </handler> |
michael@0 | 824 | |
michael@0 | 825 | <handler event="drop"> |
michael@0 | 826 | <![CDATA[ |
michael@0 | 827 | var dataTransfer = event.dataTransfer; |
michael@0 | 828 | var data = dataTransfer.getData("text/plain"); |
michael@0 | 829 | if (!data) |
michael@0 | 830 | data = dataTransfer.getData("text/x-moz-text-internal"); |
michael@0 | 831 | if (data) { |
michael@0 | 832 | event.preventDefault(); |
michael@0 | 833 | this.value = data; |
michael@0 | 834 | this.onTextEntered(event); |
michael@0 | 835 | } |
michael@0 | 836 | ]]> |
michael@0 | 837 | </handler> |
michael@0 | 838 | |
michael@0 | 839 | </handlers> |
michael@0 | 840 | </binding> |
michael@0 | 841 | </bindings> |