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 +}