browser/base/content/abouthome/aboutHome.js

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 const SEARCH_ENGINES = {
michael@0 6 "Google": {
michael@0 7 // This is the "2x" image designed for OS X retina resolution, Windows at 192dpi, etc.;
michael@0 8 // it will be scaled down as necessary on lower-dpi displays.
michael@0 9 // This needs to be defined in a single line to keep the JS parser from creating many
michael@0 10 // intermediate strings in memory. See bug 986672.
michael@0 11 image: "data:image/png;base64,\
michael@0 12 iVBORw0KGgoAAAANSUhEUgAAAIwAAAA4CAYAAAAvmxBdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ\
michael@0 13 bWFnZVJlYWR5ccllPAAAGrFJREFUeNrtfHt4VdW172+utZOASLJ5+BaIFrUeXkFsa0Fl++gDnznV\
michael@0 14 VlvFxt7aqvUUarXtse3Bau35ak/rZ9XT26NtfOvV6wFET+FYCQEKWqsQIT5RCAgSXnlnrzXneNw/\
michael@0 15 1lphJSSQ8BB7bub3zW+LO3uN+fiNMcf4jTEX0N/6W3/rb/2tv30smtnXB3zmRi2FQakxQNKX3WkW\
michael@0 16 9S/tgW3HLpmQM543A0BWVSHMYGIwOTDxzxrOf3/RQQfMZ2/SLAvKhTFVBGUqKFONH2QAzwOMF38a\
michael@0 17 wHhYZAxWAqhe/iszp3+b970d/sInc57vz/J8L2eMB2MAEYkBQ6DQ3dRw4dq7AUjcP3rAfPZmLWXC\
michael@0 18 LHKoIAcQAUxaB5EaEfc6AEBhjDEwmcx43/fO9HxT4vkReBIAAZgjgodW3NcPnn1sHgD/iHknn+0d\
michael@0 19 6s8XEUhsXXac/34WAAGw8afuT8GZ3X055YeSJcIsG+pMZwFn0UihezRofPt3G54f/0E8cNMN+Myo\
michael@0 20 8jVTCgYd823PLzrPeIBnABiUQ1F+UoWsVOYb33mkoKp/7/dKyT0AGc47X4s0sjBEoLxbBqAQAMfW\
michael@0 21 Rfe38B4BM+VHUkYOs8mi1FrABbK4dcvK73zwp1M3xYPOxANKBqbpCdXNGb0UwPKRF74xpfDQ0t+K\
michael@0 22 54+IvlKoahmAhaO/mv/ZmicG3tqPgT61ZM2dZMQJOYhIdByRM/F3dCCOox4Bc3oEliqyyNoQCPPu\
michael@0 23 sXceKZqRsigu7pwaWBowiRb46+f9Q1V2wl1nDx09/R7jF30x9adNlN8yPx4DHwht+B/cBIBoRqeI\
michael@0 24 E4hE/oshTcB0wNbT6/o/zrhFyohR5ZxmrVWE+fDxdx4puhGAH4OkPe5B6pykeJAc/7cDEMZ/095Y\
michael@0 25 870P339m+BXs2v4kbCFsm9u2vnpJ3bzR7wAo2B/R2v+PjSnyXcRxtOLUSXFxwAFz5i2SZUIVO82S\
michael@0 26 BWye/vLOIwNvjL8OYqCEfXCmJAZPHkC7sK1REbj2+lmbq86qTVmmfuuyN2cTiREWKCvACgml9kDL\
michael@0 27 7HQksehsZmSdA6yVpsa6P38v3swg7m4vN1dGXrThKGP8yS5fP33j/LEvxKDbl2f2A0YFCtkZQDOa\
michael@0 28 PjLAnP4jrmBGjh1AVhG2ttxfX33++vjY2eeNXf/siLUAzgEwMJZrY2vF/Vu/t4BRqCqgCmj07wMV\
michael@0 29 HXUCzJQfUlZE72ICnANcqNj21h8eiK1AX46gXh29KT9H+rd9XxBjYGCgig7QHOgjPgMAKigXQZYp\
michael@0 30 si4uCOc3v35zY2wF9ufGSgxA7fdd9g8ho9ol4P4ojiQWnSUMMANECrJNy1NWYH8eGfsEvJbLv1IK\
michael@0 31 1XIAUwEtA0xplJMwjcaYlTDeShg8dOgjj6/cJxNYfWIWkHJoh5yyjkSZ8RbB89YBZq4/pXafGeuz\
michael@0 32 b9WciXJxo2B2houqgAjABJCLOwFMqFv57+bBxMIAJm1det3avnl1OYCLAeSgWhofaY1QXQSRuYc+\
michael@0 33 /OiD3QLmUzNdqTBKhRVMADsF5beuToXJB90KtFz+lVIVniXOVUAUqjpXVB4WwPjGTPB8/0zjeTnj\
michael@0 34 ezl43szmKy6vNkDF4MeeXNc3oJyUhfAMkJsJkSxUVrLos6o6z/O8Ucb3phrPzyHKeVTwkpPXseg3\
michael@0 35 Cqe+1SfG+swfaw6KGTAoJ5eyGF3IBeEIJB2AcXxb0FI/L45uFQBMGiu6Z3ai9eqrclBUClFWVatV\
michael@0 36 5GERNT5wEVQnQLUcIuVNX75kFjn60rA5c1d0AoywlkcxfdwZ2LSgbOmBZAv70povu7RcyFUqcZYd\
michael@0 37 Pbxix44fnLv8pbYUOWh+P3ZM9uJRo34xoLDgq8b3YTxvqhqsaPzyJTdmn36msjdyqPqkMhWqBFGZ\
michael@0 38 MtV8uDX4zMjp2zemyEoPgGn4zyOvGzy48A54GcD3Sz1jFrqqE+4uOOvdmb0ASlYEs5mQE9afUdhy\
michael@0 39 0yv3lHzwya/8ZcjgI0+5yssU3QKYkgQ4Ivp60LL1n8kBQfOWuvdnj6uLldgHQKoKxU7HV/eg2y1X\
michael@0 40 XXmXEs1U0ZVb29o//4k5c5P5eQB+s+68aVeUFBTcCxUoS6kRWfjhueecc9SfX3ytA9QTr7eVACqY\
michael@0 41 FDYEwnbB2qcHHg6gLY6ODhpomi77coUyVaojhKH9+ZHzF/wqXiztEg34APxNX/jCvQOLCi83fpy8\
michael@0 42 UsCJXHLYnGdn785S0uKTyyBUBXJZcW5x4bSN56ciyLQcD4Bf/+ThVwwbUvRb+JkoswqAWX5b9Lm1\
michael@0 43 M3uSM/UnUiaCKiZk2blvvnxX0ePxuBNAmpMur51wyLBPzjVeBBoVwIXBk6vuP+SG+LkcuwkWAA96\
michael@0 44 /JjZKnKxkACkkFb5Nztz220xX9bJlWi+6opKFalQlpqlmzZNu6B6SaJ0knKJ/DW5qd8p8TO3x6AB\
michael@0 45 qza1EE06cdmy9wDAY5LjmBTMkQnUnZ42H0ywNF52aU6FK4UY5NySI+cv+E3MCnMM5HyqtwFoO3rB\
michael@0 46 gmuDMFjGjiCOIEQwzH9c+7lzju+JTaYlJ2ehUqXMWWFqeurFxqsAFMVf25Ss9kTOEZdvebClJbxT\
michael@0 47 yUGZoEzwlL/b9tzRX+pOztSfSBZApSqyIrL45buKnkaUJEzLCN5+csxr+ab6fyILkI2OIZYBlx9/\
michael@0 48 2bYvpLgw2+EqKLKdwoceVKJp+tfuEpYKZcaW1tZbLqheEsbj3GV+oxdV3x0GwQZrHUIiWKIST3Vm\
michael@0 49 DG54zFrKrBBWiGgSyx9Uv6Xh0n/MKlGlOII4h80trQ+kuJt8HGklZHg6FZF/Y/uOb7O1YOvAzkGt\
michael@0 50 Kxmoehe6SYNEpkErwZIFC4I2fuLKf2tLtDOPzumPhA6wAPJDLt1yuzjaAEcAMUCMApXfvPP7IcO6\
michael@0 51 gkYFs4RRpgy49qanUsAPu/T8W48e/YwL6S/kYtBYwM8U/yu6KVlQUShr9CkKyK7b1vDVy0qVeaYy\
michael@0 52 gaxbdeK85/8a/z7sYR3zgXM1gXUInEPoCEw8PR6z8YQxaidQPh6RrgrPEOZS4chKjFuydEEKFD1x\
michael@0 53 QgrAnfO3V98Jw/B5dhFgmByU+MK/nnrq6K6gcQtPyqlIubJAibCxPv/fsVVNgCI9yGEAQdBq71NH\
michael@0 54 UEdQIoBo5PBBeklazuQfSpYFM0UAFsDmd2yMf9+1XkUT3otc8AiRwpFChCBCI0detGbSLtYr5uw6\
michael@0 55 tk26XctZwgxhRt65ZSmr1t389M1Jk85wzKcHRAiJkCfasDnI/0sMGN+jlLMrAigMhp0+f+TBBIw4\
michael@0 56 milEYOcQBHZZAoZeEIgKgIIgeJbD2MqEFhxaDAFmdAWMisxQFigzlAUnX9e4rA9yeHuTna3koBQB\
michael@0 57 RogxwOPvxNbQAAA7VHQEFKSQKEFIu4lA5d3HiiuFNB4XQZlhUHBK11QO0oRdD7ouROVCkeJZG7ak\
michael@0 58 /KBOYHlz4sTy1WVlVY5oYego2+bs82+3tFw6YcVrp01dteqpxNfyhKQuGlxCMSsKBh570ABT/8XP\
michael@0 59 5dhRVpyDWAd2Ns0O9yrhWdfcMpvCEByEoNCCwhBgvgBdM+PM5TH5FPW+1ZLo8de2viehe12dhVoH\
michael@0 60 OAtDPO61O4o+kYCTnE5wVuGsxlzKHul7BUDKdomKgwpB2QHAyNiP2Dl+0Z2WRXZ9YP0F55WJczvX\
michael@0 61 0jp09U3fLiurWD1+/NqQaHZIVNbu3O1vt7aM+fSqVRWXvPvu0pRldwAkQ5brjO+NMh0kgMIvGjYZ\
michael@0 62 wIKETPxIrYt1U5M8iThKJil9yZGc++ab298dP36Jb8wZohqhQHRErKEeAA6fG5FT5yIlYYI6tzfO\
michael@0 63 vtiQni3MYDw0ChqEgUMyejyAdwGwDeW4ZI9FAGQOmwzgv/cERmZbDXhnKBNUGMJkUhGVduSSJJ1P\
michael@0 64 6rw8HIalJo7ilBkchgCgL48fVzLceDc4kZnWUdap1AQi10x+660n4jXyk1M7ZXEZgHhMUkMO4Njp\
michael@0 65 hQGMf8h56Fx++ZE1a+1xZC2Szjs3sk9uUEhUbSMvP3LeyOGZ0tKJiearo1J1DHVRPYmS7JUcG2g1\
michael@0 66 pxxUsooBnpmQWAOb10YbKGygcKFCZOC0XqxrRKokCBQG5euX77In2k1P+2hhWEZBAAoCuCCEcW7E\
michael@0 67 2xMn/m6oYo0jyjnmuc3Off6UN96YMvmtt5LILSmQ61r3xAA0I+xqPBiIejAd1f7e2MPPfvm4LQs/\
michael@0 68 89a+bP6nZuSzfsaU+T7g+UBixYQVRFGS01kFO22srRy0EgA4CEvFRHS3MANMY/fGbybmlQqAFSBV\
michael@0 69 sCp8kWwCGA5dqefFShnnRV77ecHYU37iXuqLoB0tsuIo34v3NfJR1GlJsrnOuiXGy1y8k+rwxh57\
michael@0 70 3srSD/6rbLdra7yMqgjUCGAULR8uWr0LJPYAGApCeCbKNygLPKIxJ65YOSU+YpLUUCYGiqBzQVy3\
michael@0 71 Ft1zbevnJl60UARqACgcVDo9ZZr63Mqua68QxlpmrWJC1FmrmLSKCFVktcpZrbKhzg4D26E5Lgjg\
michael@0 72 8vnoMwwh1hU/dvTRo/qcDyJqcESw5Dp6o3XNHVrqLDSubAdFjuXwwWZcX+Wc9APboKxQUoiLurXa\
michael@0 73 IYfCpjlCDsoxZ6OCouLRt+xpbY3nA8aDMR6E2+9vffOWxl02cQ+Bbdjevt7l83D5ABRaKNHYO484\
michael@0 74 YmgMkoJ4jElCOL8Lz9NN87YumrRDxc2DElQZKgIVhZcZcO1hZ74wtK/H0thvtuXGXdM2S0S/ziQ1\
michael@0 75 FPJiG7pHwvbgDhtKnQ0VNhCEeUHQLmiuf2fymieGvJGY8DCfX+yCEC5xWIlwtO+P6+s4VESJGS4+\
michael@0 76 liwxKjZ/2FGRZvPhYgktxEZdHWOAr2P34ihWIQWTgJ2CnWJbo9Ymz1g/5+h1QsF9wgKJ19Z4hV87\
michael@0 77 4fKNE3cnx8v4V8H4UOjqhvce+zW6qdWVlOvSjQsDlw/WUT4A5QNQGIJDizMPHXR+CiRBb4GSzlYr\
michael@0 78 26Z7vYKSC42nUOPBqA9VU1I0ZOJPEYWj1NvVW/3AoEUAFgO4IzZ1hYk2jf9WUw7IjCIXHUVhXrFp\
michael@0 79 /sQtKZPIoXXr/PjoSkZeoHo6gP/bFyeciECqcHG3IrXp37a2SF3xQNPxRAXgq5nS1bHsDWCYALYA\
michael@0 80 u+h0W/impI8Pad9ec/vAoWVTjV84Nsn5FAwcvmDMN5rOqf1jyatdHzjuGjvThloKYH3b5qVXt775\
michael@0 81 44ZuN1QEKknF3a6ImfDee4tWjBrV6R5Qoeq1AP6Avaxx8gDolhdPXAh2qzQmZFQ4ZhALrj/mvLpT\
michael@0 82 +qhxya0BP5VVZQBkA6jNR0AJ2xUUcjKGjsx4k3PVYUwaJU6rJ3reLiHlHppjBjF3fLYSzU/noEZ8\
michael@0 83 3611VusoVJBVsFWAdezim/3jemSFe+SNIsvCpAhCXf7TBZI+PnTr4nO2t2xcME3ZroYKIouEEqDo\
michael@0 84 xfHfav/GxOttFgBOucGWll0XVqrqXYDWNLz3aG7bsovWp4i2TvkhScLqNBezq/M/zxLBxV2Yx/75\
michael@0 85 yCPP6usc04CJ+B3bcLMwQTiK+0UIwgz1ip8+4pyaYX0x0SnWMkjnYGygkm9nBO0MGzoI2TTDyQBw\
michael@0 86 7ubNawPmeZYZNt5wZhrxX8OHX9yXSTJzGcVgIWasbs8/hc7XRzXM670cg0Vs5H+MHm6u74ucrb/K\
michael@0 87 lAlFPoySoqFFn+rm+OCGV762df2cYWe4fP0M5qDWhoowRIm1/h+s1YZx3wrVOV1LDhXMaGzfXntF\
michael@0 88 46vXtMQRS/clsqRRT9SNd0GMBo6edRStZbKeg4D//ciQIcP2CTDbqsdVKQePq1JMFkXxv4qO9AaM\
michael@0 89 fPGoaeuG9kXp0LkU0wGgMFC1gYAdAeyg0m3IrE3W3mtTvodjRpHq9X3xL4h5Qsq63P/z9ra6LqSc\
michael@0 90 vvmBPkwOTex2lnf4wNee/47fa99NGGVJ8Zl1qP3UPfwkdr15mDDV+Y3Pf+Kh9c9kz9pee89J7dve\
michael@0 91 vaRt+7qLbVv47y5UUKggp3BB/okNz0/aHI8332OaIgELxWDpptQtt6X+Qcu03nVYGQYxjxzl+7/e\
michael@0 92 GyvjdYrCtv31JiW7QTjy6qWj83jF4AeP/MLaodiHRtZBXAihEEIWkq4eSgGmvKGhqpX5d1YEVhiW\
michael@0 93 BaI6Zf6QITN7s5ELhw4tZZavkwhIZMOC1rZfo5s64nPv4+1NzXot2/hYiqKckglH4/7eRojCOosp\
michael@0 94 St6u2ijfS1Hv3I0SdVy5aam9ecumBeOqN8w7aRkxSlMVdRDmRHa4m5xWPKPEusUA6maIrcy/cCKw\
michael@0 95 InASKaCoXrlo2LAH+xpMpAEjLauu2ObaNnxVmZqUHaI8SaR+KnIhTPHCo6ZtOn6vk4qUPNNGnV2P\
michael@0 96 J0ptENweMq92zHBMcMwwIrfMLS6etKdJEnMlCYOZm9YE4dUPkWvsIUckJ/+SZwd5PCEOEBc5rh7j\
michael@0 97 grqf+VfvSc7mO/xZSihVAra3YMY/PqqrUhZVe7C8yRHTBqAVQJuQN5idgJ2ASQAz4PJjptWevKc0\
michael@0 98 RZQ0TQATRWDd/dmFDQ2VeaLH0z4dRVTK9EXZ7IqFJSXH7W6eLw0blntp2NAydGOSqPGVs/5mW9Zc\
michael@0 99 JGKbRSxELIRDCFuIuAmiBa8eMW37rcdc1JDtM+3PYdSp43k9/ulPgmDrsnz+vFBktRWBZYEVKSlU\
michael@0 100 feH5wYPP7u5Hfy4uzi4oLq50IjkSaXrf2vIfBPnV6PlKiwKg0XfyNe2BPkmJ8+oUGeh/bLjNu7En\
michael@0 101 0Gy+w5sppLcyKRra9IZJ98hTvciop9MPSSFUwGTnEjHICsgpyKHYHzjquWMvrJ+wewUENPFjCIAx\
michael@0 102 k3uStyIMbw5FVieWJvJpBE5kgqq+X1VcPGdRcfHMxSUluSUlJbmlUZ+1tKRkLRGVnrZ9Rw12rSLt\
michael@0 103 sDpFg8vmfbpw0HH3wcuMMSaiao2XAbwMjPFhPL/ReN6DfsY8tHHekN0WXR929vqsCpWruFshPEqF\
michael@0 104 o3IyADuWTxgea1rYTbRVeEMmc+SnCwp+OcB4l3kmLq0D4BnzkA/MMUBjvDMXC1DBqlkCFr9N9E//\
michael@0 105 HIZpPyDsQVuTFwsMfP273k8GFeLbvo9izwe8DGA8VMPgIc/D2piALlPFDGWUMqNuazOun/RbeQU7\
michael@0 106 L/zl0cfC+SPOXjG84NBRawCvJNoSE7PiBgr5Xx/MKf7jLnzIbUPKlHVF5C11KgJfD9+shY8Vxjd3\
michael@0 107 0780rEvP8bFDDvnVQGO+lU5MeTDwzM5aTbOzNyrw/XNbWx9JFLknk+sjqjobUHJq9XS/cNj3jZcZ\
michael@0 108 Ac9PwBIDyAeMD2O8RhhvpTFYqYpGqMQOM2UhlFOhsvjfgNJ6ofxyoZaXbHPt8mDNjDU9ACYBbyGA\
michael@0 109 AT/KZEZ/MpO5qciYyRlgROeJGSh0nQCL21Ufmx4EL8dMpqScRt4DFVAAYMCtORx+0Rhz7aFF+GJB\
michael@0 110 BmNM/JKklGo1KlBtHZ474U79P9hZOZcQYb0unD/mwu05qADCZwE4C8Y7I3kTk4kFx+mUuzfMKf5e\
michael@0 111 +rn+rUMq4PR4hFII0gw0xpdvGAWGoDqHf9m8IuV8m2Qtf1pQMPok37+50JhpHlC8EzwRcAzwOqs+\
michael@0 112 Vkv06I+da04nInd3RvuxgCIAhcUTF5zvFQ79oucP+Cy8zIjE6qQnt5Pviu5IqAogVKNCNSrBUte6\
michael@0 113 blnrqi/Vo3O9rI3Pc7cbP6sgGQcAf7rvl3zK908uBKjAGK5jrrmNKKHj/RS3E6L3V2USLUzkZAB4\
michael@0 114 i75pTivwwQMyoKYQ685+QOtScvzUHPbIlJ54ZVsuDPTrZDmnQqUQggo1qkoNRDyFeJ6XGQfjF0fW\
michael@0 115 3O9YWxW6adNzw36Dzm/JKEJ0k7QgtfiSygd1vSrkdZ3jlb6fneT7Y+MN1xrmVX9gbkw9q1MdsemF\
michael@0 116 U5wkpwqSRSw49gfZAcPPHOsVlIww/sBjjPEVnqfGZEQlWKVCjWK31TW/dv56pCruU126TGxPl+US\
michael@0 117 IrAgNQ7TQ+pNukQqfalLNimApvMt6CZMTvsiu3VOJ17XnrNWZ9m85oK8Qmz4sFB+CeXrF29dfOqG\
michael@0 118 1PwKs6fOKyvKjrnb8wrHGD8TWfCOEoX85zb96dgXY9leN2NM+y3SJZG4u7XsSldIykFPz09NHxbR\
michael@0 119 T2U3M11AsKf8aRqtnBqQoG91oWkGOS0/XaQo2Pf3u5mUDK9LukD7Mv5Tv9teSQ4VzipsINUtW9Zc\
michael@0 120 t/mFiRu7WbcOuQNP+MXQ4hGX3mEKBl1mjB9bbwAqSz6cf+TZ8Qaabta/u6hM92ItpZs5dvyor5R/\
michael@0 121 dwvp9QAa6eFzfxRlpVMk2mXh93czeyPn1Bn5ShWtYAJsyEve+OPgC7Hzmgx3USDtejQedlbtDX7h\
michael@0 122 0Ns6HChV5LcvP7rpb1+qx/690dHrtewL05c2c7ZLtrM91fOpDGjXyvT9+WYBPQAg3NPcey1n4vVt\
michael@0 123 FUJSIfGNjJZNy2ekkqzpazIJOefSoTaA9q1VY+5Wbvs9NAoYVBkFh5Sesi9lJ/u6lt5+WETpoi2M\
michael@0 124 PpZU/k9szmKGtVGRWBjQ6g3zP78pxfSGKb+tJ4LPAsi31S/+uXCUlVZmCIc+DlI15L4Cpr/1FA1d\
michael@0 125 0VLqAilzgcCGChdQc5eoTXqpkNS66hv1YLsUElURiG1sOZj7lunf3v3fwlBKjRfX9EjEHKcscV98\
michael@0 126 D40zRKIqgEpz4yvTVnfjU/VbmL/r4yhwTTbPCNsZNi8g50/OnvbCsXu5wQqVURCBuOb7seu98n7A\
michael@0 127 /L23Tc8NX8mW6pL73UoOhYPH/GJv/I7Dzlqbg5pRUG1q++A//+Ng+4f9gDlATVzLHfErZiHioKrn\
michael@0 128 H37uhgeG597sdYnIYeeszypQqQawre9dHNbd0Yj9/5KnfsB8DJpuXXj8Q+ryj3dUZglD1Uz3MsWv\
michael@0 129 HX7uh1fv6QGHn7upAmrWQpEV2zSt+bVptamw+6C9VaP/hcoHrvkABgydUjPLywy6Oboh6HW6PgLj\
michael@0 130 LYqStqYRQHKDMQflMhXOQrnata27tvGvufrEn8ZBfmdPP2AO7NpmAAw85B8qTyjKlt1svAHTjPGL\
michael@0 131 k4w0jAcTAyllnBoh9Kxw/tEdS8cuT0WyH4vX1PYD5qMBzQDE2eFDxz09zsscWuwVHX6a8YwaFAiM\
michael@0 132 NAkHr4vdUdf82rQN6JwnSl4N4vAxeKdxP2A+mjXuKTvcXcY9TdOnyxPk4zKZ/vbRAqe75C3QfZZY\
michael@0 133 0P/y6/7299z+H4QrdGsoib8JAAAAAElFTkSuQmCC"
michael@0 134 }
michael@0 135 };
michael@0 136
michael@0 137 // The process of adding a new default snippet involves:
michael@0 138 // * add a new entity to aboutHome.dtd
michael@0 139 // * add a <span/> for it in aboutHome.xhtml
michael@0 140 // * add an entry here in the proper ordering (based on spans)
michael@0 141 // The <a/> part of the snippet will be linked to the corresponding url.
michael@0 142 const DEFAULT_SNIPPETS_URLS = [
michael@0 143 "https://www.mozilla.org/firefox/features/?utm_source=snippet&utm_medium=snippet&utm_campaign=default+feature+snippet"
michael@0 144 , "https://addons.mozilla.org/firefox/?utm_source=snippet&utm_medium=snippet&utm_campaign=addons"
michael@0 145 ];
michael@0 146
michael@0 147 const SNIPPETS_UPDATE_INTERVAL_MS = 86400000; // 1 Day.
michael@0 148
michael@0 149 // IndexedDB storage constants.
michael@0 150 const DATABASE_NAME = "abouthome";
michael@0 151 const DATABASE_VERSION = 1;
michael@0 152 const SNIPPETS_OBJECTSTORE_NAME = "snippets";
michael@0 153
michael@0 154 // This global tracks if the page has been set up before, to prevent double inits
michael@0 155 let gInitialized = false;
michael@0 156 let gObserver = new MutationObserver(function (mutations) {
michael@0 157 for (let mutation of mutations) {
michael@0 158 if (mutation.attributeName == "searchEngineName") {
michael@0 159 setupSearchEngine();
michael@0 160 if (!gInitialized) {
michael@0 161 ensureSnippetsMapThen(loadSnippets);
michael@0 162 gInitialized = true;
michael@0 163 }
michael@0 164 return;
michael@0 165 }
michael@0 166 }
michael@0 167 });
michael@0 168
michael@0 169 window.addEventListener("pageshow", function () {
michael@0 170 // Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs
michael@0 171 // later and may use asynchronous getters.
michael@0 172 window.gObserver.observe(document.documentElement, { attributes: true });
michael@0 173 fitToWidth();
michael@0 174 window.addEventListener("resize", fitToWidth);
michael@0 175
michael@0 176 // Ask chrome to update snippets.
michael@0 177 var event = new CustomEvent("AboutHomeLoad", {bubbles:true});
michael@0 178 document.dispatchEvent(event);
michael@0 179 });
michael@0 180
michael@0 181 window.addEventListener("pagehide", function() {
michael@0 182 window.gObserver.disconnect();
michael@0 183 window.removeEventListener("resize", fitToWidth);
michael@0 184 });
michael@0 185
michael@0 186 // This object has the same interface as Map and is used to store and retrieve
michael@0 187 // the snippets data. It is lazily initialized by ensureSnippetsMapThen(), so
michael@0 188 // be sure its callback returned before trying to use it.
michael@0 189 let gSnippetsMap;
michael@0 190 let gSnippetsMapCallbacks = [];
michael@0 191
michael@0 192 /**
michael@0 193 * Ensure the snippets map is properly initialized.
michael@0 194 *
michael@0 195 * @param aCallback
michael@0 196 * Invoked once the map has been initialized, gets the map as argument.
michael@0 197 * @note Snippets should never directly manage the underlying storage, since
michael@0 198 * it may change inadvertently.
michael@0 199 */
michael@0 200 function ensureSnippetsMapThen(aCallback)
michael@0 201 {
michael@0 202 if (gSnippetsMap) {
michael@0 203 aCallback(gSnippetsMap);
michael@0 204 return;
michael@0 205 }
michael@0 206
michael@0 207 // Handle multiple requests during the async initialization.
michael@0 208 gSnippetsMapCallbacks.push(aCallback);
michael@0 209 if (gSnippetsMapCallbacks.length > 1) {
michael@0 210 // We are already updating, the callbacks will be invoked when done.
michael@0 211 return;
michael@0 212 }
michael@0 213
michael@0 214 let invokeCallbacks = function () {
michael@0 215 if (!gSnippetsMap) {
michael@0 216 gSnippetsMap = Object.freeze(new Map());
michael@0 217 }
michael@0 218
michael@0 219 for (let callback of gSnippetsMapCallbacks) {
michael@0 220 callback(gSnippetsMap);
michael@0 221 }
michael@0 222 gSnippetsMapCallbacks.length = 0;
michael@0 223 }
michael@0 224
michael@0 225 let openRequest = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
michael@0 226
michael@0 227 openRequest.onerror = function (event) {
michael@0 228 // Try to delete the old database so that we can start this process over
michael@0 229 // next time.
michael@0 230 indexedDB.deleteDatabase(DATABASE_NAME);
michael@0 231 invokeCallbacks();
michael@0 232 };
michael@0 233
michael@0 234 openRequest.onupgradeneeded = function (event) {
michael@0 235 let db = event.target.result;
michael@0 236 if (!db.objectStoreNames.contains(SNIPPETS_OBJECTSTORE_NAME)) {
michael@0 237 db.createObjectStore(SNIPPETS_OBJECTSTORE_NAME);
michael@0 238 }
michael@0 239 }
michael@0 240
michael@0 241 openRequest.onsuccess = function (event) {
michael@0 242 let db = event.target.result;
michael@0 243
michael@0 244 db.onerror = function (event) {
michael@0 245 invokeCallbacks();
michael@0 246 }
michael@0 247
michael@0 248 db.onversionchange = function (event) {
michael@0 249 event.target.close();
michael@0 250 invokeCallbacks();
michael@0 251 }
michael@0 252
michael@0 253 let cache = new Map();
michael@0 254 let cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME)
michael@0 255 .objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor();
michael@0 256 cursorRequest.onerror = function (event) {
michael@0 257 invokeCallbacks();
michael@0 258 }
michael@0 259
michael@0 260 cursorRequest.onsuccess = function(event) {
michael@0 261 let cursor = event.target.result;
michael@0 262
michael@0 263 // Populate the cache from the persistent storage.
michael@0 264 if (cursor) {
michael@0 265 cache.set(cursor.key, cursor.value);
michael@0 266 cursor.continue();
michael@0 267 return;
michael@0 268 }
michael@0 269
michael@0 270 // The cache has been filled up, create the snippets map.
michael@0 271 gSnippetsMap = Object.freeze({
michael@0 272 get: function (aKey) cache.get(aKey),
michael@0 273 set: function (aKey, aValue) {
michael@0 274 db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
michael@0 275 .objectStore(SNIPPETS_OBJECTSTORE_NAME).put(aValue, aKey);
michael@0 276 return cache.set(aKey, aValue);
michael@0 277 },
michael@0 278 has: function (aKey) cache.has(aKey),
michael@0 279 delete: function (aKey) {
michael@0 280 db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
michael@0 281 .objectStore(SNIPPETS_OBJECTSTORE_NAME).delete(aKey);
michael@0 282 return cache.delete(aKey);
michael@0 283 },
michael@0 284 clear: function () {
michael@0 285 db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
michael@0 286 .objectStore(SNIPPETS_OBJECTSTORE_NAME).clear();
michael@0 287 return cache.clear();
michael@0 288 },
michael@0 289 get size() cache.size
michael@0 290 });
michael@0 291
michael@0 292 setTimeout(invokeCallbacks, 0);
michael@0 293 }
michael@0 294 }
michael@0 295 }
michael@0 296
michael@0 297 function onSearchSubmit(aEvent)
michael@0 298 {
michael@0 299 let searchTerms = document.getElementById("searchText").value;
michael@0 300 let engineName = document.documentElement.getAttribute("searchEngineName");
michael@0 301
michael@0 302 if (engineName && searchTerms.length > 0) {
michael@0 303 // Send an event that will perform a search and Firefox Health Report will
michael@0 304 // record that a search from about:home has occurred.
michael@0 305 let eventData = JSON.stringify({
michael@0 306 engineName: engineName,
michael@0 307 searchTerms: searchTerms
michael@0 308 });
michael@0 309 let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
michael@0 310 document.dispatchEvent(event);
michael@0 311 }
michael@0 312
michael@0 313 aEvent.preventDefault();
michael@0 314 }
michael@0 315
michael@0 316
michael@0 317 function setupSearchEngine()
michael@0 318 {
michael@0 319 // The "autofocus" attribute doesn't focus the form element
michael@0 320 // immediately when the element is first drawn, so the
michael@0 321 // attribute is also used for styling when the page first loads.
michael@0 322 let searchText = document.getElementById("searchText");
michael@0 323 searchText.addEventListener("blur", function searchText_onBlur() {
michael@0 324 searchText.removeEventListener("blur", searchText_onBlur);
michael@0 325 searchText.removeAttribute("autofocus");
michael@0 326 });
michael@0 327
michael@0 328 let searchEngineName = document.documentElement.getAttribute("searchEngineName");
michael@0 329 let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
michael@0 330 let logoElt = document.getElementById("searchEngineLogo");
michael@0 331
michael@0 332 // Add search engine logo.
michael@0 333 if (searchEngineInfo && searchEngineInfo.image) {
michael@0 334 logoElt.parentNode.hidden = false;
michael@0 335 logoElt.src = searchEngineInfo.image;
michael@0 336 logoElt.alt = searchEngineName;
michael@0 337 searchText.placeholder = "";
michael@0 338 }
michael@0 339 else {
michael@0 340 logoElt.parentNode.hidden = true;
michael@0 341 searchText.placeholder = searchEngineName;
michael@0 342 }
michael@0 343
michael@0 344 }
michael@0 345
michael@0 346 /**
michael@0 347 * Inform the test harness that we're done loading the page.
michael@0 348 */
michael@0 349 function loadSucceeded()
michael@0 350 {
michael@0 351 var event = new CustomEvent("AboutHomeLoadSnippetsSucceeded", {bubbles:true});
michael@0 352 document.dispatchEvent(event);
michael@0 353 }
michael@0 354
michael@0 355 /**
michael@0 356 * Update the local snippets from the remote storage, then show them through
michael@0 357 * showSnippets.
michael@0 358 */
michael@0 359 function loadSnippets()
michael@0 360 {
michael@0 361 if (!gSnippetsMap)
michael@0 362 throw new Error("Snippets map has not properly been initialized");
michael@0 363
michael@0 364 // Allow tests to modify the snippets map before using it.
michael@0 365 var event = new CustomEvent("AboutHomeLoadSnippets", {bubbles:true});
michael@0 366 document.dispatchEvent(event);
michael@0 367
michael@0 368 // Check cached snippets version.
michael@0 369 let cachedVersion = gSnippetsMap.get("snippets-cached-version") || 0;
michael@0 370 let currentVersion = document.documentElement.getAttribute("snippetsVersion");
michael@0 371 if (cachedVersion < currentVersion) {
michael@0 372 // The cached snippets are old and unsupported, restart from scratch.
michael@0 373 gSnippetsMap.clear();
michael@0 374 }
michael@0 375
michael@0 376 // Check last snippets update.
michael@0 377 let lastUpdate = gSnippetsMap.get("snippets-last-update");
michael@0 378 let updateURL = document.documentElement.getAttribute("snippetsURL");
michael@0 379 let shouldUpdate = !lastUpdate ||
michael@0 380 Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
michael@0 381 if (updateURL && shouldUpdate) {
michael@0 382 // Try to update from network.
michael@0 383 let xhr = new XMLHttpRequest();
michael@0 384 try {
michael@0 385 xhr.open("GET", updateURL, true);
michael@0 386 } catch (ex) {
michael@0 387 showSnippets();
michael@0 388 loadSucceeded();
michael@0 389 return;
michael@0 390 }
michael@0 391 // Even if fetching should fail we don't want to spam the server, thus
michael@0 392 // set the last update time regardless its results. Will retry tomorrow.
michael@0 393 gSnippetsMap.set("snippets-last-update", Date.now());
michael@0 394 xhr.onerror = function (event) {
michael@0 395 showSnippets();
michael@0 396 };
michael@0 397 xhr.onload = function (event)
michael@0 398 {
michael@0 399 if (xhr.status == 200) {
michael@0 400 gSnippetsMap.set("snippets", xhr.responseText);
michael@0 401 gSnippetsMap.set("snippets-cached-version", currentVersion);
michael@0 402 }
michael@0 403 showSnippets();
michael@0 404 loadSucceeded();
michael@0 405 };
michael@0 406 xhr.send(null);
michael@0 407 } else {
michael@0 408 showSnippets();
michael@0 409 loadSucceeded();
michael@0 410 }
michael@0 411 }
michael@0 412
michael@0 413 /**
michael@0 414 * Shows locally cached remote snippets, or default ones when not available.
michael@0 415 *
michael@0 416 * @note: snippets should never invoke showSnippets(), or they may cause
michael@0 417 * a "too much recursion" exception.
michael@0 418 */
michael@0 419 let _snippetsShown = false;
michael@0 420 function showSnippets()
michael@0 421 {
michael@0 422 let snippetsElt = document.getElementById("snippets");
michael@0 423
michael@0 424 // Show about:rights notification, if needed.
michael@0 425 let showRights = document.documentElement.getAttribute("showKnowYourRights");
michael@0 426 if (showRights) {
michael@0 427 let rightsElt = document.getElementById("rightsSnippet");
michael@0 428 let anchor = rightsElt.getElementsByTagName("a")[0];
michael@0 429 anchor.href = "about:rights";
michael@0 430 snippetsElt.appendChild(rightsElt);
michael@0 431 rightsElt.removeAttribute("hidden");
michael@0 432 return;
michael@0 433 }
michael@0 434
michael@0 435 if (!gSnippetsMap)
michael@0 436 throw new Error("Snippets map has not properly been initialized");
michael@0 437 if (_snippetsShown) {
michael@0 438 // There's something wrong with the remote snippets, just in case fall back
michael@0 439 // to the default snippets.
michael@0 440 showDefaultSnippets();
michael@0 441 throw new Error("showSnippets should never be invoked multiple times");
michael@0 442 }
michael@0 443 _snippetsShown = true;
michael@0 444
michael@0 445 let snippets = gSnippetsMap.get("snippets");
michael@0 446 // If there are remotely fetched snippets, try to to show them.
michael@0 447 if (snippets) {
michael@0 448 // Injecting snippets can throw if they're invalid XML.
michael@0 449 try {
michael@0 450 snippetsElt.innerHTML = snippets;
michael@0 451 // Scripts injected by innerHTML are inactive, so we have to relocate them
michael@0 452 // through DOM manipulation to activate their contents.
michael@0 453 Array.forEach(snippetsElt.getElementsByTagName("script"), function(elt) {
michael@0 454 let relocatedScript = document.createElement("script");
michael@0 455 relocatedScript.type = "text/javascript;version=1.8";
michael@0 456 relocatedScript.text = elt.text;
michael@0 457 elt.parentNode.replaceChild(relocatedScript, elt);
michael@0 458 });
michael@0 459 return;
michael@0 460 } catch (ex) {
michael@0 461 // Bad content, continue to show default snippets.
michael@0 462 }
michael@0 463 }
michael@0 464
michael@0 465 showDefaultSnippets();
michael@0 466 }
michael@0 467
michael@0 468 /**
michael@0 469 * Clear snippets element contents and show default snippets.
michael@0 470 */
michael@0 471 function showDefaultSnippets()
michael@0 472 {
michael@0 473 // Clear eventual contents...
michael@0 474 let snippetsElt = document.getElementById("snippets");
michael@0 475 snippetsElt.innerHTML = "";
michael@0 476
michael@0 477 // ...then show default snippets.
michael@0 478 let defaultSnippetsElt = document.getElementById("defaultSnippets");
michael@0 479 let entries = defaultSnippetsElt.querySelectorAll("span");
michael@0 480 // Choose a random snippet. Assume there is always at least one.
michael@0 481 let randIndex = Math.floor(Math.random() * entries.length);
michael@0 482 let entry = entries[randIndex];
michael@0 483 // Inject url in the eventual link.
michael@0 484 if (DEFAULT_SNIPPETS_URLS[randIndex]) {
michael@0 485 let links = entry.getElementsByTagName("a");
michael@0 486 // Default snippets can have only one link, otherwise something is messed
michael@0 487 // up in the translation.
michael@0 488 if (links.length == 1) {
michael@0 489 links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
michael@0 490 }
michael@0 491 }
michael@0 492 // Move the default snippet to the snippets element.
michael@0 493 snippetsElt.appendChild(entry);
michael@0 494 }
michael@0 495
michael@0 496 function fitToWidth() {
michael@0 497 if (window.scrollMaxX) {
michael@0 498 document.body.setAttribute("narrow", "true");
michael@0 499 } else if (document.body.hasAttribute("narrow")) {
michael@0 500 document.body.removeAttribute("narrow");
michael@0 501 fitToWidth();
michael@0 502 }
michael@0 503 }

mercurial