browser/base/content/abouthome/aboutHome.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/abouthome/aboutHome.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,503 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +const SEARCH_ENGINES = {
     1.9 +  "Google": {
    1.10 +    // This is the "2x" image designed for OS X retina resolution, Windows at 192dpi, etc.;
    1.11 +    // it will be scaled down as necessary on lower-dpi displays.
    1.12 +    // This needs to be defined in a single line to keep the JS parser from creating many
    1.13 +    // intermediate strings in memory.  See bug 986672.
    1.14 +    image: "data:image/png;base64,\
    1.15 +iVBORw0KGgoAAAANSUhEUgAAAIwAAAA4CAYAAAAvmxBdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ\
    1.16 +bWFnZVJlYWR5ccllPAAAGrFJREFUeNrtfHt4VdW172+utZOASLJ5+BaIFrUeXkFsa0Fl++gDnznV\
    1.17 +VlvFxt7aqvUUarXtse3Bau35ak/rZ9XT26NtfOvV6wFET+FYCQEKWqsQIT5RCAgSXnlnrzXneNw/\
    1.18 +1lphJSSQ8BB7bub3zW+LO3uN+fiNMcf4jTEX0N/6W3/rb/2tv30smtnXB3zmRi2FQakxQNKX3WkW\
    1.19 +9S/tgW3HLpmQM543A0BWVSHMYGIwOTDxzxrOf3/RQQfMZ2/SLAvKhTFVBGUqKFONH2QAzwOMF38a\
    1.20 +wHhYZAxWAqhe/iszp3+b970d/sInc57vz/J8L2eMB2MAEYkBQ6DQ3dRw4dq7AUjcP3rAfPZmLWXC\
    1.21 +LHKoIAcQAUxaB5EaEfc6AEBhjDEwmcx43/fO9HxT4vkReBIAAZgjgodW3NcPnn1sHgD/iHknn+0d\
    1.22 +6s8XEUhsXXac/34WAAGw8afuT8GZ3X055YeSJcIsG+pMZwFn0UihezRofPt3G54f/0E8cNMN+Myo\
    1.23 +8jVTCgYd823PLzrPeIBnABiUQ1F+UoWsVOYb33mkoKp/7/dKyT0AGc47X4s0sjBEoLxbBqAQAMfW\
    1.24 +Rfe38B4BM+VHUkYOs8mi1FrABbK4dcvK73zwp1M3xYPOxANKBqbpCdXNGb0UwPKRF74xpfDQ0t+K\
    1.25 +54+IvlKoahmAhaO/mv/ZmicG3tqPgT61ZM2dZMQJOYhIdByRM/F3dCCOox4Bc3oEliqyyNoQCPPu\
    1.26 +sXceKZqRsigu7pwaWBowiRb46+f9Q1V2wl1nDx09/R7jF30x9adNlN8yPx4DHwht+B/cBIBoRqeI\
    1.27 +E4hE/oshTcB0wNbT6/o/zrhFyohR5ZxmrVWE+fDxdx4puhGAH4OkPe5B6pykeJAc/7cDEMZ/095Y\
    1.28 +870P339m+BXs2v4kbCFsm9u2vnpJ3bzR7wAo2B/R2v+PjSnyXcRxtOLUSXFxwAFz5i2SZUIVO82S\
    1.29 +BWye/vLOIwNvjL8OYqCEfXCmJAZPHkC7sK1REbj2+lmbq86qTVmmfuuyN2cTiREWKCvACgml9kDL\
    1.30 +7HQksehsZmSdA6yVpsa6P38v3swg7m4vN1dGXrThKGP8yS5fP33j/LEvxKDbl2f2A0YFCtkZQDOa\
    1.31 +PjLAnP4jrmBGjh1AVhG2ttxfX33++vjY2eeNXf/siLUAzgEwMJZrY2vF/Vu/t4BRqCqgCmj07wMV\
    1.32 +HXUCzJQfUlZE72ICnANcqNj21h8eiK1AX46gXh29KT9H+rd9XxBjYGCgig7QHOgjPgMAKigXQZYp\
    1.33 +si4uCOc3v35zY2wF9ufGSgxA7fdd9g8ho9ol4P4ojiQWnSUMMANECrJNy1NWYH8eGfsEvJbLv1IK\
    1.34 +1XIAUwEtA0xplJMwjcaYlTDeShg8dOgjj6/cJxNYfWIWkHJoh5yyjkSZ8RbB89YBZq4/pXafGeuz\
    1.35 +b9WciXJxo2B2houqgAjABJCLOwFMqFv57+bBxMIAJm1det3avnl1OYCLAeSgWhofaY1QXQSRuYc+\
    1.36 +/OiD3QLmUzNdqTBKhRVMADsF5beuToXJB90KtFz+lVIVniXOVUAUqjpXVB4WwPjGTPB8/0zjeTnj\
    1.37 +ezl43szmKy6vNkDF4MeeXNc3oJyUhfAMkJsJkSxUVrLos6o6z/O8Ucb3phrPzyHKeVTwkpPXseg3\
    1.38 +Cqe+1SfG+swfaw6KGTAoJ5eyGF3IBeEIJB2AcXxb0FI/L45uFQBMGiu6Z3ai9eqrclBUClFWVatV\
    1.39 +5GERNT5wEVQnQLUcIuVNX75kFjn60rA5c1d0AoywlkcxfdwZ2LSgbOmBZAv70povu7RcyFUqcZYd\
    1.40 +Pbxix44fnLv8pbYUOWh+P3ZM9uJRo34xoLDgq8b3YTxvqhqsaPzyJTdmn36msjdyqPqkMhWqBFGZ\
    1.41 +MtV8uDX4zMjp2zemyEoPgGn4zyOvGzy48A54GcD3Sz1jFrqqE+4uOOvdmb0ASlYEs5mQE9afUdhy\
    1.42 +0yv3lHzwya/8ZcjgI0+5yssU3QKYkgQ4Ivp60LL1n8kBQfOWuvdnj6uLldgHQKoKxU7HV/eg2y1X\
    1.43 +XXmXEs1U0ZVb29o//4k5c5P5eQB+s+68aVeUFBTcCxUoS6kRWfjhueecc9SfX3ytA9QTr7eVACqY\
    1.44 +FDYEwnbB2qcHHg6gLY6ODhpomi77coUyVaojhKH9+ZHzF/wqXiztEg34APxNX/jCvQOLCi83fpy8\
    1.45 +UsCJXHLYnGdn785S0uKTyyBUBXJZcW5x4bSN56ciyLQcD4Bf/+ThVwwbUvRb+JkoswqAWX5b9Lm1\
    1.46 +M3uSM/UnUiaCKiZk2blvvnxX0ePxuBNAmpMur51wyLBPzjVeBBoVwIXBk6vuP+SG+LkcuwkWAA96\
    1.47 +/JjZKnKxkACkkFb5Nztz220xX9bJlWi+6opKFalQlpqlmzZNu6B6SaJ0knKJ/DW5qd8p8TO3x6AB\
    1.48 +qza1EE06cdmy9wDAY5LjmBTMkQnUnZ42H0ywNF52aU6FK4UY5NySI+cv+E3MCnMM5HyqtwFoO3rB\
    1.49 +gmuDMFjGjiCOIEQwzH9c+7lzju+JTaYlJ2ehUqXMWWFqeurFxqsAFMVf25Ss9kTOEZdvebClJbxT\
    1.50 +yUGZoEzwlL/b9tzRX+pOztSfSBZApSqyIrL45buKnkaUJEzLCN5+csxr+ab6fyILkI2OIZYBlx9/\
    1.51 +2bYvpLgw2+EqKLKdwoceVKJp+tfuEpYKZcaW1tZbLqheEsbj3GV+oxdV3x0GwQZrHUIiWKIST3Vm\
    1.52 +DG54zFrKrBBWiGgSyx9Uv6Xh0n/MKlGlOII4h80trQ+kuJt8HGklZHg6FZF/Y/uOb7O1YOvAzkGt\
    1.53 +Kxmoehe6SYNEpkErwZIFC4I2fuLKf2tLtDOPzumPhA6wAPJDLt1yuzjaAEcAMUCMApXfvPP7IcO6\
    1.54 +gkYFs4RRpgy49qanUsAPu/T8W48e/YwL6S/kYtBYwM8U/yu6KVlQUShr9CkKyK7b1vDVy0qVeaYy\
    1.55 +gaxbdeK85/8a/z7sYR3zgXM1gXUInEPoCEw8PR6z8YQxaidQPh6RrgrPEOZS4chKjFuydEEKFD1x\
    1.56 +QgrAnfO3V98Jw/B5dhFgmByU+MK/nnrq6K6gcQtPyqlIubJAibCxPv/fsVVNgCI9yGEAQdBq71NH\
    1.57 +UEdQIoBo5PBBeklazuQfSpYFM0UAFsDmd2yMf9+1XkUT3otc8AiRwpFChCBCI0detGbSLtYr5uw6\
    1.58 +tk26XctZwgxhRt65ZSmr1t389M1Jk85wzKcHRAiJkCfasDnI/0sMGN+jlLMrAigMhp0+f+TBBIw4\
    1.59 +milEYOcQBHZZAoZeEIgKgIIgeJbD2MqEFhxaDAFmdAWMisxQFigzlAUnX9e4rA9yeHuTna3koBQB\
    1.60 +RogxwOPvxNbQAAA7VHQEFKSQKEFIu4lA5d3HiiuFNB4XQZlhUHBK11QO0oRdD7ouROVCkeJZG7ak\
    1.61 +/KBOYHlz4sTy1WVlVY5oYego2+bs82+3tFw6YcVrp01dteqpxNfyhKQuGlxCMSsKBh570ABT/8XP\
    1.62 +5dhRVpyDWAd2Ns0O9yrhWdfcMpvCEByEoNCCwhBgvgBdM+PM5TH5FPW+1ZLo8de2viehe12dhVoH\
    1.63 +OAtDPO61O4o+kYCTnE5wVuGsxlzKHul7BUDKdomKgwpB2QHAyNiP2Dl+0Z2WRXZ9YP0F55WJczvX\
    1.64 +0jp09U3fLiurWD1+/NqQaHZIVNbu3O1vt7aM+fSqVRWXvPvu0pRldwAkQ5brjO+NMh0kgMIvGjYZ\
    1.65 +wIKETPxIrYt1U5M8iThKJil9yZGc++ab298dP36Jb8wZohqhQHRErKEeAA6fG5FT5yIlYYI6tzfO\
    1.66 +vtiQni3MYDw0ChqEgUMyejyAdwGwDeW4ZI9FAGQOmwzgv/cERmZbDXhnKBNUGMJkUhGVduSSJJ1P\
    1.67 +6rw8HIalJo7ilBkchgCgL48fVzLceDc4kZnWUdap1AQi10x+660n4jXyk1M7ZXEZgHhMUkMO4Njp\
    1.68 +hQGMf8h56Fx++ZE1a+1xZC2Szjs3sk9uUEhUbSMvP3LeyOGZ0tKJiearo1J1DHVRPYmS7JUcG2g1\
    1.69 +pxxUsooBnpmQWAOb10YbKGygcKFCZOC0XqxrRKokCBQG5euX77In2k1P+2hhWEZBAAoCuCCEcW7E\
    1.70 +2xMn/m6oYo0jyjnmuc3Off6UN96YMvmtt5LILSmQ61r3xAA0I+xqPBiIejAd1f7e2MPPfvm4LQs/\
    1.71 +89a+bP6nZuSzfsaU+T7g+UBixYQVRFGS01kFO22srRy0EgA4CEvFRHS3MANMY/fGbybmlQqAFSBV\
    1.72 +sCp8kWwCGA5dqefFShnnRV77ecHYU37iXuqLoB0tsuIo34v3NfJR1GlJsrnOuiXGy1y8k+rwxh57\
    1.73 +3srSD/6rbLdra7yMqgjUCGAULR8uWr0LJPYAGApCeCbKNygLPKIxJ65YOSU+YpLUUCYGiqBzQVy3\
    1.74 +Ft1zbevnJl60UARqACgcVDo9ZZr63Mqua68QxlpmrWJC1FmrmLSKCFVktcpZrbKhzg4D26E5Lgjg\
    1.75 +8vnoMwwh1hU/dvTRo/qcDyJqcESw5Dp6o3XNHVrqLDSubAdFjuXwwWZcX+Wc9APboKxQUoiLurXa\
    1.76 +IYfCpjlCDsoxZ6OCouLRt+xpbY3nA8aDMR6E2+9vffOWxl02cQ+Bbdjevt7l83D5ABRaKNHYO484\
    1.77 +YmgMkoJ4jElCOL8Lz9NN87YumrRDxc2DElQZKgIVhZcZcO1hZ74wtK/H0thvtuXGXdM2S0S/ziQ1\
    1.78 +FPJiG7pHwvbgDhtKnQ0VNhCEeUHQLmiuf2fymieGvJGY8DCfX+yCEC5xWIlwtO+P6+s4VESJGS4+\
    1.79 +liwxKjZ/2FGRZvPhYgktxEZdHWOAr2P34ihWIQWTgJ2CnWJbo9Ymz1g/5+h1QsF9wgKJ19Z4hV87\
    1.80 +4fKNE3cnx8v4V8H4UOjqhvce+zW6qdWVlOvSjQsDlw/WUT4A5QNQGIJDizMPHXR+CiRBb4GSzlYr\
    1.81 +26Z7vYKSC42nUOPBqA9VU1I0ZOJPEYWj1NvVW/3AoEUAFgO4IzZ1hYk2jf9WUw7IjCIXHUVhXrFp\
    1.82 +/sQtKZPIoXXr/PjoSkZeoHo6gP/bFyeciECqcHG3IrXp37a2SF3xQNPxRAXgq5nS1bHsDWCYALYA\
    1.83 +u+h0W/impI8Pad9ec/vAoWVTjV84Nsn5FAwcvmDMN5rOqf1jyatdHzjuGjvThloKYH3b5qVXt775\
    1.84 +44ZuN1QEKknF3a6ImfDee4tWjBrV6R5Qoeq1AP6Avaxx8gDolhdPXAh2qzQmZFQ4ZhALrj/mvLpT\
    1.85 ++qhxya0BP5VVZQBkA6jNR0AJ2xUUcjKGjsx4k3PVYUwaJU6rJ3reLiHlHppjBjF3fLYSzU/noEZ8\
    1.86 +3611VusoVJBVsFWAdezim/3jemSFe+SNIsvCpAhCXf7TBZI+PnTr4nO2t2xcME3ZroYKIouEEqDo\
    1.87 +xfHfav/GxOttFgBOucGWll0XVqrqXYDWNLz3aG7bsovWp4i2TvkhScLqNBezq/M/zxLBxV2Yx/75\
    1.88 +yCPP6usc04CJ+B3bcLMwQTiK+0UIwgz1ip8+4pyaYX0x0SnWMkjnYGygkm9nBO0MGzoI2TTDyQBw\
    1.89 +7ubNawPmeZYZNt5wZhrxX8OHX9yXSTJzGcVgIWasbs8/hc7XRzXM670cg0Vs5H+MHm6u74ucrb/K\
    1.90 +lAlFPoySoqFFn+rm+OCGV762df2cYWe4fP0M5qDWhoowRIm1/h+s1YZx3wrVOV1LDhXMaGzfXntF\
    1.91 +46vXtMQRS/clsqRRT9SNd0GMBo6edRStZbKeg4D//ciQIcP2CTDbqsdVKQePq1JMFkXxv4qO9AaM\
    1.92 +fPGoaeuG9kXp0LkU0wGgMFC1gYAdAeyg0m3IrE3W3mtTvodjRpHq9X3xL4h5Qsq63P/z9ra6LqSc\
    1.93 +vvmBPkwOTex2lnf4wNee/47fa99NGGVJ8Zl1qP3UPfwkdr15mDDV+Y3Pf+Kh9c9kz9pee89J7dve\
    1.94 +vaRt+7qLbVv47y5UUKggp3BB/okNz0/aHI8332OaIgELxWDpptQtt6X+Qcu03nVYGQYxjxzl+7/e\
    1.95 +GyvjdYrCtv31JiW7QTjy6qWj83jF4AeP/MLaodiHRtZBXAihEEIWkq4eSgGmvKGhqpX5d1YEVhiW\
    1.96 +BaI6Zf6QITN7s5ELhw4tZZavkwhIZMOC1rZfo5s64nPv4+1NzXot2/hYiqKckglH4/7eRojCOosp\
    1.97 +St6u2ijfS1Hv3I0SdVy5aam9ecumBeOqN8w7aRkxSlMVdRDmRHa4m5xWPKPEusUA6maIrcy/cCKw\
    1.98 +InASKaCoXrlo2LAH+xpMpAEjLauu2ObaNnxVmZqUHaI8SaR+KnIhTPHCo6ZtOn6vk4qUPNNGnV2P\
    1.99 +J0ptENweMq92zHBMcMwwIrfMLS6etKdJEnMlCYOZm9YE4dUPkWvsIUckJ/+SZwd5PCEOEBc5rh7j\
   1.100 +grqf+VfvSc7mO/xZSihVAra3YMY/PqqrUhZVe7C8yRHTBqAVQJuQN5idgJ2ASQAz4PJjptWevKc0\
   1.101 +RZQ0TQATRWDd/dmFDQ2VeaLH0z4dRVTK9EXZ7IqFJSXH7W6eLw0blntp2NAydGOSqPGVs/5mW9Zc\
   1.102 +JGKbRSxELIRDCFuIuAmiBa8eMW37rcdc1JDtM+3PYdSp43k9/ulPgmDrsnz+vFBktRWBZYEVKSlU\
   1.103 +feH5wYPP7u5Hfy4uzi4oLq50IjkSaXrf2vIfBPnV6PlKiwKg0XfyNe2BPkmJ8+oUGeh/bLjNu7En\
   1.104 +0Gy+w5sppLcyKRra9IZJ98hTvciop9MPSSFUwGTnEjHICsgpyKHYHzjquWMvrJ+wewUENPFjCIAx\
   1.105 +k3uStyIMbw5FVieWJvJpBE5kgqq+X1VcPGdRcfHMxSUluSUlJbmlUZ+1tKRkLRGVnrZ9Rw12rSLt\
   1.106 +sDpFg8vmfbpw0HH3wcuMMSaiao2XAbwMjPFhPL/ReN6DfsY8tHHekN0WXR929vqsCpWruFshPEqF\
   1.107 +o3IyADuWTxgea1rYTbRVeEMmc+SnCwp+OcB4l3kmLq0D4BnzkA/MMUBjvDMXC1DBqlkCFr9N9E//\
   1.108 +HIZpPyDsQVuTFwsMfP273k8GFeLbvo9izwe8DGA8VMPgIc/D2piALlPFDGWUMqNuazOun/RbeQU7\
   1.109 +L/zl0cfC+SPOXjG84NBRawCvJNoSE7PiBgr5Xx/MKf7jLnzIbUPKlHVF5C11KgJfD9+shY8Vxjd3\
   1.110 +0780rEvP8bFDDvnVQGO+lU5MeTDwzM5aTbOzNyrw/XNbWx9JFLknk+sjqjobUHJq9XS/cNj3jZcZ\
   1.111 +Ac9PwBIDyAeMD2O8RhhvpTFYqYpGqMQOM2UhlFOhsvjfgNJ6ofxyoZaXbHPt8mDNjDU9ACYBbyGA\
   1.112 +AT/KZEZ/MpO5qciYyRlgROeJGSh0nQCL21Ufmx4EL8dMpqScRt4DFVAAYMCtORx+0Rhz7aFF+GJB\
   1.113 +BmNM/JKklGo1KlBtHZ474U79P9hZOZcQYb0unD/mwu05qADCZwE4C8Y7I3kTk4kFx+mUuzfMKf5e\
   1.114 ++rn+rUMq4PR4hFII0gw0xpdvGAWGoDqHf9m8IuV8m2Qtf1pQMPok37+50JhpHlC8EzwRcAzwOqs+\
   1.115 +Vkv06I+da04nInd3RvuxgCIAhcUTF5zvFQ79oucP+Cy8zIjE6qQnt5Pviu5IqAogVKNCNSrBUte6\
   1.116 +blnrqi/Vo3O9rI3Pc7cbP6sgGQcAf7rvl3zK908uBKjAGK5jrrmNKKHj/RS3E6L3V2USLUzkZAB4\
   1.117 +i75pTivwwQMyoKYQ685+QOtScvzUHPbIlJ54ZVsuDPTrZDmnQqUQggo1qkoNRDyFeJ6XGQfjF0fW\
   1.118 +3O9YWxW6adNzw36Dzm/JKEJ0k7QgtfiSygd1vSrkdZ3jlb6fneT7Y+MN1xrmVX9gbkw9q1MdsemF\
   1.119 +U5wkpwqSRSw49gfZAcPPHOsVlIww/sBjjPEVnqfGZEQlWKVCjWK31TW/dv56pCruU126TGxPl+US\
   1.120 +IrAgNQ7TQ+pNukQqfalLNimApvMt6CZMTvsiu3VOJ17XnrNWZ9m85oK8Qmz4sFB+CeXrF29dfOqG\
   1.121 +1PwKs6fOKyvKjrnb8wrHGD8TWfCOEoX85zb96dgXY9leN2NM+y3SJZG4u7XsSldIykFPz09NHxbR\
   1.122 +T2U3M11AsKf8aRqtnBqQoG91oWkGOS0/XaQo2Pf3u5mUDK9LukD7Mv5Tv9teSQ4VzipsINUtW9Zc\
   1.123 +t/mFiRu7WbcOuQNP+MXQ4hGX3mEKBl1mjB9bbwAqSz6cf+TZ8Qaabta/u6hM92ItpZs5dvyor5R/\
   1.124 +dwvp9QAa6eFzfxRlpVMk2mXh93czeyPn1Bn5ShWtYAJsyEve+OPgC7Hzmgx3USDtejQedlbtDX7h\
   1.125 +0Ns6HChV5LcvP7rpb1+qx/690dHrtewL05c2c7ZLtrM91fOpDGjXyvT9+WYBPQAg3NPcey1n4vVt\
   1.126 +FUJSIfGNjJZNy2ekkqzpazIJOefSoTaA9q1VY+5Wbvs9NAoYVBkFh5Sesi9lJ/u6lt5+WETpoi2M\
   1.127 +PpZU/k9szmKGtVGRWBjQ6g3zP78pxfSGKb+tJ4LPAsi31S/+uXCUlVZmCIc+DlI15L4Cpr/1FA1d\
   1.128 +0VLqAilzgcCGChdQc5eoTXqpkNS66hv1YLsUElURiG1sOZj7lunf3v3fwlBKjRfX9EjEHKcscV98\
   1.129 +D40zRKIqgEpz4yvTVnfjU/VbmL/r4yhwTTbPCNsZNi8g50/OnvbCsXu5wQqVURCBuOb7seu98n7A\
   1.130 +/L23Tc8NX8mW6pL73UoOhYPH/GJv/I7Dzlqbg5pRUG1q++A//+Ng+4f9gDlATVzLHfErZiHioKrn\
   1.131 +H37uhgeG597sdYnIYeeszypQqQawre9dHNbd0Yj9/5KnfsB8DJpuXXj8Q+ryj3dUZglD1Uz3MsWv\
   1.132 +HX7uh1fv6QGHn7upAmrWQpEV2zSt+bVptamw+6C9VaP/hcoHrvkABgydUjPLywy6Oboh6HW6PgLj\
   1.133 +LYqStqYRQHKDMQflMhXOQrnata27tvGvufrEn8ZBfmdPP2AO7NpmAAw85B8qTyjKlt1svAHTjPGL\
   1.134 +k4w0jAcTAyllnBoh9Kxw/tEdS8cuT0WyH4vX1PYD5qMBzQDE2eFDxz09zsscWuwVHX6a8YwaFAiM\
   1.135 +NAkHr4vdUdf82rQN6JwnSl4N4vAxeKdxP2A+mjXuKTvcXcY9TdOnyxPk4zKZ/vbRAqe75C3QfZZY\
   1.136 +0P/y6/7299z+H4QrdGsoib8JAAAAAElFTkSuQmCC"
   1.137 +  }
   1.138 +};
   1.139 +
   1.140 +// The process of adding a new default snippet involves:
   1.141 +//   * add a new entity to aboutHome.dtd
   1.142 +//   * add a <span/> for it in aboutHome.xhtml
   1.143 +//   * add an entry here in the proper ordering (based on spans)
   1.144 +// The <a/> part of the snippet will be linked to the corresponding url.
   1.145 +const DEFAULT_SNIPPETS_URLS = [
   1.146 +  "https://www.mozilla.org/firefox/features/?utm_source=snippet&utm_medium=snippet&utm_campaign=default+feature+snippet"
   1.147 +, "https://addons.mozilla.org/firefox/?utm_source=snippet&utm_medium=snippet&utm_campaign=addons"
   1.148 +];
   1.149 +
   1.150 +const SNIPPETS_UPDATE_INTERVAL_MS = 86400000; // 1 Day.
   1.151 +
   1.152 +// IndexedDB storage constants.
   1.153 +const DATABASE_NAME = "abouthome";
   1.154 +const DATABASE_VERSION = 1;
   1.155 +const SNIPPETS_OBJECTSTORE_NAME = "snippets";
   1.156 +
   1.157 +// This global tracks if the page has been set up before, to prevent double inits
   1.158 +let gInitialized = false;
   1.159 +let gObserver = new MutationObserver(function (mutations) {
   1.160 +  for (let mutation of mutations) {
   1.161 +    if (mutation.attributeName == "searchEngineName") {
   1.162 +      setupSearchEngine();
   1.163 +      if (!gInitialized) {
   1.164 +        ensureSnippetsMapThen(loadSnippets);
   1.165 +        gInitialized = true;
   1.166 +      }
   1.167 +      return;
   1.168 +    }
   1.169 +  }
   1.170 +});
   1.171 +
   1.172 +window.addEventListener("pageshow", function () {
   1.173 +  // Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs
   1.174 +  // later and may use asynchronous getters.
   1.175 +  window.gObserver.observe(document.documentElement, { attributes: true });
   1.176 +  fitToWidth();
   1.177 +  window.addEventListener("resize", fitToWidth);
   1.178 +
   1.179 +  // Ask chrome to update snippets.
   1.180 +  var event = new CustomEvent("AboutHomeLoad", {bubbles:true});
   1.181 +  document.dispatchEvent(event);
   1.182 +});
   1.183 +
   1.184 +window.addEventListener("pagehide", function() {
   1.185 +  window.gObserver.disconnect();
   1.186 +  window.removeEventListener("resize", fitToWidth);
   1.187 +});
   1.188 +
   1.189 +// This object has the same interface as Map and is used to store and retrieve
   1.190 +// the snippets data.  It is lazily initialized by ensureSnippetsMapThen(), so
   1.191 +// be sure its callback returned before trying to use it.
   1.192 +let gSnippetsMap;
   1.193 +let gSnippetsMapCallbacks = [];
   1.194 +
   1.195 +/**
   1.196 + * Ensure the snippets map is properly initialized.
   1.197 + *
   1.198 + * @param aCallback
   1.199 + *        Invoked once the map has been initialized, gets the map as argument.
   1.200 + * @note Snippets should never directly manage the underlying storage, since
   1.201 + *       it may change inadvertently.
   1.202 + */
   1.203 +function ensureSnippetsMapThen(aCallback)
   1.204 +{
   1.205 +  if (gSnippetsMap) {
   1.206 +    aCallback(gSnippetsMap);
   1.207 +    return;
   1.208 +  }
   1.209 +
   1.210 +  // Handle multiple requests during the async initialization.
   1.211 +  gSnippetsMapCallbacks.push(aCallback);
   1.212 +  if (gSnippetsMapCallbacks.length > 1) {
   1.213 +    // We are already updating, the callbacks will be invoked when done.
   1.214 +    return;
   1.215 +  }
   1.216 +
   1.217 +  let invokeCallbacks = function () {
   1.218 +    if (!gSnippetsMap) {
   1.219 +      gSnippetsMap = Object.freeze(new Map());
   1.220 +    }
   1.221 +
   1.222 +    for (let callback of gSnippetsMapCallbacks) {
   1.223 +      callback(gSnippetsMap);
   1.224 +    }
   1.225 +    gSnippetsMapCallbacks.length = 0;
   1.226 +  }
   1.227 +
   1.228 +  let openRequest = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
   1.229 +
   1.230 +  openRequest.onerror = function (event) {
   1.231 +    // Try to delete the old database so that we can start this process over
   1.232 +    // next time.
   1.233 +    indexedDB.deleteDatabase(DATABASE_NAME);
   1.234 +    invokeCallbacks();
   1.235 +  };
   1.236 +
   1.237 +  openRequest.onupgradeneeded = function (event) {
   1.238 +    let db = event.target.result;
   1.239 +    if (!db.objectStoreNames.contains(SNIPPETS_OBJECTSTORE_NAME)) {
   1.240 +      db.createObjectStore(SNIPPETS_OBJECTSTORE_NAME);
   1.241 +    }
   1.242 +  }
   1.243 +
   1.244 +  openRequest.onsuccess = function (event) {
   1.245 +    let db = event.target.result;
   1.246 +
   1.247 +    db.onerror = function (event) {
   1.248 +      invokeCallbacks();
   1.249 +    }
   1.250 +
   1.251 +    db.onversionchange = function (event) {
   1.252 +      event.target.close();
   1.253 +      invokeCallbacks();
   1.254 +    }
   1.255 +
   1.256 +    let cache = new Map();
   1.257 +    let cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME)
   1.258 +                          .objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor();
   1.259 +    cursorRequest.onerror = function (event) {
   1.260 +      invokeCallbacks();
   1.261 +    }
   1.262 +
   1.263 +    cursorRequest.onsuccess = function(event) {
   1.264 +      let cursor = event.target.result;
   1.265 +
   1.266 +      // Populate the cache from the persistent storage.
   1.267 +      if (cursor) {
   1.268 +        cache.set(cursor.key, cursor.value);
   1.269 +        cursor.continue();
   1.270 +        return;
   1.271 +      }
   1.272 +
   1.273 +      // The cache has been filled up, create the snippets map.
   1.274 +      gSnippetsMap = Object.freeze({
   1.275 +        get: function (aKey) cache.get(aKey),
   1.276 +        set: function (aKey, aValue) {
   1.277 +          db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
   1.278 +            .objectStore(SNIPPETS_OBJECTSTORE_NAME).put(aValue, aKey);
   1.279 +          return cache.set(aKey, aValue);
   1.280 +        },
   1.281 +        has: function (aKey) cache.has(aKey),
   1.282 +        delete: function (aKey) {
   1.283 +          db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
   1.284 +            .objectStore(SNIPPETS_OBJECTSTORE_NAME).delete(aKey);
   1.285 +          return cache.delete(aKey);
   1.286 +        },
   1.287 +        clear: function () {
   1.288 +          db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
   1.289 +            .objectStore(SNIPPETS_OBJECTSTORE_NAME).clear();
   1.290 +          return cache.clear();
   1.291 +        },
   1.292 +        get size() cache.size
   1.293 +      });
   1.294 +
   1.295 +      setTimeout(invokeCallbacks, 0);
   1.296 +    }
   1.297 +  }
   1.298 +}
   1.299 +
   1.300 +function onSearchSubmit(aEvent)
   1.301 +{
   1.302 +  let searchTerms = document.getElementById("searchText").value;
   1.303 +  let engineName = document.documentElement.getAttribute("searchEngineName");
   1.304 +
   1.305 +  if (engineName && searchTerms.length > 0) {
   1.306 +    // Send an event that will perform a search and Firefox Health Report will
   1.307 +    // record that a search from about:home has occurred.
   1.308 +    let eventData = JSON.stringify({
   1.309 +      engineName: engineName,
   1.310 +      searchTerms: searchTerms
   1.311 +    });
   1.312 +    let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
   1.313 +    document.dispatchEvent(event);
   1.314 +  }
   1.315 +
   1.316 +  aEvent.preventDefault();
   1.317 +}
   1.318 +
   1.319 +
   1.320 +function setupSearchEngine()
   1.321 +{
   1.322 +  // The "autofocus" attribute doesn't focus the form element
   1.323 +  // immediately when the element is first drawn, so the
   1.324 +  // attribute is also used for styling when the page first loads.
   1.325 +  let searchText = document.getElementById("searchText");
   1.326 +  searchText.addEventListener("blur", function searchText_onBlur() {
   1.327 +    searchText.removeEventListener("blur", searchText_onBlur);
   1.328 +    searchText.removeAttribute("autofocus");
   1.329 +  });
   1.330 + 
   1.331 +  let searchEngineName = document.documentElement.getAttribute("searchEngineName");
   1.332 +  let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
   1.333 +  let logoElt = document.getElementById("searchEngineLogo");
   1.334 +
   1.335 +  // Add search engine logo.
   1.336 +  if (searchEngineInfo && searchEngineInfo.image) {
   1.337 +    logoElt.parentNode.hidden = false;
   1.338 +    logoElt.src = searchEngineInfo.image;
   1.339 +    logoElt.alt = searchEngineName;
   1.340 +    searchText.placeholder = "";
   1.341 +  }
   1.342 +  else {
   1.343 +    logoElt.parentNode.hidden = true;
   1.344 +    searchText.placeholder = searchEngineName;
   1.345 +  }
   1.346 +
   1.347 +}
   1.348 +
   1.349 +/**
   1.350 + * Inform the test harness that we're done loading the page.
   1.351 + */
   1.352 +function loadSucceeded()
   1.353 +{
   1.354 +  var event = new CustomEvent("AboutHomeLoadSnippetsSucceeded", {bubbles:true});
   1.355 +  document.dispatchEvent(event);
   1.356 +}
   1.357 +
   1.358 +/**
   1.359 + * Update the local snippets from the remote storage, then show them through
   1.360 + * showSnippets.
   1.361 + */
   1.362 +function loadSnippets()
   1.363 +{
   1.364 +  if (!gSnippetsMap)
   1.365 +    throw new Error("Snippets map has not properly been initialized");
   1.366 +
   1.367 +  // Allow tests to modify the snippets map before using it.
   1.368 +  var event = new CustomEvent("AboutHomeLoadSnippets", {bubbles:true});
   1.369 +  document.dispatchEvent(event);
   1.370 +
   1.371 +  // Check cached snippets version.
   1.372 +  let cachedVersion = gSnippetsMap.get("snippets-cached-version") || 0;
   1.373 +  let currentVersion = document.documentElement.getAttribute("snippetsVersion");
   1.374 +  if (cachedVersion < currentVersion) {
   1.375 +    // The cached snippets are old and unsupported, restart from scratch.
   1.376 +    gSnippetsMap.clear();
   1.377 +  }
   1.378 +
   1.379 +  // Check last snippets update.
   1.380 +  let lastUpdate = gSnippetsMap.get("snippets-last-update");
   1.381 +  let updateURL = document.documentElement.getAttribute("snippetsURL");
   1.382 +  let shouldUpdate = !lastUpdate ||
   1.383 +                     Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
   1.384 +  if (updateURL && shouldUpdate) {
   1.385 +    // Try to update from network.
   1.386 +    let xhr = new XMLHttpRequest();
   1.387 +    try {
   1.388 +      xhr.open("GET", updateURL, true);
   1.389 +    } catch (ex) {
   1.390 +      showSnippets();
   1.391 +      loadSucceeded();
   1.392 +      return;
   1.393 +    }
   1.394 +    // Even if fetching should fail we don't want to spam the server, thus
   1.395 +    // set the last update time regardless its results.  Will retry tomorrow.
   1.396 +    gSnippetsMap.set("snippets-last-update", Date.now());
   1.397 +    xhr.onerror = function (event) {
   1.398 +      showSnippets();
   1.399 +    };
   1.400 +    xhr.onload = function (event)
   1.401 +    {
   1.402 +      if (xhr.status == 200) {
   1.403 +        gSnippetsMap.set("snippets", xhr.responseText);
   1.404 +        gSnippetsMap.set("snippets-cached-version", currentVersion);
   1.405 +      }
   1.406 +      showSnippets();
   1.407 +      loadSucceeded();
   1.408 +    };
   1.409 +    xhr.send(null);
   1.410 +  } else {
   1.411 +    showSnippets();
   1.412 +    loadSucceeded();
   1.413 +  }
   1.414 +}
   1.415 +
   1.416 +/**
   1.417 + * Shows locally cached remote snippets, or default ones when not available.
   1.418 + *
   1.419 + * @note: snippets should never invoke showSnippets(), or they may cause
   1.420 + *        a "too much recursion" exception.
   1.421 + */
   1.422 +let _snippetsShown = false;
   1.423 +function showSnippets()
   1.424 +{
   1.425 +  let snippetsElt = document.getElementById("snippets");
   1.426 +
   1.427 +  // Show about:rights notification, if needed.
   1.428 +  let showRights = document.documentElement.getAttribute("showKnowYourRights");
   1.429 +  if (showRights) {
   1.430 +    let rightsElt = document.getElementById("rightsSnippet");
   1.431 +    let anchor = rightsElt.getElementsByTagName("a")[0];
   1.432 +    anchor.href = "about:rights";
   1.433 +    snippetsElt.appendChild(rightsElt);
   1.434 +    rightsElt.removeAttribute("hidden");
   1.435 +    return;
   1.436 +  }
   1.437 +
   1.438 +  if (!gSnippetsMap)
   1.439 +    throw new Error("Snippets map has not properly been initialized");
   1.440 +  if (_snippetsShown) {
   1.441 +    // There's something wrong with the remote snippets, just in case fall back
   1.442 +    // to the default snippets.
   1.443 +    showDefaultSnippets();
   1.444 +    throw new Error("showSnippets should never be invoked multiple times");
   1.445 +  }
   1.446 +  _snippetsShown = true;
   1.447 +
   1.448 +  let snippets = gSnippetsMap.get("snippets");
   1.449 +  // If there are remotely fetched snippets, try to to show them.
   1.450 +  if (snippets) {
   1.451 +    // Injecting snippets can throw if they're invalid XML.
   1.452 +    try {
   1.453 +      snippetsElt.innerHTML = snippets;
   1.454 +      // Scripts injected by innerHTML are inactive, so we have to relocate them
   1.455 +      // through DOM manipulation to activate their contents.
   1.456 +      Array.forEach(snippetsElt.getElementsByTagName("script"), function(elt) {
   1.457 +        let relocatedScript = document.createElement("script");
   1.458 +        relocatedScript.type = "text/javascript;version=1.8";
   1.459 +        relocatedScript.text = elt.text;
   1.460 +        elt.parentNode.replaceChild(relocatedScript, elt);
   1.461 +      });
   1.462 +      return;
   1.463 +    } catch (ex) {
   1.464 +      // Bad content, continue to show default snippets.
   1.465 +    }
   1.466 +  }
   1.467 +
   1.468 +  showDefaultSnippets();
   1.469 +}
   1.470 +
   1.471 +/**
   1.472 + * Clear snippets element contents and show default snippets.
   1.473 + */
   1.474 +function showDefaultSnippets()
   1.475 +{
   1.476 +  // Clear eventual contents...
   1.477 +  let snippetsElt = document.getElementById("snippets");
   1.478 +  snippetsElt.innerHTML = "";
   1.479 +
   1.480 +  // ...then show default snippets.
   1.481 +  let defaultSnippetsElt = document.getElementById("defaultSnippets");
   1.482 +  let entries = defaultSnippetsElt.querySelectorAll("span");
   1.483 +  // Choose a random snippet.  Assume there is always at least one.
   1.484 +  let randIndex = Math.floor(Math.random() * entries.length);
   1.485 +  let entry = entries[randIndex];
   1.486 +  // Inject url in the eventual link.
   1.487 +  if (DEFAULT_SNIPPETS_URLS[randIndex]) {
   1.488 +    let links = entry.getElementsByTagName("a");
   1.489 +    // Default snippets can have only one link, otherwise something is messed
   1.490 +    // up in the translation.
   1.491 +    if (links.length == 1) {
   1.492 +      links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
   1.493 +    }
   1.494 +  }
   1.495 +  // Move the default snippet to the snippets element.
   1.496 +  snippetsElt.appendChild(entry);
   1.497 +}
   1.498 +
   1.499 +function fitToWidth() {
   1.500 +  if (window.scrollMaxX) {
   1.501 +    document.body.setAttribute("narrow", "true");
   1.502 +  } else if (document.body.hasAttribute("narrow")) {
   1.503 +    document.body.removeAttribute("narrow");
   1.504 +    fitToWidth();
   1.505 +  }
   1.506 +}

mercurial